浏览代码

substrate storage access

Signed-off-by: Sean Young <sean@mess.org>
Sean Young 6 年之前
父节点
当前提交
852788c9c7
共有 4 个文件被更改,包括 139 次插入14 次删除
  1. 1 1
      Cargo.toml
  2. 2 2
      src/emit/burrow.rs
  3. 29 5
      src/emit/mod.rs
  4. 107 6
      src/emit/substrate.rs

+ 1 - 1
Cargo.toml

@@ -28,7 +28,7 @@ ethabi = "7.0.0"
 ethereum-types = "0.5.2"
 
 [dependencies.inkwell]
-git = "https://github.com/TheDan64/inkwell"
+git = "https://github.com/seanyoung/inkwell"
 branch = "llvm8-0"
 features = ["target-webassembly", "llvm8-0"]
 

+ 2 - 2
src/emit/burrow.rs

@@ -252,7 +252,7 @@ impl BurrowTarget {
 }
 
 impl TargetRuntime for BurrowTarget {
-    fn set_storage<'a>(&self, contract: &'a Contract, slot: u32, dest: inkwell::values::PointerValue<'a>) {
+    fn set_storage<'a>(&self, contract: &'a Contract, _function: FunctionValue, slot: u32, dest: inkwell::values::PointerValue<'a>) {
         contract.builder.build_call(
             contract.module.get_function("set_storage32").unwrap(),
             &[
@@ -265,7 +265,7 @@ impl TargetRuntime for BurrowTarget {
             "");
     }
 
-    fn get_storage<'a>(&self, contract: &'a Contract, slot: u32, dest: inkwell::values::PointerValue<'a>) {
+    fn get_storage<'a>(&self, contract: &'a Contract, _function: FunctionValue, slot: u32, dest: inkwell::values::PointerValue<'a>) {
         contract.builder.build_call(
             contract.module.get_function("get_storage32").unwrap(),
             &[

+ 29 - 5
src/emit/mod.rs

@@ -19,7 +19,7 @@ use inkwell::memory_buffer::MemoryBuffer;
 use inkwell::targets::{CodeModel, RelocMode, FileType, Target};
 use inkwell::AddressSpace;
 use inkwell::types::{IntType, StringRadix};
-use inkwell::values::{PointerValue, IntValue, PhiValue, FunctionValue, BasicValueEnum};
+use inkwell::values::{PointerValue, IntValue, PhiValue, FunctionValue, BasicValueEnum, GlobalValue};
 use inkwell::IntPredicate;
 
 const WASMTRIPLE: &str = "wasm32-unknown-unknown-wasm";
@@ -47,8 +47,8 @@ struct Function<'a> {
 
 pub trait TargetRuntime {
     //
-    fn set_storage<'a>(&self, contract: &'a Contract, slot: u32, dest: inkwell::values::PointerValue<'a>);
-    fn get_storage<'a>(&self, contract: &'a Contract, slot: u32, dest: inkwell::values::PointerValue<'a>);
+    fn set_storage<'a>(&self, contract: &'a Contract, function: FunctionValue, slot: u32, dest: inkwell::values::PointerValue<'a>);
+    fn get_storage<'a>(&self, contract: &'a Contract, function: FunctionValue, slot: u32, dest: inkwell::values::PointerValue<'a>);
     
     fn abi_decode<'b>(
         &self,
@@ -79,6 +79,7 @@ pub struct Contract<'a> {
     ns: &'a resolver::Contract,
     constructors: Vec<Function<'a>>,
     functions: Vec<Function<'a>>,
+    globals: Vec<GlobalValue<'a>>,
 }
 
 impl<'a> Contract<'a> {
@@ -140,9 +141,32 @@ impl<'a> Contract<'a> {
             ns: contract,
             constructors: Vec::new(),
             functions: Vec::new(),
+            globals: Vec::new(),
         }
     }
 
+    /// Creates global string in the llvm module with initializer
+    ///
+    fn emit_global_string(&mut self, name: &str, data: &[u8], constant: bool) -> usize {
+        let ty = self.context.i8_type().array_type(data.len() as u32);
+
+        let gv = self.module.add_global(ty, Some(AddressSpace::Generic), name);
+
+        gv.set_linkage(Linkage::Internal);
+
+        gv.set_initializer(&self.context.const_string(data, false));
+        
+        if constant {
+            gv.set_constant(true);
+        }
+
+        let last = self.globals.len();
+
+        self.globals.push(gv);
+
+        last
+    }
+
     fn emit_functions(&mut self, runtime: &dyn TargetRuntime) {
         self.constructors = self.ns.constructors.iter()
             .map(|func| self.emit_func(&format!("sol::constructor::{}", func.wasm_symbol(&self.ns)), func, runtime))
@@ -464,12 +488,12 @@ impl<'a> Contract<'a> {
                     cfg::Instr::GetStorage { local, storage } => {
                         let dest = w.vars[*local].value.into_pointer_value();
 
-                        runtime.get_storage(&self, *storage as u32, dest);
+                        runtime.get_storage(&self, function, *storage as u32, dest);
                     }
                     cfg::Instr::SetStorage { local, storage } => {
                         let dest = w.vars[*local].value.into_pointer_value();
 
-                        runtime.set_storage(&self, *storage as u32, dest);
+                        runtime.set_storage(&self, function, *storage as u32, dest);
                     }
                     cfg::Instr::Call { res, func, args } => {
                         let mut parms: Vec<BasicValueEnum> = Vec::new();

+ 107 - 6
src/emit/substrate.rs

@@ -8,17 +8,23 @@ use inkwell::AddressSpace;
 use inkwell::values::{BasicValueEnum, IntValue, PointerValue, FunctionValue};
 use inkwell::IntPredicate;
 
+use std::collections::HashMap;
+
 use super::{TargetRuntime, Contract};
 
 pub struct SubstrateTarget {
+    /// This field maps a storage slot to llvm global
+    slot_mapping: HashMap<usize, usize>
 }
 
 impl SubstrateTarget {
     pub fn build<'a>(context: &'a Context, contract: &'a resolver::Contract, filename: &'a str) -> Contract<'a> {
         let mut c = Contract::new(context, contract, filename);
-        let b = SubstrateTarget{};
+        let mut b = SubstrateTarget{
+            slot_mapping: HashMap::new()
+        };
 
-        // externals
+        b.storage_keys(&mut c);
         b.declare_externals(&c);
 
         c.emit_functions(&b);
@@ -29,6 +35,20 @@ impl SubstrateTarget {
         c
     }
 
+    fn storage_keys<'a>(&mut self, contract: &'a mut Contract) {
+        for var in &contract.ns.variables {
+            if let Some(slot) = var.storage {
+                let mut key = slot.to_le_bytes().to_vec();
+
+                key.resize(32, 0);
+
+                let v = contract.emit_global_string(&format!("sol::key::{}", var.name), &key, true);
+
+                self.slot_mapping.insert(slot, v);
+            }
+        }
+    }
+
     fn public_function_prelude<'a>(&self, contract: &'a Contract, function: FunctionValue) -> (PointerValue<'a>, IntValue<'a>) {
         let entry = contract.context.append_basic_block(function, "entry");
 
@@ -91,6 +111,25 @@ impl SubstrateTarget {
             ], false),
             Some(Linkage::External)
         );
+
+        contract.module.add_function(
+            "ext_set_storage",
+            contract.context.void_type().fn_type(&[
+                contract.context.i8_type().ptr_type(AddressSpace::Generic).into(), // key_ptr
+                contract.context.i32_type().into(), // value_non_null
+                contract.context.i8_type().ptr_type(AddressSpace::Generic).into(), // value_ptr
+                contract.context.i32_type().into(), // value_len
+            ], false),
+            Some(Linkage::External)
+        );
+
+        contract.module.add_function(
+            "ext_get_storage",
+            contract.context.i32_type().fn_type(&[
+                contract.context.i8_type().ptr_type(AddressSpace::Generic).into(), // key_ptr
+            ], false),
+            Some(Linkage::External)
+        );
     }
 
     fn emit_deploy(&self, contract: &Contract) {
@@ -139,12 +178,71 @@ impl SubstrateTarget {
 }
 
 impl TargetRuntime for SubstrateTarget {
-    // TODO
-    fn set_storage<'a>(&self, _contract: &'a Contract, _slot: u32, _dest: inkwell::values::PointerValue<'a>) {
+    fn set_storage<'a>(&self, contract: &'a Contract, _function: FunctionValue, slot: u32, dest: inkwell::values::PointerValue<'a>) {
+        // FIXME: check for non-zero
+        let key = contract.globals[self.slot_mapping[&(slot as usize)]];
+
+        contract.builder.build_call(
+            contract.module.get_function("ext_set_storage").unwrap(),
+            &[
+                contract.builder.build_pointer_cast(key.as_pointer_value(),
+                    contract.context.i8_type().ptr_type(AddressSpace::Generic), "").into(),
+                contract.context.i32_type().const_int(1, false).into(),
+                contract.builder.build_pointer_cast(dest,
+                    contract.context.i8_type().ptr_type(AddressSpace::Generic), "").into(),
+                dest.get_type().size_of().const_cast(
+                    contract.context.i32_type(), false).into()
+            ],
+            "");
     }
 
-    // TODO
-    fn get_storage<'a>(&self, _contract: &'a Contract, _slot: u32, _dest: inkwell::values::PointerValue<'a>) {
+    /// Read from substrate storage
+    fn get_storage<'a>(&self, contract: &'a Contract, function: FunctionValue, slot: u32, dest: inkwell::values::PointerValue<'a>) {
+        let key = contract.globals[self.slot_mapping[&(slot as usize)]];
+
+        let exists = contract.builder.build_call(
+            contract.module.get_function("ext_get_storage").unwrap(),
+            &[
+                contract.builder.build_pointer_cast(key.as_pointer_value(),
+                    contract.context.i8_type().ptr_type(AddressSpace::Generic), "").into(),
+            ],
+            "").try_as_basic_value().left().unwrap();
+
+        let exists = contract.builder.build_int_compare(
+            IntPredicate::EQ,
+            exists.into_int_value(),
+            contract.context.i32_type().const_zero(),
+            "storage_exists");
+        
+        let clear_block = contract.context.append_basic_block(function, "not_in_storage");
+        let retrieve_block = contract.context.append_basic_block(function, "in_storage");
+        let done_storage = contract.context.append_basic_block(function, "done_storage");
+        
+        contract.builder.build_conditional_branch(exists, &retrieve_block, &clear_block);
+
+        contract.builder.position_at_end(&retrieve_block);
+
+        contract.builder.build_call(
+            contract.module.get_function("ext_scratch_read").unwrap(),
+            &[
+                contract.builder.build_pointer_cast(dest,
+                    contract.context.i8_type().ptr_type(AddressSpace::Generic), "").into(),
+                contract.context.i32_type().const_zero().into(),
+                dest.get_type().size_of().const_cast(
+                    contract.context.i32_type(), false).into()
+            ],
+            ""
+        );
+
+        contract.builder.build_unconditional_branch(&done_storage);
+
+        contract.builder.position_at_end(&clear_block);
+
+        contract.builder.build_store(dest, dest.get_type().get_element_type().into_int_type().const_zero());
+
+        contract.builder.build_unconditional_branch(&done_storage);
+
+        contract.builder.position_at_end(&done_storage);
     }
 
     fn abi_decode<'b>(
@@ -182,6 +280,9 @@ impl TargetRuntime for SubstrateTarget {
 
         contract.builder.build_conditional_branch(is_ok, &decode_block, &wrong_length_block);
 
+        contract.builder.position_at_end(&wrong_length_block);
+        contract.builder.build_unreachable();
+
         contract.builder.position_at_end(&decode_block);
 
         let mut argsdata = data;