From 780abaec988df302e0c98f1a35e9af35b5623746 Mon Sep 17 00:00:00 2001 From: obscuren Date: Tue, 23 Dec 2014 18:35:36 +0100 Subject: Switched to new trie --- state/dump.go | 26 ++-- state/state.go | 325 ------------------------------------------------- state/state_object.go | 42 ++++--- state/statedb.go | 326 ++++++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 365 insertions(+), 354 deletions(-) delete mode 100644 state/state.go create mode 100644 state/statedb.go (limited to 'state') diff --git a/state/dump.go b/state/dump.go index c1f5ecf3a..40ecff50c 100644 --- a/state/dump.go +++ b/state/dump.go @@ -22,22 +22,23 @@ type World struct { func (self *StateDB) Dump() []byte { world := World{ - Root: ethutil.Bytes2Hex(self.Trie.GetRoot()), + Root: ethutil.Bytes2Hex(self.trie.Root()), Accounts: make(map[string]Account), } - self.Trie.NewIterator().Each(func(key string, value *ethutil.Value) { - stateObject := NewStateObjectFromBytes([]byte(key), value.Bytes()) + it := self.trie.Iterator() + for it.Next() { + stateObject := NewStateObjectFromBytes(it.Key, it.Value) account := Account{Balance: stateObject.balance.String(), Nonce: stateObject.Nonce, Root: ethutil.Bytes2Hex(stateObject.Root()), CodeHash: ethutil.Bytes2Hex(stateObject.codeHash)} account.Storage = make(map[string]string) - stateObject.EachStorage(func(key string, value *ethutil.Value) { - value.Decode() - account.Storage[ethutil.Bytes2Hex([]byte(key))] = ethutil.Bytes2Hex(value.Bytes()) - }) - world.Accounts[ethutil.Bytes2Hex([]byte(key))] = account - }) + storageIt := stateObject.State.trie.Iterator() + for storageIt.Next() { + account.Storage[ethutil.Bytes2Hex(it.Key)] = ethutil.Bytes2Hex(it.Value) + } + world.Accounts[ethutil.Bytes2Hex(it.Key)] = account + } json, err := json.MarshalIndent(world, "", " ") if err != nil { @@ -50,7 +51,8 @@ func (self *StateDB) Dump() []byte { // Debug stuff func (self *StateObject) CreateOutputForDiff() { fmt.Printf("%x %x %x %x\n", self.Address(), self.State.Root(), self.balance.Bytes(), self.Nonce) - self.EachStorage(func(addr string, value *ethutil.Value) { - fmt.Printf("%x %x\n", addr, value.Bytes()) - }) + it := self.State.trie.Iterator() + for it.Next() { + fmt.Printf("%x %x\n", it.Key, it.Value) + } } diff --git a/state/state.go b/state/state.go deleted file mode 100644 index f77da72f0..000000000 --- a/state/state.go +++ /dev/null @@ -1,325 +0,0 @@ -package state - -import ( - "math/big" - - "github.com/ethereum/go-ethereum/ethutil" - "github.com/ethereum/go-ethereum/logger" - "github.com/ethereum/go-ethereum/trie" -) - -var statelogger = logger.NewLogger("STATE") - -// StateDBs within the ethereum protocol are used to store anything -// within the merkle trie. StateDBs take care of caching and storing -// nested states. It's the general query interface to retrieve: -// * Contracts -// * Accounts -type StateDB struct { - // The trie for this structure - Trie *trie.Trie - - stateObjects map[string]*StateObject - - manifest *Manifest - - refund map[string]*big.Int - - logs Logs -} - -// Create a new state from a given trie -func New(trie *trie.Trie) *StateDB { - return &StateDB{Trie: trie, stateObjects: make(map[string]*StateObject), manifest: NewManifest(), refund: make(map[string]*big.Int)} -} - -func (self *StateDB) EmptyLogs() { - self.logs = nil -} - -func (self *StateDB) AddLog(log Log) { - self.logs = append(self.logs, log) -} - -func (self *StateDB) Logs() Logs { - return self.logs -} - -// Retrieve the balance from the given address or 0 if object not found -func (self *StateDB) GetBalance(addr []byte) *big.Int { - stateObject := self.GetStateObject(addr) - if stateObject != nil { - return stateObject.balance - } - - return ethutil.Big0 -} - -func (self *StateDB) Refund(addr []byte, gas *big.Int) { - if self.refund[string(addr)] == nil { - self.refund[string(addr)] = new(big.Int) - } - self.refund[string(addr)].Add(self.refund[string(addr)], gas) -} - -func (self *StateDB) AddBalance(addr []byte, amount *big.Int) { - stateObject := self.GetStateObject(addr) - if stateObject != nil { - stateObject.AddBalance(amount) - } -} - -func (self *StateDB) GetNonce(addr []byte) uint64 { - stateObject := self.GetStateObject(addr) - if stateObject != nil { - return stateObject.Nonce - } - - return 0 -} - -func (self *StateDB) SetNonce(addr []byte, nonce uint64) { - stateObject := self.GetStateObject(addr) - if stateObject != nil { - stateObject.Nonce = nonce - } -} - -func (self *StateDB) GetCode(addr []byte) []byte { - stateObject := self.GetStateObject(addr) - if stateObject != nil { - return stateObject.Code - } - - return nil -} - -func (self *StateDB) SetCode(addr, code []byte) { - stateObject := self.GetStateObject(addr) - if stateObject != nil { - stateObject.SetCode(code) - } -} - -func (self *StateDB) GetState(a, b []byte) []byte { - stateObject := self.GetStateObject(a) - if stateObject != nil { - return stateObject.GetState(b).Bytes() - } - - return nil -} - -func (self *StateDB) SetState(addr, key []byte, value interface{}) { - stateObject := self.GetStateObject(addr) - if stateObject != nil { - stateObject.SetState(key, ethutil.NewValue(value)) - } -} - -func (self *StateDB) Delete(addr []byte) bool { - stateObject := self.GetStateObject(addr) - if stateObject != nil { - stateObject.MarkForDeletion() - - return true - } - - return false -} - -// -// Setting, updating & deleting state object methods -// - -// 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 { - ethutil.Config.Db.Put(stateObject.CodeHash(), stateObject.Code) - } - - self.Trie.Update(string(addr), string(stateObject.RlpEncode())) -} - -// Delete the given state object and delete it from the state trie -func (self *StateDB) DeleteStateObject(stateObject *StateObject) { - self.Trie.Delete(string(stateObject.Address())) - - delete(self.stateObjects, string(stateObject.Address())) -} - -// Retrieve a state object given my the address. Nil if not found -func (self *StateDB) GetStateObject(addr []byte) *StateObject { - addr = ethutil.Address(addr) - - stateObject := self.stateObjects[string(addr)] - if stateObject != nil { - return stateObject - } - - data := self.Trie.Get(string(addr)) - if len(data) == 0 { - return nil - } - - stateObject = NewStateObjectFromBytes(addr, []byte(data)) - self.SetStateObject(stateObject) - - return stateObject -} - -func (self *StateDB) SetStateObject(object *StateObject) { - self.stateObjects[string(object.address)] = object -} - -// Retrieve a state object or create a new state object if nil -func (self *StateDB) GetOrNewStateObject(addr []byte) *StateObject { - stateObject := self.GetStateObject(addr) - if stateObject == nil { - stateObject = self.NewStateObject(addr) - } - - return stateObject -} - -// Create a state object whether it exist in the trie or not -func (self *StateDB) NewStateObject(addr []byte) *StateObject { - addr = ethutil.Address(addr) - - statelogger.Debugf("(+) %x\n", addr) - - stateObject := NewStateObject(addr) - self.stateObjects[string(addr)] = stateObject - - return stateObject -} - -// Deprecated -func (self *StateDB) GetAccount(addr []byte) *StateObject { - return self.GetOrNewStateObject(addr) -} - -// -// Setting, copying of the state methods -// - -func (s *StateDB) Cmp(other *StateDB) bool { - return s.Trie.Cmp(other.Trie) -} - -func (self *StateDB) Copy() *StateDB { - if self.Trie != nil { - state := New(self.Trie.Copy()) - for k, stateObject := range self.stateObjects { - state.stateObjects[k] = stateObject.Copy() - } - - for addr, refund := range self.refund { - state.refund[addr] = new(big.Int).Set(refund) - } - - logs := make(Logs, len(self.logs)) - copy(logs, self.logs) - state.logs = logs - - return state - } - - return nil -} - -func (self *StateDB) Set(state *StateDB) { - if state == nil { - panic("Tried setting 'state' to nil through 'Set'") - } - - self.Trie = state.Trie - self.stateObjects = state.stateObjects - self.refund = state.refund - self.logs = state.logs -} - -func (s *StateDB) Root() []byte { - return s.Trie.GetRoot() -} - -// Resets the trie and all siblings -func (s *StateDB) Reset() { - s.Trie.Undo() - - // Reset all nested states - for _, stateObject := range s.stateObjects { - if stateObject.State == nil { - continue - } - - stateObject.Reset() - } - - s.Empty() -} - -// Syncs the trie and all siblings -func (s *StateDB) Sync() { - // Sync all nested states - for _, stateObject := range s.stateObjects { - if stateObject.State == nil { - continue - } - - stateObject.State.Sync() - } - - s.Trie.Sync() - - s.Empty() -} - -func (self *StateDB) Empty() { - self.stateObjects = make(map[string]*StateObject) - self.refund = make(map[string]*big.Int) -} - -func (self *StateDB) Refunds() map[string]*big.Int { - return self.refund -} - -func (self *StateDB) Update(gasUsed *big.Int) { - var deleted bool - - self.refund = make(map[string]*big.Int) - - for _, stateObject := range self.stateObjects { - if stateObject.remove { - self.DeleteStateObject(stateObject) - deleted = true - } else { - stateObject.Sync() - - self.UpdateStateObject(stateObject) - } - } - - // FIXME trie delete is broken - if deleted { - valid, t2 := trie.ParanoiaCheck(self.Trie) - if !valid { - statelogger.Infof("Warn: PARANOIA: Different state root during copy %x vs %x\n", self.Trie.GetRoot(), t2.GetRoot()) - - self.Trie = t2 - } - } -} - -func (self *StateDB) Manifest() *Manifest { - return self.manifest -} - -// Debug stuff -func (self *StateDB) CreateOutputForDiff() { - for _, stateObject := range self.stateObjects { - stateObject.CreateOutputForDiff() - } -} diff --git a/state/state_object.go b/state/state_object.go index b8af4e702..420ad9757 100644 --- a/state/state_object.go +++ b/state/state_object.go @@ -6,7 +6,7 @@ import ( "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/ethutil" - "github.com/ethereum/go-ethereum/trie" + "github.com/ethereum/go-ethereum/ptrie" ) type Code []byte @@ -62,7 +62,7 @@ func NewStateObject(addr []byte) *StateObject { address := ethutil.Address(addr) object := &StateObject{address: address, balance: new(big.Int), gasPool: new(big.Int)} - object.State = New(trie.New(ethutil.Config.Db, "")) + object.State = New(ptrie.New(nil, ethutil.Config.Db)) //New(trie.New(ethutil.Config.Db, "")) object.storage = make(Storage) object.gasPool = new(big.Int) @@ -72,7 +72,7 @@ func NewStateObject(addr []byte) *StateObject { func NewContract(address []byte, balance *big.Int, root []byte) *StateObject { contract := NewStateObject(address) contract.balance = balance - contract.State = New(trie.New(ethutil.Config.Db, string(root))) + contract.State = New(ptrie.New(nil, ethutil.Config.Db)) //New(trie.New(ethutil.Config.Db, string(root))) return contract } @@ -89,12 +89,12 @@ func (self *StateObject) MarkForDeletion() { statelogger.DebugDetailf("%x: #%d %v (deletion)\n", self.Address(), self.Nonce, self.balance) } -func (c *StateObject) GetAddr(addr []byte) *ethutil.Value { - return ethutil.NewValueFromBytes([]byte(c.State.Trie.Get(string(addr)))) +func (c *StateObject) getAddr(addr []byte) *ethutil.Value { + return ethutil.NewValueFromBytes([]byte(c.State.trie.Get(addr))) } -func (c *StateObject) SetAddr(addr []byte, value interface{}) { - c.State.Trie.Update(string(addr), string(ethutil.NewValue(value).Encode())) +func (c *StateObject) setAddr(addr []byte, value interface{}) { + c.State.trie.Update(addr, ethutil.Encode(value)) } func (self *StateObject) GetStorage(key *big.Int) *ethutil.Value { @@ -113,7 +113,7 @@ func (self *StateObject) GetState(k []byte) *ethutil.Value { value := self.storage[string(key)] if value == nil { - value = self.GetAddr(key) + value = self.getAddr(key) if !value.IsNil() { self.storage[string(key)] = value @@ -128,6 +128,7 @@ func (self *StateObject) SetState(k []byte, value *ethutil.Value) { self.storage[string(key)] = value.Copy() } +/* // Iterate over each storage address and yield callback func (self *StateObject) EachStorage(cb trie.EachCallback) { // First loop over the uncommit/cached values in storage @@ -145,23 +146,26 @@ func (self *StateObject) EachStorage(cb trie.EachCallback) { } }) } +*/ func (self *StateObject) Sync() { for key, value := range self.storage { if value.Len() == 0 { - self.State.Trie.Delete(string(key)) + self.State.trie.Delete([]byte(key)) continue } - self.SetAddr([]byte(key), value) + self.setAddr([]byte(key), value) } - valid, t2 := trie.ParanoiaCheck(self.State.Trie) - if !valid { - statelogger.Infof("Warn: PARANOIA: Different state storage root during copy %x vs %x\n", self.State.Root(), t2.GetRoot()) + /* + valid, t2 := ptrie.ParanoiaCheck(self.State.trie, ethutil.Config.Db) + if !valid { + statelogger.Infof("Warn: PARANOIA: Different state storage root during copy %x vs %x\n", self.State.Root(), t2.Root()) - self.State.Trie = t2 - } + self.State.trie = t2 + } + */ } func (c *StateObject) GetInstr(pc *big.Int) *ethutil.Value { @@ -276,8 +280,12 @@ func (c *StateObject) Init() Code { return c.InitCode } +func (self *StateObject) Trie() *ptrie.Trie { + return self.State.trie +} + func (self *StateObject) Root() []byte { - return self.State.Trie.GetRoot() + return self.Trie().Root() } func (self *StateObject) SetCode(code []byte) { @@ -302,7 +310,7 @@ func (c *StateObject) RlpDecode(data []byte) { c.Nonce = decoder.Get(0).Uint() c.balance = decoder.Get(1).BigInt() - c.State = New(trie.New(ethutil.Config.Db, decoder.Get(2).Interface())) + c.State = New(ptrie.New(decoder.Get(2).Bytes(), ethutil.Config.Db)) //New(trie.New(ethutil.Config.Db, decoder.Get(2).Interface())) c.storage = make(map[string]*ethutil.Value) c.gasPool = new(big.Int) diff --git a/state/statedb.go b/state/statedb.go new file mode 100644 index 000000000..6a11fc328 --- /dev/null +++ b/state/statedb.go @@ -0,0 +1,326 @@ +package state + +import ( + "bytes" + "math/big" + + "github.com/ethereum/go-ethereum/ethutil" + "github.com/ethereum/go-ethereum/logger" + "github.com/ethereum/go-ethereum/ptrie" +) + +var statelogger = logger.NewLogger("STATE") + +// StateDBs within the ethereum protocol are used to store anything +// within the merkle trie. StateDBs take care of caching and storing +// nested states. It's the general query interface to retrieve: +// * Contracts +// * Accounts +type StateDB struct { + //Trie *trie.Trie + trie *ptrie.Trie + + stateObjects map[string]*StateObject + + manifest *Manifest + + refund map[string]*big.Int + + logs Logs +} + +// Create a new state from a given trie +func New(trie *ptrie.Trie) *StateDB { + return &StateDB{trie: trie, stateObjects: make(map[string]*StateObject), manifest: NewManifest(), refund: make(map[string]*big.Int)} +} + +func (self *StateDB) EmptyLogs() { + self.logs = nil +} + +func (self *StateDB) AddLog(log Log) { + self.logs = append(self.logs, log) +} + +func (self *StateDB) Logs() Logs { + return self.logs +} + +// Retrieve the balance from the given address or 0 if object not found +func (self *StateDB) GetBalance(addr []byte) *big.Int { + stateObject := self.GetStateObject(addr) + if stateObject != nil { + return stateObject.balance + } + + return ethutil.Big0 +} + +func (self *StateDB) Refund(addr []byte, gas *big.Int) { + if self.refund[string(addr)] == nil { + self.refund[string(addr)] = new(big.Int) + } + self.refund[string(addr)].Add(self.refund[string(addr)], gas) +} + +func (self *StateDB) AddBalance(addr []byte, amount *big.Int) { + stateObject := self.GetStateObject(addr) + if stateObject != nil { + stateObject.AddBalance(amount) + } +} + +func (self *StateDB) GetNonce(addr []byte) uint64 { + stateObject := self.GetStateObject(addr) + if stateObject != nil { + return stateObject.Nonce + } + + return 0 +} + +func (self *StateDB) SetNonce(addr []byte, nonce uint64) { + stateObject := self.GetStateObject(addr) + if stateObject != nil { + stateObject.Nonce = nonce + } +} + +func (self *StateDB) GetCode(addr []byte) []byte { + stateObject := self.GetStateObject(addr) + if stateObject != nil { + return stateObject.Code + } + + return nil +} + +func (self *StateDB) SetCode(addr, code []byte) { + stateObject := self.GetStateObject(addr) + if stateObject != nil { + stateObject.SetCode(code) + } +} + +func (self *StateDB) GetState(a, b []byte) []byte { + stateObject := self.GetStateObject(a) + if stateObject != nil { + return stateObject.GetState(b).Bytes() + } + + return nil +} + +func (self *StateDB) SetState(addr, key []byte, value interface{}) { + stateObject := self.GetStateObject(addr) + if stateObject != nil { + stateObject.SetState(key, ethutil.NewValue(value)) + } +} + +func (self *StateDB) Delete(addr []byte) bool { + stateObject := self.GetStateObject(addr) + if stateObject != nil { + stateObject.MarkForDeletion() + + return true + } + + return false +} + +// +// Setting, updating & deleting state object methods +// + +// 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 { + ethutil.Config.Db.Put(stateObject.CodeHash(), stateObject.Code) + } + + self.trie.Update(addr, stateObject.RlpEncode()) +} + +// Delete the given state object and delete it from the state trie +func (self *StateDB) DeleteStateObject(stateObject *StateObject) { + self.trie.Delete(stateObject.Address()) + + delete(self.stateObjects, string(stateObject.Address())) +} + +// Retrieve a state object given my the address. Nil if not found +func (self *StateDB) GetStateObject(addr []byte) *StateObject { + addr = ethutil.Address(addr) + + stateObject := self.stateObjects[string(addr)] + if stateObject != nil { + return stateObject + } + + data := self.trie.Get(addr) + if len(data) == 0 { + return nil + } + + stateObject = NewStateObjectFromBytes(addr, []byte(data)) + self.SetStateObject(stateObject) + + return stateObject +} + +func (self *StateDB) SetStateObject(object *StateObject) { + self.stateObjects[string(object.address)] = object +} + +// Retrieve a state object or create a new state object if nil +func (self *StateDB) GetOrNewStateObject(addr []byte) *StateObject { + stateObject := self.GetStateObject(addr) + if stateObject == nil { + stateObject = self.NewStateObject(addr) + } + + return stateObject +} + +// Create a state object whether it exist in the trie or not +func (self *StateDB) NewStateObject(addr []byte) *StateObject { + addr = ethutil.Address(addr) + + statelogger.Debugf("(+) %x\n", addr) + + stateObject := NewStateObject(addr) + self.stateObjects[string(addr)] = stateObject + + return stateObject +} + +// Deprecated +func (self *StateDB) GetAccount(addr []byte) *StateObject { + return self.GetOrNewStateObject(addr) +} + +// +// Setting, copying of the state methods +// + +func (s *StateDB) Cmp(other *StateDB) bool { + return bytes.Equal(s.trie.Root(), other.trie.Root()) +} + +func (self *StateDB) Copy() *StateDB { + if self.trie != nil { + state := New(self.trie.Copy()) + for k, stateObject := range self.stateObjects { + state.stateObjects[k] = stateObject.Copy() + } + + for addr, refund := range self.refund { + state.refund[addr] = new(big.Int).Set(refund) + } + + logs := make(Logs, len(self.logs)) + copy(logs, self.logs) + state.logs = logs + + return state + } + + return nil +} + +func (self *StateDB) Set(state *StateDB) { + if state == nil { + panic("Tried setting 'state' to nil through 'Set'") + } + + self.trie = state.trie + self.stateObjects = state.stateObjects + self.refund = state.refund + self.logs = state.logs +} + +func (s *StateDB) Root() []byte { + return s.trie.Root() +} + +// Resets the trie and all siblings +func (s *StateDB) Reset() { + s.trie.Reset() + + // Reset all nested states + for _, stateObject := range s.stateObjects { + if stateObject.State == nil { + continue + } + + stateObject.Reset() + } + + s.Empty() +} + +// Syncs the trie and all siblings +func (s *StateDB) Sync() { + // Sync all nested states + for _, stateObject := range s.stateObjects { + if stateObject.State == nil { + continue + } + + stateObject.State.Sync() + } + + s.trie.Commit() + + s.Empty() +} + +func (self *StateDB) Empty() { + self.stateObjects = make(map[string]*StateObject) + self.refund = make(map[string]*big.Int) +} + +func (self *StateDB) Refunds() map[string]*big.Int { + return self.refund +} + +func (self *StateDB) Update(gasUsed *big.Int) { + var deleted bool + + self.refund = make(map[string]*big.Int) + + for _, stateObject := range self.stateObjects { + if stateObject.remove { + self.DeleteStateObject(stateObject) + deleted = true + } else { + stateObject.Sync() + + self.UpdateStateObject(stateObject) + } + } + + // FIXME trie delete is broken + if deleted { + valid, t2 := ptrie.ParanoiaCheck(self.trie, ethutil.Config.Db) + if !valid { + statelogger.Infof("Warn: PARANOIA: Different state root during copy %x vs %x\n", self.trie.Root(), t2.Root()) + + self.trie = t2 + } + } +} + +func (self *StateDB) Manifest() *Manifest { + return self.manifest +} + +// Debug stuff +func (self *StateDB) CreateOutputForDiff() { + for _, stateObject := range self.stateObjects { + stateObject.CreateOutputForDiff() + } +} -- cgit