ota underlying storage

This commit is contained in:
fnaticwang 2019-09-25 10:46:10 +08:00
parent 7f5d6a0bde
commit fcb5aaff61
4 changed files with 130 additions and 8 deletions

View file

@ -111,6 +111,11 @@ type (
account *common.Address
key, prevalue common.Hash
}
storageByteArrayChange struct {
account *common.Address
key common.Hash
prevalue []byte
}
codeChange struct {
account *common.Address
prevcode, prevhash []byte
@ -179,6 +184,14 @@ func (ch balanceChange) dirtied() *common.Address {
return ch.account
}
func (ch storageByteArrayChange) revert(s *StateDB) {
s.getStateObject(*ch.account).setStateByteArray(ch.key, ch.prevalue)
}
func (ch storageByteArrayChange) dirtied() *common.Address {
return ch.account
}
func (ch nonceChange) revert(s *StateDB) {
s.getStateObject(*ch.account).setNonce(ch.prev)
}

View file

@ -39,6 +39,8 @@ func (c Code) String() string {
type Storage map[common.Hash]common.Hash
type StorageByteArray map[common.Hash][]byte
func (s Storage) String() (str string) {
for key, value := range s {
str += fmt.Sprintf("%X : %X\n", key, value)
@ -56,6 +58,15 @@ func (s Storage) Copy() Storage {
return cpy
}
func (self StorageByteArray) Copy() StorageByteArray {
cpy := make(StorageByteArray)
for key, value := range self {
cpy[key] = value
}
return cpy
}
// stateObject represents an Ethereum account which is being modified.
//
// The usage pattern is as follows:
@ -84,6 +95,9 @@ type stateObject struct {
dirtyStorage Storage // Storage entries that have been modified in the current transaction execution
fakeStorage Storage // Fake storage which constructed by caller for debugging purpose.
cachedStorageByteArray StorageByteArray
dirtyStorageByteArray StorageByteArray
// Cache flags.
// When an object is marked suicided it will be delete from the trie
// during the "update" phase of the state transition.
@ -94,7 +108,7 @@ type stateObject struct {
// empty returns whether the account is considered empty.
func (s *stateObject) empty() bool {
return s.data.Nonce == 0 && s.data.Balance.Sign() == 0 && bytes.Equal(s.data.CodeHash, emptyCodeHash)
return s.data.Nonce == 0 && s.data.Balance.Sign() == 0 && bytes.Equal(s.data.CodeHash, emptyCodeHash) && len(s.dirtyStorage) == 0 && len(s.dirtyStorageByteArray) == 0
}
// Account is the Ethereum consensus representation of accounts.
@ -118,13 +132,15 @@ func newObject(db *StateDB, address common.Address, data Account) *stateObject {
data.Root = emptyRoot
}
return &stateObject{
db: db,
address: address,
addrHash: crypto.Keccak256Hash(address[:]),
data: data,
originStorage: make(Storage),
pendingStorage: make(Storage),
dirtyStorage: make(Storage),
db: db,
address: address,
addrHash: crypto.Keccak256Hash(address[:]),
data: data,
originStorage: make(Storage),
pendingStorage: make(Storage),
dirtyStorage: make(Storage),
cachedStorageByteArray: make(StorageByteArray),
dirtyStorageByteArray: make(StorageByteArray),
}
}
@ -260,6 +276,34 @@ func (s *stateObject) setState(key, value common.Hash) {
s.dirtyStorage[key] = value
}
func (s *stateObject) GetStateByteArray(db Database, key common.Hash) []byte {
value, exists := s.cachedStorageByteArray[key]
if exists {
return value
}
// Load from DB in case it is missing.
value, err := s.getTrie(db).TryGet(key[:])
if err == nil && len(value) != 0 {
s.cachedStorageByteArray[key] = value
}
return value
}
func (s *stateObject) SetStateByteArray(db Database, key common.Hash, value []byte) {
s.db.journal.append(storageByteArrayChange{
account: &s.address,
key: key,
prevalue: s.GetStateByteArray(db, key),
})
s.setStateByteArray(key, value)
}
func (self *stateObject) setStateByteArray(key common.Hash, value []byte) {
self.cachedStorageByteArray[key] = value
self.dirtyStorageByteArray[key] = value
}
// finalise moves all dirty storage slots into the pending area to be hashed or
// committed later. It is invoked at the end of every transaction.
func (s *stateObject) finalise() {
@ -300,6 +344,19 @@ func (s *stateObject) updateTrie(db Database) Trie {
if len(s.pendingStorage) > 0 {
s.pendingStorage = make(Storage)
}
for key, value := range s.dirtyStorageByteArray {
delete(s.dirtyStorageByteArray, key)
if len(value) == 0 {
s.setError(tr.TryDelete(key[:]))
continue
}
s.setError(tr.TryUpdate(key[:], value))
}
if len(s.dirtyStorageByteArray) > 0 {
s.dirtyStorageByteArray = make(StorageByteArray)
}
return tr
}
@ -380,6 +437,8 @@ func (s *stateObject) deepCopy(db *StateDB) *stateObject {
stateObject.dirtyStorage = s.dirtyStorage.Copy()
stateObject.originStorage = s.originStorage.Copy()
stateObject.pendingStorage = s.pendingStorage.Copy()
stateObject.dirtyStorageByteArray = s.dirtyStorageByteArray.Copy()
stateObject.cachedStorageByteArray = s.cachedStorageByteArray.Copy()
stateObject.suicided = s.suicided
stateObject.dirtyCode = s.dirtyCode
stateObject.deleted = s.deleted

View file

@ -398,6 +398,51 @@ func (self *StateDB) SetStorage(addr common.Address, storage map[common.Hash]com
}
}
// cb is callback function. cb return true indicating like to continue, return false indicating stop
func (db *StateDB) ForEachStorageByteArray(addr common.Address, cb func(key common.Hash, value []byte) bool) {
so := db.getStateObject(addr)
if so == nil {
return
}
// When iterating over the storage check the cache first
for h, value := range so.cachedStorageByteArray {
if !cb(h, value) {
return
}
}
it := trie.NewIterator(so.getTrie(db.db).NodeIterator(nil))
for it.Next() {
// ignore cached values
key := common.BytesToHash(db.trie.GetKey(it.Key))
if _, ok := so.fakeStorage[key]; !ok {
if !cb(key, it.Value) {
return
}
} else if _, ok := so.dirtyStorage[key]; !ok {
if !cb(key, it.Value) {
return
}
}
}
}
func (self *StateDB) GetStateByteArray(a common.Address, b common.Hash) []byte {
stateObject := self.getStateObject(a)
if stateObject != nil {
return stateObject.GetStateByteArray(self.db, b)
}
return nil
}
func (self *StateDB) SetStateByteArray(addr common.Address, key common.Hash, value []byte) {
stateObject := self.GetOrNewStateObject(addr)
if stateObject != nil {
stateObject.SetStateByteArray(self.db, key, value)
}
}
// Suicide marks the given account as suicided.
// This clears the account balance.
//

View file

@ -47,6 +47,9 @@ type StateDB interface {
GetState(common.Address, common.Hash) common.Hash
SetState(common.Address, common.Hash, common.Hash)
GetStateByteArray(common.Address, common.Hash) []byte
SetStateByteArray(common.Address, common.Hash, []byte)
Suicide(common.Address) bool
HasSuicided(common.Address) bool
@ -64,6 +67,8 @@ type StateDB interface {
AddPreimage(common.Hash, []byte)
ForEachStorage(common.Address, func(common.Hash, common.Hash) bool) error
ForEachStorageByteArray(common.Address, func(common.Hash, []byte) bool)
}
// CallContext provides a basic interface for the EVM calling conventions. The EVM