This repository has been archived by the owner on Nov 28, 2024. It is now read-only.
forked from hyperledger-iroha/iroha
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat!: reimplement multisignature transactions (hyperledger-iroha#4788)
Signed-off-by: Shanin Roman <[email protected]>
- Loading branch information
Showing
21 changed files
with
633 additions
and
18 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,150 @@ | ||
use std::{collections::BTreeMap, str::FromStr}; | ||
|
||
use executor_custom_data_model::multisig::{MultisigArgs, MultisigRegisterArgs}; | ||
use eyre::Result; | ||
use iroha::{ | ||
client::{self, ClientQueryError}, | ||
crypto::KeyPair, | ||
data_model::{ | ||
prelude::*, | ||
transaction::{TransactionBuilder, WasmSmartContract}, | ||
}, | ||
}; | ||
use iroha_data_model::parameter::SmartContractParameter; | ||
use nonzero_ext::nonzero; | ||
use test_network::*; | ||
use test_samples::{gen_account_in, ALICE_ID}; | ||
|
||
#[test] | ||
fn mutlisig() -> Result<()> { | ||
let (_rt, _peer, test_client) = <PeerBuilder>::new().with_port(11_400).start_with_runtime(); | ||
wait_for_genesis_committed(&vec![test_client.clone()], 0); | ||
|
||
test_client.submit_all_blocking([ | ||
SetParameter::new(Parameter::SmartContract(SmartContractParameter::Fuel( | ||
nonzero!(100_000_000_u64), | ||
))), | ||
SetParameter::new(Parameter::Executor(SmartContractParameter::Fuel(nonzero!( | ||
100_000_000_u64 | ||
)))), | ||
])?; | ||
|
||
let account_id = ALICE_ID.clone(); | ||
let multisig_register_trigger_id = TriggerId::from_str("multisig_register")?; | ||
|
||
let wasm = | ||
iroha_wasm_builder::Builder::new("tests/integration/smartcontracts/multisig_register") | ||
.show_output() | ||
.build()? | ||
.optimize()? | ||
.into_bytes()?; | ||
let wasm = WasmSmartContract::from_compiled(wasm); | ||
|
||
let trigger = Trigger::new( | ||
multisig_register_trigger_id.clone(), | ||
Action::new( | ||
wasm, | ||
Repeats::Indefinitely, | ||
account_id.clone(), | ||
ExecuteTriggerEventFilter::new().for_trigger(multisig_register_trigger_id.clone()), | ||
), | ||
); | ||
|
||
// Register trigger which would allow multisig account creation in wonderland domain | ||
// Access to call this trigger shouldn't be restricted | ||
test_client.submit_blocking(Register::trigger(trigger))?; | ||
|
||
// Create multisig account id and destroy it's private key | ||
let multisig_account_id = gen_account_in("wonderland").0; | ||
|
||
let multisig_trigger_id: TriggerId = format!( | ||
"{}_{}_multisig_trigger", | ||
multisig_account_id.signatory(), | ||
multisig_account_id.domain() | ||
) | ||
.parse()?; | ||
|
||
let signatories = core::iter::repeat_with(|| gen_account_in("wonderland")) | ||
.take(5) | ||
.collect::<BTreeMap<AccountId, KeyPair>>(); | ||
|
||
let args = MultisigRegisterArgs { | ||
account: Account::new(multisig_account_id.clone()), | ||
signatories: signatories.keys().cloned().collect(), | ||
}; | ||
|
||
test_client.submit_all_blocking( | ||
signatories | ||
.keys() | ||
.cloned() | ||
.map(Account::new) | ||
.map(Register::account), | ||
)?; | ||
|
||
let call_trigger = ExecuteTrigger::new(multisig_register_trigger_id).with_args(&args); | ||
test_client.submit_blocking(call_trigger)?; | ||
|
||
// Check that multisig account exist | ||
let account = test_client | ||
.request(client::account::by_id(multisig_account_id.clone())) | ||
.expect("multisig account should be created after the call to register multisig trigger"); | ||
|
||
assert_eq!(account.id(), &multisig_account_id); | ||
|
||
// Check that multisig trigger exist | ||
let trigger = test_client | ||
.request(client::trigger::by_id(multisig_trigger_id.clone())) | ||
.expect("multisig trigger should be created after the call to register multisig trigger"); | ||
|
||
assert_eq!(trigger.id(), &multisig_trigger_id); | ||
|
||
let domain_id: DomainId = "domain_controlled_by_multisig".parse().unwrap(); | ||
let isi = vec![InstructionBox::from(Register::domain(Domain::new( | ||
domain_id.clone(), | ||
)))]; | ||
let isi_hash = HashOf::new(&isi); | ||
|
||
let mut signatories_iter = signatories.into_iter(); | ||
|
||
if let Some((signatory, key_pair)) = signatories_iter.next() { | ||
let args = MultisigArgs::Instructions(isi); | ||
let call_trigger = ExecuteTrigger::new(multisig_trigger_id.clone()).with_args(&args); | ||
test_client.submit_transaction_blocking( | ||
&TransactionBuilder::new(test_client.chain.clone(), signatory) | ||
.with_instructions([call_trigger]) | ||
.sign(key_pair.private_key()), | ||
)?; | ||
} | ||
|
||
// Check that domain isn't created yet | ||
let err = test_client | ||
.request(client::domain::by_id(domain_id.clone())) | ||
.expect_err("domain shouldn't be created before enough votes are collected"); | ||
assert!(matches!( | ||
err, | ||
ClientQueryError::Validation(iroha_data_model::ValidationFail::QueryFailed( | ||
iroha_data_model::query::error::QueryExecutionFail::Find( | ||
iroha_data_model::query::error::FindError::Domain(err_domain_id) | ||
) | ||
)) if domain_id == err_domain_id | ||
)); | ||
|
||
for (signatory, key_pair) in signatories_iter { | ||
let args = MultisigArgs::Vote(isi_hash); | ||
let call_trigger = ExecuteTrigger::new(multisig_trigger_id.clone()).with_args(&args); | ||
test_client.submit_transaction_blocking( | ||
&TransactionBuilder::new(test_client.chain.clone(), signatory) | ||
.with_instructions([call_trigger]) | ||
.sign(key_pair.private_key()), | ||
)?; | ||
} | ||
|
||
// Check that new domain was created and multisig account is owner | ||
let domain = test_client | ||
.request(client::domain::by_id(domain_id.clone())) | ||
.expect("domain should be created after enough votes are collected"); | ||
|
||
assert_eq!(domain.owned_by(), &multisig_account_id); | ||
|
||
Ok(()) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
10 changes: 10 additions & 0 deletions
10
client/tests/integration/smartcontracts/executor_custom_data_model/src/mint_rose_args.rs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
//! Arguments to mint rose with args trigger | ||
use serde::{Deserialize, Serialize}; | ||
|
||
/// Arguments to mint rose with args trigger | ||
#[derive(Serialize, Deserialize)] | ||
pub struct MintRoseArgs { | ||
// Amount to mint | ||
pub val: u32, | ||
} |
24 changes: 24 additions & 0 deletions
24
client/tests/integration/smartcontracts/executor_custom_data_model/src/multisig.rs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
//! Arguments to register and manage multisig account | ||
use alloc::{collections::btree_set::BTreeSet, vec::Vec}; | ||
|
||
use iroha_data_model::{account::NewAccount, prelude::*}; | ||
use serde::{Deserialize, Serialize}; | ||
|
||
/// Arguments to multisig account register trigger | ||
#[derive(Serialize, Deserialize)] | ||
pub struct MultisigRegisterArgs { | ||
// Account id of multisig account should be manually checked to not have corresponding private key (or having master key is ok) | ||
pub account: NewAccount, | ||
// List of accounts responsible for handling multisig account | ||
pub signatories: BTreeSet<AccountId>, | ||
} | ||
|
||
/// Arguments to multisig account manager trigger | ||
#[derive(Serialize, Deserialize)] | ||
pub enum MultisigArgs { | ||
/// Accept instructions proposal and initialize votes with the proposer's one | ||
Instructions(Vec<InstructionBox>), | ||
/// Accept vote for certain instructions | ||
Vote(HashOf<Vec<InstructionBox>>), | ||
} |
22 changes: 22 additions & 0 deletions
22
client/tests/integration/smartcontracts/mint_rose_trigger_args/Cargo.toml
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
[package] | ||
name = "mint_rose_args" | ||
|
||
edition.workspace = true | ||
version.workspace = true | ||
authors.workspace = true | ||
|
||
license.workspace = true | ||
|
||
[lib] | ||
crate-type = ['cdylib'] | ||
|
||
[dependencies] | ||
iroha_trigger.workspace = true | ||
executor_custom_data_model.workspace = true | ||
|
||
panic-halt.workspace = true | ||
lol_alloc.workspace = true | ||
getrandom.workspace = true | ||
|
||
serde = { workspace = true, features = ["derive"] } | ||
serde_json = { workspace = true, default-features = false } |
41 changes: 41 additions & 0 deletions
41
client/tests/integration/smartcontracts/mint_rose_trigger_args/src/lib.rs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,41 @@ | ||
//! trigger which mints rose for its owner based on input args. | ||
#![no_std] | ||
|
||
#[cfg(not(test))] | ||
extern crate panic_halt; | ||
|
||
use core::str::FromStr as _; | ||
|
||
use executor_custom_data_model::mint_rose_args::MintRoseArgs; | ||
use iroha_trigger::{debug::dbg_panic, prelude::*}; | ||
use lol_alloc::{FreeListAllocator, LockedAllocator}; | ||
use serde::{Deserialize, Serialize}; | ||
|
||
#[global_allocator] | ||
static ALLOC: LockedAllocator<FreeListAllocator> = LockedAllocator::new(FreeListAllocator::new()); | ||
|
||
getrandom::register_custom_getrandom!(iroha_trigger::stub_getrandom); | ||
|
||
/// Mint 1 rose for owner | ||
#[iroha_trigger::main] | ||
fn main(_id: TriggerId, owner: AccountId, event: EventBox) { | ||
let rose_definition_id = AssetDefinitionId::from_str("rose#wonderland") | ||
.dbg_expect("Failed to parse `rose#wonderland` asset definition id"); | ||
let rose_id = AssetId::new(rose_definition_id, owner); | ||
|
||
let args: MintRoseArgs = match event { | ||
EventBox::ExecuteTrigger(event) => event | ||
.args() | ||
.dbg_expect("Trigger expect parameters") | ||
.try_into_any() | ||
.dbg_expect("Failed to parse args"), | ||
_ => dbg_panic("Only work as by call trigger"), | ||
}; | ||
|
||
let val = args.val; | ||
|
||
Mint::asset_numeric(val, rose_id) | ||
.execute() | ||
.dbg_expect("Failed to mint rose"); | ||
} |
25 changes: 25 additions & 0 deletions
25
client/tests/integration/smartcontracts/multisig/Cargo.toml
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
[package] | ||
name = "multisig" | ||
|
||
edition.workspace = true | ||
version.workspace = true | ||
authors.workspace = true | ||
|
||
license.workspace = true | ||
|
||
[lib] | ||
crate-type = ['cdylib'] | ||
|
||
[dependencies] | ||
iroha_trigger.workspace = true | ||
executor_custom_data_model.workspace = true | ||
|
||
panic-halt.workspace = true | ||
lol_alloc.workspace = true | ||
getrandom.workspace = true | ||
|
||
serde = { workspace = true, features = ["derive"] } | ||
serde_json = { workspace = true, default-features = false } | ||
|
||
[build-dependencies] | ||
iroha_wasm_builder = { version = "=2.0.0-pre-rc.21", path = "../../../../../wasm_builder" } |
Oops, something went wrong.