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

[contracts] make debug_message execution outcome invariant to node debug logging setting #13197

Merged
merged 17 commits into from
Feb 14, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
57 changes: 19 additions & 38 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion client/executor/wasmtime/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ wasmtime = { version = "5.0.0", default-features = false, features = [
"jitdump",
"parallel-compilation",
"pooling-allocator"
] }
], git = "https://github.com/paritytech/wasmtime.git", branch = "v5.0.0_lto_fix" }
anyhow = "1.0.68"
sc-allocator = { version = "4.1.0-dev", path = "../../allocator" }
sc-executor-common = { version = "0.10.0-dev", path = "../common" }
Expand Down
24 changes: 17 additions & 7 deletions frame/contracts/fixtures/debug_message_invalid_utf8.wat
Original file line number Diff line number Diff line change
@@ -1,18 +1,28 @@
;; Emit a "Hello World!" debug message
;; Emit a debug message with an invalid utf-8 code
(module
(import "seal0" "seal_debug_message" (func $seal_debug_message (param i32 i32) (result i32)))
(import "env" "memory" (memory 1 1))

(data (i32.const 0) "\fc")

(func (export "call")
(call $seal_debug_message
(i32.const 0) ;; Pointer to the text buffer
(i32.const 12) ;; The size of the buffer
(func $assert_eq (param i32 i32)
(block $ok
(br_if $ok
(i32.eq (get_local 0) (get_local 1))
)
(unreachable)
)
;; the above call traps because we supplied invalid utf8
unreachable
)

(func (export "call")
(call $assert_eq
(call $seal_debug_message
(i32.const 0) ;; Pointer to the text buffer
(i32.const 12) ;; The size of the buffer
)
(i32.const 0) ;; Success return code
)
)

(func (export "deploy"))
)
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
(i32.const 0) ;; Pointer to the text buffer
(i32.const 12) ;; The size of the buffer
)
(i32.const 9) ;; LoggingDisabled return code
(i32.const 0) ;; Success return code
)
)

Expand Down
73 changes: 66 additions & 7 deletions frame/contracts/src/benchmarking/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -910,13 +910,12 @@ benchmarks! {
let origin = RawOrigin::Signed(instance.caller.clone());
}: call(origin, instance.addr, 0u32.into(), Weight::MAX, None, vec![])

// The size of the supplied message does not influence the weight because as it is never
// processed during on-chain execution: It is only ever read during debugging which happens
// when the contract is called as RPC where weights do not matter.
// Benchmark debug_message call with zero input data.
// Whereas this function is used in RPC mode only, it still should be secured
// against an excessive use.
#[pov_mode = Ignored]
seal_debug_message {
let r in 0 .. API_BENCHMARK_BATCHES;
let max_bytes = code::max_pages::<T>() * 64 * 1024;
let code = WasmModule::<T>::from(ModuleDefinition {
memory: Some(ImportedMemory { min_pages: 1, max_pages: 1 }),
imported_functions: vec![ImportedFunction {
Expand All @@ -927,15 +926,75 @@ benchmarks! {
}],
call_body: Some(body::repeated(r * API_BENCHMARK_BATCH_SIZE, &[
Instruction::I32Const(0), // value_ptr
Instruction::I32Const(max_bytes as i32), // value_len
Instruction::I32Const(0), // value_len
Instruction::Call(0),
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![])
}: {
<Contracts<T>>::bare_call(
instance.caller,
instance.account_id,
0u32.into(),
Weight::MAX,
None,
vec![],
true,
Determinism::Deterministic,
)
.result?;
}

seal_debug_message_per_kb {
// Vary size of input in kilobytes up to maximum allowed contract memory
// or maximum allowed debug buffer size, whichever is less.
let i in 0 .. (T::Schedule::get().limits.memory_pages * 64).min(T::MaxDebugBufferLen::get() / 1024);
// We benchmark versus messages containing printable ASCII codes.
// About 1Kb goes to the instrumented contract code instructions,
// whereas all the space left we use for the initialization of the debug messages data.
let message = (0 .. T::MaxCodeLen::get() - 1024).zip((32..127).cycle()).map(|i| i.1).collect::<Vec<_>>();
let code = WasmModule::<T>::from(ModuleDefinition {
memory: Some(ImportedMemory {
min_pages: T::Schedule::get().limits.memory_pages,
max_pages: T::Schedule::get().limits.memory_pages,
}),
imported_functions: vec![ImportedFunction {
module: "seal0",
name: "seal_debug_message",
params: vec![ValueType::I32, ValueType::I32],
return_type: Some(ValueType::I32),
}],
data_segments: vec![
DataSegment {
offset: 0,
value: message,
},
],
call_body: Some(body::plain(vec![
Instruction::I32Const(0), // value_ptr
Instruction::I32Const((i * 1024) as i32), // value_len increments by i Kb
Instruction::Call(0),
Instruction::Drop,
Instruction::End,
])),
..Default::default()
});
let instance = Contract::<T>::new(code, vec![])?;
}: {
<Contracts<T>>::bare_call(
instance.caller,
instance.account_id,
0u32.into(),
Weight::MAX,
None,
vec![],
true,
Determinism::Deterministic,
)
.result?;
}

// Only the overhead of calling the function itself with minimal arguments.
// The contract is a bit more complex because it needs to use different keys in order
Expand Down
Loading