Finish initial StateDB implementation
This commit is contained in:
parent
817f9605aa
commit
2feb5bbb5b
@ -8,16 +8,17 @@ import (
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
auth "github.com/cosmos/cosmos-sdk/x/auth"
|
||||
|
||||
tmcrypto "github.com/tendermint/tendermint/crypto"
|
||||
|
||||
"github.com/cosmos/ethermint/types"
|
||||
ethcmn "github.com/ethereum/go-ethereum/common"
|
||||
ethstate "github.com/ethereum/go-ethereum/core/state"
|
||||
"github.com/ethereum/go-ethereum/crypto"
|
||||
)
|
||||
|
||||
var (
|
||||
_ ethstate.StateObject = (*stateObject)(nil)
|
||||
|
||||
emptyCodeHash = crypto.Keccak256(nil)
|
||||
emptyCodeHash = tmcrypto.Sha256(nil)
|
||||
)
|
||||
|
||||
type (
|
||||
@ -194,6 +195,33 @@ func (so *stateObject) markSuicided() {
|
||||
so.suicided = true
|
||||
}
|
||||
|
||||
// commitState commits all dirty storage to a KVStore.
|
||||
func (so *stateObject) commitState() {
|
||||
ctx := so.stateDB.ctx
|
||||
store := ctx.KVStore(so.stateDB.storageKey)
|
||||
|
||||
for key, value := range so.dirtyStorage {
|
||||
delete(so.dirtyStorage, key)
|
||||
|
||||
// skip no-op changes, persist actual changes
|
||||
if value == so.originStorage[key] {
|
||||
continue
|
||||
}
|
||||
|
||||
so.originStorage[key] = value
|
||||
|
||||
// delete empty values
|
||||
if (value == ethcmn.Hash{}) {
|
||||
store.Delete(key.Bytes())
|
||||
continue
|
||||
}
|
||||
|
||||
store.Set(key.Bytes(), value.Bytes())
|
||||
}
|
||||
|
||||
// TODO: Set the account (storage) root (but we probably don't need this)
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// Getters
|
||||
// ----------------------------------------------------------------------------
|
||||
@ -271,6 +299,8 @@ func (so *stateObject) GetCommittedState(_ ethstate.Database, key ethcmn.Hash) e
|
||||
store := ctx.KVStore(so.stateDB.storageKey)
|
||||
rawValue := store.Get(prefixKey.Bytes())
|
||||
|
||||
// TODO: Do we need to RLP split/decode?
|
||||
|
||||
if len(rawValue) > 0 {
|
||||
value.SetBytes(rawValue)
|
||||
}
|
||||
@ -328,5 +358,5 @@ func (so stateObject) GetStorageByAddressKey(key []byte) ethcmn.Hash {
|
||||
copy(compositeKey, prefix)
|
||||
copy(compositeKey[len(prefix):], key)
|
||||
|
||||
return crypto.Keccak256Hash(compositeKey)
|
||||
return ethcmn.BytesToHash(tmcrypto.Sha256(compositeKey))
|
||||
}
|
||||
|
144
state/statedb.go
144
state/statedb.go
@ -9,10 +9,11 @@ import (
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
"github.com/cosmos/cosmos-sdk/x/auth"
|
||||
|
||||
tmcrypto "github.com/tendermint/tendermint/crypto"
|
||||
|
||||
ethcmn "github.com/ethereum/go-ethereum/common"
|
||||
ethstate "github.com/ethereum/go-ethereum/core/state"
|
||||
ethtypes "github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/ethereum/go-ethereum/crypto"
|
||||
)
|
||||
|
||||
var (
|
||||
@ -126,7 +127,8 @@ func (csdb *CommitStateDB) SetState(addr ethcmn.Address, key, value ethcmn.Hash)
|
||||
func (csdb *CommitStateDB) SetCode(addr ethcmn.Address, code []byte) {
|
||||
so := csdb.GetOrNewStateObject(addr)
|
||||
if so != nil {
|
||||
so.SetCode(crypto.Keccak256Hash(code), code)
|
||||
key := ethcmn.BytesToHash(tmcrypto.Sha256(code))
|
||||
so.SetCode(key, code)
|
||||
}
|
||||
}
|
||||
|
||||
@ -299,22 +301,94 @@ func (csdb *CommitStateDB) StorageTrie(addr ethcmn.Address) ethstate.Trie {
|
||||
|
||||
// TODO: Commit writes the state ...
|
||||
func (csdb *CommitStateDB) Commit(deleteEmptyObjects bool) (root ethcmn.Hash, err error) {
|
||||
// TODO: ...
|
||||
defer csdb.clearJournalAndRefund()
|
||||
|
||||
// remove dirty state object entries based on the journal
|
||||
for addr := range csdb.journal.dirties {
|
||||
csdb.stateObjectsDirty[addr] = struct{}{}
|
||||
}
|
||||
|
||||
// set the state objects
|
||||
for addr, so := range csdb.stateObjects {
|
||||
_, isDirty := csdb.stateObjectsDirty[addr]
|
||||
switch {
|
||||
case so.suicided || (isDirty && deleteEmptyObjects && so.empty()):
|
||||
// If the state object has been removed, don't bother syncing it and just
|
||||
// remove it from the store.
|
||||
csdb.deleteStateObject(so)
|
||||
|
||||
case isDirty:
|
||||
// write any contract code associated with the state object
|
||||
if so.code != nil && so.dirtyCode {
|
||||
csdb.SetCode(so.Address(), so.code)
|
||||
so.dirtyCode = false
|
||||
}
|
||||
|
||||
// update the object in the KVStore
|
||||
csdb.updateStateObject(so)
|
||||
}
|
||||
|
||||
delete(csdb.stateObjectsDirty, addr)
|
||||
}
|
||||
|
||||
// TODO: Get and return the commit/root from the context
|
||||
return
|
||||
}
|
||||
|
||||
// TODO: ...
|
||||
// Finalize finalizes the state objects (accounts) state by setting their state,
|
||||
// removing the csdb destructed objects and clearing the journal as well as the
|
||||
// refunds.
|
||||
func (csdb *CommitStateDB) Finalize(deleteEmptyObjects bool) {
|
||||
// TODO: ...
|
||||
for addr := range csdb.journal.dirties {
|
||||
so, exist := csdb.stateObjects[addr]
|
||||
if !exist {
|
||||
// ripeMD is 'touched' at block 1714175, in tx:
|
||||
// 0x1237f737031e40bcde4a8b7e717b2d15e3ecadfe49bb1bbc71ee9deb09c6fcf2
|
||||
//
|
||||
// That tx goes out of gas, and although the notion of 'touched' does not
|
||||
// exist there, the touch-event will still be recorded in the journal.
|
||||
// Since ripeMD is a special snowflake, it will persist in the journal even
|
||||
// though the journal is reverted. In this special circumstance, it may
|
||||
// exist in journal.dirties but not in stateObjects. Thus, we can safely
|
||||
// ignore it here.
|
||||
continue
|
||||
}
|
||||
|
||||
if so.suicided || (deleteEmptyObjects && so.empty()) {
|
||||
csdb.deleteStateObject(so)
|
||||
} else {
|
||||
// Set all the dirty state storage items for the state object in the
|
||||
// KVStore and finally set the account in the account mapper.
|
||||
so.commitState()
|
||||
csdb.updateStateObject(so)
|
||||
}
|
||||
|
||||
csdb.stateObjectsDirty[addr] = struct{}{}
|
||||
}
|
||||
|
||||
// invalidate journal because reverting across transactions is not allowed
|
||||
csdb.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.
|
||||
// IntermediateRoot returns the current root hash of the state. It is called in
|
||||
// between transactions to get the root hash that goes into transaction
|
||||
// receipts.
|
||||
func (csdb *CommitStateDB) IntermediateRoot(deleteEmptyObjects bool) ethcmn.Hash {
|
||||
// TODO: ...
|
||||
// csdb.Finalize(deleteEmptyObjects)
|
||||
// return csdb.trie.Hash()
|
||||
csdb.Finalize(deleteEmptyObjects)
|
||||
|
||||
// TODO: Get and return the commit/root from the context
|
||||
return ethcmn.Hash{}
|
||||
}
|
||||
|
||||
// updateStateObject writes the given state object to the store.
|
||||
func (csdb *CommitStateDB) updateStateObject(so *stateObject) {
|
||||
csdb.am.SetAccount(csdb.ctx, so.account)
|
||||
}
|
||||
|
||||
// deleteStateObject removes the given state object from the state store.
|
||||
func (csdb *CommitStateDB) deleteStateObject(so *stateObject) {
|
||||
so.deleted = true
|
||||
csdb.am.RemoveAccount(csdb.ctx, so.account)
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
@ -351,6 +425,30 @@ func (csdb *CommitStateDB) RevertToSnapshot(revID int) {
|
||||
// Auxiliary
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
// Database retrieves the low level database supporting the lower level trie
|
||||
// ops. It is not used in Ethermint, so it returns nil.
|
||||
func (csdb *CommitStateDB) Database() ethstate.Database {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Empty returns whether the state object is either non-existent or empty
|
||||
// according to the EIP161 specification (balance = nonce = code = 0).
|
||||
func (csdb *CommitStateDB) Empty(addr ethcmn.Address) bool {
|
||||
so := csdb.getStateObject(addr)
|
||||
return so == nil || so.empty()
|
||||
}
|
||||
|
||||
// Exist reports whether the given account address exists in the state. Notably,
|
||||
// this also returns true for suicided accounts.
|
||||
func (csdb *CommitStateDB) Exist(addr ethcmn.Address) bool {
|
||||
return csdb.getStateObject(addr) != nil
|
||||
}
|
||||
|
||||
// Error returns the first non-nil error the StateDB encountered.
|
||||
func (csdb *CommitStateDB) Error() error {
|
||||
return csdb.dbErr
|
||||
}
|
||||
|
||||
// Suicide marks the given account as suicided and clears the account balance.
|
||||
//
|
||||
// The account's state object is still available until the state is committed,
|
||||
@ -540,24 +638,6 @@ func (csdb *CommitStateDB) createObject(addr ethcmn.Address) (newObj, prevObj *s
|
||||
return newObj, prevObj
|
||||
}
|
||||
|
||||
// Empty returns whether the state object is either non-existent or empty
|
||||
// according to the EIP161 specification (balance = nonce = code = 0).
|
||||
func (csdb *CommitStateDB) Empty(addr ethcmn.Address) bool {
|
||||
so := csdb.getStateObject(addr)
|
||||
return so == nil || so.empty()
|
||||
}
|
||||
|
||||
// Exist reports whether the given account address exists in the state. Notably,
|
||||
// this also returns true for suicided accounts.
|
||||
func (csdb *CommitStateDB) Exist(addr ethcmn.Address) bool {
|
||||
return csdb.getStateObject(addr) != nil
|
||||
}
|
||||
|
||||
// Error returns the first non-nil error the StateDB encountered.
|
||||
func (csdb *CommitStateDB) Error() error {
|
||||
return csdb.dbErr
|
||||
}
|
||||
|
||||
// setError remembers the first non-nil error it is called with.
|
||||
func (csdb *CommitStateDB) setError(err error) {
|
||||
if csdb.dbErr == nil {
|
||||
@ -593,9 +673,3 @@ func (csdb *CommitStateDB) getStateObject(addr ethcmn.Address) (stateObject *sta
|
||||
func (csdb *CommitStateDB) setStateObject(object *stateObject) {
|
||||
csdb.stateObjects[object.Address()] = object
|
||||
}
|
||||
|
||||
// Database retrieves the low level database supporting the lower level trie
|
||||
// ops. It is not used in Ethermint, so it returns nil.
|
||||
func (csdb *CommitStateDB) Database() ethstate.Database {
|
||||
return nil
|
||||
}
|
||||
|
@ -21,7 +21,11 @@ var _ auth.Account = (*Account)(nil)
|
||||
type Account struct {
|
||||
*auth.BaseAccount
|
||||
|
||||
Root ethcmn.Hash // merkle root of the storage trie
|
||||
// merkle root of the storage trie
|
||||
//
|
||||
// TODO: good chance we may not need this
|
||||
Root ethcmn.Hash
|
||||
|
||||
CodeHash []byte
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user