diff --git a/e2e/framework/helper.go b/e2e/framework/helper.go index c8cebe319b..6b5eecba54 100644 --- a/e2e/framework/helper.go +++ b/e2e/framework/helper.go @@ -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 @@ -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) diff --git a/helper/predeployment/predeployment.go b/helper/predeployment/predeployment.go index 633a4c457f..ce6d0f4ce6 100644 --- a/helper/predeployment/predeployment.go +++ b/helper/predeployment/predeployment.go @@ -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( @@ -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) diff --git a/server/server.go b/server/server.go index fda49fb68a..40857ce27c 100644 --- a/server/server.go +++ b/server/server.go @@ -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" @@ -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 } @@ -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 @@ -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) { @@ -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) { diff --git a/state/executor.go b/state/executor.go index 865f697a2f..28a937e44c 100644 --- a/state/executor.go +++ b/state/executor.go @@ -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 { @@ -138,7 +138,7 @@ func (e *Executor) BeginTxn( return nil, err } - newTxn := NewTxn(e.state, auxSnap2) + newTxn := NewTxn(auxSnap2) env2 := runtime.TxContext{ Coinbase: coinbaseReceiver, @@ -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, @@ -174,6 +175,7 @@ type Transition struct { // dummy auxState State + snap Snapshot config chain.ForksInTime state *Txn @@ -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(), } @@ -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. @@ -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) } @@ -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 } diff --git a/state/immutable-trie/snapshot.go b/state/immutable-trie/snapshot.go new file mode 100644 index 0000000000..13fd20f8ef --- /dev/null +++ b/state/immutable-trie/snapshot.go @@ -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 +} diff --git a/state/immutable-trie/state.go b/state/immutable-trie/state.go index 65836edec7..d0538e50f2 100644 --- a/state/immutable-trie/state.go +++ b/state/immutable-trie/state.go @@ -1,7 +1,6 @@ package itrie import ( - "errors" "fmt" lru "github.com/hashicorp/golang-lru" @@ -27,6 +26,19 @@ func NewState(storage Storage) *State { } func (s *State) NewSnapshot() state.Snapshot { + return &Snapshot{state: s, trie: s.newTrie()} +} + +func (s *State) NewSnapshotAt(root types.Hash) (state.Snapshot, error) { + t, err := s.newTrieAt(root) + if err != nil { + return nil, err + } + + return &Snapshot{state: s, trie: t}, nil +} + +func (s *State) newTrie() *Trie { t := NewTrie() t.state = s t.storage = s.storage @@ -42,33 +54,32 @@ func (s *State) GetCode(hash types.Hash) ([]byte, bool) { return s.storage.GetCode(hash) } -func (s *State) NewSnapshotAt(root types.Hash) (state.Snapshot, error) { +func (s *State) newTrieAt(root types.Hash) (*Trie, error) { if root == types.EmptyRootHash { // empty state - return s.NewSnapshot(), nil + return s.newTrie(), nil } tt, ok := s.cache.Get(root) if ok { t, ok := tt.(*Trie) if !ok { - return nil, errors.New("invalid type assertion") + return nil, fmt.Errorf("invalid type assertion on root: %s", root) } t.state = s trie, ok := tt.(*Trie) if !ok { - return nil, errors.New("invalid type assertion") + return nil, fmt.Errorf("invalid type assertion on root: %s", root) } return trie, nil } n, ok, err := GetNode(root.Bytes(), s.storage) - if err != nil { - return nil, err + return nil, fmt.Errorf("failed to get storage root %s: %w", root, err) } if !ok { diff --git a/state/immutable-trie/state_test.go b/state/immutable-trie/state_test.go index 85021442dc..e4e2ce4a98 100644 --- a/state/immutable-trie/state_test.go +++ b/state/immutable-trie/state_test.go @@ -10,10 +10,10 @@ func TestState(t *testing.T) { state.TestState(t, buildPreState) } -func buildPreState(pre state.PreStates) (state.State, state.Snapshot) { +func buildPreState(pre state.PreStates) state.Snapshot { storage := NewMemoryStorage() st := NewState(storage) snap := st.NewSnapshot() - return st, snap + return snap } diff --git a/state/immutable-trie/trie.go b/state/immutable-trie/trie.go index f6076840b5..d802119cd0 100644 --- a/state/immutable-trie/trie.go +++ b/state/immutable-trie/trie.go @@ -118,7 +118,7 @@ var accountArenaPool fastrlp.ArenaPool var stateArenaPool fastrlp.ArenaPool // TODO, Remove once we do update in fastrlp -func (t *Trie) Commit(objs []*state.Object) (state.Snapshot, []byte) { +func (t *Trie) Commit(objs []*state.Object) (*Trie, []byte) { // Create an insertion batch for all the entries batch := t.storage.Batch() @@ -143,16 +143,11 @@ func (t *Trie) Commit(objs []*state.Object) (state.Snapshot, []byte) { } if len(obj.Storage) != 0 { - localSnapshot, err := t.state.NewSnapshotAt(obj.Root) + trie, err := t.state.newTrieAt(obj.Root) if err != nil { panic(err) } - trie, ok := localSnapshot.(*Trie) - if !ok { - panic("invalid type assertion") - } - localTxn := trie.Txn() localTxn.batch = batch diff --git a/state/state.go b/state/state.go index ded9038209..36c0ac01b0 100644 --- a/state/state.go +++ b/state/state.go @@ -19,13 +19,9 @@ type State interface { } type Snapshot interface { - Get(k []byte) ([]byte, bool) - Commit(objs []*Object) (Snapshot, []byte) -} + readSnapshot -// account trie -type accountTrie interface { - Get(k []byte) ([]byte, bool) + Commit(objs []*Object) (Snapshot, []byte) } // Account is the account reference in the ethereum state @@ -34,7 +30,6 @@ type Account struct { Balance *big.Int Root types.Hash CodeHash []byte - Trie accountTrie } func (a *Account) MarshalWith(ar *fastrlp.Arena) *fastrlp.Value { @@ -103,7 +98,6 @@ func (a *Account) Copy() *Account { aa.Nonce = a.Nonce aa.CodeHash = a.CodeHash aa.Root = a.Root - aa.Trie = a.Trie return aa } @@ -124,30 +118,6 @@ func (s *StateObject) Empty() bool { return s.Account.Nonce == 0 && s.Account.Balance.Sign() == 0 && bytes.Equal(s.Account.CodeHash, emptyCodeHash) } -var stateStateParserPool fastrlp.ParserPool - -func (s *StateObject) GetCommitedState(key types.Hash) types.Hash { - val, ok := s.Account.Trie.Get(key.Bytes()) - if !ok { - return types.Hash{} - } - - p := stateStateParserPool.Get() - defer stateStateParserPool.Put(p) - - 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) -} - // Copy makes a copy of the state object func (s *StateObject) Copy() *StateObject { ss := new(StateObject) diff --git a/state/testing.go b/state/testing.go index f7f2371237..6880725b12 100644 --- a/state/testing.go +++ b/state/testing.go @@ -34,7 +34,7 @@ type PreState struct { // PreStates is a set of pre states type PreStates map[types.Address]*PreState -type buildPreState func(p PreStates) (State, Snapshot) +type buildPreState func(p PreStates) Snapshot // TestState tests a set of tests on a state func TestState(t *testing.T, buildPreState buildPreState) { @@ -106,8 +106,8 @@ func TestState(t *testing.T, buildPreState buildPreState) { func testDeleteCommonStateRoot(t *testing.T, buildPreState buildPreState) { t.Helper() - state, snap := buildPreState(nil) - txn := newTxn(state, snap) + snap := buildPreState(nil) + txn := newTxn(snap) txn.SetNonce(addr1, 1) txn.SetState(addr1, hash0, hash1) @@ -120,14 +120,14 @@ func testDeleteCommonStateRoot(t *testing.T, buildPreState buildPreState) { txn.SetState(addr2, hash2, hash1) snap2, _ := snap.Commit(txn.Commit(false)) - txn2 := newTxn(state, snap2) + txn2 := newTxn(snap2) txn2.SetState(addr1, hash0, hash0) txn2.SetState(addr1, hash1, hash0) snap3, _ := snap2.Commit(txn2.Commit(false)) - txn3 := newTxn(state, snap3) + txn3 := newTxn(snap3) assert.Equal(t, hash1, txn3.GetState(addr1, hash2)) assert.Equal(t, hash1, txn3.GetState(addr2, hash0)) assert.Equal(t, hash1, txn3.GetState(addr2, hash1)) @@ -137,8 +137,8 @@ func testDeleteCommonStateRoot(t *testing.T, buildPreState buildPreState) { func testWriteState(t *testing.T, buildPreState buildPreState) { t.Helper() - state, snap := buildPreState(nil) - txn := newTxn(state, snap) + snap := buildPreState(nil) + txn := newTxn(snap) txn.SetState(addr1, hash1, hash1) txn.SetState(addr1, hash2, hash2) @@ -148,7 +148,7 @@ func testWriteState(t *testing.T, buildPreState buildPreState) { snap, _ = snap.Commit(txn.Commit(false)) - txn = newTxn(state, snap) + txn = newTxn(snap) assert.Equal(t, hash1, txn.GetState(addr1, hash1)) assert.Equal(t, hash2, txn.GetState(addr1, hash2)) } @@ -156,24 +156,24 @@ func testWriteState(t *testing.T, buildPreState buildPreState) { func testWriteEmptyState(t *testing.T, buildPreState buildPreState) { t.Helper() // Create account and write empty state - state, snap := buildPreState(nil) - txn := newTxn(state, snap) + snap := buildPreState(nil) + txn := newTxn(snap) // Without EIP150 the data is added txn.SetState(addr1, hash1, hash0) snap, _ = snap.Commit(txn.Commit(false)) - txn = newTxn(state, snap) + txn = newTxn(snap) assert.True(t, txn.Exist(addr1)) - _, snap = buildPreState(nil) - txn = newTxn(state, snap) + snap = buildPreState(nil) + txn = newTxn(snap) // With EIP150 the empty data is removed txn.SetState(addr1, hash1, hash0) snap, _ = snap.Commit(txn.Commit(true)) - txn = newTxn(state, snap) + txn = newTxn(snap) assert.False(t, txn.Exist(addr1)) } @@ -181,16 +181,16 @@ func testUpdateStateWithEmpty(t *testing.T, buildPreState buildPreState) { t.Helper() // If the state (in prestate) is updated to empty it should be removed - state, snap := buildPreState(defaultPreState) + snap := buildPreState(defaultPreState) - txn := newTxn(state, snap) + txn := newTxn(snap) txn.SetState(addr1, hash1, hash0) // TODO, test with false (should not be deleted) // TODO, test with balance on the account and nonce snap, _ = snap.Commit(txn.Commit(true)) - txn = newTxn(state, snap) + txn = newTxn(snap) assert.False(t, txn.Exist(addr1)) } @@ -198,22 +198,22 @@ func testSuicideAccountInPreState(t *testing.T, buildPreState buildPreState) { t.Helper() // Suicide an account created in the prestate - state, snap := buildPreState(defaultPreState) + snap := buildPreState(defaultPreState) - txn := newTxn(state, snap) + txn := newTxn(snap) txn.Suicide(addr1) snap, _ = snap.Commit(txn.Commit(true)) - txn = newTxn(state, snap) + txn = newTxn(snap) assert.False(t, txn.Exist(addr1)) } func testSuicideAccount(t *testing.T, buildPreState buildPreState) { t.Helper() // Create a new account and suicide it - state, snap := buildPreState(nil) + snap := buildPreState(nil) - txn := newTxn(state, snap) + txn := newTxn(snap) txn.SetState(addr1, hash1, hash1) txn.Suicide(addr1) @@ -222,16 +222,16 @@ func testSuicideAccount(t *testing.T, buildPreState buildPreState) { snap, _ = snap.Commit(txn.Commit(true)) - txn = newTxn(state, snap) + txn = newTxn(snap) assert.False(t, txn.Exist(addr1)) } func testSuicideAccountWithData(t *testing.T, buildPreState buildPreState) { t.Helper() // Data (nonce, balance, code) from a suicided account should be empty - state, snap := buildPreState(nil) + snap := buildPreState(nil) - txn := newTxn(state, snap) + txn := newTxn(snap) code := []byte{0x1, 0x2, 0x3} @@ -243,7 +243,7 @@ func testSuicideAccountWithData(t *testing.T, buildPreState buildPreState) { txn.Suicide(addr1) snap, _ = snap.Commit(txn.Commit(true)) - txn = newTxn(state, snap) + txn = newTxn(snap) assert.Equal(t, big.NewInt(0), txn.GetBalance(addr1)) assert.Equal(t, uint64(0), txn.GetNonce(addr1)) @@ -259,23 +259,23 @@ func testSuicideAccountWithData(t *testing.T, buildPreState buildPreState) { func testSuicideCoinbase(t *testing.T, buildPreState buildPreState) { t.Helper() // Suicide the coinbase of the block - state, snap := buildPreState(defaultPreState) + snap := buildPreState(defaultPreState) - txn := newTxn(state, snap) + txn := newTxn(snap) txn.Suicide(addr1) txn.AddSealingReward(addr1, big.NewInt(10)) snap, _ = snap.Commit(txn.Commit(true)) - txn = newTxn(state, snap) + txn = newTxn(snap) assert.Equal(t, big.NewInt(10), txn.GetBalance(addr1)) } func testSuicideWithIntermediateCommit(t *testing.T, buildPreState buildPreState) { t.Helper() - state, snap := buildPreState(defaultPreState) + snap := buildPreState(defaultPreState) - txn := newTxn(state, snap) + txn := newTxn(snap) txn.SetNonce(addr1, 10) txn.Suicide(addr1) @@ -292,9 +292,9 @@ func testRestartRefunds(t *testing.T, buildPreState buildPreState) { t.Helper() // refunds are only valid per single txn so after each // intermediateCommit they have to be restarted - state, snap := buildPreState(nil) + snap := buildPreState(nil) - txn := newTxn(state, snap) + txn := newTxn(snap) txn.AddRefund(1000) assert.Equal(t, uint64(1000), txn.GetRefund()) @@ -314,27 +314,27 @@ func testChangePrestateAccountBalanceToZero(t *testing.T, buildPreState buildPre }, } - state, snap := buildPreState(preState) + snap := buildPreState(preState) - txn := newTxn(state, snap) + txn := newTxn(snap) txn.SetBalance(addr1, big.NewInt(0)) snap, _ = snap.Commit(txn.Commit(true)) - txn = newTxn(state, snap) + txn = newTxn(snap) assert.False(t, txn.Exist(addr1)) } func testChangeAccountBalanceToZero(t *testing.T, buildPreState buildPreState) { t.Helper() // If the balance of the account changes to zero the account is deleted - state, snap := buildPreState(nil) + snap := buildPreState(nil) - txn := newTxn(state, snap) + txn := newTxn(snap) txn.SetBalance(addr1, big.NewInt(10)) txn.SetBalance(addr1, big.NewInt(0)) snap, _ = snap.Commit(txn.Commit(true)) - txn = newTxn(state, snap) + txn = newTxn(snap) assert.False(t, txn.Exist(addr1)) } diff --git a/state/txn.go b/state/txn.go index 755a9d5e23..4084809822 100644 --- a/state/txn.go +++ b/state/txn.go @@ -8,13 +8,18 @@ import ( "github.com/0xPolygon/polygon-edge/chain" "github.com/0xPolygon/polygon-edge/crypto" - "github.com/0xPolygon/polygon-edge/helper/keccak" "github.com/0xPolygon/polygon-edge/state/runtime" "github.com/0xPolygon/polygon-edge/types" ) var emptyStateHash = types.StringToHash("0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421") +type readSnapshot interface { + GetStorage(addr types.Address, root types.Hash, key types.Hash) types.Hash + GetAccount(addr types.Address) (*Account, error) + GetCode(hash types.Hash) ([]byte, bool) +} + var ( // logIndex is the index of the logs in the trie logIndex = types.BytesToHash([]byte{2}).Bytes() @@ -25,45 +30,33 @@ var ( // Txn is a reference of the state type Txn struct { - snapshot Snapshot - state State + snapshot readSnapshot snapshots []*iradix.Tree txn *iradix.Txn codeCache *lru.Cache - hash *keccak.Keccak } -func NewTxn(state State, snapshot Snapshot) *Txn { - return newTxn(state, snapshot) +func NewTxn(snapshot Snapshot) *Txn { + return newTxn(snapshot) } func (txn *Txn) GetRadix() *iradix.Txn { return txn.txn } -func newTxn(state State, snapshot Snapshot) *Txn { +func newTxn(snapshot readSnapshot) *Txn { i := iradix.New() codeCache, _ := lru.New(20) return &Txn{ snapshot: snapshot, - state: state, snapshots: []*iradix.Tree{}, txn: i.Txn(), codeCache: codeCache, - hash: keccak.NewKeccak256(), } } -func (txn *Txn) hashit(src []byte) []byte { - txn.hash.Reset() - txn.hash.Write(src) - // hashit is used to make queries so we do not need to - // make copies of the result - return txn.hash.Read() -} - // Snapshot takes a snapshot at this point in time func (txn *Txn) Snapshot() int { t := txn.txn.CommitOnly() @@ -106,28 +99,15 @@ func (txn *Txn) getStateObject(addr types.Address) (*StateObject, bool) { return obj.Copy(), true } - data, ok := txn.snapshot.Get(txn.hashit(addr.Bytes())) - if !ok { + account, err := txn.snapshot.GetAccount(addr) + if err != nil { return nil, false } - var err error - - var account Account - if err = account.UnmarshalRlp(data); err != nil { + if account == nil { return nil, false } - // Load trie from memory if there is some state - if account.Root == emptyStateHash { - account.Trie = txn.state.NewSnapshot() - } else { - account.Trie, err = txn.state.NewSnapshotAt(account.Root) - if err != nil { - return nil, false - } - } - obj := &StateObject{ Account: account.Copy(), } @@ -141,7 +121,6 @@ func (txn *Txn) upsertAccount(addr types.Address, create bool, f func(object *St object = &StateObject{ Account: &Account{ Balance: big.NewInt(0), - Trie: txn.state.NewSnapshot(), CodeHash: emptyCodeHash, Root: emptyStateHash, }, @@ -360,10 +339,7 @@ func (txn *Txn) GetState(addr types.Address, key types.Hash) types.Hash { } } - // If the object was not found in the radix trie due to no state update, we fetch it from the trie tre - k := txn.hashit(key.Bytes()) - - return object.GetCommitedState(types.BytesToHash(k)) + return txn.snapshot.GetStorage(addr, object.Account.Root, key) } // Nonce @@ -420,7 +396,7 @@ func (txn *Txn) GetCode(addr types.Address) []byte { return v.([]byte) } - code, _ := txn.state.GetCode(types.BytesToHash(object.Account.CodeHash)) + code, _ := txn.snapshot.GetCode(types.BytesToHash(object.Account.CodeHash)) txn.codeCache.Add(addr, code) return code @@ -504,7 +480,7 @@ func (txn *Txn) GetCommittedState(addr types.Address, key types.Hash) types.Hash return types.Hash{} } - return obj.GetCommitedState(types.BytesToHash(txn.hashit(key.Bytes()))) + return txn.snapshot.GetStorage(addr, obj.Account.Root, key) } func (txn *Txn) TouchAccount(addr types.Address) { @@ -534,7 +510,6 @@ func newStateObject(txn *Txn) *StateObject { return &StateObject{ Account: &Account{ Balance: big.NewInt(0), - Trie: txn.state.NewSnapshot(), CodeHash: emptyCodeHash, Root: emptyStateHash, }, @@ -545,7 +520,6 @@ func (txn *Txn) CreateAccount(addr types.Address) { obj := &StateObject{ Account: &Account{ Balance: big.NewInt(0), - Trie: txn.state.NewSnapshot(), CodeHash: emptyCodeHash, Root: emptyStateHash, }, diff --git a/state/txn_test.go b/state/txn_test.go index 0a3a56a1f2..d1ff74b519 100644 --- a/state/txn_test.go +++ b/state/txn_test.go @@ -1,128 +1,56 @@ package state import ( - "bytes" - "crypto/rand" "fmt" "math/big" "testing" - "github.com/0xPolygon/polygon-edge/helper/hex" "github.com/0xPolygon/polygon-edge/types" "github.com/stretchr/testify/assert" - "github.com/umbracle/fastrlp" - "golang.org/x/crypto/sha3" ) -type mockState struct { - snapshots map[types.Hash]Snapshot +type mockSnapshot struct { + state map[types.Address]*PreState } -func (m *mockState) NewSnapshotAt(root types.Hash) (Snapshot, error) { - t, ok := m.snapshots[root] +func (m *mockSnapshot) GetStorage(addr types.Address, root types.Hash, key types.Hash) types.Hash { + raw, ok := m.state[addr] if !ok { - return nil, fmt.Errorf("not found") + return types.Hash{} } - return t, nil -} - -func (m *mockState) NewSnapshot() Snapshot { - return &mockSnapshot{data: map[string][]byte{}} -} - -func (m *mockState) GetCode(hash types.Hash) ([]byte, bool) { - panic("Not implemented in tests") -} - -type mockSnapshot struct { - data map[string][]byte -} - -func (m *mockSnapshot) Get(k []byte) ([]byte, bool) { - v, ok := m.data[hex.EncodeToHex(k)] - - return v, ok -} + res, ok := raw.State[key] + if !ok { + return types.Hash{} + } -func (m *mockSnapshot) Commit(objs []*Object) (Snapshot, []byte) { - panic("Not implemented in tests") + return res } -func newStateWithPreState(preState map[types.Address]*PreState) (*mockState, *mockSnapshot) { - state := &mockState{ - snapshots: map[types.Hash]Snapshot{}, - } - snapshot := &mockSnapshot{ - data: map[string][]byte{}, +func (m *mockSnapshot) GetAccount(addr types.Address) (*Account, error) { + raw, ok := m.state[addr] + if !ok { + return nil, fmt.Errorf("account not found") } - ar := &fastrlp.Arena{} - - for addr, p := range preState { - account, snap := buildMockPreState(p) - if snap != nil { - state.snapshots[account.Root] = snap - } - - v := account.MarshalWith(ar) - accountRlp := v.MarshalTo(nil) - /* - accountRlp, err := rlp.EncodeToBytes(account) - if err != nil { - panic(err) - } - */ - snapshot.data[hex.EncodeToHex(hashit(addr.Bytes()))] = accountRlp + acct := &Account{ + Balance: new(big.Int).SetUint64(raw.Balance), + Nonce: raw.Nonce, } - return state, snapshot + return acct, nil } -func newTestTxn(p map[types.Address]*PreState) *Txn { - return newTxn(newStateWithPreState(p)) +func (m *mockSnapshot) GetCode(hash types.Hash) ([]byte, bool) { + return nil, false } -func buildMockPreState(p *PreState) (*Account, *mockSnapshot) { - var snap *mockSnapshot - - root := emptyStateHash - - ar := &fastrlp.Arena{} - - if p.State != nil { - data := map[string][]byte{} - - for k, v := range p.State { - vv := ar.NewBytes(bytes.TrimLeft(v.Bytes(), "\x00")) - data[k.String()] = vv.MarshalTo(nil) - } - - root = randomHash() - snap = &mockSnapshot{ - data: data, - } - } - - account := &Account{ - Nonce: p.Nonce, - Balance: big.NewInt(int64(p.Balance)), - Root: root, - } - - return account, snap +func newStateWithPreState(preState map[types.Address]*PreState) readSnapshot { + return &mockSnapshot{state: preState} } -const letterBytes = "0123456789ABCDEF" - -func randomHash() types.Hash { - b := make([]byte, types.HashLength) - for i := range b { - randNum, _ := rand.Int(rand.Reader, big.NewInt(int64(len(letterBytes)))) - b[i] = letterBytes[randNum.Int64()] - } - - return types.BytesToHash(b) +func newTestTxn(p map[types.Address]*PreState) *Txn { + return newTxn(newStateWithPreState(p)) } func TestSnapshotUpdateData(t *testing.T) { @@ -138,10 +66,3 @@ func TestSnapshotUpdateData(t *testing.T) { txn.RevertToSnapshot(ss) assert.Equal(t, hash1, txn.GetState(addr1, hash1)) } - -func hashit(k []byte) []byte { - h := sha3.NewLegacyKeccak256() - h.Write(k) - - return h.Sum(nil) -} diff --git a/tests/testing.go b/tests/testing.go index 5efb83ec37..f24d2c6755 100644 --- a/tests/testing.go +++ b/tests/testing.go @@ -228,7 +228,7 @@ func buildState( s := itrie.NewState(itrie.NewMemoryStorage()) snap := s.NewSnapshot() - txn := state.NewTxn(s, snap) + txn := state.NewTxn(snap) for addr, alloc := range allocs { txn.CreateAccount(addr)