浏览代码

Create contract using seed and PDA

Signed-off-by: Sean Young <sean@mess.org>
Sean Young 4 年之前
父节点
当前提交
7d5fdd3fbc

+ 2 - 2
clippy.toml

@@ -1,2 +1,2 @@
-too-many-arguments-threshold = 12
-cognitive-complexity-threshold = 37
+too-many-arguments-threshold = 13
+cognitive-complexity-threshold = 37

+ 3 - 3
integration/solana/calls.spec.ts

@@ -23,9 +23,9 @@ describe('Deploy solang contract and test', () => {
 
         expect(res["0"]).toBe("102");
 
-        let address_caller = '0x' + caller.get_storage_key().toBuffer().toString('hex');
-        let address_callee = '0x' + callee.get_storage_key().toBuffer().toString('hex');
-        let address_callee2 = '0x' + callee2.get_storage_key().toBuffer().toString('hex');
+        let address_caller = '0x' + caller.get_storage_keypair().publicKey.toBuffer().toString('hex');
+        let address_callee = '0x' + callee.get_storage_keypair().publicKey.toBuffer().toString('hex');
+        let address_callee2 = '0x' + callee2.get_storage_keypair().publicKey.toBuffer().toString('hex');
         console.log("addres: " + address_callee);
 
         res = await caller.call_function(conn, "who_am_i", []);

+ 1 - 1
integration/solana/create_contract.sol

@@ -18,4 +18,4 @@ contract child {
     function say_hello() pure public {
         print("Hello there");
     }
-}
+}

+ 4 - 2
integration/solana/create_contract.spec.ts

@@ -1,6 +1,6 @@
 import { Keypair } from '@solana/web3.js';
 import expect from 'expect';
-import { establishConnection } from './index';
+import { createProgramAddress, establishConnection } from './index';
 
 describe('Deploy solang contract and test', () => {
     it('create_contract', async function () {
@@ -13,10 +13,12 @@ describe('Deploy solang contract and test', () => {
         // call the constructor
         await creator.call_constructor(conn, 'creator', []);
 
+        let seed = await createProgramAddress(creator.get_program_key());
+
         console.log("now create child");
 
         let child = await conn.createStorageAccount(creator.get_program_key(), 1024);
 
-        await creator.call_function(conn, "create_child", [], [child.publicKey, creator.get_program_key()]);
+        await creator.call_function(conn, "create_child", [], [child.publicKey, creator.get_program_key()], [seed], [creator.get_storage_keypair()]);
     });
 });

+ 53 - 9
integration/solana/index.ts

@@ -13,6 +13,8 @@ import {
 import fs from 'fs';
 import { AbiItem } from 'web3-utils';
 import { utils } from 'ethers';
+import crypto from 'crypto';
+import { SigningKey } from 'ethers/lib/utils';
 const Web3EthAbi = require('web3-eth-abi');
 
 const default_url: string = "http://localhost:8899";
@@ -38,6 +40,19 @@ export async function establishConnection(): Promise<TestConnection> {
     return new TestConnection(connection, payerAccount);
 }
 
+export async function createProgramAddress(program: PublicKey): Promise<any> {
+    while (true) {
+        let seed = crypto.randomBytes(7);
+        let pda: any = undefined;
+
+        await PublicKey.createProgramAddress([seed], program).then(v => { pda = v; }).catch(_ => { });
+
+        if (pda) {
+            return { address: pda, seed };
+        }
+    }
+}
+
 const sleep = (ms: number) => new Promise(res => setTimeout(res, ms));
 
 async function newAccountWithLamports(
@@ -126,7 +141,25 @@ class TestConnection {
 class Program {
     constructor(private programId: PublicKey, private contractStorageAccount: Keypair, private abi: string) { }
 
-    async call_constructor(test: TestConnection, contract: string, params: string[]): Promise<void> {
+    encode_seeds(seeds: any[]): Buffer {
+        let seed_encoded = Buffer.alloc(1 + seeds.map(seed => seed.seed.length + 1).reduce((a, b) => a + b, 0));
+
+        seed_encoded.writeUInt8(seeds.length);
+        let offset = 1;
+
+        seeds.forEach((v) => {
+            let seed = v.seed;
+
+            seed_encoded.writeUInt8(seed.length, offset);
+            offset += 1;
+            seed.copy(seed_encoded, offset);
+            offset += seed.length;
+        });
+
+        return seed_encoded;
+    }
+
+    async call_constructor(test: TestConnection, contract: string, params: string[], seeds: any[] = []): Promise<void> {
         let abi: AbiItem | undefined = JSON.parse(this.abi).find((e: AbiItem) => e.type == "constructor");
 
         let inputs = abi?.inputs! || [];
@@ -137,7 +170,9 @@ class Program {
 
         const data = Buffer.concat([
             this.contractStorageAccount.publicKey.toBuffer(),
+            test.payerAccount.publicKey.toBuffer(),
             Buffer.from(hash.substr(2, 8), 'hex'),
+            this.encode_seeds(seeds),
             Buffer.from(input.replace('0x', ''), 'hex')
         ]);
 
@@ -162,22 +197,29 @@ class Program {
         );
     }
 
-    async call_function(test: TestConnection, name: string, params: any[], pubkeys: PublicKey[] = []): Promise<{ [key: string]: any }> {
+    async call_function(test: TestConnection, name: string, params: any[], pubkeys: PublicKey[] = [], seeds: any[] = [], signers: Keypair[] = []): Promise<{ [key: string]: any }> {
         let abi: AbiItem = JSON.parse(this.abi).find((e: AbiItem) => e.name == name);
 
         const input: string = Web3EthAbi.encodeFunctionCall(abi, params);
         const data = Buffer.concat([
             this.contractStorageAccount.publicKey.toBuffer(),
+            test.payerAccount.publicKey.toBuffer(),
             Buffer.from('00000000', 'hex'),
+            this.encode_seeds(seeds),
             Buffer.from(input.replace('0x', ''), 'hex')
         ]);
 
         let debug = 'calling function ' + name + ' [' + params + ']';
 
-        let keys = [
-            { pubkey: this.contractStorageAccount.publicKey, isSigner: false, isWritable: true },
-            { pubkey: SYSVAR_CLOCK_PUBKEY, isSigner: false, isWritable: false },
-        ];
+        let keys = [];
+
+        seeds.forEach((seed) => {
+            keys.push({ pubkey: seed.address, isSigner: false, isWritable: true });
+        });
+
+        keys.push({ pubkey: this.contractStorageAccount.publicKey, isSigner: false, isWritable: true });
+        keys.push({ pubkey: SYSVAR_CLOCK_PUBKEY, isSigner: false, isWritable: false });
+        keys.push({ pubkey: PublicKey.default, isSigner: false, isWritable: false });
 
         for (let i = 0; i < pubkeys.length; i++) {
             keys.push({ pubkey: pubkeys[i], isSigner: false, isWritable: true });
@@ -189,10 +231,12 @@ class Program {
             data,
         });
 
+        signers.unshift(test.payerAccount);
+
         await sendAndConfirmTransaction(
             test.connection,
             new Transaction().add(instruction),
-            [test.payerAccount],
+            signers,
             {
                 skipPreflight: false,
                 commitment: 'recent',
@@ -238,7 +282,7 @@ class Program {
         return this.programId;
     }
 
-    get_storage_key(): PublicKey {
-        return this.contractStorageAccount.publicKey;
+    get_storage_keypair(): Keypair {
+        return this.contractStorageAccount;
     }
 }

+ 4 - 0
src/bin/languageserver/mod.rs

@@ -744,6 +744,7 @@ impl SolangServer {
                 gas,
                 value,
                 salt,
+                space,
             } => {
                 SolangServer::construct_expr(gas, lookup_tbl, symtab, fnc_map, ns);
                 for expp in args {
@@ -755,6 +756,9 @@ impl SolangServer {
                 if let Some(optsalt) = salt {
                     SolangServer::construct_expr(optsalt, lookup_tbl, symtab, fnc_map, ns);
                 }
+                if let Some(space) = space {
+                    SolangServer::construct_expr(space, lookup_tbl, symtab, fnc_map, ns);
+                }
             }
 
             //Hash table operation expression

+ 1 - 1
src/bin/solang.rs

@@ -374,7 +374,7 @@ fn process_filename(
                 );
             }
             // we don't generate llvm here; this is done in one go for all contracts
-            return ns;
+            continue;
         }
 
         if verbose {

+ 8 - 1
src/codegen/cfg.rs

@@ -20,6 +20,7 @@ use crate::Target;
 pub type Vars = HashMap<usize, Variable>;
 
 #[derive(Clone)]
+#[allow(clippy::large_enum_variant)]
 pub enum Instr {
     /// Set variable
     Set {
@@ -104,6 +105,7 @@ pub enum Instr {
         value: Option<Expression>,
         gas: Expression,
         salt: Option<Expression>,
+        space: Option<Expression>,
     },
     /// Call external functions. If the call fails, set the success failure
     /// or abort if this is None
@@ -876,8 +878,9 @@ impl ControlFlowGraph {
                 gas,
                 salt,
                 value,
+                space,
             } => format!(
-                "%{}, {} = constructor salt:{} value:{} gas:{} {} #{:?} ({})",
+                "%{}, {} = constructor salt:{} value:{} gas:{} space:{} {} #{:?} ({})",
                 self.vars[res].id.name,
                 match success {
                     Some(i) => format!("%{}", self.vars[i].id.name),
@@ -892,6 +895,10 @@ impl ControlFlowGraph {
                     None => "".to_string(),
                 },
                 self.expr_to_string(contract, ns, gas),
+                match space {
+                    Some(space) => self.expr_to_string(contract, ns, space),
+                    None => "".to_string(),
+                },
                 ns.contracts[*contract_no].name,
                 constructor_no,
                 args.iter()

+ 5 - 0
src/codegen/constant_folding.rs

@@ -175,6 +175,7 @@ pub fn constant_folding(cfg: &mut ControlFlowGraph, ns: &mut Namespace) {
                     value,
                     gas,
                     salt,
+                    space,
                 } => {
                     let args = args
                         .iter()
@@ -187,6 +188,9 @@ pub fn constant_folding(cfg: &mut ControlFlowGraph, ns: &mut Namespace) {
                     let salt = salt
                         .as_ref()
                         .map(|expr| expression(expr, Some(&vars), &cur, cfg, ns).0);
+                    let space = space
+                        .as_ref()
+                        .map(|expr| expression(expr, Some(&vars), &cur, cfg, ns).0);
 
                     cfg.blocks[block_no].instr[instr_no] = Instr::Constructor {
                         success: *success,
@@ -197,6 +201,7 @@ pub fn constant_folding(cfg: &mut ControlFlowGraph, ns: &mut Namespace) {
                         value,
                         gas,
                         salt,
+                        space,
                     };
                 }
                 Instr::ExternalCall {

+ 29 - 2
src/codegen/expression.rs

@@ -318,6 +318,7 @@ pub fn expression(
             value,
             gas,
             salt,
+            space,
         } => {
             let address_res = vartab.temp_anonymous(&Type::Contract(*contract_no));
 
@@ -332,6 +333,9 @@ pub fn expression(
             let salt = salt
                 .as_ref()
                 .map(|salt| expression(&salt, cfg, *contract_no, ns, vartab));
+            let space = space
+                .as_ref()
+                .map(|space| expression(&space, cfg, *contract_no, ns, vartab));
 
             cfg.add(
                 vartab,
@@ -344,6 +348,7 @@ pub fn expression(
                     value,
                     gas,
                     salt,
+                    space,
                 },
             );
 
@@ -1266,11 +1271,24 @@ pub fn emit_function_call(
                         loc: *loc,
                         packed: vec![
                             address,
+                            Expression::Builtin(
+                                *loc,
+                                vec![Type::Address(false)],
+                                Builtin::GetAddress,
+                                Vec::new(),
+                            ),
                             Expression::NumberLiteral(*loc, Type::Bytes(4), BigInt::zero()),
+                            Expression::NumberLiteral(*loc, Type::Bytes(1), BigInt::zero()),
                             args,
                         ],
                         args: Vec::new(),
-                        tys: vec![Type::Address(false), Type::Bytes(4), Type::DynamicBytes],
+                        tys: vec![
+                            Type::Address(false),
+                            Type::Address(false),
+                            Type::Bytes(4),
+                            Type::Bytes(1),
+                            Type::DynamicBytes,
+                        ],
                     },
                     None,
                 )
@@ -1325,7 +1343,9 @@ pub fn emit_function_call(
 
                 let (payload, address) = if ns.target == Target::Solana {
                     tys.insert(0, Type::Address(false));
-                    tys.insert(1, Type::Bytes(4));
+                    tys.insert(1, Type::Address(false));
+                    tys.insert(2, Type::Bytes(4));
+                    tys.insert(3, Type::Bytes(1));
 
                     (
                         Expression::AbiEncode {
@@ -1333,7 +1353,14 @@ pub fn emit_function_call(
                             tys,
                             packed: vec![
                                 address,
+                                Expression::Builtin(
+                                    *loc,
+                                    vec![Type::Address(false)],
+                                    Builtin::GetAddress,
+                                    Vec::new(),
+                                ),
                                 Expression::NumberLiteral(*loc, Type::Bytes(4), BigInt::zero()),
+                                Expression::NumberLiteral(*loc, Type::Bytes(1), BigInt::zero()),
                                 Expression::NumberLiteral(
                                     *loc,
                                     Type::Bytes(4),

+ 6 - 1
src/codegen/statements.rs

@@ -906,6 +906,7 @@ fn try_catch(
             value,
             gas,
             salt,
+            space,
             ..
         } => {
             let address_res = match returns.get(0) {
@@ -920,7 +921,10 @@ fn try_catch(
             let gas = expression(gas, cfg, callee_contract_no, ns, vartab);
             let salt = salt
                 .as_ref()
-                .map(|gas| expression(gas, cfg, callee_contract_no, ns, vartab));
+                .map(|salt| expression(salt, cfg, callee_contract_no, ns, vartab));
+            let space = space
+                .as_ref()
+                .map(|space| expression(space, cfg, callee_contract_no, ns, vartab));
 
             let args = args
                 .iter()
@@ -938,6 +942,7 @@ fn try_catch(
                     value,
                     gas,
                     salt,
+                    space,
                 },
             );
 

+ 1 - 0
src/emit/ewasm.rs

@@ -1224,6 +1224,7 @@ impl<'a> TargetRuntime<'a> for EwasmTarget {
         _gas: IntValue<'b>,
         value: Option<IntValue<'b>>,
         _salt: Option<IntValue<'b>>,
+        _space: Option<IntValue<'b>>,
         ns: &ast::Namespace,
     ) {
         let resolver_binary = &ns.contracts[contract_no];

+ 1 - 0
src/emit/generic.rs

@@ -619,6 +619,7 @@ impl<'a> TargetRuntime<'a> for GenericTarget {
         _gas: IntValue<'b>,
         _value: Option<IntValue<'b>>,
         _salt: Option<IntValue<'b>>,
+        _space: Option<IntValue<'b>>,
         _ns: &ast::Namespace,
     ) {
         panic!("generic cannot create new contracts");

+ 7 - 0
src/emit/mod.rs

@@ -246,6 +246,7 @@ pub trait TargetRuntime<'a> {
         gas: IntValue<'b>,
         value: Option<IntValue<'b>>,
         salt: Option<IntValue<'b>>,
+        space: Option<IntValue<'b>>,
         ns: &ast::Namespace,
     );
 
@@ -3750,6 +3751,7 @@ pub trait TargetRuntime<'a> {
                         value,
                         gas,
                         salt,
+                        space,
                     } => {
                         let args = &args
                             .iter()
@@ -3769,6 +3771,10 @@ pub trait TargetRuntime<'a> {
                             self.expression(bin, &v, &w.vars, function, ns)
                                 .into_int_value()
                         });
+                        let space = space.as_ref().map(|v| {
+                            self.expression(bin, &v, &w.vars, function, ns)
+                                .into_int_value()
+                        });
 
                         let success = match success {
                             Some(n) => Some(&mut w.vars.get_mut(n).unwrap().value),
@@ -3790,6 +3796,7 @@ pub trait TargetRuntime<'a> {
                             gas,
                             value,
                             salt,
+                            space,
                             ns,
                         );
 

+ 1 - 0
src/emit/sabre.rs

@@ -695,6 +695,7 @@ impl<'a> TargetRuntime<'a> for SabreTarget {
         _gas: IntValue<'b>,
         _value: Option<IntValue<'b>>,
         _salt: Option<IntValue<'b>>,
+        _space: Option<IntValue<'b>>,
         _ns: &ast::Namespace,
     ) {
         panic!("Sabre cannot create new binarys");

+ 27 - 9
src/emit/solana.rs

@@ -2562,10 +2562,11 @@ impl<'a> TargetRuntime<'a> for SolanaTarget {
         _gas: IntValue<'b>,
         value: Option<IntValue<'b>>,
         _salt: Option<IntValue<'b>>,
+        space: Option<IntValue<'b>>,
         ns: &ast::Namespace,
     ) {
         // abi encode the arguments. The
-        let mut tys = vec![ast::Type::Bytes(4)];
+        let mut tys = vec![ast::Type::Bytes(4), ast::Type::Bytes(1)];
 
         if let Some(function_no) = constructor_no {
             for param in &ns.functions[function_no].params {
@@ -2573,11 +2574,14 @@ impl<'a> TargetRuntime<'a> for SolanaTarget {
             }
         };
 
-        let packed = [binary
-            .context
-            .i32_type()
-            .const_int(ns.contracts[contract_no].selector().to_be() as u64, false)
-            .into()];
+        let packed = [
+            binary
+                .context
+                .i32_type()
+                .const_int(ns.contracts[contract_no].selector().to_be() as u64, false)
+                .into(),
+            binary.context.i8_type().const_zero().into(),
+        ];
 
         let encoder = ethabiencoder::EncoderBuilder::new(
             binary, function, false, &packed, args, &tys, true, ns,
@@ -2587,14 +2591,15 @@ impl<'a> TargetRuntime<'a> for SolanaTarget {
         let address_length = binary
             .context
             .i32_type()
-            .const_int(ns.address_length as u64, false);
+            .const_int(ns.address_length as u64 * 2, false);
 
         let malloc_length = binary
             .builder
             .build_int_add(length, address_length, "malloc_length");
 
         // The format of the payload is:
-        // 32 bytes address (will be filled in by create_contract C function)
+        // 32 bytes recv (will be filled in by create_contract C function)
+        // 32 bytes sender (will be filled in by create_contract C function)
         // 4 bytes contract selector/magic
         // remainder: eth abi encoded constructor arguments
         let payload = binary
@@ -2613,10 +2618,22 @@ impl<'a> TargetRuntime<'a> for SolanaTarget {
 
         encoder.finish(binary, function, enc, ns);
 
+        let space = binary.builder.build_int_add(
+            binary.context.i64_type().const_int(
+                ns.contracts[contract_no]
+                    .fixed_layout_size
+                    .to_u64()
+                    .unwrap(),
+                false,
+            ),
+            space.unwrap_or_else(|| binary.context.i64_type().const_int(1024, false)),
+            "space",
+        );
+
         let value = if let Some(value) = value {
             value
         } else {
-            binary.context.i64_type().const_int(0, false)
+            binary.context.i64_type().const_zero()
         };
 
         let sol_params = function.get_last_param().unwrap().into_pointer_value();
@@ -2629,6 +2646,7 @@ impl<'a> TargetRuntime<'a> for SolanaTarget {
                     payload.into(),
                     malloc_length.into(),
                     value.into(),
+                    space.into(),
                     sol_params.into(),
                 ],
                 "",

+ 1 - 0
src/emit/substrate.rs

@@ -3250,6 +3250,7 @@ impl<'a> TargetRuntime<'a> for SubstrateTarget {
         gas: IntValue<'b>,
         value: Option<IntValue<'b>>,
         salt: Option<IntValue<'b>>,
+        _space: Option<IntValue<'b>>,
         ns: &ast::Namespace,
     ) {
         let resolver_binary = &ns.contracts[contract_no];

+ 1 - 0
src/parser/pt.rs

@@ -2,6 +2,7 @@ use num_bigint::BigInt;
 use std::fmt;
 
 #[derive(Debug, PartialEq, Eq, Hash, Clone, Copy)]
+/// file no, start offset, end offset (in bytes)
 pub struct Loc(pub usize, pub usize, pub usize);
 
 #[derive(Debug, PartialEq, Clone)]

+ 3 - 0
src/sema/ast.rs

@@ -572,6 +572,7 @@ pub enum Expression {
         gas: Box<Expression>,
         value: Option<Box<Expression>>,
         salt: Option<Box<Expression>>,
+        space: Option<Box<Expression>>,
     },
     FormatString(pt::Loc, Vec<(FormatArg, Expression)>),
     Keccak256(pt::Loc, Type, Vec<Expression>),
@@ -923,6 +924,7 @@ impl Expression {
                     gas,
                     value,
                     salt,
+                    space,
                 } => Expression::Constructor {
                     loc: *loc,
                     contract_no: *contract_no,
@@ -931,6 +933,7 @@ impl Expression {
                     value: value.as_ref().map(|e| Box::new(filter(e, ctx))),
                     gas: Box::new(filter(gas, ctx)),
                     salt: salt.as_ref().map(|e| Box::new(filter(e, ctx))),
+                    space: space.as_ref().map(|e| Box::new(filter(e, ctx))),
                 },
                 Expression::Keccak256(loc, ty, args) => {
                     let args = args.iter().map(|e| filter(e, ctx)).collect();

+ 47 - 0
src/sema/expression.rs

@@ -2971,6 +2971,7 @@ fn constructor(
             value: call_args.value,
             gas: call_args.gas,
             salt: call_args.salt,
+            space: call_args.space,
         }),
         Err(()) => Err(()),
     }
@@ -3267,6 +3268,7 @@ pub fn constructor_named_args(
                 value: call_args.value,
                 gas: call_args.gas,
                 salt: call_args.salt,
+                space: call_args.space,
             });
         }
     }
@@ -3280,6 +3282,7 @@ pub fn constructor_named_args(
             value: call_args.value,
             gas: call_args.gas,
             salt: call_args.salt,
+            space: call_args.space,
         }),
         1 => Err(()),
         _ => {
@@ -6735,6 +6738,7 @@ struct CallArgs {
     gas: Box<Expression>,
     salt: Option<Box<Expression>>,
     value: Option<Box<Expression>>,
+    space: Option<Box<Expression>>,
 }
 
 /// Parse call arguments for external calls
@@ -6776,6 +6780,7 @@ fn parse_call_args(
         )),
         value: None,
         salt: None,
+        space: None,
     };
 
     for arg in args.values() {
@@ -6829,6 +6834,48 @@ fn parse_call_args(
 
                 res.gas = Box::new(cast(&arg.expr.loc(), expr, &ty, true, ns, diagnostics)?);
             }
+            "space" => {
+                if ns.target != Target::Solana {
+                    diagnostics.push(Diagnostic::error(
+                        arg.loc,
+                        format!(
+                            "‘space’ not permitted for external calls or constructors on {}",
+                            ns.target
+                        ),
+                    ));
+                    return Err(());
+                }
+
+                if external_call {
+                    diagnostics.push(Diagnostic::error(
+                        arg.loc,
+                        "‘space’ not valid for external calls".to_string(),
+                    ));
+                    return Err(());
+                }
+
+                let ty = Type::Uint(64);
+
+                let expr = expression(
+                    &arg.expr,
+                    file_no,
+                    contract_no,
+                    ns,
+                    symtable,
+                    false,
+                    diagnostics,
+                    Some(&ty),
+                )?;
+
+                res.space = Some(Box::new(cast(
+                    &arg.expr.loc(),
+                    expr,
+                    &ty,
+                    true,
+                    ns,
+                    diagnostics,
+                )?));
+            }
             "salt" => {
                 if ns.target == Target::Solana {
                     diagnostics.push(Diagnostic::error(

二进制
stdlib/bpf/solana.bc


+ 71 - 16
stdlib/solana.c

@@ -8,6 +8,8 @@ extern uint64_t solang_dispatch(const SolParameters *param);
 
 // The address 'SysvarC1ock11111111111111111111111111111111' base58 decoded
 static const SolPubkey clock_address = {0x06, 0xa7, 0xd5, 0x17, 0x18, 0xc7, 0x74, 0xc9, 0x28, 0x56, 0x63, 0x98, 0x69, 0x1d, 0x5e, 0xb6, 0x8b, 0x5e, 0xb8, 0xa3, 0x9b, 0x4b, 0x6d, 0x5c, 0x73, 0x55, 0x5b, 0x21, 0x00, 0x00, 0x00, 0x00};
+// The address '11111111111111111111111111111111' base58 decoded
+static const SolPubkey system_address = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
 
 uint64_t
 entrypoint(const uint8_t *input)
@@ -101,9 +103,74 @@ uint64_t external_call(uint8_t *input, uint32_t input_len, SolParameters *params
 }
 
 // This function creates a new address and calls its constructor.
-uint64_t create_contract(uint8_t *input, uint32_t input_len, uint64_t lamports, SolParameters *params)
+uint64_t create_contract(uint8_t *input, uint32_t input_len, uint64_t lamports, uint64_t space, SolParameters *params)
 {
-    const SolAccountInfo *new_acc = NULL;
+    SolAccountInfo *new_acc = NULL;
+    const SolSignerSeed *seed = NULL;
+
+    // find a suitable new account and seed
+    for (int i = 0; i < params->seeds_len; i++)
+    {
+        SolAccountInfo *acc = &params->ka[i];
+
+        if (acc->data_len == 0 && SolPubkey_same(&system_address, acc->owner))
+        {
+            new_acc = acc;
+            seed = &params->seeds[i];
+        }
+    }
+
+    if (!new_acc)
+    {
+        sol_log("create contract requires a new account");
+
+        return ERROR_NEW_ACCOUNT_NEEDED;
+    }
+
+    SolAccountMeta create_metas[2] = {
+        {params->account_id, true, true},
+        {new_acc->key, true, true},
+    };
+
+    // FIXME we need to add our own seed if we have one in order to fund/approve it
+    SolSignerSeeds signer_seeds[1] = {
+        {seed, 1},
+    };
+
+    // create the account
+    struct create_account
+    {
+        uint32_t instruction;
+        uint64_t lamports;
+        uint64_t space;
+        SolPubkey owner;
+    } __attribute__((__packed__)) create_account = {
+        0,
+        lamports,
+        space,
+        *params->ka[params->ka_cur].owner,
+    };
+
+    const SolInstruction create_instruction = {
+        .program_id = (SolPubkey *)&system_address,
+        .accounts = create_metas,
+        .account_len = SOL_ARRAY_SIZE(create_metas),
+        .data = (uint8_t *)&create_account,
+        .data_len = sizeof(create_account),
+    };
+
+    uint64_t ret = sol_invoke_signed_c(&create_instruction, params->ka, params->ka_num,
+                                       signer_seeds, SOL_ARRAY_SIZE(signer_seeds));
+
+    if (ret != 0)
+    {
+        sol_log("failed to create new account");
+
+        sol_panic();
+    }
+
+    // Our new account now has some space
+    new_acc->data_len = space;
 
     SolAccountMeta metas[10];
     const SolInstruction instruction = {
@@ -119,27 +186,15 @@ uint64_t create_contract(uint8_t *input, uint32_t input_len, uint64_t lamports,
     {
         const SolAccountInfo *acc = &params->ka[account_no];
 
-        uint64_t *data = (uint64_t *)acc->data;
-
-        if (!new_acc && !*data && SolPubkey_same(params->program_id, acc->owner))
-        {
-            new_acc = acc;
-            params->ka_last_called = new_acc;
-        }
-
         metas[account_no].pubkey = acc->key;
         metas[account_no].is_writable = acc->is_writable;
         metas[account_no].is_signer = acc->is_signer;
     }
 
-    if (!new_acc)
-    {
-        sol_log("create contract requires a new account");
-
-        return ERROR_NEW_ACCOUNT_NEEDED;
-    }
+    params->ka_last_called = new_acc;
 
     __memcpy8(input, new_acc->key->x, SIZE_PUBKEY / 8);
+    __memcpy8(input + SIZE_PUBKEY, params->account_id->x, SIZE_PUBKEY / 8);
 
     return sol_invoke_signed_c(&instruction, params->ka, params->ka_num, NULL, 0);
 }

+ 45 - 14
stdlib/solana_sdk.h

@@ -214,6 +214,15 @@ void sol_panic_(const char *, uint64_t, uint64_t, uint64_t);
     sol_panic();         \
   }
 
+/**
+ * Seed used to create a program address or passed to sol_invoke_signed
+ */
+typedef struct
+{
+  const uint8_t *addr; /** Seed bytes */
+  uint64_t len;        /** Length of the seed bytes */
+} SolSignerSeed;
+
 /**
  * Structure that the program's entrypoint input data is deserialized into.
  */
@@ -224,12 +233,15 @@ typedef struct
   uint64_t ka_num;       /** Number of SolAccountInfo entries in `ka` */
   uint64_t ka_cur;
   const SolAccountInfo *ka_last_called;
-  const SolPubkey *account_id;
-  const uint8_t *input;        /** pointer to the instruction data */
-  uint64_t input_len;          /** Length in bytes of the instruction data */
-  const SolPubkey *program_id; /** program_id of the currently executing program */
+  SolPubkey *account_id;
+  const uint8_t *input;  /** pointer to the instruction data */
+  uint64_t input_len;    /** Length in bytes of the instruction data */
+  SolPubkey *program_id; /** program_id of the currently executing program */
   const SolAccountInfo *ka_clock;
   uint32_t contract;
+  const SolPubkey *sender;
+  SolSignerSeed seeds[10];
+  int seeds_len;
 } SolParameters;
 
 /**
@@ -345,7 +357,7 @@ static uint64_t sol_deserialize(
   uint64_t data_len = *(uint64_t *)input;
   input += sizeof(uint64_t);
 
-  if (data_len < SIZE_PUBKEY + sizeof(uint32_t))
+  if (data_len < SIZE_PUBKEY * 2 + sizeof(uint32_t) + 1)
   {
     return ERROR_INVALID_INSTRUCTION_DATA;
   }
@@ -353,9 +365,37 @@ static uint64_t sol_deserialize(
   params->account_id = (SolPubkey *)input;
   input += SIZE_PUBKEY;
   data_len -= SIZE_PUBKEY;
+  params->sender = (SolPubkey *)input;
+  input += SIZE_PUBKEY;
+  data_len -= SIZE_PUBKEY;
+
+  // FIXME: check that sender is a signer
+
   params->contract = *(uint32_t *)input;
   input += sizeof(uint32_t);
   data_len -= sizeof(uint32_t);
+  uint8_t seeds_len = *input;
+  input += 1;
+  data_len -= 1;
+
+  for (int i = 0; i < seeds_len; i++)
+  {
+    uint8_t seed_len = *input;
+    input += 1;
+    data_len -= 1;
+
+    if (data_len < seed_len)
+    {
+      return ERROR_INVALID_INSTRUCTION_DATA;
+    }
+
+    params->seeds[i].len = seed_len;
+    params->seeds[i].addr = input;
+    input += seed_len;
+    data_len -= seed_len;
+  }
+  params->seeds_len = seeds_len;
+
   params->input_len = data_len;
   params->input = input;
   input += data_len;
@@ -414,15 +454,6 @@ typedef struct
   uint64_t data_len;        /** Length of the data in bytes */
 } SolInstruction;
 
-/**
- * Seed used to create a program address or passed to sol_invoke_signed
- */
-typedef struct
-{
-  const uint8_t *addr; /** Seed bytes */
-  uint64_t len;        /** Length of the seed bytes */
-} SolSignerSeed;
-
 /**
  * Seeds used by a signer to create a program address or passed to
  * sol_invoke_signed

+ 275 - 108
tests/solana.rs

@@ -83,6 +83,16 @@ struct CreateAccount {
     program_id: Account,
 }
 
+#[derive(Deserialize)]
+struct CreateAccountWithSeed {
+    instruction: u32,
+    base: Account,
+    seed: String,
+    _lamports: u64,
+    space: u64,
+    program_id: Account,
+}
+
 fn build_solidity(src: &str) -> VirtualMachine {
     let mut cache = FileCache::new();
 
@@ -185,10 +195,26 @@ fn build_solidity(src: &str) -> VirtualMachine {
 
 const MAX_PERMITTED_DATA_INCREASE: usize = 10 * 1024;
 
-fn serialize_parameters(input: &[u8], vm: &VirtualMachine) -> Vec<u8> {
+struct AccountRef {
+    account: Account,
+    offset: usize,
+    length: usize,
+}
+
+fn serialize_parameters(
+    input: &[u8],
+    vm: &VirtualMachine,
+    seeds: &[&(Account, Vec<u8>)],
+) -> (Vec<u8>, Vec<AccountRef>) {
+    let mut refs = Vec::new();
     let mut v: Vec<u8> = Vec::new();
 
-    fn serialize_account(v: &mut Vec<u8>, key: &Account, acc: &AccountState) {
+    fn serialize_account(
+        v: &mut Vec<u8>,
+        refs: &mut Vec<AccountRef>,
+        key: &Account,
+        acc: &AccountState,
+    ) {
         // dup_info
         v.write_u8(0xff).unwrap();
         // signer
@@ -208,6 +234,13 @@ fn serialize_parameters(input: &[u8], vm: &VirtualMachine) -> Vec<u8> {
 
         // account data
         v.write_u64::<LittleEndian>(acc.data.len() as u64).unwrap();
+
+        refs.push(AccountRef {
+            account: *key,
+            offset: v.len(),
+            length: acc.data.len(),
+        });
+
         v.write_all(&acc.data).unwrap();
         v.write_all(&[0u8; MAX_PERMITTED_DATA_INCREASE]).unwrap();
 
@@ -225,9 +258,20 @@ fn serialize_parameters(input: &[u8], vm: &VirtualMachine) -> Vec<u8> {
     v.write_u64::<LittleEndian>(vm.account_data.len() as u64)
         .unwrap();
 
+    // first do the seeds
+    for (acc, _) in seeds {
+        let data = &vm.account_data[acc];
+
+        assert!(data.data.is_empty());
+
+        serialize_account(&mut v, &mut refs, acc, data);
+    }
+
     for (acc, data) in &vm.account_data {
         //println!("acc:{} {}", hex::encode(acc), hex::encode(&data.0));
-        serialize_account(&mut v, acc, data);
+        if seeds.iter().find(|seed| seed.0 == *acc).is_none() {
+            serialize_account(&mut v, &mut refs, acc, data);
+        }
     }
 
     // calldata
@@ -237,77 +281,48 @@ fn serialize_parameters(input: &[u8], vm: &VirtualMachine) -> Vec<u8> {
     // program id
     v.write_all(&vm.stack[0].program).unwrap();
 
-    v
+    (v, refs)
 }
 
 // We want to extract the account data
-fn deserialize_parameters(input: &[u8], accounts_data: &mut HashMap<Account, AccountState>) {
-    let mut start = 0;
-
-    let ka_num = LittleEndian::read_u64(&input[start..]);
-    start += size_of::<u64>();
-
-    for _ in 0..ka_num {
-        start += 8;
-
-        let account: Account = input[start..start + 32].try_into().unwrap();
-
-        start += 32 + 32 + 8;
-
-        let data_len = LittleEndian::read_u64(&input[start..]) as usize;
-        start += size_of::<u64>();
-        let data = input[start..start + data_len].to_vec();
+fn deserialize_parameters(
+    input: &[u8],
+    refs: &[AccountRef],
+    accounts_data: &mut HashMap<Account, AccountState>,
+) {
+    for r in refs {
+        if let Some(entry) = accounts_data.get_mut(&r.account) {
+            let data = input[r.offset..r.offset + r.length].to_vec();
 
-        if let Some(entry) = accounts_data.get_mut(&account) {
             entry.data = data;
         }
-
-        start += data_len + MAX_PERMITTED_DATA_INCREASE;
-
-        let padding = start % 8;
-        if padding > 0 {
-            start += 8 - padding
-        }
-
-        start += size_of::<u64>();
     }
 }
 
 // We want to extract the account data
-fn update_parameters(input: &[u8], accounts_data: &HashMap<Account, AccountState>) {
-    let mut start = 0;
-
-    let ka_num = LittleEndian::read_u64(&input[start..]);
-    start += size_of::<u64>();
-
-    for _ in 0..ka_num {
-        start += 8;
-
-        let account: Account = input[start..start + 32].try_into().unwrap();
-
-        start += 32 + 32 + 8;
-
-        let data_len = LittleEndian::read_u64(&input[start..]) as usize;
-        start += size_of::<u64>();
+fn update_parameters(
+    input: &[u8],
+    refs: &[AccountRef],
+    accounts_data: &HashMap<Account, AccountState>,
+) {
+    for r in refs {
+        if let Some(entry) = accounts_data.get(&r.account) {
+            unsafe {
+                std::ptr::copy(
+                    r.length.to_le_bytes().as_ptr(),
+                    input[r.offset - 8..].as_ptr() as *mut u8,
+                    8,
+                );
+            }
 
-        if let Some(entry) = accounts_data.get(&account) {
             unsafe {
                 std::ptr::copy(
                     entry.data.as_ptr(),
-                    input[start..].as_ptr() as *mut u8,
-                    data_len,
+                    input[r.offset..].as_ptr() as *mut u8,
+                    r.length,
                 );
             }
         }
-
-        start += data_len + MAX_PERMITTED_DATA_INCREASE;
-
-        let padding = start % 8;
-        if padding > 0 {
-            start += 8 - padding
-        }
-
-        start += size_of::<u64>();
     }
 }
 
@@ -645,6 +660,7 @@ fn translate_slice_inner<'a, T>(
 struct SyscallInvokeSignedC<'a> {
     context: Rc<RefCell<&'a mut VirtualMachine>>,
     input: &'a [u8],
+    refs: Rc<RefCell<&'a mut Vec<AccountRef>>>,
 }
 
 impl<'a> SyscallInvokeSignedC<'a> {
@@ -683,14 +699,33 @@ impl<'a> SyscallInvokeSignedC<'a> {
     }
 }
 
+fn create_program_address(program_id: &Account, seeds: &[&[u8]]) -> Pubkey {
+    let mut hasher = Sha256::new();
+
+    for seed in seeds {
+        hasher.update(seed);
+    }
+
+    hasher.update(&program_id);
+    hasher.update(b"ProgramDerivedAddress");
+
+    let hash = hasher.finalize();
+
+    let new_address: [u8; 32] = hash.try_into().unwrap();
+
+    // the real runtime does checks if this address exists on the ed25519 curve
+
+    Pubkey(new_address)
+}
+
 impl<'a> SyscallObject<UserError> for SyscallInvokeSignedC<'a> {
     fn call(
         &mut self,
         instruction_addr: u64,
         _account_infos_addr: u64,
         _account_infos_len: u64,
-        _signers_seeds_addr: u64,
-        _signers_seeds_len: u64,
+        signers_seeds_addr: u64,
+        signers_seeds_len: u64,
         memory_mapping: &MemoryMapping,
         result: &mut Result<u64, EbpfError<UserError>>,
     ) {
@@ -700,35 +735,120 @@ impl<'a> SyscallObject<UserError> for SyscallInvokeSignedC<'a> {
 
         println!("instruction:{:?}", instruction);
 
-        if let Ok(mut context) = self.context.try_borrow_mut() {
-            if instruction.program_id.is_system_instruction() {
-                let create_account: CreateAccount =
-                    bincode::deserialize(&instruction.data).unwrap();
-
-                let address = &instruction.accounts[1].pubkey;
-
-                assert_eq!(create_account.instruction, 0);
-
-                println!(
-                    "creating account {} with space {} owner {}",
-                    hex::encode(address.0),
-                    create_account.space,
-                    hex::encode(create_account.program_id)
-                );
+        let seeds = question_mark!(
+            translate_slice::<SolSignerSeedsC>(
+                memory_mapping,
+                signers_seeds_addr,
+                signers_seeds_len
+            ),
+            result
+        );
 
-                context.account_data.insert(
-                    address.0,
-                    AccountState {
-                        data: vec![0; create_account.space as usize],
-                        owner: Some(create_account.program_id),
-                    },
-                );
+        if let Ok(mut context) = self.context.try_borrow_mut() {
+            let signers: Vec<Pubkey> = seeds
+                .iter()
+                .map(|seed| {
+                    let seeds: Vec<&[u8]> =
+                        translate_slice::<SolSignerSeedC>(memory_mapping, seed.addr, seed.len)
+                            .unwrap()
+                            .iter()
+                            .map(|seed| {
+                                translate_slice::<u8>(memory_mapping, seed.addr, seed.len).unwrap()
+                            })
+                            .collect();
+
+                    let pda = create_program_address(&context.stack[0].program, &seeds);
+
+                    println!(
+                        "pda: {} seeds {}",
+                        pda.0.to_base58(),
+                        seeds
+                            .iter()
+                            .map(hex::encode)
+                            .collect::<Vec<String>>()
+                            .join(" ")
+                    );
+
+                    pda
+                })
+                .collect();
 
-                context.programs.push(Contract {
-                    program: create_account.program_id,
-                    abi: None,
-                    data: address.0,
-                });
+            if instruction.program_id.is_system_instruction() {
+                match bincode::deserialize::<u32>(&instruction.data).unwrap() {
+                    0 => {
+                        let create_account: CreateAccount =
+                            bincode::deserialize(&instruction.data).unwrap();
+
+                        let address = &instruction.accounts[1].pubkey;
+
+                        println!("new address: {}", address.0.to_base58());
+                        for s in &signers {
+                            println!("signer: {}", s.0.to_base58());
+                        }
+                        assert!(signers.contains(&address));
+
+                        assert_eq!(create_account.instruction, 0);
+
+                        println!(
+                            "creating account {} with space {} owner {}",
+                            address.0.to_base58(),
+                            create_account.space,
+                            create_account.program_id.to_base58()
+                        );
+
+                        assert_eq!(context.account_data[&address.0].data.len(), 0);
+
+                        if let Some(entry) = context.account_data.get_mut(&address.0) {
+                            entry.data = vec![0; create_account.space as usize];
+                            entry.owner = Some(create_account.program_id);
+                        }
+
+                        let mut refs = self.refs.try_borrow_mut().unwrap();
+
+                        for r in refs.iter_mut() {
+                            if r.account == address.0 {
+                                r.length = create_account.space as usize;
+                            }
+                        }
+                    }
+                    3 => {
+                        let create_account: CreateAccountWithSeed =
+                            bincode::deserialize(&instruction.data).unwrap();
+
+                        assert_eq!(create_account.instruction, 3);
+
+                        let mut hasher = Sha256::new();
+                        hasher.update(create_account.base);
+                        hasher.update(create_account.seed);
+                        hasher.update(create_account.program_id);
+
+                        let hash = hasher.finalize();
+
+                        let new_address: [u8; 32] = hash.try_into().unwrap();
+
+                        println!(
+                            "creating account {} with space {} owner {}",
+                            hex::encode(new_address),
+                            create_account.space,
+                            hex::encode(create_account.program_id)
+                        );
+
+                        context.account_data.insert(
+                            new_address,
+                            AccountState {
+                                data: vec![0; create_account.space as usize],
+                                owner: Some(create_account.program_id),
+                            },
+                        );
+
+                        context.programs.push(Contract {
+                            program: create_account.program_id,
+                            abi: None,
+                            data: new_address,
+                        });
+                    }
+                    instruction => panic!("instruction {} not supported", instruction),
+                }
             } else {
                 let data_id: Account = instruction.data[..32].try_into().unwrap();
 
@@ -747,9 +867,11 @@ impl<'a> SyscallObject<UserError> for SyscallInvokeSignedC<'a> {
 
                 context.stack.insert(0, p);
 
-                context.execute(&instruction.data);
+                context.execute(&instruction.data, &[]);
+
+                let refs = self.refs.try_borrow().unwrap();
 
-                update_parameters(self.input, &context.account_data);
+                update_parameters(self.input, &refs, &context.account_data);
 
                 context.stack.remove(0);
             }
@@ -760,10 +882,10 @@ impl<'a> SyscallObject<UserError> for SyscallInvokeSignedC<'a> {
 }
 
 impl VirtualMachine {
-    fn execute(&mut self, calldata: &[u8]) {
+    fn execute(&mut self, calldata: &[u8], seeds: &[&(Account, Vec<u8>)]) {
         println!("running bpf with calldata:{}", hex::encode(calldata));
 
-        let mut parameter_bytes = serialize_parameters(&calldata, &self);
+        let (mut parameter_bytes, mut refs) = serialize_parameters(&calldata, &self, seeds);
         let heap = vec![0_u8; DEFAULT_HEAP_SIZE];
         let heap_region = MemoryRegion::new_from_slice(&heap, MM_HEAP_START, 0, true);
 
@@ -811,6 +933,7 @@ impl VirtualMachine {
         .unwrap();
 
         let context = Rc::new(RefCell::new(self));
+        let refs = Rc::new(RefCell::new(&mut refs));
 
         vm.bind_syscall_context_object(
             Box::new(SolLog {
@@ -840,6 +963,7 @@ impl VirtualMachine {
             Box::new(SyscallInvokeSignedC {
                 context: context.clone(),
                 input: &parameter_bytes,
+                refs: refs.clone(),
             }),
             None,
         )
@@ -850,8 +974,9 @@ impl VirtualMachine {
             .unwrap();
 
         let mut elf = context.try_borrow_mut().unwrap();
+        let refs = refs.try_borrow().unwrap();
 
-        deserialize_parameters(&parameter_bytes, &mut elf.account_data);
+        deserialize_parameters(&parameter_bytes, &refs, &mut elf.account_data);
 
         let output = &elf.account_data[&elf.stack[0].data].data;
 
@@ -871,29 +996,28 @@ impl VirtualMachine {
 
         println!("constructor for {}", hex::encode(&program.data));
 
-        let mut calldata: Vec<u8> = program.data.to_vec();
-
-        let mut hasher = Keccak::v256();
-        let mut hash = [0u8; 32];
-        hasher.update(name.as_bytes());
-        hasher.finalize(&mut hash);
-        calldata.extend(&hash[0..4]);
+        let mut calldata = VirtualMachine::input(&program.data, &account_new(), name, &[]);
 
         if let Some(constructor) = &program.abi.as_ref().unwrap().constructor {
             calldata.extend(&constructor.encode_input(vec![], args).unwrap());
         };
 
-        self.execute(&calldata);
+        self.execute(&calldata, &[]);
     }
 
-    fn function(&mut self, name: &str, args: &[Token]) -> Vec<Token> {
+    fn function(
+        &mut self,
+        name: &str,
+        args: &[Token],
+        seeds: &[&(Account, Vec<u8>)],
+    ) -> Vec<Token> {
         let program = &self.stack[0];
 
         println!("function for {}", hex::encode(&program.data));
 
-        let mut calldata: Vec<u8> = program.data.to_vec();
+        let mut calldata = VirtualMachine::input(&program.data, &account_new(), name, seeds);
 
-        calldata.extend(&0u32.to_le_bytes());
+        println!("input: {} seeds {:?}", hex::encode(&calldata), seeds);
 
         match program.abi.as_ref().unwrap().functions[name][0].encode_input(args) {
             Ok(n) => calldata.extend(&n),
@@ -902,7 +1026,7 @@ impl VirtualMachine {
 
         println!("input: {}", hex::encode(&calldata));
 
-        self.execute(&calldata);
+        self.execute(&calldata, seeds);
 
         println!("output: {}", hex::encode(&self.output));
 
@@ -913,6 +1037,35 @@ impl VirtualMachine {
             .unwrap()
     }
 
+    fn input(
+        recv: &Account,
+        sender: &Account,
+        name: &str,
+        seeds: &[&(Account, Vec<u8>)],
+    ) -> Vec<u8> {
+        let mut calldata: Vec<u8> = recv.to_vec();
+        calldata.extend_from_slice(sender);
+
+        let mut hasher = Keccak::v256();
+        let mut hash = [0u8; 32];
+        hasher.update(name.as_bytes());
+        hasher.finalize(&mut hash);
+        calldata.extend(&hash[0..4]);
+
+        let seeds_len = seeds.len() as u8;
+
+        calldata.extend(&[seeds_len]);
+
+        for (_, seed) in seeds {
+            let seed_len = seed.len() as u8;
+
+            calldata.extend(&[seed_len]);
+            calldata.extend_from_slice(seed);
+        }
+
+        calldata
+    }
+
     fn data(&self) -> &Vec<u8> {
         let program = &self.stack[0];
 
@@ -925,16 +1078,28 @@ impl VirtualMachine {
         self.stack = vec![cur];
     }
 
-    fn create_empty_account(&mut self, size: usize) {
-        let account = account_new();
+    fn create_empty_account(&mut self) -> (Account, Vec<u8>) {
+        let mut rng = rand::thread_rng();
+
+        let mut seed = [0u8; 7];
 
-        println!("new empty account {}", account.to_base58());
+        rng.fill(&mut seed[..]);
+
+        let pk = create_program_address(&self.stack[0].program, &[&seed]);
+
+        let account = pk.0;
+
+        println!(
+            "new empty account {} with seed {}",
+            account.to_base58(),
+            hex::encode(seed)
+        );
 
         self.account_data.insert(
             account,
             AccountState {
-                data: vec![0u8; size],
-                owner: Some(self.stack[0].program),
+                data: vec![],
+                owner: Some([0u8; 32]),
             },
         );
 
@@ -943,6 +1108,8 @@ impl VirtualMachine {
             abi: None,
             data: account,
         });
+
+        (account, seed.to_vec())
     }
 
     fn validate_heap(data: &[u8]) {

+ 3 - 3
tests/solana_tests/abi.rs

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

+ 8 - 7
tests/solana_tests/accessor.rs

@@ -12,7 +12,7 @@ fn types() {
 
     vm.constructor("foo", &[]);
 
-    let returns = vm.function("f1", &[]);
+    let returns = vm.function("f1", &[], &[]);
 
     assert_eq!(returns, vec![Token::Int(ethereum_types::U256::from(102))]);
 
@@ -25,7 +25,7 @@ fn types() {
 
     vm.constructor("foo", &[]);
 
-    let returns = vm.function("f1", &[Token::Uint(ethereum_types::U256::from(2))]);
+    let returns = vm.function("f1", &[Token::Uint(ethereum_types::U256::from(2))], &[]);
 
     assert_eq!(returns, vec![Token::Int(ethereum_types::U256::from(5))]);
 
@@ -51,6 +51,7 @@ fn types() {
             Token::Uint(ethereum_types::U256::from(1)),
             Token::Uint(ethereum_types::U256::from(2)),
         ],
+        &[],
     );
 
     assert_eq!(returns, vec![Token::Int(ethereum_types::U256::from(2))]);
@@ -69,7 +70,7 @@ fn types() {
 
     vm.constructor("foo", &[]);
 
-    let returns = vm.function("f1", &[Token::Int(ethereum_types::U256::from(4000))]);
+    let returns = vm.function("f1", &[Token::Int(ethereum_types::U256::from(4000))], &[]);
 
     assert_eq!(returns, vec![Token::Uint(ethereum_types::U256::from(2))]);
 }
@@ -90,7 +91,7 @@ fn interfaces() {
 
     vm.constructor("foo", &[]);
 
-    let returns = vm.function("f1", &[]);
+    let returns = vm.function("f1", &[], &[]);
 
     assert_eq!(returns, vec![Token::FixedBytes(b"ab".to_vec())]);
 }
@@ -106,7 +107,7 @@ fn constant() {
 
     vm.constructor("x", &[]);
 
-    let returns = vm.function("z", &[]);
+    let returns = vm.function("z", &[], &[]);
 
     assert_eq!(
         returns,
@@ -125,7 +126,7 @@ fn constant() {
 
     vm.constructor("x", &[]);
 
-    let returns = vm.function("z", &[]);
+    let returns = vm.function("z", &[], &[]);
 
     assert_eq!(
         returns,
@@ -144,7 +145,7 @@ fn constant() {
 
     vm.constructor("x", &[]);
 
-    let returns = vm.function("z", &[]);
+    let returns = vm.function("z", &[], &[]);
 
     assert_eq!(
         returns,

+ 87 - 40
tests/solana_tests/arrays.rs

@@ -19,7 +19,7 @@ fn fixed_array() {
 
     vm.constructor("foo", &[]);
 
-    let returns = vm.function("get", &[]);
+    let returns = vm.function("get", &[], &[]);
 
     assert_eq!(
         returns,
@@ -53,7 +53,7 @@ fn fixed_array() {
 
     vm.constructor("foo", &[]);
 
-    let returns = vm.function("get", &[]);
+    let returns = vm.function("get", &[], &[]);
 
     assert_eq!(
         returns,
@@ -110,7 +110,7 @@ fn fixed_array() {
 
     vm.constructor("foo", &[]);
 
-    let returns = vm.function("get", &[]);
+    let returns = vm.function("get", &[], &[]);
 
     assert_eq!(
         returns,
@@ -138,6 +138,7 @@ fn fixed_array() {
             ]),
             Token::Bool(true),
         ])],
+        &[],
     );
 
     assert_eq!(returns, vec![Token::Uint(ethereum_types::U256::from(26))]);
@@ -185,12 +186,13 @@ fn dynamic_array_fixed_elements() {
             ]),
             Token::Uint(ethereum_types::U256::from(102)),
         ],
+        &[],
     );
 
     assert_eq!(returns, vec![Token::Uint(ethereum_types::U256::from(26))]);
 
     // test that the abi encoder can handle fixed arrays
-    let returns = vm.function("set", &[]);
+    let returns = vm.function("set", &[], &[]);
 
     assert_eq!(
         returns,
@@ -252,11 +254,12 @@ fn fixed_array_dynamic_elements() {
             ]),
             Token::Uint(ethereum_types::U256::from(102)),
         ],
+        &[],
     );
 
     assert_eq!(returns, vec![Token::Uint(ethereum_types::U256::from(127))]);
 
-    let returns = vm.function("set", &[]);
+    let returns = vm.function("set", &[], &[]);
 
     assert_eq!(
         returns,
@@ -319,11 +322,12 @@ fn dynamic_array_dynamic_elements() {
             ]),
             Token::Uint(ethereum_types::U256::from(102)),
         ],
+        &[],
     );
 
     assert_eq!(returns, vec![Token::Uint(ethereum_types::U256::from(127))]);
 
-    let returns = vm.function("set", &[]);
+    let returns = vm.function("set", &[], &[]);
 
     assert_eq!(
         returns,
@@ -377,6 +381,7 @@ fn fixed_array_fixed_elements_storage() {
             Token::Uint(ethereum_types::U256::from(2)),
             Token::Int(ethereum_types::U256::from(12123123)),
         ],
+        &[],
     );
 
     vm.function(
@@ -385,16 +390,21 @@ fn fixed_array_fixed_elements_storage() {
             Token::Uint(ethereum_types::U256::from(3)),
             Token::Int(ethereum_types::U256::from(123456789)),
         ],
+        &[],
     );
 
-    let returns = vm.function("get_elem", &[Token::Uint(ethereum_types::U256::from(2))]);
+    let returns = vm.function(
+        "get_elem",
+        &[Token::Uint(ethereum_types::U256::from(2))],
+        &[],
+    );
 
     assert_eq!(
         returns,
         vec![Token::Int(ethereum_types::U256::from(12123123)),],
     );
 
-    let returns = vm.function("get", &[]);
+    let returns = vm.function("get", &[], &[]);
 
     assert_eq!(
         returns,
@@ -414,9 +424,10 @@ fn fixed_array_fixed_elements_storage() {
             Token::Int(ethereum_types::U256::from(3)),
             Token::Int(ethereum_types::U256::from(4)),
         ])],
+        &[],
     );
 
-    let returns = vm.function("get", &[]);
+    let returns = vm.function("get", &[], &[]);
 
     assert_eq!(
         returns,
@@ -428,9 +439,9 @@ fn fixed_array_fixed_elements_storage() {
         ]),],
     );
 
-    vm.function("del", &[]);
+    vm.function("del", &[], &[]);
 
-    let returns = vm.function("get", &[]);
+    let returns = vm.function("get", &[], &[]);
 
     assert_eq!(
         returns,
@@ -480,6 +491,7 @@ fn fixed_array_dynamic_elements_storage() {
             Token::Uint(ethereum_types::U256::from(2)),
             Token::String(String::from("abcd")),
         ],
+        &[],
     );
 
     vm.function(
@@ -490,13 +502,18 @@ fn fixed_array_dynamic_elements_storage() {
                 "you can lead a horse to water but you can’t make him drink",
             )),
         ],
+        &[],
     );
 
-    let returns = vm.function("get_elem", &[Token::Uint(ethereum_types::U256::from(2))]);
+    let returns = vm.function(
+        "get_elem",
+        &[Token::Uint(ethereum_types::U256::from(2))],
+        &[],
+    );
 
     assert_eq!(returns, vec![Token::String(String::from("abcd"))]);
 
-    let returns = vm.function("get", &[]);
+    let returns = vm.function("get", &[], &[]);
 
     assert_eq!(
         returns,
@@ -518,9 +535,10 @@ fn fixed_array_dynamic_elements_storage() {
             Token::String(String::from("c")),
             Token::String(String::from("d")),
         ])],
+        &[],
     );
 
-    let returns = vm.function("get", &[]);
+    let returns = vm.function("get", &[], &[]);
 
     assert_eq!(
         returns,
@@ -532,9 +550,9 @@ fn fixed_array_dynamic_elements_storage() {
         ]),],
     );
 
-    vm.function("del", &[]);
+    vm.function("del", &[], &[]);
 
-    let returns = vm.function("get", &[]);
+    let returns = vm.function("get", &[], &[]);
 
     assert_eq!(
         returns,
@@ -590,35 +608,48 @@ fn storage_simple_dynamic_array() {
 
     vm.constructor("foo", &[]);
 
-    let returns = vm.function("len", &[]);
+    let returns = vm.function("len", &[], &[]);
 
     assert_eq!(returns, vec![Token::Uint(ethereum_types::U256::from(0))]);
 
-    vm.function("push", &[Token::Int(ethereum_types::U256::from(102))]);
+    vm.function("push", &[Token::Int(ethereum_types::U256::from(102))], &[]);
 
-    vm.function("push_zero", &[]);
+    vm.function("push_zero", &[], &[]);
 
     vm.function(
         "push",
         &[Token::Int(ethereum_types::U256::from(12345678901u64))],
+        &[],
     );
 
-    let returns = vm.function("subscript", &[Token::Uint(ethereum_types::U256::from(0))]);
+    let returns = vm.function(
+        "subscript",
+        &[Token::Uint(ethereum_types::U256::from(0))],
+        &[],
+    );
 
     assert_eq!(returns, vec![Token::Int(ethereum_types::U256::from(102))]);
 
-    let returns = vm.function("subscript", &[Token::Uint(ethereum_types::U256::from(1))]);
+    let returns = vm.function(
+        "subscript",
+        &[Token::Uint(ethereum_types::U256::from(1))],
+        &[],
+    );
 
     assert_eq!(returns, vec![Token::Int(ethereum_types::U256::from(0))]);
 
-    let returns = vm.function("subscript", &[Token::Uint(ethereum_types::U256::from(2))]);
+    let returns = vm.function(
+        "subscript",
+        &[Token::Uint(ethereum_types::U256::from(2))],
+        &[],
+    );
 
     assert_eq!(
         returns,
         vec![Token::Int(ethereum_types::U256::from(12345678901u64))]
     );
 
-    let returns = vm.function("copy", &[]);
+    let returns = vm.function("copy", &[], &[]);
 
     assert_eq!(
         returns,
@@ -629,14 +660,14 @@ fn storage_simple_dynamic_array() {
         ])],
     );
 
-    let returns = vm.function("pop", &[]);
+    let returns = vm.function("pop", &[], &[]);
 
     assert_eq!(
         returns,
         vec![Token::Int(ethereum_types::U256::from(12345678901u64))]
     );
 
-    let returns = vm.function("len", &[]);
+    let returns = vm.function("len", &[], &[]);
 
     assert_eq!(returns, vec![Token::Uint(ethereum_types::U256::from(2))]);
 
@@ -651,9 +682,10 @@ fn storage_simple_dynamic_array() {
             Token::Int(ethereum_types::U256::from(6)),
             Token::Int(ethereum_types::U256::from(7)),
         ])],
+        &[],
     );
 
-    let returns = vm.function("copy", &[]);
+    let returns = vm.function("copy", &[], &[]);
 
     assert_eq!(
         returns,
@@ -668,9 +700,9 @@ fn storage_simple_dynamic_array() {
         ])],
     );
 
-    vm.function("rm", &[]);
+    vm.function("rm", &[], &[]);
 
-    let returns = vm.function("len", &[]);
+    let returns = vm.function("len", &[], &[]);
 
     assert_eq!(returns, vec![Token::Uint(ethereum_types::U256::from(0))]);
 }
@@ -691,7 +723,7 @@ fn storage_pop_running_on_empty() {
 
     vm.constructor("foo", &[]);
 
-    vm.function("pop", &[]);
+    vm.function("pop", &[], &[]);
 }
 
 #[test]
@@ -748,7 +780,7 @@ fn storage_dynamic_array_of_structs() {
 
     vm.constructor("foo", &[]);
 
-    let returns = vm.function("len", &[]);
+    let returns = vm.function("len", &[], &[]);
 
     assert_eq!(returns, vec![Token::Uint(ethereum_types::U256::from(0))]);
 
@@ -758,9 +790,10 @@ fn storage_dynamic_array_of_structs() {
             Token::Uint(ethereum_types::U256::from(13819038012u64)),
             Token::Bool(true),
         ])],
+        &[],
     );
 
-    vm.function("push_empty", &[]);
+    vm.function("push_empty", &[], &[]);
 
     vm.function(
         "push2",
@@ -768,9 +801,14 @@ fn storage_dynamic_array_of_structs() {
             Token::Uint(ethereum_types::U256::from(12313123141123213u64)),
             Token::Bool(true),
         ])],
+        &[],
     );
 
-    let returns = vm.function("subscript", &[Token::Uint(ethereum_types::U256::from(0))]);
+    let returns = vm.function(
+        "subscript",
+        &[Token::Uint(ethereum_types::U256::from(0))],
+        &[],
+    );
 
     assert_eq!(
         returns,
@@ -780,7 +818,11 @@ fn storage_dynamic_array_of_structs() {
         ])]
     );
 
-    let returns = vm.function("subscript", &[Token::Uint(ethereum_types::U256::from(1))]);
+    let returns = vm.function(
+        "subscript",
+        &[Token::Uint(ethereum_types::U256::from(1))],
+        &[],
+    );
 
     assert_eq!(
         returns,
@@ -790,7 +832,11 @@ fn storage_dynamic_array_of_structs() {
         ])]
     );
 
-    let returns = vm.function("subscript", &[Token::Uint(ethereum_types::U256::from(2))]);
+    let returns = vm.function(
+        "subscript",
+        &[Token::Uint(ethereum_types::U256::from(2))],
+        &[],
+    );
 
     assert_eq!(
         returns,
@@ -800,7 +846,7 @@ fn storage_dynamic_array_of_structs() {
         ])]
     );
 
-    let returns = vm.function("copy", &[]);
+    let returns = vm.function("copy", &[], &[]);
 
     assert_eq!(
         returns,
@@ -820,7 +866,7 @@ fn storage_dynamic_array_of_structs() {
         ])]
     );
 
-    let returns = vm.function("pop", &[]);
+    let returns = vm.function("pop", &[], &[]);
 
     assert_eq!(
         returns,
@@ -830,7 +876,7 @@ fn storage_dynamic_array_of_structs() {
         ])]
     );
 
-    let returns = vm.function("len", &[]);
+    let returns = vm.function("len", &[], &[]);
 
     assert_eq!(returns, vec![Token::Uint(ethereum_types::U256::from(2))]);
 
@@ -862,9 +908,10 @@ fn storage_dynamic_array_of_structs() {
                 Token::Bool(true),
             ]),
         ])],
+        &[],
     );
 
-    let returns = vm.function("copy", &[]);
+    let returns = vm.function("copy", &[], &[]);
 
     assert_eq!(
         returns,
@@ -896,9 +943,9 @@ fn storage_dynamic_array_of_structs() {
         ])]
     );
 
-    vm.function("rm", &[]);
+    vm.function("rm", &[], &[]);
 
-    let returns = vm.function("len", &[]);
+    let returns = vm.function("len", &[], &[]);
 
     assert_eq!(returns, vec![Token::Uint(ethereum_types::U256::from(0))]);
 }

+ 1 - 1
tests/solana_tests/builtin.rs

@@ -14,7 +14,7 @@ fn timestamp() {
 
     vm.constructor("timestamp", &[]);
 
-    let returns = vm.function("mr_now", &[]);
+    let returns = vm.function("mr_now", &[], &[]);
 
     assert_eq!(
         returns,

+ 54 - 8
tests/solana_tests/call.rs

@@ -79,7 +79,7 @@ fn simple_external_call() {
 
     vm.constructor("bar1", &[]);
 
-    vm.function("test_bar", &[Token::String(String::from("yo"))]);
+    vm.function("test_bar", &[Token::String(String::from("yo"))], &[]);
 
     assert_eq!(vm.printbuf, "bar1 says: yo");
 
@@ -91,13 +91,21 @@ fn simple_external_call() {
 
     vm.constructor("bar0", &[]);
 
-    vm.function("test_bar", &[Token::String(String::from("uncle beau"))]);
+    vm.function(
+        "test_bar",
+        &[Token::String(String::from("uncle beau"))],
+        &[],
+    );
 
     assert_eq!(vm.printbuf, "bar0 says: uncle beau");
 
     vm.printbuf.truncate(0);
 
-    vm.function("test_other", &[Token::FixedBytes(bar1_account.to_vec())]);
+    vm.function(
+        "test_other",
+        &[Token::FixedBytes(bar1_account.to_vec())],
+        &[],
+    );
 
     assert_eq!(vm.printbuf, "bar1 says: cross contract call");
 }
@@ -121,7 +129,11 @@ fn external_call_with_returns() {
 
     vm.constructor("bar1", &[]);
 
-    let res = vm.function("test_bar", &[Token::Int(ethereum_types::U256::from(21))]);
+    let res = vm.function(
+        "test_bar",
+        &[Token::Int(ethereum_types::U256::from(21))],
+        &[],
+    );
 
     assert_eq!(res, vec![Token::Int(ethereum_types::U256::from(24))]);
 
@@ -131,7 +143,11 @@ fn external_call_with_returns() {
 
     vm.constructor("bar0", &[]);
 
-    let res = vm.function("test_other", &[Token::FixedBytes(bar1_account.to_vec())]);
+    let res = vm.function(
+        "test_other",
+        &[Token::FixedBytes(bar1_account.to_vec())],
+        &[],
+    );
 
     assert_eq!(res, vec![Token::Int(ethereum_types::U256::from(15))]);
 }
@@ -151,6 +167,10 @@ fn external_call_with_string_returns() {
                 address a = x.who_am_i();
                 assert(a == address(x));
             }
+
+            function test_sender(bar1 x) public returns (address) {
+                return x.who_is_sender();
+            }
         }
 
         contract bar1 {
@@ -161,12 +181,20 @@ fn external_call_with_string_returns() {
             function who_am_i() public returns (address) {
                 return address(this);
             }
+
+            function who_is_sender() public returns (address) {
+                return msg.sender;
+            }
         }"#,
     );
 
     vm.constructor("bar1", &[]);
 
-    let res = vm.function("test_bar", &[Token::Int(ethereum_types::U256::from(22))]);
+    let res = vm.function(
+        "test_bar",
+        &[Token::Int(ethereum_types::U256::from(22))],
+        &[],
+    );
 
     assert_eq!(res, vec![Token::String(String::from("foo:22"))]);
 
@@ -176,9 +204,27 @@ fn external_call_with_string_returns() {
 
     vm.constructor("bar0", &[]);
 
-    let res = vm.function("test_other", &[Token::FixedBytes(bar1_account.to_vec())]);
+    let bar0_account = vm.stack[0].data;
+
+    let res = vm.function(
+        "test_other",
+        &[Token::FixedBytes(bar1_account.to_vec())],
+        &[],
+    );
 
     assert_eq!(res, vec![Token::String(String::from("foo:7"))]);
 
-    vm.function("test_this", &[Token::FixedBytes(bar1_account.to_vec())]);
+    vm.function(
+        "test_this",
+        &[Token::FixedBytes(bar1_account.to_vec())],
+        &[],
+    );
+
+    let res = vm.function(
+        "test_sender",
+        &[Token::FixedBytes(bar1_account.to_vec())],
+        &[],
+    );
+
+    assert_eq!(res[0], Token::FixedBytes(bar0_account.to_vec()));
 }

+ 7 - 6
tests/solana_tests/create_contract.rs

@@ -33,9 +33,9 @@ fn simple_create_contract() {
 
     vm.constructor("bar0", &[]);
 
-    vm.create_empty_account(8192);
+    let seed = vm.create_empty_account();
 
-    let bar1 = vm.function("test_other", &[]);
+    let bar1 = vm.function("test_other", &[], &[&seed]);
 
     assert_eq!(vm.printbuf, "bar1 says: yo from bar0");
 
@@ -46,6 +46,7 @@ fn simple_create_contract() {
     vm.function(
         "call_bar1_at_address",
         &[bar1[0].clone(), Token::String(String::from("xywoleh"))],
+        &[],
     );
 
     assert_eq!(vm.printbuf, "Hello xywoleh");
@@ -84,7 +85,7 @@ fn missing_contract() {
 
     vm.constructor("bar0", &[]);
 
-    let _ = vm.function("test_other", &[]);
+    let _ = vm.function("test_other", &[], &[]);
 }
 
 #[test]
@@ -111,10 +112,10 @@ fn two_contracts() {
 
     vm.constructor("bar0", &[]);
 
-    vm.create_empty_account(8192);
-    vm.create_empty_account(8192);
+    let seed1 = vm.create_empty_account();
+    let seed2 = vm.create_empty_account();
 
-    let _bar1 = vm.function("test_other", &[]);
+    let _bar1 = vm.function("test_other", &[], &[&seed1, &seed2]);
 
     assert_eq!(
         vm.printbuf,

+ 4 - 4
tests/solana_tests/destructure.rs

@@ -17,7 +17,7 @@ fn conditional_destructure() {
 
     vm.constructor("foo", &[]);
 
-    let returns = vm.function("f", &[Token::Bool(true), Token::Bool(true)]);
+    let returns = vm.function("f", &[Token::Bool(true), Token::Bool(true)], &[]);
 
     assert_eq!(
         returns,
@@ -27,7 +27,7 @@ fn conditional_destructure() {
         ]
     );
 
-    let returns = vm.function("f", &[Token::Bool(true), Token::Bool(false)]);
+    let returns = vm.function("f", &[Token::Bool(true), Token::Bool(false)], &[]);
 
     assert_eq!(
         returns,
@@ -37,7 +37,7 @@ fn conditional_destructure() {
         ]
     );
 
-    let returns = vm.function("f", &[Token::Bool(false), Token::Bool(false)]);
+    let returns = vm.function("f", &[Token::Bool(false), Token::Bool(false)], &[]);
 
     assert_eq!(
         returns,
@@ -47,7 +47,7 @@ fn conditional_destructure() {
         ]
     );
 
-    let returns = vm.function("f", &[Token::Bool(false), Token::Bool(true)]);
+    let returns = vm.function("f", &[Token::Bool(false), Token::Bool(true)], &[]);
 
     assert_eq!(
         returns,

+ 6 - 6
tests/solana_tests/hash.rs

@@ -16,7 +16,7 @@ fn constants_hash_tests() {
     );
 
     runtime.constructor("tester", &[]);
-    runtime.function("test", &[]);
+    runtime.function("test", &[], &[]);
 
     let mut runtime = build_solidity(
         r##"
@@ -30,7 +30,7 @@ fn constants_hash_tests() {
     );
 
     runtime.constructor("tester", &[]);
-    runtime.function("test", &[]);
+    runtime.function("test", &[], &[]);
 
     let mut runtime = build_solidity(
         r##"
@@ -44,7 +44,7 @@ fn constants_hash_tests() {
     );
 
     runtime.constructor("tester", &[]);
-    runtime.function("test", &[]);
+    runtime.function("test", &[], &[]);
 
     // blake2 hash functions are substrate isms. Ensure they don't exist
     let ns = parse_and_resolve(
@@ -96,7 +96,7 @@ fn hash_tests() {
     );
 
     runtime.constructor("tester", &[]);
-    let hash = runtime.function("test", &[Token::Bytes(b"Hello, World!".to_vec())]);
+    let hash = runtime.function("test", &[Token::Bytes(b"Hello, World!".to_vec())], &[]);
 
     assert_eq!(
         hash,
@@ -117,7 +117,7 @@ fn hash_tests() {
     );
 
     runtime.constructor("tester", &[]);
-    let hash = runtime.function("test", &[Token::Bytes(b"Hello, World!".to_vec())]);
+    let hash = runtime.function("test", &[Token::Bytes(b"Hello, World!".to_vec())], &[]);
 
     assert_eq!(
         hash,
@@ -139,7 +139,7 @@ fn hash_tests() {
     );
 
     runtime.constructor("tester", &[]);
-    let hash = runtime.function("test", &[Token::Bytes(b"Hello, World!".to_vec())]);
+    let hash = runtime.function("test", &[Token::Bytes(b"Hello, World!".to_vec())], &[]);
 
     assert_eq!(
         hash,

+ 33 - 10
tests/solana_tests/mappings.rs

@@ -31,11 +31,16 @@ fn simple_mapping() {
                 Token::Uint(ethereum_types::U256::from(102 + i)),
                 Token::Uint(ethereum_types::U256::from(300331 + i)),
             ],
+            &[],
         );
     }
 
     for i in 0..10 {
-        let returns = vm.function("get", &[Token::Uint(ethereum_types::U256::from(102 + i))]);
+        let returns = vm.function(
+            "get",
+            &[Token::Uint(ethereum_types::U256::from(102 + i))],
+            &[],
+        );
 
         assert_eq!(
             returns,
@@ -43,14 +48,18 @@ fn simple_mapping() {
         );
     }
 
-    let returns = vm.function("get", &[Token::Uint(ethereum_types::U256::from(101))]);
+    let returns = vm.function("get", &[Token::Uint(ethereum_types::U256::from(101))], &[]);
 
     assert_eq!(returns, vec![Token::Uint(ethereum_types::U256::from(0))]);
 
-    vm.function("rm", &[Token::Uint(ethereum_types::U256::from(104))]);
+    vm.function("rm", &[Token::Uint(ethereum_types::U256::from(104))], &[]);
 
     for i in 0..10 {
-        let returns = vm.function("get", &[Token::Uint(ethereum_types::U256::from(102 + i))]);
+        let returns = vm.function(
+            "get",
+            &[Token::Uint(ethereum_types::U256::from(102 + i))],
+            &[],
+        );
 
         if 102 + i != 104 {
             assert_eq!(
@@ -100,7 +109,7 @@ fn less_simple_mapping() {
         &[
             Token::Uint(ethereum_types::U256::from(12313132131321312311213131u128)),
             Token::String(String::from("This is a string which should be a little longer than 32 bytes so we the the abi encoder")),
-        ],
+        ], &[]
     );
 
     vm.function(
@@ -109,6 +118,7 @@ fn less_simple_mapping() {
             Token::Uint(ethereum_types::U256::from(12313132131321312311213131u128)),
             Token::Int(ethereum_types::U256::from(102)),
         ],
+        &[],
     );
 
     let returns = vm.function(
@@ -116,6 +126,7 @@ fn less_simple_mapping() {
         &[Token::Uint(ethereum_types::U256::from(
             12313132131321312311213131u128,
         ))],
+        &[],
     );
 
     assert_eq!(
@@ -164,7 +175,7 @@ fn string_mapping() {
         &[
             Token::String(String::from("a")),
             Token::String(String::from("This is a string which should be a little longer than 32 bytes so we the the abi encoder")),
-        ],
+        ], &[]
     );
 
     vm.function(
@@ -173,9 +184,10 @@ fn string_mapping() {
             Token::String(String::from("a")),
             Token::Int(ethereum_types::U256::from(102)),
         ],
+        &[],
     );
 
-    let returns = vm.function("get", &[Token::String(String::from("a"))]);
+    let returns = vm.function("get", &[Token::String(String::from("a"))], &[]);
 
     assert_eq!(
         returns,
@@ -208,6 +220,7 @@ fn mapping_in_mapping() {
             Token::Int(ethereum_types::U256::from(102)),
             Token::FixedBytes(vec![0x98]),
         ],
+        &[],
     );
 
     let returns = vm.function(
@@ -216,6 +229,7 @@ fn mapping_in_mapping() {
             Token::String(String::from("a")),
             Token::Int(ethereum_types::U256::from(102)),
         ],
+        &[],
     );
 
     assert_eq!(returns, vec![Token::FixedBytes(vec![0x98])]);
@@ -226,6 +240,7 @@ fn mapping_in_mapping() {
             Token::String(String::from("a")),
             Token::Int(ethereum_types::U256::from(103)),
         ],
+        &[],
     );
 
     assert_eq!(returns, vec![Token::FixedBytes(vec![0])]);
@@ -236,6 +251,7 @@ fn mapping_in_mapping() {
             Token::String(String::from("b")),
             Token::Int(ethereum_types::U256::from(102)),
         ],
+        &[],
     );
 
     assert_eq!(returns, vec![Token::FixedBytes(vec![0])]);
@@ -278,7 +294,7 @@ fn sparse_array() {
         &[
             Token::Uint(ethereum_types::U256::from(909090909)),
             Token::String(String::from("This is a string which should be a little longer than 32 bytes so we the the abi encoder")),
-        ],
+        ], &[]
     );
 
     vm.function(
@@ -287,9 +303,14 @@ fn sparse_array() {
             Token::Uint(ethereum_types::U256::from(909090909)),
             Token::Int(ethereum_types::U256::from(102)),
         ],
+        &[],
     );
 
-    let returns = vm.function("get", &[Token::Uint(ethereum_types::U256::from(909090909))]);
+    let returns = vm.function(
+        "get",
+        &[Token::Uint(ethereum_types::U256::from(909090909))],
+        &[],
+    );
 
     assert_eq!(
         returns,
@@ -337,7 +358,7 @@ fn massive_sparse_array() {
         &[
             Token::Uint(ethereum_types::U256::from(786868768768678687686877u128)),
             Token::String(String::from("This is a string which should be a little longer than 32 bytes so we the the abi encoder")),
-        ],
+        ], &[]
     );
 
     vm.function(
@@ -346,6 +367,7 @@ fn massive_sparse_array() {
             Token::Uint(ethereum_types::U256::from(786868768768678687686877u128)),
             Token::Int(ethereum_types::U256::from(102)),
         ],
+        &[],
     );
 
     let returns = vm.function(
@@ -353,6 +375,7 @@ fn massive_sparse_array() {
         &[Token::Uint(ethereum_types::U256::from(
             786868768768678687686877u128,
         ))],
+        &[],
     );
 
     assert_eq!(

+ 71 - 27
tests/solana_tests/primitives.rs

@@ -25,7 +25,7 @@ fn assert_false() {
 
     vm.constructor("foo", &[]);
 
-    vm.function("assert_fails", &[]);
+    vm.function("assert_fails", &[], &[]);
 }
 
 #[test]
@@ -42,7 +42,7 @@ fn assert_true() {
 
     vm.constructor("foo", &[]);
 
-    vm.function("assert_fails", &[]);
+    vm.function("assert_fails", &[], &[]);
 }
 
 #[test]
@@ -74,16 +74,16 @@ fn boolean() {
 
     vm.constructor("foo", &[]);
 
-    let returns = vm.function("return_true", &[]);
+    let returns = vm.function("return_true", &[], &[]);
 
     assert_eq!(returns, vec![ethabi::Token::Bool(true),]);
 
-    let returns = vm.function("return_false", &[]);
+    let returns = vm.function("return_false", &[], &[]);
 
     assert_eq!(returns, vec![ethabi::Token::Bool(false),]);
 
-    vm.function("true_arg", &[ethabi::Token::Bool(true)]);
-    vm.function("false_arg", &[ethabi::Token::Bool(false)]);
+    vm.function("true_arg", &[ethabi::Token::Bool(true)], &[]);
+    vm.function("false_arg", &[ethabi::Token::Bool(false)], &[]);
 }
 
 #[test]
@@ -108,7 +108,7 @@ fn address() {
 
     vm.constructor("foo", &[]);
 
-    let returns = vm.function("return_address", &[]);
+    let returns = vm.function("return_address", &[], &[]);
 
     assert_eq!(
         returns,
@@ -124,6 +124,7 @@ fn address() {
             75, 161, 209, 89, 47, 84, 50, 13, 23, 127, 94, 21, 50, 249, 250, 185, 117, 49, 186,
             134, 82, 130, 112, 97, 218, 24, 157, 198, 40, 105, 118, 27,
         ])],
+        &[],
     );
 }
 
@@ -150,7 +151,7 @@ fn test_enum() {
 
     vm.constructor("foo", &[]);
 
-    let returns = vm.function("return_enum", &[]);
+    let returns = vm.function("return_enum", &[], &[]);
 
     assert_eq!(
         returns,
@@ -160,6 +161,7 @@ fn test_enum() {
     vm.function(
         "enum_arg",
         &[ethabi::Token::Uint(ethereum_types::U256::from(6))],
+        &[],
     );
 }
 
@@ -204,7 +206,7 @@ fn bytes() {
 
         vm.constructor("test", &[]);
 
-        let returns = vm.function("return_literal", &[]);
+        let returns = vm.function("return_literal", &[], &[]);
 
         assert_eq!(
             returns,
@@ -214,6 +216,7 @@ fn bytes() {
         let returns = vm.function(
             "return_arg",
             &[ethabi::Token::FixedBytes(vec![1, 2, 3, 4, 5, 6, 7])],
+            &[],
         );
 
         assert_eq!(
@@ -237,6 +240,7 @@ fn bytes() {
                     ethabi::Token::FixedBytes(a.to_vec()),
                     ethabi::Token::FixedBytes(b.to_vec()),
                 ],
+                &[],
             );
 
             let res: Vec<u8> = a.iter().zip(b.iter()).map(|(a, b)| a | b).collect();
@@ -256,6 +260,7 @@ fn bytes() {
                     ethabi::Token::FixedBytes(a.to_vec()),
                     ethabi::Token::FixedBytes(b.to_vec()),
                 ],
+                &[],
             );
 
             let res: Vec<u8> = a.iter().zip(b.iter()).map(|(a, b)| a & b).collect();
@@ -268,6 +273,7 @@ fn bytes() {
                     ethabi::Token::FixedBytes(a.to_vec()),
                     ethabi::Token::FixedBytes(b.to_vec()),
                 ],
+                &[],
             );
 
             let res: Vec<u8> = a.iter().zip(b.iter()).map(|(a, b)| a ^ b).collect();
@@ -284,6 +290,7 @@ fn bytes() {
                     ethabi::Token::FixedBytes(a.to_vec()),
                     ethabi::Token::Uint(ethereum_types::U256::from(r)),
                 ],
+                &[],
             );
 
             let mut res = (BigUint::from_bytes_be(&a) << r).to_bytes_be();
@@ -304,6 +311,7 @@ fn bytes() {
                     ethabi::Token::FixedBytes(a.to_vec()),
                     ethabi::Token::Uint(ethereum_types::U256::from(r)),
                 ],
+                &[],
             );
 
             let mut res = (BigUint::from_bytes_be(&a) >> r).to_bytes_be();
@@ -400,11 +408,15 @@ fn uint() {
             truncate_uint(&mut a, width);
             truncate_uint(&mut b, width);
 
-            let res = vm.function("pass", &[ethabi::Token::Uint(a)]);
+            let res = vm.function("pass", &[ethabi::Token::Uint(a)], &[]);
 
             println!("{:x} = {:?} o", a, res);
 
-            let add = vm.function("add", &[ethabi::Token::Uint(a), ethabi::Token::Uint(b)]);
+            let add = vm.function(
+                "add",
+                &[ethabi::Token::Uint(a), ethabi::Token::Uint(b)],
+                &[],
+            );
 
             let (mut res, _) = a.overflowing_add(b);
 
@@ -414,7 +426,11 @@ fn uint() {
 
             assert_eq!(add, vec![ethabi::Token::Uint(res)]);
 
-            let sub = vm.function("sub", &[ethabi::Token::Uint(a), ethabi::Token::Uint(b)]);
+            let sub = vm.function(
+                "sub",
+                &[ethabi::Token::Uint(a), ethabi::Token::Uint(b)],
+                &[],
+            );
 
             let (mut res, _) = a.overflowing_sub(b);
 
@@ -422,7 +438,11 @@ fn uint() {
 
             assert_eq!(sub, vec![ethabi::Token::Uint(res)]);
 
-            let mul = vm.function("mul", &[ethabi::Token::Uint(a), ethabi::Token::Uint(b)]);
+            let mul = vm.function(
+                "mul",
+                &[ethabi::Token::Uint(a), ethabi::Token::Uint(b)],
+                &[],
+            );
 
             let (mut res, _) = a.overflowing_mul(b);
 
@@ -430,7 +450,11 @@ fn uint() {
 
             assert_eq!(mul, vec![ethabi::Token::Uint(res)]);
 
-            let pow = vm.function("pow", &[ethabi::Token::Uint(a), ethabi::Token::Uint(b)]);
+            let pow = vm.function(
+                "pow",
+                &[ethabi::Token::Uint(a), ethabi::Token::Uint(b)],
+                &[],
+            );
 
             let (mut res, _) = a.overflowing_pow(b);
 
@@ -439,7 +463,11 @@ fn uint() {
             assert_eq!(pow, vec![ethabi::Token::Uint(res)]);
 
             if b != ethereum_types::U256::zero() {
-                let div = vm.function("div", &[ethabi::Token::Uint(a), ethabi::Token::Uint(b)]);
+                let div = vm.function(
+                    "div",
+                    &[ethabi::Token::Uint(a), ethabi::Token::Uint(b)],
+                    &[],
+                );
 
                 let mut res = a.div(b);
 
@@ -447,7 +475,11 @@ fn uint() {
 
                 assert_eq!(div, vec![ethabi::Token::Uint(res)]);
 
-                let add = vm.function("mod", &[ethabi::Token::Uint(a), ethabi::Token::Uint(b)]);
+                let add = vm.function(
+                    "mod",
+                    &[ethabi::Token::Uint(a), ethabi::Token::Uint(b)],
+                    &[],
+                );
 
                 let mut res = a.rem(b);
 
@@ -456,7 +488,7 @@ fn uint() {
                 assert_eq!(add, vec![ethabi::Token::Uint(res)]);
             }
 
-            let or = vm.function("or", &[ethabi::Token::Uint(a), ethabi::Token::Uint(b)]);
+            let or = vm.function("or", &[ethabi::Token::Uint(a), ethabi::Token::Uint(b)], &[]);
 
             let mut res = ethereum_types::U256([
                 a.0[0] | b.0[0],
@@ -469,7 +501,11 @@ fn uint() {
 
             assert_eq!(or, vec![ethabi::Token::Uint(res)]);
 
-            let and = vm.function("and", &[ethabi::Token::Uint(a), ethabi::Token::Uint(b)]);
+            let and = vm.function(
+                "and",
+                &[ethabi::Token::Uint(a), ethabi::Token::Uint(b)],
+                &[],
+            );
 
             let mut res = ethereum_types::U256([
                 a.0[0] & b.0[0],
@@ -482,7 +518,11 @@ fn uint() {
 
             assert_eq!(and, vec![ethabi::Token::Uint(res)]);
 
-            let xor = vm.function("xor", &[ethabi::Token::Uint(a), ethabi::Token::Uint(b)]);
+            let xor = vm.function(
+                "xor",
+                &[ethabi::Token::Uint(a), ethabi::Token::Uint(b)],
+                &[],
+            );
 
             let mut res = ethereum_types::U256([
                 a.0[0] ^ b.0[0],
@@ -503,6 +543,7 @@ fn uint() {
                     ethabi::Token::Uint(a),
                     ethabi::Token::Uint(ethereum_types::U256::from(r)),
                 ],
+                &[],
             );
 
             let mut res = a.shl(r);
@@ -517,6 +558,7 @@ fn uint() {
                     ethabi::Token::Uint(a),
                     ethabi::Token::Uint(ethereum_types::U256::from(r)),
                 ],
+                &[],
             );
 
             let mut res = a.shr(r);
@@ -617,7 +659,7 @@ fn int() {
             let big_a = eth_to_bigint(&a, width);
             let big_b = eth_to_bigint(&b, width);
 
-            let add = vm.function("add", &[ethabi::Token::Int(a), ethabi::Token::Int(b)]);
+            let add = vm.function("add", &[ethabi::Token::Int(a), ethabi::Token::Int(b)], &[]);
 
             let res = big_a.clone().add(&big_b);
 
@@ -625,26 +667,26 @@ fn int() {
 
             assert_eq!(add, vec![ethabi::Token::Int(res)]);
 
-            let sub = vm.function("sub", &[ethabi::Token::Int(a), ethabi::Token::Int(b)]);
+            let sub = vm.function("sub", &[ethabi::Token::Int(a), ethabi::Token::Int(b)], &[]);
 
             let res = bigint_to_eth(&big_a.clone().sub(&big_b), width);
 
             assert_eq!(sub, vec![ethabi::Token::Int(res)]);
 
-            let mul = vm.function("mul", &[ethabi::Token::Int(a), ethabi::Token::Int(b)]);
+            let mul = vm.function("mul", &[ethabi::Token::Int(a), ethabi::Token::Int(b)], &[]);
 
             let res = bigint_to_eth(&big_a.clone().mul(&big_b), width);
 
             assert_eq!(mul, vec![ethabi::Token::Int(res)]);
 
             if b != ethereum_types::U256::zero() {
-                let div = vm.function("div", &[ethabi::Token::Int(a), ethabi::Token::Int(b)]);
+                let div = vm.function("div", &[ethabi::Token::Int(a), ethabi::Token::Int(b)], &[]);
 
                 let res = bigint_to_eth(&big_a.clone().div(&big_b), width);
 
                 assert_eq!(div, vec![ethabi::Token::Int(res)]);
 
-                let add = vm.function("mod", &[ethabi::Token::Int(a), ethabi::Token::Int(b)]);
+                let add = vm.function("mod", &[ethabi::Token::Int(a), ethabi::Token::Int(b)], &[]);
 
                 let res = big_a.clone().rem(&big_b);
 
@@ -653,7 +695,7 @@ fn int() {
                 assert_eq!(add, vec![ethabi::Token::Int(res)]);
             }
 
-            let or = vm.function("or", &[ethabi::Token::Int(a), ethabi::Token::Int(b)]);
+            let or = vm.function("or", &[ethabi::Token::Int(a), ethabi::Token::Int(b)], &[]);
 
             let mut res = ethereum_types::U256([
                 a.0[0] | b.0[0],
@@ -666,7 +708,7 @@ fn int() {
 
             assert_eq!(or, vec![ethabi::Token::Int(res)]);
 
-            let and = vm.function("and", &[ethabi::Token::Int(a), ethabi::Token::Int(b)]);
+            let and = vm.function("and", &[ethabi::Token::Int(a), ethabi::Token::Int(b)], &[]);
 
             let mut res = ethereum_types::U256([
                 a.0[0] & b.0[0],
@@ -679,7 +721,7 @@ fn int() {
 
             assert_eq!(and, vec![ethabi::Token::Int(res)]);
 
-            let xor = vm.function("xor", &[ethabi::Token::Int(a), ethabi::Token::Int(b)]);
+            let xor = vm.function("xor", &[ethabi::Token::Int(a), ethabi::Token::Int(b)], &[]);
 
             let mut res = ethereum_types::U256([
                 a.0[0] ^ b.0[0],
@@ -700,6 +742,7 @@ fn int() {
                     ethabi::Token::Int(a),
                     ethabi::Token::Uint(ethereum_types::U256::from(r)),
                 ],
+                &[],
             );
 
             let mut res = a.shl(r);
@@ -714,6 +757,7 @@ fn int() {
                     ethabi::Token::Int(a),
                     ethabi::Token::Uint(ethereum_types::U256::from(r)),
                 ],
+                &[],
             );
 
             let res = bigint_to_eth(&big_a.clone().shr(r), width);

+ 8 - 4
tests/solana_tests/simple.rs

@@ -21,7 +21,7 @@ fn simple() {
 
     vm.printbuf.truncate(0);
 
-    vm.function("test", &[]);
+    vm.function("test", &[], &[]);
 
     assert_eq!(vm.printbuf, "Hello from function");
 }
@@ -72,6 +72,7 @@ fn parameters() {
             ethabi::Token::Uint(ethereum_types::U256::from(10)),
             ethabi::Token::Uint(ethereum_types::U256::from(10)),
         ],
+        &[],
     );
 
     assert_eq!(vm.printbuf, "x is 10");
@@ -84,6 +85,7 @@ fn parameters() {
             ethabi::Token::Uint(ethereum_types::U256::from(99)),
             ethabi::Token::Uint(ethereum_types::U256::from(102)),
         ],
+        &[],
     );
 
     assert_eq!(vm.printbuf, "y is 102");
@@ -105,6 +107,7 @@ fn returns() {
     let returns = vm.function(
         "test",
         &[ethabi::Token::Uint(ethereum_types::U256::from(10))],
+        &[],
     );
 
     assert_eq!(
@@ -126,6 +129,7 @@ fn returns() {
     let returns = vm.function(
         "test",
         &[ethabi::Token::Uint(ethereum_types::U256::from(982451653))],
+        &[],
     );
 
     assert_eq!(
@@ -170,18 +174,18 @@ fn flipper() {
         hex::decode("6fc90ec500000000000000001800000001").unwrap()
     );
 
-    let returns = vm.function("get", &[]);
+    let returns = vm.function("get", &[], &[]);
 
     assert_eq!(returns, vec![ethabi::Token::Bool(true)]);
 
-    vm.function("flip", &[]);
+    vm.function("flip", &[], &[]);
 
     assert_eq!(
         vm.data()[0..17].to_vec(),
         hex::decode("6fc90ec500000000000000001800000000").unwrap()
     );
 
-    let returns = vm.function("get", &[]);
+    let returns = vm.function("get", &[], &[]);
 
     assert_eq!(returns, vec![ethabi::Token::Bool(false)]);
 }

+ 42 - 30
tests/solana_tests/storage.rs

@@ -25,11 +25,11 @@ fn string() {
         vec![65, 177, 160, 100, 0, 0, 0, 0, 0, 0, 0, 0, 24, 0, 0, 0, 0, 0, 0, 0]
     );
 
-    let returns = vm.function("get", &[]);
+    let returns = vm.function("get", &[], &[]);
 
     assert_eq!(returns, vec![Token::String(String::from(""))]);
 
-    vm.function("set", &[Token::String(String::from("Hello, World!"))]);
+    vm.function("set", &[Token::String(String::from("Hello, World!"))], &[]);
 
     assert_eq!(
         vm.data()[0..20].to_vec(),
@@ -38,15 +38,15 @@ fn string() {
 
     assert_eq!(vm.data()[120..133].to_vec(), b"Hello, World!");
 
-    let returns = vm.function("get", &[]);
+    let returns = vm.function("get", &[], &[]);
 
     assert_eq!(returns, vec![Token::String(String::from("Hello, World!"))]);
 
     // try replacing it with a string of the same length. This is a special
     // fast-path handling
-    vm.function("set", &[Token::String(String::from("Hallo, Werld!"))]);
+    vm.function("set", &[Token::String(String::from("Hallo, Werld!"))], &[]);
 
-    let returns = vm.function("get", &[]);
+    let returns = vm.function("get", &[], &[]);
 
     assert_eq!(returns, vec![Token::String(String::from("Hallo, Werld!"))]);
 
@@ -57,9 +57,9 @@ fn string() {
 
     // Try setting this to an empty string. This is also a special case where
     // the result should be offset 0
-    vm.function("set", &[Token::String(String::from(""))]);
+    vm.function("set", &[Token::String(String::from(""))], &[]);
 
-    let returns = vm.function("get", &[]);
+    let returns = vm.function("get", &[], &[]);
 
     assert_eq!(returns, vec![Token::String(String::from(""))]);
 
@@ -101,7 +101,7 @@ fn bytes() {
         vec![11, 66, 182, 57, 0, 0, 0, 0, 0, 0, 0, 0, 24, 0, 0, 0, 0, 0, 0, 0]
     );
 
-    let returns = vm.function("foo_length", &[]);
+    let returns = vm.function("foo_length", &[], &[]);
 
     assert_eq!(returns, vec![Token::Uint(ethereum_types::U256::from(0))]);
 
@@ -110,6 +110,7 @@ fn bytes() {
         &[Token::Bytes(
             b"The shoemaker always wears the worst shoes".to_vec(),
         )],
+        &[],
     );
 
     assert_eq!(
@@ -125,6 +126,7 @@ fn bytes() {
         let returns = vm.function(
             "get_foo_offset",
             &[Token::Uint(ethereum_types::U256::from(i))],
+            &[],
         );
 
         assert_eq!(returns, vec![Token::FixedBytes(vec![b])]);
@@ -136,6 +138,7 @@ fn bytes() {
             Token::Uint(ethereum_types::U256::from(2)),
             Token::FixedBytes(b"E".to_vec()),
         ],
+        &[],
     );
 
     vm.function(
@@ -144,6 +147,7 @@ fn bytes() {
             Token::Uint(ethereum_types::U256::from(7)),
             Token::FixedBytes(b"E".to_vec()),
         ],
+        &[],
     );
 
     for (i, b) in b"ThE shoEmaker always wears the worst shoes"
@@ -154,6 +158,7 @@ fn bytes() {
         let returns = vm.function(
             "get_foo_offset",
             &[Token::Uint(ethereum_types::U256::from(i))],
+            &[],
         );
 
         assert_eq!(returns, vec![Token::FixedBytes(vec![b])]);
@@ -194,6 +199,7 @@ fn bytes_set_subscript_range() {
             Token::Uint(ethereum_types::U256::from(0)),
             Token::FixedBytes(b"E".to_vec()),
         ],
+        &[],
     );
 }
 
@@ -230,11 +236,13 @@ fn bytes_get_subscript_range() {
         &[Token::Bytes(
             b"The shoemaker always wears the worst shoes".to_vec(),
         )],
+        &[],
     );
 
     vm.function(
         "get_foo_offset",
         &[Token::Uint(ethereum_types::U256::from(0x80000000u64))],
+        &[],
     );
 }
 
@@ -285,29 +293,29 @@ fn bytes_push_pop() {
 
     vm.constructor("c", &[]);
 
-    let returns = vm.function("get_bs", &[]);
+    let returns = vm.function("get_bs", &[], &[]);
 
     assert_eq!(returns, vec![Token::Bytes(vec!(0x0e, 0xda))]);
 
-    let returns = vm.function("pop", &[]);
+    let returns = vm.function("pop", &[], &[]);
 
     assert_eq!(returns, vec![Token::FixedBytes(vec!(0xda))]);
 
-    let returns = vm.function("get_bs", &[]);
+    let returns = vm.function("get_bs", &[], &[]);
 
     assert_eq!(returns, vec![Token::Bytes(vec!(0x0e))]);
 
-    vm.function("push", &[Token::FixedBytes(vec![0x41])]);
+    vm.function("push", &[Token::FixedBytes(vec![0x41])], &[]);
 
     println!("data:{}", hex::encode(&vm.data()));
 
-    let returns = vm.function("get_bs", &[]);
+    let returns = vm.function("get_bs", &[], &[]);
 
     assert_eq!(returns, vec![Token::Bytes(vec!(0x0e, 0x41))]);
 
-    vm.function("push", &[Token::FixedBytes(vec![0x01])]);
+    vm.function("push", &[Token::FixedBytes(vec![0x01])], &[]);
 
-    let returns = vm.function("get_bs", &[]);
+    let returns = vm.function("get_bs", &[], &[]);
 
     assert_eq!(returns, vec![Token::Bytes(vec!(0x0e, 0x41, 0x01))]);
 }
@@ -328,7 +336,7 @@ fn bytes_empty_pop() {
 
     vm.constructor("c", &[]);
 
-    vm.function("pop", &[]);
+    vm.function("pop", &[], &[]);
 }
 
 #[test]
@@ -360,7 +368,7 @@ fn simple_struct() {
 
     vm.constructor("c", &[]);
 
-    vm.function("set_s2", &[]);
+    vm.function("set_s2", &[], &[]);
 
     assert_eq!(
         vm.data()[0..32].to_vec(),
@@ -370,7 +378,7 @@ fn simple_struct() {
         ]
     );
 
-    let returns = vm.function("get_s1", &[]);
+    let returns = vm.function("get_s1", &[], &[]);
 
     assert_eq!(
         returns,
@@ -386,9 +394,10 @@ fn simple_struct() {
             Token::Uint(ethereum_types::U256::from(102)),
             Token::Uint(ethereum_types::U256::from(3240121)),
         ])],
+        &[],
     );
 
-    let returns = vm.function("get_s1", &[]);
+    let returns = vm.function("get_s1", &[], &[]);
 
     assert_eq!(
         returns,
@@ -434,7 +443,7 @@ fn struct_in_struct() {
 
     vm.constructor("c", &[]);
 
-    vm.function("set_s2", &[]);
+    vm.function("set_s2", &[], &[]);
 
     assert_eq!(
         vm.data()[0..52].to_vec(),
@@ -445,7 +454,7 @@ fn struct_in_struct() {
         ]
     );
 
-    let returns = vm.function("get_s1", &[]);
+    let returns = vm.function("get_s1", &[], &[]);
 
     assert_eq!(
         returns,
@@ -469,9 +478,10 @@ fn struct_in_struct() {
             ]),
             Token::Uint(ethereum_types::U256::from(12345678901234567890u64)),
         ])],
+        &[],
     );
 
-    let returns = vm.function("get_s1", &[]);
+    let returns = vm.function("get_s1", &[], &[]);
 
     assert_eq!(
         returns,
@@ -516,7 +526,7 @@ fn string_in_struct() {
 
     vm.constructor("c", &[]);
 
-    vm.function("set_s2", &[]);
+    vm.function("set_s2", &[], &[]);
 
     assert_eq!(
         vm.data()[0..64].to_vec(),
@@ -527,7 +537,7 @@ fn string_in_struct() {
         ]
     );
 
-    let returns = vm.function("get_s1", &[]);
+    let returns = vm.function("get_s1", &[], &[]);
 
     assert_eq!(
         returns,
@@ -545,9 +555,10 @@ fn string_in_struct() {
             Token::String(String::from("foobar foobar foobar foobar foobar foobar")),
             Token::Uint(ethereum_types::U256::from(12345678901234567890u64)),
         ])],
+        &[],
     );
 
-    let returns = vm.function("get_s1", &[]);
+    let returns = vm.function("get_s1", &[], &[]);
 
     assert_eq!(
         returns,
@@ -613,9 +624,9 @@ fn complex_struct() {
 
     vm.constructor("c", &[]);
 
-    vm.function("set_s2", &[]);
+    vm.function("set_s2", &[], &[]);
 
-    let returns = vm.function("get_s1", &[]);
+    let returns = vm.function("get_s1", &[], &[]);
 
     assert_eq!(
         returns,
@@ -661,9 +672,10 @@ fn complex_struct() {
             ]),
             Token::String(String::from("yadayada")),
         ],
+        &[],
     );
 
-    let returns = vm.function("get_s1", &[]);
+    let returns = vm.function("get_s1", &[], &[]);
 
     assert_eq!(
         returns,
@@ -688,9 +700,9 @@ fn complex_struct() {
         ]
     );
 
-    vm.function("rm", &[]);
+    vm.function("rm", &[], &[]);
 
-    let returns = vm.function("get_s1", &[]);
+    let returns = vm.function("get_s1", &[], &[]);
 
     assert_eq!(
         returns,