Skip to content

Commit

Permalink
Add read abstraction to the State (0xPolygon#827)
Browse files Browse the repository at this point in the history
* Add read abstraction in the state

* Add more error context to newTrie
  • Loading branch information
ferranbt authored Nov 15, 2022
1 parent 62d5981 commit 4f0161f
Show file tree
Hide file tree
Showing 13 changed files with 223 additions and 299 deletions.
11 changes: 10 additions & 1 deletion e2e/framework/helper.go
Original file line number Diff line number Diff line change
Expand Up @@ -460,6 +460,11 @@ func NewTestServers(t *testing.T, num int, conf func(*TestServerConfig)) []*Test
}
})

logsDir, err := initLogsDir(t)
if err != nil {
t.Fatal(err)
}

// It is safe to use a dummy MultiAddr here, since this init method
// is called for the Dev consensus mode, and IBFT servers are initialized with NewIBFTServersManager.
// This method needs to be standardized in the future
Expand All @@ -471,7 +476,11 @@ func NewTestServers(t *testing.T, num int, conf func(*TestServerConfig)) []*Test
t.Fatal(err)
}

srv := NewTestServer(t, dataDir, conf)
srv := NewTestServer(t, dataDir, func(c *TestServerConfig) {
c.SetLogsDir(logsDir)
c.SetSaveLogs(true)
conf(c)
})
srv.Config.SetBootnodes(bootnodes)

srvs = append(srvs, srv)
Expand Down
4 changes: 2 additions & 2 deletions helper/predeployment/predeployment.go
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,7 @@ func getPredeployAccount(address types.Address, input, deployedBytecode []byte)
snapshot := st.NewSnapshot()

// Create a radix
radix := state.NewTxn(st, snapshot)
radix := state.NewTxn(snapshot)

