ソースを参照

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),