Skip to content
This repository has been archived by the owner on Oct 4, 2019. It is now read-only.

Commit

Permalink
Receipt fixes and state trie clearing detail (#62)
Browse files Browse the repository at this point in the history
* receipt and state trie clearing changes

* Fixed receipt storage and added backward compatible decoding
  • Loading branch information
austinabell authored and soc1c committed Jun 17, 2019
1 parent 3512b59 commit 8c43b48
Show file tree
Hide file tree
Showing 7 changed files with 135 additions and 51 deletions.
2 changes: 1 addition & 1 deletion core/block_validator.go
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,7 @@ func (v *BlockValidator) ValidateState(block, parent *types.Block, statedb *stat
}
// Validate the state root against the received state root and throw
// an error if they don't match.
if root := statedb.IntermediateRoot(false); header.Root != root {
if root := statedb.IntermediateRoot(v.config.IsAtlantis(header.Number)); header.Root != root {
return fmt.Errorf("invalid merkle root: header=%x computed=%x", header.Root, root)
}
return nil
Expand Down
2 changes: 1 addition & 1 deletion core/chain_manager.go
Original file line number Diff line number Diff line change
Expand Up @@ -263,7 +263,7 @@ func makeHeader(config *ChainConfig, parent *types.Block, state *state.StateDB)
time = new(big.Int).Add(parent.Time(), big.NewInt(10)) // block time is fixed at 10 seconds
}
return &types.Header{
Root: state.IntermediateRoot(false),
Root: state.IntermediateRoot(config.IsAtlantis(parent.Number())),
ParentHash: parent.Hash(),
Coinbase: parent.Coinbase(),
Difficulty: CalcDifficulty(config, time.Uint64(), parent.Header()),
Expand Down
11 changes: 6 additions & 5 deletions core/database_util_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,14 +28,15 @@ import (

"crypto/ecdsa"
"encoding/binary"
"strings"

"github.com/eth-classic/go-ethereum/common"
"github.com/eth-classic/go-ethereum/core/types"
"github.com/eth-classic/go-ethereum/core/vm"
"github.com/eth-classic/go-ethereum/crypto"
"github.com/eth-classic/go-ethereum/crypto/sha3"
"github.com/eth-classic/go-ethereum/ethdb"
"github.com/eth-classic/go-ethereum/rlp"
"strings"
)

type diffTest struct {
Expand Down Expand Up @@ -647,7 +648,7 @@ func TestReceiptStorage(t *testing.T) {
db, _ := ethdb.NewMemDatabase()

receipt1 := &types.Receipt{
PostState: []byte{0x01},
Status: types.TxFailure,
CumulativeGasUsed: big.NewInt(1),
Logs: vm.Logs{
&vm.Log{Address: common.BytesToAddress([]byte{0x11})},
Expand All @@ -658,7 +659,7 @@ func TestReceiptStorage(t *testing.T) {
GasUsed: big.NewInt(111111),
}
receipt2 := &types.Receipt{
PostState: []byte{0x02},
PostState: []byte{},
CumulativeGasUsed: big.NewInt(2),
Logs: vm.Logs{
&vm.Log{Address: common.BytesToAddress([]byte{0x22})},
Expand Down Expand Up @@ -706,7 +707,7 @@ func TestBlockReceiptStorage(t *testing.T) {
db, _ := ethdb.NewMemDatabase()

receipt1 := &types.Receipt{
PostState: []byte{0x01},
Status: types.TxFailure,
CumulativeGasUsed: big.NewInt(1),
Logs: vm.Logs{
&vm.Log{Address: common.BytesToAddress([]byte{0x11})},
Expand All @@ -717,7 +718,7 @@ func TestBlockReceiptStorage(t *testing.T) {
GasUsed: big.NewInt(111111),
}
receipt2 := &types.Receipt{
PostState: []byte{0x02},
PostState: []byte{},
CumulativeGasUsed: big.NewInt(2),
Logs: vm.Logs{
&vm.Log{Address: common.BytesToAddress([]byte{0x22})},
Expand Down
2 changes: 1 addition & 1 deletion core/multivm_processor.go
Original file line number Diff line number Diff line change
Expand Up @@ -179,7 +179,7 @@ Loop:
usedGas := vm.UsedGas()
totalUsedGas.Add(totalUsedGas, usedGas)

receipt := types.NewReceipt(statedb.IntermediateRoot(false).Bytes(), totalUsedGas)
receipt := types.NewReceipt(statedb.IntermediateRoot(config.IsAtlantis(header.Number)).Bytes(), totalUsedGas)
receipt.TxHash = tx.Hash()
receipt.GasUsed = new(big.Int).Set(totalUsedGas)
if vm.Failed() {
Expand Down
9 changes: 8 additions & 1 deletion core/state_processor.go
Original file line number Diff line number Diff line change
Expand Up @@ -119,8 +119,15 @@ func ApplyTransaction(config *ChainConfig, bc *BlockChain, gp *GasPool, statedb
}

// Update the state with pending changes
var root []byte
if config.IsAtlantis(header.Number) {
statedb.Finalise(true)
} else {
root = statedb.IntermediateRoot(config.IsAtlantis(header.Number)).Bytes()
}

usedGas.Add(usedGas, gas)
receipt := types.NewReceipt(statedb.IntermediateRoot(false).Bytes(), usedGas)
receipt := types.NewReceipt(root, usedGas)
receipt.TxHash = tx.Hash()
receipt.GasUsed = new(big.Int).Set(gas)
if MessageCreatesContract(tx) {
Expand Down
158 changes: 117 additions & 41 deletions core/types/receipt.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
package types

import (
"bytes"
"fmt"
"io"
"math/big"
Expand All @@ -26,6 +27,11 @@ import (
"github.com/eth-classic/go-ethereum/rlp"
)

var (
receiptStatusFailedRLP = []byte{}
receiptStatusSuccessfulRLP = []byte{0x01}
)

type ReceiptStatus byte

const (
Expand All @@ -49,6 +55,29 @@ type Receipt struct {
Status ReceiptStatus
}

// storedReceiptRLP is the storage encoding of a receipt.
type storedReceiptRLP struct {
PostStateOrStatus []byte
CumulativeGasUsed *big.Int
Bloom Bloom
TxHash common.Hash
ContractAddress common.Address
Logs []*vm.LogForStorage
GasUsed *big.Int
}

// storedReceiptRLP is the storage encoding of a receipt.
type oldStoredReceiptRLP struct {
PostStateOrStatus []byte
CumulativeGasUsed *big.Int
Bloom Bloom
TxHash common.Hash
ContractAddress common.Address
Logs []*vm.LogForStorage
GasUsed *big.Int
Status ReceiptStatus
}

// 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), Status: TxStatusUnknown}
Expand All @@ -57,22 +86,51 @@ func NewReceipt(root []byte, cumulativeGasUsed *big.Int) *Receipt {
// EncodeRLP implements rlp.Encoder, and flattens the consensus fields of a receipt
// into an RLP stream.
func (r *Receipt) EncodeRLP(w io.Writer) error {
return rlp.Encode(w, []interface{}{r.PostState, r.CumulativeGasUsed, r.Bloom, r.Logs})
return rlp.Encode(w, []interface{}{r.statusEncoding(), r.CumulativeGasUsed, r.Bloom, r.Logs})
}

// DecodeRLP implements rlp.Decoder, and loads the consensus fields of a receipt
// from an RLP stream.
func (r *Receipt) DecodeRLP(s *rlp.Stream) error {
var receipt struct {
PostState []byte
PostStateOrStatus []byte
CumulativeGasUsed *big.Int
Bloom Bloom
Logs vm.Logs
}
if err := s.Decode(&receipt); err != nil {
return err
}
r.PostState, r.CumulativeGasUsed, r.Bloom, r.Logs = receipt.PostState, receipt.CumulativeGasUsed, receipt.Bloom, receipt.Logs

if err := r.setStatus(receipt.PostStateOrStatus); err != nil {
return err
}

r.CumulativeGasUsed, r.Bloom, r.Logs = receipt.CumulativeGasUsed, receipt.Bloom, receipt.Logs
return nil
}

func (r *Receipt) statusEncoding() []byte {
if len(r.PostState) == 0 {
if r.Status == TxFailure {
return receiptStatusFailedRLP
}
return receiptStatusSuccessfulRLP
}
return r.PostState
}

func (r *Receipt) setStatus(postStateOrStatus []byte) error {
switch {
case bytes.Equal(postStateOrStatus, receiptStatusSuccessfulRLP):
r.Status = TxSuccess
case bytes.Equal(postStateOrStatus, receiptStatusFailedRLP):
r.Status = TxFailure
case len(postStateOrStatus) == len(common.Hash{}):
r.PostState = postStateOrStatus
default:
return fmt.Errorf("invalid receipt status %x", postStateOrStatus)
}
return nil
}

Expand Down Expand Up @@ -101,60 +159,78 @@ func (r *ReceiptForStorage) EncodeRLP(w io.Writer) error {
for i, log := range r.Logs {
logs[i] = (*vm.LogForStorage)(log)
}
return rlp.Encode(w, []interface{}{r.PostState, r.CumulativeGasUsed, r.Bloom, r.TxHash, r.ContractAddress, logs, r.GasUsed, r.Status})
receiptToStore := &storedReceiptRLP{
PostStateOrStatus: (*Receipt)(r).statusEncoding(),
CumulativeGasUsed: r.CumulativeGasUsed,
Logs: logs,
Bloom: r.Bloom,
TxHash: r.TxHash,
ContractAddress: r.ContractAddress,
GasUsed: r.GasUsed,
}
return rlp.Encode(w, receiptToStore)
}

// DecodeRLP implements rlp.Decoder, and loads both consensus and implementation
// fields of a receipt from an RLP stream.
func (r *ReceiptForStorage) DecodeRLP(s *rlp.Stream) error {
var oldReceipt struct {
PostState []byte
CumulativeGasUsed *big.Int
Bloom Bloom
TxHash common.Hash
ContractAddress common.Address
Logs []*vm.LogForStorage
GasUsed *big.Int
raw, err := s.Raw()
if err != nil {
return err
}
var receipt struct {
PostState []byte
CumulativeGasUsed *big.Int
Bloom Bloom
TxHash common.Hash
ContractAddress common.Address
Logs []*vm.LogForStorage
GasUsed *big.Int
Status ReceiptStatus

// Try decoding the receipt without Status first
if err := decodeStoredReceiptRLP(r, raw); err == nil {
return nil
}
receipt.Status = TxStatusUnknown

raw, err := s.Raw()
if err != nil {
return decodeOldStoredReceiptRLP(r, raw)
}

// Decode stored receipt
func decodeStoredReceiptRLP(r *ReceiptForStorage, raw []byte) error {
var receipt storedReceiptRLP
if err := rlp.DecodeBytes(raw, &receipt); err != nil {
return err
}

r.CumulativeGasUsed = receipt.CumulativeGasUsed
r.Bloom = receipt.Bloom
r.TxHash = receipt.TxHash
r.ContractAddress = receipt.ContractAddress
r.GasUsed = receipt.GasUsed

r.Logs = make(vm.Logs, len(receipt.Logs))
for i, log := range receipt.Logs {
r.Logs[i] = (*vm.Log)(log)
}

if err := (*Receipt)(r).setStatus(receipt.PostStateOrStatus); err != nil {
return err
}

return nil
}

// Decode with status field included in storage
func decodeOldStoredReceiptRLP(r *ReceiptForStorage, raw []byte) error {
var receipt oldStoredReceiptRLP
if err := rlp.DecodeBytes(raw, &receipt); err != nil {
if err := rlp.DecodeBytes(raw, &oldReceipt); err != nil {
return err
}
receipt.PostState = oldReceipt.PostState
receipt.CumulativeGasUsed = oldReceipt.CumulativeGasUsed
receipt.Bloom = oldReceipt.Bloom
receipt.TxHash = oldReceipt.TxHash
receipt.ContractAddress = oldReceipt.ContractAddress
receipt.Logs = oldReceipt.Logs
receipt.GasUsed = oldReceipt.GasUsed
receipt.Status = TxStatusUnknown

}
// Assign the consensus fields
r.PostState, r.CumulativeGasUsed, r.Bloom = receipt.PostState, receipt.CumulativeGasUsed, receipt.Bloom
return err
}

r.PostState = receipt.PostStateOrStatus
r.Status = receipt.Status
r.CumulativeGasUsed = receipt.CumulativeGasUsed
r.Bloom = receipt.Bloom
r.TxHash = receipt.TxHash
r.ContractAddress = receipt.ContractAddress
r.GasUsed = receipt.GasUsed

r.Logs = make(vm.Logs, len(receipt.Logs))
for i, log := range receipt.Logs {
r.Logs[i] = (*vm.Log)(log)
}
// Assign the implementation fields
r.TxHash, r.ContractAddress, r.GasUsed, r.Status = receipt.TxHash, receipt.ContractAddress, receipt.GasUsed, receipt.Status

return nil
}
Expand Down
2 changes: 1 addition & 1 deletion miner/worker.go
Original file line number Diff line number Diff line change
Expand Up @@ -556,7 +556,7 @@ func (self *worker) commitNewWork() {
if atomic.LoadInt32(&self.mining) == 1 {
// commit state root after all state transitions.
core.AccumulateRewards(work.config, work.state, header, uncles)
header.Root = work.state.IntermediateRoot(false)
header.Root = work.state.IntermediateRoot(self.config.IsAtlantis(header.Number))
}

// create the new block whose nonce will be mined.
Expand Down

0 comments on commit 8c43b48

Please sign in to comment.