Bläddra i källkod

Define set_contract_storage/get_contract_storage for tests

Signed-off-by: Sean Young <sean@mess.org>
Sean Young 6 år sedan
förälder
incheckning
0b9dd86393
2 ändrade filer med 98 tillägg och 23 borttagningar
  1. 7 6
      src/link.rs
  2. 91 17
      src/test.rs

+ 7 - 6
src/link.rs

@@ -3,7 +3,7 @@ use parity_wasm;
 use parity_wasm::elements::{Internal, Module, ExportEntry, GlobalEntry, GlobalType, ValueType, InitExpr};
 use parity_wasm::builder;
 
-use parity_wasm::elements::{VarUint7, VarUint32, Deserialize};
+use parity_wasm::elements::{VarUint7, VarUint32, Deserialize, ImportEntry};
 use parity_wasm::elements;
 
 #[allow(dead_code)]
@@ -63,6 +63,12 @@ pub fn link(input: &[u8]) -> Vec<u8> {
                 ind += 1;
             }
         }
+
+        imports.push(ImportEntry::new(
+            "env".into(),
+            "memory".into(),
+            elements::External::Memory(elements::MemoryType::new(2, Some(2), false)),
+        ));
     }
 
     module.clear_custom_section("linking");
@@ -77,11 +83,6 @@ pub fn link(input: &[u8]) -> Vec<u8> {
         linked = linked.with_global(e);
     }
 
-    linked.push_memory(builder::MemoryBuilder::new().with_min(2).build());
-
-    // FIXME: export our memory so that it's easy to reference in the tests. Shouldn't really be needed.
-    linked.push_export(builder::ExportBuilder::new().field("memory").internal().memory(0).build());
-
     parity_wasm::serialize(linked.build()).expect("cannot serialize linked wasm")
 }
 

+ 91 - 17
src/test.rs

