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