浏览代码

substrate: remove scratch buffer from ext_call, ext_instantiate, and ext_return

This removes the last of the scratch buffer.

Signed-off-by: Sean Young <sean@mess.org>
Sean Young 5 年之前
父节点
当前提交
86274501ff
共有 9 个文件被更改,包括 366 次插入424 次删除
  1. 36 3
      src/emit/ewasm.rs
  2. 3 1
      src/emit/generic.rs
  3. 11 27
      src/emit/mod.rs
  4. 3 1
      src/emit/sabre.rs
  5. 178 246
      src/emit/substrate.rs
  6. 二进制
      stdlib/stdlib.bc
  7. 2 2
      stdlib/stdlib.c
  8. 93 104
      tests/substrate.rs
  9. 40 40
      tests/substrate_calls/mod.rs

+ 36 - 3
src/emit/ewasm.rs

@@ -1312,13 +1312,15 @@ impl<'a> TargetRuntime<'a> for EwasmTarget {
     fn external_call<'b>(
         &self,
         contract: &Contract<'b>,
+        function: FunctionValue,
+        success: Option<&mut BasicValueEnum<'b>>,
         payload: PointerValue<'b>,
         payload_len: IntValue<'b>,
         address: PointerValue<'b>,
         gas: IntValue<'b>,
         value: IntValue<'b>,
         callty: ast::CallTy,
-    ) -> IntValue<'b> {
+    ) {
         // value is a u128
         let value_ptr = contract
             .builder
@@ -1326,7 +1328,7 @@ impl<'a> TargetRuntime<'a> for EwasmTarget {
         contract.builder.build_store(value_ptr, value);
 
         // call create
-        contract
+        let ret = contract
             .builder
             .build_call(
                 contract
@@ -1356,7 +1358,38 @@ impl<'a> TargetRuntime<'a> for EwasmTarget {
             .try_as_basic_value()
             .left()
             .unwrap()
-            .into_int_value()
+            .into_int_value();
+
+        let is_success = contract.builder.build_int_compare(
+            IntPredicate::EQ,
+            ret,
+            contract.context.i32_type().const_zero(),
+            "success",
+        );
+
+        if let Some(success) = success {
+            *success = is_success.into();
+        } else {
+            let success_block = contract.context.append_basic_block(function, "success");
+            let bail_block = contract.context.append_basic_block(function, "bail");
+            contract
+                .builder
+                .build_conditional_branch(is_success, success_block, bail_block);
+
+            contract.builder.position_at_end(bail_block);
+
+            self.assert_failure(
+                contract,
+                contract
+                    .context
+                    .i8_type()
+                    .ptr_type(AddressSpace::Generic)
+                    .const_null(),
+                contract.context.i32_type().const_zero(),
+            );
+
+            contract.builder.position_at_end(success_block);
+        }
     }
 
     fn return_data<'b>(&self, contract: &Contract<'b>) -> PointerValue<'b> {

+ 3 - 1
src/emit/generic.rs

@@ -676,13 +676,15 @@ impl<'a> TargetRuntime<'a> for GenericTarget {
     fn external_call<'b>(
         &self,
         _contract: &Contract<'b>,
+        _function: FunctionValue,
+        _success: Option<&mut BasicValueEnum<'b>>,
         _payload: PointerValue<'b>,
         _payload_len: IntValue<'b>,
         _address: PointerValue<'b>,
         _gas: IntValue<'b>,
         _value: IntValue<'b>,
         _ty: ast::CallTy,
-    ) -> IntValue<'b> {
+    ) {
         panic!("generic cannot call other contracts");
     }
 

+ 11 - 27
src/emit/mod.rs

@@ -191,13 +191,15 @@ pub trait TargetRuntime<'a> {
     fn external_call<'b>(
         &self,
         contract: &Contract<'b>,
+        function: FunctionValue,
+        success: Option<&mut BasicValueEnum<'b>>,
         payload: PointerValue<'b>,
         payload_len: IntValue<'b>,
         address: PointerValue<'b>,
         gas: IntValue<'b>,
         value: IntValue<'b>,
         ty: ast::CallTy,
-    ) -> IntValue<'b>;
+    );
 
     /// builtin expressions
     fn builtin<'b>(
@@ -3331,8 +3333,15 @@ pub trait TargetRuntime<'a> {
                             address,
                         );
 
-                        let ret = self.external_call(
+                        let success = match success {
+                            Some(n) => Some(&mut w.vars.get_mut(n).unwrap().value),
+                            None => None,
+                        };
+
+                        self.external_call(
                             contract,
+                            function,
+                            success,
                             payload,
                             payload_len,
                             addr,
@@ -3340,31 +3349,6 @@ pub trait TargetRuntime<'a> {
                             value,
                             callty.clone(),
                         );
-
-                        let is_success = contract.builder.build_int_compare(
-                            IntPredicate::EQ,
-                            ret,
-                            contract.context.i32_type().const_zero(),
-                            "success",
-                        );
-
-                        if let Some(success) = success {
-                            w.vars.get_mut(success).unwrap().value = is_success.into();
-                        } else {
-                            let success_block =
-                                contract.context.append_basic_block(function, "success");
-                            let bail_block = contract.context.append_basic_block(function, "bail");
-                            contract.builder.build_conditional_branch(
-                                is_success,
-                                success_block,
-                                bail_block,
-                            );
-                            contract.builder.position_at_end(bail_block);
-
-                            contract.builder.build_return(Some(&ret));
-
-                            contract.builder.position_at_end(success_block);
-                        }
                     }
                     cfg::Instr::AbiEncodeVector {
                         res,

+ 3 - 1
src/emit/sabre.rs

@@ -744,13 +744,15 @@ impl<'a> TargetRuntime<'a> for SabreTarget {
     fn external_call<'b>(
         &self,
         _contract: &Contract<'b>,
+        _function: FunctionValue,
+        _success: Option<&mut BasicValueEnum<'b>>,
         _payload: PointerValue<'b>,
         _payload_len: IntValue<'b>,
         _address: PointerValue<'b>,
         _gas: IntValue<'b>,
         _value: IntValue<'b>,
         _ty: ast::CallTy,
-    ) -> IntValue<'b> {
+    ) {
         panic!("Sabre cannot call other contracts");
     }
 

