From 28aea46ac08579f3ecd1c35620915b8e1bfcc8b0 Mon Sep 17 00:00:00 2001
From: rjl493456442 <garyrong0905@gmail.com>
Date: Mon, 21 Aug 2017 08:47:15 +0800
Subject: core: implement Metropolis EIP 658, receipt status byte

---
 accounts/abi/bind/backends/simulated.go |  3 ++-
 common/bytes.go                         |  3 +++
 core/database_util_test.go              |  4 +--
 core/state_processor.go                 |  4 +--
 core/state_transition.go                | 19 +++++++-------
 core/types/gen_receipt_json.go          |  6 +++++
 core/types/receipt.go                   | 44 ++++++++++++++++++++++-----------
 core/vm/evm.go                          |  6 ++++-
 core/vm/instructions.go                 |  2 +-
 eth/api.go                              |  5 ++--
 eth/backend_test.go                     |  4 +--
 eth/filters/filter_test.go              | 10 ++++----
 internal/ethapi/api.go                  |  3 ++-
 les/odr_test.go                         |  4 +--
 light/odr_test.go                       |  2 +-
 tests/state_test_util.go                |  2 +-
 16 files changed, 75 insertions(+), 46 deletions(-)

diff --git a/accounts/abi/bind/backends/simulated.go b/accounts/abi/bind/backends/simulated.go
index e0ee06a0a..88fb3331e 100644
--- a/accounts/abi/bind/backends/simulated.go
+++ b/accounts/abi/bind/backends/simulated.go
@@ -253,7 +253,8 @@ func (b *SimulatedBackend) callContract(ctx context.Context, call ethereum.CallM
 	// about the transaction and calling mechanisms.
 	vmenv := vm.NewEVM(evmContext, statedb, b.config, vm.Config{})
 	gaspool := new(core.GasPool).AddGas(math.MaxBig256)
-	ret, gasUsed, _, err := core.NewStateTransition(vmenv, msg, gaspool).TransitionDb()
+	// TODO utilize returned failed flag to help gas estimation.
+	ret, gasUsed, _, _, err := core.NewStateTransition(vmenv, msg, gaspool).TransitionDb()
 	return ret, gasUsed, err
 }
 
