mirror of
https://github.com/pezkuwichain/wasm-instrument.git
synced 2026-04-29 12:37:56 +00:00
committed by
GitHub
parent
c2db4b8365
commit
0229f865b6
@@ -16,7 +16,17 @@ The interface provided to smart contracts will adhere to semver with one excepti
|
||||
major version bumps will be backwards compatible with regard to already deployed contracts.
|
||||
In other words: Upgrading this pallet will not break pre-existing contracts.
|
||||
|
||||
## [v0.3.0]
|
||||
|
||||
### Changed
|
||||
|
||||
- Use 64bit arithmetic for per-block gas counter
|
||||
[#30](https://github.com/paritytech/wasm-instrument/pull/30)
|
||||
|
||||
## [v0.2.0] 2022-06-06
|
||||
|
||||
### Changed
|
||||
|
||||
- Adjust debug information (if already parsed) when injecting gas metering
|
||||
[#16](https://github.com/paritytech/wasm-instrument/pull/16)
|
||||
|
||||
|
||||
+2
-1
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "wasm-instrument"
|
||||
version = "0.2.0"
|
||||
version = "0.3.0"
|
||||
edition = "2021"
|
||||
rust-version = "1.56.1"
|
||||
authors = ["Parity Technologies <admin@parity.io>"]
|
||||
@@ -27,6 +27,7 @@ parity-wasm = { version = "0.45", default-features = false }
|
||||
binaryen = "0.12"
|
||||
criterion = "0.3"
|
||||
diff = "0.1"
|
||||
pretty_assertions = "1"
|
||||
rand = "0.8"
|
||||
wat = "1"
|
||||
wasmparser = "0.90"
|
||||
|
||||
+49
-38
@@ -105,7 +105,7 @@ impl Rules for ConstantCostRules {
|
||||
/// imported gas metering function.
|
||||
///
|
||||
/// The output module imports a function "gas" from the specified module with type signature
|
||||
/// [i32] -> []. The argument is the amount of gas required to continue execution. The external
|
||||
/// [i64] -> []. The argument is the amount of gas required to continue execution. The external
|
||||
/// function is meant to keep track of the total amount of gas used and trap or otherwise halt
|
||||
/// execution of the runtime if the gas usage exceeds some allowed limit.
|
||||
///
|
||||
@@ -144,7 +144,7 @@ pub fn inject<R: Rules>(
|
||||
// Injecting gas counting external
|
||||
let mut mbuilder = builder::from_module(module);
|
||||
let import_sig =
|
||||
mbuilder.push_signature(builder::signature().with_param(ValueType::I32).build_sig());
|
||||
mbuilder.push_signature(builder::signature().with_param(ValueType::I64).build_sig());
|
||||
|
||||
mbuilder.push_import(
|
||||
builder::import()
|
||||
@@ -284,7 +284,7 @@ struct MeteredBlock {
|
||||
/// Index of the first instruction (aka `Opcode`) in the block.
|
||||
start_pos: usize,
|
||||
/// Sum of costs of all instructions until end of the block.
|
||||
cost: u32,
|
||||
cost: u64,
|
||||
}
|
||||
|
||||
/// Counter is used to manage state during the gas metering algorithm implemented by
|
||||
@@ -375,7 +375,8 @@ impl Counter {
|
||||
.expect("last_index is greater than 0; last_index is stack size - 1; qed");
|
||||
let prev_metered_block = &mut prev_control_block.active_metered_block;
|
||||
if closing_metered_block.start_pos == prev_metered_block.start_pos {
|
||||
prev_metered_block.cost += closing_metered_block.cost;
|
||||
prev_metered_block.cost =
|
||||
prev_metered_block.cost.checked_add(closing_metered_block.cost).ok_or(())?;
|
||||
return Ok(())
|
||||
}
|
||||
}
|
||||
@@ -425,7 +426,7 @@ impl Counter {
|
||||
/// Increment the cost of the current block by the specified value.
|
||||
fn increment(&mut self, val: u32) -> Result<(), ()> {
|
||||
let top_block = self.active_metered_block()?;
|
||||
top_block.cost = top_block.cost.checked_add(val).ok_or(())?;
|
||||
top_block.cost = top_block.cost.checked_add(val.into()).ok_or(())?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
@@ -465,8 +466,9 @@ fn add_grow_counter<R: Rules>(
|
||||
.with_instructions(elements::Instructions::new(vec![
|
||||
GetLocal(0),
|
||||
GetLocal(0),
|
||||
I32Const(cost as i32),
|
||||
I32Mul,
|
||||
I64ExtendUI32,
|
||||
I64Const(i64::from(cost)),
|
||||
I64Mul,
|
||||
// todo: there should be strong guarantee that it does not return anything on
|
||||
// stack?
|
||||
Call(gas_func),
|
||||
@@ -583,7 +585,7 @@ fn insert_metering_calls(
|
||||
// If there the next block starts at this position, inject metering instructions.
|
||||
let used_block = if let Some(block) = block_iter.peek() {
|
||||
if block.start_pos == original_pos {
|
||||
new_instrs.push(I32Const(block.cost as i32));
|
||||
new_instrs.push(I64Const(block.cost as i64));
|
||||
new_instrs.push(Call(gas_func));
|
||||
true
|
||||
} else {
|
||||
@@ -612,6 +614,7 @@ fn insert_metering_calls(
|
||||
mod tests {
|
||||
use super::*;
|
||||
use parity_wasm::{builder, elements, elements::Instruction::*, serialize};
|
||||
use pretty_assertions::assert_eq;
|
||||
|
||||
fn get_function_body(
|
||||
module: &elements::Module,
|
||||
@@ -639,12 +642,20 @@ mod tests {
|
||||
|
||||
assert_eq!(
|
||||
get_function_body(&injected_module, 0).unwrap(),
|
||||
&vec![I32Const(2), Call(0), GetGlobal(0), Call(2), End][..]
|
||||
&vec![I64Const(2), Call(0), GetGlobal(0), Call(2), End][..]
|
||||
);
|
||||
assert_eq!(
|
||||
get_function_body(&injected_module, 1).unwrap(),
|
||||
&vec![GetLocal(0), GetLocal(0), I32Const(10000), I32Mul, Call(0), GrowMemory(0), End,]
|
||||
[..]
|
||||
&vec![
|
||||
GetLocal(0),
|
||||
GetLocal(0),
|
||||
I64ExtendUI32,
|
||||
I64Const(10000),
|
||||
I64Mul,
|
||||
Call(0),
|
||||
GrowMemory(0),
|
||||
End,
|
||||
][..]
|
||||
);
|
||||
|
||||
let binary = serialize(injected_module).expect("serialization failed");
|
||||
@@ -667,7 +678,7 @@ mod tests {
|
||||
|
||||
assert_eq!(
|
||||
get_function_body(&injected_module, 0).unwrap(),
|
||||
&vec![I32Const(2), Call(0), GetGlobal(0), GrowMemory(0), End][..]
|
||||
&vec![I64Const(2), Call(0), GetGlobal(0), GrowMemory(0), End][..]
|
||||
);
|
||||
|
||||
assert_eq!(injected_module.functions_space(), 2);
|
||||
@@ -719,17 +730,17 @@ mod tests {
|
||||
assert_eq!(
|
||||
get_function_body(&injected_module, 1).unwrap(),
|
||||
&vec![
|
||||
I32Const(3),
|
||||
I64Const(3),
|
||||
Call(0),
|
||||
Call(1),
|
||||
If(elements::BlockType::NoResult),
|
||||
I32Const(3),
|
||||
I64Const(3),
|
||||
Call(0),
|
||||
Call(1),
|
||||
Call(1),
|
||||
Call(1),
|
||||
Else,
|
||||
I32Const(2),
|
||||
I64Const(2),
|
||||
Call(0),
|
||||
Call(1),
|
||||
Call(1),
|
||||
@@ -775,7 +786,7 @@ mod tests {
|
||||
expected = r#"
|
||||
(module
|
||||
(func (result i32)
|
||||
(call 0 (i32.const 1))
|
||||
(call 0 (i64.const 1))
|
||||
(get_global 0)))
|
||||
"#
|
||||
}
|
||||
@@ -795,7 +806,7 @@ mod tests {
|
||||
expected = r#"
|
||||
(module
|
||||
(func (result i32)
|
||||
(call 0 (i32.const 6))
|
||||
(call 0 (i64.const 6))
|
||||
(get_global 0)
|
||||
(block
|
||||
(get_global 0)
|
||||
@@ -824,16 +835,16 @@ mod tests {
|
||||
expected = r#"
|
||||
(module
|
||||
(func (result i32)
|
||||
(call 0 (i32.const 3))
|
||||
(call 0 (i64.const 3))
|
||||
(get_global 0)
|
||||
(if
|
||||
(then
|
||||
(call 0 (i32.const 3))
|
||||
(call 0 (i64.const 3))
|
||||
(get_global 0)
|
||||
(get_global 0)
|
||||
(get_global 0))
|
||||
(else
|
||||
(call 0 (i32.const 2))
|
||||
(call 0 (i64.const 2))
|
||||
(get_global 0)
|
||||
(get_global 0)))
|
||||
(get_global 0)))
|
||||
@@ -857,13 +868,13 @@ mod tests {
|
||||
expected = r#"
|
||||
(module
|
||||
(func (result i32)
|
||||
(call 0 (i32.const 6))
|
||||
(call 0 (i64.const 6))
|
||||
(get_global 0)
|
||||
(block
|
||||
(get_global 0)
|
||||
(drop)
|
||||
(br 0)
|
||||
(call 0 (i32.const 2))
|
||||
(call 0 (i64.const 2))
|
||||
(get_global 0)
|
||||
(drop))
|
||||
(get_global 0)))
|
||||
@@ -891,18 +902,18 @@ mod tests {
|
||||
expected = r#"
|
||||
(module
|
||||
(func (result i32)
|
||||
(call 0 (i32.const 5))
|
||||
(call 0 (i64.const 5))
|
||||
(get_global 0)
|
||||
(block
|
||||
(get_global 0)
|
||||
(if
|
||||
(then
|
||||
(call 0 (i32.const 4))
|
||||
(call 0 (i64.const 4))
|
||||
(get_global 0)
|
||||
(get_global 0)
|
||||
(drop)
|
||||
(br_if 1)))
|
||||
(call 0 (i32.const 2))
|
||||
(call 0 (i64.const 2))
|
||||
(get_global 0)
|
||||
(drop))
|
||||
(get_global 0)))
|
||||
@@ -933,18 +944,18 @@ mod tests {
|
||||
expected = r#"
|
||||
(module
|
||||
(func (result i32)
|
||||
(call 0 (i32.const 3))
|
||||
(call 0 (i64.const 3))
|
||||
(get_global 0)
|
||||
(loop
|
||||
(call 0 (i32.const 4))
|
||||
(call 0 (i64.const 4))
|
||||
(get_global 0)
|
||||
(if
|
||||
(then
|
||||
(call 0 (i32.const 2))
|
||||
(call 0 (i64.const 2))
|
||||
(get_global 0)
|
||||
(br_if 0))
|
||||
(else
|
||||
(call 0 (i32.const 4))
|
||||
(call 0 (i64.const 4))
|
||||
(get_global 0)
|
||||
(get_global 0)
|
||||
(drop)
|
||||
@@ -969,13 +980,13 @@ mod tests {
|
||||
expected = r#"
|
||||
(module
|
||||
(func (result i32)
|
||||
(call 0 (i32.const 2))
|
||||
(call 0 (i64.const 2))
|
||||
(get_global 0)
|
||||
(if
|
||||
(then
|
||||
(call 0 (i32.const 1))
|
||||
(call 0 (i64.const 1))
|
||||
(return)))
|
||||
(call 0 (i32.const 1))
|
||||
(call 0 (i64.const 1))
|
||||
(get_global 0)))
|
||||
"#
|
||||
}
|
||||
@@ -998,18 +1009,18 @@ mod tests {
|
||||
expected = r#"
|
||||
(module
|
||||
(func (result i32)
|
||||
(call 0 (i32.const 5))
|
||||
(call 0 (i64.const 5))
|
||||
(get_global 0)
|
||||
(block
|
||||
(get_global 0)
|
||||
(if
|
||||
(then
|
||||
(call 0 (i32.const 1))
|
||||
(call 0 (i64.const 1))
|
||||
(br 1))
|
||||
(else
|
||||
(call 0 (i32.const 1))
|
||||
(call 0 (i64.const 1))
|
||||
(br 0)))
|
||||
(call 0 (i32.const 2))
|
||||
(call 0 (i64.const 2))
|
||||
(get_global 0)
|
||||
(drop))
|
||||
(get_global 0)))
|
||||
@@ -1031,9 +1042,9 @@ mod tests {
|
||||
expected = r#"
|
||||
(module
|
||||
(func
|
||||
(call 0 (i32.const 2))
|
||||
(call 0 (i64.const 2))
|
||||
(loop
|
||||
(call 0 (i32.const 1))
|
||||
(call 0 (i64.const 1))
|
||||
(br 0)
|
||||
)
|
||||
unreachable
|
||||
|
||||
@@ -23,10 +23,10 @@ struct ControlFlowNode {
|
||||
first_instr_pos: Option<usize>,
|
||||
|
||||
/// The actual gas cost of executing all instructions in the basic block.
|
||||
actual_cost: u32,
|
||||
actual_cost: u64,
|
||||
|
||||
/// The amount of gas charged by the injected metering instructions within this basic block.
|
||||
charged_cost: u32,
|
||||
charged_cost: u64,
|
||||
|
||||
/// Whether there are any other nodes in the graph that loop back to this one. Every cycle in
|
||||
/// the control flow graph contains at least one node with this flag set.
|
||||
@@ -68,10 +68,10 @@ impl ControlFlowGraph {
|
||||
}
|
||||
|
||||
fn increment_actual_cost(&mut self, node_id: NodeId, cost: u32) {
|
||||
self.get_node_mut(node_id).actual_cost += cost;
|
||||
self.get_node_mut(node_id).actual_cost += u64::from(cost);
|
||||
}
|
||||
|
||||
fn increment_charged_cost(&mut self, node_id: NodeId, cost: u32) {
|
||||
fn increment_charged_cost(&mut self, node_id: NodeId, cost: u64) {
|
||||
self.get_node_mut(node_id).charged_cost += cost;
|
||||
}
|
||||
|
||||
@@ -267,9 +267,9 @@ fn validate_graph_gas_costs(graph: &ControlFlowGraph) -> bool {
|
||||
fn visit(
|
||||
graph: &ControlFlowGraph,
|
||||
node_id: NodeId,
|
||||
mut total_actual: u32,
|
||||
mut total_charged: u32,
|
||||
loop_costs: &mut Map<NodeId, (u32, u32)>,
|
||||
mut total_actual: u64,
|
||||
mut total_charged: u64,
|
||||
loop_costs: &mut Map<NodeId, (u64, u64)>,
|
||||
) -> bool {
|
||||
let node = graph.get_node(node_id);
|
||||
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
(module
|
||||
(type (;0;) (func (result i32)))
|
||||
(type (;1;) (func (param i32)))
|
||||
(type (;1;) (func (param i64)))
|
||||
(import "env" "gas" (func (;0;) (type 1)))
|
||||
(func $fibonacci_with_break (;1;) (type 0) (result i32)
|
||||
(local i32 i32)
|
||||
i32.const 13
|
||||
i64.const 13
|
||||
call 0
|
||||
block ;; label = @1
|
||||
i32.const 0
|
||||
@@ -18,7 +18,7 @@
|
||||
local.set 1
|
||||
i32.const 1
|
||||
br_if 0 (;@1;)
|
||||
i32.const 5
|
||||
i64.const 5
|
||||
call 0
|
||||
local.get 0
|
||||
local.get 1
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
(module
|
||||
(type (;0;) (func (param i32 i32) (result i32)))
|
||||
(type (;1;) (func (param i32)))
|
||||
(type (;1;) (func (param i64)))
|
||||
(import "env" "gas" (func (;0;) (type 1)))
|
||||
(func $add_locals (;1;) (type 0) (param $x i32) (param $y i32) (result i32)
|
||||
(local i32)
|
||||
i32.const 5
|
||||
i64.const 5
|
||||
call 0
|
||||
local.get $x
|
||||
local.get $y
|
||||
@@ -13,7 +13,7 @@
|
||||
local.get 2
|
||||
)
|
||||
(func $add (;2;) (type 0) (param i32 i32) (result i32)
|
||||
i32.const 3
|
||||
i64.const 3
|
||||
call 0
|
||||
local.get 0
|
||||
local.get 1
|
||||
|
||||
@@ -1,19 +1,19 @@
|
||||
(module
|
||||
(type (;0;) (func (param i32) (result i32)))
|
||||
(type (;1;) (func (param i32)))
|
||||
(type (;1;) (func (param i64)))
|
||||
(import "env" "gas" (func (;0;) (type 1)))
|
||||
(func (;1;) (type 0) (param i32) (result i32)
|
||||
i32.const 2
|
||||
i64.const 2
|
||||
call 0
|
||||
i32.const 1
|
||||
if (result i32) ;; label = @1
|
||||
i32.const 3
|
||||
i64.const 3
|
||||
call 0
|
||||
local.get 0
|
||||
i32.const 1
|
||||
i32.add
|
||||
else
|
||||
i32.const 2
|
||||
i64.const 2
|
||||
call 0
|
||||
local.get 0
|
||||
i32.popcnt
|
||||
|
||||
@@ -1,16 +1,16 @@
|
||||
(module
|
||||
(type (;0;) (func))
|
||||
(type (;1;) (func (param i32)))
|
||||
(type (;1;) (func (param i64)))
|
||||
(import "env" "gas" (func (;0;) (type 1)))
|
||||
(func (;1;) (type 0)
|
||||
i32.const 2
|
||||
i64.const 2
|
||||
call 0
|
||||
i32.const 1
|
||||
if ;; label = @1
|
||||
i32.const 1
|
||||
i64.const 1
|
||||
call 0
|
||||
loop ;; label = @2
|
||||
i32.const 2
|
||||
i64.const 2
|
||||
call 0
|
||||
i32.const 123
|
||||
drop
|
||||
@@ -18,7 +18,7 @@
|
||||
end
|
||||
)
|
||||
(func (;2;) (type 0)
|
||||
i32.const 1
|
||||
i64.const 1
|
||||
call 0
|
||||
block ;; label = @1
|
||||
end
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
(module
|
||||
(type (;0;) (func (param i32 i32)))
|
||||
(type (;1;) (func))
|
||||
(type (;2;) (func (param i32)))
|
||||
(type (;2;) (func (param i64)))
|
||||
(import "env" "ext_return" (func $ext_return (;0;) (type 0)))
|
||||
(import "env" "memory" (memory (;0;) 1 1))
|
||||
(import "env" "gas" (func (;1;) (type 2)))
|
||||
(func $start (;2;) (type 1)
|
||||
i32.const 4
|
||||
i64.const 4
|
||||
call 1
|
||||
i32.const 8
|
||||
i32.const 4
|
||||
|
||||
Reference in New Issue
Block a user