@@ -6,10 +6,88 @@ mod tests {
     use emit;
     use link;
     use output;
-    use wasmi::{ImportsBuilder, Module, ModuleInstance, NopExternals, RuntimeValue, ModuleRef};
+    use wasmi::memory_units::Pages;
+    use wasmi::*;
     use std::mem;
+    use std::collections::HashMap;
 
-    fn build_solidity(src: &'static str) -> (ModuleRef, String) {
+    struct ContractStorage {
+        memory: MemoryRef,
+        store: HashMap<u32, Vec<u8>>,
+    }
+
+    const SET_CONTRACT_STORAGE32 : usize = 0;
+    const GET_CONTRACT_STORAGE32 : usize = 1;
+
+    impl ContractStorage {
+        fn new() -> Self {
+            ContractStorage{
+                memory: MemoryInstance::alloc(Pages(2), Some(Pages(2))).unwrap(),
+                store: HashMap::new(),
+            }
+        }
+    }
+
+    impl Externals for ContractStorage {
+        fn invoke_index(
+            &mut self,
+            index: usize,
+            args: RuntimeArgs,
+        ) -> Result<Option<RuntimeValue>, Trap> {
+            let slot: u32 = args.nth_checked(0)?;
+            let offset: u32 = args.nth_checked(1)?;
+            let len: u32 = args.nth_checked(2)?;
+
+            match index {
+                SET_CONTRACT_STORAGE32 => {
+                    let mut c = Vec::new();
+                    c.resize(len as usize, 0u8);
+                    if let Err(e) = self.memory.get_into(offset, &mut c) {
+                        panic!("set_contract_storage32: {}", e);
+                    }
+                    self.store.insert(slot, c);
+                },
+                GET_CONTRACT_STORAGE32 => {
+                    let mut c = Vec::new();
+                    if let Some(k) = self.store.get(&slot) {
+                        c = k.clone();
+                    }
+                    c.resize(len as usize, 0u8);
+
+                    if let Err(e) = self.memory.set(offset, &c) {
+                        panic!("get_contract_storage32: {}", e);
+                    }
+                },
+                _ => panic!("external {} unknown", index)
+            }
+
+            Ok(Some(RuntimeValue::I32(0 as i32)))
+        }
+    }
+
+    impl ModuleImportResolver for ContractStorage {
+        fn resolve_func(&self, field_name: &str, signature: &Signature) -> Result<FuncRef, Error> {
+            let index = match field_name {
+                "set_contract_storage32" => SET_CONTRACT_STORAGE32,
+                "get_contract_storage32" => GET_CONTRACT_STORAGE32,
+                _ => {
+                    panic!("{} not implemented", field_name);
+                }
+            };
+
+            Ok(FuncInstance::alloc_host(signature.clone(), index))
+        }
+
+        fn resolve_memory(
+            &self,
+            _field_name: &str,
+            _memory_type: &MemoryDescriptor,
+        ) -> Result<MemoryRef, Error> {
+            Ok(self.memory.clone())
+        }
+    }
+
+    fn build_solidity(src: &'static str) -> (ModuleRef, ContractStorage, String) {
         let s = parser::parse(src).expect("parse should succeed");
         
         // resolve
@@ -33,17 +111,20 @@ mod tests {
 
         let module = Module::from_buffer(bc).expect("parse wasm should work");
 
-        (ModuleInstance::new(&module, &ImportsBuilder::default())
+        let store = ContractStorage::new();
+
+        (ModuleInstance::new(&module, &ImportsBuilder::new().with_resolver("env", &store))
             .expect("Failed to instantiate module")
             .run_start(&mut NopExternals)
             .expect("Failed to run start function in module"),
+         store,
          serde_json::to_string(&abi).unwrap())
     }
 
     #[test]
     fn simple_solidiy_compile_and_run() {
         // parse
-        let (main, _) = build_solidity("
+        let (main, _, _) = build_solidity("
             contract test {
                 function foo() public returns (uint32) {
                     return 2;
@@ -57,7 +138,7 @@ mod tests {
 
     #[test]
     fn simple_loops() {
-        let (main, _) = build_solidity(r##"
+        let (main, _, _) = build_solidity(r##"
 contract test3 {
 	function foo(uint32 a) public returns (uint32) {
 		uint32 b = 50 - a;
@@ -145,7 +226,7 @@ contract test3 {
 
     #[test]
     fn stack_test() {
-        let (main, _) = build_solidity(r##"
+        let (main, _, _) = build_solidity(r##"
 contract test3 {
 	function foo() public returns (bool) {
 		uint b = 18446744073709551616;
@@ -162,13 +243,12 @@ contract test3 {
 
     #[test]
     fn abi_call_return_test() {
-        let (wasm, abi) = build_solidity(r##"
+        let (wasm, store, abi) = build_solidity(r##"
 contract test {
 	function foo() public returns (uint32) {
         return 102;
 	}
 }"##);
-
         let abi = ethabi::Contract::load(abi.as_bytes()).unwrap();
 
         // call constructor so that heap is initialised
@@ -179,10 +259,7 @@ contract test {
         // create call for foo
         let calldata = abi.functions["foo"].encode_input(&[]).unwrap();
         // need to prepend length
-        let wmem = match wasm.export_by_name("memory") {
-            Some(wasmi::ExternVal::Memory(n)) => n,
-            _ => panic!()
-        };
+        let wmem = store.memory;
 
         wmem.set_value(0, calldata.len() as u32).unwrap();
         wmem.set(mem::size_of::<u32>() as  u32, &calldata).unwrap();
@@ -204,7 +281,7 @@ contract test {
 
     #[test]
     fn abi_call_pass_return_test() {
-        let (wasm, abi) = build_solidity(r##"
+        let (wasm, store, abi) = build_solidity(r##"
 contract test {
 	function foo(uint32 a) public returns (uint32) {
         return a;
@@ -215,6 +292,7 @@ contract test {
 
         // call constructor so that heap is initialised
         let ret = wasm.invoke_export("constructor", &[RuntimeValue::I32(0)], &mut NopExternals).expect("failed to call constructor");
+        let wmem = store.memory;
 
         assert_eq!(ret, None);
 
@@ -223,10 +301,6 @@ contract test {
             // create call for foo
             let calldata = abi.functions["foo"].encode_input(&[ ethabi::Token::Uint(eval) ]).unwrap();
             // need to prepend length
-            let wmem = match wasm.export_by_name("memory") {
-                Some(wasmi::ExternVal::Memory(n)) => n,
-                _ => panic!()
-            };
 
             wmem.set_value(0, calldata.len() as u32).unwrap();
             wmem.set(mem::size_of::<u32>() as  u32, &calldata).unwrap();