Эх сурвалжийг харах

Test for contract storage

Signed-off-by: Sean Young <sean@mess.org>
Sean Young 6 жил өмнө
parent
commit
119f43c362
4 өөрчлөгдсөн 99 нэмэгдсэн , 27 устгасан
  1. 27 20
      src/emit.rs
  2. 70 5
      src/test.rs
  3. BIN
      stdlib/stdlib.bc
  4. 2 2
      stdlib/stdlib.c

+ 27 - 20
src/emit.rs

@@ -185,13 +185,21 @@ impl<'a> Contract<'a> {
             unsafe { LLVMInt32TypeInContext(self.context) },
         ];
         let ftype = unsafe { LLVMFunctionType(ret, args.as_mut_ptr(), args.len() as u32, 0) };
+        let func = unsafe { LLVMAddFunction(self.module, "get_storage32\0".as_ptr() as *const i8, ftype) };
+        unsafe {
+            LLVMSetLinkage(func, LLVMLinkage::LLVMExternalLinkage);
+        }
         self.externals.insert(
             "get_storage32".to_owned(),
-            unsafe { LLVMAddFunction(self.module, "get_storage32\0".as_ptr() as *const i8, ftype) }
+            func,
         );
+        let func = unsafe { LLVMAddFunction(self.module, "set_storage32\0".as_ptr() as *const i8, ftype) };
+        unsafe {
+            LLVMSetLinkage(func, LLVMLinkage::LLVMExternalLinkage);
+        }
         self.externals.insert(
             "set_storage32".to_owned(),
-            unsafe { LLVMAddFunction(self.module, "set_storage32\0".as_ptr() as *const i8, ftype) }
+            func,
         );
     }
 
