From da099db7ef5fa006bda2a3156f3dc64817bf3a4c Mon Sep 17 00:00:00 2001 From: Silas Davis <silas@monax.io> Date: Fri, 9 Feb 2018 21:24:36 +0000 Subject: [PATCH] Add Bytecode lexer and remove panic from splice Signed-off-by: Silas Davis <silas@monax.io> --- account/address.go | 6 +- account/bytecode.go | 72 ++++++++++++++++++++-- account/bytecode_test.go | 85 ++++++++++++++++++++++++++ binary/word160.go | 4 ++ execution/evm/asm/bc/helpers.go | 104 +++++++++++++++++++------------- execution/evm/asm/opcodes.go | 25 ++++++-- execution/evm/snative_test.go | 4 +- execution/evm/vm_test.go | 69 ++++++++++----------- execution/execution_test.go | 2 +- 9 files changed, 278 insertions(+), 93 deletions(-) create mode 100644 account/bytecode_test.go diff --git a/account/address.go b/account/address.go index 2b2e5ebe..23525397 100644 --- a/account/address.go +++ b/account/address.go @@ -74,7 +74,11 @@ func (address *Address) UnmarshalJSON(data []byte) error { } func (address Address) MarshalJSON() ([]byte, error) { - return json.Marshal(hex.EncodeUpperToString(address[:])) + text, err := address.MarshalText() + if err != nil { + return nil, err + } + return json.Marshal(string(text)) } func (address *Address) UnmarshalText(text []byte) error { diff --git a/account/bytecode.go b/account/bytecode.go index 76a49f97..f0f8b9a8 100644 --- a/account/bytecode.go +++ b/account/bytecode.go @@ -2,35 +2,95 @@ package account import ( "encoding/json" + "fmt" + "github.com/hyperledger/burrow/execution/evm/asm" + "github.com/hyperledger/burrow/execution/evm/asm/bc" "github.com/tmthrgd/go-hex" ) -// TODO: write a simple lexer that prints the opcodes. Each byte is either an OpCode or part of the Word256 argument -// to Push[1...32] type Bytecode []byte +// Builds new bytecode using the Splice helper to map byte-like and byte-slice-like types to a flat bytecode slice +func NewBytecode(bytelikes ...interface{}) (Bytecode, error) { + return bc.Splice(bytelikes...) +} + +func BytecodeFromHex(hexString string) (Bytecode, error) { + var bc Bytecode + err := bc.UnmarshalText([]byte(hexString)) + if err != nil { + return nil, err + } + return bc, nil +} + func (bc Bytecode) Bytes() []byte { return bc[:] } func (bc Bytecode) String() string { - return hex.EncodeToString(bc[:]) + return hex.EncodeUpperToString(bc[:]) } func (bc Bytecode) MarshalJSON() ([]byte, error) { - return json.Marshal(hex.EncodeUpperToString(bc[:])) + text, err := bc.MarshalText() + if err != nil { + return nil, err + } + return json.Marshal(string(text)) } -func (bc Bytecode) UnmarshalJSON(data []byte) error { +func (bc *Bytecode) UnmarshalJSON(data []byte) error { str := new(string) err := json.Unmarshal(data, str) if err != nil { return err } - _, err = hex.Decode(bc[:], []byte(*str)) + err = bc.UnmarshalText([]byte(*str)) if err != nil { return err } return nil } + +func (bc Bytecode) MarshalText() ([]byte, error) { + return ([]byte)(hex.EncodeUpperToString(bc)), nil +} + +func (bc *Bytecode) UnmarshalText(text []byte) error { + *bc = make([]byte, hex.DecodedLen(len(text))) + _, err := hex.Decode(*bc, text) + return err +} + +// Tokenises the bytecode into opcodes and values +func (bc Bytecode) Tokens() ([]string, error) { + // Overestimate of capacity in the presence of pushes + tokens := make([]string, 0, len(bc)) + + for i := 0; i < len(bc); i++ { + op, ok := asm.GetOpCode(bc[i]) + if !ok { + return tokens, fmt.Errorf("did not recognise byte %#x at position %v as an OpCode:\n %s", + bc[i], i, lexingPositionString(bc, i, tokens)) + } + tokens = append(tokens, op.Name()) + pushes := op.Pushes() + if pushes > 0 { + // This is a PUSH<N> OpCode so consume N bytes from the input, render them as hex, and skip to next OpCode + if i+pushes >= len(bc) { + return tokens, fmt.Errorf("token %v of input is %s but not enough input remains to push %v: %s", + i, op.Name(), pushes, lexingPositionString(bc, i, tokens)) + } + pushedBytes := bc[i+1 : i+pushes+1] + tokens = append(tokens, fmt.Sprintf("0x%s", pushedBytes)) + i += pushes + } + } + return tokens, nil +} + +func lexingPositionString(bc Bytecode, position int, produced []string) string { + return fmt.Sprintf("%v_%v", produced, []byte(bc[position:])) +} diff --git a/account/bytecode_test.go b/account/bytecode_test.go new file mode 100644 index 00000000..00f1bd0c --- /dev/null +++ b/account/bytecode_test.go @@ -0,0 +1,85 @@ +package account + +import ( + "encoding/json" + "strings" + "testing" + + "github.com/hyperledger/burrow/execution/evm/asm" + "github.com/hyperledger/burrow/execution/evm/asm/bc" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestBytecode_MarshalJSON(t *testing.T) { + bytecode := Bytecode{ + 73, 234, 48, 252, 174, + 115, 27, 222, 54, 116, + 47, 133, 144, 21, 73, + 245, 21, 234, 26, 50, + } + + bs, err := json.Marshal(bytecode) + assert.NoError(t, err) + + bytecodeOut := new(Bytecode) + err = json.Unmarshal(bs, bytecodeOut) + + assert.Equal(t, bytecode, *bytecodeOut) +} + +func TestBytecode_MarshalText(t *testing.T) { + bytecode := Bytecode{ + 73, 234, 48, 252, 174, + 115, 27, 222, 54, 116, + 47, 133, 144, 21, 73, + 245, 21, 234, 26, 50, + } + + bs, err := bytecode.MarshalText() + assert.NoError(t, err) + + bytecodeOut := new(Bytecode) + err = bytecodeOut.UnmarshalText(bs) + + assert.Equal(t, bytecode, *bytecodeOut) +} + +func TestBytecode_Tokens(t *testing.T) { + /* + pragma solidity ^0.4.0; + + contract SimpleStorage { + function get() public constant returns (address) { + return msg.sender; + } + } + */ + + // This bytecode is compiled from Solidity contract above the remix compiler with 0.4.0 + codeHex := "606060405260808060106000396000f360606040526000357c0100000000000000000000000000000000000000000000000" + + "000000000900480636d4ce63c146039576035565b6002565b34600257604860048050506074565b604051808273fffffffffffffff" + + "fffffffffffffffffffffffff16815260200191505060405180910390f35b6000339050607d565b9056" + bytecode, err := BytecodeFromHex(codeHex) + require.NoError(t, err) + tokens, err := bytecode.Tokens() + require.NoError(t, err) + // With added leading zero in hex where needed + remixOpcodes := "PUSH1 0x60 PUSH1 0x40 MSTORE PUSH1 0x80 DUP1 PUSH1 0x10 PUSH1 0x00 CODECOPY PUSH1 0x00 RETURN " + + "PUSH1 0x60 PUSH1 0x40 MSTORE PUSH1 0x00 CALLDATALOAD " + + "PUSH29 0x0100000000000000000000000000000000000000000000000000000000 SWAP1 DIV DUP1 PUSH4 0x6D4CE63C EQ " + + "PUSH1 0x39 JUMPI PUSH1 0x35 JUMP JUMPDEST PUSH1 0x02 JUMP JUMPDEST CALLVALUE PUSH1 0x02 JUMPI " + + "PUSH1 0x48 PUSH1 0x04 DUP1 POP POP PUSH1 0x74 JUMP JUMPDEST PUSH1 0x40 MLOAD DUP1 DUP3 " + + "PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF AND DUP2 MSTORE PUSH1 0x20 ADD " + + "SWAP2 POP POP PUSH1 0x40 MLOAD DUP1 SWAP2 SUB SWAP1 RETURN JUMPDEST PUSH1 0x00 " + + "CALLER SWAP1 POP PUSH1 0x7D JUMP JUMPDEST SWAP1 JUMP" + assert.Equal(t, remixOpcodes, strings.Join(tokens, " ")) + + // Test empty bytecode + tokens, err = Bytecode(nil).Tokens() + require.NoError(t, err) + assert.Equal(t, []string{}, tokens) + + tokens, err = Bytecode(bc.MustSplice(asm.PUSH3, 1, 2)).Tokens() + assert.Error(t, err, "not enough bytes to push") +} diff --git a/binary/word160.go b/binary/word160.go index 991cbee7..21456d1b 100644 --- a/binary/word160.go +++ b/binary/word160.go @@ -12,3 +12,7 @@ func (w Word160) Word256() (word256 Word256) { copy(word256[Word256Word160Delta:], w[:]) return } + +func (w Word160) Bytes() []byte { + return w[:] +} diff --git a/execution/evm/asm/bc/helpers.go b/execution/evm/asm/bc/helpers.go index 4d8058d1..f3a10a59 100644 --- a/execution/evm/asm/bc/helpers.go +++ b/execution/evm/asm/bc/helpers.go @@ -3,54 +3,14 @@ package bc import ( "fmt" - "github.com/hyperledger/burrow/account" - "github.com/hyperledger/burrow/binary" "github.com/hyperledger/burrow/execution/evm/asm" ) -// Convenience function to allow us to mix bytes, ints, and OpCodes that -// represent bytes in an EVM assembly code to make assembly more readable. -// Also allows us to splice together assembly -// fragments because any []byte arguments are flattened in the result. -func Splice(bytelikes ...interface{}) []byte { - bytes := make([]byte, len(bytelikes)) - for i, bytelike := range bytelikes { - switch b := bytelike.(type) { - case byte: - bytes[i] = b - case asm.OpCode: - bytes[i] = byte(b) - case int: - bytes[i] = byte(b) - if int(bytes[i]) != b { - panic(fmt.Sprintf("The int %v does not fit inside a byte", b)) - } - case int64: - bytes[i] = byte(b) - if int64(bytes[i]) != b { - panic(fmt.Sprintf("The int64 %v does not fit inside a byte", b)) - } - case uint64: - bytes[i] = byte(b) - if uint64(bytes[i]) != b { - panic(fmt.Sprintf("The uint64 %v does not fit inside a byte", b)) - } - case binary.Word256: - return Concat(bytes[:i], b[:], Splice(bytelikes[i+1:]...)) - case binary.Word160: - return Concat(bytes[:i], b[:], Splice(bytelikes[i+1:]...)) - case account.Address: - return Concat(bytes[:i], b[:], Splice(bytelikes[i+1:]...)) - case []byte: - // splice - return Concat(bytes[:i], b, Splice(bytelikes[i+1:]...)) - default: - panic(fmt.Errorf("could not convert %s to a byte or sequence of bytes", bytelike)) - } - } - return bytes +type ByteSlicable interface { + Bytes() []byte } +// Concatenate multiple byte slices without unnecessary copying func Concat(bss ...[]byte) []byte { offset := 0 for _, bs := range bss { @@ -66,3 +26,61 @@ func Concat(bss ...[]byte) []byte { } return bytes } + +// Splice or panic +func MustSplice(bytelikes ...interface{}) []byte { + spliced, err := Splice(bytelikes...) + if err != nil { + panic(err) + } + return spliced +} + +// Convenience function to allow us to mix bytes, ints, and OpCodes that +// represent bytes in an EVM assembly code to make assembly more readable. +// Also allows us to splice together assembly +// fragments because any []byte arguments are flattened in the result. +func Splice(bytelikes ...interface{}) ([]byte, error) { + bytes := make([]byte, 0, len(bytelikes)) + for _, bytelike := range bytelikes { + bs, err := byteSlicify(bytelike) + if err != nil { + return nil, err + } + bytes = append(bytes, bs...) + } + return bytes, nil +} + +// Convert anything byte or byte slice like to a byte slice +func byteSlicify(bytelike interface{}) ([]byte, error) { + switch b := bytelike.(type) { + case byte: + return []byte{b}, nil + case asm.OpCode: + return []byte{byte(b)}, nil + case int: + if int(byte(b)) != b { + return nil, fmt.Errorf("the int %v does not fit inside a byte", b) + } + return []byte{byte(b)}, nil + case int64: + if int64(byte(b)) != b { + return nil, fmt.Errorf("the int64 %v does not fit inside a byte", b) + } + return []byte{byte(b)}, nil + case uint64: + if uint64(byte(b)) != b { + return nil, fmt.Errorf("the uint64 %v does not fit inside a byte", b) + } + return []byte{byte(b)}, nil + case string: + return []byte(b), nil + case ByteSlicable: + return b.Bytes(), nil + case []byte: + return b, nil + default: + return nil, fmt.Errorf("could not convert %s to a byte or sequence of bytes", bytelike) + } +} diff --git a/execution/evm/asm/opcodes.go b/execution/evm/asm/opcodes.go index bbe5dec9..71719d1e 100644 --- a/execution/evm/asm/opcodes.go +++ b/execution/evm/asm/opcodes.go @@ -187,8 +187,7 @@ const ( SELFDESTRUCT = 0xff ) -// Since the opcodes aren't all in order we can't use a regular slice -var opCodeToString = map[OpCode]string{ +var opCodeNames = map[OpCode]string{ // 0x0 range - arithmetic ops STOP: "STOP", ADD: "ADD", @@ -340,13 +339,19 @@ var opCodeToString = map[OpCode]string{ SELFDESTRUCT: "SELFDESTRUCT", } -func OpCodeName(op OpCode) (name string, isOpcode bool) { - name, isOpcode = opCodeToString[op] - return name, isOpcode +func GetOpCode(b byte) (OpCode, bool) { + op := OpCode(b) + _, isOpcode := opCodeNames[op] + return op, isOpcode + } func (o OpCode) String() string { - str := opCodeToString[o] + return o.Name() +} + +func (o OpCode) Name() string { + str := opCodeNames[o] if len(str) == 0 { return fmt.Sprintf("Missing opcode 0x%x", int(o)) } @@ -354,6 +359,14 @@ func (o OpCode) String() string { return str } +// If OpCode is a Push<N> returns the number of bytes pushed (between 1 and 32 inclusive) +func (o OpCode) Pushes() int { + if o >= PUSH1 && o <= PUSH32 { + return int(o - PUSH1 + 1) + } + return 0 +} + //----------------------------------------------------------------------------- func AnalyzeJumpDests(code []byte) (dests *set.Set) { diff --git a/execution/evm/snative_test.go b/execution/evm/snative_test.go index ab173b97..39c31e72 100644 --- a/execution/evm/snative_test.go +++ b/execution/evm/snative_test.go @@ -81,7 +81,7 @@ func TestSNativeContractDescription_Dispatch(t *testing.T) { gas := uint64(1000) // Should fail since we have no permissions - retValue, err := contract.Dispatch(state, caller, bc.Splice(funcID[:], + retValue, err := contract.Dispatch(state, caller, bc.MustSplice(funcID[:], grantee.Address(), permFlagToWord256(permission.CreateAccount)), &gas, logger) if !assert.Error(t, err, "Should fail due to lack of permissions") { return @@ -90,7 +90,7 @@ func TestSNativeContractDescription_Dispatch(t *testing.T) { // Grant all permissions and dispatch should success caller.SetPermissions(allAccountPermissions()) - retValue, err = contract.Dispatch(state, caller, bc.Splice(funcID[:], + retValue, err = contract.Dispatch(state, caller, bc.MustSplice(funcID[:], grantee.Address().Word256(), permFlagToWord256(permission.CreateAccount)), &gas, logger) assert.NoError(t, err) assert.Equal(t, retValue, LeftPadBytes([]byte{1}, 32)) diff --git a/execution/evm/vm_test.go b/execution/evm/vm_test.go index dad398cd..ecff1891 100644 --- a/execution/evm/vm_test.go +++ b/execution/evm/vm_test.go @@ -80,7 +80,7 @@ func TestVM(t *testing.T) { var gas uint64 = 100000 - bytecode := Splice(PUSH1, 0x00, PUSH1, 0x20, MSTORE, JUMPDEST, PUSH2, 0x0F, 0x0F, PUSH1, 0x20, MLOAD, + bytecode := MustSplice(PUSH1, 0x00, PUSH1, 0x20, MSTORE, JUMPDEST, PUSH2, 0x0F, 0x0F, PUSH1, 0x20, MLOAD, SLT, ISZERO, PUSH1, 0x1D, JUMPI, PUSH1, 0x01, PUSH1, 0x20, MLOAD, ADD, PUSH1, 0x20, MSTORE, PUSH1, 0x05, JUMP, JUMPDEST) @@ -103,7 +103,7 @@ func TestJumpErr(t *testing.T) { var gas uint64 = 100000 - bytecode := Splice(PUSH1, 0x10, JUMP) + bytecode := MustSplice(PUSH1, 0x10, JUMP) var err error ch := make(chan struct{}) @@ -136,7 +136,7 @@ func TestSubcurrency(t *testing.T) { var gas uint64 = 1000 - bytecode := Splice(PUSH3, 0x0F, 0x42, 0x40, CALLER, SSTORE, PUSH29, 0x01, 0x00, 0x00, 0x00, + bytecode := MustSplice(PUSH3, 0x0F, 0x42, 0x40, CALLER, SSTORE, PUSH29, 0x01, 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, PUSH1, 0x00, CALLDATALOAD, DIV, PUSH4, 0x15, 0xCF, 0x26, 0x84, DUP2, EQ, ISZERO, PUSH2, @@ -220,20 +220,20 @@ func TestDelegateCallGas(t *testing.T) { // Do a simple operation using 1 gas unit calleeAccount, calleeAddress := makeAccountWithCode(state, "callee", - Splice(PUSH1, calleeReturnValue, return1())) + MustSplice(PUSH1, calleeReturnValue, return1())) // Here we split up the caller code so we can make a DELEGATE call with // different amounts of gas. The value we sandwich in the middle is the amount // we subtract from the available gas (that the caller has available), so: - // code := Splice(callerCodePrefix, <amount to subtract from GAS> , callerCodeSuffix) + // code := MustSplice(callerCodePrefix, <amount to subtract from GAS> , callerCodeSuffix) // gives us the code to make the call - callerCodePrefix := Splice(PUSH1, retSize, PUSH1, retOff, PUSH1, inSize, + callerCodePrefix := MustSplice(PUSH1, retSize, PUSH1, retOff, PUSH1, inSize, PUSH1, inOff, PUSH20, calleeAddress, PUSH1) - callerCodeSuffix := Splice(GAS, SUB, DELEGATECALL, returnWord()) + callerCodeSuffix := MustSplice(GAS, SUB, DELEGATECALL, returnWord()) // Perform a delegate call callerAccount, _ := makeAccountWithCode(state, "caller", - Splice(callerCodePrefix, + MustSplice(callerCodePrefix, // Give just enough gas to make the DELEGATECALL costBetweenGasAndDelegateCall, callerCodeSuffix)) @@ -244,7 +244,7 @@ func TestDelegateCallGas(t *testing.T) { assert.NoError(t, err, "Should have sufficient funds for call") assert.Equal(t, Int64ToWord256(calleeReturnValue).Bytes(), output) - callerAccount.SetCode(Splice(callerCodePrefix, + callerAccount.SetCode(MustSplice(callerCodePrefix, // Shouldn't be enough gas to make call costBetweenGasAndDelegateCall-1, callerCodeSuffix)) @@ -267,7 +267,7 @@ func TestMemoryBounds(t *testing.T) { // This attempts to store a value at the memory boundary and return it word := One256 output, err := ourVm.call(caller, callee, - Splice(pushWord(word), storeAtEnd(), MLOAD, storeAtEnd(), returnAfterStore()), + MustSplice(pushWord(word), storeAtEnd(), MLOAD, storeAtEnd(), returnAfterStore()), nil, 0, &gas) assert.NoError(t, err) assert.Equal(t, word.Bytes(), output) @@ -275,7 +275,7 @@ func TestMemoryBounds(t *testing.T) { // Same with number word = Int64ToWord256(232234234432) output, err = ourVm.call(caller, callee, - Splice(pushWord(word), storeAtEnd(), MLOAD, storeAtEnd(), returnAfterStore()), + MustSplice(pushWord(word), storeAtEnd(), MLOAD, storeAtEnd(), returnAfterStore()), nil, 0, &gas) assert.NoError(t, err) assert.Equal(t, word.Bytes(), output) @@ -283,9 +283,9 @@ func TestMemoryBounds(t *testing.T) { // Now test a series of boundary stores code := pushWord(word) for i := 0; i < 10; i++ { - code = Splice(code, storeAtEnd(), MLOAD) + code = MustSplice(code, storeAtEnd(), MLOAD) } - output, err = ourVm.call(caller, callee, Splice(code, storeAtEnd(), returnAfterStore()), + output, err = ourVm.call(caller, callee, MustSplice(code, storeAtEnd(), returnAfterStore()), nil, 0, &gas) assert.NoError(t, err) assert.Equal(t, word.Bytes(), output) @@ -293,9 +293,9 @@ func TestMemoryBounds(t *testing.T) { // Same as above but we should breach the upper memory limit set in memoryProvider code = pushWord(word) for i := 0; i < 100; i++ { - code = Splice(code, storeAtEnd(), MLOAD) + code = MustSplice(code, storeAtEnd(), MLOAD) } - output, err = ourVm.call(caller, callee, Splice(code, storeAtEnd(), returnAfterStore()), + output, err = ourVm.call(caller, callee, MustSplice(code, storeAtEnd(), returnAfterStore()), nil, 0, &gas) assert.Error(t, err, "Should hit memory out of bounds") } @@ -355,22 +355,22 @@ func TestMsgSender(t *testing.T) { // stores that value at the current memory boundary func storeAtEnd() []byte { // Pull in MSIZE (to carry forward to MLOAD), swap in value to store, store it at MSIZE - return Splice(MSIZE, SWAP1, DUP2, MSTORE) + return MustSplice(MSIZE, SWAP1, DUP2, MSTORE) } func returnAfterStore() []byte { - return Splice(PUSH1, 32, DUP2, RETURN) + return MustSplice(PUSH1, 32, DUP2, RETURN) } // Store the top element of the stack (which is a 32-byte word) in memory // and return it. Useful for a simple return value. func return1() []byte { - return Splice(PUSH1, 0, MSTORE, returnWord()) + return MustSplice(PUSH1, 0, MSTORE, returnWord()) } func returnWord() []byte { // PUSH1 => return size, PUSH1 => return offset, RETURN - return Splice(PUSH1, 32, PUSH1, 0, RETURN) + return MustSplice(PUSH1, 32, PUSH1, 0, RETURN) } func makeAccountWithCode(state acm.Updater, name string, @@ -442,7 +442,7 @@ func callContractCode(addr acm.Address) []byte { inOff, inSize := byte(0x0), byte(0x0) // no call data retOff, retSize := byte(0x0), byte(0x20) // this is the code we want to run (send funds to an account and return) - return Splice(PUSH1, retSize, PUSH1, retOff, PUSH1, inSize, PUSH1, + return MustSplice(PUSH1, retSize, PUSH1, retOff, PUSH1, inSize, PUSH1, inOff, PUSH1, value, PUSH20, addr, PUSH2, gas1, gas2, CALL, PUSH1, retSize, PUSH1, retOff, RETURN) } @@ -459,38 +459,39 @@ func pushWord(word Word256) []byte { if word[leadingZeros] == 0 { leadingZeros++ } else { - return Splice(byte(PUSH32)-leadingZeros, word[leadingZeros:]) + return MustSplice(byte(PUSH32)-leadingZeros, word[leadingZeros:]) } } - return Splice(PUSH1, 0) + return MustSplice(PUSH1, 0) } func TestPushWord(t *testing.T) { word := Int64ToWord256(int64(2133213213)) - assert.Equal(t, Splice(PUSH4, 0x7F, 0x26, 0x40, 0x1D), pushWord(word)) + assert.Equal(t, MustSplice(PUSH4, 0x7F, 0x26, 0x40, 0x1D), pushWord(word)) word[0] = 1 - assert.Equal(t, Splice(PUSH32, + assert.Equal(t, MustSplice(PUSH32, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x7F, 0x26, 0x40, 0x1D), pushWord(word)) - assert.Equal(t, Splice(PUSH1, 0), pushWord(Word256{})) - assert.Equal(t, Splice(PUSH1, 1), pushWord(Int64ToWord256(1))) + assert.Equal(t, MustSplice(PUSH1, 0), pushWord(Word256{})) + assert.Equal(t, MustSplice(PUSH1, 1), pushWord(Int64ToWord256(1))) } +// Kind of indirect test of Splice, but here to avoid import cycles func TestBytecode(t *testing.T) { assert.Equal(t, - Splice(1, 2, 3, 4, 5, 6), - Splice(1, 2, 3, Splice(4, 5, 6))) + MustSplice(1, 2, 3, 4, 5, 6), + MustSplice(1, 2, 3, MustSplice(4, 5, 6))) assert.Equal(t, - Splice(1, 2, 3, 4, 5, 6, 7, 8), - Splice(1, 2, 3, Splice(4, Splice(5), 6), 7, 8)) + MustSplice(1, 2, 3, 4, 5, 6, 7, 8), + MustSplice(1, 2, 3, MustSplice(4, MustSplice(5), 6), 7, 8)) assert.Equal(t, - Splice(PUSH1, 2), - Splice(byte(PUSH1), 0x02)) + MustSplice(PUSH1, 2), + MustSplice(byte(PUSH1), 0x02)) assert.Equal(t, []byte{}, - Splice(Splice(Splice()))) + MustSplice(MustSplice(MustSplice()))) contractAccount := &acm.ConcreteAccount{Address: acm.AddressFromWord256(Int64ToWord256(102))} addr := contractAccount.Address @@ -498,7 +499,7 @@ func TestBytecode(t *testing.T) { value := byte(0x69) inOff, inSize := byte(0x0), byte(0x0) // no call data retOff, retSize := byte(0x0), byte(0x20) - contractCodeBytecode := Splice(PUSH1, retSize, PUSH1, retOff, PUSH1, inSize, PUSH1, + contractCodeBytecode := MustSplice(PUSH1, retSize, PUSH1, retOff, PUSH1, inSize, PUSH1, inOff, PUSH1, value, PUSH20, addr, PUSH2, gas1, gas2, CALL, PUSH1, retSize, PUSH1, retOff, RETURN) contractCode := []byte{0x60, retSize, 0x60, retOff, 0x60, inSize, 0x60, inOff, 0x60, value, 0x73} diff --git a/execution/execution_test.go b/execution/execution_test.go index 68e4d96b..7d1ac046 100644 --- a/execution/execution_test.go +++ b/execution/execution_test.go @@ -1292,7 +1292,7 @@ func callContractCode(contractAddr acm.Address) []byte { retOff, retSize := byte(0x0), byte(0x20) // this is the code we want to run (call a contract and return) - return bc.Splice(CALLDATASIZE, PUSH1, inputOff, PUSH1, memOff, + return bc.MustSplice(CALLDATASIZE, PUSH1, inputOff, PUSH1, memOff, CALLDATACOPY, PUSH1, retSize, PUSH1, retOff, CALLDATASIZE, PUSH1, inOff, PUSH1, value, PUSH20, contractAddr, // Zeno loves us - call with half of the available gas each time we CALL -- GitLab