+ 178 - 246
src/emit/substrate.rs

@@ -65,9 +65,6 @@ impl SubstrateTarget {
             "deploy",
             "call",
             "ext_input",
-            "ext_scratch_size",
-            "ext_scratch_read",
-            "ext_scratch_write",
             "ext_set_storage",
             "ext_get_storage",
             "ext_clear_storage",
@@ -171,13 +168,6 @@ impl SubstrateTarget {
             .into();
         let u64_val = contract.context.i64_type().into();
 
-        // Access to scratch buffer
-        contract.module.add_function(
-            "ext_scratch_size",
-            contract.context.i32_type().fn_type(&[], false),
-            Some(Linkage::External),
-        );
-
         contract.module.add_function(
             "ext_input",
             contract
@@ -187,23 +177,6 @@ impl SubstrateTarget {
             Some(Linkage::External),
         );
 
-        contract.module.add_function(
-            "ext_scratch_read",
-            contract.context.void_type().fn_type(
-                &[
-                    contract
-                        .context
-                        .i8_type()
-                        .ptr_type(AddressSpace::Generic)
-                        .into(), // dest_ptr
-                    contract.context.i32_type().into(), // offset
-                    contract.context.i32_type().into(), // len
-                ],
-                false,
-            ),
-            Some(Linkage::External),
-        );
-
         contract.module.add_function(
             "ext_hash_keccak_256",
             contract.context.void_type().fn_type(
@@ -288,22 +261,6 @@ impl SubstrateTarget {
             Some(Linkage::External),
         );
 
-        contract.module.add_function(
-            "ext_scratch_write",
-            contract.context.void_type().fn_type(
-                &[
-                    contract
-                        .context
-                        .i8_type()
-                        .ptr_type(AddressSpace::Generic)
-                        .into(), // dest_ptr
-                    contract.context.i32_type().into(), // len
-                ],
-                false,
-            ),
-            Some(Linkage::External),
-        );
-
         contract.module.add_function(
             "ext_random",
             contract
@@ -317,17 +274,9 @@ impl SubstrateTarget {
             "ext_set_storage",
             contract.context.void_type().fn_type(
                 &[
-                    contract
-                        .context
-                        .i8_type()
-                        .ptr_type(AddressSpace::Generic)
-                        .into(), // key_ptr
-                    contract
-                        .context
-                        .i8_type()
-                        .ptr_type(AddressSpace::Generic)
-                        .into(), // value_ptr
-                    contract.context.i32_type().into(), // value_len
+                    u8_ptr,  // key_ptr
+                    u8_ptr,  // value_ptr
+                    u32_val, // value_len
                 ],
                 false,
             ),
@@ -338,12 +287,8 @@ impl SubstrateTarget {
             "ext_println",
             contract.context.void_type().fn_type(
                 &[
-                    contract
-                        .context
-                        .i8_type()
-                        .ptr_type(AddressSpace::Generic)
-                        .into(), // string_ptr
-                    contract.context.i32_type().into(), // string_len
+                    u8_ptr,  // string_ptr
+                    u32_val, // string_len
                 ],
                 false,
             ),
@@ -354,11 +299,7 @@ impl SubstrateTarget {
             "ext_clear_storage",
             contract.context.void_type().fn_type(
                 &[
-                    contract
-                        .context
-                        .i8_type()
-                        .ptr_type(AddressSpace::Generic)
-                        .into(), // key_ptr
+                    u8_ptr, // key_ptr
                 ],
                 false,
             ),
@@ -378,7 +319,7 @@ impl SubstrateTarget {
             "ext_return",
             contract.context.void_type().fn_type(
                 &[
-                    u8_ptr, u32_val, // data ptr and len
+                    u32_val, u8_ptr, u32_val, // flags, data ptr, and len
                 ],
                 false,
             ),
@@ -393,6 +334,8 @@ impl SubstrateTarget {
                     u64_val, // gas
                     u8_ptr, u32_val, // value ptr and len
                     u8_ptr, u32_val, // input ptr and len
+                    u8_ptr, u32_ptr, // address ptr and len
+                    u8_ptr, u32_ptr, // output ptr and len
                 ],
                 false,
             ),
@@ -407,6 +350,7 @@ impl SubstrateTarget {
                     u64_val, // gas
                     u8_ptr, u32_val, // value ptr and len
                     u8_ptr, u32_val, // input ptr and len
+                    u8_ptr, u32_ptr, // output ptr and len
                 ],
                 false,
             ),
@@ -533,7 +477,7 @@ impl SubstrateTarget {
         // create deploy function
         let function = contract.module.add_function(
             "deploy",
-            contract.context.i32_type().fn_type(&[], false),
+            contract.context.void_type().fn_type(&[], false),
             None,
         );
 
@@ -558,16 +502,23 @@ impl SubstrateTarget {
 
         // emit fallback code
         contract.builder.position_at_end(fallback_block);
-        contract
-            .builder
-            .build_return(Some(&contract.context.i32_type().const_int(2, false)));
+
+        self.assert_failure(
+            contract,
+            contract
+                .context
+                .i8_type()
+                .ptr_type(AddressSpace::Generic)
+                .const_null(),
+            contract.context.i32_type().const_zero(),
+        );
     }
 
     fn emit_call(&mut self, contract: &Contract) {
         // create call function
         let function = contract.module.add_function(
             "call",
-            contract.context.i32_type().fn_type(&[], false),
+            contract.context.void_type().fn_type(&[], false),
             None,
         );
 
@@ -696,9 +647,15 @@ impl SubstrateTarget {
 
         contract.builder.position_at_end(bail_block);
 
-        contract
-            .builder
-            .build_return(Some(&contract.context.i32_type().const_int(3, false)));
+        self.assert_failure(
+            contract,
+            contract
+                .context
+                .i8_type()
+                .ptr_type(AddressSpace::Generic)
+                .const_null(),
+            contract.context.i32_type().const_zero(),
+        );
 
         contract.builder.position_at_end(success_block);
     }
@@ -2385,10 +2342,10 @@ impl<'a> TargetRuntime<'a> for SubstrateTarget {
     }
 
     fn return_empty_abi(&self, contract: &Contract) {
-        // This will clear the scratch buffer
         contract.builder.build_call(
-            contract.module.get_function("ext_scratch_write").unwrap(),
+            contract.module.get_function("ext_return").unwrap(),
             &[
+                contract.context.i32_type().const_zero().into(),
                 contract
                     .context
                     .i8_type()
@@ -2400,13 +2357,20 @@ impl<'a> TargetRuntime<'a> for SubstrateTarget {
             "",
         );
 
-        contract
-            .builder
-            .build_return(Some(&contract.context.i32_type().const_zero()));
+        contract.builder.build_unreachable();
     }
 
-    fn return_u32<'b>(&self, contract: &'b Contract, ret: IntValue<'b>) {
-        contract.builder.build_return(Some(&ret));
+    fn return_u32<'b>(&self, contract: &'b Contract, _ret: IntValue<'b>) {
+        // we can't return specific errors
+        self.assert_failure(
+            contract,
+            contract
+                .context
+                .i8_type()
+                .ptr_type(AddressSpace::Generic)
+                .const_null(),
+            contract.context.i32_type().const_zero(),
+        );
     }
 
     /// Call the  keccak256 host function
@@ -2444,26 +2408,30 @@ impl<'a> TargetRuntime<'a> for SubstrateTarget {
 
     fn return_abi<'b>(&self, contract: &'b Contract, data: PointerValue<'b>, length: IntValue) {
         contract.builder.build_call(
-            contract.module.get_function("ext_scratch_write").unwrap(),
-            &[data.into(), length.into()],
+            contract.module.get_function("ext_return").unwrap(),
+            &[
+                contract.context.i32_type().const_zero().into(),
+                data.into(),
+                length.into(),
+            ],
             "",
         );
 
-        contract
-            .builder
-            .build_return(Some(&contract.context.i32_type().const_zero()));
+        contract.builder.build_unreachable();
     }
 
     fn assert_failure<'b>(&self, contract: &'b Contract, data: PointerValue, length: IntValue) {
         contract.builder.build_call(
-            contract.module.get_function("ext_scratch_write").unwrap(),
-            &[data.into(), length.into()],
+            contract.module.get_function("ext_return").unwrap(),
+            &[
+                contract.context.i32_type().const_int(1, false).into(),
+                data.into(),
+                length.into(),
+            ],
             "",
         );
 
-        contract
-            .builder
-            .build_return(Some(&contract.context.i32_type().const_int(1, false)));
+        contract.builder.build_unreachable();
     }
 
     fn abi_decode<'b>(
@@ -2818,6 +2786,12 @@ impl<'a> TargetRuntime<'a> for SubstrateTarget {
 
         let mut args = args.to_vec();
         let mut params = constructor.params.to_vec();
+        let scratch_buf = contract.builder.build_pointer_cast(
+            contract.scratch.unwrap().as_pointer_value(),
+            contract.context.i8_type().ptr_type(AddressSpace::Generic),
+            "scratch_buf",
+        );
+        let scratch_len = contract.scratch_len.unwrap().as_pointer_value();
 
         // salt
         let salt_ty = ast::Type::Uint(256);
@@ -2827,13 +2801,6 @@ impl<'a> TargetRuntime<'a> for SubstrateTarget {
         } else {
             let (ptr, len) = self.contract_unique_salt(contract, contract_no);
 
-            let scratch_buf = contract.builder.build_pointer_cast(
-                contract.scratch.unwrap().as_pointer_value(),
-                contract.context.i8_type().ptr_type(AddressSpace::Generic),
-                "scratch_buf",
-            );
-            let scratch_len = contract.scratch_len.unwrap().as_pointer_value();
-
             contract.builder.build_store(
                 scratch_len,
                 contract.context.i32_type().const_int(32, false),
@@ -2850,14 +2817,19 @@ impl<'a> TargetRuntime<'a> for SubstrateTarget {
                 "random",
             );
 
-            args.push(contract.builder.build_load(
-                contract.builder.build_pointer_cast(
-                    scratch_buf,
-                    contract.context.i8_type().ptr_type(AddressSpace::Generic),
-                    "salt_buf",
+            args.push(
+                contract.builder.build_load(
+                    contract.builder.build_pointer_cast(
+                        scratch_buf,
+                        contract
+                            .context
+                            .custom_width_int_type(256)
+                            .ptr_type(AddressSpace::Generic),
+                        "salt_buf",
+                    ),
+                    "salt",
                 ),
-                "salt",
-            ));
+            );
         }
 
         params.push(ast::Parameter {
@@ -2932,6 +2904,26 @@ impl<'a> TargetRuntime<'a> for SubstrateTarget {
             true,
         );
 
+        let address_len_ptr = contract
+            .builder
+            .build_alloca(contract.context.i32_type(), "address_len_ptr");
+
+        contract.builder.build_store(
+            address_len_ptr,
+            contract
+                .context
+                .i32_type()
+                .const_int(contract.ns.address_length as u64, false),
+        );
+
+        contract.builder.build_store(
+            scratch_len,
+            contract
+                .context
+                .i32_type()
+                .const_int(SCRATCH_SIZE as u64, false),
+        );
+
         // ext_instantiate returns 0x0100 if the contract cannot be instantiated
         // due to insufficient funds, etc. If the return value is < 0x100, then
         // this is return value from the constructor (or deploy function) of
@@ -2955,6 +2947,10 @@ impl<'a> TargetRuntime<'a> for SubstrateTarget {
                     contract.context.i32_type().const_int(16, false).into(),
                     input.into(),
                     input_len.into(),
+                    address.into(),
+                    address_len_ptr.into(),
+                    scratch_buf.into(),
+                    scratch_len.into(),
                 ],
                 "",
             )
@@ -2978,21 +2974,6 @@ impl<'a> TargetRuntime<'a> for SubstrateTarget {
 
         contract.builder.position_at_end(success_block);
 
-        // scratch buffer contains address
-        contract.builder.build_call(
-            contract.module.get_function("ext_scratch_read").unwrap(),
-            &[
-                address.into(),
-                contract.context.i32_type().const_zero().into(),
-                contract
-                    .context
-                    .i32_type()
-                    .const_int(contract.ns.address_length as u64, false)
-                    .into(),
-            ],
-            "",
-        );
-
         if let Some(success) = success {
             // we're in the try path. This means:
             // return success or not in success variable
@@ -3008,30 +2989,15 @@ impl<'a> TargetRuntime<'a> for SubstrateTarget {
         } else {
             contract.builder.position_at_end(bail_block);
 
-            // if ext_instantiate returned 0x100, we cannot return that here. This is because
-            // only the lower 8 bits of our return value are taken.
-
-            // ext_call can return 0x100 if the call cannot be made. We cannot return this value
-            // from the smart contract, so replace it with 4.
-            let call_not_made = contract.builder.build_int_compare(
-                IntPredicate::EQ,
-                ret,
-                contract.context.i32_type().const_int(0x100, false),
-                "success",
+            self.assert_failure(
+                contract,
+                scratch_buf,
+                contract
+                    .builder
+                    .build_load(scratch_len, "string_len")
+                    .into_int_value(),
             );
 
-            let ret = contract
-                .builder
-                .build_select(
-                    call_not_made,
-                    contract.context.i32_type().const_int(4, false),
-                    ret,
-                    "return_value",
-                )
-                .into_int_value();
-
-            contract.builder.build_return(Some(&ret));
-
             contract.builder.position_at_end(success_block);
         }
     }
@@ -3040,19 +3006,36 @@ impl<'a> TargetRuntime<'a> for SubstrateTarget {
     fn external_call<'b>(
         &self,
         contract: &Contract<'b>,
+        function: FunctionValue,
+        success: Option<&mut BasicValueEnum<'b>>,
         payload: PointerValue<'b>,
         payload_len: IntValue<'b>,
         address: PointerValue<'b>,
         gas: IntValue<'b>,
         value: IntValue<'b>,
         _ty: ast::CallTy,
-    ) -> IntValue<'b> {
+    ) {
         // balance is a u128
         let value_ptr = contract
             .builder
             .build_alloca(contract.value_type(), "balance");
         contract.builder.build_store(value_ptr, value);
 
+        let scratch_buf = contract.builder.build_pointer_cast(
+            contract.scratch.unwrap().as_pointer_value(),
+            contract.context.i8_type().ptr_type(AddressSpace::Generic),
+            "scratch_buf",
+        );
+        let scratch_len = contract.scratch_len.unwrap().as_pointer_value();
+
+        contract.builder.build_store(
+            scratch_len,
+            contract
+                .context
+                .i32_type()
+                .const_int(SCRATCH_SIZE as u64, false),
+        );
+
         // do the actual call
         let ret = contract
             .builder
@@ -3081,6 +3064,8 @@ impl<'a> TargetRuntime<'a> for SubstrateTarget {
                         .into(),
                     payload.into(),
                     payload_len.into(),
+                    scratch_buf.into(),
+                    scratch_len.into(),
                 ],
                 "",
             )
@@ -3088,128 +3073,75 @@ impl<'a> TargetRuntime<'a> for SubstrateTarget {
             .left()
             .unwrap()
             .into_int_value();
-        // ext_call can return 0x100 if the call cannot be made. We cannot return this value
-        // from the smart contract, so replace it with 4.
-        let call_not_made = contract.builder.build_int_compare(
+
+        let is_success = contract.builder.build_int_compare(
             IntPredicate::EQ,
             ret,
-            contract.context.i32_type().const_int(0x100, false),
+            contract.context.i32_type().const_zero(),
             "success",
         );
 
+        let success_block = contract.context.append_basic_block(function, "success");
+        let bail_block = contract.context.append_basic_block(function, "bail");
         contract
             .builder
-            .build_select(
-                call_not_made,
-                contract.context.i32_type().const_int(4, false),
-                ret,
-                "return_value",
-            )
-            .into_int_value()
+            .build_conditional_branch(is_success, success_block, bail_block);
+
+        contract.builder.position_at_end(success_block);
+
+        if let Some(success) = success {
+            // we're in the try path. This means:
+            // return success or not in success variable
+            // do not abort execution
+            //
+            *success = is_success.into();
+
+            let done_block = contract.context.append_basic_block(function, "done");
+            contract.builder.build_unconditional_branch(done_block);
+            contract.builder.position_at_end(bail_block);
+            contract.builder.build_unconditional_branch(done_block);
+            contract.builder.position_at_end(done_block);
+        } else {
+            contract.builder.position_at_end(bail_block);
+
+            self.assert_failure(
+                contract,
+                scratch_buf,
+                contract
+                    .builder
+                    .build_load(scratch_len, "string_len")
+                    .into_int_value(),
+            );
+
+            contract.builder.position_at_end(success_block);
+        }
     }
 
     fn return_data<'b>(&self, contract: &Contract<'b>) -> PointerValue<'b> {
-        let length = contract
-            .builder
-            .build_call(
-                contract.module.get_function("ext_scratch_size").unwrap(),
-                &[],
-                "returndatasize",
-            )
-            .try_as_basic_value()
-            .left()
-            .unwrap()
-            .into_int_value();
-
-        let malloc_length = contract.builder.build_int_add(
-            length,
-            contract
-                .module
-                .get_struct_type("struct.vector")
-                .unwrap()
-                .size_of()
-                .unwrap()
-                .const_cast(contract.context.i32_type(), false),
-            "size",
+        let scratch_buf = contract.builder.build_pointer_cast(
+            contract.scratch.unwrap().as_pointer_value(),
+            contract.context.i8_type().ptr_type(AddressSpace::Generic),
+            "scratch_buf",
         );
+        let scratch_len = contract.scratch_len.unwrap().as_pointer_value();
 
-        let p = contract
+        let length = contract.builder.build_load(scratch_len, "string_len");
+
+        contract
             .builder
             .build_call(
-                contract.module.get_function("__malloc").unwrap(),
-                &[malloc_length.into()],
+                contract.module.get_function("vector_new").unwrap(),
+                &[
+                    length,
+                    contract.context.i32_type().const_int(1, false).into(),
+                    scratch_buf.into(),
+                ],
                 "",
             )
             .try_as_basic_value()
             .left()
             .unwrap()
-            .into_pointer_value();
-
-        let v = contract.builder.build_pointer_cast(
-            p,
-            contract
-                .module
-                .get_struct_type("struct.vector")
-                .unwrap()
-                .ptr_type(AddressSpace::Generic),
-            "string",
-        );
-
-        let data_len = unsafe {
-            contract.builder.build_gep(
-                v,
-                &[
-                    contract.context.i32_type().const_zero(),
-                    contract.context.i32_type().const_zero(),
-                ],
-                "data_len",
-            )
-        };
-
-        contract.builder.build_store(data_len, length);
-
-        let data_size = unsafe {
-            contract.builder.build_gep(
-                v,
-                &[
-                    contract.context.i32_type().const_zero(),
-                    contract.context.i32_type().const_int(1, false),
-                ],
-                "data_size",
-            )
-        };
-
-        contract.builder.build_store(data_size, length);
-
-        let data = unsafe {
-            contract.builder.build_gep(
-                v,
-                &[
-                    contract.context.i32_type().const_zero(),
-                    contract.context.i32_type().const_int(2, false),
-                ],
-                "data",
-            )
-        };
-
-        contract.builder.build_call(
-            contract.module.get_function("ext_scratch_read").unwrap(),
-            &[
-                contract
-                    .builder
-                    .build_pointer_cast(
-                        data,
-                        contract.context.i8_type().ptr_type(AddressSpace::Generic),
-                        "",
-                    )
-                    .into(),
-                contract.context.i32_type().const_zero().into(),
-                length.into(),
-            ],
-            "",
-        );
-
-        v
+            .into_pointer_value()
     }
 
     /// Substrate value is usually 128 bits

二进制
stdlib/stdlib.bc


+ 2 - 2
stdlib/stdlib.c

@@ -405,10 +405,10 @@ struct vector *vector_new(uint32_t members, uint32_t size, uint8_t *initial)
 
 	if ((int)initial != -1)
 	{
-		do
+		while (size_array--)
 		{
 			*data++ = *initial++;
-		} while (--size_array);
+		}
 	}
 	else
 	{

+ 93 - 104
tests/substrate.rs

@@ -79,13 +79,21 @@ impl fmt::Display for HostCodeTerminate {
     }
 }
 
+#[derive(Debug, Clone, PartialEq)]
+struct HostCodeReturn(i32);
+
+impl fmt::Display for HostCodeReturn {
+    fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
+        write!(f, "return {}", self.0)
+    }
+}
+
+impl HostError for HostCodeReturn {}
+
 #[derive(FromPrimitive)]
 #[allow(non_camel_case_types)]
 enum SubstrateExternal {
     ext_input = 0,
-    ext_scratch_size,
-    ext_scratch_read,
-    ext_scratch_write,
     ext_set_storage,
     ext_clear_storage,
     ext_get_storage,
@@ -207,50 +215,6 @@ impl Externals for TestRuntime {
 
                 Ok(None)
             }
-            Some(SubstrateExternal::ext_scratch_size) => {
-                Ok(Some(RuntimeValue::I32(self.vm.scratch.len() as i32)))
-            }
-            Some(SubstrateExternal::ext_scratch_read) => {
-                let dest: u32 = args.nth_checked(0)?;
-                let offset: u32 = args.nth_checked(1)?;
-                let len: u32 = args.nth_checked(2)?;
-
-                println!(
-                    "ext_scratch_read({}, {}, {}) scratch={} {}",
-                    dest,
-                    offset,
-                    len,
-                    self.vm.scratch.len(),
-                    hex::encode(&self.vm.scratch)
-                );
-
-                if let Err(e) = self.vm.memory.set(
-                    dest,
-                    &self.vm.scratch[offset as usize..(offset + len) as usize],
-                ) {
-                    panic!("ext_scratch_read: {}", e);
-                }
-
-                Ok(None)
-            }
-            Some(SubstrateExternal::ext_scratch_write) => {
-                let dest: u32 = args.nth_checked(0)?;
-                let len: u32 = args.nth_checked(1)?;
-
-                self.vm.scratch.resize(len as usize, 0u8);
-
-                if let Err(e) = self.vm.memory.get_into(dest, &mut self.vm.scratch) {
-                    panic!("ext_scratch_write: {}", e);
-                }
-
-                println!(
-                    "ext_scratch_write({}, {})",
-                    len,
-                    hex::encode(&self.vm.scratch)
-                );
-
-                Ok(None)
-            }
             Some(SubstrateExternal::ext_get_storage) => {
                 assert_eq!(args.len(), 3);
 
@@ -445,8 +409,9 @@ impl Externals for TestRuntime {
                 Ok(None)
             }
             Some(SubstrateExternal::ext_return) => {
-                let data_ptr: u32 = args.nth_checked(0)?;
-                let len: u32 = args.nth_checked(1)?;
+                let flags: i32 = args.nth_checked(0)?;
+                let data_ptr: u32 = args.nth_checked(1)?;
+                let len: u32 = args.nth_checked(2)?;
 
                 self.vm.scratch.resize(len as usize, 0u8);
 
@@ -454,7 +419,10 @@ impl Externals for TestRuntime {
                     panic!("ext_return: {}", e);
                 }
 
-                Ok(None)
+                match flags {
+                    0 | 1 => Err(Trap::new(TrapKind::Host(Box::new(HostCodeReturn(flags))))),
+                    _ => panic!("ext_return flag {} not valid", flags),
+                }
             }
             Some(SubstrateExternal::ext_println) => {
                 let data_ptr: u32 = args.nth_checked(0)?;
@@ -523,6 +491,8 @@ impl Externals for TestRuntime {
                 let value_len: u32 = args.nth_checked(4)?;
                 let input_ptr: u32 = args.nth_checked(5)?;
                 let input_len: u32 = args.nth_checked(6)?;
+                let output_ptr: u32 = args.nth_checked(7)?;
+                let output_len_ptr: u32 = args.nth_checked(8)?;
 
                 let mut address = [0u8; 32];
 
@@ -547,8 +517,8 @@ impl Externals for TestRuntime {
                 let value = u128::from_le_bytes(value);
 
                 if !self.accounts.contains_key(&address) {
-                    // substrate would return TRAP_RETURN_CODE (0x0100)
-                    return Ok(Some(RuntimeValue::I32(0x100)));
+                    // substrate would return NotCallable
+                    return Ok(Some(RuntimeValue::I32(0x8)));
                 }
 
                 let mut input = Vec::new();
@@ -572,20 +542,7 @@ impl Externals for TestRuntime {
 
                 self.vm.input = input;
 
-                let ret = match module.invoke_export("call", &[], self) {
-                    Err(wasmi::Error::Trap(trap)) => match trap.kind() {
-                        TrapKind::Host(host_error) => {
-                            if host_error.downcast_ref::<HostCodeTerminate>().is_some() {
-                                Some(RuntimeValue::I32(1))
-                            } else {
-                                panic!("did not go as planned");
-                            }
-                        }
-                        _ => panic!("fail to invoke main via create: {}", trap),
-                    },
-                    Ok(v) => v,
-                    Err(e) => panic!("fail to invoke main via create: {}", e),
-                };
+                let ret = self.invoke_call(module);
 
                 let output = self.vm.scratch.clone();
 
@@ -596,7 +553,8 @@ impl Externals for TestRuntime {
                 if let Some(acc) = self.accounts.get_mut(&vm.address) {
                     acc.1 += vm.value;
                 }
-                self.vm.scratch = output;
+
+                set_seal_value!("ext_call return buf", output_ptr, output_len_ptr, &output);
 
                 Ok(ret)
             }
@@ -608,6 +566,10 @@ impl Externals for TestRuntime {
                 let value_len: u32 = args.nth_checked(4)?;
                 let input_ptr: u32 = args.nth_checked(5)?;
                 let input_len: u32 = args.nth_checked(6)?;
+                let address_ptr: u32 = args.nth_checked(7)?;
+                let address_len_ptr: u32 = args.nth_checked(8)?;
+                let output_ptr: u32 = args.nth_checked(9)?;
+                let output_len_ptr: u32 = args.nth_checked(10)?;
 
                 let mut codehash = [0u8; 32];
 
@@ -677,21 +639,32 @@ impl Externals for TestRuntime {
                 let module = self.create_module(&code.0);
 
                 self.vm.input = input;
-                let ret = module
-                    .invoke_export("deploy", &[], self)
-                    .expect("failed to call constructor");
+
+                let ret = self.invoke_deploy(module);
 
                 let output = self.vm.scratch.clone();
 
                 std::mem::swap(&mut self.vm, &mut vm);
 
+                set_seal_value!(
+                    "ext_instantiate output",
+                    output_ptr,
+                    output_len_ptr,
+                    &output
+                );
+
                 if let Some(RuntimeValue::I32(0)) = ret {
                     self.accounts.get_mut(&vm.address).unwrap().1 += vm.value;
-                    self.vm.scratch = address.to_vec();
-                } else {
-                    self.vm.scratch = output;
+                    set_seal_value!(
+                        "ext_instantiate address",
+                        address_ptr,
+                        address_len_ptr,
+                        &address
+                    );
                 }
 
+                println!("ext_instantiate ret:{:?}", ret);
+
                 Ok(ret)
             }
             Some(SubstrateExternal::ext_value_transferred) => {
@@ -884,9 +857,6 @@ impl ModuleImportResolver for TestRuntime {
     fn resolve_func(&self, field_name: &str, signature: &Signature) -> Result<FuncRef, Error> {
         let index = match field_name {
             "ext_input" => SubstrateExternal::ext_input,
-            "ext_scratch_size" => SubstrateExternal::ext_scratch_size,
-            "ext_scratch_read" => SubstrateExternal::ext_scratch_read,
-            "ext_scratch_write" => SubstrateExternal::ext_scratch_write,
             "ext_get_storage" => SubstrateExternal::ext_get_storage,
             "ext_set_storage" => SubstrateExternal::ext_set_storage,
             "ext_clear_storage" => SubstrateExternal::ext_clear_storage,
@@ -938,6 +908,42 @@ impl TestRuntime {
             .expect("Failed to run start function in module")
     }
 
+    fn invoke_deploy(&mut self, module: ModuleRef) -> Option<RuntimeValue> {
+        match module.invoke_export("deploy", &[], self) {
+            Err(wasmi::Error::Trap(trap)) => match trap.kind() {
+                TrapKind::Host(host_error) => {
+                    if let Some(ret) = host_error.downcast_ref::<HostCodeReturn>() {
+                        Some(RuntimeValue::I32(ret.0))
+                    } else {
+                        panic!("did not go as planned");
+                    }
+                }
+                _ => panic!("fail to invoke deploy: {}", trap),
+            },
+            Ok(v) => v,
+            Err(e) => panic!("fail to invoke deploy: {}", e),
+        }
+    }
+
+    fn invoke_call(&mut self, module: ModuleRef) -> Option<RuntimeValue> {
+        match module.invoke_export("call", &[], self) {
+            Err(wasmi::Error::Trap(trap)) => match trap.kind() {
+                TrapKind::Host(host_error) => {
+                    if let Some(ret) = host_error.downcast_ref::<HostCodeReturn>() {
+                        Some(RuntimeValue::I32(ret.0))
+                    } else if host_error.downcast_ref::<HostCodeTerminate>().is_some() {
+                        Some(RuntimeValue::I32(1))
+                    } else {
+                        panic!("did not go as planned");
+                    }
+                }
+                _ => panic!("fail to invoke call: {}", trap),
+            },
+            Ok(v) => v,
+            Err(e) => panic!("fail to invoke call: {}", e),
+        }
+    }
+
     pub fn constructor(&mut self, index: usize, args: Vec<u8>) {
         let m = &self.abi.contract.constructors[index];
 
@@ -945,10 +951,9 @@ impl TestRuntime {
 
         self.vm.input = m.selector().into_iter().chain(args).collect();
 
-        if let Some(RuntimeValue::I32(ret)) = module
-            .invoke_export("deploy", &[], self)
-            .expect("failed to call function")
-        {
+        let ret = self.invoke_deploy(module);
+
+        if let Some(RuntimeValue::I32(ret)) = ret {
             if ret != 0 {
                 panic!("non zero return")
             }
@@ -962,10 +967,9 @@ impl TestRuntime {
 
         self.vm.input = m.selector().into_iter().chain(args).collect();
 
-        if let Some(RuntimeValue::I32(ret)) = module
-            .invoke_export("deploy", &[], self)
-            .expect("failed to call function")
-        {
+        let ret = self.invoke_deploy(module);
+
+        if let Some(RuntimeValue::I32(ret)) = ret {
             println!(
                 "function_expected_return: got {} expected {}",
                 ret, expected_ret
@@ -984,10 +988,7 @@ impl TestRuntime {
 
         self.vm.input = m.selector().into_iter().chain(args).collect();
 
-        if let Some(RuntimeValue::I32(ret)) = module
-            .invoke_export("call", &[], self)
-            .expect("failed to call function")
-        {
+        if let Some(RuntimeValue::I32(ret)) = self.invoke_call(module) {
             if ret != 0 {
                 panic!(format!("non zero return: {}", ret));
             }
@@ -1001,10 +1002,7 @@ impl TestRuntime {
 
         self.vm.input = m.selector().into_iter().chain(args).collect();
 
-        if let Some(RuntimeValue::I32(ret)) = module
-            .invoke_export("call", &[], self)
-            .expect("failed to call function")
-        {
+        if let Some(RuntimeValue::I32(ret)) = self.invoke_call(module) {
             println!(
                 "function_expected_return: got {} expected {}",
                 ret, expected_ret
@@ -1021,10 +1019,7 @@ impl TestRuntime {
 
         self.vm.input = input;
 
-        if let Some(RuntimeValue::I32(ret)) = module
-            .invoke_export("call", &[], self)
-            .expect("failed to call function")
-        {
+        if let Some(RuntimeValue::I32(ret)) = self.invoke_call(module) {
             if ret != 0 {
                 panic!("non zero return")
             }
@@ -1036,10 +1031,7 @@ impl TestRuntime {
 
         self.vm.input = input;
 
-        if let Some(RuntimeValue::I32(ret)) = module
-            .invoke_export("call", &[], self)
-            .expect("failed to call function")
-        {
+        if let Some(RuntimeValue::I32(ret)) = self.invoke_call(module) {
             println!("got {} expected {}", ret, expect_ret);
 
             if ret != expect_ret {
@@ -1053,10 +1045,7 @@ impl TestRuntime {
 
         self.vm.input = input;
 
-        if let Some(RuntimeValue::I32(ret)) = module
-            .invoke_export("deploy", &[], self)
-            .expect("failed to call constructor")
-        {
+        if let Some(RuntimeValue::I32(ret)) = self.invoke_deploy(module) {
             if ret != 0 {
                 panic!("non zero return")
             }

+ 40 - 40
tests/substrate_calls/mod.rs

@@ -215,7 +215,7 @@ fn input_wrong_size() {
         }"##,
     );
 
-    runtime.function_expect_return("test", b"A".to_vec(), 3);
+    runtime.function_expect_return("test", b"A".to_vec(), 1);
 
     // the decoder does not check if there is too much data
     runtime.function_expect_return("test", b"ABCDE".to_vec(), 0);
@@ -232,7 +232,7 @@ fn external_call_not_exist() {
                 o.test();
             }
         }
-        
+
         contract other {
             function test() public {
 
@@ -240,7 +240,7 @@ fn external_call_not_exist() {
         }"##,
     );
 
-    runtime.function_expect_return("test", Vec::new(), 4);
+    runtime.function_expect_return("test", Vec::new(), 1);
 }
 
 #[test]
@@ -254,7 +254,7 @@ fn contract_already_exists() {
                 other t = new other{salt: 0}();
             }
         }
-        
+
         contract other {
             function test() public {
 
@@ -262,7 +262,7 @@ fn contract_already_exists() {
         }"##,
     );
 
-    runtime.function_expect_return("test", Vec::new(), 4);
+    runtime.function_expect_return("test", Vec::new(), 1);
 
     let mut runtime = build_solidity(
         r##"
@@ -273,7 +273,7 @@ fn contract_already_exists() {
                 other t = new other();
             }
         }
-        
+
         contract other {
             function test() public {
 
@@ -300,7 +300,7 @@ fn try_catch_external_calls() {
                 assert(x == 1);
             }
         }
-        
+
         contract other {
             function test() public returns (int32, bool) {
                 return (102, true);
@@ -329,7 +329,7 @@ fn try_catch_external_calls() {
                 assert(x == 1);
             }
         }
-        
+
         contract other {
             function test() public returns (int32, bool) {
                 return (102, true);
@@ -358,7 +358,7 @@ fn try_catch_external_calls() {
                 assert(x == 102);
             }
         }
-        
+
         contract other {
             function test() public returns (int32, bool) {
                 return (102, true);
@@ -384,7 +384,7 @@ fn try_catch_external_calls() {
                 assert(x == 2);
             }
         }
-        
+
         contract other {
             function test() public returns (int32, bool) {
                 revert("foo");
@@ -409,7 +409,7 @@ fn try_catch_external_calls() {
                 assert(x == 1);
             }
         }
-        
+
         contract other {
             function test() public returns (int32, bool) {
                 return (102, true);
@@ -438,7 +438,7 @@ fn try_catch_external_calls() {
                 assert(x == 1);
             }
         }
-        
+
         contract other {
             function test() public returns (int32, bool) {
                 return (102, true);
@@ -466,7 +466,7 @@ fn try_catch_external_calls() {
                 assert(x == 1);
             }
         }
-        
+
         contract other {
             function test() public returns (int32, bool) {
                 return (102, true);
@@ -551,7 +551,7 @@ fn try_catch_external_calls() {
                 }
             }
         }
-        
+
         contract other {
             function test(int x) public {
                 if (x == 1) {
@@ -575,15 +575,15 @@ fn try_catch_external_calls() {
         r##"
         contract dominator {
             child c;
-        
+
             function create_child() public {
                 c = new child();
             }
-        
+
             function call_child() public view returns (int64) {
                 return c.get_a();
             }
-        
+
             function test() public pure returns (int32) {
                 try c.go_bang() returns (int32 l) {
                     print("try call success");
@@ -598,7 +598,7 @@ fn try_catch_external_calls() {
                     print("try catch path");
                     return 2000;
                 }
-        
+
             }
         }
 
@@ -607,15 +607,15 @@ fn try_catch_external_calls() {
             constructor() public {
                 a = 102;
             }
-        
+
             function get_a() public view returns (int64) {
                 return a;
             }
-        
+
             function set_a(int64 l) public {
                 a = l;
             }
-        
+
             function go_bang() public pure returns (int32) {
                 revert("gone bang in child");
             }
@@ -643,7 +643,7 @@ fn try_catch_constructor() {
                 assert(x == 1);
             }
         }
-        
+
         contract other {
             function test() public returns (int32, bool) {
                 return (102, true);
@@ -670,7 +670,7 @@ fn try_catch_constructor() {
                 assert(x == 1);
             }
         }
-        
+
         contract other {
             function test() public returns (int32, bool) {
                 return (102, true);
@@ -698,7 +698,7 @@ fn try_catch_constructor() {
                 assert(x == 102);
             }
         }
-        
+
         contract other {
             function test() public returns (int32, bool) {
                 return (102, true);
@@ -722,7 +722,7 @@ fn try_catch_constructor() {
                 assert(x == 102);
             }
         }
-        
+
         contract other {
             constructor(bool foo) public {
                 //
@@ -751,7 +751,7 @@ fn try_catch_constructor() {
                 assert(x == 2);
             }
         }
-        
+
         contract other {
             constructor(bool foo) public {
                 revert("foo");
@@ -774,7 +774,7 @@ fn try_catch_constructor() {
                 assert(x == 1);
             }
         }
-        
+
         contract other {
             function test() public returns (int32, bool) {
                 return (102, true);
@@ -808,14 +808,14 @@ fn try_catch_constructor() {
         r##"
         contract c {
             function test() public {
-                try new other() 
+                try new other()
                 catch (string) {
                     x = 2;
                 }
                 assert(x == 1);
             }
         }
-        
+
         contract other {
             function test() public  {
             }
@@ -844,7 +844,7 @@ fn try_catch_constructor() {
                 assert(x == 1);
             }
         }
-        
+
         contract other {
             function test() public  {
             }
@@ -870,7 +870,7 @@ fn try_catch_constructor() {
                 assert(x == 1);
             }
         }
-        
+
         contract other {
             function test() public  {
             }
@@ -1054,7 +1054,7 @@ fn payable_functions() {
     assert_eq!(runtime.vm.scratch, Ret(3).encode());
 
     runtime.vm.value = 0;
-    runtime.raw_function_return(2, b"abde".to_vec());
+    runtime.raw_function_return(1, b"abde".to_vec());
     let mut runtime = build_solidity(
         r##"
         contract c {
@@ -1076,7 +1076,7 @@ fn payable_functions() {
 
     runtime.constructor(0, Vec::new());
     runtime.vm.value = 1;
-    runtime.raw_function_return(2, b"abde".to_vec());
+    runtime.raw_function_return(1, b"abde".to_vec());
 
     runtime.vm.value = 0;
     runtime.raw_function(b"abde".to_vec());
@@ -1156,7 +1156,7 @@ fn hash_tests() {
         contract tester {
             function test() public {
                 bytes32 hash = keccak256("Hello, World!");
-            
+
                 assert(hash == hex"acaf3289d7b601cbd114fb36c4d29c85bbfd5e133f14cb355c3fd8d99367964f");
             }
         }"##,
@@ -1170,7 +1170,7 @@ fn hash_tests() {
             function test() public {
                 bytes memory s = "Hello, World!";
                 bytes32 hash = keccak256(s);
-            
+
                 assert(hash == hex"acaf3289d7b601cbd114fb36c4d29c85bbfd5e133f14cb355c3fd8d99367964f");
             }
         }"##,
@@ -1185,7 +1185,7 @@ fn hash_tests() {
 
             function test() public {
                 bytes32 hash = keccak256(s);
-            
+
                 assert(hash == hex"acaf3289d7b601cbd114fb36c4d29c85bbfd5e133f14cb355c3fd8d99367964f");
             }
         }"##,
@@ -1199,7 +1199,7 @@ fn hash_tests() {
         contract tester {
             function test() public {
                 bytes32 hash = sha256("Hello, World!");
-            
+
                 assert(hash == hex"dffd6021bb2bd5b0af676290809ec3a53191dd81c7f70a4b28688a362182986f");
             }
         }"##,
@@ -1212,7 +1212,7 @@ fn hash_tests() {
         contract tester {
             function test() public {
                 bytes32 hash = blake2_256("Hello, World!");
-            
+
                 assert(hash == hex"511bc81dde11180838c562c82bb35f3223f46061ebde4a955c27b3f489cf1e03");
             }
         }"##,
@@ -1225,7 +1225,7 @@ fn hash_tests() {
         contract tester {
             function test() public {
                 bytes16 hash = blake2_128("Hello, World!");
-            
+
                 assert(hash == hex"3895c59e4aeb0903396b5be3fbec69fe");
             }
         }"##,
@@ -1238,7 +1238,7 @@ fn hash_tests() {
         contract tester {
             function test() public {
                 bytes20 hash = ripemd160("Hello, World!");
-            
+
                 assert(hash == hex"527a6a4b9a6da75607546842e0e00105350b1aaf");
             }
         }"##,