@@ -835,22 +843,21 @@ impl<'a> Contract<'a> {
 
         for v in &cfg.vars {
             match v.storage {
+                cfg::Storage::Local if !v.ty.stack_based() => {
+                    vars.push(Variable{
+                        value_ref: null_mut(),
+                        stack: false,
+                    });
+                },
                 cfg::Storage::Local | cfg::Storage::Contract(_) => {
-                    if v.ty.stack_based() {
-                        let name = CString::new(v.id.name.to_string()).unwrap();
-
-                        vars.push(Variable{
-                            value_ref: unsafe {
-                                LLVMBuildAlloca(builder, v.ty.LLVMType(self.ns, self.context), name.as_ptr() as *const _)
-                            },
-                            stack: true,
-                        });
-                    } else {
-                        vars.push(Variable{
-                            value_ref: null_mut(),
-                            stack: false,
-                        });
-                    }
+                    let name = CString::new(v.id.name.to_string()).unwrap();
+
+                    vars.push(Variable{
+                        value_ref: unsafe {
+                            LLVMBuildAlloca(builder, v.ty.LLVMType(self.ns, self.context), name.as_ptr() as *const _)
+                        },
+                        stack: true,
+                    });
                 },
                 cfg::Storage::Constant => {
                     // nothing to do
@@ -1026,10 +1033,10 @@ impl<'a> Contract<'a> {
                         let mut args = vec![
                             // key
                             unsafe { LLVMConstInt(LLVMInt32TypeInContext(self.context), *storage as _, LLVM_FALSE) },
-                            // dest
-                            unsafe { LLVMBuildPointerCast(builder, dest, LLVMPointerType(LLVMInt8TypeInContext(self.context), 0), "\0".as_ptr() as *const _) },
+                            // src
+                            unsafe { LLVMBuildPointerCast(builder, dest, LLVMPointerType(LLVMInt8TypeInContext(self.context), 0), "src\0".as_ptr() as *const _) },
                             // length
-                            unsafe { LLVMBuildPtrToInt(builder, off1, LLVMInt32TypeInContext(self.context), "\0".as_ptr() as *const i8) }
+                            unsafe { LLVMBuildPtrToInt(builder, off1, LLVMInt32TypeInContext(self.context), "length\0".as_ptr() as *const i8) }
                         ];
 
                         unsafe {

+ 70 - 5
src/test.rs

@@ -10,6 +10,7 @@ mod tests {
     use wasmi::*;
     use std::mem;
     use std::collections::HashMap;
+    use std::convert::TryInto;
 
     struct ContractStorage {
         memory: MemoryRef,
@@ -43,7 +44,7 @@ mod tests {
                     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);
+                        panic!("set_storage32: {}", e);
                     }
                     self.store.insert(slot, c);
                 },
@@ -55,21 +56,21 @@ mod tests {
                     c.resize(len as usize, 0u8);
 
                     if let Err(e) = self.memory.set(offset, &c) {
-                        panic!("get_contract_storage32: {}", e);
+                        panic!("get_storage32: {}", e);
                     }
                 },
                 _ => panic!("external {} unknown", index)
             }
 
-            Ok(Some(RuntimeValue::I32(0 as i32)))
+            Ok(None)
         }
     }
 
     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,
+                "set_storage32" => SET_CONTRACT_STORAGE32,
+                "get_storage32" => GET_CONTRACT_STORAGE32,
                 _ => {
                     panic!("{} not implemented", field_name);
                 }
@@ -320,4 +321,68 @@ contract test {
             }
         }
     }
+
+    #[test]
+    fn contract_storage_test() {
+        let (wasm, mut store, abi) = build_solidity(r##"
+contract test {
+    uint32 foo;
+    constructor() public {
+        foo = 102;
+    }
+	function getFoo() public returns (uint32) {
+        return foo + 256;
+	}
+	function setFoo(uint32 a) public  {
+        foo = a - 256;
+	}
+}"##);
+
+        let abi = ethabi::Contract::load(abi.as_bytes()).unwrap();
+
+        // call constructor so that heap is initialised
+        let ret = wasm.invoke_export("constructor", &[RuntimeValue::I32(0)], &mut store).expect("failed to call constructor");
+        let wmem = store.memory.clone();
+
+        assert_eq!(ret, None);
+
+        for val in [4096u32, 1000u32].iter() {
+            let eval = ethereum_types::U256::from(*val);
+            // create call for foo
+            let mut calldata = abi.functions["setFoo"].encode_input(&[ ethabi::Token::Uint(eval) ]).unwrap();
+            // need to prepend length
+
+            wmem.set_value(0, calldata.len() as u32).unwrap();
+            wmem.set(mem::size_of::<u32>() as u32, &calldata).unwrap();
+
+            wasm.invoke_export("function", &[RuntimeValue::I32(0)], &mut store).expect("failed to call function");
+
+            {
+                let v = store.store.get(&0).unwrap();
+                assert_eq!(v.len(), 4);
+                assert_eq!(val - 256, u32::from_le_bytes(v.as_slice().try_into().unwrap()));
+            }
+
+            // now try retrieve
+            calldata = abi.functions["getFoo"].encode_input(&[]).unwrap();
+            // need to prepend length
+
+            wmem.set_value(0, calldata.len() as u32).unwrap();
+            wmem.set(mem::size_of::<u32>() as u32, &calldata).unwrap();
+
+            let ret = wasm.invoke_export("function", &[RuntimeValue::I32(0)], &mut store).expect("failed to call function");
+
+            if let Some(RuntimeValue::I32(offset)) = ret {
+                let offset = offset as u32;
+                assert_eq!(wmem.get_value::<u32>(offset).unwrap(), 32);
+                let returndata = wmem.get(offset + mem::size_of::<u32>() as u32, 32).unwrap();
+
+                let returns = abi.functions["getFoo"].decode_output(&returndata).unwrap();
+
+                assert_eq!(returns, vec![ ethabi::Token::Uint(eval) ]);
+            } else {
+                panic!("expected offset to return data");
+            }
+        }
+    }
 }

BIN
stdlib/stdlib.bc


+ 2 - 2
stdlib/stdlib.c

@@ -13,8 +13,8 @@
  * set the memory at dest to 0. If the storage is shorter, pad the remaining bytes
  * with 0.
  */
-extern void get_contract_storage32(uint32_t key, void *dest, int32_t length);
-extern void set_contract_storage32(uint32_t key, void *src, int32_t length);
+extern void get_storage32(uint32_t key, void *dest, int32_t length);
+extern void set_storage32(uint32_t key, void *src, int32_t length);
 
 
 /*