Skip to content

Commit a9e6767

Browse files
authored
Merge pull request #12 from bandprotocol/refactor
Refactor
2 parents 03212a3 + ba5b7ce commit a9e6767

File tree

10 files changed

+1416
-694
lines changed

10 files changed

+1416
-694
lines changed

packages/vm/Cargo.toml

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "owasm-vm"
3-
version = "0.1.12"
3+
version = "0.2.0"
44
authors = ["Band Protocol <[email protected]>"]
55
edition = "2021"
66
license = "Apache-2.0"
@@ -11,11 +11,10 @@ readme = "README.md"
1111
[dependencies]
1212
assert_matches = "1.3.0"
1313
hex = "0.4"
14-
parity-wasm = "0.41"
15-
pwasm-utils = "0.12"
14+
wasm-instrument = "0.2.0"
1615
wasmer = { version = "2.3.0", default-features = false, features = ["singlepass", "compiler", "universal"] }
1716
wasmer-middlewares = "2.3.0"
1817
# owasm-crypto = { path = "../crypto", version = "0.1.13" }
1918
tempfile = "3.1.0"
2019
clru = "0.2.0"
21-
cosmwasm-vm = "0.13.2"
20+
sha2 = "0.9.1"

packages/vm/src/cache.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
1+
use crate::checksum::Checksum;
12
use crate::error::Error;
23

34
use clru::CLruCache;
4-
use cosmwasm_vm::Checksum;
55
use wasmer::{Instance, Module, Store};
66

77
#[derive(Debug, Default, Clone, Copy, PartialEq)]

packages/vm/src/calls.rs