diff --git a/common/bytes.go b/common/bytes.go
index c445968f2..66577bbfd 100644
--- a/common/bytes.go
+++ b/common/bytes.go
@@ -47,6 +47,9 @@ func FromHex(s string) []byte {
 //
 // Returns an exact copy of the provided bytes
 func CopyBytes(b []byte) (copiedBytes []byte) {
+	if b == nil {
+		return nil
+	}
 	copiedBytes = make([]byte, len(b))
 	copy(copiedBytes, b)
 
diff --git a/core/database_util_test.go b/core/database_util_test.go
index e9a6df97b..5c75d53d0 100644
--- a/core/database_util_test.go
+++ b/core/database_util_test.go
@@ -461,12 +461,12 @@ func TestMipmapChain(t *testing.T) {
 		var receipts types.Receipts
 		switch i {
 		case 1:
-			receipt := types.NewReceipt(nil, new(big.Int))
+			receipt := types.NewReceipt(nil, false, new(big.Int))
 			receipt.Logs = []*types.Log{{Address: addr, Topics: []common.Hash{hash1}}}
 			gen.AddUncheckedReceipt(receipt)
 			receipts = types.Receipts{receipt}
 		case 1000:
-			receipt := types.NewReceipt(nil, new(big.Int))
+			receipt := types.NewReceipt(nil, false, new(big.Int))
 			receipt.Logs = []*types.Log{{Address: addr2}}
 			gen.AddUncheckedReceipt(receipt)
 			receipts = types.Receipts{receipt}
diff --git a/core/state_processor.go b/core/state_processor.go
index 4489cfce2..a4b554b10 100644
--- a/core/state_processor.go
+++ b/core/state_processor.go
@@ -98,7 +98,7 @@ func ApplyTransaction(config *params.ChainConfig, bc *BlockChain, author *common
 	// about the transaction and calling mechanisms.
 	vmenv := vm.NewEVM(context, statedb, config, cfg)
 	// Apply the transaction to the current state (included in the env)
-	_, gas, err := ApplyMessage(vmenv, msg, gp)
+	_, gas, failed, err := ApplyMessage(vmenv, msg, gp)
 	if err != nil {
 		return nil, nil, err
 	}
@@ -114,7 +114,7 @@ func ApplyTransaction(config *params.ChainConfig, bc *BlockChain, author *common
 
 	// Create a new receipt for the transaction, storing the intermediate root and gas used by the tx
 	// based on the eip phase, we're passing wether the root touch-delete accounts.
-	receipt := types.NewReceipt(root, usedGas)
+	receipt := types.NewReceipt(root, failed, usedGas)
 	receipt.TxHash = tx.Hash()
 	receipt.GasUsed = new(big.Int).Set(gas)
 	// if the transaction created a contract, store the creation address in the receipt.
diff --git a/core/state_transition.go b/core/state_transition.go
index 0ae9d7fcb..bab4540be 100644
--- a/core/state_transition.go
+++ b/core/state_transition.go
@@ -59,8 +59,7 @@ type StateTransition struct {
 	value      *big.Int
 	data       []byte
 	state      vm.StateDB
-
-	evm *vm.EVM
+	evm        *vm.EVM
 }
 
 // Message represents a message sent to a contract.
@@ -127,11 +126,11 @@ func NewStateTransition(evm *vm.EVM, msg Message, gp *GasPool) *StateTransition
 // the gas used (which includes gas refunds) and an error if it failed. An error always
 // indicates a core error meaning that the message would always fail for that particular
 // state and would never be accepted within a block.
-func ApplyMessage(evm *vm.EVM, msg Message, gp *GasPool) ([]byte, *big.Int, error) {
+func ApplyMessage(evm *vm.EVM, msg Message, gp *GasPool) ([]byte, *big.Int, bool, error) {
 	st := NewStateTransition(evm, msg, gp)
 
-	ret, _, gasUsed, err := st.TransitionDb()
-	return ret, gasUsed, err
+	ret, _, gasUsed, failed, err := st.TransitionDb()
+	return ret, gasUsed, failed, err
 }
 
 func (st *StateTransition) from() vm.AccountRef {
@@ -208,7 +207,7 @@ func (st *StateTransition) preCheck() error {
 // TransitionDb will transition the state by applying the current message and returning the result
 // including the required gas for the operation as well as the used gas. It returns an error if it
 // failed. An error indicates a consensus issue.
-func (st *StateTransition) TransitionDb() (ret []byte, requiredGas, usedGas *big.Int, err error) {
+func (st *StateTransition) TransitionDb() (ret []byte, requiredGas, usedGas *big.Int, failed bool, err error) {
 	if err = st.preCheck(); err != nil {
 		return
 	}
@@ -222,10 +221,10 @@ func (st *StateTransition) TransitionDb() (ret []byte, requiredGas, usedGas *big
 	// TODO convert to uint64
 	intrinsicGas := IntrinsicGas(st.data, contractCreation, homestead)
 	if intrinsicGas.BitLen() > 64 {
-		return nil, nil, nil, vm.ErrOutOfGas
+		return nil, nil, nil, false, vm.ErrOutOfGas
 	}
 	if err = st.useGas(intrinsicGas.Uint64()); err != nil {
-		return nil, nil, nil, err
+		return nil, nil, nil, false, err
 	}
 
 	var (
@@ -248,7 +247,7 @@ func (st *StateTransition) TransitionDb() (ret []byte, requiredGas, usedGas *big
 		// sufficient balance to make the transfer happen. The first
 		// balance transfer may never fail.
 		if vmerr == vm.ErrInsufficientBalance {
-			return nil, nil, nil, vmerr
+			return nil, nil, nil, false, vmerr
 		}
 	}
 	requiredGas = new(big.Int).Set(st.gasUsed())
@@ -256,7 +255,7 @@ func (st *StateTransition) TransitionDb() (ret []byte, requiredGas, usedGas *big
 	st.refundGas()
 	st.state.AddBalance(st.evm.Coinbase, new(big.Int).Mul(st.gasUsed(), st.gasPrice))
 
-	return ret, requiredGas, st.gasUsed(), err
+	return ret, requiredGas, st.gasUsed(), vmerr != nil, err
 }
 
 func (st *StateTransition) refundGas() {
diff --git a/core/types/gen_receipt_json.go b/core/types/gen_receipt_json.go
index eb2e5d42b..1e6880c22 100644
--- a/core/types/gen_receipt_json.go
+++ b/core/types/gen_receipt_json.go
@@ -14,6 +14,7 @@ import (
 func (r Receipt) MarshalJSON() ([]byte, error) {
 	type Receipt struct {
 		PostState         hexutil.Bytes  `json:"root"`
+		Failed            bool           `json:"failed"`
 		CumulativeGasUsed *hexutil.Big   `json:"cumulativeGasUsed" gencodec:"required"`
 		Bloom             Bloom          `json:"logsBloom"         gencodec:"required"`
 		Logs              []*Log         `json:"logs"              gencodec:"required"`
@@ -23,6 +24,7 @@ func (r Receipt) MarshalJSON() ([]byte, error) {
 	}
 	var enc Receipt
 	enc.PostState = r.PostState
+	enc.Failed = r.Failed
 	enc.CumulativeGasUsed = (*hexutil.Big)(r.CumulativeGasUsed)
 	enc.Bloom = r.Bloom
 	enc.Logs = r.Logs
@@ -35,6 +37,7 @@ func (r Receipt) MarshalJSON() ([]byte, error) {
 func (r *Receipt) UnmarshalJSON(input []byte) error {
 	type Receipt struct {
 		PostState         hexutil.Bytes   `json:"root"`
+		Failed            *bool           `json:"failed"`
 		CumulativeGasUsed *hexutil.Big    `json:"cumulativeGasUsed" gencodec:"required"`
 		Bloom             *Bloom          `json:"logsBloom"         gencodec:"required"`
 		Logs              []*Log          `json:"logs"              gencodec:"required"`
@@ -49,6 +52,9 @@ func (r *Receipt) UnmarshalJSON(input []byte) error {
 	if dec.PostState != nil {
 		r.PostState = dec.PostState
 	}
+	if dec.Failed != nil {
+		r.Failed = *dec.Failed
+	}
 	if dec.CumulativeGasUsed == nil {
 		return errors.New("missing required field 'cumulativeGasUsed' for Receipt")
 	}
diff --git a/core/types/receipt.go b/core/types/receipt.go
index c9906b015..9c49648b4 100644
--- a/core/types/receipt.go
+++ b/core/types/receipt.go
@@ -28,10 +28,16 @@ import (
 
 //go:generate gencodec -type Receipt -field-override receiptMarshaling -out gen_receipt_json.go
 
+const (
+	receiptStatusSuccessful = byte(0x01)
+	receiptStatusFailed     = byte(0x00)
+)
+
 // Receipt represents the results of a transaction.
 type Receipt struct {
 	// Consensus fields
 	PostState         []byte   `json:"root"`
+	Failed            bool     `json:"failed"`
 	CumulativeGasUsed *big.Int `json:"cumulativeGasUsed" gencodec:"required"`
 	Bloom             Bloom    `json:"logsBloom"         gencodec:"required"`
 	Logs              []*Log   `json:"logs"              gencodec:"required"`
@@ -60,21 +66,26 @@ type homesteadReceiptRLP struct {
 // metropolisReceiptRLP contains the receipt's Metropolis consensus fields, used
 // during RLP serialization.
 type metropolisReceiptRLP struct {
+	Status            byte
 	CumulativeGasUsed *big.Int
 	Bloom             Bloom
 	Logs              []*Log
 }
 
 // NewReceipt creates a barebone transaction receipt, copying the init fields.
-func NewReceipt(root []byte, cumulativeGasUsed *big.Int) *Receipt {
-	return &Receipt{PostState: common.CopyBytes(root), CumulativeGasUsed: new(big.Int).Set(cumulativeGasUsed)}
+func NewReceipt(root []byte, failed bool, cumulativeGasUsed *big.Int) *Receipt {
+	return &Receipt{PostState: common.CopyBytes(root), Failed: failed, CumulativeGasUsed: new(big.Int).Set(cumulativeGasUsed)}
 }
 
 // EncodeRLP implements rlp.Encoder, and flattens the consensus fields of a receipt
 // into an RLP stream. If no post state is present, metropolis fork is assumed.
 func (r *Receipt) EncodeRLP(w io.Writer) error {
 	if r.PostState == nil {
-		return rlp.Encode(w, &metropolisReceiptRLP{r.CumulativeGasUsed, r.Bloom, r.Logs})
+		status := receiptStatusSuccessful
+		if r.Failed {
+			status = receiptStatusFailed
+		}
+		return rlp.Encode(w, &metropolisReceiptRLP{status, r.CumulativeGasUsed, r.Bloom, r.Logs})
 	}
 	return rlp.Encode(w, &homesteadReceiptRLP{r.PostState, r.CumulativeGasUsed, r.Bloom, r.Logs})
 }
@@ -87,29 +98,31 @@ func (r *Receipt) DecodeRLP(s *rlp.Stream) error {
 	if err != nil {
 		return err
 	}
-	list, _, err := rlp.SplitList(raw)
+	content, _, err := rlp.SplitList(raw)
 	if err != nil {
 		return err
 	}
-	items, err := rlp.CountValues(list)
+	kind, cnt, _, err := rlp.Split(content)
 	if err != nil {
 		return err
 	}
-	// Deserialize based on the number of content items
-	switch items {
-	case 3:
-		// Metropolis receipts have 3 components
+	// Deserialize based on the first component type.
+	switch {
+	case kind == rlp.Byte || kind == rlp.String && len(cnt) == 0:
+		// The first component of metropolis receipts is Byte
+		// or empty String(byte with 0x00 value).
 		var metro metropolisReceiptRLP
 		if err := rlp.DecodeBytes(raw, &metro); err != nil {
 			return err
 		}
+		r.Failed = metro.Status == receiptStatusFailed
 		r.CumulativeGasUsed = metro.CumulativeGasUsed
 		r.Bloom = metro.Bloom
 		r.Logs = metro.Logs
 		return nil
 
-	case 4:
-		// Homestead receipts have 4 components
+	case kind == rlp.String:
+		// The first component of homestead receipts is non-empty String.
 		var home homesteadReceiptRLP
 		if err := rlp.DecodeBytes(raw, &home); err != nil {
 			return err
@@ -121,14 +134,14 @@ func (r *Receipt) DecodeRLP(s *rlp.Stream) error {
 		return nil
 
 	default:
-		return fmt.Errorf("invalid receipt components: %v", items)
+		return fmt.Errorf("invalid first receipt component: %v", kind)
 	}
 }
 
 // String implements the Stringer interface.
 func (r *Receipt) String() string {
 	if r.PostState == nil {
-		return fmt.Sprintf("receipt{cgas=%v bloom=%x logs=%v}", r.CumulativeGasUsed, r.Bloom, r.Logs)
+		return fmt.Sprintf("receipt{failed=%t cgas=%v bloom=%x logs=%v}", r.Failed, r.CumulativeGasUsed, r.Bloom, r.Logs)
 	}
 	return fmt.Sprintf("receipt{med=%x cgas=%v bloom=%x logs=%v}", r.PostState, r.CumulativeGasUsed, r.Bloom, r.Logs)
 }
@@ -144,7 +157,7 @@ func (r *ReceiptForStorage) EncodeRLP(w io.Writer) error {
 	for i, log := range r.Logs {
 		logs[i] = (*LogForStorage)(log)
 	}
-	return rlp.Encode(w, []interface{}{r.PostState, r.CumulativeGasUsed, r.Bloom, r.TxHash, r.ContractAddress, logs, r.GasUsed})
+	return rlp.Encode(w, []interface{}{r.PostState, r.Failed, r.CumulativeGasUsed, r.Bloom, r.TxHash, r.ContractAddress, logs, r.GasUsed})
 }
 
 // DecodeRLP implements rlp.Decoder, and loads both consensus and implementation
@@ -152,6 +165,7 @@ func (r *ReceiptForStorage) EncodeRLP(w io.Writer) error {
 func (r *ReceiptForStorage) DecodeRLP(s *rlp.Stream) error {
 	var receipt struct {
 		PostState         []byte
+		Failed            bool
 		CumulativeGasUsed *big.Int
 		Bloom             Bloom
 		TxHash            common.Hash
@@ -163,7 +177,7 @@ func (r *ReceiptForStorage) DecodeRLP(s *rlp.Stream) error {
 		return err
 	}
 	// Assign the consensus fields
-	r.PostState, r.CumulativeGasUsed, r.Bloom = receipt.PostState, receipt.CumulativeGasUsed, receipt.Bloom
+	r.PostState, r.Failed, r.CumulativeGasUsed, r.Bloom = receipt.PostState, receipt.Failed, receipt.CumulativeGasUsed, receipt.Bloom
 	r.Logs = make([]*Log, len(receipt.Logs))
 	for i, log := range receipt.Logs {
 		r.Logs[i] = (*Log)(log)
diff --git a/core/vm/evm.go b/core/vm/evm.go
index 8d654c666..34933a1dc 100644
--- a/core/vm/evm.go
+++ b/core/vm/evm.go
@@ -158,7 +158,7 @@ func (evm *EVM) Call(caller ContractRef, addr common.Address, input []byte, gas
 	evm.Transfer(evm.StateDB, caller.Address(), to.Address(), value)
 
 	// initialise a new contract and set the code that is to be used by the
-	// E The contract is a scoped evmironment for this execution context
+	// E The contract is a scoped environment for this execution context
 	// only.
 	contract := NewContract(caller, to, value, gas)
 	contract.SetCallCode(&addr, evm.StateDB.GetCodeHash(addr), evm.StateDB.GetCode(addr))
@@ -351,6 +351,10 @@ func (evm *EVM) Create(caller ContractRef, code []byte, gas uint64, value *big.I
 			contract.UseGas(contract.Gas)
 		}
 	}
+	// Assign err if contract code size exceeds the max while the err is still empty.
+	if maxCodeSizeExceeded && err == nil {
+		err = errMaxCodeSizeExceeded
+	}
 	return ret, contractAddr, contract.Gas, err
 }
 
diff --git a/core/vm/instructions.go b/core/vm/instructions.go
index ece4d2229..b6d6e22c4 100644
--- a/core/vm/instructions.go
+++ b/core/vm/instructions.go
@@ -33,6 +33,7 @@ var (
 	errWriteProtection       = errors.New("evm: write protection")
 	errReturnDataOutOfBounds = errors.New("evm: return data out of bounds")
 	errExecutionReverted     = errors.New("evm: execution reverted")
+	errMaxCodeSizeExceeded   = errors.New("evm: max code size exceeded")
 )
 
 func opAdd(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
@@ -619,7 +620,6 @@ func opCall(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Sta
 	if value.Sign() != 0 {
 		gas += params.CallStipend
 	}
-
 	ret, returnGas, err := evm.Call(contract, address, args, gas, value)
 	if err != nil {
 		stack.push(new(big.Int))
diff --git a/eth/api.go b/eth/api.go
index f5214fc37..9904c6f53 100644
--- a/eth/api.go
+++ b/eth/api.go
@@ -523,7 +523,8 @@ func (api *PrivateDebugAPI) TraceTransaction(ctx context.Context, txHash common.
 
 	// Run the transaction with tracing enabled.
 	vmenv := vm.NewEVM(context, statedb, api.config, vm.Config{Debug: true, Tracer: tracer})
-	ret, gas, err := core.ApplyMessage(vmenv, msg, new(core.GasPool).AddGas(tx.Gas()))
+	// TODO utilize failed flag
+	ret, gas, _, err := core.ApplyMessage(vmenv, msg, new(core.GasPool).AddGas(tx.Gas()))
 	if err != nil {
 		return nil, fmt.Errorf("tracing failed: %v", err)
 	}
@@ -570,7 +571,7 @@ func (api *PrivateDebugAPI) computeTxEnv(blockHash common.Hash, txIndex int) (co
 
 		vmenv := vm.NewEVM(context, statedb, api.config, vm.Config{})
 		gp := new(core.GasPool).AddGas(tx.Gas())
-		_, _, err := core.ApplyMessage(vmenv, msg, gp)
+		_, _, _, err := core.ApplyMessage(vmenv, msg, gp)
 		if err != nil {
 			return nil, vm.Context{}, nil, fmt.Errorf("tx %x failed: %v", tx.Hash(), err)
 		}
diff --git a/eth/backend_test.go b/eth/backend_test.go
index 4351b24cf..1fd25e95a 100644
--- a/eth/backend_test.go
+++ b/eth/backend_test.go
@@ -35,11 +35,11 @@ func TestMipmapUpgrade(t *testing.T) {
 	chain, receipts := core.GenerateChain(params.TestChainConfig, genesis, db, 10, func(i int, gen *core.BlockGen) {
 		switch i {
 		case 1:
-			receipt := types.NewReceipt(nil, new(big.Int))
+			receipt := types.NewReceipt(nil, false, new(big.Int))
 			receipt.Logs = []*types.Log{{Address: addr}}
 			gen.AddUncheckedReceipt(receipt)
 		case 2:
-			receipt := types.NewReceipt(nil, new(big.Int))
+			receipt := types.NewReceipt(nil, false, new(big.Int))
 			receipt.Logs = []*types.Log{{Address: addr}}
 			gen.AddUncheckedReceipt(receipt)
 		}
diff --git a/eth/filters/filter_test.go b/eth/filters/filter_test.go
index 3244c04d7..cf508a218 100644
--- a/eth/filters/filter_test.go
+++ b/eth/filters/filter_test.go
@@ -33,7 +33,7 @@ import (
 )
 
 func makeReceipt(addr common.Address) *types.Receipt {
-	receipt := types.NewReceipt(nil, new(big.Int))
+	receipt := types.NewReceipt(nil, false, new(big.Int))
 	receipt.Logs = []*types.Log{
 		{Address: addr},
 	}
@@ -145,7 +145,7 @@ func TestFilters(t *testing.T) {
 		var receipts types.Receipts
 		switch i {
 		case 1:
-			receipt := types.NewReceipt(nil, new(big.Int))
+			receipt := types.NewReceipt(nil, false, new(big.Int))
 			receipt.Logs = []*types.Log{
 				{
 					Address: addr,
@@ -155,7 +155,7 @@ func TestFilters(t *testing.T) {
 			gen.AddUncheckedReceipt(receipt)
 			receipts = types.Receipts{receipt}
 		case 2:
-			receipt := types.NewReceipt(nil, new(big.Int))
+			receipt := types.NewReceipt(nil, false, new(big.Int))
 			receipt.Logs = []*types.Log{
 				{
 					Address: addr,
@@ -165,7 +165,7 @@ func TestFilters(t *testing.T) {
 			gen.AddUncheckedReceipt(receipt)
 			receipts = types.Receipts{receipt}
 		case 998:
-			receipt := types.NewReceipt(nil, new(big.Int))
+			receipt := types.NewReceipt(nil, false, new(big.Int))
 			receipt.Logs = []*types.Log{
 				{
 					Address: addr,
@@ -175,7 +175,7 @@ func TestFilters(t *testing.T) {
 			gen.AddUncheckedReceipt(receipt)
 			receipts = types.Receipts{receipt}
 		case 999:
-			receipt := types.NewReceipt(nil, new(big.Int))
+			receipt := types.NewReceipt(nil, false, new(big.Int))
 			receipt.Logs = []*types.Log{
 				{
 					Address: addr,
diff --git a/internal/ethapi/api.go b/internal/ethapi/api.go
index c3924b93a..30710aaab 100644
--- a/internal/ethapi/api.go
+++ b/internal/ethapi/api.go
@@ -635,7 +635,8 @@ func (s *PublicBlockChainAPI) doCall(ctx context.Context, args CallArgs, blockNr
 	// Setup the gas pool (also for unmetered requests)
 	// and apply the message.
 	gp := new(core.GasPool).AddGas(math.MaxBig256)
-	res, gas, err := core.ApplyMessage(evm, msg, gp)
+	// TODO utilize failed flag to help gas estimation
+	res, gas, _, err := core.ApplyMessage(evm, msg, gp)
 	if err := vmError(); err != nil {
 		return nil, common.Big0, err
 	}
diff --git a/les/odr_test.go b/les/odr_test.go
index 3a0fd6738..f56c4036d 100644
--- a/les/odr_test.go
+++ b/les/odr_test.go
@@ -127,7 +127,7 @@ func odrContractCall(ctx context.Context, db ethdb.Database, config *params.Chai
 
 				//vmenv := core.NewEnv(statedb, config, bc, msg, header, vm.Config{})
 				gp := new(core.GasPool).AddGas(math.MaxBig256)
-				ret, _, _ := core.ApplyMessage(vmenv, msg, gp)
+				ret, _, _, _ := core.ApplyMessage(vmenv, msg, gp)
 				res = append(res, ret...)
 			}
 		} else {
@@ -138,7 +138,7 @@ func odrContractCall(ctx context.Context, db ethdb.Database, config *params.Chai
 			context := core.NewEVMContext(msg, header, lc, nil)
 			vmenv := vm.NewEVM(context, state, config, vm.Config{})
 			gp := new(core.GasPool).AddGas(math.MaxBig256)
-			ret, _, _ := core.ApplyMessage(vmenv, msg, gp)
+			ret, _, _, _ := core.ApplyMessage(vmenv, msg, gp)
 			if state.Error() == nil {
 				res = append(res, ret...)
 			}
diff --git a/light/odr_test.go b/light/odr_test.go
index bd1e976e8..c0c5438fd 100644
--- a/light/odr_test.go
+++ b/light/odr_test.go
@@ -180,7 +180,7 @@ func odrContractCall(ctx context.Context, db ethdb.Database, bc *core.BlockChain
 		context := core.NewEVMContext(msg, header, chain, nil)
 		vmenv := vm.NewEVM(context, st, config, vm.Config{})
 		gp := new(core.GasPool).AddGas(math.MaxBig256)
-		ret, _, _ := core.ApplyMessage(vmenv, msg, gp)
+		ret, _, _, _ := core.ApplyMessage(vmenv, msg, gp)
 		res = append(res, ret...)
 		if st.Error() != nil {
 			return res, st.Error()
diff --git a/tests/state_test_util.go b/tests/state_test_util.go
index 2bf940bab..0eb85ab28 100644
--- a/tests/state_test_util.go
+++ b/tests/state_test_util.go
@@ -156,7 +156,7 @@ func (t *StateTest) Run(subtest StateSubtest, vmconfig vm.Config) error {
 	gaspool := new(core.GasPool)
 	gaspool.AddGas(block.GasLimit())
 	snapshot := statedb.Snapshot()
-	if _, _, err := core.ApplyMessage(evm, msg, gaspool); err != nil {
+	if _, _, _, err := core.ApplyMessage(evm, msg, gaspool); err != nil {
 		statedb.RevertToSnapshot(snapshot)
 	}
 	if post.Logs != nil {
-- 
cgit