Skip to content

Commit

Permalink
feat!: recognize and activate accounts
Browse files Browse the repository at this point in the history
BREAKING_CHANGE:

- `Account` contains `is_active` field
- `AccountEvent::Created` divides into `Recognized` and `Activated`
- `ValidationFail` contains `UnrecognizedAuthority` and `InactiveAuthority` variants

Signed-off-by: Shunkichi Sato <[email protected]>
  • Loading branch information
s8sato committed Jun 7, 2024
1 parent e9557e5 commit d72a0df
Show file tree
Hide file tree
Showing 26 changed files with 495 additions and 128 deletions.
2 changes: 2 additions & 0 deletions client/tests/integration/account_registration/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
mod private;
mod public;
90 changes: 90 additions & 0 deletions client/tests/integration/account_registration/private.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
//! In private use cases, a new account should be:
//! - recognized when targeted as (a part of) a destination (or an object) of a creative instruction
//! - becomes able to hold assets, permissions, roles, and metadata
//! - cannot yet execute any queries or transactions
//! - activated when manually registered by an administrative account
//! - becomes able to execute queries and transactions
use iroha::data_model::prelude::*;
use test_network::*;
use test_samples::{gen_account_in, ALICE_ID};

/// A new account e.g. "carol" should be:
/// - recognized when targeted as a destination of a transfer of numeric asset e.g. "rose"
/// - activated when manually registered by an administrative account e.g. "alice"
///
/// # Scenario
///
/// 0. new carol targeted ... carol recognized
/// 0. carol tries query ... err
/// 0. carol tries transaction ... err
/// 0. register carol ... carol activated
/// 0. carol tries query ... ok
/// 0. carol tries transaction ... ok
#[test]
fn on_transfer_asset_numeric() {
let (_rt, _peer, client_alice) = <PeerBuilder>::new().with_port(10_820).start_with_runtime();
wait_for_genesis_committed(&[client_alice.clone()], 0);
let observer = client_alice.clone();

// new carol targeted ... carol recognized
let (carol_id, carol_keypair) = gen_account_in("wonderland");
let _ = observer
.request(FindAccountById::new(carol_id.clone()))
.expect_err("carol should not be recognized at this point");
let rose: AssetDefinitionId = "rose#wonderland".parse().unwrap();
let rose_alice = AssetId::new(rose.clone(), ALICE_ID.clone());
let n_roses_alice = observer
.request(FindAssetQuantityById::new(rose_alice.clone()))
.expect("alice should have roses");
assert!(numeric!(3) < n_roses_alice);
let transfer = Transfer::asset_numeric(rose_alice, 3_u32, carol_id.clone());
client_alice
.submit_blocking(transfer)
.expect("alice should succeed to transfer");
let _ = observer
.request(FindAccountById::new(carol_id.clone()))
.expect("carol should be recognized now");
let rose_carol = AssetId::new(rose.clone(), carol_id.clone());
let n_roses_carol = observer
.request(FindAssetQuantityById::new(rose_carol.clone()))
.expect("carol should have roses");
assert_eq!(n_roses_carol, numeric!(3));

// carol tries query ... err
let client_carol = {
let mut client = client_alice.clone();
client.key_pair = carol_keypair;
client.account_id = carol_id.clone();
client
};
let query = FindAssetQuantityById::new(rose_carol.clone());
let _ = client_carol
.request(query.clone())
.expect_err("queries from inactive carol should be rejected");

// carol tries transaction ... err
let instruction = Log::new(
iroha_data_model::Level::DEBUG,
"the one least likely to be rejected".to_string(),
);
let _ = client_carol
.submit_blocking(instruction.clone())
.expect_err("transactions from inactive carol should be rejected");

// register carol ... carol activated
let register = Register::account(Account::new(carol_id.clone()));
client_alice
.submit_blocking(register)
.expect("alice should succeed to register");

// carol tries query ... ok
let _ = client_carol
.request(query)
.expect("queries from active carol should be accepted");

// carol tries transaction ... ok
client_carol
.submit_blocking(instruction)
.expect("transactions from active carol should be accepted");
}
102 changes: 102 additions & 0 deletions client/tests/integration/account_registration/public.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
//! In public use cases, a new account should be:
//! - recognized when targeted as (a part of) a destination (or an object) of a creative instruction
//! - becomes able to hold assets, permissions, roles, and metadata
//! - cannot yet execute any queries or transactions
//! - automatically activated by an administrative trigger
//! - becomes able to execute queries and transactions
use eyre::Result;
use iroha::data_model::prelude::*;
use test_network::*;
use test_samples::{gen_account_in, ALICE_ID};