Lines changed: 217 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,217 @@
1+
use crate::cache::Cache;
2+
use crate::error::Error;
3+
use crate::imports::create_import_object;
4+
use crate::store::make_store;
5+
use crate::vm::{Env, Environment};
6+
7+
use std::ptr::NonNull;
8+
use wasmer_middlewares::metering::{get_remaining_points, MeteringPoints};
9+
10+
pub fn run<E>(
11+
cache: &mut Cache,
12+
code: &[u8],
13+
gas_limit: u64,
14+
is_prepare: bool,
15+
env: E,
16+
) -> Result<u64, Error>
17+
where
18+
E: Env + 'static,
19+
{
20+
let owasm_env = Environment::new(env);
21+
let store = make_store();
22+
let import_object = create_import_object(&store, owasm_env.clone());
23+
24+
let instance = cache.get_instance(code, &store, &import_object)?;
25+
let instance_ptr = NonNull::from(&instance);
26+
owasm_env.set_wasmer_instance(Some(instance_ptr));
27+
owasm_env.set_gas_left(gas_limit);
28+
29+
// get function and exec
30+
let entry = if is_prepare { "prepare" } else { "execute" };
31+
let function = instance
32+
.exports
33+
.get_function(entry)
34+
.unwrap()
35+
.native::<(), ()>()
36+
.map_err(|_| Error::BadEntrySignatureError)?;
37+
38+
function.call().map_err(|runtime_err| {
39+
if let Ok(err) = runtime_err.downcast::<Error>() {
40+
return err.clone();
41+
}
42+
43+
match get_remaining_points(&instance) {
44+
MeteringPoints::Remaining(_) => Error::RuntimeError,
45+
MeteringPoints::Exhausted => Error::OutOfGasError,
46+
}
47+
})?;
48+
49+
match get_remaining_points(&instance) {
50+
MeteringPoints::Remaining(count) => Ok(gas_limit.saturating_sub(count)),
51+
MeteringPoints::Exhausted => Err(Error::OutOfGasError),
52+
}
53+
}
54+
55+
#[cfg(test)]
56+
mod test {
57+
use crate::cache::CacheOptions;
58+
59+
use super::*;
60+
use crate::compile::compile;
61+
use std::io::{Read, Write};
62+
use std::process::Command;
63+
use tempfile::NamedTempFile;
64+
65+
pub struct MockEnv {}
66+
67+
impl Env for MockEnv {
68+
fn get_span_size(&self) -> i64 {
69+
300
70+
}
71+
fn get_calldata(&self) -> Result<Vec<u8>, Error> {
72+
Ok(vec![1])
73+
}
74+
fn set_return_data(&self, _: &[u8]) -> Result<(), Error> {
75+
Ok(())
76+
}
77+
fn get_ask_count(&self) -> i64 {
78+
10
79+
}
80+
fn get_min_count(&self) -> i64 {
81+
8
82+
}
83+
fn get_prepare_time(&self) -> i64 {
84+
100_000
85+
}
86+
fn get_execute_time(&self) -> Result<i64, Error> {
87+
Ok(100_000)
88+
}
89+
fn get_ans_count(&self) -> Result<i64, Error> {
90+
Ok(8)
91+
}
92+
fn ask_external_data(&self, _: i64, _: i64, _: &[u8]) -> Result<(), Error> {
93+
Ok(())
94+
}
95+
fn get_external_data_status(&self, _: i64, _: i64) -> Result<i64, Error> {
96+
Ok(1)
97+
}
98+
fn get_external_data(&self, _: i64, _: i64) -> Result<Vec<u8>, Error> {
99+
Ok(vec![1])
100+
}
101+
}
102+
103+
fn wat2wasm(wat: impl AsRef<[u8]>) -> Vec<u8> {
104+
let mut input_file = NamedTempFile::new().unwrap();
105+
let mut output_file = NamedTempFile::new().unwrap();
106+
input_file.write_all(wat.as_ref()).unwrap();
107+
Command::new("wat2wasm")
108+
.args(&[
109+
input_file.path().to_str().unwrap(),
110+
"-o",
111+
output_file.path().to_str().unwrap(),
112+
])
113+
.output()
114+
.unwrap();
115+
let mut wasm = Vec::new();
116+
output_file.read_to_end(&mut wasm).unwrap();
117+
wasm
118+
}
119+
120+
#[test]
121+
fn test_simple_gas_used() {
122+
let wasm = wat2wasm(
123+
r#"(module
124+
(type (func (param i64 i64 i64 i64) (result)))
125+
(func
126+
(local $idx i32)
127+
(local.set $idx (i32.const 0))
128+
(block
129+
(loop
130+
(local.set $idx (local.get $idx) (i32.const 1) (i32.add) )
131+
(br_if 0 (i32.lt_u (local.get $idx) (i32.const 100000)))
132+
)
133+
)
134+
)
135+
(func (;"execute": Resolves with result "beeb";)
136+
)
137+
(memory 17)
138+
(data (i32.const 1048576) "beeb") (;str = "beeb";)
139+
(export "prepare" (func 0))
140+
(export "execute" (func 1)))
141+
"#,
142+
);
143+
let code = compile(&wasm).unwrap();
144+
let mut cache = Cache::new(CacheOptions { cache_size: 10000 });
145+
let env = MockEnv {};
146+
let gas_used = run(&mut cache, &code, u64::MAX, true, env).unwrap();
147+
assert_eq!(gas_used, 2000032500000 as u64);
148+
}
149+
150+
#[test]
151+
fn test_ask_count_gas_used() {
152+
let wasm = wat2wasm(
153+
r#"(module
154+
(type (func (param i64 i64 i64 i64) (result)))
155+
(import "env" "ask_external_data" (func (type 0)))
156+
(func
157+
(local $idx i32)
158+
159+
(i64.const 1)
160+
(i64.const 1)
161+
(i64.const 1048576)
162+
(i64.const 4)
163+
call 0
164+
165+
(local.set $idx (i32.const 0))
166+
(block
167+
(loop
168+
(local.set $idx (local.get $idx) (i32.const 1) (i32.add) )
169+
(br_if 0 (i32.lt_u (local.get $idx) (i32.const 100000)))
170+
)
171+
)
172+
)
173+
(func (;"execute": Resolves with result "beeb";))
174+
(memory (export "memory") 17)
175+
(data (i32.const 1048576) "beeb")
176+
(export "prepare" (func 1))
177+
(export "execute" (func 2)))
178+
"#,
179+
);
180+
181+
let code = compile(&wasm).unwrap();
182+
let mut cache = Cache::new(CacheOptions { cache_size: 10000 });
183+
let env = MockEnv {};
184+
let gas_used = run(&mut cache, &code, u64::MAX, true, env).unwrap();
185+
assert_eq!(gas_used, 2000045000000 as u64);
186+
}
187+
188+
#[test]
189+
fn test_out_of_gas() {
190+
let wasm = wat2wasm(
191+
r#"(module
192+
(type (func (param i64 i64 i64 i64) (result)))
193+
(func
194+
(local $idx i32)
195+
(local.set $idx (i32.const 0))
196+
(block
197+
(loop
198+
(local.set $idx (local.get $idx) (i32.const 1) (i32.add) )
199+
(br_if 0 (i32.lt_u (local.get $idx) (i32.const 100000)))
200+
)
201+
)
202+
)
203+
(func (;"execute": Resolves with result "beeb";)
204+
)
205+
(memory 17)
206+
(data (i32.const 1048576) "beeb") (;str = "beeb";)
207+
(export "prepare" (func 0))
208+
(export "execute" (func 1)))
209+
"#,
210+
);
211+
let code = compile(&wasm).unwrap();
212+
let mut cache = Cache::new(CacheOptions { cache_size: 10000 });
213+
let env = MockEnv {};
214+
let out_of_gas_err = run(&mut cache, &code, 0, true, env).unwrap_err();
215+
assert_eq!(out_of_gas_err, Error::OutOfGasError);
216+
}
217+
}

