Skip to content

Commit

Permalink
[refactor] hyperledger#3901: Add method to define parameter in execut…
Browse files Browse the repository at this point in the history
…or `migrate` entrypoint

Signed-off-by: Dmitry Murzin <diralik@yandex.ru>
  • Loading branch information
dima74 committed Apr 25, 2024
1 parent 28a6e35 commit 41c2be8
Show file tree
Hide file tree
Showing 7 changed files with 200 additions and 6 deletions.
1 change: 1 addition & 0 deletions client/tests/integration/smartcontracts/Cargo.toml
Expand Up @@ -14,6 +14,7 @@ members = [
"executor_with_admin",
"executor_with_custom_token",
"executor_with_migration_fail",
"executor_add_parameter",
"query_assets_and_save_cursor",
]

Expand Down
@@ -0,0 +1,24 @@
[package]
name = "executor_add_parameter"

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

license.workspace = true

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

[dependencies]
iroha_executor.workspace = true
iroha_schema.workspace = true

parity-scale-codec.workspace = true
anyhow.workspace = true
serde_json.workspace = true
serde.workspace = true

panic-halt.workspace = true
lol_alloc.workspace = true
getrandom.workspace = true
@@ -0,0 +1,83 @@
//! Runtime Executor which adds new parameter `max_accounts_per_domain`.
//! And checks that parameter when registering new account.

#![no_std]

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

use lol_alloc::{FreeListAllocator, LockedAllocator};

use iroha_executor::data_model::parameter::ParameterValueBox;
use iroha_executor::debug::{dbg_panic, DebugExpectExt};
use iroha_executor::prelude::*;

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

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

#[derive(Constructor, ValidateEntrypoints, Validate, Visit)]
#[visit(custom(visit_register_account))]
struct Executor {
verdict: Result,
block_height: u64,
}

const MAX_ACCOUNTS_PER_DOMAIN: &str = "max_accounts_per_domain";

fn visit_register_account(executor: &mut Executor, _authority: &AccountId, isi: &Register<Account>) {
if executor.block_height() == 0 {
execute!(executor, isi);
}

let max_accounts_per_domain = get_max_accounts_per_domain();

let domain_id = isi.object().id().domain_id();
let number_accounts = FindAccountsByDomainId::new(domain_id.clone())
.execute()
.dbg_expect("Failed to execute FindAccountsByDomainId")
.into_iter()
.count();

if number_accounts >= max_accounts_per_domain {
deny!(executor, "Exceed max accounts per domain limit");
}

execute!(executor, isi);
}

fn get_max_accounts_per_domain() -> usize {
let parameter_id: ParameterId = MAX_ACCOUNTS_PER_DOMAIN
.parse()
.dbg_expect("Valid parameter name");
let parameters = FindAllParameters::new()
.execute()
.dbg_expect("Failed to execute FindAllParameters")
.into_iter()
.collect::<Result<Vec<_>, _>>()
.dbg_expect("Failed to execute FindAllParameters");
let parameter = parameters
.into_iter()
.find(|parameter| parameter.id() == &parameter_id)
.dbg_expect("Can't find parameter");
let ParameterValueBox::Numeric(value) = parameter.val() else {
dbg_panic("Unexpected parameter value");
};
let value: u64 = value
.clone()
.try_into()
.dbg_expect("Can't cast parameter value to usize");
value as usize
}

#[entrypoint]
pub fn migrate(_block_height: u64) -> MigrationResult {
let parameter_id: ParameterId = MAX_ACCOUNTS_PER_DOMAIN
.parse()
.dbg_expect("Valid parameter name");
let parameter = Parameter::new(parameter_id, Numeric::new(1, 0).into());
iroha_executor::add_parameter(&parameter)?;
Ok(())
}
33 changes: 33 additions & 0 deletions client/tests/integration/upgrade.rs
Expand Up @@ -113,6 +113,39 @@ fn executor_upgrade_should_run_migration() -> Result<()> {
Ok(())
}

#[test]
fn executor_upgrade_add_parameter() -> Result<()> {
let (_rt, _peer, client) = <PeerBuilder>::new().with_port(11_170).start_with_runtime();
wait_for_genesis_committed(&vec![client.clone()], 0);

upgrade_executor(
&client,
"tests/integration/smartcontracts/executor_add_parameter",
)?;

let domain_id = "test_domain".parse()?;
client.submit_blocking(Register::domain(Domain::new(domain_id)))?;

let account_id1 = "account1@test_domain".parse()?;
let (public_key1, _) = KeyPair::random().into_parts();

let account_id2 = "account2@test_domain".parse()?;
let (public_key2, _) = KeyPair::random().into_parts();
client.submit_blocking(Register::account(Account::new(account_id1, public_key1)))?;

// limit is 1, can't register second account
let register2 = Register::account(Account::new(account_id2, public_key2));
assert!(client.submit_blocking(register2.clone()).is_err());

let parameter = Parameter::from_str("?max_accounts_per_domain=10")?;
client.submit_blocking(SetParameter::new(parameter))?;

// limit was set to 10, second account can be registered
assert!(client.submit_blocking(register2).is_ok());

Ok(())
}