// Create the contract object for the EVM
contract := runtime.NewContractCreation(
Expand All @@ -141,7 +141,7 @@ func getPredeployAccount(address types.Address, input, deployedBytecode []byte)
config := chain.AllForksEnabled.At(0)

// Create a transition
transition := state.NewTransition(config, radix)
transition := state.NewTransition(config, snapshot, radix)

// Run the transition through the EVM
res := evm.NewEVM().Run(contract, transition, &config)
Expand Down
57 changes: 17 additions & 40 deletions server/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@ import (
"github.com/0xPolygon/polygon-edge/crypto"
"github.com/0xPolygon/polygon-edge/helper/common"
configHelper "github.com/0xPolygon/polygon-edge/helper/config"
"github.com/0xPolygon/polygon-edge/helper/keccak"
"github.com/0xPolygon/polygon-edge/helper/progress"
"github.com/0xPolygon/polygon-edge/jsonrpc"
"github.com/0xPolygon/polygon-edge/network"
Expand Down Expand Up @@ -299,19 +298,14 @@ type txpoolHub struct {
}

func (t *txpoolHub) GetNonce(root types.Hash, addr types.Address) uint64 {
// TODO: Use a function that returns only Account
snap, err := t.state.NewSnapshotAt(root)
if err != nil {
return 0
}

result, ok := snap.Get(keccak.Keccak256(nil, addr.Bytes()))
if !ok {
return 0
}

var account state.Account

if err := account.UnmarshalRlp(result); err != nil {
account, err := snap.GetAccount(addr)
if err != nil {
return 0
}

Expand All @@ -324,14 +318,9 @@ func (t *txpoolHub) GetBalance(root types.Hash, addr types.Address) (*big.Int, e
return nil, fmt.Errorf("unable to get snapshot for root, %w", err)
}

result, ok := snap.Get(keccak.Keccak256(nil, addr.Bytes()))
if !ok {
return big.NewInt(0), nil
}

var account state.Account
if err = account.UnmarshalRlp(result); err != nil {
return nil, fmt.Errorf("unable to unmarshal account from snapshot, %w", err)
account, err := snap.GetAccount(addr)
if err != nil {
return big.NewInt(0), err
}

return account.Balance, nil
Expand Down Expand Up @@ -442,36 +431,22 @@ func (j *jsonRPCHub) GetPeers() int {
return len(j.Server.Peers())
}

func (j *jsonRPCHub) getState(root types.Hash, slot []byte) ([]byte, error) {
// the values in the trie are the hashed objects of the keys
key := keccak.Keccak256(nil, slot)

func (j *jsonRPCHub) getAccountImpl(root types.Hash, addr types.Address) (*state.Account, error) {
snap, err := j.state.NewSnapshotAt(root)
if err != nil {
return nil, err
}

result, ok := snap.Get(key)

if !ok {
return nil, jsonrpc.ErrStateNotFound
}

return result, nil
}

func (j *jsonRPCHub) getAccountImpl(root types.Hash, addr types.Address) (*state.Account, error) {
obj, err := j.getState(root, addr.Bytes())
account, err := snap.GetAccount(addr)
if err != nil {
return nil, err
}

var account state.Account
if err := account.UnmarshalRlp(obj); err != nil {
return nil, err
if account == nil {
return nil, jsonrpc.ErrStateNotFound
}

return &account, nil
return account, nil
}

func (j *jsonRPCHub) GetAccount(root types.Hash, addr types.Address) (*jsonrpc.Account, error) {
Expand All @@ -493,18 +468,20 @@ func (j *jsonRPCHub) GetForksInTime(blockNumber uint64) chain.ForksInTime {
return j.Executor.GetForksInTime(blockNumber)
}

func (j *jsonRPCHub) GetStorage(root types.Hash, addr types.Address, slot types.Hash) ([]byte, error) {
account, err := j.getAccountImpl(root, addr)
func (j *jsonRPCHub) GetStorage(stateRoot types.Hash, addr types.Address, slot types.Hash) ([]byte, error) {
account, err := j.getAccountImpl(stateRoot, addr)
if err != nil {
return nil, err
}

obj, err := j.getState(account.Root, slot.Bytes())
snap, err := j.state.NewSnapshotAt(stateRoot)
if err != nil {
return nil, err
}

return obj, nil
res := snap.GetStorage(addr, account.Root, slot)

return res.Bytes(), nil
}

func (j *jsonRPCHub) GetCode(root types.Hash, addr types.Address) ([]byte, error) {
Expand Down
35 changes: 12 additions & 23 deletions state/executor.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ func NewExecutor(config *chain.Params, s State, logger hclog.Logger) *Executor {

func (e *Executor) WriteGenesis(alloc map[types.Address]*chain.GenesisAccount) types.Hash {
snap := e.state.NewSnapshot()
txn := NewTxn(e.state, snap)
txn := NewTxn(snap)

for addr, account := range alloc {
if account.Balance != nil {
Expand Down Expand Up @@ -138,7 +138,7 @@ func (e *Executor) BeginTxn(
return nil, err
}

newTxn := NewTxn(e.state, auxSnap2)
newTxn := NewTxn(auxSnap2)

env2 := runtime.TxContext{
Coinbase: coinbaseReceiver,
Expand All @@ -153,6 +153,7 @@ func (e *Executor) BeginTxn(
logger: e.logger,
ctx: env2,
state: newTxn,
snap: auxSnap2,
getHash: e.GetHash(header),
auxState: e.state,
config: config,
Expand All @@ -174,6 +175,7 @@ type Transition struct {

// dummy
auxState State
snap Snapshot

config chain.ForksInTime
state *Txn
Expand All @@ -192,10 +194,11 @@ type Transition struct {
precompiles *precompiled.Precompiled
}

func NewTransition(config chain.ForksInTime, radix *Txn) *Transition {
func NewTransition(config chain.ForksInTime, snap Snapshot, radix *Txn) *Transition {
return &Transition{
config: config,
state: radix,
snap: snap,
evm: evm.NewEVM(),
precompiles: precompiled.NewPrecompiled(),
}
Expand Down Expand Up @@ -268,29 +271,19 @@ func (t *Transition) Write(txn *types.Transaction) error {

logs := t.state.Logs()

var root []byte

receipt := &types.Receipt{
CumulativeGasUsed: t.totalGas,
TxHash: txn.Hash,
GasUsed: result.GasUsed,
}

if t.config.Byzantium {
// The suicided accounts are set as deleted for the next iteration
t.state.CleanDeleteObjects(true)
// The suicided accounts are set as deleted for the next iteration
t.state.CleanDeleteObjects(true)

if result.Failed() {
receipt.SetStatus(types.ReceiptFailed)
} else {
receipt.SetStatus(types.ReceiptSuccess)
}
if result.Failed() {
receipt.SetStatus(types.ReceiptFailed)
} else {
objs := t.state.Commit(t.config.EIP155)
ss, aux := t.state.snapshot.Commit(objs)
t.state = NewTxn(t.auxState, ss)
root = aux
receipt.Root = types.BytesToHash(root)
receipt.SetStatus(types.ReceiptSuccess)
}

// if the transaction created a contract, store the creation address in the receipt.
Expand All @@ -309,7 +302,7 @@ func (t *Transition) Write(txn *types.Transaction) error {
// Commit commits the final result
func (t *Transition) Commit() (Snapshot, types.Hash) {
objs := t.state.Commit(t.config.EIP155)
s2, root := t.state.snapshot.Commit(objs)
s2, root := t.snap.Commit(objs)

return s2, types.BytesToHash(root)
}
Expand All @@ -328,10 +321,6 @@ func (t *Transition) addGasPool(amount uint64) {
t.gasPool += amount
}

func (t *Transition) SetTxn(txn *Txn) {
t.state = txn
}

func (t *Transition) Txn() *Txn {
return t.state
}
Expand Down
78 changes: 78 additions & 0 deletions state/immutable-trie/snapshot.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
package itrie

import (
"github.com/0xPolygon/polygon-edge/crypto"
"github.com/0xPolygon/polygon-edge/state"
"github.com/0xPolygon/polygon-edge/types"
"github.com/umbracle/fastrlp"
)

type Snapshot struct {
state *State
trie *Trie
}

var emptyStateHash = types.StringToHash("0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421")

func (s *Snapshot) GetStorage(addr types.Address, root types.Hash, rawkey types.Hash) types.Hash {
var (
err error
trie *Trie
)

if root == emptyStateHash {
trie = s.state.newTrie()
} else {
trie, err = s.state.newTrieAt(root)
if err != nil {
return types.Hash{}
}
}

key := crypto.Keccak256(rawkey.Bytes())

val, ok := trie.Get(key)
if !ok {
return types.Hash{}
}

p := &fastrlp.Parser{}

v, err := p.Parse(val)
if err != nil {
return types.Hash{}
}

res := []byte{}
if res, err = v.GetBytes(res[:0]); err != nil {
return types.Hash{}
}

return types.BytesToHash(res)
}

func (s *Snapshot) GetAccount(addr types.Address) (*state.Account, error) {
key := crypto.Keccak256(addr.Bytes())

data, ok := s.trie.Get(key)
if !ok {
return nil, nil
}

var account state.Account
if err := account.UnmarshalRlp(data); err != nil {
return nil, err
}

return &account, nil
}

func (s *Snapshot) GetCode(hash types.Hash) ([]byte, bool) {
return s.state.GetCode(hash)
}

func (s *Snapshot) Commit(objs []*state.Object) (state.Snapshot, []byte) {
trie, root := s.trie.Commit(objs)

return &Snapshot{trie: trie, state: s.state}, root
}
Loading

0 comments on commit 4f0161f

Please sign in to comment.