From 9dc8f6f9dc31d9266ddf4daa2386753d3f598ca1 Mon Sep 17 00:00:00 2001 From: Silas Davis <silas@monax.io> Date: Fri, 6 Apr 2018 23:57:50 +0100 Subject: [PATCH] Add strange test case Signed-off-by: Silas Davis <silas@monax.io> --- execution/accounts.go | 1 + execution/events/events.go | 5 ++- execution/evm/abi/types.go | 12 ++++++ execution/evm/events/events.go | 1 + execution/evm/snative.go | 10 +---- execution/evm/vm.go | 4 +- execution/transactor.go | 60 ++--------------------------- rpc/v0/client.go | 11 +++++- rpc/v0/integration/strange_loop.go | 3 ++ rpc/v0/integration/strange_loop.sh | 5 +++ rpc/v0/integration/strange_loop.sol | 31 +++++++++++++++ rpc/v0/integration/v0_test.go | 25 ++++++++++-- rpc/v0/methods.go | 17 ++++---- 13 files changed, 107 insertions(+), 78 deletions(-) create mode 100644 rpc/v0/integration/strange_loop.go create mode 100755 rpc/v0/integration/strange_loop.sh create mode 100644 rpc/v0/integration/strange_loop.sol diff --git a/execution/accounts.go b/execution/accounts.go index 7dc11149..ec9953d4 100644 --- a/execution/accounts.go +++ b/execution/accounts.go @@ -59,6 +59,7 @@ func (accs *Accounts) SigningAccountFromPrivateKey(privateKeyBytes []byte) (*Sig if account != nil { account = acm.ConcreteAccount{ Address: privateAccount.Address(), + PublicKey: privateAccount.PublicKey(), }.Account() } return &SigningAccount{ diff --git a/execution/events/events.go b/execution/events/events.go index efbe1f24..5315a741 100644 --- a/execution/events/events.go +++ b/execution/events/events.go @@ -32,6 +32,10 @@ var sendTxQuery = event.NewQueryBuilder(). AndEquals(event.MessageTypeKey, reflect.TypeOf(EventDataTx{}).String()). AndEquals(event.TxTypeKey, reflect.TypeOf(&txs.SendTx{}).String()) +var callTxQuery = event.NewQueryBuilder(). + AndEquals(event.MessageTypeKey, reflect.TypeOf(EventDataTx{}).String()). + AndEquals(event.TxTypeKey, reflect.TypeOf(&txs.CallTx{}).String()) + type eventDataTx struct { Tx txs.Wrapper Return []byte @@ -60,7 +64,6 @@ func (edTx *EventDataTx) UnmarshalJSON(data []byte) error { } // Publish/Subscribe - func SubscribeAccountOutputSendTx(ctx context.Context, subscribable event.Subscribable, subscriber string, address acm.Address, txHash []byte, ch chan<- *txs.SendTx) error { diff --git a/execution/evm/abi/types.go b/execution/evm/abi/types.go index 09ac0e4c..0c7f300d 100644 --- a/execution/evm/abi/types.go +++ b/execution/evm/abi/types.go @@ -14,6 +14,8 @@ package abi +import "github.com/hyperledger/burrow/execution/evm/sha3" + // Ethereum defines types and calling conventions for the ABI // (application binary interface) here: https://github.com/ethereum/wiki/wiki/Ethereum-Contract-ABI // We make a start of representing them here @@ -51,3 +53,13 @@ type ( Address [AddressLength]byte FunctionSelector [FunctionSelectorLength]byte ) + +func FunctionID(signature string) FunctionSelector { + return FirstFourBytes(sha3.Sha3([]byte(signature))) +} + +func FirstFourBytes(byteSlice []byte) [4]byte { + var bs [4]byte + copy(bs[:], byteSlice[:4]) + return bs +} diff --git a/execution/evm/events/events.go b/execution/evm/events/events.go index a9308bb5..719e3354 100644 --- a/execution/evm/events/events.go +++ b/execution/evm/events/events.go @@ -99,6 +99,7 @@ func SubscribeLogEvent(ctx context.Context, subscribable event.Subscribable, sub } func PublishAccountCall(publisher event.Publisher, address acm.Address, eventDataCall *EventDataCall) error { + fmt.Printf("%v: %v\n", eventDataCall.StackDepth, eventDataCall.Return[31]) return event.PublishWithEventID(publisher, EventStringAccountCall(address), eventDataCall, map[string]interface{}{ "address": address, diff --git a/execution/evm/snative.go b/execution/evm/snative.go index 34375a0a..1bb3e89c 100644 --- a/execution/evm/snative.go +++ b/execution/evm/snative.go @@ -248,7 +248,7 @@ func (contract *SNativeContractDescription) Dispatch(state state.Writer, caller "identifier but arguments are only %v bytes long", len(args)) } - function, err := contract.FunctionByID(firstFourBytes(args)) + function, err := contract.FunctionByID(abi.FirstFourBytes(args)) if err != nil { return nil, err } @@ -325,7 +325,7 @@ func (function *SNativeFunctionDescription) Signature() string { // Get function calling identifier FunctionSelector func (function *SNativeFunctionDescription) ID() abi.FunctionSelector { - return firstFourBytes(sha3.Sha3([]byte(function.Signature()))) + return abi.FunctionID(function.Signature()) } // Get number of function arguments @@ -566,9 +566,3 @@ func byteFromBool(b bool) byte { } return 0x0 } - -func firstFourBytes(byteSlice []byte) [4]byte { - var bs [4]byte - copy(bs[:], byteSlice[:4]) - return bs -} diff --git a/execution/evm/vm.go b/execution/evm/vm.go index 1837b47d..ea29e487 100644 --- a/execution/evm/vm.go +++ b/execution/evm/vm.go @@ -156,6 +156,8 @@ func HasPermission(stateWriter state.Writer, acc acm.Account, perm ptypes.PermFl } func (vm *VM) fireCallEvent(exception *string, output *[]byte, callerAddress, calleeAddress acm.Address, input []byte, value uint64, gas *uint64) { + ret := make([]byte, len(*output)) + copy(ret, *output) // fire the post call event (including exception if applicable) if vm.publisher != nil { events.PublishAccountCall(vm.publisher, calleeAddress, &events.EventDataCall{ @@ -169,7 +171,7 @@ func (vm *VM) fireCallEvent(exception *string, output *[]byte, callerAddress, ca Origin: vm.origin, TxHash: vm.txHash, StackDepth: vm.stackDepth, - Return: *output, + Return: ret, Exception: *exception, }) } diff --git a/execution/transactor.go b/execution/transactor.go index 662407ec..0acb2fd3 100644 --- a/execution/transactor.go +++ b/execution/transactor.go @@ -166,7 +166,7 @@ func (trans *Transactor) BroadcastTx(tx txs.Tx) (*txs.Receipt, error) { } // Orders calls to BroadcastTx using lock (waits for response from core before releasing) -func (trans *Transactor) Transact2(inputAccount SequencedAddressableSigner, address *acm.Address, data []byte, gasLimit, +func (trans *Transactor) Transact(inputAccount SequencedAddressableSigner, address *acm.Address, data []byte, gasLimit, fee uint64) (*txs.Receipt, error) { trans.Lock() defer trans.Unlock() @@ -203,65 +203,11 @@ func (trans *Transactor) Transact2(inputAccount SequencedAddressableSigner, addr return trans.BroadcastTx(tx) } -// Orders calls to BroadcastTx using lock (waits for response from core before releasing) -func (trans *Transactor) Transact(privKey []byte, address *acm.Address, data []byte, gasLimit, - fee uint64) (*txs.Receipt, error) { - - if len(privKey) != 64 { - return nil, fmt.Errorf("Private key is not of the right length: %d\n", len(privKey)) - } - trans.Lock() - defer trans.Unlock() - pa, err := acm.GeneratePrivateAccountFromPrivateKeyBytes(privKey) - if err != nil { - return nil, err - } - acc, err := trans.state.GetAccount(pa.Address()) - if err != nil { - return nil, err - } - sequence := uint64(1) - if acc != nil { - sequence = acc.Sequence() + uint64(1) - } - - // TODO: [Silas] we should consider revising this method and removing fee, or - // possibly adding an amount parameter. It is non-sensical to just be able to - // set the fee. Our support of fees in general is questionable since at the - // moment all we do is deduct the fee effectively leaking token. It is possible - // someone may be using the sending of native token to payable functions but - // they can be served by broadcasting a token. - - // We hard-code the amount to be equal to the fee which means the CallTx we - // generate transfers 0 value, which is the most sensible default since in - // recent solidity compilers the EVM generated will throw an error if value - // is transferred to a non-payable function. - txInput := &txs.TxInput{ - Address: pa.Address(), - Amount: fee, - Sequence: sequence, - PublicKey: pa.PublicKey(), - } - tx := &txs.CallTx{ - Input: txInput, - Address: address, - GasLimit: gasLimit, - Fee: fee, - Data: data, - } - - // Got ourselves a tx. - txS, errS := trans.SignTx(tx, []acm.AddressableSigner{pa}) - if errS != nil { - return nil, errS - } - return trans.BroadcastTx(txS) -} -func (trans *Transactor) TransactAndHold(privKey []byte, address *acm.Address, data []byte, gasLimit, +func (trans *Transactor) TransactAndHold(inputAccount SequencedAddressableSigner, address *acm.Address, data []byte, gasLimit, fee uint64) (*evm_events.EventDataCall, error) { - receipt, err := trans.Transact(privKey, address, data, gasLimit, fee) + receipt, err := trans.Transact(inputAccount, address, data, gasLimit, fee) if err != nil { return nil, err } diff --git a/rpc/v0/client.go b/rpc/v0/client.go index 8f635e17..351fd130 100644 --- a/rpc/v0/client.go +++ b/rpc/v0/client.go @@ -44,6 +44,15 @@ func (vc *V0Client) Transact(param TransactParam) (*txs.Receipt, error) { return receipt, nil } +func (vc *V0Client) TransactAndHold2(param TransactParam) (*events.EventDataCall, error) { + eventDataCall := new(events.EventDataCall) + err := vc.Call(TRANSACT_AND_HOLD+"2", param, eventDataCall) + if err != nil { + return nil, err + } + return eventDataCall, nil +} + func (vc *V0Client) TransactAndHold(param TransactParam) (*events.EventDataCall, error) { eventDataCall := new(events.EventDataCall) err := vc.Call(TRANSACT_AND_HOLD, param, eventDataCall) @@ -82,7 +91,7 @@ func (vc *V0Client) Call(method string, param interface{}, result interface{}) e if rpcResponse.Error != nil { return rpcResponse.Error } - err = json.Unmarshal(rpcResponse.Result, result) + vc.codec.DecodeBytes(result, rpcResponse.Result) if err != nil { return err } diff --git a/rpc/v0/integration/strange_loop.go b/rpc/v0/integration/strange_loop.go new file mode 100644 index 00000000..f8137df9 --- /dev/null +++ b/rpc/v0/integration/strange_loop.go @@ -0,0 +1,3 @@ +package integration + +const strangeLoopBytecode = "60606040526017600055602260015560116002556001600360006101000a81548160ff021916908315150217905550341561003957600080fd5b6102c9806100486000396000f300606060405260043610610041576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff168063ebb384dd14610046575b600080fd5b341561005157600080fd5b61005961006f565b6040518082815260200191505060405180910390f35b60006002549050600360009054906101000a900460ff16156101cf57600154600254121561012e5760026000815480929190600101919050555060025490503073ffffffffffffffffffffffffffffffffffffffff1663ebb384dd6040518163ffffffff167c0100000000000000000000000000000000000000000000000000000000028152600401602060405180830381600087803b151561011157600080fd5b5af1151561011e57600080fd5b50505060405180519050506101ca565b6000600360006101000a81548160ff02191690831515021790555060025490503073ffffffffffffffffffffffffffffffffffffffff1663ebb384dd6040518163ffffffff167c0100000000000000000000000000000000000000000000000000000000028152600401602060405180830381600087803b15156101b157600080fd5b5af115156101be57600080fd5b50505060405180519050505b610299565b6000546002541315610273576002600081548092919060019003919050555060025490503073ffffffffffffffffffffffffffffffffffffffff1663ebb384dd6040518163ffffffff167c0100000000000000000000000000000000000000000000000000000000028152600401602060405180830381600087803b151561025657600080fd5b5af1151561026357600080fd5b5050506040518051905050610298565b6001600360006101000a81548160ff021916908315150217905550600254905061029a565b5b5b905600a165627a7a7230582071446a8de59540361bd59bb4f5a84f884006f53e50c1c89d2bfbdb72f92fd4700029" diff --git a/rpc/v0/integration/strange_loop.sh b/rpc/v0/integration/strange_loop.sh new file mode 100755 index 00000000..d0cf04ee --- /dev/null +++ b/rpc/v0/integration/strange_loop.sh @@ -0,0 +1,5 @@ +#!/usr/bin/env bash + +script_dir="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" + +echo -e "package integration\n\nconst strangeLoopBytecode = \"$(solc --bin rpc/v0/integration/strange_loop.sol | tail -1)\"" > "${script_dir}/strange_loop.go" diff --git a/rpc/v0/integration/strange_loop.sol b/rpc/v0/integration/strange_loop.sol new file mode 100644 index 00000000..2a3e07f2 --- /dev/null +++ b/rpc/v0/integration/strange_loop.sol @@ -0,0 +1,31 @@ +pragma solidity ^0.4.16; + +contract StrangeLoop { + int top = 23; + int bottom = 34; + int depth = 17; + bool down = true; + + function UpsieDownsie() public returns (int i) { + i = depth; + if (down) { + if (depth < bottom) { + depth++; + i = depth; + this.UpsieDownsie(); + } else { + down = false; + i = depth; + this.UpsieDownsie(); + } + } else if (depth > top) { + depth--; + i = depth; + this.UpsieDownsie(); + } else { + down = true; + i = depth; + return; + } + } +} \ No newline at end of file diff --git a/rpc/v0/integration/v0_test.go b/rpc/v0/integration/v0_test.go index 91ef3715..ede0a128 100644 --- a/rpc/v0/integration/v0_test.go +++ b/rpc/v0/integration/v0_test.go @@ -20,9 +20,13 @@ package integration import ( "testing" + "encoding/hex" + + "github.com/hyperledger/burrow/execution/evm/abi" "github.com/hyperledger/burrow/rpc/v0" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + "github.com/hyperledger/burrow/binary" ) func TestTransact(t *testing.T) { @@ -44,13 +48,28 @@ func TestTransact(t *testing.T) { func TestTransactAndHold(t *testing.T) { cli := v0.NewV0Client("http://localhost:1337/rpc") - call, err := cli.TransactAndHold(v0.TransactParam{ + bc, err := hex.DecodeString(strangeLoopBytecode) + require.NoError(t, err) + create, err := cli.TransactAndHold(v0.TransactParam{ PrivKey: privateAccounts[0].PrivateKey().RawBytes(), Address: nil, - Data: []byte{}, + Data: bc, Fee: 2, GasLimit: 10000, }) require.NoError(t, err) - assert.Equal(t, 0, call.StackDepth) + assert.Equal(t, 0, create.StackDepth) + functionID := abi.FunctionID("UpsieDownsie()") + call, err := cli.TransactAndHold2(v0.TransactParam{ + PrivKey: privateAccounts[0].PrivateKey().RawBytes(), + Address: create.CallData.Callee.Bytes(), + Data: functionID[:], + Fee: 2, + GasLimit: 10000, + }) + require.NoError(t, err) + depth := binary.Uint64FromWord256(binary.LeftPadWord256(call.Return)) + // Would give 23 if taken from wrong frame + assert.Equal(t, 18, int(depth)) } + diff --git a/rpc/v0/methods.go b/rpc/v0/methods.go index cfa07587..77848f4a 100644 --- a/rpc/v0/methods.go +++ b/rpc/v0/methods.go @@ -229,12 +229,11 @@ func GetMethods(codec rpc.Codec, service *rpc.Service, logger *logging.Logger) m if err != nil { return nil, rpc.INVALID_PARAMS, err } - //inputAccount, err := service.Mempool().SigningAccountFromPrivateKey(param.PrivKey) - //if err != nil { - // return nil, rpc.INVALID_PARAMS, err - //} - //receipt, err := service.Transactor().Transact2(inputAccount, address, param.Data, param.GasLimit, param.Fee) - receipt, err := service.Transactor().Transact(param.PrivKey, address, param.Data, param.GasLimit, param.Fee) + inputAccount, err := service.Mempool().SigningAccountFromPrivateKey(param.PrivKey) + if err != nil { + return nil, rpc.INVALID_PARAMS, err + } + receipt, err := service.Transactor().Transact(inputAccount, address, param.Data, param.GasLimit, param.Fee) if err != nil { return nil, rpc.INTERNAL_ERROR, err } @@ -250,7 +249,11 @@ func GetMethods(codec rpc.Codec, service *rpc.Service, logger *logging.Logger) m if err != nil { return nil, rpc.INVALID_PARAMS, err } - ce, err := service.Transactor().TransactAndHold(param.PrivKey, address, param.Data, param.GasLimit, param.Fee) + inputAccount, err := service.Mempool().SigningAccountFromPrivateKey(param.PrivKey) + if err != nil { + return nil, rpc.INVALID_PARAMS, err + } + ce, err := service.Transactor().TransactAndHold(inputAccount, address, param.Data, param.GasLimit, param.Fee) if err != nil { return nil, rpc.INTERNAL_ERROR, err } -- GitLab