diff --git a/crates/iroha/Cargo.toml b/crates/iroha/Cargo.toml index 0c1b753c0f7..a97bf53a566 100644 --- a/crates/iroha/Cargo.toml +++ b/crates/iroha/Cargo.toml @@ -59,7 +59,6 @@ iroha_logger = { workspace = true } iroha_telemetry = { workspace = true } iroha_torii_const = { workspace = true } iroha_version = { workspace = true } -iroha_test_samples = { workspace = true } attohttpc = { version = "0.28.0", default-features = false } eyre = { workspace = true } @@ -83,6 +82,7 @@ toml = { workspace = true } nonzero_ext = { workspace = true } [dev-dependencies] +iroha_test_samples = { workspace = true } iroha_genesis = { workspace = true } iroha_test_network = { workspace = true } executor_custom_data_model = { version = "=2.0.0-rc.1.0", path = "../../wasm_samples/executor_custom_data_model" } diff --git a/crates/iroha/examples/tutorial.rs b/crates/iroha/examples/tutorial.rs index 75238e76b00..5867bd15fb5 100644 --- a/crates/iroha/examples/tutorial.rs +++ b/crates/iroha/examples/tutorial.rs @@ -2,8 +2,7 @@ //! use eyre::{Error, WrapErr}; -use iroha::config::Config; -use iroha_primitives::numeric::Numeric; +use iroha::{config::Config, data_model::prelude::Numeric}; // #region rust_config_crates // #endregion rust_config_crates diff --git a/crates/iroha/src/query.rs b/crates/iroha/src/query.rs index c676b31e609..42880b28727 100644 --- a/crates/iroha/src/query.rs +++ b/crates/iroha/src/query.rs @@ -4,25 +4,24 @@ use std::{collections::HashMap, fmt::Debug}; use eyre::{eyre, Context, Result}; use http::StatusCode; -use iroha_crypto::KeyPair; -use iroha_data_model::{ - account::AccountId, - query::{ - builder::{QueryBuilder, QueryExecutor}, - parameters::ForwardCursor, - predicate::HasPredicateBox, - QueryOutput, QueryOutputBatchBox, QueryRequest, QueryResponse, QueryWithParams, - SingularQuery, SingularQueryBox, SingularQueryOutputBox, - }, - ValidationFail, -}; use iroha_torii_const::uri as torii_uri; use parity_scale_codec::{DecodeAll, Encode}; use url::Url; use crate::{ client::{join_torii_url, Client, QueryResult, ResponseReport}, - data_model::query::Query, + crypto::KeyPair, + data_model::{ + account::AccountId, + query::{ + builder::{QueryBuilder, QueryExecutor}, + parameters::ForwardCursor, + predicate::HasPredicateBox, + Query, QueryOutput, QueryOutputBatchBox, QueryRequest, QueryResponse, QueryWithParams, + SingularQuery, SingularQueryBox, SingularQueryOutputBox, + }, + ValidationFail, + }, http::{Method as HttpMethod, RequestBuilder}, http_default::DefaultRequestBuilder, }; diff --git a/crates/iroha/tests/integration/events/pipeline.rs b/crates/iroha/tests/integration/events/pipeline.rs index 26427dff2c0..f0c990ad440 100644 --- a/crates/iroha/tests/integration/events/pipeline.rs +++ b/crates/iroha/tests/integration/events/pipeline.rs @@ -77,5 +77,5 @@ async fn test_with_instruction_and_status( #[test] #[ignore = "TODO: implement with the help of Kura Inspector, "] fn applied_block_must_be_available_in_kura() { - unimplemented!(); + unimplemented!("Take a look at previous implementation and restore this test"); } diff --git a/crates/iroha/tests/integration/extra_functional/multiple_blocks_created.rs b/crates/iroha/tests/integration/extra_functional/multiple_blocks_created.rs index b5335e1af58..43ff51ec13a 100644 --- a/crates/iroha/tests/integration/extra_functional/multiple_blocks_created.rs +++ b/crates/iroha/tests/integration/extra_functional/multiple_blocks_created.rs @@ -4,11 +4,11 @@ use eyre::Result; use futures_util::StreamExt; use iroha::{ client::{self}, - data_model::prelude::*, -}; -use iroha_data_model::{ - events::pipeline::{BlockEventFilter, TransactionEventFilter}, - parameter::BlockParameter, + data_model::{ + events::pipeline::{BlockEventFilter, TransactionEventFilter}, + parameter::BlockParameter, + prelude::*, + }, }; use iroha_test_network::*; use iroha_test_samples::gen_account_in; diff --git a/crates/iroha/tests/integration/set_parameter.rs b/crates/iroha/tests/integration/set_parameter.rs index 00dd8517677..af366ffc93b 100644 --- a/crates/iroha/tests/integration/set_parameter.rs +++ b/crates/iroha/tests/integration/set_parameter.rs @@ -2,11 +2,10 @@ use eyre::Result; use iroha::{ client, data_model::{ - parameter::{Parameter, Parameters}, + parameter::{BlockParameter, Parameter, Parameters}, prelude::*, }, }; -use iroha_data_model::parameter::BlockParameter; use iroha_test_network::*; use nonzero_ext::nonzero; diff --git a/crates/iroha/tests/integration/transfer_domain.rs b/crates/iroha/tests/integration/transfer_domain.rs index 414346bacd2..0c9250e0512 100644 --- a/crates/iroha/tests/integration/transfer_domain.rs +++ b/crates/iroha/tests/integration/transfer_domain.rs @@ -11,7 +11,6 @@ use iroha_executor_data_model::permission::{ domain::CanUnregisterDomain, trigger::CanUnregisterTrigger, }; -use iroha_primitives::json::Json; use iroha_test_network::*; use iroha_test_samples::{gen_account_in, ALICE_ID, BOB_ID, SAMPLE_GENESIS_ACCOUNT_ID}; diff --git a/crates/iroha/tests/integration/triggers/mod.rs b/crates/iroha/tests/integration/triggers/mod.rs index 74c374352cc..329e0f06ecb 100644 --- a/crates/iroha/tests/integration/triggers/mod.rs +++ b/crates/iroha/tests/integration/triggers/mod.rs @@ -1,8 +1,11 @@ use assert_matches::assert_matches; -use iroha::{client, client::Client}; -use iroha_data_model::{ - asset::{AssetId, AssetValue}, - prelude::{Numeric, QueryBuilderExt}, +use iroha::{ + client, + client::Client, + data_model::{ + asset::{AssetId, AssetValue}, + prelude::{Numeric, QueryBuilderExt}, + }, }; mod by_call_trigger; diff --git a/crates/iroha/tests/integration/triggers/time_trigger.rs b/crates/iroha/tests/integration/triggers/time_trigger.rs index be1e9fc49cc..c565610f702 100644 --- a/crates/iroha/tests/integration/triggers/time_trigger.rs +++ b/crates/iroha/tests/integration/triggers/time_trigger.rs @@ -25,6 +25,8 @@ fn curr_time() -> Duration { #[test] fn mint_asset_after_3_sec() -> Result<()> { + const GAP: Duration = Duration::from_secs(3); + let (network, _rt) = NetworkBuilder::new() .with_default_pipeline_time() .start_blocking()?; @@ -43,7 +45,6 @@ fn mint_asset_after_3_sec() -> Result<()> { })?; let start_time = curr_time(); - const GAP: Duration = Duration::from_secs(3); assert!( GAP < network.pipeline_time(), "Schedule should be in the future but within block estimation" diff --git a/crates/iroha_core/benches/blocks/apply_blocks.rs b/crates/iroha_core/benches/blocks/apply_blocks.rs index fbbec3afbad..b82b5ab4fff 100644 --- a/crates/iroha_core/benches/blocks/apply_blocks.rs +++ b/crates/iroha_core/benches/blocks/apply_blocks.rs @@ -48,9 +48,8 @@ impl StateApplyBlocks { instructions .into_iter() .map(|instructions| { - let mut state_block = state.block(); - let block = create_block( - &mut state_block, + let (block, mut state_block) = create_block( + &state, instructions, alice_id.clone(), alice_keypair.private_key(), @@ -88,10 +87,10 @@ impl StateApplyBlocks { }: &Self, ) -> Result<()> { for (block, i) in blocks.iter().zip(1..) { - let mut state_block = state.block(); + let mut state_block = state.block(block.as_ref().header()); let _events = state_block.apply(block, topology.as_ref().to_owned())?; - assert_eq!(state_block.height(), i); state_block.commit(); + assert_eq!(state.view().height(), i); } Ok(()) diff --git a/crates/iroha_core/benches/blocks/common.rs b/crates/iroha_core/benches/blocks/common.rs index fa9aafa6fca..388cde51f34 100644 --- a/crates/iroha_core/benches/blocks/common.rs +++ b/crates/iroha_core/benches/blocks/common.rs @@ -23,46 +23,50 @@ use iroha_executor_data_model::permission::{ }; /// Create block -pub fn create_block( - state: &mut StateBlock<'_>, +pub fn create_block<'a>( + state: &'a State, instructions: Vec, account_id: AccountId, account_private_key: &PrivateKey, topology: &Topology, peer_private_key: &PrivateKey, -) -> CommittedBlock { +) -> (CommittedBlock, StateBlock<'a>) { let chain_id = ChainId::from("00000000-0000-0000-0000-000000000000"); let transaction = TransactionBuilder::new(chain_id.clone(), account_id) .with_instructions(instructions) .sign(account_private_key); let (max_clock_drift, tx_limits) = { - let params = state.world.parameters(); + let state_view = state.view(); + let params = state_view.world.parameters(); (params.sumeragi().max_clock_drift(), params.transaction) }; - let block = BlockBuilder::new(vec![AcceptedTransaction::accept( + let unverified_block = BlockBuilder::new(vec![AcceptedTransaction::accept( transaction, &chain_id, max_clock_drift, tx_limits, ) .unwrap()]) - .chain(0, state) + .chain(0, state.view().latest_block().as_deref()) .sign(peer_private_key) - .unpack(|_| {}) - .categorize(state) - .unpack(|_| {}) - .commit(topology) - .unpack(|_| {}) - .unwrap(); + .unpack(|_| {}); + + let mut state_block = state.block(unverified_block.header()); + let block = unverified_block + .categorize(&mut state_block) + .unpack(|_| {}) + .commit(topology) + .unpack(|_| {}) + .unwrap(); // Verify that transactions are valid for tx in block.as_ref().transactions() { assert_eq!(tx.error, None); } - block + (block, state_block) } pub fn populate_state( @@ -201,7 +205,27 @@ pub fn build_state(rt: &tokio::runtime::Handle, account_id: &AccountId) -> State ); { - let mut state_block = state.block(); + let private_key = KeyPair::random().into_parts().1; + let chain_id = ChainId::from("00000000-0000-0000-0000-000000000000"); + let transaction = TransactionBuilder::new(chain_id.clone(), account_id.clone()) + .with_instructions(Vec::::new()) + .sign(&private_key); + let (max_clock_drift, tx_limits) = { + let state_view = state.view(); + let params = state_view.world.parameters(); + (params.sumeragi().max_clock_drift(), params.transaction) + }; + let unverified_block = BlockBuilder::new(vec![AcceptedTransaction::accept( + transaction, + &chain_id, + max_clock_drift, + tx_limits, + ) + .unwrap()]) + .chain(0, state.view().latest_block().as_deref()) + .sign(&private_key) + .unpack(|_| {}); + let mut state_block = state.block(unverified_block.header()); state_block.world.parameters.transaction = TransactionParameters::new(NonZeroU64::MAX, NonZeroU64::MAX); diff --git a/crates/iroha_core/benches/blocks/common/mod.rs b/crates/iroha_core/benches/blocks/common/mod.rs new file mode 100644 index 00000000000..82f2c266bc8 --- /dev/null +++ b/crates/iroha_core/benches/blocks/common/mod.rs @@ -0,0 +1,273 @@ +#[allow(clippy::module_inception)] +#[path = "../../common/mod.rs"] +mod common; + +use std::num::NonZeroU64; + +pub use common::*; +use iroha_core::{ + block::{BlockBuilder, CommittedBlock}, + prelude::*, + query::store::LiveQueryStore, + smartcontracts::{Execute, Registrable as _}, + state::{State, StateBlock, World}, + sumeragi::network_topology::Topology, +}; +use iroha_data_model::{ + account::Account, + asset::{AssetDefinition, AssetDefinitionId}, + domain::Domain, + isi::InstructionBox, + parameter::TransactionParameters, + prelude::*, + ChainId, +}; +use iroha_executor_data_model::permission::{ + account::CanUnregisterAccount, asset_definition::CanUnregisterAssetDefinition, + domain::CanUnregisterDomain, +}; + +/// Create block +pub fn create_block( + state: &mut StateBlock<'_>, + instructions: Vec, + account_id: AccountId, + account_private_key: &PrivateKey, + topology: &Topology, + peer_private_key: &PrivateKey, +) -> CommittedBlock { + let chain_id = ChainId::from("00000000-0000-0000-0000-000000000000"); + + let transaction = TransactionBuilder::new(chain_id.clone(), account_id) + .with_instructions(instructions) + .sign(account_private_key); + let (max_clock_drift, tx_limits) = { + let params = state.world.parameters(); + (params.sumeragi().max_clock_drift(), params.transaction) + }; + + let block = BlockBuilder::new(vec![AcceptedTransaction::accept( + transaction, + &chain_id, + max_clock_drift, + tx_limits, + ) + .unwrap()]) + .chain(0, state) + .sign(peer_private_key) + .unpack(|_| {}) + .categorize(state) + .unpack(|_| {}) + .commit(topology) + .unpack(|_| {}) + .unwrap(); + + // Verify that transactions are valid + for tx in block.as_ref().transactions() { + assert_eq!(tx.error, None); + } + + block +} + +pub fn populate_state( + domains: &[DomainId], + accounts: &[AccountId], + asset_definitions: &[AssetDefinitionId], + owner_id: &AccountId, +) -> Vec { + let mut instructions: Vec = Vec::new(); + + for domain_id in domains { + let domain = Domain::new(domain_id.clone()); + instructions.push(Register::domain(domain).into()); + let can_unregister_domain = Grant::account_permission( + CanUnregisterDomain { + domain: domain_id.clone(), + }, + owner_id.clone(), + ); + instructions.push(can_unregister_domain.into()); + } + + for account_id in accounts { + let account = Account::new(account_id.clone()); + instructions.push(Register::account(account).into()); + let can_unregister_account = Grant::account_permission( + CanUnregisterAccount { + account: account_id.clone(), + }, + owner_id.clone(), + ); + instructions.push(can_unregister_account.into()); + } + + for asset_definition_id in asset_definitions { + let asset_definition = AssetDefinition::numeric(asset_definition_id.clone()); + instructions.push(Register::asset_definition(asset_definition).into()); + let can_unregister_asset_definition = Grant::account_permission( + CanUnregisterAssetDefinition { + asset_definition: asset_definition_id.clone(), + }, + owner_id.clone(), + ); + instructions.push(can_unregister_asset_definition.into()); + } + + instructions +} + +pub fn delete_every_nth( + domains: &[DomainId], + accounts: &[AccountId], + asset_definitions: &[AssetDefinitionId], + nth: usize, +) -> Vec { + let mut instructions: Vec = Vec::new(); + for (i, domain_id) in domains.iter().enumerate() { + if i % nth == 0 { + instructions.push(Unregister::domain(domain_id.clone()).into()); + } else { + for (j, account_id) in accounts + .iter() + .filter(|account_id| account_id.domain() == domain_id) + .enumerate() + { + if j % nth == 0 { + instructions.push(Unregister::account(account_id.clone()).into()); + } + } + for (k, asset_definition_id) in asset_definitions + .iter() + .filter(|asset_definition_id| asset_definition_id.domain() == domain_id) + .enumerate() + { + if k % nth == 0 { + instructions + .push(Unregister::asset_definition(asset_definition_id.clone()).into()); + } + } + } + } + instructions +} + +pub fn restore_every_nth( + domains: &[DomainId], + accounts: &[AccountId], + asset_definitions: &[AssetDefinitionId], + nth: usize, +) -> Vec { + let mut instructions: Vec = Vec::new(); + for (i, domain_id) in domains.iter().enumerate() { + if i % nth == 0 { + let domain = Domain::new(domain_id.clone()); + instructions.push(Register::domain(domain).into()); + } + for (j, account_id) in accounts + .iter() + .filter(|account_id| account_id.domain() == domain_id) + .enumerate() + { + if j % nth == 0 || i % nth == 0 { + let account = Account::new(account_id.clone()); + instructions.push(Register::account(account).into()); + } + } + for (k, asset_definition_id) in asset_definitions + .iter() + .filter(|asset_definition_id| asset_definition_id.domain() == domain_id) + .enumerate() + { + if k % nth == 0 || i % nth == 0 { + let asset_definition = AssetDefinition::numeric(asset_definition_id.clone()); + instructions.push(Register::asset_definition(asset_definition).into()); + } + } + } + instructions +} + +pub fn build_state(rt: &tokio::runtime::Handle, account_id: &AccountId) -> State { + let kura = iroha_core::kura::Kura::blank_kura_for_testing(); + let query_handle = { + let _guard = rt.enter(); + LiveQueryStore::start_test() + }; + let domain = Domain::new(account_id.domain().clone()).build(account_id); + let state = State::new( + World::with( + [domain], + [Account::new(account_id.clone()).build(account_id)], + [], + ), + kura, + query_handle, + ); + + { + let mut state_block = state.block(); + + state_block.world.parameters.transaction = + TransactionParameters::new(NonZeroU64::MAX, NonZeroU64::MAX); + state_block.world.parameters.executor.fuel = NonZeroU64::MAX; + state_block.world.parameters.executor.memory = NonZeroU64::MAX; + + let mut state_transaction = state_block.transaction(); + let path_to_executor = std::path::PathBuf::from(env!("CARGO_MANIFEST_DIR")) + .join("../../defaults/executor.wasm"); + let wasm = std::fs::read(&path_to_executor) + .unwrap_or_else(|_| panic!("Failed to read file: {}", path_to_executor.display())); + let executor = Executor::new(WasmSmartContract::from_compiled(wasm)); + Upgrade::new(executor) + .execute(account_id, &mut state_transaction) + .expect("Failed to load executor"); + + state_transaction.apply(); + state_block.commit(); + } + + state +} + +fn construct_domain_id(i: usize) -> DomainId { + format!("non_inlinable_domain_name_{i}").parse().unwrap() +} + +fn generate_account_id(domain_id: DomainId) -> AccountId { + AccountId::new(domain_id, KeyPair::random().into_parts().0) +} + +fn construct_asset_definition_id(i: usize, domain_id: DomainId) -> AssetDefinitionId { + AssetDefinitionId::new( + domain_id, + format!("non_inlinable_asset_definition_name_{i}") + .parse() + .unwrap(), + ) +} + +pub fn generate_ids( + domains: usize, + accounts_per_domain: usize, + assets_per_domain: usize, +) -> (Vec, Vec, Vec) { + let mut domain_ids = Vec::new(); + let mut account_ids = Vec::new(); + let mut asset_definition_ids = Vec::new(); + + for i in 0..domains { + let domain_id = construct_domain_id(i); + domain_ids.push(domain_id.clone()); + for _ in 0..accounts_per_domain { + let account_id = generate_account_id(domain_id.clone()); + account_ids.push(account_id) + } + for k in 0..assets_per_domain { + let asset_definition_id = construct_asset_definition_id(k, domain_id.clone()); + asset_definition_ids.push(asset_definition_id); + } + } + + (domain_ids, account_ids, asset_definition_ids) +} diff --git a/crates/iroha_core/benches/blocks/validate_blocks.rs b/crates/iroha_core/benches/blocks/validate_blocks.rs index 4782876c609..46e0cf09358 100644 --- a/crates/iroha_core/benches/blocks/validate_blocks.rs +++ b/crates/iroha_core/benches/blocks/validate_blocks.rs @@ -74,9 +74,8 @@ impl StateValidateBlocks { }: Self, ) { for (instructions, i) in instructions.into_iter().zip(1..) { - let mut state_block = state.block(); - let block = create_block( - &mut state_block, + let (block, mut state_block) = create_block( + &state, instructions, account_id.clone(), &account_private_key, diff --git a/crates/iroha_core/benches/common/mod.rs b/crates/iroha_core/benches/common/mod.rs new file mode 100644 index 00000000000..9d7e7bd23b1 --- /dev/null +++ b/crates/iroha_core/benches/common/mod.rs @@ -0,0 +1,13 @@ +use iroha_crypto::KeyPair; +use iroha_data_model::account::AccountId; + +/// Create new account from a random keypair in the given domain +pub fn gen_account_in(domain: impl core::fmt::Display) -> (AccountId, KeyPair) { + let key_pair = KeyPair::random(); + + let account_id = format!("{}@{}", key_pair.public_key(), domain) + .parse() + .expect("domain name should be valid"); + + (account_id, key_pair) +} diff --git a/crates/iroha_core/benches/kura.rs b/crates/iroha_core/benches/kura.rs index 420d65cdbf6..281ff1e1c62 100644 --- a/crates/iroha_core/benches/kura.rs +++ b/crates/iroha_core/benches/kura.rs @@ -53,13 +53,15 @@ async fn measure_block_size_for_n_executors(n_executors: u32) { let peer_id = PeerId::new("127.0.0.1:8080".parse().unwrap(), peer_public_key); let topology = Topology::new(vec![peer_id]); let mut block = { - let mut state_block = state.block(); - BlockBuilder::new(vec![tx]) - .chain(0, &mut state_block) + let unverified_block = BlockBuilder::new(vec![tx]) + .chain(0, state.view().latest_block().as_deref()) .sign(&peer_private_key) - .unpack(|_| {}) - .categorize(&mut state_block) - .unpack(|_| {}) + .unpack(|_| {}); + + let mut state_block = state.block(unverified_block.header()); + let block = unverified_block.categorize(&mut state_block).unpack(|_| {}); + state_block.commit(); + block }; let key_pair = KeyPair::random(); diff --git a/crates/iroha_core/benches/validation.rs b/crates/iroha_core/benches/validation.rs index 6c25ba3ef7e..eaad96d894b 100644 --- a/crates/iroha_core/benches/validation.rs +++ b/crates/iroha_core/benches/validation.rs @@ -36,10 +36,10 @@ fn build_test_transaction(chain_id: ChainId) -> TransactionBuilder { fn build_test_and_transient_state() -> State { let kura = iroha_core::kura::Kura::blank_kura_for_testing(); let query_handle = LiveQueryStore::start_test(); + let (account_id, key_pair) = gen_account_in(&*STARTER_DOMAIN); let state = State::new( { - let (account_id, _account_keypair) = gen_account_in(&*STARTER_DOMAIN); let domain = Domain::new(STARTER_DOMAIN.clone()).build(&account_id); let account = Account::new(account_id.clone()).build(&account_id); World::with([domain], [account], []) @@ -49,7 +49,26 @@ fn build_test_and_transient_state() -> State { ); { - let mut state_block = state.block(); + let chain_id = ChainId::from("00000000-0000-0000-0000-000000000000"); + let transaction = TransactionBuilder::new(chain_id.clone(), account_id.clone()) + .with_instructions(Vec::::new()) + .sign(key_pair.private_key()); + let (max_clock_drift, tx_limits) = { + let state_view = state.view(); + let params = state_view.world.parameters(); + (params.sumeragi().max_clock_drift(), params.transaction) + }; + let unverified_block = BlockBuilder::new(vec![AcceptedTransaction::accept( + transaction, + &chain_id, + max_clock_drift, + tx_limits, + ) + .unwrap()]) + .chain(0, state.view().latest_block().as_deref()) + .sign(key_pair.private_key()) + .unpack(|_| {}); + let mut state_block = state.block(unverified_block.header()); let mut state_transaction = state_block.transaction(); let path_to_executor = std::path::PathBuf::from(env!("CARGO_MANIFEST_DIR")) .join("../../defaults/executor.wasm"); @@ -117,12 +136,26 @@ fn sign_transaction(criterion: &mut Criterion) { fn validate_transaction(criterion: &mut Criterion) { let chain_id = ChainId::from("00000000-0000-0000-0000-000000000000"); let state = build_test_and_transient_state(); + + let (account_id, key_pair) = gen_account_in(&*STARTER_DOMAIN); + let transaction = TransactionBuilder::new(chain_id.clone(), account_id.clone()) + .with_instructions(Vec::::new()) + .sign(key_pair.private_key()); let (max_clock_drift, tx_limits) = { - let state_view = state.world.view(); - let params = state_view.parameters(); + let state_view = state.view(); + let params = state_view.world.parameters(); (params.sumeragi().max_clock_drift(), params.transaction) }; - + let unverified_block = BlockBuilder::new(vec![AcceptedTransaction::accept( + transaction, + &chain_id, + max_clock_drift, + tx_limits, + ) + .unwrap()]) + .chain(0, state.view().latest_block().as_deref()) + .sign(key_pair.private_key()) + .unpack(|_| {}); let transaction = AcceptedTransaction::accept( build_test_transaction(chain_id.clone()).sign(STARTER_KEYPAIR.private_key()), &chain_id, @@ -132,15 +165,14 @@ fn validate_transaction(criterion: &mut Criterion) { .expect("Failed to accept transaction."); let mut success_count = 0; let mut failure_count = 0; - let _ = criterion.bench_function("validate", move |b| { - b.iter(|| { - let mut state_block = state.block(); - match state_block.validate(transaction.clone()) { - Ok(_) => success_count += 1, - Err(_) => failure_count += 1, - } + let mut state_block = state.block(unverified_block.header()); + let _ = criterion.bench_function("validate", |b| { + b.iter(|| match state_block.validate(transaction.clone()) { + Ok(_) => success_count += 1, + Err(_) => failure_count += 1, }); }); + state_block.commit(); println!("Success count: {success_count}, Failure count: {failure_count}"); } @@ -166,8 +198,8 @@ fn sign_blocks(criterion: &mut Criterion) { let mut count = 0; - let mut state_block = state.block(); - let block = BlockBuilder::new(vec![transaction]).chain(0, &mut state_block); + let block = + BlockBuilder::new(vec![transaction]).chain(0, state.view().latest_block().as_deref()); let _ = criterion.bench_function("sign_block", |b| { b.iter_batched( diff --git a/crates/iroha_core/src/block.rs b/crates/iroha_core/src/block.rs index e2f473b8d51..2ab0d4e8132 100644 --- a/crates/iroha_core/src/block.rs +++ b/crates/iroha_core/src/block.rs @@ -118,7 +118,6 @@ mod pending { use nonzero_ext::nonzero; use super::*; - use crate::state::StateBlock; /// First stage in the life-cycle of a [`Block`]. /// In the beginning the block is assumed to be verified and to contain only accepted transactions. @@ -211,14 +210,10 @@ mod pending { pub fn chain( self, view_change_index: usize, - state: &mut StateBlock<'_>, + latest_block: Option<&SignedBlock>, ) -> BlockBuilder { BlockBuilder(Chained { - header: Self::make_header( - state.latest_block().as_deref(), - view_change_index, - &self.0.transactions, - ), + header: Self::make_header(latest_block, view_change_index, &self.0.transactions), transactions: self.0.transactions, }) } @@ -263,9 +258,9 @@ mod new { /// Transactions in this block are not categorized. #[derive(Debug, Clone)] pub struct NewBlock { - pub(crate) signature: BlockSignature, - pub(crate) header: BlockHeader, - pub(crate) transactions: Vec, + pub(super) signature: BlockSignature, + pub(super) header: BlockHeader, + pub(super) transactions: Vec, } impl NewBlock { @@ -296,6 +291,32 @@ mod new { block.set_transaction_errors(errors); WithEvents::new(ValidBlock(block)) } + + /// Block signature + pub fn signature(&self) -> &BlockSignature { + &self.signature + } + + /// Block header + pub fn header(&self) -> BlockHeader { + self.header + } + + /// Block transactions + pub fn transactions(&self) -> &[AcceptedTransaction] { + &self.transactions + } + + #[cfg(test)] + pub(crate) fn update_header(self, header: BlockHeader, private_key: &PrivateKey) -> Self { + let signature = BlockSignature(0, iroha_crypto::SignatureOf::new(private_key, &header)); + + Self { + signature, + header, + transactions: self.transactions, + } + } } impl From for SignedBlock { @@ -470,9 +491,9 @@ mod valid { // Release block writer before creating new one let _ = voting_block.take(); let mut state_block = if soft_fork { - state.block_and_revert() + state.block_and_revert(block.header()) } else { - state.block() + state.block(block.header()) }; if let Err(error) = Self::categorize( @@ -1062,7 +1083,7 @@ mod event { impl EventProducer for NewBlock { fn produce_events(&self) -> impl Iterator { let block_event = BlockEvent { - header: self.header.clone(), + header: self.header, status: BlockStatus::Created, }; @@ -1088,7 +1109,7 @@ mod event { }); let block_event = core::iter::once(BlockEvent { - header: self.as_ref().header().clone(), + header: self.as_ref().header(), status: BlockStatus::Approved, }); @@ -1101,7 +1122,7 @@ mod event { impl EventProducer for CommittedBlock { fn produce_events(&self) -> impl Iterator { let block_event = core::iter::once(BlockEvent { - header: self.as_ref().header().clone(), + header: self.as_ref().header(), status: BlockStatus::Committed, }); @@ -1172,8 +1193,6 @@ mod tests { let params = state_view.parameters(); (params.sumeragi().max_clock_drift(), params.transaction) }; - let mut state_block = state.block(); - // Creating an instruction let asset_definition_id = "xor#wonderland".parse().expect("Valid"); let create_asset_definition = @@ -1188,13 +1207,15 @@ mod tests { // Creating a block of two identical transactions and validating it let transactions = vec![tx.clone(), tx]; - let valid_block = BlockBuilder::new(transactions) - .chain(0, &mut state_block) + let unverified_block = BlockBuilder::new(transactions) + .chain(0, state.view().latest_block().as_deref()) .sign(alice_keypair.private_key()) - .unpack(|_| {}) - .categorize(&mut state_block) .unpack(|_| {}); + let mut state_block = state.block(unverified_block.header); + let valid_block = unverified_block.categorize(&mut state_block).unpack(|_| {}); + state_block.commit(); + // The first transaction should be confirmed assert!(valid_block .as_ref() @@ -1232,8 +1253,6 @@ mod tests { let params = state_view.parameters(); (params.sumeragi().max_clock_drift(), params.transaction) }; - let mut state_block = state.block(); - // Creating an instruction let asset_definition_id = "xor#wonderland" .parse::() @@ -1270,12 +1289,13 @@ mod tests { // Creating a block of two identical transactions and validating it let transactions = vec![tx0, tx, tx2]; - let valid_block = BlockBuilder::new(transactions) - .chain(0, &mut state_block) + let unverified_block = BlockBuilder::new(transactions) + .chain(0, state.view().latest_block().as_deref()) .sign(alice_keypair.private_key()) - .unpack(|_| {}) - .categorize(&mut state_block) .unpack(|_| {}); + let mut state_block = state.block(unverified_block.header); + let valid_block = unverified_block.categorize(&mut state_block).unpack(|_| {}); + state_block.commit(); // The first transaction should fail assert!(valid_block @@ -1314,8 +1334,6 @@ mod tests { let params = state_view.parameters(); (params.sumeragi().max_clock_drift(), params.transaction) }; - let mut state_block = state.block(); - let domain_id = "domain".parse().expect("Valid"); let create_domain = Register::domain(Domain::new(domain_id)); let asset_definition_id = "coin#domain".parse().expect("Valid"); @@ -1336,13 +1354,15 @@ mod tests { // Creating a block of where first transaction must fail and second one fully executed let transactions = vec![tx_fail, tx_accept]; - let valid_block = BlockBuilder::new(transactions) - .chain(0, &mut state_block) + let unverified_block = BlockBuilder::new(transactions) + .chain(0, state.view().latest_block().as_deref()) .sign(alice_keypair.private_key()) - .unpack(|_| {}) - .categorize(&mut state_block) .unpack(|_| {}); + let mut state_block = state.block(unverified_block.header); + let valid_block = unverified_block.categorize(&mut state_block).unpack(|_| {}); + state_block.commit(); + // The first transaction should be rejected assert!( valid_block @@ -1391,7 +1411,6 @@ mod tests { let kura = Kura::blank_kura_for_testing(); let query_handle = LiveQueryStore::start_test(); let state = State::new(world, kura, query_handle); - let mut state_block = state.block(); // Creating an instruction let isi = Log::new( @@ -1412,16 +1431,19 @@ mod tests { let (peer_public_key, _) = KeyPair::random().into_parts(); let peer_id = PeerId::new("127.0.0.1:8080".parse().unwrap(), peer_public_key); let topology = Topology::new(vec![peer_id]); - let valid_block = BlockBuilder::new(transactions) - .chain(0, &mut state_block) + let unverified_block = BlockBuilder::new(transactions) + .chain(0, state.view().latest_block().as_deref()) .sign(genesis_correct_key.private_key()) - .unpack(|_| {}) - .categorize(&mut state_block) .unpack(|_| {}); + let mut state_block = state.block(unverified_block.header); + let valid_block = unverified_block.categorize(&mut state_block).unpack(|_| {}); + state_block.commit(); + // Validate genesis block // Use correct genesis key and check if transaction is rejected let block: SignedBlock = valid_block.into(); + let mut state_block = state.block(block.header()); let (_, error) = ValidBlock::validate( block, &topology, @@ -1431,6 +1453,7 @@ mod tests { ) .unpack(|_| {}) .unwrap_err(); + state_block.commit(); // The first transaction should be rejected assert_eq!( diff --git a/crates/iroha_core/src/kura.rs b/crates/iroha_core/src/kura.rs index 2a1c03cc0ca..b2251b9c773 100644 --- a/crates/iroha_core/src/kura.rs +++ b/crates/iroha_core/src/kura.rs @@ -167,7 +167,9 @@ impl Kura { Ok(()) => match SignedBlock::decode_all_versioned(&block_data_buffer) { Ok(decoded_block) => { if prev_block_hash != decoded_block.header().prev_block_hash { - error!("Block has wrong previous block hash. Not reading any blocks beyond this height."); + error!(expected=?prev_block_hash, actual=?decoded_block.header().prev_block_hash, + "Block has wrong previous block hash. Not reading any blocks beyond this height." + ); break; } let decoded_block_hash = decoded_block.hash(); @@ -302,7 +304,7 @@ impl Kura { } /// Get a reference to block by height, loading it from disk if needed. - pub fn get_block_by_height(&self, block_height: NonZeroUsize) -> Option> { + pub fn get_block(&self, block_height: NonZeroUsize) -> Option> { let mut data_array_guard = self.block_data.lock(); if data_array_guard.len() < block_height.get() { @@ -833,7 +835,7 @@ mod tests { smartcontracts::Registrable, state::State, sumeragi::network_topology::Topology, - World, + StateReadOnly, World, }; fn indices(value: [(u64, u64); N]) -> [BlockIndex; N] { @@ -1047,15 +1049,15 @@ mod tests { assert_eq!(block_count.0, 3); assert_eq!( - kura.get_block_by_height(nonzero!(1_usize)), + kura.get_block(nonzero!(1_usize)), Some(Arc::new(block_genesis.into())) ); assert_eq!( - kura.get_block_by_height(nonzero!(2_usize)), + kura.get_block(nonzero!(2_usize)), Some(Arc::new(block_soft_fork.into())) ); assert_eq!( - kura.get_block_by_height(nonzero!(3_usize)), + kura.get_block(nonzero!(3_usize)), Some(Arc::new(block_next.into())) ); } @@ -1121,9 +1123,9 @@ mod tests { ); { - let mut state_block = state.block(); + let mut state_block = state.block(genesis.0.header()); let block_genesis = ValidBlock::validate( - genesis.0, + genesis.0.clone(), &topology, &chain_id, &genesis_id, @@ -1138,7 +1140,7 @@ mod tests { state_block.apply_without_execution(&block_genesis, topology.as_ref().to_owned()); state_block.commit(); blocks.push(block_genesis.clone()); - kura.store_block(block_genesis); + kura.store_block(block_genesis.clone()); } let (max_clock_drift, tx_limits) = { @@ -1158,11 +1160,13 @@ mod tests { crate::AcceptedTransaction::accept(tx2, &chain_id, max_clock_drift, tx_limits).unwrap(); { - let mut state_block = state.block(); - let block = BlockBuilder::new(vec![tx1.clone()]) - .chain(0, &mut state_block) + let unverified_block = BlockBuilder::new(vec![tx1.clone()]) + .chain(0, state.view().latest_block().as_deref()) .sign(&leader_private_key) - .unpack(|_| {}) + .unpack(|_| {}); + + let mut state_block = state.block(unverified_block.header()); + let block = unverified_block .categorize(&mut state_block) .unpack(|_| {}) .commit(&topology) @@ -1176,11 +1180,13 @@ mod tests { thread::sleep(BLOCK_FLUSH_TIMEOUT); { - let mut state_block = state.block_and_revert(); - let block_soft_fork = BlockBuilder::new(vec![tx1]) - .chain(1, &mut state_block) + let unverified_block_soft_fork = BlockBuilder::new(vec![tx1]) + .chain(1, Some(&genesis.0)) .sign(&leader_private_key) - .unpack(|_| {}) + .unpack(|_| {}); + + let mut state_block = state.block_and_revert(unverified_block_soft_fork.header()); + let block_soft_fork = unverified_block_soft_fork .categorize(&mut state_block) .unpack(|_| {}) .commit(&topology) @@ -1195,11 +1201,13 @@ mod tests { thread::sleep(BLOCK_FLUSH_TIMEOUT); { - let mut state_block: crate::state::StateBlock = state.block(); - let block_next = BlockBuilder::new(vec![tx2]) - .chain(0, &mut state_block) + let unverified_block_next = BlockBuilder::new(vec![tx2]) + .chain(0, state.view().latest_block().as_deref()) .sign(&leader_private_key) - .unpack(|_| {}) + .unpack(|_| {}); + + let mut state_block = state.block(unverified_block_next.header()); + let block_next = unverified_block_next .categorize(&mut state_block) .unpack(|_| {}) .commit(&topology) diff --git a/crates/iroha_core/src/metrics.rs b/crates/iroha_core/src/metrics.rs index b377cd1986a..a2fab052f9d 100644 --- a/crates/iroha_core/src/metrics.rs +++ b/crates/iroha_core/src/metrics.rs @@ -71,7 +71,7 @@ impl MetricsReporter { .checked_add(1) .expect("INTERNAL BUG: Blockchain height exceeds usize::MAX"), ) - .and_then(|index| self.kura.get_block_by_height(index)) else { + .and_then(|index| self.kura.get_block(index)) else { break; }; block_index += 1; diff --git a/crates/iroha_core/src/queue.rs b/crates/iroha_core/src/queue.rs index ac23eb2079d..e5b2b5d46f2 100644 --- a/crates/iroha_core/src/queue.rs +++ b/crates/iroha_core/src/queue.rs @@ -417,6 +417,7 @@ pub mod tests { use super::*; use crate::{ + block::ValidBlock, kura::Kura, query::store::LiveQueryStore, smartcontracts::isi::Registrable as _, @@ -567,7 +568,10 @@ pub mod tests { let state = State::new(world_with_test_domains(), kura, query_handle); let (_time_handle, time_source) = TimeSource::new_mock(Duration::default()); let tx = accepted_tx_by_someone(&time_source); - let mut state_block = state.block(); + let block_header = ValidBlock::new_dummy(&KeyPair::random().into_parts().1) + .as_ref() + .header(); + let mut state_block = state.block(block_header); state_block .transactions .insert(tx.as_ref().hash(), nonzero!(1_usize)); @@ -594,7 +598,10 @@ pub mod tests { let queue = Queue::test(config_factory(), &time_source); let queue = Arc::new(queue); queue.push(tx.clone(), state.view()).unwrap(); - let mut state_block = state.block(); + let block_header = ValidBlock::new_dummy(&KeyPair::random().into_parts().1) + .as_ref() + .header(); + let mut state_block = state.block(block_header); state_block .transactions .insert(tx.as_ref().hash(), nonzero!(1_usize)); @@ -762,13 +769,16 @@ pub mod tests { // Spawn a thread where we get_transactions_for_block and add them to state let get_txs_handle = { + let block_header = ValidBlock::new_dummy(&KeyPair::random().into_parts().1) + .as_ref() + .header(); let queue = Arc::clone(&queue); thread::spawn(move || { while start_time.elapsed() < run_for { for tx in queue.collect_transactions_for_block(&state.view(), max_txs_in_block) { - let mut state_block = state.block(); + let mut state_block = state.block(block_header); state_block .transactions .insert(tx.as_ref().hash(), nonzero!(1_usize)); @@ -855,7 +865,10 @@ pub mod tests { let transactions = queue.collect_transactions_for_block(&state.view(), nonzero!(10_usize)); assert_eq!(transactions.len(), 2); - let mut state_block = state.block(); + let block_header = ValidBlock::new_dummy(&KeyPair::random().into_parts().1) + .as_ref() + .header(); + let mut state_block = state.block(block_header); for transaction in transactions { // Put transaction hashes into state as if they were in the blockchain state_block diff --git a/crates/iroha_core/src/smartcontracts/isi/block.rs b/crates/iroha_core/src/smartcontracts/isi/block.rs index b41451c68c2..c398c7a7378 100644 --- a/crates/iroha_core/src/smartcontracts/isi/block.rs +++ b/crates/iroha_core/src/smartcontracts/isi/block.rs @@ -38,7 +38,7 @@ impl ValidQuery for FindBlockHeaders { Ok(state_ro .all_blocks(nonzero!(1_usize)) .rev() - .filter(move |block| filter.applies(block.header())) - .map(|block| block.header().clone())) + .filter(move |block| filter.applies(&block.header())) + .map(|block| block.header())) } } diff --git a/crates/iroha_core/src/smartcontracts/isi/mod.rs b/crates/iroha_core/src/smartcontracts/isi/mod.rs index 46140458ba6..570f1d64475 100644 --- a/crates/iroha_core/src/smartcontracts/isi/mod.rs +++ b/crates/iroha_core/src/smartcontracts/isi/mod.rs @@ -235,6 +235,7 @@ mod tests { use super::*; use crate::{ + block::ValidBlock, kura::Kura, query::store::LiveQueryStore, state::{State, World}, @@ -246,7 +247,10 @@ mod tests { let query_handle = LiveQueryStore::start_test(); let state = State::new(world, kura.clone(), query_handle); let asset_definition_id = "rose#wonderland".parse()?; - let mut state_block = state.block(); + let block_header = ValidBlock::new_dummy(&KeyPair::random().into_parts().1) + .as_ref() + .header(); + let mut state_block = state.block(block_header); let mut state_transaction = state_block.transaction(); Register::domain(Domain::new("wonderland".parse()?)) .execute(&SAMPLE_GENESIS_ACCOUNT_ID, &mut state_transaction)?; @@ -263,7 +267,10 @@ mod tests { async fn asset_store() -> Result<()> { let kura = Kura::blank_kura_for_testing(); let state = state_with_test_domains(&kura)?; - let mut state_block = state.block(); + let block_header = ValidBlock::new_dummy(&KeyPair::random().into_parts().1) + .as_ref() + .header(); + let mut state_block = state.block(block_header); let mut state_transaction = state_block.transaction(); let account_id = ALICE_ID.clone(); let asset_definition_id = "rose#wonderland".parse()?; @@ -271,7 +278,9 @@ mod tests { let key = "Bytes".parse::()?; SetKeyValue::asset(asset_id.clone(), key.clone(), vec![1_u32, 2_u32, 3_u32]) .execute(&account_id, &mut state_transaction)?; - let asset = state_transaction.world.asset(&asset_id)?; + state_transaction.apply(); + state_block.commit(); + let asset = state.view().world.asset(&asset_id)?; let AssetValue::Store(store) = &asset.value else { panic!("expected store asset"); }; @@ -284,13 +293,19 @@ mod tests { async fn account_metadata() -> Result<()> { let kura = Kura::blank_kura_for_testing(); let state = state_with_test_domains(&kura)?; - let mut state_block = state.block(); + let block_header = ValidBlock::new_dummy(&KeyPair::random().into_parts().1) + .as_ref() + .header(); + let mut state_block = state.block(block_header); let mut state_transaction = state_block.transaction(); let account_id = ALICE_ID.clone(); let key = "Bytes".parse::()?; SetKeyValue::account(account_id.clone(), key.clone(), vec![1_u32, 2_u32, 3_u32]) .execute(&account_id, &mut state_transaction)?; - let bytes = state_transaction + state_transaction.apply(); + state_block.commit(); + let bytes = state + .view() .world .map_account(&account_id, |account| account.metadata().get(&key).cloned())?; assert_eq!(bytes, Some(vec![1_u32, 2_u32, 3_u32,].into())); @@ -301,7 +316,10 @@ mod tests { async fn asset_definition_metadata() -> Result<()> { let kura = Kura::blank_kura_for_testing(); let state = state_with_test_domains(&kura)?; - let mut state_block = state.block(); + let block_header = ValidBlock::new_dummy(&KeyPair::random().into_parts().1) + .as_ref() + .header(); + let mut state_block = state.block(block_header); let mut state_transaction = state_block.transaction(); let definition_id = "rose#wonderland".parse::()?; let account_id = ALICE_ID.clone(); @@ -312,7 +330,10 @@ mod tests { vec![1_u32, 2_u32, 3_u32], ) .execute(&account_id, &mut state_transaction)?; - let value = state_transaction + state_transaction.apply(); + state_block.commit(); + let value = state + .view() .world .asset_definition(&definition_id)? .metadata() @@ -326,14 +347,20 @@ mod tests { async fn domain_metadata() -> Result<()> { let kura = Kura::blank_kura_for_testing(); let state = state_with_test_domains(&kura)?; - let mut state_block = state.block(); + let block_header = ValidBlock::new_dummy(&KeyPair::random().into_parts().1) + .as_ref() + .header(); + let mut state_block = state.block(block_header); let mut state_transaction = state_block.transaction(); let domain_id = "wonderland".parse::()?; let account_id = ALICE_ID.clone(); let key = "Bytes".parse::()?; SetKeyValue::domain(domain_id.clone(), key.clone(), vec![1_u32, 2_u32, 3_u32]) .execute(&account_id, &mut state_transaction)?; - let bytes = state_transaction + state_transaction.apply(); + state_block.commit(); + let bytes = state + .view() .world .domain(&domain_id)? .metadata() @@ -347,7 +374,10 @@ mod tests { async fn executing_unregistered_trigger_should_return_error() -> Result<()> { let kura = Kura::blank_kura_for_testing(); let state = state_with_test_domains(&kura)?; - let mut state_block = state.block(); + let block_header = ValidBlock::new_dummy(&KeyPair::random().into_parts().1) + .as_ref() + .header(); + let mut state_block = state.block(block_header); let mut state_transaction = state_block.transaction(); let account_id = ALICE_ID.clone(); let trigger_id = "test_trigger_id".parse()?; @@ -359,6 +389,9 @@ mod tests { Error::Find(_) )); + state_transaction.apply(); + state_block.commit(); + Ok(()) } @@ -366,7 +399,10 @@ mod tests { async fn unauthorized_trigger_execution_should_return_error() -> Result<()> { let kura = Kura::blank_kura_for_testing(); let state = state_with_test_domains(&kura)?; - let mut state_block = state.block(); + let block_header = ValidBlock::new_dummy(&KeyPair::random().into_parts().1) + .as_ref() + .header(); + let mut state_block = state.block(block_header); let mut state_transaction = state_block.transaction(); let account_id = ALICE_ID.clone(); let (fake_account_id, _fake_account_keypair) = gen_account_in("wonderland"); @@ -402,6 +438,9 @@ mod tests { Error::InvariantViolation(_) )); + state_transaction.apply(); + state_block.commit(); + Ok(()) } @@ -409,7 +448,10 @@ mod tests { async fn not_allowed_to_register_genesis_domain_or_account() -> Result<()> { let kura = Kura::blank_kura_for_testing(); let state = state_with_test_domains(&kura)?; - let mut state_block = state.block(); + let block_header = ValidBlock::new_dummy(&KeyPair::random().into_parts().1) + .as_ref() + .header(); + let mut state_block = state.block(block_header); let mut state_transaction = state_block.transaction(); let account_id = ALICE_ID.clone(); assert!(matches!( @@ -425,6 +467,9 @@ mod tests { .expect_err("Error expected"), Error::InvariantViolation(_) )); + state_transaction.apply(); + state_block.commit(); + Ok(()) } diff --git a/crates/iroha_core/src/smartcontracts/isi/query.rs b/crates/iroha_core/src/smartcontracts/isi/query.rs index 4fbaa74c1a7..61e5d7c6afb 100644 --- a/crates/iroha_core/src/smartcontracts/isi/query.rs +++ b/crates/iroha_core/src/smartcontracts/isi/query.rs @@ -412,7 +412,6 @@ mod tests { (params.sumeragi().max_clock_drift(), params.transaction) }; - let mut state_block = state.block(); let valid_tx = { let tx = TransactionBuilder::new(chain_id.clone(), ALICE_ID.clone()) .with_instructions::([]) @@ -433,10 +432,12 @@ mod tests { let (peer_public_key, peer_private_key) = KeyPair::random().into_parts(); let peer_id = PeerId::new("127.0.0.1:8080".parse().unwrap(), peer_public_key); let topology = Topology::new(vec![peer_id]); - let first_block = BlockBuilder::new(transactions.clone()) - .chain(0, &mut state_block) + let unverified_first_block = BlockBuilder::new(transactions.clone()) + .chain(0, state.view().latest_block().as_deref()) .sign(&peer_private_key) - .unpack(|_| {}) + .unpack(|_| {}); + let mut state_block = state.block(unverified_first_block.header()); + let first_block = unverified_first_block .categorize(&mut state_block) .unpack(|_| {}) .commit(&topology) @@ -445,12 +446,16 @@ mod tests { let _events = state_block.apply(&first_block, topology.as_ref().to_owned())?; kura.store_block(first_block); + state_block.commit(); for _ in 1u64..blocks { - let block = BlockBuilder::new(transactions.clone()) - .chain(0, &mut state_block) + let unverified_block = BlockBuilder::new(transactions.clone()) + .chain(0, state.view().latest_block().as_deref()) .sign(&peer_private_key) - .unpack(|_| {}) + .unpack(|_| {}); + let mut state_block = state.block(unverified_block.header()); + + let block = unverified_block .categorize(&mut state_block) .unpack(|_| {}) .commit(&topology) @@ -459,8 +464,8 @@ mod tests { let _events = state_block.apply(&block, topology.as_ref().to_owned())?; kura.store_block(block); + state_block.commit(); } - state_block.commit(); } Ok(state) @@ -540,7 +545,7 @@ mod tests { .expect("Query execution should not fail") .next() .expect("Query should return a block header"), - *block.header() + block.header() ); assert!( FindBlockHeaders::new() @@ -599,7 +604,6 @@ mod tests { (params.sumeragi().max_clock_drift(), params.transaction) }; - let mut state_block = state.block(); let tx = TransactionBuilder::new(chain_id.clone(), ALICE_ID.clone()) .with_instructions::([]) .sign(ALICE_KEYPAIR.private_key()); @@ -609,10 +613,12 @@ mod tests { let (peer_public_key, _) = KeyPair::random().into_parts(); let peer_id = PeerId::new("127.0.0.1:8080".parse().unwrap(), peer_public_key); let topology = Topology::new(vec![peer_id]); - let vcb = BlockBuilder::new(vec![va_tx.clone()]) - .chain(0, &mut state_block) + let unverified_block = BlockBuilder::new(vec![va_tx.clone()]) + .chain(0, state.view().latest_block().as_deref()) .sign(ALICE_KEYPAIR.private_key()) - .unpack(|_| {}) + .unpack(|_| {}); + let mut state_block = state.block(unverified_block.header()); + let vcb = unverified_block .categorize(&mut state_block) .unpack(|_| {}) .commit(&topology) diff --git a/crates/iroha_core/src/smartcontracts/isi/triggers/mod.rs b/crates/iroha_core/src/smartcontracts/isi/triggers/mod.rs index cf18bf0e0c8..411076e5a56 100644 --- a/crates/iroha_core/src/smartcontracts/isi/triggers/mod.rs +++ b/crates/iroha_core/src/smartcontracts/isi/triggers/mod.rs @@ -44,7 +44,6 @@ pub mod isi { .map(|block| block.header().creation_time()); let engine = state_transaction.engine.clone(); // Cloning engine is cheap - let genesis_creation_time_ms = state_transaction.world().genesis_creation_time_ms(); let triggers = &mut state_transaction.world.triggers; let trigger_id = new_trigger.id().clone(); @@ -63,25 +62,14 @@ pub mod isi { ), EventFilterBox::Time(time_filter) => { if let ExecutionTime::Schedule(schedule) = time_filter.0 { - match latest_block_time { + let latest_block_time = latest_block_time.unwrap_or_else(|| { // Genesis block - None => { - let genesis_creation_time_ms = genesis_creation_time_ms - .expect("INTERNAL BUG: genesis creation time not set"); - - if schedule.start_ms < genesis_creation_time_ms { - return Err(Error::InvalidParameter( - InvalidParameterError::TimeTriggerInThePast, - )); - } - } - Some(latest_block_time) => { - if schedule.start() < latest_block_time { - return Err(Error::InvalidParameter( - InvalidParameterError::TimeTriggerInThePast, - )); - } - } + state_transaction.curr_block.creation_time() + }); + if schedule.start() < latest_block_time { + return Err(Error::InvalidParameter( + InvalidParameterError::TimeTriggerInThePast, + )); } } triggers.add_time_trigger( diff --git a/crates/iroha_core/src/smartcontracts/wasm.rs b/crates/iroha_core/src/smartcontracts/wasm.rs index e046928829e..450621f5fdc 100644 --- a/crates/iroha_core/src/smartcontracts/wasm.rs +++ b/crates/iroha_core/src/smartcontracts/wasm.rs @@ -461,12 +461,15 @@ pub mod state { pub mod executor { //! States related to *Executor* execution. + use iroha_data_model::block::BlockHeader; + use super::*; /// Struct to encapsulate common state kinds for `validate_*` entrypoints #[derive(Constructor)] pub struct Validate { pub(in super::super::super::super) to_validate: T, + pub(in super::super::super::super) curr_block: BlockHeader, } /// State kind for executing `execute_transaction()` entrypoint of executor @@ -802,7 +805,7 @@ where let payload = payloads::Validate { context: payloads::ExecutorContext { authority: state.authority.clone(), - block_height: state.state.state().height() as u64, + curr_block: state.specific_state.curr_block, }, target: state.specific_state.to_validate.clone(), }; @@ -957,6 +960,7 @@ impl<'wrld, 'block: 'wrld, 'state: 'block> Runtime payloads::SmartContractContext { payloads::SmartContractContext { authority: state.authority.clone(), + curr_block: state.state.0.curr_block, } } } @@ -1033,6 +1037,7 @@ impl<'wrld, 'block: 'wrld, 'state: 'block> Runtime Runtime Result { let span = wasm_log_span!("Running `execute_transaction()`"); + let curr_block = state_transaction.curr_block; let state = state::executor::ExecuteTransaction::new( authority.clone(), self.config, span, state::chain_state::WithMut(state_transaction), - state::specific::executor::ExecuteTransaction::new(transaction), + state::specific::executor::ExecuteTransaction::new(transaction, curr_block), ); self.execute_executor_execute_internal(module, state, import::EXECUTOR_EXECUTE_TRANSACTION) @@ -1178,13 +1184,14 @@ impl<'wrld, 'block, 'state> Runtime Result { let span = wasm_log_span!("Running `execute_instruction()`"); + let curr_block = state_transaction.curr_block; let state = state::executor::ExecuteInstruction::new( authority.clone(), self.config, span, state::chain_state::WithMut(state_transaction), - state::specific::executor::ExecuteInstruction::new(instruction), + state::specific::executor::ExecuteInstruction::new(instruction, curr_block), ); self.execute_executor_execute_internal(module, state, import::EXECUTOR_EXECUTE_INSTRUCTION) @@ -1220,12 +1227,18 @@ impl<'wrld, S: StateReadOnly> Runtime> ) -> Result { let span = wasm_log_span!("Running `validate_query()`"); + let Some(latest_block) = state_ro.latest_block() else { + return Ok(Err(ValidationFail::NotPermitted( + "Genesis not committed".to_owned(), + ))); + }; + let state = state::executor::ValidateQuery::new( authority.clone(), self.config, span, state::chain_state::WithConst(state_ro), - state::specific::executor::ValidateQuery::new(query), + state::specific::executor::ValidateQuery::new(query, latest_block.as_ref().header()), ); self.execute_executor_execute_internal(module, state, import::EXECUTOR_VALIDATE_QUERY) @@ -1304,7 +1317,7 @@ impl<'wrld, 'block, 'state> Runtime WasmUsize { let payload = payloads::ExecutorContext { authority: store.data().authority.clone(), - block_height: store.data().state.0.height() as u64, + curr_block: store.data().state.0.curr_block, }; Self::encode_payload(instance, store, payload) } @@ -1559,6 +1572,7 @@ impl GetExport for (&wasmtime::Instance, C) { #[cfg(test)] mod tests { + use iroha_crypto::KeyPair; use iroha_test_samples::gen_account_in; use nonzero_ext::nonzero; use parity_scale_codec::Encode; @@ -1566,8 +1580,8 @@ mod tests { use super::*; use crate::{ - kura::Kura, query::store::LiveQueryStore, smartcontracts::isi::Registrable as _, - state::State, World, + block::ValidBlock, kura::Kura, query::store::LiveQueryStore, + smartcontracts::isi::Registrable as _, state::State, World, }; fn world_with_test_account(authority: &AccountId) -> World { @@ -1657,9 +1671,16 @@ mod tests { isi_len = isi_hex.len() / 3, ); let mut runtime = RuntimeBuilder::::new().build()?; + let block_header = ValidBlock::new_dummy(&KeyPair::random().into_parts().1) + .as_ref() + .header(); + let mut state_block = state.block(block_header); + let mut state_transaction = state_block.transaction(); runtime - .execute(&mut state.block().transaction(), authority, wat) + .execute(&mut state_transaction, authority, wat) .expect("Execution failed"); + state_transaction.apply(); + state_block.commit(); Ok(()) } @@ -1697,9 +1718,16 @@ mod tests { ); let mut runtime = RuntimeBuilder::::new().build()?; + let block_header = ValidBlock::new_dummy(&KeyPair::random().into_parts().1) + .as_ref() + .header(); + let mut state_block = state.block(block_header); + let mut state_transaction = state_block.transaction(); runtime - .execute(&mut state.block().transaction(), authority, wat) + .execute(&mut state_transaction, authority, wat) .expect("Execution failed"); + state_transaction.apply(); + state_block.commit(); Ok(()) } @@ -1741,12 +1769,14 @@ mod tests { ); let mut runtime = RuntimeBuilder::::new().build()?; - let res = runtime.validate( - &mut state.block().transaction(), - authority, - wat, - nonzero!(1_u64), - ); + let block_header = ValidBlock::new_dummy(&KeyPair::random().into_parts().1) + .as_ref() + .header(); + let mut state_block = state.block(block_header); + let mut state_transaction = state_block.transaction(); + let res = runtime.validate(&mut state_transaction, authority, wat, nonzero!(1_u64)); + state_transaction.apply(); + state_block.commit(); if let Error::ExportFnCall(ExportFnCallError::Other(report)) = res.expect_err("Execution should fail") @@ -1795,12 +1825,14 @@ mod tests { ); let mut runtime = RuntimeBuilder::::new().build()?; - let res = runtime.validate( - &mut state.block().transaction(), - authority, - wat, - nonzero!(1_u64), - ); + let block_header = ValidBlock::new_dummy(&KeyPair::random().into_parts().1) + .as_ref() + .header(); + let mut state_block = state.block(block_header); + let mut state_transaction = state_block.transaction(); + let res = runtime.validate(&mut state_transaction, authority, wat, nonzero!(1_u64)); + state_transaction.apply(); + state_block.commit(); if let Error::ExportFnCall(ExportFnCallError::HostExecution(report)) = res.expect_err("Execution should fail") @@ -1846,12 +1878,14 @@ mod tests { ); let mut runtime = RuntimeBuilder::::new().build()?; - let res = runtime.validate( - &mut state.block().transaction(), - authority, - wat, - nonzero!(1_u64), - ); + let block_header = ValidBlock::new_dummy(&KeyPair::random().into_parts().1) + .as_ref() + .header(); + let mut state_block = state.block(block_header); + let mut state_transaction = state_block.transaction(); + let res = runtime.validate(&mut state_transaction, authority, wat, nonzero!(1_u64)); + state_transaction.apply(); + state_block.commit(); if let Error::ExportFnCall(ExportFnCallError::HostExecution(report)) = res.expect_err("Execution should fail") @@ -1892,9 +1926,16 @@ mod tests { ); let mut runtime = RuntimeBuilder::::new().build()?; + let block_header = ValidBlock::new_dummy(&KeyPair::random().into_parts().1) + .as_ref() + .header(); + let mut state_block = state.block(block_header); + let mut state_transaction = state_block.transaction(); let err = runtime - .execute(&mut state.block().transaction(), authority, wat) + .execute(&mut state_transaction, authority, wat) .expect_err("Execution should fail"); + state_transaction.apply(); + state_block.commit(); assert!(matches!( err, diff --git a/crates/iroha_core/src/snapshot.rs b/crates/iroha_core/src/snapshot.rs index bc0b37312f1..d2f85dc65b5 100644 --- a/crates/iroha_core/src/snapshot.rs +++ b/crates/iroha_core/src/snapshot.rs @@ -160,22 +160,22 @@ pub fn try_read_snapshot( }); } for height in 1..=snapshot_height { - let kura_block_hash = NonZeroUsize::new(height) - .and_then(|height| kura.get_block_hash(height)) + let kura_block = NonZeroUsize::new(height) + .and_then(|height| kura.get_block(height)) .expect("Kura has height at least as large as state height"); let snapshot_block_hash = state_view.block_hashes[height - 1]; - if kura_block_hash != snapshot_block_hash { + if kura_block.hash() != snapshot_block_hash { // If last block hash is different it might mean that snapshot was crated for soft-fork block so just drop changes made by this block if height == snapshot_height { iroha_logger::warn!( "Snapshot has incorrect latest block hash, discarding changes made by this block" ); - state.block_and_revert().commit(); + state.block_and_revert(kura_block.header()).commit(); } else { return Err(TryReadError::MismatchedHash { height, snapshot_block_hash, - kura_block_hash, + kura_block_hash: kura_block.hash(), }); } } @@ -354,7 +354,7 @@ mod tests { .unwrap(); { - let mut state_block = state.block(); + let mut state_block = state.block(committed_block.as_ref().header()); let _events = state_block.apply_without_execution(&committed_block, topology.as_ref().to_owned()); state_block.commit(); @@ -372,7 +372,7 @@ mod tests { .unwrap(); { - let mut state_block = state.block(); + let mut state_block = state.block(committed_block.as_ref().header()); let _events = state_block.apply_without_execution(&committed_block, topology.as_ref().to_owned()); state_block.commit(); @@ -413,7 +413,7 @@ mod tests { .unwrap(); { - let mut state_block = state.block(); + let mut state_block = state.block(committed_block.as_ref().header()); let _events = state_block.apply_without_execution(&committed_block, topology.as_ref().to_owned()); state_block.commit(); @@ -431,7 +431,7 @@ mod tests { .unwrap(); { - let mut state_block = state.block(); + let mut state_block = state.block(committed_block.as_ref().header()); let _events = state_block.apply_without_execution(&committed_block, topology.as_ref().to_owned()); state_block.commit(); diff --git a/crates/iroha_core/src/state.rs b/crates/iroha_core/src/state.rs index 6eb0c7e1fa1..cd19fe23bf1 100644 --- a/crates/iroha_core/src/state.rs +++ b/crates/iroha_core/src/state.rs @@ -117,8 +117,6 @@ pub struct WorldBlock<'world> { pub(crate) executor_data_model: CellBlock<'world, ExecutorDataModel>, /// Events produced during execution of block events_buffer: Vec, - - pub(crate) genesis_creation_time_ms: Option, } /// Struct for single transaction's aggregated changes @@ -150,8 +148,6 @@ pub struct WorldTransaction<'block, 'world> { pub(crate) executor_data_model: CellTransaction<'block, 'world, ExecutorDataModel>, /// Events produced during execution of a transaction events_buffer: TransactionEventBuffer<'block>, - - pub(crate) genesis_creation_time_ms: Option, } /// Wrapper for event's buffer to apply transaction rollback @@ -188,8 +184,6 @@ pub struct WorldView<'world> { pub(crate) executor: CellView<'world, Executor>, /// Executor-defined data model pub(crate) executor_data_model: CellView<'world, ExecutorDataModel>, - - pub(crate) genesis_creation_time_ms: Option, } /// Current state of the blockchain @@ -249,6 +243,8 @@ pub struct StateBlock<'state> { pub new_tx_amounts: &'state Mutex>, /// Lock to prevent getting inconsistent view of the state view_lock: &'state parking_lot::RwLock<()>, + + pub(crate) curr_block: BlockHeader, } /// Struct for single transaction's aggregated changes @@ -273,6 +269,8 @@ pub struct StateTransaction<'block, 'state> { /// Temporary metrics buffer of amounts of any asset that has been transacted. /// TODO: this should be done through events pub new_tx_amounts: &'state Mutex>, + + pub(crate) curr_block: BlockHeader, } /// Consistent point in time view of the [`State`] @@ -366,8 +364,6 @@ impl World { executor: self.executor.block(), executor_data_model: self.executor_data_model.block(), events_buffer: Vec::new(), - - genesis_creation_time_ms: None, } } @@ -387,8 +383,6 @@ impl World { executor: self.executor.block_and_revert(), executor_data_model: self.executor_data_model.block_and_revert(), events_buffer: Vec::new(), - - genesis_creation_time_ms: None, } } @@ -407,8 +401,6 @@ impl World { triggers: self.triggers.view(), executor: self.executor.view(), executor_data_model: self.executor_data_model.view(), - - genesis_creation_time_ms: None, } } } @@ -429,8 +421,6 @@ pub trait WorldReadOnly { fn executor(&self) -> &Executor; fn executor_data_model(&self) -> &ExecutorDataModel; - fn genesis_creation_time_ms(&self) -> Option; - // Domain-related methods /// Get `Domain` without an ability to modify it. @@ -697,10 +687,6 @@ macro_rules! impl_world_ro { fn executor_data_model(&self) -> &ExecutorDataModel { &self.executor_data_model } - - fn genesis_creation_time_ms(&self) -> Option { - self.genesis_creation_time_ms - } } )*}; } @@ -713,8 +699,6 @@ impl<'world> WorldBlock<'world> { /// Create struct to apply transaction's changes pub fn trasaction(&mut self) -> WorldTransaction<'_, 'world> { WorldTransaction { - genesis_creation_time_ms: self.genesis_creation_time_ms(), - parameters: self.parameters.transaction(), trusted_peers_ids: self.trusted_peers_ids.transaction(), domains: self.domains.transaction(), @@ -751,7 +735,6 @@ impl<'world> WorldBlock<'world> { executor, executor_data_model, events_buffer: _, - genesis_creation_time_ms: _, } = self; // IMPORTANT!!! Commit fields in reverse order, this way consistent results are insured executor_data_model.commit(); @@ -787,7 +770,6 @@ impl WorldTransaction<'_, '_> { executor, executor_data_model, mut events_buffer, - genesis_creation_time_ms: _, } = self; executor_data_model.apply(); executor.apply(); @@ -1100,7 +1082,7 @@ impl State { } /// Create structure to execute a block - pub fn block(&self) -> StateBlock<'_> { + pub fn block(&self, curr_block: BlockHeader) -> StateBlock<'_> { StateBlock { world: self.world.block(), block_hashes: self.block_hashes.block(), @@ -1112,11 +1094,12 @@ impl State { query_handle: &self.query_handle, new_tx_amounts: &self.new_tx_amounts, view_lock: &self.view_lock, + curr_block, } } /// Create structure to execute a block while reverting changes made in the latest block - pub fn block_and_revert(&self) -> StateBlock<'_> { + pub fn block_and_revert(&self, curr_block: BlockHeader) -> StateBlock<'_> { StateBlock { world: self.world.block_and_revert(), block_hashes: self.block_hashes.block_and_revert(), @@ -1128,6 +1111,7 @@ impl State { query_handle: &self.query_handle, new_tx_amounts: &self.new_tx_amounts, view_lock: &self.view_lock, + curr_block, } } @@ -1170,7 +1154,7 @@ pub trait StateReadOnly { self.height() .checked_sub(1) .and_then(NonZeroUsize::new) - .and_then(|height| self.kura().get_block_by_height(height)) + .and_then(|height| self.kura().get_block(height)) } /// Get a reference to the latest block. Returns none if genesis is not committed. @@ -1178,7 +1162,7 @@ pub trait StateReadOnly { /// If you only need hash of the latest block prefer using [`Self::latest_block_hash`] #[inline] fn latest_block(&self) -> Option> { - NonZeroUsize::new(self.height()).and_then(|height| self.kura().get_block_by_height(height)) + NonZeroUsize::new(self.height()).and_then(|height| self.kura().get_block(height)) } /// Return the hash of the latest block @@ -1199,51 +1183,17 @@ pub trait StateReadOnly { ) -> impl DoubleEndedIterator> + '_ { (start.get()..=self.height()).map(|height| { NonZeroUsize::new(height) - .and_then(|height| self.kura().get_block_by_height(height)) + .and_then(|height| self.kura().get_block(height)) .expect("INTERNAL BUG: Failed to load block") }) } - /// Return a vector of blockchain blocks after the block with the given `hash` - fn block_hashes_after_hash( - &self, - hash: Option>, - ) -> Vec> { - hash.map_or_else( - || self.block_hashes().to_vec(), - |block_hash| { - self.block_hashes() - .iter() - .skip_while(|&x| *x != block_hash) - .skip(1) - .copied() - .collect() - }, - ) - } - - /// Return an iterator over blockchain block hashes starting with the block of the given `height` - fn block_hashes_from_height(&self, height: usize) -> Vec> { - self.block_hashes() - .iter() - .skip(height.saturating_sub(1)) - .copied() - .collect() - } - /// Height of blockchain #[inline] fn height(&self) -> usize { self.block_hashes().len() } - /// Find a [`SignedBlock`] by hash. - fn block_with_tx(&self, hash: &HashOf) -> Option> { - self.transactions() - .get(hash) - .and_then(|&height| self.kura().get_block_by_height(height)) - } - /// Returns [`Some`] milliseconds since the genesis block was /// committed, or [`None`] if it wasn't. #[inline] @@ -1253,7 +1203,7 @@ pub trait StateReadOnly { } else { let opt = self .kura() - .get_block_by_height(nonzero!(1_usize)) + .get_block(nonzero!(1_usize)) .map(|genesis_block| genesis_block.header().creation_time()); if opt.is_none() { @@ -1322,6 +1272,7 @@ impl<'state> StateBlock<'state> { kura: self.kura, query_handle: self.query_handle, new_tx_amounts: self.new_tx_amounts, + curr_block: self.curr_block, } } @@ -1335,10 +1286,7 @@ impl<'state> StateBlock<'state> { commit_topology: committed_topology, prev_commit_topology: prev_committed_topology, view_lock, - engine: _, - kura: _, - query_handle: _, - new_tx_amounts: _, + .. } = self; let _view_lock = view_lock.write(); prev_committed_topology.commit(); @@ -1445,7 +1393,7 @@ impl<'state> StateBlock<'state> { self.world.events_buffer.push( BlockEvent { - header: block.as_ref().header().clone(), + header: block.as_ref().header(), status: BlockStatus::Applied, } .into(), @@ -1528,10 +1476,7 @@ impl StateTransaction<'_, '_> { transactions, commit_topology: committed_topology, prev_commit_topology: prev_committed_topology, - engine: _, - kura: _, - query_handle: _, - new_tx_amounts: _, + .. } = self; prev_committed_topology.apply(); committed_topology.apply(); @@ -2156,7 +2101,6 @@ mod tests { let kura = Kura::blank_kura_for_testing(); let query_handle = LiveQueryStore::start_test(); let state = State::new(World::default(), kura, query_handle); - let mut state_block = state.block(); let mut block_hashes = vec![]; for i in 1..=BLOCK_CNT { @@ -2165,12 +2109,20 @@ mod tests { header.prev_block_hash = block_hashes.last().copied(); }); + let mut state_block = state.block(block.as_ref().header()); block_hashes.push(block.as_ref().hash()); let _events = state_block.apply(&block, Vec::new()).unwrap(); + state_block.commit(); } - assert!(state_block - .block_hashes_after_hash(Some(block_hashes[6])) + assert!(state + .view() + .block_hashes() + .iter() + .skip_while(|&x| *x != block_hashes[6]) + .skip(1) + .copied() + .collect::>() .into_iter() .eq(block_hashes.into_iter().skip(7))); } @@ -2182,19 +2134,21 @@ mod tests { let kura = Kura::blank_kura_for_testing(); let query_handle = LiveQueryStore::start_test(); let state = State::new(World::default(), kura.clone(), query_handle); - let mut state_block = state.block(); for i in 1..=BLOCK_CNT { let block = new_dummy_block_with_payload(|header| { header.height = NonZeroU64::new(i as u64).unwrap(); }); + let mut state_block = state.block(block.as_ref().header()); let _events = state_block.apply(&block, Vec::new()).unwrap(); + state_block.commit(); kura.store_block(block); } assert_eq!( - &state_block + &state + .view() .all_blocks(nonzero!(8_usize)) .map(|block| block.header().height().get()) .collect::>(), diff --git a/crates/iroha_core/src/sumeragi/main_loop.rs b/crates/iroha_core/src/sumeragi/main_loop.rs index 050c95c1d66..f89203ada14 100644 --- a/crates/iroha_core/src/sumeragi/main_loop.rs +++ b/crates/iroha_core/src/sumeragi/main_loop.rs @@ -229,9 +229,7 @@ impl Sumeragi { } }; - let mut state_block = state.block(); - state_block.world.genesis_creation_time_ms = - Some(block.header().creation_time_ms); + let mut state_block = state.block(block.header()); let block = match ValidBlock::validate( block, &self.topology, @@ -293,8 +291,7 @@ impl Sumeragi { assert_eq!(state_view.latest_block_hash(), None); } - let mut state_block = state.block(); - state_block.world.genesis_creation_time_ms = Some(genesis.header().creation_time_ms); + let mut state_block = state.block(genesis.header()); let msg = BlockCreated::from(&genesis); self.broadcast_packet(msg); @@ -864,16 +861,17 @@ impl Sumeragi { .map(|tx| tx.deref().clone()) .collect::>(); - let mut state_block = state.block(); let unverified_block = BlockBuilder::new(transactions) - .chain(self.topology.view_change_index(), &mut state_block) + .chain( + self.topology.view_change_index(), + state.view().latest_block().as_deref(), + ) .sign(self.key_pair.private_key()) .unpack(|e| self.send_event(e)); - info!( peer_id=%self.peer_id, - block_hash=%unverified_block.header.hash(), - txns=%unverified_block.transactions.len(), + block_hash=%unverified_block.header().hash(), + txns=%unverified_block.transactions().len(), view_change_index=%self.topology.view_change_index(), "Block created" ); @@ -883,6 +881,7 @@ impl Sumeragi { self.broadcast_packet(msg); } + let mut state_block = state.block(unverified_block.header()); let block = unverified_block .categorize(&mut state_block) .unpack(|e| self.send_event(e)); @@ -1405,7 +1404,6 @@ fn categorize_block_sync( #[cfg(test)] mod tests { - use iroha_crypto::SignatureOf; use iroha_data_model::{isi::InstructionBox, transaction::TransactionBuilder}; use iroha_genesis::GENESIS_DOMAIN_ID; use iroha_test_samples::gen_account_in; @@ -1421,14 +1419,10 @@ mod tests { private_key: &PrivateKey, f: impl FnOnce(&mut BlockHeader), ) -> NewBlock { - let mut header = block.header.clone(); + let mut header = block.header(); f(&mut header); - NewBlock { - signature: BlockSignature(0, SignatureOf::new(private_key, &header)), - header, - transactions: block.transactions.clone(), - } + block.clone().update_header(header, private_key) } fn create_data_for_test( @@ -1459,8 +1453,6 @@ mod tests { let params = state_view.parameters(); (params.sumeragi().max_clock_drift(), params.transaction) }; - let mut state_block = state.block(); - // Making two transactions that have the same instruction let tx = TransactionBuilder::new(chain_id.clone(), alice_id.clone()) .with_instructions([fail_isi]) @@ -1483,10 +1475,13 @@ mod tests { .expect("Valid"); // Creating a block of two identical transactions and validating it - let genesis = BlockBuilder::new(vec![peers, tx.clone(), tx]) - .chain(0, &mut state_block) + let unverified_genesis = BlockBuilder::new(vec![peers, tx.clone(), tx]) + .chain(0, state.view().latest_block().as_deref()) .sign(leader_private_key) - .unpack(|_| {}) + .unpack(|_| {}); + + let mut state_block = state.block(unverified_genesis.header()); + let genesis = unverified_genesis .categorize(&mut state_block) .unpack(|_| {}) .commit(topology) @@ -1498,7 +1493,6 @@ mod tests { kura.store_block(genesis); let block = { - let mut state_block = state.block(); // Making two transactions that have the same instruction let create_asset_definition1 = Register::asset_definition(AssetDefinition::numeric( "xor1#wonderland".parse().expect("Valid"), @@ -1522,7 +1516,7 @@ mod tests { // Creating a block of two identical transactions and validating it BlockBuilder::new(vec![tx1, tx2]) - .chain(0, &mut state_block) + .chain(0, state.view().latest_block().as_deref()) .sign(leader_private_key) .unpack(|_| {}) }; @@ -1560,7 +1554,7 @@ mod tests { let (state, kura, unverified_block, genesis_public_key) = create_data_for_test(&chain_id, &topology, &leader_private_key); - let mut state_block = state.block(); + let mut state_block = state.block(unverified_block.header()); let committed_block = unverified_block .clone() .categorize(&mut state_block) @@ -1651,7 +1645,7 @@ mod tests { let (state, kura, unverified_block, genesis_public_key) = create_data_for_test(&chain_id, &topology, &leader_private_key); - let mut state_block = state.block(); + let mut state_block = state.block(unverified_block.header()); let committed_block = unverified_block .clone() .categorize(&mut state_block) @@ -1693,7 +1687,7 @@ mod tests { header.view_change_index = 42; }); - let mut state_block = state.block(); + let mut state_block = state.block(unverified_block.header()); let committed_block = unverified_block .clone() .categorize(&mut state_block) @@ -1777,9 +1771,9 @@ mod tests { let topology = Topology::new(vec![peer_id]); let (state, _, unverified_block, genesis_public_key) = create_data_for_test(&chain_id, &topology, &leader_private_key); - let valid_block = unverified_block - .categorize(&mut state.block()) - .unpack(|_| {}); + let mut state_block = state.block(unverified_block.header()); + let valid_block = unverified_block.categorize(&mut state_block).unpack(|_| {}); + state_block.commit(); // Malform block signatures so that block going to be rejected let dummy_signature = BlockSignature( @@ -1788,10 +1782,9 @@ mod tests { ); let mut block: SignedBlock = valid_block.into(); let _prev_signatures = block.replace_signatures(vec![dummy_signature]).unwrap(); - let mut voting_block = Some(VotingBlock::new( - ValidBlock::new_dummy(&leader_private_key), - state.block(), - )); + let dummy_block = ValidBlock::new_dummy(&leader_private_key); + let dummy_state_block = state.block(dummy_block.as_ref().header()); + let mut voting_block = Some(VotingBlock::new(dummy_block, dummy_state_block)); let block_sync_type = categorize_block_sync(&block, &state.view()); let result = handle_categorized_block_sync( diff --git a/crates/iroha_core/src/sumeragi/mod.rs b/crates/iroha_core/src/sumeragi/mod.rs index baa8f13ef02..eafe0eacf9a 100644 --- a/crates/iroha_core/src/sumeragi/mod.rs +++ b/crates/iroha_core/src/sumeragi/mod.rs @@ -97,10 +97,6 @@ impl SumeragiHandle { // NOTE: topology need to be updated up to block's view_change_index topology.nth_rotation(block.header().view_change_index as usize); - if block.header().is_genesis() { - state_block.world.genesis_creation_time_ms = Some(block.header().creation_time_ms); - } - let block = ValidBlock::validate( block.clone(), topology, @@ -168,7 +164,7 @@ impl SumeragiStartArgs { let state_view = state.view(); let skip_block_count = state_view.height(); blocks_iter = (skip_block_count + 1..=block_count).map(|block_height| { - NonZeroUsize::new(block_height).and_then(|height| kura.get_block_by_height(height)).expect( + NonZeroUsize::new(block_height).and_then(|height| kura.get_block(height)).expect( "Sumeragi should be able to load the block that was reported as presented. \ If not, the block storage was probably disconnected.", ) @@ -192,7 +188,7 @@ impl SumeragiStartArgs { ); for block in blocks_iter { - let mut state_block = state.block(); + let mut state_block = state.block(block.header()); SumeragiHandle::replay_block( &common_config.chain, &genesis_account, diff --git a/crates/iroha_data_model/src/block.rs b/crates/iroha_data_model/src/block.rs index 4f060e0be67..5eac540a25d 100644 --- a/crates/iroha_data_model/src/block.rs +++ b/crates/iroha_data_model/src/block.rs @@ -30,6 +30,7 @@ mod model { Debug, Display, Clone, + Copy, PartialEq, Eq, PartialOrd, @@ -119,7 +120,6 @@ declare_versioned!(SignedBlock 1..2, Debug, Clone, PartialEq, Eq, PartialOrd, Or impl BlockHeader { /// Checks if it's a header of a genesis block. #[inline] - #[cfg(feature = "transparent_api")] pub const fn is_genesis(&self) -> bool { self.height.get() == 1 } @@ -141,8 +141,8 @@ impl SignedBlockV1 { self.payload.header.hash() } - fn header(&self) -> &BlockHeader { - &self.payload.header + fn header(&self) -> BlockHeader { + self.payload.header } } @@ -200,9 +200,9 @@ impl SignedBlock { /// Block header #[inline] - pub fn header(&self) -> &BlockHeader { + pub fn header(&self) -> BlockHeader { let SignedBlock::V1(block) = self; - &block.payload.header + block.header() } /// Block transactions diff --git a/crates/iroha_data_model/src/query/predicate/predicate_atoms/block.rs b/crates/iroha_data_model/src/query/predicate/predicate_atoms/block.rs index c1d7ecb24b3..57dee5b99ce 100644 --- a/crates/iroha_data_model/src/query/predicate/predicate_atoms/block.rs +++ b/crates/iroha_data_model/src/query/predicate/predicate_atoms/block.rs @@ -73,7 +73,7 @@ impl_predicate_box!(SignedBlock: SignedBlockPredicateBox); impl EvaluatePredicate for SignedBlockPredicateBox { fn applies(&self, input: &SignedBlock) -> bool { match self { - SignedBlockPredicateBox::Header(header) => header.applies(input.header()), + SignedBlockPredicateBox::Header(header) => header.applies(&input.header()), } } } diff --git a/crates/iroha_data_model/src/smart_contract.rs b/crates/iroha_data_model/src/smart_contract.rs index aa27818711d..76d42aba45e 100644 --- a/crates/iroha_data_model/src/smart_contract.rs +++ b/crates/iroha_data_model/src/smart_contract.rs @@ -5,13 +5,15 @@ pub mod payloads { use parity_scale_codec::{Decode, Encode}; - use crate::prelude::*; + use crate::{block::BlockHeader, prelude::*}; /// Context for smart contract entrypoint #[derive(Debug, Clone, Encode, Decode)] pub struct SmartContractContext { /// Account that submitted the transaction containing the smart contract pub authority: AccountId, + /// Block currently being processed + pub curr_block: BlockHeader, } /// Context for trigger entrypoint @@ -21,6 +23,8 @@ pub mod payloads { pub id: TriggerId, /// Account that registered the trigger pub authority: AccountId, + /// Block currently being processed + pub curr_block: BlockHeader, /// Event which triggered the execution pub event: EventBox, } @@ -30,8 +34,8 @@ pub mod payloads { pub struct ExecutorContext { /// Account that is executing the operation pub authority: AccountId, - /// Height of the latest block in the blockchain - pub block_height: u64, + /// Block currently being processed (or latest block hash for queries) + pub curr_block: BlockHeader, } /// Generic payload for `validate_*()` entrypoints of executor. diff --git a/crates/iroha_executor/src/default.rs b/crates/iroha_executor/src/default.rs index 3b6382666af..4155135e7a9 100644 --- a/crates/iroha_executor/src/default.rs +++ b/crates/iroha_executor/src/default.rs @@ -131,7 +131,7 @@ pub mod peer { executor: &mut V, isi: &Register, ) { - if is_genesis(executor) { + if executor.context().curr_block.is_genesis() { execute!(executor, isi); } if CanManagePeers.is_owned_by(&executor.context().authority, executor.host()) { @@ -145,7 +145,7 @@ pub mod peer { executor: &mut V, isi: &Unregister, ) { - if is_genesis(executor) { + if executor.context().curr_block.is_genesis() { execute!(executor, isi); } if CanManagePeers.is_owned_by(&executor.context().authority, executor.host()) { @@ -171,7 +171,7 @@ pub mod domain { executor: &mut V, isi: &Register, ) { - if is_genesis(executor) { + if executor.context().curr_block.is_genesis() { execute!(executor, isi); } if CanRegisterDomain.is_owned_by(&executor.context().authority, executor.host()) { @@ -187,7 +187,7 @@ pub mod domain { ) { let domain_id = isi.object(); - if is_genesis(executor) + if executor.context().curr_block.is_genesis() || match is_domain_owner(domain_id, &executor.context().authority, executor.host()) { Err(err) => deny!(executor, err), Ok(is_domain_owner) => is_domain_owner, @@ -236,7 +236,7 @@ pub mod domain { let source_id = isi.source(); let domain_id = isi.object(); - if is_genesis(executor) { + if executor.context().curr_block.is_genesis() { execute!(executor, isi); } match is_account_owner(source_id, &executor.context().authority, executor.host()) { @@ -259,7 +259,7 @@ pub mod domain { ) { let domain_id = isi.object(); - if is_genesis(executor) { + if executor.context().curr_block.is_genesis() { execute!(executor, isi); } match is_domain_owner(domain_id, &executor.context().authority, executor.host()) { @@ -285,7 +285,7 @@ pub mod domain { ) { let domain_id = isi.object(); - if is_genesis(executor) { + if executor.context().curr_block.is_genesis() { execute!(executor, isi); } match is_domain_owner(domain_id, &executor.context().authority, executor.host()) { @@ -426,7 +426,7 @@ pub mod account { ) { let account_id = isi.object(); - if is_genesis(executor) + if executor.context().curr_block.is_genesis() || match is_account_owner(account_id, &executor.context().authority, executor.host()) { Err(err) => deny!(executor, err), Ok(is_account_owner) => is_account_owner, @@ -474,7 +474,7 @@ pub mod account { ) { let account_id = isi.object(); - if is_genesis(executor) { + if executor.context().curr_block.is_genesis() { execute!(executor, isi); } match is_account_owner(account_id, &executor.context().authority, executor.host()) { @@ -503,7 +503,7 @@ pub mod account { ) { let account_id = isi.object(); - if is_genesis(executor) { + if executor.context().curr_block.is_genesis() { execute!(executor, isi); } match is_account_owner(account_id, &executor.context().authority, executor.host()) { @@ -619,7 +619,7 @@ pub mod asset_definition { ) { let asset_definition_id = isi.object(); - if is_genesis(executor) + if executor.context().curr_block.is_genesis() || match is_asset_definition_owner( asset_definition_id, &executor.context().authority, @@ -675,7 +675,7 @@ pub mod asset_definition { let source_id = isi.source(); let asset_definition_id = isi.object(); - if is_genesis(executor) { + if executor.context().curr_block.is_genesis() { execute!(executor, isi); } match is_account_owner(source_id, &executor.context().authority, executor.host()) { @@ -705,7 +705,7 @@ pub mod asset_definition { ) { let asset_definition_id = isi.object(); - if is_genesis(executor) { + if executor.context().curr_block.is_genesis() { execute!(executor, isi); } match is_asset_definition_owner( @@ -738,7 +738,7 @@ pub mod asset_definition { ) { let asset_definition_id = isi.object(); - if is_genesis(executor) { + if executor.context().curr_block.is_genesis() { execute!(executor, isi); } match is_asset_definition_owner( @@ -850,7 +850,7 @@ pub mod asset { ) { let asset = isi.object(); - if is_genesis(executor) { + if executor.context().curr_block.is_genesis() { execute!(executor, isi); } match is_asset_definition_owner( @@ -890,7 +890,7 @@ pub mod asset { ) { let asset_id = isi.object(); - if is_genesis(executor) { + if executor.context().curr_block.is_genesis() { execute!(executor, isi); } match is_asset_owner(asset_id, &executor.context().authority, executor.host()) { @@ -934,7 +934,7 @@ pub mod asset { Mint: BuiltInInstruction + Encode, { let asset_id = isi.destination(); - if is_genesis(executor) { + if executor.context().curr_block.is_genesis() { execute!(executor, isi); } match is_asset_definition_owner( @@ -981,7 +981,7 @@ pub mod asset { Burn: BuiltInInstruction + Encode, { let asset_id = isi.destination(); - if is_genesis(executor) { + if executor.context().curr_block.is_genesis() { execute!(executor, isi); } match is_asset_owner(asset_id, &executor.context().authority, executor.host()) { @@ -1030,7 +1030,7 @@ pub mod asset { Transfer: BuiltInInstruction + Encode, { let asset_id = isi.source(); - if is_genesis(executor) { + if executor.context().curr_block.is_genesis() { execute!(executor, isi); } match is_asset_owner(asset_id, &executor.context().authority, executor.host()) { @@ -1086,7 +1086,7 @@ pub mod asset { ) { let asset_id = isi.object(); - if is_genesis(executor) { + if executor.context().curr_block.is_genesis() { execute!(executor, isi); } match is_asset_owner(asset_id, &executor.context().authority, executor.host()) { @@ -1116,7 +1116,7 @@ pub mod asset { ) { let asset_id = isi.object(); - if is_genesis(executor) { + if executor.context().curr_block.is_genesis() { execute!(executor, isi); } match is_asset_owner(asset_id, &executor.context().authority, executor.host()) { @@ -1146,7 +1146,7 @@ pub mod parameter { use super::*; pub fn visit_set_parameter(executor: &mut V, isi: &SetParameter) { - if is_genesis(executor) { + if executor.context().curr_block.is_genesis() { execute!(executor, isi); } if CanSetParameters.is_owned_by(&executor.context().authority, executor.host()) { @@ -1170,7 +1170,7 @@ pub mod role { ($executor:ident, $isi:ident) => { let role_id = $isi.object(); - if is_genesis($executor) + if $executor.context().curr_block.is_genesis() || find_account_roles($executor.context().authority.clone(), $executor.host()) .any(|authority_role_id| authority_role_id == *role_id) { @@ -1187,7 +1187,7 @@ pub mod role { let permission = $isi.object(); if let Ok(any_permission) = AnyPermission::try_from(permission) { - if !is_genesis($executor) { + if !$executor.context().curr_block.is_genesis() { if !find_account_roles($executor.context().authority.clone(), $executor.host()) .any(|authority_role_id| authority_role_id == role_id) { @@ -1235,7 +1235,7 @@ pub mod role { iroha_smart_contract::debug!(&format!("Checking `{permission:?}`")); if let Ok(any_permission) = AnyPermission::try_from(permission) { - if !is_genesis(executor) { + if !executor.context().curr_block.is_genesis() { if let Err(error) = crate::permission::ValidateGrantRevoke::validate_grant( &any_permission, role.grant_to(), @@ -1255,7 +1255,7 @@ pub mod role { } } - if is_genesis(executor) + if executor.context().curr_block.is_genesis() || CanManageRoles.is_owned_by(&executor.context().authority, executor.host()) { let grant_role = &Grant::account_role(role.id().clone(), role.grant_to().clone()); @@ -1275,7 +1275,7 @@ pub mod role { executor: &mut V, isi: &Unregister, ) { - if is_genesis(executor) { + if executor.context().curr_block.is_genesis() { execute!(executor, isi); } if CanManageRoles.is_owned_by(&executor.context().authority, executor.host()) { @@ -1332,7 +1332,7 @@ pub mod trigger { ) { let trigger = isi.object(); - if is_genesis(executor) + if executor.context().curr_block.is_genesis() || { match is_domain_owner( trigger.action().authority().domain(), @@ -1362,7 +1362,7 @@ pub mod trigger { ) { let trigger_id = isi.object(); - if is_genesis(executor) + if executor.context().curr_block.is_genesis() || match is_trigger_owner(trigger_id, &executor.context().authority, executor.host()) { Err(err) => deny!(executor, err), Ok(is_trigger_owner) => is_trigger_owner, @@ -1413,7 +1413,7 @@ pub mod trigger { ) { let trigger_id = isi.destination(); - if is_genesis(executor) { + if executor.context().curr_block.is_genesis() { execute!(executor, isi); } match is_trigger_owner(trigger_id, &executor.context().authority, executor.host()) { @@ -1440,7 +1440,7 @@ pub mod trigger { ) { let trigger_id = isi.destination(); - if is_genesis(executor) { + if executor.context().curr_block.is_genesis() { execute!(executor, isi); } match is_trigger_owner(trigger_id, &executor.context().authority, executor.host()) { @@ -1467,7 +1467,7 @@ pub mod trigger { ) { let trigger_id = isi.trigger(); - if is_genesis(executor) { + if executor.context().curr_block.is_genesis() { execute!(executor, isi); } match is_trigger_owner(trigger_id, &executor.context().authority, executor.host()) { @@ -1491,7 +1491,7 @@ pub mod trigger { ) { let trigger_id = isi.object(); - if is_genesis(executor) { + if executor.context().curr_block.is_genesis() { execute!(executor, isi); } match is_trigger_owner(trigger_id, &executor.context().authority, executor.host()) { @@ -1520,7 +1520,7 @@ pub mod trigger { ) { let trigger_id = isi.object(); - if is_genesis(executor) { + if executor.context().curr_block.is_genesis() { execute!(executor, isi); } match is_trigger_owner(trigger_id, &executor.context().authority, executor.host()) { @@ -1592,7 +1592,7 @@ pub mod permission { let permission = $isi.object(); if let Ok(any_permission) = AnyPermission::try_from(permission) { - if !is_genesis($executor) { + if !$executor.context().curr_block.is_genesis() { if let Err(error) = crate::permission::ValidateGrantRevoke::$method( &any_permission, &$executor.context().authority, @@ -1635,7 +1635,7 @@ pub mod executor { use super::*; pub fn visit_upgrade(executor: &mut V, isi: &Upgrade) { - if is_genesis(executor) { + if executor.context().curr_block.is_genesis() { execute!(executor, isi); } if CanUpgradeExecutor.is_owned_by(&executor.context().authority, executor.host()) { @@ -1664,7 +1664,3 @@ pub mod custom { ) } } - -fn is_genesis(executor: &V) -> bool { - executor.context().block_height == 0 -} diff --git a/crates/iroha_executor/src/permission.rs b/crates/iroha_executor/src/permission.rs index 570490af679..ebe7a873d0f 100644 --- a/crates/iroha_executor/src/permission.rs +++ b/crates/iroha_executor/src/permission.rs @@ -1038,7 +1038,7 @@ pub(crate) struct OnlyGenesis; impl PassCondition for OnlyGenesis { fn validate(&self, _authority: &AccountId, _host: &Iroha, context: &Context) -> Result { - if context.block_height == 0 { + if context.curr_block.is_genesis() { Ok(()) } else { Err(ValidationFail::NotPermitted( diff --git a/crates/iroha_torii/src/block.rs b/crates/iroha_torii/src/block.rs index 89c42e60abc..e61700cb948 100644 --- a/crates/iroha_torii/src/block.rs +++ b/crates/iroha_torii/src/block.rs @@ -55,7 +55,7 @@ impl<'ws> Consumer<'ws> { /// Can fail due to timeout. Also receiving might fail #[iroha_futures::telemetry_future] pub async fn consume(&mut self) -> Result<()> { - if let Some(block) = self.kura.get_block_by_height( + if let Some(block) = self.kura.get_block( self.height .try_into() .expect("INTERNAL BUG: Number of blocks exceeds usize::MAX"), diff --git a/wasm_samples/default_executor/src/lib.rs b/wasm_samples/default_executor/src/lib.rs index 738ad185fa4..7b1fb13d3e8 100644 --- a/wasm_samples/default_executor/src/lib.rs +++ b/wasm_samples/default_executor/src/lib.rs @@ -6,7 +6,9 @@ extern crate panic_halt; use dlmalloc::GlobalDlmalloc; -use iroha_executor::{debug::dbg_panic, prelude::*, DataModelBuilder}; +use iroha_executor::{ + data_model::block::BlockHeader, debug::dbg_panic, prelude::*, DataModelBuilder, +}; #[global_allocator] static ALLOC: GlobalDlmalloc = GlobalDlmalloc; @@ -26,8 +28,8 @@ struct Executor { } impl Executor { - fn ensure_genesis(block_height: u64) { - if block_height != 0 { + fn ensure_genesis(curr_block: BlockHeader) { + if !curr_block.is_genesis() { dbg_panic( "Default Executor is intended to be used only in genesis. \ Write your own executor if you need to upgrade executor on existing chain.", @@ -47,6 +49,6 @@ impl Executor { /// will be denied and previous executor will stay unchanged. #[entrypoint] fn migrate(host: Iroha, context: Context) { - Executor::ensure_genesis(context.block_height); + Executor::ensure_genesis(context.curr_block); DataModelBuilder::with_default_permissions().build_and_set(&host); } diff --git a/wasm_samples/executor_with_custom_permission/src/lib.rs b/wasm_samples/executor_with_custom_permission/src/lib.rs index b9730f8f8e0..8d9ba3b6bc1 100644 --- a/wasm_samples/executor_with_custom_permission/src/lib.rs +++ b/wasm_samples/executor_with_custom_permission/src/lib.rs @@ -1,5 +1,4 @@ -//! Runtime Executor which allows domain (un-)registration only for users who own -//! [`CanControlDomainLives`] permission token. +//! Runtime Executor which allows domain (un-)registration only for users who own [`CanControlDomainLives`] permission token. //! //! This executor should be applied on top of the blockchain with default validation. //! @@ -85,7 +84,7 @@ impl Executor { } fn visit_register_domain(executor: &mut Executor, isi: &Register) { - if executor.context().block_height == 0 { + if executor.context().curr_block.is_genesis() { execute!(executor, isi); } if CanControlDomainLives.is_owned_by(&executor.context().authority, executor.host()) { @@ -99,7 +98,7 @@ fn visit_register_domain(executor: &mut Executor, isi: &Register) { } fn visit_unregister_domain(executor: &mut Executor, isi: &Unregister) { - if executor.context().block_height == 0 { + if executor.context().curr_block.is_genesis() { execute!(executor, isi); } if CanControlDomainLives.is_owned_by(&executor.context().authority, executor.host()) {