Skip to content
This repository has been archived by the owner on Nov 15, 2023. It is now read-only.

Commit

Permalink
Delegate update take 2
Browse files Browse the repository at this point in the history
  • Loading branch information
pgherveou committed Jul 4, 2023
1 parent e04058e commit 1bc77d2
Show file tree
Hide file tree
Showing 13 changed files with 963 additions and 38 deletions.
3 changes: 3 additions & 0 deletions bin/node/runtime/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1223,6 +1223,7 @@ parameter_types! {
pub const DepositPerByte: Balance = deposit(0, 1);
pub const DefaultDepositLimit: Balance = deposit(1024, 1024 * 1024);
pub Schedule: pallet_contracts::Schedule<Runtime> = Default::default();
pub CodeHashLockupDepositPercent: Perbill = Perbill::from_percent(30);
}

impl pallet_contracts::Config for Runtime {
Expand Down Expand Up @@ -1255,6 +1256,8 @@ impl pallet_contracts::Config for Runtime {
type Migrations = ();
#[cfg(feature = "runtime-benchmarks")]
type Migrations = (NoopMigration<1>, NoopMigration<2>);
type MaxDelegateDependencies = ConstU32<32>;
type CodeHashLockupDepositPercent = CodeHashLockupDepositPercent;
}

impl pallet_sudo::Config for Runtime {
Expand Down
118 changes: 118 additions & 0 deletions frame/contracts/fixtures/add_remove_delegate_dependency.wat
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
;; This contract tests the behavior of adding / removing delegate_dependencies when delegate calling into a contract.
(module
(import "seal0" "add_delegate_dependency" (func $add_delegate_dependency (param i32) (result i32)))
(import "seal0" "remove_delegate_dependency" (func $remove_delegate_dependency (param i32) (result i32)))
(import "seal0" "seal_input" (func $seal_input (param i32 i32)))
(import "seal0" "seal_terminate" (func $seal_terminate (param i32 i32)))
(import "seal0" "seal_delegate_call" (func $seal_delegate_call (param i32 i32 i32 i32 i32 i32) (result i32)))
(import "env" "memory" (memory 1 1))

;; [100, 132) Address of Alice
(data (i32.const 100)
"\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01"
"\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01"
)

(func $assert (param i32)
(block $ok
(br_if $ok
(get_local 0)
)
(unreachable)
)
)

;; This function loads input data and performs the action specified.
;; The first 4 bytes of the input specify the action to perform.
;; The next 32 bytes specify the code hash to use when calling add_delegate_dependency or remove_delegate_dependency.
;; Actions are:
;; 1: call add_delegate_dependency
;; 2: call remove_delegate_dependency.
;; 3: call terminate.
;; Any other value is a no-op.
(func $load_input
(local $action i32)
(local $code_hash_ptr i32)

;; Store available input size at offset 0.
(i32.store (i32.const 0) (i32.const 512))

;; Read input data.
(call $seal_input (i32.const 4) (i32.const 0))

;; Input data layout.
;; [0..4) - size of the call
;; [4..8) - action to perform
;; [8..42) - code hash of the callee
(set_local $action (i32.load (i32.const 4)))
(set_local $code_hash_ptr (i32.const 8))

;; Assert input size == 36 (4 for action + 32 for code_hash).
(call $assert
(i32.eq
(i32.load (i32.const 0))
(i32.const 36)
)
)

;; Call add_delegate_dependency when action == 1.
(if (i32.eq (get_local $action) (i32.const 1))
(then
(call $assert (i32.eqz
(call $add_delegate_dependency
(get_local $code_hash_ptr)
)
))
)
(else)
)

;; Call remove_delegate_dependency when action == 2.
(if (i32.eq (get_local $action) (i32.const 2))
(then
(call $assert (i32.eqz
(call $remove_delegate_dependency
(get_local $code_hash_ptr)
)
))
)
(else)
)

;; Call terminate when action == 3.
(if (i32.eq (get_local $action) (i32.const 3))
(then
(call $seal_terminate
(i32.const 100) ;; Pointer to beneficiary address
(i32.const 32) ;; Length of beneficiary address
)
(unreachable) ;; seal_terminate never returns
)
(else)
)
)

(func (export "deploy")
(call $load_input)
)

(func (export "call")
(call $load_input)

;; Delegate call into passed code hash.
(call $assert
(i32.eq
(call $seal_delegate_call
(i32.const 0) ;; Set no call flags.
(i32.const 8) ;; Pointer to "callee" code_hash.
(i32.const 0) ;; Input is ignored.
(i32.const 0) ;; Length of the input.
(i32.const 4294967295) ;; u32 max sentinel value: do not copy output.
(i32.const 0) ;; Length is ignored in this case.
)
(i32.const 0)
)
)
)

)
140 changes: 134 additions & 6 deletions frame/contracts/src/benchmarking/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -257,6 +257,19 @@ benchmarks! {
m.step();
}

// This benchmarks the v13 migration step.
#[pov_mode = Measured]
v13_migration_step {
let contract = <Contract<T>>::with_caller(
whitelisted_caller(), WasmModule::dummy(), vec![],
)?;

v13::store_old_contrat_info::<T>(contract.account_id.clone(), contract.info()?);
let mut m = v13::Migration::<T>::default();
}: {
m.step();
}

// This benchmarks the weight of executing Migration::migrate to execute a noop migration.
#[pov_mode = Measured]
migration_noop {
Expand Down Expand Up @@ -832,20 +845,49 @@ benchmarks! {
let beneficiary = account::<T::AccountId>("beneficiary", 0, 0);
let beneficiary_bytes = beneficiary.encode();
let beneficiary_len = beneficiary_bytes.len();

// Maximize the delegate_dependencies to account for the worst-case scenario.
let code_hashes = (0..T::MaxDelegateDependencies::get())
.map(|i| {
let new_code = WasmModule::<T>::dummy_with_bytes(65 + i);
Contracts::<T>::store_code_raw(new_code.code, whitelisted_caller())?;
Ok(new_code.hash)
})
.collect::<Result<Vec<_>, &'static str>>()?;
let code_hash_len = code_hashes.get(0).map(|x| x.encode().len()).unwrap_or(0);
let code_hashes_bytes = code_hashes.iter().flat_map(|x| x.encode()).collect::<Vec<_>>();

let code = WasmModule::<T>::from(ModuleDefinition {
memory: Some(ImportedMemory::max::<T>()),
imported_functions: vec![ImportedFunction {
module: "seal0",
name: "seal_terminate",
params: vec![ValueType::I32, ValueType::I32],
return_type: None,
}],
imported_functions: vec![
ImportedFunction {
module: "seal0",
name: "seal_terminate",
params: vec![ValueType::I32, ValueType::I32],
return_type: None,
},
ImportedFunction {
module: "seal0",
name: "add_delegate_dependency",
params: vec![ValueType::I32],
return_type: Some(ValueType::I32),
}
],
data_segments: vec![
DataSegment {
offset: 0,
value: beneficiary_bytes,
},
DataSegment {
offset: beneficiary_len as u32,
value: code_hashes_bytes,
},
],
deploy_body: Some(body::repeated_dyn(r, vec![
Counter(beneficiary_len as u32, code_hash_len as u32), // code_hash_ptr
Regular(Instruction::Call(1)),
Regular(Instruction::Drop),
])),
call_body: Some(body::repeated(r, &[
Instruction::I32Const(0), // beneficiary_ptr
Instruction::I32Const(beneficiary_len as i32), // beneficiary_len
Expand Down Expand Up @@ -2327,6 +2369,92 @@ benchmarks! {
let origin = RawOrigin::Signed(instance.caller.clone());
}: call(origin, instance.addr, 0u32.into(), Weight::MAX, None, vec![])

#[pov_mode = Measured]
add_delegate_dependency {
let r in 0 .. T::MaxDelegateDependencies::get();
let code_hashes = (0..r)
.map(|i| {
let new_code = WasmModule::<T>::dummy_with_bytes(65 + i);
Contracts::<T>::store_code_raw(new_code.code, whitelisted_caller())?;
Ok(new_code.hash)
})
.collect::<Result<Vec<_>, &'static str>>()?;
let code_hash_len = code_hashes.get(0).map(|x| x.encode().len()).unwrap_or(0);
let code_hashes_bytes = code_hashes.iter().flat_map(|x| x.encode()).collect::<Vec<_>>();

let code = WasmModule::<T>::from(ModuleDefinition {
memory: Some(ImportedMemory::max::<T>()),
imported_functions: vec![ImportedFunction {
module: "seal0",
name: "add_delegate_dependency",
params: vec![ValueType::I32],
return_type: Some(ValueType::I32),
}],
data_segments: vec![
DataSegment {
offset: 0,
value: code_hashes_bytes,
},
],
call_body: Some(body::repeated_dyn(r, vec![
Counter(0, code_hash_len as u32), // code_hash_ptr
Regular(Instruction::Call(0)),
Regular(Instruction::Drop),
])),
.. Default::default()
});
let instance = Contract::<T>::new(code, vec![])?;
let origin = RawOrigin::Signed(instance.caller.clone());
}: call(origin, instance.addr, 0u32.into(), Weight::MAX, None, vec![])

remove_delegate_dependency {
let r in 0 .. T::MaxDelegateDependencies::get();
let code_hashes = (0..r)
.map(|i| {
let new_code = WasmModule::<T>::dummy_with_bytes(65 + i);
Contracts::<T>::store_code_raw(new_code.code, whitelisted_caller())?;
Ok(new_code.hash)
})
.collect::<Result<Vec<_>, &'static str>>()?;

let code_hash_len = code_hashes.get(0).map(|x| x.encode().len()).unwrap_or(0);
let code_hashes_bytes = code_hashes.iter().flat_map(|x| x.encode()).collect::<Vec<_>>();

let code = WasmModule::<T>::from(ModuleDefinition {
memory: Some(ImportedMemory::max::<T>()),
imported_functions: vec![ImportedFunction {
module: "seal0",
name: "remove_delegate_dependency",
params: vec![ValueType::I32],
return_type: Some(ValueType::I32),
}, ImportedFunction {
module: "seal0",
name: "add_delegate_dependency",
params: vec![ValueType::I32],
return_type: Some(ValueType::I32),
}],
data_segments: vec![
DataSegment {
offset: 0,
value: code_hashes_bytes,
},
],
deploy_body: Some(body::repeated_dyn(r, vec![
Counter(0, code_hash_len as u32), // code_hash_ptr
Regular(Instruction::Call(1)),
Regular(Instruction::Drop),
])),
call_body: Some(body::repeated_dyn(r, vec![
Counter(0, code_hash_len as u32), // code_hash_ptr
Regular(Instruction::Call(0)),
Regular(Instruction::Drop),
])),
.. Default::default()
});
let instance = Contract::<T>::new(code, vec![])?;
let origin = RawOrigin::Signed(instance.caller.clone());
}: call(origin, instance.addr, 0u32.into(), Weight::MAX, None, vec![])

#[pov_mode = Measured]
seal_reentrance_count {
let r in 0 .. API_BENCHMARK_RUNS;
Expand Down
Loading

0 comments on commit 1bc77d2

Please sign in to comment.