فهرست منبع

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 سال پیش
والد
کامیت
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");
 
         // call the constructor
-        await hash_functions.call_constructor(conn, []);
+        await hash_functions.call_constructor(conn, 'builtins', []);
 
         console.log("calling ripemd160");
         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");
 
         // 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"]);
 

+ 7 - 2
integration/solana/index.ts

@@ -11,7 +11,8 @@ import {
     sendAndConfirmTransaction, SYSVAR_CLOCK_PUBKEY,
 } from '@solana/web3.js';
 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 default_url: string = "http://localhost:8899";
@@ -123,15 +124,18 @@ class TestConnection {
 class Program {
     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 inputs = abi?.inputs! || [];
 
         const input = Web3EthAbi.encodeParameters(inputs, params);
 
+        let hash = utils.keccak256(Buffer.from(contract));
+
         const data = Buffer.concat([
             this.contractStorageAccount.publicKey.toBuffer(),
+            Buffer.from(hash.substr(2, 8), 'hex'),
             Buffer.from(input.replace('0x', ''), 'hex')
         ]);
 
@@ -162,6 +166,7 @@ class Program {
         const input: string = Web3EthAbi.encodeFunctionCall(abi, params);
         const data = Buffer.concat([
             this.contractStorageAccount.publicKey.toBuffer(),
+            Buffer.from('00000000', 'hex'),
             Buffer.from(input.replace('0x', ''), 'hex')
         ]);
 

+ 1 - 0
integration/solana/package.json

@@ -19,6 +19,7 @@
   },
   "dependencies": {
     "@solana/web3.js": "^1.11.0",
+    "ethers": "^5.2.0",
     "web3-eth-abi": "^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");
 
         // call the constructor
-        await prog.call_constructor(conn, ["true"]);
+        await prog.call_constructor(conn, 'flipper', ["true"]);
 
         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");
 
         // call the constructor
-        await prog.call_constructor(conn, []);
+        await prog.call_constructor(conn, 'primitives', []);
 
         // TEST Basic enums
         // 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");
 
         // call the constructor
-        await prog.call_constructor(conn, []);
+        await prog.call_constructor(conn, 'store', []);
 
         function returns_to_array(res: Object) {
             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);
 
         // call the constructor
-        await prog.call_constructor(conn, []);
+        await prog.call_constructor(conn, 'store', []);
 
         function returns(res: Object) {
             let arr = Object.values(res);
@@ -402,7 +402,7 @@ describe('Deploy solang contract and test', () => {
         // storage.sol needs 168 byes
         let prog = await conn.loadProgram("store.so", "store.abi", 100);
 
-        await expect(prog.call_constructor(conn, []))
+        await expect(prog.call_constructor(conn, 'store', []))
             .rejects
             .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
         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", []);
 
@@ -433,7 +433,7 @@ describe('Deploy solang contract and test', () => {
         // storage.sol needs 168 bytes on constructor, more for string data
         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
         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
         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() {
             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
         let prog = await conn.loadProgram("arrays.so", "arrays.abi", 4096);
 
-        await prog.call_constructor(conn, []);
+        await prog.call_constructor(conn, 'arrays', []);
 
         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::Target;
 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::ops::Mul;
 
@@ -1266,9 +1264,13 @@ pub fn emit_function_call(
                 (
                     Expression::AbiEncode {
                         loc: *loc,
-                        packed: vec![address, args],
+                        packed: vec![
+                            address,
+                            Expression::NumberLiteral(*loc, Type::Bytes(4), BigInt::zero()),
+                            args,
+                        ],
                         args: Vec::new(),
-                        tys: vec![Type::Address(false), Type::DynamicBytes],
+                        tys: vec![Type::Address(false), Type::Bytes(4), Type::DynamicBytes],
                     },
                     None,
                 )
@@ -1323,12 +1325,15 @@ 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));
+
                     (
                         Expression::AbiEncode {
                             loc: *loc,
                             tys,
                             packed: vec![
                                 address,
+                                Expression::NumberLiteral(*loc, Type::Bytes(4), BigInt::zero()),
                                 Expression::NumberLiteral(
                                     *loc,
                                     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 */
   const SolPubkey *program_id; /** program_id of the currently executing program */
   const SolAccountInfo *ka_clock;
+  uint32_t contract;
 } SolParameters;
 
 /**
@@ -342,14 +343,19 @@ static uint64_t sol_deserialize(
   uint64_t data_len = *(uint64_t *)input;
   input += sizeof(uint64_t);
 
-  if (data_len < SIZE_PUBKEY)
+  if (data_len < SIZE_PUBKEY + sizeof(uint32_t))
   {
     return ERROR_INVALID_INSTRUCTION_DATA;
   }
 
   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;
 
   params->program_id = (SolPubkey *)input;

+ 8 - 6
tests/solana.rs

@@ -761,12 +761,12 @@ impl VirtualMachine {
 
         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);
@@ -779,6 +779,8 @@ impl VirtualMachine {
 
         let mut calldata: Vec<u8> = program.data.to_vec();
 
+        calldata.extend(&0u32.to_le_bytes());
+
         match program.abi.functions[name][0].encode_input(args) {
             Ok(n) => calldata.extend(&n),
             Err(x) => panic!("{}", x),