Browse Source

Add contract field to instruction (currently unused)

This field will be used for contract instantiation, so the program knows
which contract should be instantiated.

This will be added in a later commit, this just adds the field.

Signed-off-by: Sean Young <sean@mess.org>
Sean Young 4 years ago
parent
commit
a83b08e216

+ 1 - 1
integration/solana/builtins.spec.ts

@@ -11,7 +11,7 @@ describe('Deploy solang contract and test', () => {
         let hash_functions = await conn.loadProgram("builtins.so", "builtins.abi");
         let hash_functions = await conn.loadProgram("builtins.so", "builtins.abi");
 
 
         // call the constructor
         // call the constructor
-        await hash_functions.call_constructor(conn, []);
+        await hash_functions.call_constructor(conn, 'builtins', []);
 
 
         console.log("calling ripemd160");
         console.log("calling ripemd160");
         let res = await hash_functions.call_function(conn, "hash_ripemd160", ['0x' + Buffer.from('Call me Ishmael.', 'utf8').toString('hex')]);
         let res = await hash_functions.call_function(conn, "hash_ripemd160", ['0x' + Buffer.from('Call me Ishmael.', 'utf8').toString('hex')]);

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

@@ -13,9 +13,9 @@ describe('Deploy solang contract and test', () => {
         let callee2 = await conn.loadProgram("callee2.so", "callee2.abi");
         let callee2 = await conn.loadProgram("callee2.so", "callee2.abi");
 
 
         // call the constructor
         // call the constructor
-        await caller.call_constructor(conn, []);
-        await callee.call_constructor(conn, []);
-        await callee2.call_constructor(conn, []);
+        await caller.call_constructor(conn, 'caller', []);
+        await callee.call_constructor(conn, 'callee', []);
+        await callee2.call_constructor(conn, 'callee2', []);
 
 
         await callee.call_function(conn, "set_x", ["102"]);
         await callee.call_function(conn, "set_x", ["102"]);
 
 

+ 7 - 2
integration/solana/index.ts

@@ -11,7 +11,8 @@ import {
     sendAndConfirmTransaction, SYSVAR_CLOCK_PUBKEY,
     sendAndConfirmTransaction, SYSVAR_CLOCK_PUBKEY,
 } from '@solana/web3.js';
 } from '@solana/web3.js';
 import fs from 'fs';
 import fs from 'fs';
-import { AbiItem, AbiInput } from 'web3-utils';
+import { AbiItem } from 'web3-utils';
+import { utils } from 'ethers';
 const Web3EthAbi = require('web3-eth-abi');
 const Web3EthAbi = require('web3-eth-abi');
 
 
 const default_url: string = "http://localhost:8899";
 const default_url: string = "http://localhost:8899";
@@ -123,15 +124,18 @@ class TestConnection {
 class Program {
 class Program {
     constructor(private programId: PublicKey, private contractStorageAccount: Account, private abi: string) { }
     constructor(private programId: PublicKey, private contractStorageAccount: Account, private abi: string) { }
 
 
-    async call_constructor(test: TestConnection, params: string[]): Promise<void> {
+    async call_constructor(test: TestConnection, contract: string, params: string[]): Promise<void> {
         let abi: AbiItem | undefined = JSON.parse(this.abi).find((e: AbiItem) => e.type == "constructor");
         let abi: AbiItem | undefined = JSON.parse(this.abi).find((e: AbiItem) => e.type == "constructor");
 
 
         let inputs = abi?.inputs! || [];
         let inputs = abi?.inputs! || [];
 
 
         const input = Web3EthAbi.encodeParameters(inputs, params);
         const input = Web3EthAbi.encodeParameters(inputs, params);
 
 
+        let hash = utils.keccak256(Buffer.from(contract));
+
         const data = Buffer.concat([
         const data = Buffer.concat([
             this.contractStorageAccount.publicKey.toBuffer(),
             this.contractStorageAccount.publicKey.toBuffer(),
+            Buffer.from(hash.substr(2, 8), 'hex'),
             Buffer.from(input.replace('0x', ''), 'hex')
             Buffer.from(input.replace('0x', ''), 'hex')
         ]);
         ]);
 
 
@@ -162,6 +166,7 @@ class Program {
         const input: string = Web3EthAbi.encodeFunctionCall(abi, params);
         const input: string = Web3EthAbi.encodeFunctionCall(abi, params);
         const data = Buffer.concat([
         const data = Buffer.concat([
             this.contractStorageAccount.publicKey.toBuffer(),
             this.contractStorageAccount.publicKey.toBuffer(),
+            Buffer.from('00000000', 'hex'),
             Buffer.from(input.replace('0x', ''), 'hex')
             Buffer.from(input.replace('0x', ''), 'hex')
         ]);
         ]);
 
 

+ 1 - 0
integration/solana/package.json

@@ -19,6 +19,7 @@
   },
   },
   "dependencies": {
   "dependencies": {
     "@solana/web3.js": "^1.11.0",
     "@solana/web3.js": "^1.11.0",
+    "ethers": "^5.2.0",
     "web3-eth-abi": "^1.3.0",
     "web3-eth-abi": "^1.3.0",
     "web3-utils": "^1.3.0"
     "web3-utils": "^1.3.0"
   }
   }

+ 9 - 9
integration/solana/simple.spec.ts

@@ -11,7 +11,7 @@ describe('Deploy solang contract and test', () => {
         let prog = await conn.loadProgram("flipper.so", "flipper.abi");
         let prog = await conn.loadProgram("flipper.so", "flipper.abi");
 
 
         // call the constructor
         // call the constructor
-        await prog.call_constructor(conn, ["true"]);
+        await prog.call_constructor(conn, 'flipper', ["true"]);
 
 
         let res = await prog.call_function(conn, "get", []);
         let res = await prog.call_function(conn, "get", []);
 
 
@@ -34,7 +34,7 @@ describe('Deploy solang contract and test', () => {
         let prog = await conn.loadProgram("primitives.so", "primitives.abi");
         let prog = await conn.loadProgram("primitives.so", "primitives.abi");
 
 
         // call the constructor
         // call the constructor
-        await prog.call_constructor(conn, []);
+        await prog.call_constructor(conn, 'primitives', []);
 
 
         // TEST Basic enums
         // TEST Basic enums
         // in ethereum, an enum is described as an uint8 so can't use the enum
         // in ethereum, an enum is described as an uint8 so can't use the enum
@@ -157,7 +157,7 @@ describe('Deploy solang contract and test', () => {
         let prog = await conn.loadProgram("store.so", "store.abi");
         let prog = await conn.loadProgram("store.so", "store.abi");
 
 
         // call the constructor
         // call the constructor
-        await prog.call_constructor(conn, []);
+        await prog.call_constructor(conn, 'store', []);
 
 
         function returns_to_array(res: Object) {
         function returns_to_array(res: Object) {
             let arr = Object.values(res);
             let arr = Object.values(res);
@@ -254,7 +254,7 @@ describe('Deploy solang contract and test', () => {
         let prog = await conn.loadProgram("store.so", "store.abi", 8192);
         let prog = await conn.loadProgram("store.so", "store.abi", 8192);
 
 
         // call the constructor
         // call the constructor
-        await prog.call_constructor(conn, []);
+        await prog.call_constructor(conn, 'store', []);
 
 
         function returns(res: Object) {
         function returns(res: Object) {
             let arr = Object.values(res);
             let arr = Object.values(res);
@@ -402,7 +402,7 @@ describe('Deploy solang contract and test', () => {
         // storage.sol needs 168 byes
         // storage.sol needs 168 byes
         let prog = await conn.loadProgram("store.so", "store.abi", 100);
         let prog = await conn.loadProgram("store.so", "store.abi", 100);
 
 
-        await expect(prog.call_constructor(conn, []))
+        await expect(prog.call_constructor(conn, 'store', []))
             .rejects
             .rejects
             .toThrowError(new Error('failed to send transaction: Transaction simulation failed: Error processing Instruction 0: account data too small for instruction'));
             .toThrowError(new Error('failed to send transaction: Transaction simulation failed: Error processing Instruction 0: account data too small for instruction'));
     });
     });
@@ -415,7 +415,7 @@ describe('Deploy solang contract and test', () => {
         // storage.sol needs 168 byes
         // storage.sol needs 168 byes
         let prog = await conn.loadProgram("store.so", "store.abi", 512);
         let prog = await conn.loadProgram("store.so", "store.abi", 512);
 
 
-        await prog.call_constructor(conn, []);
+        await prog.call_constructor(conn, 'store', []);
 
 
         await prog.call_function(conn, "set_foo1", []);
         await prog.call_function(conn, "set_foo1", []);
 
 
@@ -433,7 +433,7 @@ describe('Deploy solang contract and test', () => {
         // storage.sol needs 168 bytes on constructor, more for string data
         // storage.sol needs 168 bytes on constructor, more for string data
         let prog = await conn.loadProgram("store.so", "store.abi", 180);
         let prog = await conn.loadProgram("store.so", "store.abi", 180);
 
 
-        await prog.call_constructor(conn, []);
+        await prog.call_constructor(conn, 'store', []);
 
 
         // set a load of string which will overflow
         // set a load of string which will overflow
         await expect(prog.call_function(conn, "set_foo1", []))
         await expect(prog.call_function(conn, "set_foo1", []))
@@ -450,7 +450,7 @@ describe('Deploy solang contract and test', () => {
         // storage.sol needs 168 bytes on constructor, more for string data
         // storage.sol needs 168 bytes on constructor, more for string data
         let prog = await conn.loadProgram("store.so", "store.abi", 210);
         let prog = await conn.loadProgram("store.so", "store.abi", 210);
 
 
-        await prog.call_constructor(conn, []);
+        await prog.call_constructor(conn, 'store', []);
 
 
         async function push_until_bang() {
         async function push_until_bang() {
             for (let i = 0; i < 100; i++) {
             for (let i = 0; i < 100; i++) {
@@ -473,7 +473,7 @@ describe('Deploy solang contract and test', () => {
         // storage.sol needs 168 bytes on constructor, more for string data
         // storage.sol needs 168 bytes on constructor, more for string data
         let prog = await conn.loadProgram("arrays.so", "arrays.abi", 4096);
         let prog = await conn.loadProgram("arrays.so", "arrays.abi", 4096);
 
 
-        await prog.call_constructor(conn, []);
+        await prog.call_constructor(conn, 'arrays', []);
 
 
         let users = [];
         let users = [];
 
 

+ 10 - 5
src/codegen/expression.rs

@@ -8,9 +8,7 @@ use crate::sema::eval::eval_const_number;
 use crate::sema::expression::{bigint_to_expression, cast, cast_shift_arg};
 use crate::sema::expression::{bigint_to_expression, cast, cast_shift_arg};
 use crate::Target;
 use crate::Target;
 use num_bigint::BigInt;
 use num_bigint::BigInt;
-use num_traits::FromPrimitive;
-use num_traits::One;
-use num_traits::ToPrimitive;
+use num_traits::{FromPrimitive, One, ToPrimitive, Zero};
 use std::collections::HashSet;
 use std::collections::HashSet;
 use std::ops::Mul;
 use std::ops::Mul;
 
 
@@ -1266,9 +1264,13 @@ pub fn emit_function_call(
                 (
                 (
                     Expression::AbiEncode {
                     Expression::AbiEncode {
                         loc: *loc,
                         loc: *loc,
-                        packed: vec![address, args],
+                        packed: vec![
+                            address,
+                            Expression::NumberLiteral(*loc, Type::Bytes(4), BigInt::zero()),
+                            args,
+                        ],
                         args: Vec::new(),
                         args: Vec::new(),
-                        tys: vec![Type::Address(false), Type::DynamicBytes],
+                        tys: vec![Type::Address(false), Type::Bytes(4), Type::DynamicBytes],
                     },
                     },
                     None,
                     None,
                 )
                 )
@@ -1323,12 +1325,15 @@ pub fn emit_function_call(
 
 
                 let (payload, address) = if ns.target == Target::Solana {
                 let (payload, address) = if ns.target == Target::Solana {
                     tys.insert(0, Type::Address(false));
                     tys.insert(0, Type::Address(false));
+                    tys.insert(1, Type::Bytes(4));
+
                     (
                     (
                         Expression::AbiEncode {
                         Expression::AbiEncode {
                             loc: *loc,
                             loc: *loc,
                             tys,
                             tys,
                             packed: vec![
                             packed: vec![
                                 address,
                                 address,
+                                Expression::NumberLiteral(*loc, Type::Bytes(4), BigInt::zero()),
                                 Expression::NumberLiteral(
                                 Expression::NumberLiteral(
                                     *loc,
                                     *loc,
                                     Type::Bytes(4),
                                     Type::Bytes(4),

BIN
stdlib/bpf/solana.bc


+ 9 - 3
stdlib/solana_sdk.h

@@ -227,6 +227,7 @@ typedef struct
   uint64_t input_len;          /** Length in bytes of 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 */
   const SolPubkey *program_id; /** program_id of the currently executing program */
   const SolAccountInfo *ka_clock;
   const SolAccountInfo *ka_clock;
+  uint32_t contract;
 } SolParameters;
 } SolParameters;
 
 
 /**
 /**
@@ -342,14 +343,19 @@ static uint64_t sol_deserialize(
   uint64_t data_len = *(uint64_t *)input;
   uint64_t data_len = *(uint64_t *)input;
   input += sizeof(uint64_t);
   input += sizeof(uint64_t);
 
 
-  if (data_len < SIZE_PUBKEY)
+  if (data_len < SIZE_PUBKEY + sizeof(uint32_t))
   {
   {
     return ERROR_INVALID_INSTRUCTION_DATA;
     return ERROR_INVALID_INSTRUCTION_DATA;
   }
   }
 
 
   params->account_id = (SolPubkey *)input;
   params->account_id = (SolPubkey *)input;
-  params->input_len = data_len - SIZE_PUBKEY;
-  params->input = input + SIZE_PUBKEY;
+  input += SIZE_PUBKEY;
+  data_len -= SIZE_PUBKEY;
+  params->contract = *(uint32_t *)input;
+  input += sizeof(uint32_t);
+  data_len -= sizeof(uint32_t);
+  params->input_len = data_len;
+  params->input = input;
   input += data_len;
   input += data_len;
 
 
   params->program_id = (SolPubkey *)input;
   params->program_id = (SolPubkey *)input;

+ 8 - 6
tests/solana.rs

@@ -761,12 +761,12 @@ impl VirtualMachine {
 
 
         println!("constructor for {}", hex::encode(&program.data));
         println!("constructor for {}", hex::encode(&program.data));
 
 
-        let calldata = if let Some(constructor) = &program.abi.constructor {
-            constructor
-                .encode_input(program.data.to_vec(), args)
-                .unwrap()
-        } else {
-            program.data.to_vec()
+        let mut calldata: Vec<u8> = program.data.to_vec();
+
+        calldata.extend(&0u32.to_le_bytes());
+
+        if let Some(constructor) = &program.abi.constructor {
+            calldata.extend(&constructor.encode_input(vec![], args).unwrap());
         };
         };
 
 
         self.execute(&calldata);
         self.execute(&calldata);
@@ -779,6 +779,8 @@ impl VirtualMachine {
 
 
         let mut calldata: Vec<u8> = program.data.to_vec();
         let mut calldata: Vec<u8> = program.data.to_vec();
 
 
+        calldata.extend(&0u32.to_le_bytes());
+
         match program.abi.functions[name][0].encode_input(args) {
         match program.abi.functions[name][0].encode_input(args) {
             Ok(n) => calldata.extend(&n),
             Ok(n) => calldata.extend(&n),
             Err(x) => panic!("{}", x),
             Err(x) => panic!("{}", x),