/// A new account e.g. "carol" should be:
/// - recognized when targeted as a destination of a transfer of numeric asset e.g. "rose"
/// - automatically activated by an administrative trigger
///
/// # Scenario
///
/// 0. ... administrative trigger registered
/// 0. new carol targeted ... carol recognized
/// 0. clock forward by one block ... carol activated
/// 0. carol tries query ... ok
/// 0. carol tries transaction ... ok
#[test]
fn on_transfer_asset_numeric() -> Result<()> {
let (_rt, _peer, client_alice) = <PeerBuilder>::new().with_port(10_810).start_with_runtime();
wait_for_genesis_committed(&[client_alice.clone()], 0);
let observer = client_alice.clone();

// ... administrative trigger registered
let wasm = iroha_wasm_builder::Builder::new(
"tests/integration/smartcontracts/trigger_activate_account",
)
.show_output()
.build()?
.optimize()?
.into_bytes()?;
let register_activator = Register::trigger(Trigger::new(
"activator$wonderland".parse().unwrap(),
Action::new(
WasmSmartContract::from_compiled(wasm),
Repeats::Indefinitely,
ALICE_ID.clone(),
AccountEventFilter::new().for_events(AccountEventSet::Recognized),
),
));
client_alice
.submit_blocking(register_activator)
.expect("alice should succeed to register activator");

// new carol targeted ... carol recognized
let (carol_id, carol_keypair) = gen_account_in("wonderland");
let _ = observer
.request(FindAccountById::new(carol_id.clone()))
.expect_err("carol should not be recognized at this point");
let rose: AssetDefinitionId = "rose#wonderland".parse().unwrap();
let rose_alice = AssetId::new(rose.clone(), ALICE_ID.clone());
let n_roses_alice = observer
.request(FindAssetQuantityById::new(rose_alice.clone()))
.expect("alice should have roses");
assert!(numeric!(3) < n_roses_alice);
let transfer = Transfer::asset_numeric(rose_alice, 3_u32, carol_id.clone());
client_alice
.submit_blocking(transfer)
.expect("alice should succeed to transfer");
let _ = observer
.request(FindAccountById::new(carol_id.clone()))
.expect("carol should be recognized now");
let rose_carol = AssetId::new(rose.clone(), carol_id.clone());
let n_roses_carol = observer
.request(FindAssetQuantityById::new(rose_carol.clone()))
.expect("carol should have roses");
assert_eq!(n_roses_carol, numeric!(3));

// clock forward by one block ... carol activated
let instruction = Log::new(
iroha_data_model::Level::DEBUG,
"the one least likely to be rejected".to_string(),
);
client_alice
.submit_blocking(instruction.clone())
.expect("instruction should succeed");

// carol tries query ... ok
let client_carol = {
let mut client = client_alice.clone();
client.key_pair = carol_keypair;
client.account_id = carol_id.clone();
client
};
let query = FindAssetQuantityById::new(rose_carol.clone());
let _ = client_carol
.request(query)
.expect("queries from active carol should be accepted");

// carol tries transaction ... ok
client_carol
.submit_blocking(instruction)
.expect("transactions from active carol should be accepted");

Ok(())
}
1 change: 1 addition & 0 deletions client/tests/integration/mod.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
mod account_registration;
mod add_domain;
mod asset;
mod asset_propagation;
Expand Down
1 change: 1 addition & 0 deletions client/tests/integration/smartcontracts/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ members = [
"executor_with_migration_fail",
"query_assets_and_save_cursor",
"smart_contract_can_filter_queries",
"trigger_activate_account",
]

[profile.dev]
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
[package]
name = "trigger_activate_account"

edition.workspace = true
version.workspace = true
authors.workspace = true

license.workspace = true

[lib]
crate-type = ['cdylib']

[dependencies]
iroha_trigger.workspace = true