packages/vm/src/checksum.rs

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
use crate::Error;
2+
3+
use sha2::{Digest, Sha256};
4+
use std::convert::TryFrom;
5+
6+
/// This is a copy of checksum.rs from https://github.com/CosmWasm/cosmwasm/blob/6082e8a35a193f1365cb3367f77bd87c593a7ae4/packages/vm/src/checksum.rs
7+
///
8+
/// A SHA-256 checksum of a Wasm blob, used to identify a Wasm code.
9+
/// This must remain stable since this checksum is stored in the blockchain state.
10+
///
11+
/// This is often referred to as "code ID" in go-cosmwasm, even if code ID
12+
/// usually refers to an auto-incrementing number.
13+
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
14+
pub struct Checksum([u8; 32]);
15+
16+
impl Checksum {
17+
pub fn generate(wasm: &[u8]) -> Self {
18+
Checksum(Sha256::digest(wasm).into())
19+
}
20+
21+
/// Creates a lowercase hex encoded copy of this checksum
22+
pub fn to_hex(&self) -> String {
23+
hex::encode(self.0)
24+
}
25+
}
26+
27+
impl From<[u8; 32]> for Checksum {
28+
fn from(data: [u8; 32]) -> Self {
29+
Checksum(data)
30+
}
31+
}
32+
33+
impl TryFrom<&[u8]> for Checksum {
34+
type Error = Error;
35+
36+
fn try_from(value: &[u8]) -> Result<Self, Self::Error> {
37+
if value.len() != 32 {
38+
return Err(Error::ChecksumLengthNotMatch);
39+
}
40+
let mut data = [0u8; 32];
41+
data.copy_from_slice(value);
42+
Ok(Checksum(data))
43+
}
44+
}
45+
46+
impl Into<Vec<u8>> for Checksum {
47+
fn into(self) -> Vec<u8> {
48+
// Rust 1.43+ also supports self.0.into()
49+
self.0.to_vec()
50+
}
51+
}
52+
53+
#[cfg(test)]
54+
mod tests {
55+
use super::*;
56+
57+
#[test]
58+
fn generate_works() {
59+
let wasm = vec![0x68, 0x69, 0x6a];
60+
let checksum = Checksum::generate(&wasm);
61+
62+
// echo -n "hij" | sha256sum
63+
let expected = [
64+
0x72, 0x2c, 0x8c, 0x99, 0x3f, 0xd7, 0x5a, 0x76, 0x27, 0xd6, 0x9e, 0xd9, 0x41, 0x34,
65+
0x4f, 0xe2, 0xa1, 0x42, 0x3a, 0x3e, 0x75, 0xef, 0xd3, 0xe6, 0x77, 0x8a, 0x14, 0x28,
66+
0x84, 0x22, 0x71, 0x04,
67+
];
68+
assert_eq!(checksum.0, expected);
69+
}
70+
71+
#[test]
72+
fn to_hex_works() {
73+
let wasm = vec![0x68, 0x69, 0x6a];
74+
let checksum = Checksum::generate(&wasm);
75+
// echo -n "hij" | sha256sum
76+
assert_eq!(
77+
checksum.to_hex(),
78+
"722c8c993fd75a7627d69ed941344fe2a1423a3e75efd3e6778a142884227104"
79+
);
80+
}
81+
82+
#[test]
83+
fn into_vec_works() {
84+
let checksum = Checksum::generate(&vec![12u8; 17]);
85+
let as_vec: Vec<u8> = checksum.into();
86+
assert_eq!(as_vec, checksum.0);
87+
}
88+
}

0 commit comments

Comments
 (0)