diff options
-rw-r--r-- | core/database_util_test.go | 4 | ||||
-rw-r--r-- | core/state/journal.go | 5 | ||||
-rw-r--r-- | core/state/statedb.go | 69 | ||||
-rw-r--r-- | core/state_processor.go | 2 | ||||
-rw-r--r-- | core/types/receipt.go | 141 | ||||
-rw-r--r-- | core/types/transaction.go | 2 | ||||
-rw-r--r-- | rlp/decode.go | 2 | ||||
-rw-r--r-- | rlp/decode_test.go | 35 | ||||
-rw-r--r-- | tests/block_test.go | 1 |
9 files changed, 117 insertions, 144 deletions
diff --git a/core/database_util_test.go b/core/database_util_test.go index 5c75d53d0..e91f1b593 100644 --- a/core/database_util_test.go +++ b/core/database_util_test.go @@ -340,7 +340,7 @@ func TestBlockReceiptStorage(t *testing.T) { db, _ := ethdb.NewMemDatabase() receipt1 := &types.Receipt{ - PostState: []byte{0x01}, + Failed: true, CumulativeGasUsed: big.NewInt(1), Logs: []*types.Log{ {Address: common.BytesToAddress([]byte{0x11})}, @@ -351,7 +351,7 @@ func TestBlockReceiptStorage(t *testing.T) { GasUsed: big.NewInt(111111), } receipt2 := &types.Receipt{ - PostState: []byte{0x02}, + PostState: common.Hash{2}.Bytes(), CumulativeGasUsed: big.NewInt(2), Logs: []*types.Log{ {Address: common.BytesToAddress([]byte{0x22})}, diff --git a/core/state/journal.go b/core/state/journal.go index b5c8ca9a2..ddb76f1a2 100644 --- a/core/state/journal.go +++ b/core/state/journal.go @@ -91,11 +91,6 @@ func (ch suicideChange) undo(s *StateDB) { if obj != nil { obj.suicided = ch.prev obj.setBalance(ch.prevbalance) - // if the object wasn't suicided before, remove - // it from the list of destructed objects as well. - if !obj.suicided { - delete(s.stateObjectsDestructed, *ch.account) - } } } diff --git a/core/state/statedb.go b/core/state/statedb.go index 694374f82..002fa6249 100644 --- a/core/state/statedb.go +++ b/core/state/statedb.go @@ -46,9 +46,8 @@ type StateDB struct { trie Trie // This map holds 'live' objects, which will get modified while processing a state transition. - stateObjects map[common.Address]*stateObject - stateObjectsDirty map[common.Address]struct{} - stateObjectsDestructed map[common.Address]struct{} + stateObjects map[common.Address]*stateObject + stateObjectsDirty map[common.Address]struct{} // DB error. // State objects are used by the consensus core and VM which are @@ -83,14 +82,13 @@ func New(root common.Hash, db Database) (*StateDB, error) { return nil, err } return &StateDB{ - db: db, - trie: tr, - stateObjects: make(map[common.Address]*stateObject), - stateObjectsDirty: make(map[common.Address]struct{}), - stateObjectsDestructed: make(map[common.Address]struct{}), - refund: new(big.Int), - logs: make(map[common.Hash][]*types.Log), - preimages: make(map[common.Hash][]byte), + db: db, + trie: tr, + stateObjects: make(map[common.Address]*stateObject), + stateObjectsDirty: make(map[common.Address]struct{}), + refund: new(big.Int), + logs: make(map[common.Hash][]*types.Log), + preimages: make(map[common.Hash][]byte), }, nil } @@ -115,7 +113,6 @@ func (self *StateDB) Reset(root common.Hash) error { self.trie = tr self.stateObjects = make(map[common.Address]*stateObject) self.stateObjectsDirty = make(map[common.Address]struct{}) - self.stateObjectsDestructed = make(map[common.Address]struct{}) self.thash = common.Hash{} self.bhash = common.Hash{} self.txIndex = 0 @@ -323,7 +320,6 @@ func (self *StateDB) Suicide(addr common.Address) bool { }) stateObject.markSuicided() stateObject.data.Balance = new(big.Int) - self.stateObjectsDestructed[addr] = struct{}{} return true } @@ -456,23 +452,19 @@ func (self *StateDB) Copy() *StateDB { // Copy all the basic fields, initialize the memory ones state := &StateDB{ - db: self.db, - trie: self.trie, - stateObjects: make(map[common.Address]*stateObject, len(self.stateObjectsDirty)), - stateObjectsDirty: make(map[common.Address]struct{}, len(self.stateObjectsDirty)), - stateObjectsDestructed: make(map[common.Address]struct{}, len(self.stateObjectsDestructed)), - refund: new(big.Int).Set(self.refund), - logs: make(map[common.Hash][]*types.Log, len(self.logs)), - logSize: self.logSize, - preimages: make(map[common.Hash][]byte), + db: self.db, + trie: self.trie, + stateObjects: make(map[common.Address]*stateObject, len(self.stateObjectsDirty)), + stateObjectsDirty: make(map[common.Address]struct{}, len(self.stateObjectsDirty)), + refund: new(big.Int).Set(self.refund), + logs: make(map[common.Hash][]*types.Log, len(self.logs)), + logSize: self.logSize, + preimages: make(map[common.Hash][]byte), } // Copy the dirty states, logs, and preimages for addr := range self.stateObjectsDirty { state.stateObjects[addr] = self.stateObjects[addr].deepCopy(state, state.MarkStateObjectDirty) state.stateObjectsDirty[addr] = struct{}{} - if self.stateObjects[addr].suicided { - state.stateObjectsDestructed[addr] = struct{}{} - } } for hash, logs := range self.logs { state.logs[hash] = make([]*types.Log, len(logs)) @@ -520,10 +512,9 @@ func (self *StateDB) GetRefund() *big.Int { return self.refund } -// IntermediateRoot computes the current root hash of the state trie. -// It is called in between transactions to get the root hash that -// goes into transaction receipts. -func (s *StateDB) IntermediateRoot(deleteEmptyObjects bool) common.Hash { +// Finalise finalises the state by removing the self destructed objects +// and clears the journal as well as the refunds. +func (s *StateDB) Finalise(deleteEmptyObjects bool) { for addr := range s.stateObjectsDirty { stateObject := s.stateObjects[addr] if stateObject.suicided || (deleteEmptyObjects && stateObject.empty()) { @@ -535,6 +526,13 @@ func (s *StateDB) IntermediateRoot(deleteEmptyObjects bool) common.Hash { } // Invalidate journal because reverting across transactions is not allowed. s.clearJournalAndRefund() +} + +// IntermediateRoot computes the current root hash of the state trie. +// It is called in between transactions to get the root hash that +// goes into transaction receipts. +func (s *StateDB) IntermediateRoot(deleteEmptyObjects bool) common.Hash { + s.Finalise(deleteEmptyObjects) return s.trie.Hash() } @@ -546,19 +544,6 @@ func (self *StateDB) Prepare(thash, bhash common.Hash, ti int) { self.txIndex = ti } -// Finalise finalises the state by removing the self destructed objects -// in the current stateObjectsDestructed buffer and clears the journal -// as well as the refunds. -// -// Please note that Finalise is used by EIP#98 and is used instead of -// IntermediateRoot. -func (s *StateDB) Finalise() { - for addr := range s.stateObjectsDestructed { - s.deleteStateObject(s.stateObjects[addr]) - } - s.clearJournalAndRefund() -} - // DeleteSuicides flags the suicided objects for deletion so that it // won't be referenced again when called / queried up on. // diff --git a/core/state_processor.go b/core/state_processor.go index a4b554b10..4115eab8c 100644 --- a/core/state_processor.go +++ b/core/state_processor.go @@ -106,7 +106,7 @@ func ApplyTransaction(config *params.ChainConfig, bc *BlockChain, author *common // Update the state with pending changes var root []byte if config.IsMetropolis(header.Number) { - statedb.Finalise() + statedb.Finalise(true) } else { root = statedb.IntermediateRoot(config.IsEIP158(header.Number)).Bytes() } diff --git a/core/types/receipt.go b/core/types/receipt.go index b54bd7b4f..4f3b7357e 100644 --- a/core/types/receipt.go +++ b/core/types/receipt.go @@ -17,6 +17,7 @@ package types import ( + "bytes" "fmt" "io" "math/big" @@ -28,9 +29,9 @@ import ( //go:generate gencodec -type Receipt -field-override receiptMarshaling -out gen_receipt_json.go -const ( - receiptStatusSuccessful = byte(0x01) - receiptStatusFailed = byte(0x00) +var ( + receiptStatusFailed = []byte{} + receiptStatusSuccessful = []byte{0x01} ) // Receipt represents the results of a transaction. @@ -54,22 +55,22 @@ type receiptMarshaling struct { GasUsed *hexutil.Big } -// homesteadReceiptRLP contains the receipt's Homestead consensus fields, used -// during RLP serialization. -type homesteadReceiptRLP struct { - PostState []byte +// receiptRLP is the consensus encoding of a receipt. +type receiptRLP struct { + PostStateOrStatus []byte CumulativeGasUsed *big.Int Bloom Bloom Logs []*Log } -// metropolisReceiptRLP contains the receipt's Metropolis consensus fields, used -// during RLP serialization. -type metropolisReceiptRLP struct { - Status byte +type receiptStorageRLP struct { + PostStateOrStatus []byte CumulativeGasUsed *big.Int Bloom Bloom - Logs []*Log + TxHash common.Hash + ContractAddress common.Address + Logs []*LogForStorage + GasUsed *big.Int } // NewReceipt creates a barebone transaction receipt, copying the init fields. @@ -80,69 +81,46 @@ func NewReceipt(root []byte, failed bool, cumulativeGasUsed *big.Int) *Receipt { // 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 { - 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}) + return rlp.Encode(w, &receiptRLP{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 { - // Load the raw bytes since we have multiple possible formats - raw, err := s.Raw() - if err != nil { + var dec receiptRLP + if err := s.Decode(&dec); err != nil { return err } - content, _, err := rlp.SplitList(raw) - if err != nil { + if err := r.setStatus(dec.PostStateOrStatus); err != nil { return err } - kind, cnt, _, err := rlp.Split(content) - if err != nil { - return err - } - // 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 (0x01), or the empty - // string (0x80, decoded as a byte with 0x00 value). - var metro metropolisReceiptRLP - if err := rlp.DecodeBytes(raw, &metro); err != nil { - return err - } - switch metro.Status { - case receiptStatusSuccessful: - r.Failed = false - case receiptStatusFailed: - r.Failed = true - default: - return fmt.Errorf("invalid status byte: 0x%x", metro.Status) - } - r.CumulativeGasUsed = metro.CumulativeGasUsed - r.Bloom = metro.Bloom - r.Logs = metro.Logs - return nil - - 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 - } - r.PostState = home.PostState[:] - r.CumulativeGasUsed = home.CumulativeGasUsed - r.Bloom = home.Bloom - r.Logs = home.Logs - return nil + r.CumulativeGasUsed, r.Bloom, r.Logs = dec.CumulativeGasUsed, dec.Bloom, dec.Logs + return nil +} +func (r *Receipt) setStatus(postStateOrStatus []byte) error { + switch { + case bytes.Equal(postStateOrStatus, receiptStatusSuccessful): + r.Failed = false + case bytes.Equal(postStateOrStatus, receiptStatusFailed): + r.Failed = true + case len(postStateOrStatus) == len(common.Hash{}): + r.PostState = postStateOrStatus default: - return fmt.Errorf("invalid first receipt component: %v", kind) + return fmt.Errorf("invalid receipt status %x", postStateOrStatus) } + return nil +} + +func (r *Receipt) statusEncoding() []byte { + if len(r.PostState) == 0 { + if r.Failed { + return receiptStatusFailed + } else { + return receiptStatusSuccessful + } + } + return r.PostState } // String implements the Stringer interface. @@ -160,38 +138,39 @@ type ReceiptForStorage Receipt // EncodeRLP implements rlp.Encoder, and flattens all content fields of a receipt // into an RLP stream. func (r *ReceiptForStorage) EncodeRLP(w io.Writer) error { - logs := make([]*LogForStorage, len(r.Logs)) + enc := &receiptStorageRLP{ + PostStateOrStatus: (*Receipt)(r).statusEncoding(), + CumulativeGasUsed: r.CumulativeGasUsed, + Bloom: r.Bloom, + TxHash: r.TxHash, + ContractAddress: r.ContractAddress, + Logs: make([]*LogForStorage, len(r.Logs)), + GasUsed: r.GasUsed, + } for i, log := range r.Logs { - logs[i] = (*LogForStorage)(log) + enc.Logs[i] = (*LogForStorage)(log) } - return rlp.Encode(w, []interface{}{r.PostState, r.Failed, r.CumulativeGasUsed, r.Bloom, r.TxHash, r.ContractAddress, logs, r.GasUsed}) + return rlp.Encode(w, enc) } // 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 receipt struct { - PostState []byte - Failed bool - CumulativeGasUsed *big.Int - Bloom Bloom - TxHash common.Hash - ContractAddress common.Address - Logs []*LogForStorage - GasUsed *big.Int + var dec receiptStorageRLP + if err := s.Decode(&dec); err != nil { + return err } - if err := s.Decode(&receipt); err != nil { + if err := (*Receipt)(r).setStatus(dec.PostStateOrStatus); err != nil { return err } // Assign the consensus fields - 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.CumulativeGasUsed, r.Bloom = dec.CumulativeGasUsed, dec.Bloom + r.Logs = make([]*Log, len(dec.Logs)) + for i, log := range dec.Logs { r.Logs[i] = (*Log)(log) } // Assign the implementation fields - r.TxHash, r.ContractAddress, r.GasUsed = receipt.TxHash, receipt.ContractAddress, receipt.GasUsed - + r.TxHash, r.ContractAddress, r.GasUsed = dec.TxHash, dec.ContractAddress, dec.GasUsed return nil } diff --git a/core/types/transaction.go b/core/types/transaction.go index 8e108b2a3..947fc85d6 100644 --- a/core/types/transaction.go +++ b/core/types/transaction.go @@ -300,7 +300,7 @@ func (tx *Transaction) String() string { Hex: %x `, tx.Hash(), - len(tx.data.Recipient) == 0, + tx.data.Recipient == nil, from, to, tx.data.AccountNonce, diff --git a/rlp/decode.go b/rlp/decode.go index 78ccf6275..60d9dab2b 100644 --- a/rlp/decode.go +++ b/rlp/decode.go @@ -693,7 +693,7 @@ func (s *Stream) Raw() ([]byte, error) { return nil, err } if kind == String { - puthead(buf, 0x80, 0xB8, size) + puthead(buf, 0x80, 0xB7, size) } else { puthead(buf, 0xC0, 0xF7, size) } diff --git a/rlp/decode_test.go b/rlp/decode_test.go index d762e195d..4d8abd001 100644 --- a/rlp/decode_test.go +++ b/rlp/decode_test.go @@ -256,16 +256,31 @@ func TestStreamList(t *testing.T) { } func TestStreamRaw(t *testing.T) { - s := NewStream(bytes.NewReader(unhex("C58401010101")), 0) - s.List() - - want := unhex("8401010101") - raw, err := s.Raw() - if err != nil { - t.Fatal(err) - } - if !bytes.Equal(want, raw) { - t.Errorf("raw mismatch: got %x, want %x", raw, want) + tests := []struct { + input string + output string + }{ + { + "C58401010101", + "8401010101", + }, + { + "F842B84001010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101", + "B84001010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101", + }, + } + for i, tt := range tests { + s := NewStream(bytes.NewReader(unhex(tt.input)), 0) + s.List() + + want := unhex(tt.output) + raw, err := s.Raw() + if err != nil { + t.Fatal(err) + } + if !bytes.Equal(want, raw) { + t.Errorf("test %d: raw mismatch: got %x, want %x", i, raw, want) + } } } diff --git a/tests/block_test.go b/tests/block_test.go index 066f28034..56e1e1e8d 100644 --- a/tests/block_test.go +++ b/tests/block_test.go @@ -37,7 +37,6 @@ func TestBlockchain(t *testing.T) { // Still failing tests bt.skipLoad(`^bcWalletTest.*_Byzantium$`) - bt.skipLoad(`^bcStateTests/suicideCoinbase.json.*_Byzantium$`) bt.walk(t, blockTestDir, func(t *testing.T, name string, test *BlockTest) { if err := bt.checkFailure(t, name, test.Run()); err != nil { |