panic-halt.workspace = true
lol_alloc.workspace = true
getrandom.workspace = true
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
//! Trigger responsible for activating newly recognized accounts in "wonderland"
#![no_std]

#[cfg(not(test))]
extern crate panic_halt;

use iroha_trigger::prelude::*;
use lol_alloc::{FreeListAllocator, LockedAllocator};

#[global_allocator]
static ALLOC: LockedAllocator<FreeListAllocator> = LockedAllocator::new(FreeListAllocator::new());

getrandom::register_custom_getrandom!(iroha_trigger::stub_getrandom);

#[iroha_trigger::main]
fn main(_id: TriggerId, _owner: AccountId, event: EventBox) {
let EventBox::Data(DataEvent::Domain(DomainEvent::Account(AccountEvent::Recognized(
account_id,
)))) = event
else {
return;
};

Register::account(Account::new(account_id))
.execute()
.dbg_expect(
"authority should be alice, and alice should succeed to register accounts in wonderland",
);
}
2 changes: 1 addition & 1 deletion client/tests/integration/triggers/data_trigger.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ fn must_execute_both_triggers() -> Result<()> {
[instruction.clone()],
Repeats::Indefinitely,
account_id.clone(),
AccountEventFilter::new().for_events(AccountEventSet::Created),
AccountEventFilter::new().for_events(AccountEventSet::Activated),
),
));
test_client.submit_blocking(register_trigger)?;
Expand Down
3 changes: 2 additions & 1 deletion core/benches/validation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,8 @@ fn build_test_and_transient_state() -> State {
{
let (account_id, _account_keypair) = gen_account_in(&*STARTER_DOMAIN);
let mut domain = Domain::new(STARTER_DOMAIN.clone()).build(&account_id);
let account = Account::new(account_id.clone()).build(&account_id);
let mut account = Account::new(account_id.clone()).build(&account_id);
account.activate();
assert!(domain.add_account(account).is_none());
World::with([domain], UniqueVec::new())
},
Expand Down
12 changes: 8 additions & 4 deletions core/src/block.rs
Original file line number Diff line number Diff line change
Expand Up @@ -821,7 +821,8 @@ mod tests {

// Predefined world state
let (alice_id, alice_keypair) = gen_account_in("wonderland");
let account = Account::new(alice_id.clone()).build(&alice_id);
let mut account = Account::new(alice_id.clone()).build(&alice_id);
account.activate();
let domain_id = DomainId::from_str("wonderland").expect("Valid");
let mut domain = Domain::new(domain_id).build(&alice_id);
assert!(domain.add_account(account).is_none());
Expand Down Expand Up @@ -876,7 +877,8 @@ mod tests {

// Predefined world state
let (alice_id, alice_keypair) = gen_account_in("wonderland");
let account = Account::new(alice_id.clone()).build(&alice_id);
let mut account = Account::new(alice_id.clone()).build(&alice_id);
account.activate();
let domain_id = DomainId::from_str("wonderland").expect("Valid");
let mut domain = Domain::new(domain_id).build(&alice_id);
assert!(domain.add_account(account).is_none());
Expand Down Expand Up @@ -949,7 +951,8 @@ mod tests {

// Predefined world state
let (alice_id, alice_keypair) = gen_account_in("wonderland");
let account = Account::new(alice_id.clone()).build(&alice_id);
let mut account = Account::new(alice_id.clone()).build(&alice_id);
account.activate();
let domain_id = DomainId::from_str("wonderland").expect("Valid");
let mut domain = Domain::new(domain_id).build(&alice_id);
assert!(
Expand Down Expand Up @@ -1034,8 +1037,9 @@ mod tests {
);
let mut genesis_domain =
Domain::new(GENESIS_DOMAIN_ID.clone()).build(&genesis_correct_account_id);
let genesis_wrong_account =
let mut genesis_wrong_account =
Account::new(genesis_wrong_account_id.clone()).build(&genesis_wrong_account_id);
genesis_wrong_account.activate();
assert!(genesis_domain.add_account(genesis_wrong_account).is_none(),);
let world = World::with([genesis_domain], UniqueVec::new());
let kura = Kura::blank_kura_for_testing();
Expand Down
Loading

0 comments on commit d72a0df

Please sign in to comment.