فهرست منبع

Add Sawtooth Sabre support

Signed-off-by: Sean Young <sean@mess.org>
Sean Young 5 سال پیش
والد
کامیت
71797a460a
11فایلهای تغییر یافته به همراه709 افزوده شده و 90 حذف شده
  1. 1 2
      docs/contributing.rst
  2. 2 1
      docs/index.rst
  3. 91 11
      docs/running.rst
  4. 1 1
      src/abi/mod.rs
  5. 2 1
      src/bin/solang.rs
  6. 2 0
      src/emit/mod.rs
  7. 482 0
      src/emit/sabre.rs
  8. 3 0
      src/lib.rs
  9. 18 11
      src/link.rs
  10. BIN
      stdlib/stdlib.bc
  11. 107 63
      stdlib/stdlib.c

+ 1 - 2
docs/contributing.rst

@@ -9,8 +9,7 @@ Target Specific Tests
 
 Solang supports Substrate and ewasm. These targets need testing
 via integration tests. New targets like
-`Fabric <https://github.com/hyperledger-labs/fabric-chaincode-wasm>`_ and
-`Sawtooth Sabre <https://github.com/hyperledger/sawtooth-sabre>`_ need to be
+`Fabric <https://github.com/hyperledger-labs/fabric-chaincode-wasm>`_ need to be
 added, and tests added.
 
 How to report issues

+ 2 - 1
docs/index.rst

@@ -10,7 +10,8 @@ Solang Solidity Compiler
 Welcome to the Solang Solidity compiler, the portable Solidity compiler.
 Using Solang, you can compile smart contracts written in
 `Solidity <https://en.wikipedia.org/wiki/Solidity>`_
-for `Substrate <https://substrate.dev/>`_ and
+for `Substrate <https://substrate.dev/>`_,
+`Sawtooth Sabre <https://github.com/hyperledger/sawtooth-sabre>`_, and
 `Ethereum ewasm <https://github.com/ewasm/design>`_. It uses the
 `llvm <https://www.llvm.org/>`_ compiler framework to produce WebAssembly
 (wasm). As result, the output is highly optimized, which saves you in gas costs.

+ 91 - 11
docs/running.rst

@@ -5,16 +5,17 @@ The Solang compiler is run on the command line. The solidity source file
 names are provided as command line arguments; the output is an optimized
 wasm file which is ready for deployment on a chain, and an abi file.
 
-Two targets are supported right now:
-`Ethereum ewasm <https://github.com/ewasm/design>`_  and
-`Parity Substrate <https://substrate.dev/>`_.
+Three targets are supported right now:
+`Ethereum ewasm <https://github.com/ewasm/design>`_,  and
+`Parity Substrate <https://substrate.dev/>`_, and
+`Sawtooth Sabre <https://github.com/hyperledger/sawtooth-sabre>`_.
 
 .. note::
 
   Depending on which target Solang is compiling for, different language
   features are supported. For example, when compiling for substrate, the
   constructor can be overloaded with different prototypes. When targetting
-  ewasm, only one constructor prototype is allowed.
+  ewasm or Sawtooth Sabre, only one constructor prototype is allowed.
 
 Using Solang on the command line
 --------------------------------
@@ -120,21 +121,100 @@ Using Solang with Substrate
 ---------------------------
 
 Solang builds contracts for Substrate by default. There is an solidity example
-which can be found in the `examples <https://github.com/hyperledger-labs/solang/tree/master/examples>`_ directory.
-
-.. include:: ../examples/flipper.sol
-  :code: javascript
-
-Write this to flipper.sol and run:
+which can be found in the `examples <https://github.com/hyperledger-labs/solang/tree/master/examples>`_
+directory. Write this to flipper.sol and run:
 
 .. code-block:: bash
 
-  solang flipper.sol
+  solang --target substrate flipper.sol
 
 Now you should have ``flipper.wasm`` and ``flipper.json``. This can be used
 directly in the `Polkadot UI <https://substrate.dev/substrate-contracts-workshop/#/0/deploying-your-contract?id=putting-your-code-on-the-blockchain>`_, as if the contract was written in ink!.
 
