From 1b89bd5d269d2d85a7c72067e18212135d8757f9 Mon Sep 17 00:00:00 2001 From: Felix Lange Date: Fri, 11 Dec 2015 01:29:41 +0100 Subject: [PATCH] core/state, core/types use package rlp for state, receipt serialisation --- core/state/dump.go | 2 +- core/state/state_object.go | 93 ++++++++++++++++++-------------------- core/state/state_test.go | 6 +-- core/state/statedb.go | 22 +++++---- core/types/receipt.go | 15 +++--- 5 files changed, 67 insertions(+), 71 deletions(-) diff --git a/core/state/dump.go b/core/state/dump.go index 9acb8a024..cff9c50aa 100644 --- a/core/state/dump.go +++ b/core/state/dump.go @@ -45,7 +45,7 @@ func (self *StateDB) RawDump() World { it := self.trie.Iterator() for it.Next() { addr := self.trie.GetKey(it.Key) - stateObject := NewStateObjectFromBytes(common.BytesToAddress(addr), it.Value, self.db) + stateObject, _ := DecodeObject(common.BytesToAddress(addr), self.db, it.Value) account := Account{Balance: stateObject.balance.String(), Nonce: stateObject.nonce, Root: common.Bytes2Hex(stateObject.Root()), CodeHash: common.Bytes2Hex(stateObject.codeHash)} account.Storage = make(map[string]string) diff --git a/core/state/state_object.go b/core/state/state_object.go index c06e3d227..47546112f 100644 --- a/core/state/state_object.go +++ b/core/state/state_object.go @@ -19,17 +19,19 @@ package state import ( "bytes" "fmt" + "io" "math/big" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/crypto" - "github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/logger" "github.com/ethereum/go-ethereum/logger/glog" "github.com/ethereum/go-ethereum/rlp" "github.com/ethereum/go-ethereum/trie" ) +var emptyCodeHash = crypto.Sha3(nil) + type Code []byte func (self Code) String() string { @@ -56,8 +58,7 @@ func (self Storage) Copy() Storage { } type StateObject struct { - // State database for storing state changes - db ethdb.Database + db trie.Database // State database for storing state changes trie *trie.SecureTrie // Address belonging to this account @@ -83,39 +84,16 @@ type StateObject struct { dirty bool } -func NewStateObject(address common.Address, db ethdb.Database) *StateObject { - object := &StateObject{db: db, address: address, balance: new(big.Int), dirty: true} +func NewStateObject(address common.Address, db trie.Database) *StateObject { + object := &StateObject{ + db: db, + address: address, + balance: new(big.Int), + dirty: true, + codeHash: emptyCodeHash, + storage: make(Storage), + } object.trie, _ = trie.NewSecure(common.Hash{}, db) - object.storage = make(Storage) - return object -} - -func NewStateObjectFromBytes(address common.Address, data []byte, db ethdb.Database) *StateObject { - var extobject struct { - Nonce uint64 - Balance *big.Int - Root common.Hash - CodeHash []byte - } - err := rlp.Decode(bytes.NewReader(data), &extobject) - if err != nil { - glog.Errorf("can't decode state object %x: %v", address, err) - return nil - } - trie, err := trie.NewSecure(extobject.Root, db) - if err != nil { - // TODO: bubble this up or panic - glog.Errorf("can't create account trie with root %x: %v", extobject.Root[:], err) - return nil - } - - object := &StateObject{address: address, db: db} - object.nonce = extobject.Nonce - object.balance = extobject.Balance - object.codeHash = extobject.CodeHash - object.trie = trie - object.storage = make(map[string]common.Hash) - object.code, _ = db.Get(extobject.CodeHash) return object } @@ -172,7 +150,6 @@ func (self *StateObject) Update() { self.trie.Delete([]byte(key)) continue } - self.setAddr([]byte(key), value) } } @@ -248,6 +225,7 @@ func (self *StateObject) Code() []byte { func (self *StateObject) SetCode(code []byte) { self.code = code + self.codeHash = crypto.Sha3(code) self.dirty = true } @@ -276,23 +254,40 @@ func (self *StateObject) EachStorage(cb func(key, value []byte)) { } } -// // Encoding -// -// State object encoding methods -func (c *StateObject) RlpEncode() []byte { - return common.Encode([]interface{}{c.nonce, c.balance, c.Root(), c.CodeHash()}) +type extStateObject struct { + Nonce uint64 + Balance *big.Int + Root common.Hash + CodeHash []byte } -func (c *StateObject) CodeHash() common.Bytes { - return crypto.Sha3(c.code) +// EncodeRLP implements rlp.Encoder. +func (c *StateObject) EncodeRLP(w io.Writer) error { + return rlp.Encode(w, []interface{}{c.nonce, c.balance, c.Root(), c.codeHash}) } -// Storage change object. Used by the manifest for notifying changes to -// the sub channels. -type StorageState struct { - StateAddress []byte - Address []byte - Value *big.Int +// DecodeObject decodes an RLP-encoded state object. +func DecodeObject(address common.Address, db trie.Database, data []byte) (*StateObject, error) { + var ( + obj = &StateObject{address: address, db: db, storage: make(Storage)} + ext extStateObject + err error + ) + if err = rlp.DecodeBytes(data, &ext); err != nil { + return nil, err + } + if obj.trie, err = trie.NewSecure(ext.Root, db); err != nil { + return nil, err + } + if !bytes.Equal(ext.CodeHash, emptyCodeHash) { + if obj.code, err = db.Get(ext.CodeHash); err != nil { + return nil, fmt.Errorf("can't find code for hash %x: %v", ext.CodeHash, err) + } + } + obj.nonce = ext.Nonce + obj.balance = ext.Balance + obj.codeHash = ext.CodeHash + return obj, nil } diff --git a/core/state/state_test.go b/core/state/state_test.go index 7ddbe11a1..7ce341c36 100644 --- a/core/state/state_test.go +++ b/core/state/state_test.go @@ -138,8 +138,7 @@ func TestSnapshot2(t *testing.T) { so0 := state.GetStateObject(stateobjaddr0) so0.balance = big.NewInt(42) so0.nonce = 43 - so0.code = []byte{'c', 'a', 'f', 'e'} - so0.codeHash = so0.CodeHash() + so0.SetCode([]byte{'c', 'a', 'f', 'e'}) so0.remove = true so0.deleted = false so0.dirty = false @@ -149,8 +148,7 @@ func TestSnapshot2(t *testing.T) { so1 := state.GetStateObject(stateobjaddr1) so1.balance = big.NewInt(52) so1.nonce = 53 - so1.code = []byte{'c', 'a', 'f', 'e', '2'} - so1.codeHash = so1.CodeHash() + so1.SetCode([]byte{'c', 'a', 'f', 'e', '2'}) so1.remove = true so1.deleted = true so1.dirty = true diff --git a/core/state/statedb.go b/core/state/statedb.go index a9de71409..ab93870bf 100644 --- a/core/state/statedb.go +++ b/core/state/statedb.go @@ -18,6 +18,7 @@ package state import ( + "fmt" "math/big" "github.com/ethereum/go-ethereum/common" @@ -25,6 +26,7 @@ import ( "github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/logger" "github.com/ethereum/go-ethereum/logger/glog" + "github.com/ethereum/go-ethereum/rlp" "github.com/ethereum/go-ethereum/trie" ) @@ -205,13 +207,15 @@ func (self *StateDB) Delete(addr common.Address) bool { // Update the given state object and apply it to state trie func (self *StateDB) UpdateStateObject(stateObject *StateObject) { - //addr := stateObject.Address() - - if len(stateObject.CodeHash()) > 0 { - self.db.Put(stateObject.CodeHash(), stateObject.code) + if len(stateObject.code) > 0 { + self.db.Put(stateObject.codeHash, stateObject.code) } addr := stateObject.Address() - self.trie.Update(addr[:], stateObject.RlpEncode()) + data, err := rlp.EncodeToBytes(stateObject) + if err != nil { + panic(fmt.Errorf("can't encode object at %x: %v", addr[:], err)) + } + self.trie.Update(addr[:], data) } // Delete the given state object and delete it from the state trie @@ -238,10 +242,12 @@ func (self *StateDB) GetStateObject(addr common.Address) (stateObject *StateObje if len(data) == 0 { return nil } - - stateObject = NewStateObjectFromBytes(addr, []byte(data), self.db) + stateObject, err := DecodeObject(addr, self.db, data) + if err != nil { + glog.Errorf("can't decode object at %x: %v", addr[:], err) + return nil + } self.SetStateObject(stateObject) - return stateObject } diff --git a/core/types/receipt.go b/core/types/receipt.go index e7d5203a3..5f847fc5c 100644 --- a/core/types/receipt.go +++ b/core/types/receipt.go @@ -125,17 +125,14 @@ func (r *ReceiptForStorage) DecodeRLP(s *rlp.Stream) error { // Receipts is a wrapper around a Receipt array to implement types.DerivableList. type Receipts []*Receipt -// RlpEncode implements common.RlpEncode required for SHA3 derivation. -func (r Receipts) RlpEncode() []byte { - bytes, err := rlp.EncodeToBytes(r) +// Len returns the number of receipts in this list. +func (r Receipts) Len() int { return len(r) } + +// GetRlp returns the RLP encoding of one receipt from the list. +func (r Receipts) GetRlp(i int) []byte { + bytes, err := rlp.EncodeToBytes(r[i]) if err != nil { panic(err) } return bytes } - -// Len returns the number of receipts in this list. -func (r Receipts) Len() int { return len(r) } - -// GetRlp returns the RLP encoding of one receipt from the list. -func (r Receipts) GetRlp(i int) []byte { return common.Rlp(r[i]) }