Use u64 for gas counter (#30)

* Use `u64` for gas counter

* Update doc
This commit is contained in:
Alexander Theißen
2022-09-11 14:36:06 +02:00
committed by GitHub
parent c2db4b8365
commit 0229f865b6
9 changed files with 85 additions and 63 deletions
+10
View File
@@ -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
View File
@@ -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
View File
@@ -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
+7 -7
View File
@@ -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);
+3 -3
View File
@@ -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
+3 -3
View File
@@ -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
+4 -4
View File
@@ -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
+5 -5
View File
@@ -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
+2 -2
View File
@@ -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