+Using Solang with Sawtooth Sabre
+--------------------------------
+
+When using Solang on Sawtooth Sabre, the constructor and function calls must be encoded with Ethereum ABI encoding.
+This can be done in different ways. In this guide we use `ethabi <https://github.com/paritytech/ethabi>`_. This can
+be installed using cargo:
+
+.. code-block:: bash
+
+  cargo install ethabi-cli
+
+In order to abi encode the calls, we need the abi for the contract. Let's compile flipper.sol for Sabre:
+
+.. code-block:: bash
+
+  solang --target sabre --verbose flipper.sol
+
+We now have a file ``flipper.wasm`` and ``flipper.abi``. To deploy this, we need to create the constructor
+ABI encoding. Unfortunately ethabi already falls short here; we cannot encode constructor calls using the cli
+tools. However we can work round this by specify the constructor arguments explicitly. Note that if the
+constructor does not take any arguments, then the constructor data should be empty (0 bytes). So, since the
+constructor in flipper.sol takes a single bool, create it like so:
+
+.. code-block:: bash
+
+  ethabi encode params -v bool true | xxd -r -p > constructor
+
+For flipping the value, create it so:
+
+.. code-block:: bash
+
+  ethabi encode function flipper.abi flip | xxd -r -p  > flip
+
+You'll also need a yaml file with the following contents. Save it to flipper.yaml.
+
+.. code-block:: yaml
+
+  name: flipper
+  version: '1.0'
+  wasm: flipper.wasm
+  inputs:
+  - '12cd3c'
+  outputs:
+  - '12cd3c'
+
+Now we have to start the Sawtooth Sabre environment. First clone the
+`Sawtooth Sabre github repo <https://github.com/hyperledger/sawtooth-sabre/>`_ and then run:
+
+.. code-block:: bash
+
+  docker-compose -f docker-compose-installed.yaml up --build
+
+Now enter the sabre-cli container:
+
+.. code-block:: bash
+
+  docker exec -it sabre-cli bash
+
+To create the flipper contract, run the following:
+
+.. code-block:: bash
+
+  sabre cr --create flipper --owner $(cat /root/.sawtooth/keys/root.pub) --url http://rest-api:9708
+  sabre upload --filename flipper.yaml --url http://rest-api:9708
+  sabre ns --create 12cd3c --url http://rest-api:9708 --owner $(cat /root/.sawtooth/keys/root.pub)
+  sabre perm 12cd3c flipper --read --write --url http://rest-api:9708
+
+To run the constructor, run:
+
+.. code-block:: bash
+
+   sabre exec --contract flipper:1.0 --payload  ./constructor --inputs 12cd3c  --outputs 12cd3c --url http://rest-api:9708
+
+Lastly run the flip functin:
+
+.. code-block:: bash
+
+  sabre exec --contract flipper:1.0 --payload  ./flip --inputs 12cd3c  --outputs 12cd3c --url http://rest-api:9708
+
 Using Solang with Hyperledger Burrow
 ------------------------------------
 
+In Burrow, Solang is used transparently by the ``burrow deploy`` tool if it is given the ``--wasm`` argument.
+When building and deploying a Solidity contract, rather than running the ``solc`` compiler, it will run
+the ``solang`` compiler and deploy it as a wasm contract.
+
 This is documented in the `burrow documentation <https://hyperledger.github.io/burrow/#/reference/wasm>`_.

+ 1 - 1
src/abi/mod.rs

