-
Notifications
You must be signed in to change notification settings - Fork 824
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(examples): Add metering example
- Loading branch information
Showing
3 changed files
with
137 additions
and
3 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,124 @@ | ||
//! Wasmer will let you easily run Wasm module in a Rust host. | ||
//! | ||
//! This example illustrates the basics of using Wasmer metering features: | ||
//! | ||
//! 1. How to enable metering in a module | ||
//! 2. How to meter a specific function call | ||
//! 3. How to make execution fails if cost exceeds a given limit | ||
//! | ||
//! You can run the example directly by executing in Wasmer root: | ||
//! | ||
//! ```shell | ||
//! cargo run --example metering --release --features "cranelift" | ||
//! ``` | ||
//! | ||
//! Ready? | ||
use anyhow::bail; | ||
use std::sync::Arc; | ||
use wasmer::wasmparser::Operator; | ||
use wasmer::CompilerConfig; | ||
use wasmer::{imports, wat2wasm, Instance, Module, Store}; | ||
use wasmer_compiler_cranelift::Cranelift; | ||
use wasmer_engine_jit::JIT; | ||
use wasmer_middlewares::Metering; | ||
|
||
fn main() -> anyhow::Result<()> { | ||
// Let's declare the Wasm module. | ||
// | ||
// We are using the text representation of the module here but you can also load `.wasm` | ||
// files using the `include_bytes!` macro. | ||
let wasm_bytes = wat2wasm( | ||
br#" | ||
(module | ||
(type $add_t (func (param i32) (result i32))) | ||
(func $add_one_f (type $add_t) (param $value i32) (result i32) | ||
local.get $value | ||
i32.const 1 | ||
i32.add) | ||
(export "add_one" (func $add_one_f))) | ||
"#, | ||
)?; | ||
|
||
// Let's define our cost function. | ||
// | ||
// This function will be called for each `Operator` encountered during | ||
// the Wasm module execution. It should return the cost of the operator | ||
// that it received as it first argument. | ||
let cost_function = |operator: &Operator| -> u64 { | ||
match operator { | ||
Operator::LocalGet { .. } | Operator::I32Const { .. } => 1, | ||
Operator::I32Add { .. } => 2, | ||
_ => 0, | ||
} | ||
}; | ||
|
||
// Now let's create our metering middleware. | ||
// | ||
// `Metering` need to be configured with a limit (the gas limit) and | ||
// a cost function. | ||
// | ||
// For each `Operator`, the metering middleware will call the cost | ||
// function and subtract the cost from the gas. | ||
let metering = Arc::new(Metering::new(10, cost_function)); | ||
let mut compiler_config = Cranelift::default(); | ||
compiler_config.push_middleware(metering.clone()); | ||
|
||
// Create a Store. | ||
// | ||
// We use our previously create compiler configuration | ||
// with the JIT engine. | ||
let store = Store::new(&JIT::new(&compiler_config).engine()); | ||
|
||
println!("Compiling module..."); | ||
// Let's compile the Wasm module. | ||
let module = Module::new(&store, wasm_bytes)?; | ||
|
||
// Create an empty import object. | ||
let import_object = imports! {}; | ||
|
||
println!("Instantiating module..."); | ||
// Let's instantiate the Wasm module. | ||
let instance = Instance::new(&module, &import_object)?; | ||
|
||
// We now have an instance ready to be used. | ||
// | ||
// Our module exports a single `add_one` function. We want to | ||
// measure the cost of executing this function. | ||
let add_one = instance | ||
.exports | ||
.get_function("add_one")? | ||
.native::<i32, i32>()?; | ||
|
||
println!("Calling `add_one` function once..."); | ||
add_one.call(1)?; | ||
|
||
// TODO: display remaining gas points | ||
println!("{:?}", metering.get_remaining_points(&instance)); | ||
|
||
println!("Calling `add_one` function twice..."); | ||
add_one.call(1)?; | ||
|
||
// Because calling our `add_one` function consumes 4 gas points, | ||
// calling it a third time will fail: we already consume 8 gas | ||
// points, there are only two remaining. | ||
println!("Calling `add_one` function thrice..."); | ||
match add_one.call(1) { | ||
Ok(result) => { | ||
bail!( | ||
"Expected failure while calling `add_one`, found: {}", | ||
result | ||
); | ||
} | ||
Err(_) => { | ||
println!("Calling `add_one` failed: not enough gas points remaining."); | ||
|
||
Ok(()) | ||
} | ||
} | ||
} | ||
|
||
#[test] | ||
fn test_exported_function() -> Result<(), Box<dyn std::error::Error>> { | ||
main() | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters