Przeglądaj źródła

Add infrastructure for building multiple contracts into a single so file

When deploying a new contract, the contract field of the instruction
should specify which contract should be deployed. It should be the
keccak256 hash of the contract name.

This commit adds the infrastructure, but still builds a single so
per contract.

Signed-off-by: Sean Young <sean@mess.org>
Sean Young 4 lat temu
rodzic
commit
e532d13645

+ 4 - 2
src/emit/ewasm.rs

@@ -37,7 +37,6 @@ impl EwasmTarget {
         };
         let mut runtime_code = Binary::new(
             context,
-            contract,
             ns,
             &contract.name,
             filename,
@@ -46,6 +45,8 @@ impl EwasmTarget {
             None,
         );
 
+        runtime_code.set_early_value_aborts(contract);
+
         // externals
         b.declare_externals(&mut runtime_code);
 
@@ -65,7 +66,6 @@ impl EwasmTarget {
         };
         let mut deploy_code = Binary::new(
             context,
-            contract,
             ns,
             &contract.name,
             filename,
@@ -74,6 +74,8 @@ impl EwasmTarget {
             Some(Box::new(runtime_code)),
         );
 
+        deploy_code.set_early_value_aborts(contract);
+
         // externals
         b.declare_externals(&mut deploy_code);
 

+ 0 - 1
src/emit/generic.rs

@@ -34,7 +34,6 @@ impl GenericTarget {
 
         let mut binary = Binary::new(
             context,
-            contract,
             ns,
             &contract.name,
             filename,

+ 30 - 15
src/emit/mod.rs

@@ -5065,6 +5065,19 @@ impl<'a> Binary<'a> {
         }
     }
 
+    /// Build the LLVM IR for a set of contracts in a single namespace
+    pub fn build_bundle(
+        context: &'a Context,
+        ns: &'a ast::Namespace,
+        filename: &'a str,
+        opt: OptimizationLevel,
+        math_overflow_check: bool,
+    ) -> Self {
+        assert!(ns.target == Target::Solana);
+
+        solana::SolanaTarget::build_bundle(context, ns, filename, opt, math_overflow_check)
+    }
+
     /// Compile the bin and return the code as bytes. The result is
     /// cached, since this function can be called multiple times (e.g. one for
     /// each time a bin of this type is created).
@@ -5173,7 +5186,6 @@ impl<'a> Binary<'a> {
 
     pub fn new(
         context: &'a Context,
-        contract: &'a ast::Contract,
         ns: &'a ast::Namespace,
         name: &str,
         filename: &'a str,
@@ -5193,18 +5205,6 @@ impl<'a> Binary<'a> {
         let intr = load_stdlib(&context, &ns.target);
         module.link_in_module(intr).unwrap();
 
-        // if there is no payable function, fallback or receive then abort all value transfers at the top
-        // note that receive() is always payable so this just checkes for presence.
-        let function_abort_value_transfers = !contract.functions.iter().any(|function_no| {
-            let f = &ns.functions[*function_no];
-            !f.is_constructor() && f.is_payable()
-        });
-
-        let constructor_abort_value_transfers = !contract.functions.iter().any(|function_no| {
-            let f = &ns.functions[*function_no];
-            f.is_constructor() && f.is_payable()
-        });
-
         let selector =
             module.add_global(context.i32_type(), Some(AddressSpace::Generic), "selector");
         selector.set_linkage(Linkage::Internal);
@@ -5247,8 +5247,8 @@ impl<'a> Binary<'a> {
             name: name.to_owned(),
             module,
             runtime,
-            function_abort_value_transfers,
-            constructor_abort_value_transfers,
+            function_abort_value_transfers: false,
+            constructor_abort_value_transfers: false,
             math_overflow_check,
             builder: context.create_builder(),
             context,
@@ -5267,6 +5267,21 @@ impl<'a> Binary<'a> {
         }
     }
 
+    /// Set flags for early aborts if a value transfer is done and no function/constructor can handle it
+    pub fn set_early_value_aborts(&mut self, contract: &ast::Contract) {
+        // if there is no payable function, fallback or receive then abort all value transfers at the top
+        // note that receive() is always payable so this just checkes for presence.
+        self.function_abort_value_transfers = !contract.functions.iter().any(|function_no| {
+            let f = &self.ns.functions[*function_no];
+            !f.is_constructor() && f.is_payable()
+        });
+
+        self.constructor_abort_value_transfers = !contract.functions.iter().any(|function_no| {
+            let f = &self.ns.functions[*function_no];
+            f.is_constructor() && f.is_payable()
+        });
+    }
+
     /// llvm value type, as in chain currency (usually 128 bits int)
     fn value_type(&self) -> IntType<'a> {
         self.context

+ 0 - 1
src/emit/sabre.rs

@@ -33,7 +33,6 @@ impl SabreTarget {
         };
         let mut c = Binary::new(
             context,
-            contract,
             ns,
             &contract.name,
             filename,

+ 305 - 138
src/emit/solana.rs

@@ -2,6 +2,7 @@ use crate::codegen::cfg::HashTy;
 use crate::parser::pt;
 use crate::sema::ast;
 use std::collections::HashMap;
+use std::convert::TryInto;
 use std::str;
 
 use inkwell::module::Linkage;
@@ -21,6 +22,13 @@ pub struct SolanaTarget {
     magic: u32,
 }
 
+pub struct Contract<'a> {
+    magic: u32,
+    contract: &'a ast::Contract,
+    storage_initializer: FunctionValue<'a>,
+    constructor: Option<(FunctionValue<'a>, &'a Vec<ast::Parameter>)>,
+}
+
 // Implement the Solana target which uses BPF
 impl SolanaTarget {
     pub fn build<'a>(
@@ -37,18 +45,14 @@ impl SolanaTarget {
         let mut hash = [0u8; 32];
         hasher.update(contract.name.as_bytes());
         hasher.finalize(&mut hash);
-        let mut magic = [0u8; 4];
-
-        magic.copy_from_slice(&hash[0..4]);
 
         let mut target = SolanaTarget {
             abi: ethabiencoder::EthAbiDecoder { bswap: true },
-            magic: u32::from_le_bytes(magic),
+            magic: u32::from_le_bytes(hash[0..4].try_into().unwrap()),
         };
 
-        let mut con = Binary::new(
+        let mut binary = Binary::new(
             context,
-            contract,
             ns,
             &contract.name,
             filename,
@@ -57,25 +61,130 @@ impl SolanaTarget {
             None,
         );
 
-        con.return_values
+        binary
+            .return_values
+            .insert(ReturnCode::Success, context.i64_type().const_zero());
+        binary.return_values.insert(
+            ReturnCode::FunctionSelectorInvalid,
+            context.i64_type().const_int(2u64 << 32, false),
+        );
+        binary.return_values.insert(
+            ReturnCode::AbiEncodingInvalid,
+            context.i64_type().const_int(2u64 << 32, false),
+        );
+
+        // externals
+        target.declare_externals(&mut binary);
+
+        target.emit_functions(&mut binary, contract);
+
+        let storage_initializer = target.emit_initializer(&mut binary, contract);
+
+        let constructor = contract
+            .cfg
+            .iter()
+            .enumerate()
+            .find(|(_, cfg)| cfg.ty == pt::FunctionTy::Constructor)
+            .map(|(cfg_no, cfg)| (binary.functions[&cfg_no], &cfg.params));
+
+        target.emit_dispatch(
+            &mut binary,
+            &[Contract {
+                magic: target.magic,
+                contract,
+                storage_initializer,
+                constructor,
+            }],
+        );
+
+        binary.internalize(&[
+            "entrypoint",
+            "sol_log_",
+            "sol_alloc_free_",
+            // This entry is produced by llvm due to merging of stdlib.bc with solidity llvm ir
+            "sol_alloc_free_.1",
+        ]);
+
+        binary
+    }
+
+    /// Build a bundle of contracts from the same namespace
+    pub fn build_bundle<'a>(
+        context: &'a Context,
+        ns: &'a ast::Namespace,
+        filename: &'a str,
+        opt: OptimizationLevel,
+        math_overflow_check: bool,
+    ) -> Binary<'a> {
+        let mut target = SolanaTarget {
+            abi: ethabiencoder::EthAbiDecoder { bswap: true },
+            magic: 0,
+        };
+
+        let mut binary = Binary::new(
+            context,
+            ns,
+            "bundle",
+            filename,
+            opt,
+            math_overflow_check,
+            None,
+        );
+
+        binary
+            .return_values
             .insert(ReturnCode::Success, context.i64_type().const_zero());
-        con.return_values.insert(
+        binary.return_values.insert(
             ReturnCode::FunctionSelectorInvalid,
             context.i64_type().const_int(2u64 << 32, false),
         );
-        con.return_values.insert(
+        binary.return_values.insert(
             ReturnCode::AbiEncodingInvalid,
             context.i64_type().const_int(2u64 << 32, false),
         );
 
         // externals
-        target.declare_externals(&mut con);
+        target.declare_externals(&mut binary);
+
+        let mut contracts: Vec<Contract> = Vec::new();
+
+        for contract in &ns.contracts {
+            if !contract.is_concrete() {
+                continue;
+            }
+
+            // We need a magic number for our contract.
+            let mut hasher = Keccak::v256();
+            let mut hash = [0u8; 32];
+            hasher.update(contract.name.as_bytes());
+            hasher.finalize(&mut hash);
+
+            target.magic = u32::from_le_bytes(hash[0..4].try_into().unwrap());
+
+            target.emit_functions(&mut binary, contract);
+
+            let storage_initializer = target.emit_initializer(&mut binary, contract);
 
-        target.emit_functions(&mut con, contract);
+            let constructor = contract
+                .cfg
+                .iter()
+                .enumerate()
+                .find(|(_, cfg)| cfg.ty == pt::FunctionTy::Constructor)
+                .map(|(cfg_no, cfg)| (binary.functions[&cfg_no], &cfg.params));
 
-        target.emit_dispatch(&mut con, contract);
+            contracts.push(Contract {
+                magic: target.magic,
+                contract,
+                storage_initializer,
+                constructor,
+            });
 
-        con.internalize(&[
+            binary.functions.drain();
+        }
+
+        target.emit_dispatch(&mut binary, &contracts);
+
+        binary.internalize(&[
             "entrypoint",
             "sol_log_",
             "sol_alloc_free_",
@@ -83,7 +192,7 @@ impl SolanaTarget {
             "sol_alloc_free_.1",
         ]);
 
-        con
+        binary
     }
 
     fn declare_externals(&self, binary: &mut Binary) {
@@ -134,7 +243,7 @@ impl SolanaTarget {
     }
 
     /// Returns the SolAccountInfo of the executing binary
-    fn binary_storage_account<'b>(&self, binary: &Binary<'b>) -> PointerValue<'b> {
+    fn contract_storage_account<'b>(&self, binary: &Binary<'b>) -> PointerValue<'b> {
         let parameters = binary
             .builder
             .get_insert_block()
@@ -170,7 +279,7 @@ impl SolanaTarget {
     }
 
     /// Returns the account data of the executing binary
-    fn binary_storage_data<'b>(&self, binary: &Binary<'b>) -> PointerValue<'b> {
+    fn contract_storage_data<'b>(&self, binary: &Binary<'b>) -> PointerValue<'b> {
         let parameters = binary
             .builder
             .get_insert_block()
@@ -213,7 +322,7 @@ impl SolanaTarget {
     }
 
     /// Returns the account data length of the executing binary
-    fn binary_storage_datalen<'b>(&self, binary: &Binary<'b>) -> IntValue<'b> {
+    fn contract_storage_datalen<'b>(&self, binary: &Binary<'b>) -> IntValue<'b> {
         let parameters = binary
             .builder
             .get_insert_block()
@@ -255,9 +364,7 @@ impl SolanaTarget {
             .into_int_value()
     }
 
-    fn emit_dispatch(&mut self, binary: &mut Binary, contract: &ast::Contract) {
-        let initializer = self.emit_initializer(binary, contract);
-
+    fn emit_dispatch<'b>(&mut self, binary: &mut Binary<'b>, contracts: &[Contract<'b>]) {
         let function = binary.module.get_function("solang_dispatch").unwrap();
 
         let entry = binary.context.append_basic_block(function, "entry");
@@ -291,10 +398,10 @@ impl SolanaTarget {
         // load magic value of binary storage
         binary.parameters = Some(sol_params);
 
-        let binary_data = self.binary_storage_data(binary);
+        let storage_data = self.contract_storage_data(binary);
 
         let magic_value_ptr = binary.builder.build_pointer_cast(
-            binary_data,
+            storage_data,
             binary.context.i32_type().ptr_type(AddressSpace::Generic),
             "magic_value_ptr",
         );
@@ -334,136 +441,196 @@ impl SolanaTarget {
             &binary.context.i64_type().const_int(4u64 << 32, false),
         ));
 
-        // generate constructor code
-        binary.builder.position_at_end(constructor_block);
+        // Generate function call dispatch
+        let mut cases = Vec::new();
 
-        // do we have enough binary data
-        let binary_data_len = self.binary_storage_datalen(binary);
+        binary.builder.position_at_end(function_block);
 
-        let fixed_fields_size = contract.fixed_layout_size.to_u64().unwrap();
+        let input = binary.builder.build_pointer_cast(
+            input,
+            binary.context.i32_type().ptr_type(AddressSpace::Generic),
+            "input_ptr32",
+        );
 
-        let is_enough = binary.builder.build_int_compare(
-            IntPredicate::UGE,
-            binary_data_len,
-            binary
+        for contract in contracts {
+            let function_block = binary
                 .context
-                .i64_type()
-                .const_int(fixed_fields_size, false),
-            "is_enough",
-        );
+                .append_basic_block(function, &format!("function_{}", contract.contract.name));
+
+            binary.builder.position_at_end(function_block);
 
-        let not_enough = binary.context.append_basic_block(function, "not_enough");
-        let enough = binary.context.append_basic_block(function, "enough");
+            cases.push((
+                binary
+                    .context
+                    .i32_type()
+                    .const_int(contract.magic as u64, false),
+                function_block,
+            ));
+
+            self.emit_function_dispatch(
+                binary,
+                &contract.contract,
+                pt::FunctionTy::Function,
+                input,
+                input_len,
+                function,
+                None,
+                |_| false,
+            );
+        }
+
+        binary.builder.position_at_end(function_block);
+
+        let input = binary.builder.build_pointer_cast(
+            input,
+            binary.context.i32_type().ptr_type(AddressSpace::Generic),
+            "input_ptr32",
+        );
 
         binary
             .builder
-            .build_conditional_branch(is_enough, enough, not_enough);
+            .build_switch(magic_value, badmagic_block, &cases);
 
-        binary.builder.position_at_end(not_enough);
+        // generate constructor code
+        let mut cases = Vec::new();
 
-        binary.builder.build_return(Some(
-            &binary.context.i64_type().const_int(5u64 << 32, false),
-        ));
+        binary.builder.position_at_end(constructor_block);
 
-        binary.builder.position_at_end(enough);
+        let contract_data_len = self.contract_storage_datalen(binary);
 
-        // write our magic value to the binary
-        binary.builder.build_store(
-            magic_value_ptr,
-            binary
+        for contract in contracts {
+            let constructor_block = binary
                 .context
-                .i32_type()
-                .const_int(self.magic as u64, false),
-        );
+                .append_basic_block(function, &format!("constructor_{}", contract.contract.name));
 
-        // write heap_offset.
-        let heap_offset_ptr = unsafe {
-            binary.builder.build_gep(
-                magic_value_ptr,
-                &[binary.context.i64_type().const_int(3, false)],
-                "heap_offset",
-            )
-        };
+            binary.builder.position_at_end(constructor_block);
+
+            cases.push((
+                binary
+                    .context
+                    .i32_type()
+                    .const_int(contract.magic as u64, false),
+                constructor_block,
+            ));
 
-        // align heap to 8 bytes
-        let heap_offset = (fixed_fields_size + 7) & !7;
+            // do we have enough binary data
+            let fixed_fields_size = contract.contract.fixed_layout_size.to_u64().unwrap();
 
-        binary.builder.build_store(
-            heap_offset_ptr,
-            binary.context.i32_type().const_int(heap_offset, false),
-        );
+            let is_enough = binary.builder.build_int_compare(
+                IntPredicate::UGE,
+                contract_data_len,
+                binary
+                    .context
+                    .i64_type()
+                    .const_int(fixed_fields_size, false),
+                "is_enough",
+            );
 
-        let arg_ty = initializer.get_type().get_param_types()[0].into_pointer_type();
+            let not_enough = binary.context.append_basic_block(function, "not_enough");
+            let enough = binary.context.append_basic_block(function, "enough");
 
-        binary.builder.build_call(
-            initializer,
-            &[binary
+            binary
                 .builder
-                .build_pointer_cast(sol_params, arg_ty, "")
-                .into()],
-            "",
-        );
+                .build_conditional_branch(is_enough, enough, not_enough);
 
-        // There is only one possible constructor
-        let ret = if let Some((cfg_no, cfg)) = contract
-            .cfg
-            .iter()
-            .enumerate()
-            .find(|(_, cfg)| cfg.ty == pt::FunctionTy::Constructor)
-        {
-            let mut args = Vec::new();
+            binary.builder.position_at_end(not_enough);
 
-            // insert abi decode
-            self.abi
-                .decode(binary, function, &mut args, input, input_len, &cfg.params);
+            binary.builder.build_return(Some(
+                &binary.context.i64_type().const_int(5u64 << 32, false),
+            ));
 
-            let function = binary.functions[&cfg_no];
-            let params_ty = function
-                .get_type()
-                .get_param_types()
-                .last()
-                .unwrap()
-                .into_pointer_type();
+            binary.builder.position_at_end(enough);
 
-            args.push(
+            // write our magic value to the binary
+            binary.builder.build_store(
+                magic_value_ptr,
                 binary
+                    .context
+                    .i32_type()
+                    .const_int(contract.magic as u64, false),
+            );
+
+            // write heap_offset.
+            let heap_offset_ptr = unsafe {
+                binary.builder.build_gep(
+                    magic_value_ptr,
+                    &[binary.context.i64_type().const_int(3, false)],
+                    "heap_offset",
+                )
+            };
+
+            // align heap to 8 bytes
+            let heap_offset = (fixed_fields_size + 7) & !7;
+
+            binary.builder.build_store(
+                heap_offset_ptr,
+                binary.context.i32_type().const_int(heap_offset, false),
+            );
+
+            let arg_ty =
+                contract.storage_initializer.get_type().get_param_types()[0].into_pointer_type();
+
+            binary.builder.build_call(
+                contract.storage_initializer,
+                &[binary
                     .builder
-                    .build_pointer_cast(sol_params, params_ty, "")
-                    .into(),
+                    .build_pointer_cast(sol_params, arg_ty, "")
+                    .into()],
+                "",
             );
 
-            binary
-                .builder
-                .build_call(function, &args, "")
-                .try_as_basic_value()
-                .left()
-                .unwrap()
-        } else {
-            // return 0 for success
-            binary.context.i64_type().const_int(0, false).into()
-        };
+            // There is only one possible constructor
+            let ret = if let Some((constructor_function, params)) = contract.constructor {
+                let mut args = Vec::new();
 
-        binary.builder.build_return(Some(&ret));
+                // insert abi decode
+                self.abi
+                    .decode(binary, function, &mut args, input, input_len, params);
 
-        // Generate function call dispatch
-        binary.builder.position_at_end(function_block);
+                let params_ty = constructor_function
+                    .get_type()
+                    .get_param_types()
+                    .last()
+                    .unwrap()
+                    .into_pointer_type();
 
-        let input = binary.builder.build_pointer_cast(
-            input,
-            binary.context.i32_type().ptr_type(AddressSpace::Generic),
-            "input_ptr32",
-        );
+                args.push(
+                    binary
+                        .builder
+                        .build_pointer_cast(sol_params, params_ty, "")
+                        .into(),
+                );
 
-        self.emit_function_dispatch(
-            binary,
-            contract,
-            pt::FunctionTy::Function,
-            input,
-            input_len,
-            function,
-            None,
-            |_| false,
-        );
+                binary
+                    .builder
+                    .build_call(constructor_function, &args, "")
+                    .try_as_basic_value()
+                    .left()
+                    .unwrap()
+            } else {
+                // return 0 for success
+                binary.context.i64_type().const_int(0, false).into()
+            };
+
+            binary.builder.build_return(Some(&ret));
+        }
+
+        binary.builder.position_at_end(constructor_block);
+
+        let magic_value = binary
+            .builder
+            .build_load(
+                binary
+                    .builder
+                    .build_struct_gep(sol_params, 9, "contract")
+                    .unwrap(),
+                "magic",
+            )
+            .into_int_value();
+
+        binary
+            .builder
+            .build_switch(magic_value, badmagic_block, &cases);
     }
 
     /// Free binary storage and zero out
@@ -691,7 +858,7 @@ impl SolanaTarget {
                 .const_to_int(binary.context.i32_type())
         };
 
-        let data = self.binary_storage_data(binary);
+        let data = self.contract_storage_data(binary);
 
         let member = unsafe { binary.builder.build_gep(data, &[offset], "data") };
         let offset_ptr = binary.builder.build_pointer_cast(
@@ -883,7 +1050,7 @@ impl SolanaTarget {
             .unwrap()
             .const_cast(binary.context.i32_type(), false);
 
-        let account = self.binary_storage_account(binary);
+        let account = self.contract_storage_account(binary);
 
         // account_data_alloc will return offset = 0 if the string is length 0
         let rc = binary
@@ -1123,7 +1290,7 @@ impl<'a> TargetRuntime<'a> for SolanaTarget {
         function: FunctionValue<'a>,
     ) {
         // binary storage is in 2nd account
-        let data = self.binary_storage_data(binary);
+        let data = self.contract_storage_data(binary);
 
         self.storage_free(binary, ty, data, *slot, function, true);
     }
@@ -1174,7 +1341,7 @@ impl<'a> TargetRuntime<'a> for SolanaTarget {
         slot: IntValue<'a>,
         index: IntValue<'a>,
     ) -> IntValue<'a> {
-        let data = self.binary_storage_data(binary);
+        let data = self.contract_storage_data(binary);
 
         let member = unsafe { binary.builder.build_gep(data, &[slot], "data") };
         let offset_ptr = binary.builder.build_pointer_cast(
@@ -1242,7 +1409,7 @@ impl<'a> TargetRuntime<'a> for SolanaTarget {
         index: IntValue,
         val: IntValue,
     ) {
-        let data = self.binary_storage_data(binary);
+        let data = self.contract_storage_data(binary);
 
         let member = unsafe { binary.builder.build_gep(data, &[slot], "data") };
         let offset_ptr = binary.builder.build_pointer_cast(
@@ -1309,7 +1476,7 @@ impl<'a> TargetRuntime<'a> for SolanaTarget {
         slot: IntValue<'a>,
         index: BasicValueEnum<'a>,
     ) -> IntValue<'a> {
-        let account = self.binary_storage_account(binary);
+        let account = self.contract_storage_account(binary);
 
         if let ast::Type::Mapping(key, value) = ty.deref_any() {
             self.sparse_lookup(binary, function, key, value, slot, index)
@@ -1372,8 +1539,8 @@ impl<'a> TargetRuntime<'a> for SolanaTarget {
         slot: IntValue<'a>,
         val: BasicValueEnum<'a>,
     ) -> BasicValueEnum<'a> {
-        let data = self.binary_storage_data(binary);
-        let account = self.binary_storage_account(binary);
+        let data = self.contract_storage_data(binary);
+        let account = self.contract_storage_account(binary);
 
         let member = unsafe { binary.builder.build_gep(data, &[slot], "data") };
         let offset_ptr = binary.builder.build_pointer_cast(
@@ -1474,8 +1641,8 @@ impl<'a> TargetRuntime<'a> for SolanaTarget {
         ty: &ast::Type,
         slot: IntValue<'a>,
     ) -> BasicValueEnum<'a> {
-        let data = self.binary_storage_data(binary);
-        let account = self.binary_storage_account(binary);
+        let data = self.contract_storage_data(binary);
+        let account = self.contract_storage_account(binary);
 
         let member = unsafe { binary.builder.build_gep(data, &[slot], "data") };
         let offset_ptr = binary.builder.build_pointer_cast(
@@ -1567,7 +1734,7 @@ impl<'a> TargetRuntime<'a> for SolanaTarget {
         slot: IntValue<'a>,
         elem_ty: &ast::Type,
     ) -> IntValue<'a> {
-        let data = self.binary_storage_data(binary);
+        let data = self.contract_storage_data(binary);
 
         // the slot is simply the offset after the magic
         let member = unsafe { binary.builder.build_gep(data, &[slot], "data") };
@@ -1626,7 +1793,7 @@ impl<'a> TargetRuntime<'a> for SolanaTarget {
         slot: &mut IntValue<'a>,
         function: FunctionValue,
     ) -> BasicValueEnum<'a> {
-        let data = self.binary_storage_data(binary);
+        let data = self.contract_storage_data(binary);
 
         // the slot is simply the offset after the magic
         let member = unsafe { binary.builder.build_gep(data, &[*slot], "data") };
@@ -1847,8 +2014,8 @@ impl<'a> TargetRuntime<'a> for SolanaTarget {
         val: BasicValueEnum<'a>,
         function: FunctionValue<'a>,
     ) {
-        let data = self.binary_storage_data(binary);
-        let account = self.binary_storage_account(binary);
+        let data = self.contract_storage_data(binary);
+        let account = self.contract_storage_account(binary);
 
         // the slot is simply the offset after the magic
         let member = unsafe { binary.builder.build_gep(data, &[*slot], "data") };
@@ -2164,7 +2331,7 @@ impl<'a> TargetRuntime<'a> for SolanaTarget {
     }
 
     fn return_empty_abi(&self, binary: &Binary) {
-        let data = self.binary_storage_data(binary);
+        let data = self.contract_storage_data(binary);
 
         let header_ptr = binary.builder.build_pointer_cast(
             data,
@@ -2268,8 +2435,8 @@ impl<'a> TargetRuntime<'a> for SolanaTarget {
 
         let length = encoder.encoded_length();
 
-        let data = self.binary_storage_data(binary);
-        let account = self.binary_storage_account(binary);
+        let data = self.contract_storage_data(binary);
+        let account = self.contract_storage_account(binary);
 
         let header_ptr = binary.builder.build_pointer_cast(
             data,

+ 2 - 1
src/emit/substrate.rs

@@ -31,7 +31,6 @@ impl SubstrateTarget {
     ) -> Binary<'a> {
         let mut binary = Binary::new(
             context,
-            contract,
             ns,
             &contract.name,
             filename,
@@ -40,6 +39,8 @@ impl SubstrateTarget {
             None,
         );
 
+        binary.set_early_value_aborts(contract);
+
         let scratch_len = binary.module.add_global(
             context.i32_type(),
             Some(AddressSpace::Generic),

+ 6 - 2
tests/solana.rs

@@ -756,14 +756,18 @@ impl VirtualMachine {
         assert_eq!(res, 0);
     }
 
-    fn constructor(&mut self, args: &[Token]) {
+    fn constructor(&mut self, name: &str, args: &[Token]) {
         let program = &self.stack[0];
 
         println!("constructor for {}", hex::encode(&program.data));
 
         let mut calldata: Vec<u8> = program.data.to_vec();
 
-        calldata.extend(&0u32.to_le_bytes());
+        let mut hasher = Keccak::v256();
+        let mut hash = [0u8; 32];
+        hasher.update(name.as_bytes());
+        hasher.finalize(&mut hash);
+        calldata.extend(&hash[0..4]);
 
         if let Some(constructor) = &program.abi.constructor {
             calldata.extend(&constructor.encode_input(vec![], args).unwrap());

+ 1 - 1
tests/solana_tests/abi.rs

@@ -35,7 +35,7 @@ fn packed() {
         }"#,
     );
 
-    vm.constructor(&[]);
+    vm.constructor("bar", &[]);
 
     vm.function("test", &[]);
     vm.function("test2", &[]);

+ 8 - 8
tests/solana_tests/accessor.rs

@@ -10,7 +10,7 @@ fn types() {
         }"#,
     );
 
-    vm.constructor(&[]);
+    vm.constructor("foo", &[]);
 
     let returns = vm.function("f1", &[]);
 
@@ -23,7 +23,7 @@ fn types() {
         }"#,
     );
 
-    vm.constructor(&[]);
+    vm.constructor("foo", &[]);
 
     let returns = vm.function("f1", &[Token::Uint(ethereum_types::U256::from(2))]);
 
@@ -43,7 +43,7 @@ fn types() {
         }"#,
     );
 
-    vm.constructor(&[]);
+    vm.constructor("foo", &[]);
 
     let returns = vm.function(
         "f1",
@@ -67,7 +67,7 @@ fn types() {
         }"#,
     );
 
-    vm.constructor(&[]);
+    vm.constructor("foo", &[]);
 
     let returns = vm.function("f1", &[Token::Int(ethereum_types::U256::from(4000))]);
 
@@ -88,7 +88,7 @@ fn interfaces() {
         "#,
     );
 
-    vm.constructor(&[]);
+    vm.constructor("foo", &[]);
 
     let returns = vm.function("f1", &[]);
 
@@ -104,7 +104,7 @@ fn constant() {
         }"#,
     );
 
-    vm.constructor(&[]);
+    vm.constructor("x", &[]);
 
     let returns = vm.function("z", &[]);
 
@@ -123,7 +123,7 @@ fn constant() {
         }"#,
     );
 
-    vm.constructor(&[]);
+    vm.constructor("x", &[]);
 
     let returns = vm.function("z", &[]);
 
@@ -142,7 +142,7 @@ fn constant() {
         }"#,
     );
 
-    vm.constructor(&[]);
+    vm.constructor("x", &[]);
 
     let returns = vm.function("z", &[]);
 

+ 11 - 11
tests/solana_tests/arrays.rs

@@ -17,7 +17,7 @@ fn fixed_array() {
         }"#,
     );
 
-    vm.constructor(&[]);
+    vm.constructor("foo", &[]);
 
     let returns = vm.function("get", &[]);
 
@@ -51,7 +51,7 @@ fn fixed_array() {
         }"#,
     );
 
-    vm.constructor(&[]);
+    vm.constructor("foo", &[]);
 
     let returns = vm.function("get", &[]);
 
@@ -108,7 +108,7 @@ fn fixed_array() {
         }"#,
     );
 
-    vm.constructor(&[]);
+    vm.constructor("foo", &[]);
 
     let returns = vm.function("get", &[]);
 
@@ -171,7 +171,7 @@ fn dynamic_array_fixed_elements() {
         }"#,
     );
 
-    vm.constructor(&[]);
+    vm.constructor("foo", &[]);
 
     let returns = vm.function(
         "get",
@@ -238,7 +238,7 @@ fn fixed_array_dynamic_elements() {
         }"#,
     );
 
-    vm.constructor(&[]);
+    vm.constructor("foo", &[]);
 
     let returns = vm.function(
         "get",
@@ -305,7 +305,7 @@ fn dynamic_array_dynamic_elements() {
         }"#,
     );
 
-    vm.constructor(&[]);
+    vm.constructor("foo", &[]);
 
     let returns = vm.function(
         "get",
@@ -369,7 +369,7 @@ fn fixed_array_fixed_elements_storage() {
         }"#,
     );
 
-    vm.constructor(&[]);
+    vm.constructor("foo", &[]);
 
     vm.function(
         "set_elem",
@@ -472,7 +472,7 @@ fn fixed_array_dynamic_elements_storage() {
         }"#,
     );
 
-    vm.constructor(&[]);
+    vm.constructor("foo", &[]);
 
     vm.function(
         "set_elem",
@@ -588,7 +588,7 @@ fn storage_simple_dynamic_array() {
         }"#,
     );
 
-    vm.constructor(&[]);
+    vm.constructor("foo", &[]);
 
     let returns = vm.function("len", &[]);
 
@@ -689,7 +689,7 @@ fn storage_pop_running_on_empty() {
         }"#,
     );
 
-    vm.constructor(&[]);
+    vm.constructor("foo", &[]);
 
     vm.function("pop", &[]);
 }
@@ -746,7 +746,7 @@ fn storage_dynamic_array_of_structs() {
         }"#,
     );
 
-    vm.constructor(&[]);
+    vm.constructor("foo", &[]);
 
     let returns = vm.function("len", &[]);
 

+ 1 - 1
tests/solana_tests/builtin.rs

@@ -12,7 +12,7 @@ fn timestamp() {
         }"#,
     );
 
-    vm.constructor(&[]);
+    vm.constructor("timestamp", &[]);
 
     let returns = vm.function("mr_now", &[]);
 

+ 6 - 6
tests/solana_tests/call.rs

@@ -77,7 +77,7 @@ fn simple_external_call() {
         }"#,
     );
 
-    vm.constructor(&[]);
+    vm.constructor("bar1", &[]);
 
     vm.function("test_bar", &[Token::String(String::from("yo"))]);
 
@@ -89,7 +89,7 @@ fn simple_external_call() {
 
     vm.set_program(0);
 
-    vm.constructor(&[]);
+    vm.constructor("bar0", &[]);
 
     vm.function("test_bar", &[Token::String(String::from("uncle beau"))]);
 
@@ -119,7 +119,7 @@ fn external_call_with_returns() {
         }"#,
     );
 
-    vm.constructor(&[]);
+    vm.constructor("bar1", &[]);
 
     let res = vm.function("test_bar", &[Token::Int(ethereum_types::U256::from(21))]);
 
@@ -129,7 +129,7 @@ fn external_call_with_returns() {
 
     vm.set_program(0);
 
-    vm.constructor(&[]);
+    vm.constructor("bar0", &[]);
 
     let res = vm.function("test_other", &[Token::FixedBytes(bar1_account.to_vec())]);
 
@@ -164,7 +164,7 @@ fn external_call_with_string_returns() {
         }"#,
     );
 
-    vm.constructor(&[]);
+    vm.constructor("bar1", &[]);
 
     let res = vm.function("test_bar", &[Token::Int(ethereum_types::U256::from(22))]);
 
@@ -174,7 +174,7 @@ fn external_call_with_string_returns() {
 
     vm.set_program(0);
 
-    vm.constructor(&[]);
+    vm.constructor("bar0", &[]);
 
     let res = vm.function("test_other", &[Token::FixedBytes(bar1_account.to_vec())]);
 

+ 1 - 1
tests/solana_tests/destructure.rs

@@ -15,7 +15,7 @@ fn conditional_destructure() {
         }"#,
     );
 
-    vm.constructor(&[]);
+    vm.constructor("foo", &[]);
 
     let returns = vm.function("f", &[Token::Bool(true), Token::Bool(true)]);
 

+ 6 - 6
tests/solana_tests/hash.rs

@@ -15,7 +15,7 @@ fn constants_hash_tests() {
         }"##,
     );
 
-    runtime.constructor(&[]);
+    runtime.constructor("tester", &[]);
     runtime.function("test", &[]);
 
     let mut runtime = build_solidity(
@@ -29,7 +29,7 @@ fn constants_hash_tests() {
         }"##,
     );
 
-    runtime.constructor(&[]);
+    runtime.constructor("tester", &[]);
     runtime.function("test", &[]);
 
     let mut runtime = build_solidity(
@@ -43,7 +43,7 @@ fn constants_hash_tests() {
         }"##,
     );
 
-    runtime.constructor(&[]);
+    runtime.constructor("tester", &[]);
     runtime.function("test", &[]);
 
     // blake2 hash functions are substrate isms. Ensure they don't exist
@@ -95,7 +95,7 @@ fn hash_tests() {
         }"##,
     );
 
-    runtime.constructor(&[]);
+    runtime.constructor("tester", &[]);
     let hash = runtime.function("test", &[Token::Bytes(b"Hello, World!".to_vec())]);
 
     assert_eq!(
@@ -116,7 +116,7 @@ fn hash_tests() {
         }"##,
     );
 
-    runtime.constructor(&[]);
+    runtime.constructor("tester", &[]);
     let hash = runtime.function("test", &[Token::Bytes(b"Hello, World!".to_vec())]);
 
     assert_eq!(
@@ -138,7 +138,7 @@ fn hash_tests() {
         }"##,
     );
 
-    runtime.constructor(&[]);
+    runtime.constructor("tester", &[]);
     let hash = runtime.function("test", &[Token::Bytes(b"Hello, World!".to_vec())]);
 
     assert_eq!(

+ 6 - 6
tests/solana_tests/mappings.rs

@@ -22,7 +22,7 @@ fn simple_mapping() {
         }"#,
     );
 
-    vm.constructor(&[]);
+    vm.constructor("foo", &[]);
 
     for i in 0..10 {
         vm.function(
@@ -93,7 +93,7 @@ fn less_simple_mapping() {
         }"#,
     );
 
-    vm.constructor(&[]);
+    vm.constructor("foo", &[]);
 
     vm.function(
         "set_string",
@@ -157,7 +157,7 @@ fn string_mapping() {
         }"#,
     );
 
-    vm.constructor(&[]);
+    vm.constructor("foo", &[]);
 
     vm.function(
         "set_string",
@@ -199,7 +199,7 @@ fn mapping_in_mapping() {
         }"#,
     );
 
-    vm.constructor(&[]);
+    vm.constructor("foo", &[]);
 
     vm.function(
         "set",
@@ -271,7 +271,7 @@ fn sparse_array() {
         }"#,
     );
 
-    vm.constructor(&[]);
+    vm.constructor("foo", &[]);
 
     vm.function(
         "set_string",
@@ -330,7 +330,7 @@ fn massive_sparse_array() {
         }"#,
     );
 
-    vm.constructor(&[]);
+    vm.constructor("foo", &[]);
 
     vm.function(
         "set_string",

+ 8 - 8
tests/solana_tests/primitives.rs

@@ -23,7 +23,7 @@ fn assert_false() {
         }"#,
     );
 
-    vm.constructor(&[]);
+    vm.constructor("foo", &[]);
 
     vm.function("assert_fails", &[]);
 }
@@ -40,7 +40,7 @@ fn assert_true() {
         }"#,
     );
 
-    vm.constructor(&[]);
+    vm.constructor("foo", &[]);
 
     vm.function("assert_fails", &[]);
 }
@@ -72,7 +72,7 @@ fn boolean() {
         }"#,
     );
 
-    vm.constructor(&[]);
+    vm.constructor("foo", &[]);
 
     let returns = vm.function("return_true", &[]);
 
@@ -106,7 +106,7 @@ fn address() {
         }"#,
     );
 
-    vm.constructor(&[]);
+    vm.constructor("foo", &[]);
 
     let returns = vm.function("return_address", &[]);
 
@@ -148,7 +148,7 @@ fn test_enum() {
         }"#,
     );
 
-    vm.constructor(&[]);
+    vm.constructor("foo", &[]);
 
     let returns = vm.function("return_enum", &[]);
 
@@ -202,7 +202,7 @@ fn bytes() {
 
         let mut vm = build_solidity(&src);
 
-        vm.constructor(&[]);
+        vm.constructor("test", &[]);
 
         let returns = vm.function("return_literal", &[]);
 
@@ -377,7 +377,7 @@ fn uint() {
 
         let mut vm = build_solidity(&src);
 
-        vm.constructor(&[]);
+        vm.constructor("test", &[]);
 
         println!("width:{}", width);
 
@@ -596,7 +596,7 @@ fn int() {
 
         let mut vm = build_solidity(&src);
 
-        vm.constructor(&[]);
+        vm.constructor("test", &[]);
 
         for _ in 0..10 {
             let mut a_bs = Vec::new();

+ 6 - 6
tests/solana_tests/simple.rs

@@ -15,7 +15,7 @@ fn simple() {
         }"#,
     );
 
-    vm.constructor(&[]);
+    vm.constructor("foo", &[]);
 
     assert_eq!(vm.printbuf, "Hello from constructor");
 
@@ -39,7 +39,7 @@ fn format() {
         }"#,
     );
 
-    vm.constructor(&[]);
+    vm.constructor("foo", &[]);
 
     assert_eq!(
         vm.printbuf,
@@ -64,7 +64,7 @@ fn parameters() {
         }"#,
     );
 
-    vm.constructor(&[]);
+    vm.constructor("foo", &[]);
 
     vm.function(
         "test",
@@ -100,7 +100,7 @@ fn returns() {
         }"#,
     );
 
-    vm.constructor(&[]);
+    vm.constructor("foo", &[]);
 
     let returns = vm.function(
         "test",
@@ -121,7 +121,7 @@ fn returns() {
         }"#,
     );
 
-    vm.constructor(&[]);
+    vm.constructor("foo", &[]);
 
     let returns = vm.function(
         "test",
@@ -163,7 +163,7 @@ fn flipper() {
         }"#,
     );
 
-    vm.constructor(&[ethabi::Token::Bool(true)]);
+    vm.constructor("flipper", &[ethabi::Token::Bool(true)]);
 
     assert_eq!(
         vm.data()[0..17].to_vec(),

+ 11 - 11
tests/solana_tests/storage.rs

@@ -18,7 +18,7 @@ fn string() {
         }"#,
     );
 
-    vm.constructor(&[]);
+    vm.constructor("foo", &[]);
 
     assert_eq!(
         vm.data()[0..20].to_vec(),
@@ -94,7 +94,7 @@ fn bytes() {
         }"#,
     );
 
-    vm.constructor(&[]);
+    vm.constructor("c", &[]);
 
     assert_eq!(
         vm.data()[0..20].to_vec(),
@@ -186,7 +186,7 @@ fn bytes_set_subscript_range() {
         }"#,
     );
 
-    vm.constructor(&[]);
+    vm.constructor("c", &[]);
 
     vm.function(
         "set_foo_offset",
@@ -223,7 +223,7 @@ fn bytes_get_subscript_range() {
         }"#,
     );
 
-    vm.constructor(&[]);
+    vm.constructor("c", &[]);
 
     vm.function(
         "set_foo",
@@ -251,7 +251,7 @@ fn storage_alignment() {
         }"#,
     );
 
-    vm.constructor(&[]);
+    vm.constructor("c", &[]);
 
     assert_eq!(
         vm.data()[0..40].to_vec(),
@@ -283,7 +283,7 @@ fn bytes_push_pop() {
         }"#,
     );
 
-    vm.constructor(&[]);
+    vm.constructor("c", &[]);
 
     let returns = vm.function("get_bs", &[]);
 
@@ -326,7 +326,7 @@ fn bytes_empty_pop() {
         }"#,
     );
 
-    vm.constructor(&[]);
+    vm.constructor("c", &[]);
 
     vm.function("pop", &[]);
 }
@@ -358,7 +358,7 @@ fn simple_struct() {
         }"#,
     );
 
-    vm.constructor(&[]);
+    vm.constructor("c", &[]);
 
     vm.function("set_s2", &[]);
 
@@ -432,7 +432,7 @@ fn struct_in_struct() {
         }"#,
     );
 
-    vm.constructor(&[]);
+    vm.constructor("c", &[]);
 
     vm.function("set_s2", &[]);
 
@@ -514,7 +514,7 @@ fn string_in_struct() {
             }"#,
     );
 
-    vm.constructor(&[]);
+    vm.constructor("c", &[]);
 
     vm.function("set_s2", &[]);
 
@@ -611,7 +611,7 @@ fn complex_struct() {
         }"#,
     );
 
-    vm.constructor(&[]);
+    vm.constructor("c", &[]);
 
     vm.function("set_s2", &[]);