Skip to content

[wip] combined coreth mods #3

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
3 changes: 3 additions & 0 deletions core/state/dump.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ type DumpAccount struct {
Root hexutil.Bytes `json:"root"`
CodeHash hexutil.Bytes `json:"codeHash"`
Code hexutil.Bytes `json:"code,omitempty"`
IsMultiCoin bool `json:"isMultiCoin,omitempty"`
Storage map[common.Hash]string `json:"storage,omitempty"`
Address *common.Address `json:"address,omitempty"` // Address only present in iterative (line-by-line) mode
AddressHash hexutil.Bytes `json:"key,omitempty"` // If we don't have address, we can output the key
Expand Down Expand Up @@ -97,6 +98,7 @@ func (d iterativeDump) OnAccount(addr *common.Address, account DumpAccount) {
Root: account.Root,
CodeHash: account.CodeHash,
Code: account.Code,
IsMultiCoin: account.IsMultiCoin,
Storage: account.Storage,
AddressHash: account.AddressHash,
Address: addr,
Expand Down Expand Up @@ -144,6 +146,7 @@ func (s *StateDB) DumpToCollector(c DumpCollector, conf *DumpConfig) (nextKey []
Nonce: data.Nonce,
Root: data.Root[:],
CodeHash: data.CodeHash,
IsMultiCoin: data.IsMultiCoin,
AddressHash: it.Key,
}
address *common.Address
Expand Down
35 changes: 35 additions & 0 deletions core/state/interfaces.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package state

import (
"reflect"

"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/state/snapshot"
)

type SnapshotTree interface {
Snapshot(common.Hash) snapshot.Snapshot
UpdateWithBlockHash(
root common.Hash,
parent common.Hash,
blockHash common.Hash,
parentHash common.Hash,
destructs map[common.Hash]struct{},
accounts map[common.Hash][]byte,
storage map[common.Hash]map[common.Hash][]byte,
) error
}

// https://vitaneri.com/posts/check-for-nil-interface-in-go
func checkNilInterface(i interface{}) bool {
iv := reflect.ValueOf(i)
if !iv.IsValid() {
return true
}
switch iv.Kind() {
case reflect.Ptr, reflect.Slice, reflect.Map, reflect.Func, reflect.Interface:
return iv.IsNil()
default:
return false
}
}
38 changes: 32 additions & 6 deletions core/state/snapshot/snapshot.go
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,12 @@ type Snapshot interface {
// Storage directly retrieves the storage data associated with a particular hash,
// within a particular account.
Storage(accountHash, storageHash common.Hash) ([]byte, error)

// AccountIterator creates an account iterator over an arbitrary layer.
AccountIterator(seek common.Hash) AccountIterator

// StorageIterator creates a storage iterator over an arbitrary layer.
StorageIterator(account common.Hash, seek common.Hash) (StorageIterator, bool)
}

// snapshot is the internal version of the snapshot data layer that supports some
Expand Down Expand Up @@ -140,12 +146,6 @@ type snapshot interface {
// Stale return whether this layer has become stale (was flattened across) or
// if it's still live.
Stale() bool

// AccountIterator creates an account iterator over an arbitrary layer.
AccountIterator(seek common.Hash) AccountIterator

// StorageIterator creates a storage iterator over an arbitrary layer.
StorageIterator(account common.Hash, seek common.Hash) (StorageIterator, bool)
}

// Config includes the configurations for snapshots.
Expand Down Expand Up @@ -338,6 +338,32 @@ func (t *Tree) Snapshots(root common.Hash, limits int, nodisk bool) []Snapshot {
return ret
}

func (t *Tree) UpdateWithBlockHash(
root common.Hash,
parent common.Hash,
blockHash common.Hash,
parentHash common.Hash,
destructs map[common.Hash]struct{},
accounts map[common.Hash][]byte,
storage map[common.Hash]map[common.Hash][]byte,
) error {
// Only update if there's a state transition (skip empty Clique blocks)
if parent != root {
if err := t.Update(root, parent, destructs, accounts, storage); err != nil {
log.Warn("Failed to update snapshot tree", "from", parent, "to", root, "err", err)
}

// Keep 128 diff layers in the memory, persistent layer is 129th.
// - head layer is paired with HEAD state
// - head-1 layer is paired with HEAD-1 state
// - head-127 layer(bottom-most diff layer) is paired with HEAD-127 state
if err := t.Cap(root, 128); err != nil {
log.Warn("Failed to cap snapshot tree", "root", root, "layers", 128, "err", err)
}
}
return nil
}

// Update adds a new snapshot into the tree, if that can be linked to an existing
// old parent. It is disallowed to insert a disk layer (the origin of all).
func (t *Tree) Update(blockRoot common.Hash, parentRoot common.Hash, destructs map[common.Hash]struct{}, accounts map[common.Hash][]byte, storage map[common.Hash]map[common.Hash][]byte) error {
Expand Down
8 changes: 4 additions & 4 deletions core/state/snapshot/snapshot_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,7 @@ func TestDiskLayerExternalInvalidationFullFlatten(t *testing.T) {
}
// Since the base layer was modified, ensure that data retrievals on the external reference fail
if acc, err := ref.Account(common.HexToHash("0x01")); err != ErrSnapshotStale {
t.Errorf("stale reference returned account: %#x (err: %v)", acc, err)
t.Errorf("stale reference returned account: %#v (err: %v)", acc, err)
}
if slot, err := ref.Storage(common.HexToHash("0xa1"), common.HexToHash("0xb1")); err != ErrSnapshotStale {
t.Errorf("stale reference returned storage slot: %#x (err: %v)", slot, err)
Expand Down Expand Up @@ -169,7 +169,7 @@ func TestDiskLayerExternalInvalidationPartialFlatten(t *testing.T) {
}
// Since the base layer was modified, ensure that data retrievals on the external reference fail
if acc, err := ref.Account(common.HexToHash("0x01")); err != ErrSnapshotStale {
t.Errorf("stale reference returned account: %#x (err: %v)", acc, err)
t.Errorf("stale reference returned account: %#v (err: %v)", acc, err)
}
if slot, err := ref.Storage(common.HexToHash("0xa1"), common.HexToHash("0xb1")); err != ErrSnapshotStale {
t.Errorf("stale reference returned storage slot: %#x (err: %v)", slot, err)
Expand Down Expand Up @@ -231,7 +231,7 @@ func TestDiffLayerExternalInvalidationPartialFlatten(t *testing.T) {
}
// Since the accumulator diff layer was modified, ensure that data retrievals on the external reference fail
if acc, err := ref.Account(common.HexToHash("0x01")); err != ErrSnapshotStale {
t.Errorf("stale reference returned account: %#x (err: %v)", acc, err)
t.Errorf("stale reference returned account: %#v (err: %v)", acc, err)
}
if slot, err := ref.Storage(common.HexToHash("0xa1"), common.HexToHash("0xb1")); err != ErrSnapshotStale {
t.Errorf("stale reference returned storage slot: %#x (err: %v)", slot, err)
Expand Down Expand Up @@ -280,7 +280,7 @@ func TestPostCapBasicDataAccess(t *testing.T) {
// shouldErr checks that an account access errors as expected
shouldErr := func(layer *diffLayer, key string) error {
if data, err := layer.Account(common.HexToHash(key)); err == nil {
return fmt.Errorf("expected error, got data %x", data)
return fmt.Errorf("expected error, got data %v", data)
}
return nil
}
Expand Down
2 changes: 1 addition & 1 deletion core/state/state_object.go
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,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, types.EmptyCodeHash.Bytes())
return s.data.Nonce == 0 && s.data.Balance.Sign() == 0 && bytes.Equal(s.data.CodeHash, types.EmptyCodeHash.Bytes()) && !s.data.IsMultiCoin
}

// newObject creates a state object.
Expand Down
82 changes: 82 additions & 0 deletions core/state/state_object_coreth.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
package state

import (
"math/big"

"github.com/ethereum/go-ethereum/common"
)

// AddBalanceMultiCoin adds amount of coinID to s's balance.
// It is used to add multicoin funds to the destination account of a transfer.
func (s *stateObject) AddBalanceMultiCoin(coinID common.Hash, amount *big.Int, db Database) {
if amount.Sign() == 0 {
if s.empty() {
s.touch()
}

return
}
s.SetBalanceMultiCoin(coinID, new(big.Int).Add(s.BalanceMultiCoin(coinID, db), amount), db)
}

// SubBalanceMultiCoin removes amount of coinID from s's balance.
// It is used to remove multicoin funds from the origin account of a transfer.
func (s *stateObject) SubBalanceMultiCoin(coinID common.Hash, amount *big.Int, db Database) {
if amount.Sign() == 0 {
return
}
s.SetBalanceMultiCoin(coinID, new(big.Int).Sub(s.BalanceMultiCoin(coinID, db), amount), db)
}

func (s *stateObject) SetBalanceMultiCoin(coinID common.Hash, amount *big.Int, db Database) {
s.EnableMultiCoin()
NormalizeCoinID(&coinID)
s.SetState(coinID, common.BigToHash(amount))
}
func (s *stateObject) enableMultiCoin() {
s.data.IsMultiCoin = true
}

// NormalizeCoinID ORs the 0th bit of the first byte in
// [coinID], which ensures this bit will be 1 and all other
// bits are left the same.
// This partitions multicoin storage from normal state storage.
func NormalizeCoinID(coinID *common.Hash) {
coinID[0] |= 0x01
}

// NormalizeStateKey ANDs the 0th bit of the first byte in
// [key], which ensures this bit will be 0 and all other bits
// are left the same.
// This partitions normal state storage from multicoin storage.
func NormalizeStateKey(key *common.Hash) {
key[0] &= 0xfe
}

func (s *stateObject) BalanceMultiCoin(coinID common.Hash, db Database) *big.Int {
NormalizeCoinID(&coinID)
return s.GetState(coinID).Big()
}

func (s *stateObject) EnableMultiCoin() bool {
if s.data.IsMultiCoin {
return false
}
s.db.journal.append(multiCoinEnable{
account: &s.address,
})
s.enableMultiCoin()
return true
}

type multiCoinEnable struct {
account *common.Address
}

func (ch multiCoinEnable) revert(s *StateDB) {
s.getStateObject(*ch.account).data.IsMultiCoin = false
}

func (ch multiCoinEnable) dirtied() *common.Address {
return ch.account
}
Loading