Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[refactor] #3901: Add method to define parameter in executor migrate entrypoint #4513

Closed
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
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