#[test]
fn migration_fail_should_not_cause_any_effects() {
let (_rt, _peer, client) = <PeerBuilder>::new().with_port(10_995).start_with_runtime();
Expand Down
Binary file modified configs/swarm/executor.wasm
Binary file not shown.
44 changes: 38 additions & 6 deletions core/src/smartcontracts/wasm.rs
Expand Up @@ -5,9 +5,7 @@
use std::borrow::Borrow;

use error::*;
use import::traits::{
ExecuteOperations as _, GetExecutorPayloads as _, SetPermissionTokenSchema as _,
};
use import::traits::{ExecuteOperations as _, GetExecutorPayloads as _, MigrateOperations as _};
use iroha_config::parameters::actual::WasmRuntime as Config;
use iroha_data_model::{
account::AccountId,
Expand Down Expand Up @@ -50,6 +48,7 @@ mod export {
pub const GET_VALIDATE_INSTRUCTION_PAYLOAD: &str = "get_validate_instruction_payload";
pub const GET_VALIDATE_QUERY_PAYLOAD: &str = "get_validate_query_payload";
pub const SET_PERMISSION_TOKEN_SCHEMA: &str = "set_permission_token_schema";
pub const ADD_PARAMETER: &str = "add_parameter";

pub const DBG: &str = "dbg";
pub const LOG: &str = "log";
Expand Down Expand Up @@ -104,9 +103,12 @@ mod import {
fn get_validate_query_payload(state: &S) -> Validate<QueryBox>;
}

pub trait SetPermissionTokenSchema<S> {
pub trait MigrateOperations<S> {
#[codec::wrap_trait_fn]
fn set_permission_token_schema(schema: PermissionTokenSchema, state: &mut S);

#[codec::wrap_trait_fn]
fn add_parameter(parameter: Parameter, state: &mut S) -> Result<(), String>;
}
}
}
Expand Down Expand Up @@ -1086,7 +1088,7 @@ trait FakeSetPermissionTokenSchema<S> {
const ENTRYPOINT_FN_NAME: &'static str;
}

impl<R, S> import::traits::SetPermissionTokenSchema<S> for R
impl<R, S> import::traits::MigrateOperations<S> for R
where
R: FakeSetPermissionTokenSchema<S>,
{
Expand All @@ -1097,6 +1099,14 @@ where
Self::ENTRYPOINT_FN_NAME
)
}

#[codec::wrap]
fn add_parameter(_parameter: Parameter, _state: &mut S) -> Result<(), String> {
panic!(
"Executor `{}()` entrypoint should not add parameter",
Self::ENTRYPOINT_FN_NAME
)
}
}

impl<'wrld, 'block, 'state> Runtime<state::executor::ValidateTransaction<'wrld, 'block, 'state>> {
Expand Down Expand Up @@ -1448,7 +1458,7 @@ impl<'wrld, 'block, 'state>
}

impl<'wrld, 'block, 'state>
import::traits::SetPermissionTokenSchema<state::executor::Migrate<'wrld, 'block, 'state>>
import::traits::MigrateOperations<state::executor::Migrate<'wrld, 'block, 'state>>
for Runtime<state::executor::Migrate<'wrld, 'block, 'state>>
{
#[codec::wrap]
Expand All @@ -1460,6 +1470,24 @@ impl<'wrld, 'block, 'state>

state.state.0.world.set_permission_token_schema(schema)
}

#[codec::wrap]
fn add_parameter(
parameter: Parameter,
state: &mut state::executor::Migrate<'wrld, 'block, 'state>,
) -> Result<(), String> {
debug!(%parameter, "Setting permission token schema");

let world = &mut state.state.0.world;
let parameter_id = parameter.id.clone();
if !world.parameters.insert(parameter) {
let message = format!("Parameter with id {parameter_id} already exists");
return Err(message);
}

world.emit_events(Some(ConfigurationEvent::Created(parameter_id)));
Ok(())
}
}

