|
|
@@ -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;
|