@@ -6,7 +6,7 @@ pub mod substrate;
 
 pub fn generate_abi(contract: &Contract, verbose: bool) -> (String, &'static str) {
     match contract.target {
-        Target::Ewasm => {
+        Target::Ewasm | Target::Sabre => {
             if verbose {
                 eprintln!(
                     "info: Generating Ethereum ABI for contract {}",

+ 2 - 1
src/bin/solang.rs

@@ -61,7 +61,7 @@ fn main() {
                 .help("Target to build for")
                 .long("target")
                 .takes_value(true)
-                .possible_values(&["substrate", "ewasm"])
+                .possible_values(&["substrate", "ewasm", "sabre"])
                 .default_value("substrate"),
         )
         .arg(
@@ -92,6 +92,7 @@ fn main() {
     let target = match matches.value_of("TARGET") {
         Some("substrate") => solang::Target::Substrate,
         Some("ewasm") => solang::Target::Ewasm,
+        Some("sabre") => solang::Target::Sabre,
         _ => unreachable!(),
     };
 

+ 2 - 0
src/emit/mod.rs

@@ -29,6 +29,7 @@ const WASMTRIPLE: &str = "wasm32-unknown-unknown-wasm";
 
 mod ethabiencoder;
 mod ewasm;
+mod sabre;
 mod substrate;
 
 lazy_static::lazy_static! {
@@ -112,6 +113,7 @@ impl<'a> Contract<'a> {
                 substrate::SubstrateTarget::build(context, contract, filename)
             }
             super::Target::Ewasm => ewasm::EwasmTarget::build(context, contract, filename, opt),
+            super::Target::Sabre => sabre::SabreTarget::build(context, contract, filename),
         }
     }
 

+ 482 - 0
src/emit/sabre.rs

@@ -0,0 +1,482 @@
+use resolver;
+use std::str;
+
+use inkwell::context::Context;
+use inkwell::module::Linkage;
+use inkwell::values::{BasicValueEnum, FunctionValue, IntValue, PointerValue};
+use inkwell::AddressSpace;
+use inkwell::IntPredicate;
+
+use super::ethabiencoder;
+use super::{Contract, TargetRuntime};
+
+pub struct SabreTarget {
+    abi: ethabiencoder::EthAbiEncoder,
+}
+
+impl SabreTarget {
+    pub fn build<'a>(
+        context: &'a Context,
+        contract: &'a resolver::Contract,
+        filename: &'a str,
+    ) -> Contract<'a> {
+        let mut c = Contract::new(context, contract, filename, None);
+        let b = SabreTarget {
+            abi: ethabiencoder::EthAbiEncoder {},
+        };
+
+        // externals
+        b.declare_externals(&mut c);
+
+        c.emit_functions(&b);
+
+        b.emit_entrypoint(&mut c);
+
+        c
+    }
+
+    fn declare_externals(&self, contract: &mut Contract) {
+        let u8_ptr = contract.context.i8_type().ptr_type(AddressSpace::Generic);
+        contract.module.add_function(
+            "get_ptr_len",
+            contract.context.i32_type().fn_type(&[u8_ptr.into()], false),
+            Some(Linkage::External),
+        );
+        contract.module.add_function(
+            "set_state",
+            u8_ptr.fn_type(&[u8_ptr.into()], false),
+            Some(Linkage::External),
+        );
+        contract.module.add_function(
+            "get_state",
+            u8_ptr.fn_type(&[u8_ptr.into()], false),
+            Some(Linkage::External),
+        );
+        contract.module.add_function(
+            "create_collection",
+            u8_ptr.fn_type(&[u8_ptr.into()], false),
+            Some(Linkage::External),
+        );
+        contract.module.add_function(
+            "add_to_collection",
+            u8_ptr.fn_type(&[u8_ptr.into(), u8_ptr.into()], false),
+            Some(Linkage::External),
+        );
+        contract.module.add_function(
+            "alloc",
+            u8_ptr.fn_type(&[contract.context.i32_type().into()], false),
+            Some(Linkage::External),
+        );
+    }
+
+    fn emit_entrypoint(&self, contract: &mut Contract) {
+        let initializer = contract.emit_initializer(self);
+
+        let bytes_ptr = contract.context.i32_type().ptr_type(AddressSpace::Generic);
+
+        // create start function
+        let ret = contract.context.i32_type();
+        let ftype = ret.fn_type(
+            &[bytes_ptr.into(), bytes_ptr.into(), bytes_ptr.into()],
+            false,
+        );
+        let function = contract.module.add_function("entrypoint", ftype, None);
+
+        let entry = contract.context.append_basic_block(function, "entry");
+
+        contract.builder.position_at_end(&entry);
+
+        // we should not use our heap; use sabre provided heap instead
+        let argsdata = function.get_first_param().unwrap().into_pointer_value();
+        let argslen = contract
+            .builder
+            .build_call(
+                contract.module.get_function("get_ptr_len").unwrap(),
+                &[contract
+                    .builder
+                    .build_pointer_cast(
+                        argsdata,
+                        contract.context.i8_type().ptr_type(AddressSpace::Generic),
+                        "argsdata",
+                    )
+                    .into()],
+                "",
+            )
+            .try_as_basic_value()
+            .left()
+            .unwrap()
+            .into_int_value();
+
+        // We now have a reference to the abi encoded data
+        // Either this is a constructor call or a function call. A function call always starts with four
+        // bytes of function selector followed by a multiple of 32 bytes.
+        let is_function_call = contract.builder.build_int_compare(
+            IntPredicate::EQ,
+            contract.builder.build_and(
+                argslen,
+                contract.context.i32_type().const_int(31, false),
+                "",
+            ),
+            contract.context.i32_type().const_int(4, false),
+            "is_function_call",
+        );
+
+        let function_block = contract
+            .context
+            .append_basic_block(function, "function_call");
+        let constructor_block = contract
+            .context
+            .append_basic_block(function, "constructor_call");
+
+        contract.builder.build_conditional_branch(
+            is_function_call,
+            &function_block,
+            &constructor_block,
+        );
+
+        contract.builder.position_at_end(&constructor_block);
+
+        // init our storage vars
+        contract.builder.build_call(initializer, &[], "");
+
+        if let Some(con) = contract.ns.constructors.get(0) {
+            let mut args = Vec::new();
+
+            // insert abi decode
+            self.abi
+                .decode(contract, function, &mut args, argsdata, argslen, con);
+
+            contract
+                .builder
+                .build_call(contract.constructors[0], &args, "");
+        }
+
+        // return 1 for success
+        contract
+            .builder
+            .build_return(Some(&contract.context.i32_type().const_int(1, false)));
+
+        contract.builder.position_at_end(&function_block);
+
+        let fallback_block = contract.context.append_basic_block(function, "fallback");
+
+        contract.emit_function_dispatch(
+            &contract.ns.functions,
+            &contract.functions,
+            argsdata,
+            argslen,
+            function,
+            fallback_block,
+            self,
+        );
+
+        // emit fallback code
+        contract.builder.position_at_end(&fallback_block);
+
+        match contract.ns.fallback_function() {
+            Some(f) => {
+                contract.builder.build_call(contract.functions[f], &[], "");
+
+                // return 1 for success
+                contract
+                    .builder
+                    .build_return(Some(&contract.context.i32_type().const_int(1, false)));
+            }
+            None => {
+                // return -3 for failure
+                contract.builder.build_return(Some(
+                    &contract.context.i32_type().const_int(-3i64 as u64, true),
+                ));
+            }
+        }
+    }
+}
+
+impl TargetRuntime for SabreTarget {
+    fn set_storage<'a>(
+        &self,
+        contract: &'a Contract,
+        _function: FunctionValue,
+        slot: PointerValue<'a>,
+        dest: PointerValue<'a>,
+    ) {
+        let address = contract
+            .builder
+            .build_call(
+                contract.module.get_function("alloc").unwrap(),
+                &[contract.context.i32_type().const_int(64, false).into()],
+                "address",
+            )
+            .try_as_basic_value()
+            .left()
+            .unwrap()
+            .into_pointer_value();
+
+        // convert slot to address
+        contract.builder.build_call(
+            contract.module.get_function("__u256ptohex").unwrap(),
+            &[
+                contract
+                    .builder
+                    .build_pointer_cast(
+                        slot,
+                        contract.context.i8_type().ptr_type(AddressSpace::Generic),
+                        "slot",
+                    )
+                    .into(),
+                address.into(),
+            ],
+            "address_from_slot",
+        );
+
+        let data_size = dest
+            .get_type()
+            .get_element_type()
+            .into_int_type()
+            .size_of()
+            .const_cast(contract.context.i32_type(), false);
+
+        let data = contract
+            .builder
+            .build_call(
+                contract.module.get_function("alloc").unwrap(),
+                &[data_size.into()],
+                "data",
+            )
+            .try_as_basic_value()
+            .left()
+            .unwrap()
+            .into_pointer_value();
+
+        // store data in pointer collection
+        let dest = contract.builder.build_pointer_cast(
+            dest,
+            contract.context.i8_type().ptr_type(AddressSpace::Generic),
+            "dest",
+        );
+
+        contract.builder.build_call(
+            contract.module.get_function("__memcpy").unwrap(),
+            &[data.into(), dest.into(), data_size.into()],
+            "destdata",
+        );
+
+        // create collection for set_state
+        contract.builder.build_call(
+            contract.module.get_function("create_collection").unwrap(),
+            &[address.into()],
+            "",
+        );
+        contract.builder.build_call(
+            contract.module.get_function("add_to_collection").unwrap(),
+            &[address.into(), data.into()],
+            "",
+        );
+        contract.builder.build_call(
+            contract.module.get_function("set_state").unwrap(),
+            &[address.into()],
+            "",
+        );
+    }
+
+    fn get_storage<'a>(
+        &self,
+        contract: &'a Contract,
+        function: FunctionValue,
+        slot: PointerValue<'a>,
+        dest: PointerValue<'a>,
+    ) {
+        let address = contract
+            .builder
+            .build_call(
+                contract.module.get_function("alloc").unwrap(),
+                &[contract.context.i32_type().const_int(64, false).into()],
+                "address",
+            )
+            .try_as_basic_value()
+            .left()
+            .unwrap()
+            .into_pointer_value();
+
+        // convert slot to address
+        contract.builder.build_call(
+            contract.module.get_function("__u256ptohex").unwrap(),
+            &[
+                contract
+                    .builder
+                    .build_pointer_cast(
+                        slot,
+                        contract.context.i8_type().ptr_type(AddressSpace::Generic),
+                        "slot",
+                    )
+                    .into(),
+                address.into(),
+            ],
+            "address_from_slot",
+        );
+
+        // create collection for set_state
+        contract.builder.build_call(
+            contract.module.get_function("create_collection").unwrap(),
+            &[address.into()],
+            "",
+        );
+        let res = contract
+            .builder
+            .build_call(
+                contract.module.get_function("get_state").unwrap(),
+                &[address.into()],
+                "",
+            )
+            .try_as_basic_value()
+            .left()
+            .unwrap()
+            .into_pointer_value();
+
+        let state_size = contract
+            .builder
+            .build_call(
+                contract.module.get_function("get_ptr_len").unwrap(),
+                &[res.into()],
+                "",
+            )
+            .try_as_basic_value()
+            .left()
+            .unwrap()
+            .into_int_value();
+
+        let data_size = dest
+            .get_type()
+            .get_element_type()
+            .into_int_type()
+            .size_of()
+            .const_cast(contract.context.i32_type(), false);
+
+        let exists = contract.builder.build_int_compare(
+            IntPredicate::EQ,
+            state_size,
+            data_size,
+            "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);
+
+        let dest = contract.builder.build_pointer_cast(
+            dest,
+            contract.context.i8_type().ptr_type(AddressSpace::Generic),
+            "dest",
+        );
+
+        contract.builder.build_call(
+            contract.module.get_function("__memcpy").unwrap(),
+            &[dest.into(), res.into(), data_size.into()],
+            "copy_from_storage",
+        );
+
+        contract.builder.build_unconditional_branch(&done_storage);
+
+        contract.builder.position_at_end(&clear_block);
+
+        contract.builder.build_call(
+            contract.module.get_function("__memset").unwrap(),
+            &[
+                dest.into(),
+                contract.context.i8_type().const_zero().into(),
+                data_size.into(),
+            ],
+            "clear_storage",
+        );
+        contract.builder.build_unconditional_branch(&done_storage);
+
+        contract.builder.position_at_end(&done_storage);
+    }
+
+    fn return_empty_abi(&self, contract: &Contract) {
+        // return 1 for success
+        contract
+            .builder
+            .build_return(Some(&contract.context.i32_type().const_int(1, false)));
+    }
+
+    fn return_abi<'b>(&self, contract: &'b Contract, _data: PointerValue<'b>, _length: IntValue) {
+        // FIXME: how to return abi encoded return data?
+        // return 1 for success
+        contract
+            .builder
+            .build_return(Some(&contract.context.i32_type().const_int(1, false)));
+    }
+
+    fn assert_failure<'b>(&self, contract: &'b Contract) {
+        contract.builder.build_unreachable();
+    }
+
+    fn abi_encode<'b>(
+        &self,
+        contract: &'b Contract,
+        function: FunctionValue,
+        args: &[BasicValueEnum<'b>],
+        spec: &resolver::FunctionDecl,
+    ) -> (PointerValue<'b>, IntValue<'b>) {
+        let length = contract.context.i32_type().const_int(
+            spec.returns
+                .iter()
+                .map(|arg| self.abi.encoded_length(&arg.ty, contract.ns))
+                .sum(),
+            false,
+        );
+        let encoded_data = contract
+            .builder
+            .build_call(
+                contract.module.get_function("__malloc").unwrap(),
+                &[length.into()],
+                "",
+            )
+            .try_as_basic_value()
+            .left()
+            .unwrap()
+            .into_pointer_value();
+
+        // malloc returns u8*
+        let mut data = encoded_data;
+
+        for (i, arg) in spec.returns.iter().enumerate() {
+            let val = if arg.ty.is_reference_type() {
+                contract
+                    .builder
+                    .build_load(args[i].into_pointer_value(), "")
+            } else {
+                args[i]
+            };
+
+            self.abi
+                .encode_ty(contract, function, &arg.ty, val, &mut data);
+        }
+
+        (encoded_data, length)
+    }
+
+    fn abi_decode<'b>(
+        &self,
+        contract: &'b Contract,
+        function: FunctionValue,
+        args: &mut Vec<BasicValueEnum<'b>>,
+        data: PointerValue<'b>,
+        length: IntValue,
+        spec: &resolver::FunctionDecl,
+    ) {
+        self.abi
+            .decode(contract, function, args, data, length, spec);
+    }
+}

+ 3 - 0
src/lib.rs

@@ -29,6 +29,8 @@ pub enum Target {
     Substrate,
     /// Ethereum ewasm, see https://github.com/ewasm/design
     Ewasm,
+    /// Sawtooth Sabre, see https://github.com/hyperledger/sawtooth-sabre
+    Sabre,
 }
 
 impl fmt::Display for Target {
@@ -36,6 +38,7 @@ impl fmt::Display for Target {
         match self {
             Target::Substrate => write!(f, "Substrate"),
             Target::Ewasm => write!(f, "ewasm"),
+            Target::Sabre => write!(f, "Sawtooth Sabre"),
         }
     }
 }

+ 18 - 11
src/link.rs

@@ -30,6 +30,7 @@ pub fn link(input: &[u8], target: &Target) -> Vec<u8> {
     let allowed_externs = |name: &str| match target {
         Target::Ewasm => name == "main",
         Target::Substrate => name == "deploy" || name == "call",
+        Target::Sabre => name == "entrypoint",
     };
 
     for c in module.custom_sections() {
@@ -80,23 +81,29 @@ pub fn link(input: &[u8], target: &Target) -> Vec<u8> {
             }
         }
 
-        let module = if let Target::Ewasm = target {
-            "ethereum"
-        } else {
-            "env"
-        };
-
-        imports.push(ImportEntry::new(
-            module.into(),
-            "memory".into(),
-            elements::External::Memory(elements::MemoryType::new(2, Some(2))),
-        ));
+        match target {
+            Target::Ewasm => imports.push(ImportEntry::new(
+                "ethereum".into(),
+                "memory".into(),
+                elements::External::Memory(elements::MemoryType::new(2, Some(2))),
+            )),
+            Target::Substrate => imports.push(ImportEntry::new(
+                "env".into(),
+                "memory".into(),
+                elements::External::Memory(elements::MemoryType::new(2, Some(2))),
+            )),
+            Target::Sabre => exports.push(ExportEntry::new("memory".into(), Internal::Memory(0))),
+        }
     }
 
     module.clear_custom_section("linking");
 
     let mut linked = builder::module().with_module(module);
 
+    if &Target::Sabre == target {
+        linked.push_memory(Default::default());
+    }
+
     for e in exports {
         linked.push_export(e);
     }

BIN
stdlib/stdlib.bc


+ 107 - 63
stdlib/stdlib.c

@@ -3,7 +3,6 @@
 #include <stddef.h>
 #include <stdbool.h>
 
-
 /*
  * The external interface
  */
@@ -16,30 +15,44 @@
 extern void get_storage32(uint32_t key, void *dest, int32_t length);
 extern void set_storage32(uint32_t key, void *src, int32_t length);
 
-
 /*
  */
-__attribute__((visibility("hidden")))
-void __memset8(void *_dest, uint64_t val, size_t length)
+__attribute__((visibility("hidden"))) void __memset8(void *_dest, uint64_t val, size_t length)
 {
 	uint64_t *dest = _dest;
 
-	do {
+	do
+	{
 		*dest++ = val;
 	} while (--length);
 }
 
+__attribute__((visibility("hidden"))) void __memset(uint8_t *dest, uint8_t val, size_t length)
+{
+	do
+	{
+		*dest++ = val;
+	} while (--length);
+}
 /*
  * Our memcpy can only deal with multiples of 8 bytes. This is enough for
  * simple allocator below.
  */
-__attribute__((visibility("hidden")))
-void __memcpy8(void *_dest, void *_src, size_t length)
+__attribute__((visibility("hidden"))) void __memcpy8(void *_dest, void *_src, size_t length)
 {
 	uint64_t *dest = _dest;
 	uint64_t *src = _src;
 
-	do {
+	do
+	{
+		*dest++ = *src++;
+	} while (--length);
+}
+
+__attribute__((visibility("hidden"))) void __memcpy(uint8_t *dest, uint8_t *src, size_t length)
+{
+	do
+	{
 		*dest++ = *src++;
 	} while (--length);
 }
@@ -47,8 +60,7 @@ void __memcpy8(void *_dest, void *_src, size_t length)
 /*
  * Fast-ish clear, 8 bytes at a time.
  */
-__attribute__((visibility("hidden")))
-void __bzero8(void *_dest, size_t length)
+__attribute__((visibility("hidden"))) void __bzero8(void *_dest, size_t length)
 {
 	uint64_t *dest = _dest;
 
@@ -60,8 +72,7 @@ void __bzero8(void *_dest, size_t length)
 /*
  * Fast-ish set, 8 bytes at a time.
  */
-__attribute__((visibility("hidden")))
-void __bset8(void *_dest, size_t length)
+__attribute__((visibility("hidden"))) void __bset8(void *_dest, size_t length)
 {
 	int64_t *dest = _dest;
 
@@ -79,32 +90,32 @@ void __bset8(void *_dest, size_t length)
   So I think we should avoid fragmentation by neighbour merging. The most
   costly is walking the doubly linked list looking for free space.
 */
-struct chunk {
+struct chunk
+{
 	struct chunk *next, *prev;
 	size_t length;
 	bool allocated;
 };
 
-__attribute__((visibility("hidden")))
-void __init_heap()
+__attribute__((visibility("hidden"))) void __init_heap()
 {
-	struct chunk *first = (struct chunk*)0x10000;
+	struct chunk *first = (struct chunk *)0x10000;
 	first->next = first->prev = NULL;
 	first->allocated = false;
-	first->length = (size_t)
-		(__builtin_wasm_memory_size(0) -
-	         (size_t)first - sizeof(struct chunk));
+	first->length = (size_t)(__builtin_wasm_memory_size(0) -
+							 (size_t)first - sizeof(struct chunk));
 }
 
-__attribute__((visibility("hidden")))
-void __attribute__((noinline)) __free(void *m)
+__attribute__((visibility("hidden"))) void __attribute__((noinline)) __free(void *m)
 {
 	struct chunk *cur = m;
 	cur--;
-	if (m) {
+	if (m)
+	{
 		cur->allocated = false;
 		struct chunk *next = cur->next;
-		if (next && !next->allocated) {
+		if (next && !next->allocated)
+		{
 			// merge with next
 			if ((cur->next = next->next) != NULL)
 				cur->next->prev = cur;
@@ -113,7 +124,8 @@ void __attribute__((noinline)) __free(void *m)
 		}
 
 		struct chunk *prev = cur->prev;
-		if (prev && !prev->allocated) {
+		if (prev && !prev->allocated)
+		{
 			// merge with previous
 			prev->next = next;
 			next->prev = prev;
@@ -122,13 +134,13 @@ void __attribute__((noinline)) __free(void *m)
 	}
 }
 
-__attribute__((visibility("hidden")))
-static void shrink_chunk(struct chunk *cur, size_t size)
+__attribute__((visibility("hidden"))) static void shrink_chunk(struct chunk *cur, size_t size)
 {
 	// round up to nearest 8 bytes
 	size = (size + 7) & ~7;
 
-	if (cur->length - size >= (8 + sizeof(struct chunk))) {
+	if (cur->length - size >= (8 + sizeof(struct chunk)))
+	{
 		// split and return
 		void *data = (cur + 1);
 		struct chunk *new = data + size;
@@ -142,26 +154,27 @@ static void shrink_chunk(struct chunk *cur, size_t size)
 	}
 }
 
-__attribute__((visibility("hidden")))
-void* __attribute__((noinline)) __malloc(size_t size)
+__attribute__((visibility("hidden"))) void *__attribute__((noinline)) __malloc(size_t size)
 {
-	struct chunk *cur = (struct chunk*)0x10000;
+	struct chunk *cur = (struct chunk *)0x10000;
 
 	while (cur && (cur->allocated || size > cur->length))
 		cur = cur->next;
 
-	if (cur) {
+	if (cur)
+	{
 		shrink_chunk(cur, size);
 		cur->allocated = true;
 		return ++cur;
-	} else {
+	}
+	else
+	{
 		// go bang
 		__builtin_unreachable();
 	}
 }
 
-__attribute__((visibility("hidden")))
-void* __realloc(void *m, size_t size)
+__attribute__((visibility("hidden"))) void *__realloc(void *m, size_t size)
 {
 	struct chunk *cur = m;
 
@@ -169,8 +182,8 @@ void* __realloc(void *m, size_t size)
 
 	struct chunk *next = cur->next;
 
-	if (next && !next->allocated && size <=
-		(cur->length + next->length + sizeof(struct chunk))) {
+	if (next && !next->allocated && size <= (cur->length + next->length + sizeof(struct chunk)))
+	{
 		// merge with next
 		cur->next = next->next;
 		cur->next->prev = cur;
@@ -178,7 +191,9 @@ void* __realloc(void *m, size_t size)
 		// resplit ..
 		shrink_chunk(cur, size);
 		return m;
-	} else {
+	}
+	else
+	{
 		void *n = __malloc(size);
 		__memcpy8(n, m, size / 8);
 		__free(m);
@@ -190,44 +205,44 @@ void* __realloc(void *m, size_t size)
 // ABI encoding is big endian, and can have integers of 8 to 256 bits
 // (1 to 32 bytes). This function copies length bytes and reverses the
 // order since wasm is little endian.
-__attribute__((visibility("hidden")))
-void __be32toleN(uint8_t *from, uint8_t *to, uint32_t length)
+__attribute__((visibility("hidden"))) void __be32toleN(uint8_t *from, uint8_t *to, uint32_t length)
 {
 	from += 31;
 
-	do {
+	do
+	{
 		*to++ = *from--;
 	} while (--length);
 }
 
-__attribute__((visibility("hidden")))
-void __beNtoleN(uint8_t *from, uint8_t *to, uint32_t length)
+__attribute__((visibility("hidden"))) void __beNtoleN(uint8_t *from, uint8_t *to, uint32_t length)
 {
 	from += length;
 
-	do {
+	do
+	{
 		*to++ = *--from;
 	} while (--length);
 }
 
 // This function is for used for abi encoding integers
 // ABI encoding is big endian.
-__attribute__((visibility("hidden")))
-void __leNtobe32(uint8_t *from, uint8_t *to, uint32_t length)
+__attribute__((visibility("hidden"))) void __leNtobe32(uint8_t *from, uint8_t *to, uint32_t length)
 {
 	to += 31;
 
-	do {
+	do
+	{
 		*to-- = *from++;
 	} while (--length);
 }
 
-__attribute__((visibility("hidden")))
-void __leNtobeN(uint8_t *from, uint8_t *to, uint32_t length)
+__attribute__((visibility("hidden"))) void __leNtobeN(uint8_t *from, uint8_t *to, uint32_t length)
 {
 	to += length;
 
-	do {
+	do
+	{
 		*--to = *from++;
 	} while (--length);
 }
@@ -249,8 +264,7 @@ void __leNtobeN(uint8_t *from, uint8_t *to, uint32_t length)
 	r5*l4	r4*l4	r3*l4	r2*l4 	r1*l4	0		0 		0  +
     ------------------------------------------------------------
 */
-__attribute__((visibility("hidden")))
-void __mul32(uint32_t left[], uint32_t right[], uint32_t out[], int len)
+__attribute__((visibility("hidden"))) void __mul32(uint32_t left[], uint32_t right[], uint32_t out[], int len)
 {
 	uint64_t val1 = 0, carry = 0;
 
@@ -265,7 +279,8 @@ void __mul32(uint32_t left[], uint32_t right[], uint32_t out[], int len)
 	int right_start = 0, right_end = 0;
 	int left_start = 0;
 
-	for (int l = 0; l < len; l++) {
+	for (int l = 0; l < len; l++)
+	{
 		int i = 0;
 
 		if (l >= left_len)
@@ -277,7 +292,8 @@ void __mul32(uint32_t left[], uint32_t right[], uint32_t out[], int len)
 		if (right_end < right_len)
 			right_end++;
 
-		for (int r = right_end - 1; r >= right_start; r--) {
+		for (int r = right_end - 1; r >= right_start; r--)
+		{
 			uint64_t m = (uint64_t)left[left_start + i] * (uint64_t)right[r];
 			i++;
 			if (__builtin_add_overflow(val1, m, &val1))
@@ -293,10 +309,11 @@ void __mul32(uint32_t left[], uint32_t right[], uint32_t out[], int len)
 
 // Some compiler runtime builtins we need.
 
-// 128 bit shift left. 
+// 128 bit shift left.
 typedef union {
 	__uint128_t all;
-	struct {
+	struct
+	{
 		uint64_t low;
 		uint64_t high;
 	};
@@ -310,15 +327,20 @@ __uint128_t __ashlti3(__uint128_t val, int r)
 
 	in.all = val;
 
-	if (r == 0) {
+	if (r == 0)
+	{
 		// nothing to do
 		result.all = in.all;
-	} else if (r & 64) {
+	}
+	else if (r & 64)
+	{
 		// Shift more than or equal 64
 		result.low = 0;
 		result.high = in.low << (r & 63);
-	} else {
-		// Shift less than 64 
+	}
+	else
+	{
+		// Shift less than 64
 		result.low = in.low << r;
 		result.high = (in.high << r) | (in.low >> (64 - r));
 	}
@@ -334,15 +356,20 @@ __uint128_t __lshrti3(__uint128_t val, int r)
 
 	in.all = val;
 
-	if (r == 0) {
+	if (r == 0)
+	{
 		// nothing to do
 		result.all = in.all;
-	} else if (r & 64) {
+	}
+	else if (r & 64)
+	{
 		// Shift more than or equal 64
 		result.low = in.high >> (r & 63);
 		result.high = 0;
-	} else {
-		// Shift less than 64 
+	}
+	else
+	{
+		// Shift less than 64
 		result.low = (in.low >> r) | (in.high << (64 - r));
 		result.high = in.high >> r;
 	}
@@ -350,3 +377,20 @@ __uint128_t __lshrti3(__uint128_t val, int r)
 	return result.all;
 }
 
+// sabre wants the storage keys as a hex string. Convert the uint256 pointed
+// to be by v into a hex string
+char *__u256ptohex(uint8_t *v, char *str)
+{
+	// the uint256 will be stored little endian so fill it in reverse
+	str += 63;
+
+	for (int i = 0; i < 32; i++)
+	{
+		uint8_t l = (v[i] & 0x0f);
+		*str-- = l > 9 ? l + 'a' : '0' + l;
+		uint8_t h = (v[i] >> 4);
+		*str-- = h > 9 ? h + 'a' : '0' + h;
+	}
+
+	return str;
+}