/// `Runtime` builder
Expand Down Expand Up @@ -1604,6 +1632,7 @@ impl<'wrld, 'block, 'state>
export::GET_VALIDATE_INSTRUCTION_PAYLOAD => |caller: ::wasmtime::Caller<state::executor::ValidateTransaction<'wrld, 'block, 'state>>| Runtime::get_validate_instruction_payload(caller),
export::GET_VALIDATE_QUERY_PAYLOAD => |caller: ::wasmtime::Caller<state::executor::ValidateTransaction<'wrld, 'block, 'state>>| Runtime::get_validate_query_payload(caller),
export::SET_PERMISSION_TOKEN_SCHEMA => |caller: ::wasmtime::Caller<state::executor::ValidateTransaction<'wrld, 'block, 'state>>, offset, len| Runtime::set_permission_token_schema(caller, offset, len),
export::ADD_PARAMETER => |caller: ::wasmtime::Caller<state::executor::ValidateTransaction<'wrld, 'block, 'state>>, offset, len| Runtime::add_parameter(caller, offset, len),
)?;
Ok(linker)
})
Expand Down Expand Up @@ -1632,6 +1661,7 @@ impl<'wrld, 'block, 'state>
export::GET_VALIDATE_INSTRUCTION_PAYLOAD => |caller: ::wasmtime::Caller<state::executor::ValidateInstruction<'wrld, 'block, 'state>>| Runtime::get_validate_instruction_payload(caller),
export::GET_VALIDATE_QUERY_PAYLOAD => |caller: ::wasmtime::Caller<state::executor::ValidateInstruction<'wrld, 'block, 'state>>| Runtime::get_validate_query_payload(caller),
export::SET_PERMISSION_TOKEN_SCHEMA => |caller: ::wasmtime::Caller<state::executor::ValidateInstruction<'wrld, 'block, 'state>>, offset, len| Runtime::set_permission_token_schema(caller, offset, len),
export::ADD_PARAMETER => |caller: ::wasmtime::Caller<state::executor::ValidateInstruction<'wrld, 'block, 'state>>, offset, len| Runtime::add_parameter(caller, offset, len),
)?;
Ok(linker)
})
Expand All @@ -1657,6 +1687,7 @@ impl<'wrld, S: StateReadOnly> RuntimeBuilder<state::executor::ValidateQuery<'wrl
export::GET_VALIDATE_INSTRUCTION_PAYLOAD => |caller: ::wasmtime::Caller<state::executor::ValidateQuery<'_, S>>| Runtime::get_validate_instruction_payload(caller),
export::GET_VALIDATE_QUERY_PAYLOAD => |caller: ::wasmtime::Caller<state::executor::ValidateQuery<'_, S>>| Runtime::get_validate_query_payload(caller),
export::SET_PERMISSION_TOKEN_SCHEMA => |caller: ::wasmtime::Caller<state::executor::ValidateQuery<'_, S>>, offset, len| Runtime::set_permission_token_schema(caller, offset, len),
export::ADD_PARAMETER => |caller: ::wasmtime::Caller<state::executor::ValidateQuery<'_, S>>, offset, len| Runtime::add_parameter(caller, offset, len),
)?;
Ok(linker)
})
Expand All @@ -1681,6 +1712,7 @@ impl<'wrld, 'block, 'state> RuntimeBuilder<state::executor::Migrate<'wrld, 'bloc
export::GET_VALIDATE_INSTRUCTION_PAYLOAD => |caller: ::wasmtime::Caller<state::executor::Migrate<'wrld, 'block, 'state>>| Runtime::get_validate_instruction_payload(caller),
export::GET_VALIDATE_QUERY_PAYLOAD => |caller: ::wasmtime::Caller<state::executor::Migrate<'wrld, 'block, 'state>>| Runtime::get_validate_query_payload(caller),
export::SET_PERMISSION_TOKEN_SCHEMA => |caller: ::wasmtime::Caller<state::executor::Migrate<'wrld, 'block, 'state>>, offset, len| Runtime::set_permission_token_schema(caller, offset, len),
export::ADD_PARAMETER => |caller: ::wasmtime::Caller<state::executor::Migrate<'wrld, 'block, 'state>>, offset, len| Runtime::add_parameter(caller, offset, len),
)?;
Ok(linker)
})
Expand Down
21 changes: 21 additions & 0 deletions smart_contract/executor/src/lib.rs
Expand Up @@ -94,6 +94,24 @@ pub fn set_permission_token_schema(schema: &data_model::permission::PermissionTo
unsafe { encode_and_execute(&schema, host::set_permission_token_schema) }
}

/// Add new [`Parameter`].
///
/// # Errors
///
/// - If parameter with same id already exists
///
/// # Traps
///
/// Host side will generate a trap if this function was not called from a
/// executor's `migrate()` entrypoint.
#[cfg(not(test))]
pub fn add_parameter(parameter: &Parameter) -> Result<(), alloc::string::String> {
// Safety: - ownership of the returned result is transferred into `_decode_from_raw`
unsafe {
decode_with_length_prefix_from_raw(encode_and_execute(parameter, host::add_parameter))
}
}

#[cfg(not(test))]
mod host {
#[link(wasm_import_module = "iroha")]
Expand Down Expand Up @@ -128,6 +146,9 @@ mod host {

/// Set new [`PermissionTokenSchema`].
pub(super) fn set_permission_token_schema(ptr: *const u8, len: usize);

/// Add new [`Parameter`]
pub(super) fn add_parameter(ptr: *const u8, len: usize) -> *const u8;
}
}

Expand Down

0 comments on commit 41c2be8

Please sign in to comment.