From 0b1d547bf7fda1ea14e0968957f511b00709c77b Mon Sep 17 00:00:00 2001 From: drklee3 Date: Mon, 4 Mar 2024 14:03:24 -0800 Subject: [PATCH 01/22] test: Update apphash test for suspected issue --- x/evm/keeper/state_transition_test.go | 59 ++++++++++++++++++++++----- 1 file changed, 48 insertions(+), 11 deletions(-) diff --git a/x/evm/keeper/state_transition_test.go b/x/evm/keeper/state_transition_test.go index 635c5bdd19..500dba4088 100644 --- a/x/evm/keeper/state_transition_test.go +++ b/x/evm/keeper/state_transition_test.go @@ -727,6 +727,31 @@ func (suite *KeeperTestSuite) TestGetProposerAddress() { } } +var ( + blockHash common.Hash = common.BigToHash(big.NewInt(9999)) + emptyTxConfig statedb.TxConfig = statedb.NewEmptyTxConfig(blockHash) +) + +/* +func sortTraces(buf bytes.Buffer) { + // Iterate over buff by each newline + // module -> + traces := make(map[string][]string) + + scanner := bufio.NewScanner(strings.NewReader(buf.String())) + for scanner.Scan() { + s := scanner.Text() + // parse as json + var lineJson interface{} + json.Unmarshal([]byte(s), &lineJson) + } + + if err := scanner.Err(); err != nil { + panic(err) + } +} +*/ + func (suite *KeeperTestSuite) TestConsistency() { var tracer bytes.Buffer suite.App.SetCommitMultiStoreTracer(&tracer) @@ -738,24 +763,39 @@ func (suite *KeeperTestSuite) TestConsistency() { "tracer should be enabled", ) - contractAddr := suite.DeployTestContract(suite.T(), suite.Address, big.NewInt(100)) - suite.Require().NotEmpty(tracer.Bytes(), "tracer should have recorded something") + // evm store keys prefixes: + // Code = 1 + // Storage = 2 + // addr1 := common.BigToAddress(big.NewInt(1)) + addr2 := common.BigToAddress(big.NewInt(2)) - _, _, err := suite.TransferERC20Token(contractAddr, suite.Address, common.Address{1}, big.NewInt(1000)) - suite.Require().Error(err) + // Suspect: Inconsistent write orders to underlying ctx store: + // Journal based - sorted by addresses then inserted/deleted/etc (ascending order) + // - addr1 then addr2 + // CacheCtx based - sorted by cosmos store keys which have prefixes and different orders + // - code then storage + db := statedb.New(suite.Ctx, suite.App.EvmKeeper, emptyTxConfig) + // db.SetCode(addr1, []byte{1, 2, 3}) + db.SetState(addr2, common.BigToHash(big.NewInt(1)), common.BigToHash(big.NewInt(2))) + + suite.Require().NoError(db.Commit()) res := suite.Commit() + suite.T().Logf("commitID.Hash: %x", res.Data) + + // acc1 := suite.App.AccountKeeper.GetAccount(suite.Ctx, sdk.AccAddress(addr1.Bytes())) + // suite.Require().NotNil(acc1) + + // suite.T().Logf("AccNumber: %v", acc1.GetAccountNumber()) // Log the tracer contents suite.T().Logf("Tracer (%v): %s", tracer.Len(), tracer.String()) // Write tracer contents to file - err = os.WriteFile(fmt.Sprintf("tracer-ctx-%v.log", time.Now().Unix()), tracer.Bytes(), 0644) + err := os.WriteFile(fmt.Sprintf("ctx-state-%v-%x.log", time.Now().Unix(), res.Data), tracer.Bytes(), 0644) suite.Require().NoError(err) - suite.T().Logf("commitID.Hash: %x", res.Data) - - expectedHash := common.Hex2Bytes("10eaacd8ba1a2763c7ef1ac1090f7687baa299d2330ea1d593860a7aece3ecb5") + expectedHash := common.Hex2Bytes("87ade13f1a5f21f0346ce822a210caf29f22ffcc501e9969dddebf5b382f926a") suite.Require().Equalf( expectedHash, res.Data, @@ -763,7 +803,4 @@ func (suite *KeeperTestSuite) TestConsistency() { expectedHash, res.Data, ) - - acc := suite.App.EvmKeeper.GetAccount(suite.Ctx, contractAddr) - suite.Require().True(acc.IsContract()) } From b9acaf86274c194531d89e8f45daaa73acc4ae26 Mon Sep 17 00:00:00 2001 From: drklee3 Date: Tue, 5 Mar 2024 13:00:37 -0800 Subject: [PATCH 02/22] temp disable account clearning --- x/evm/keeper/state_transition_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/x/evm/keeper/state_transition_test.go b/x/evm/keeper/state_transition_test.go index 500dba4088..6b71d675d6 100644 --- a/x/evm/keeper/state_transition_test.go +++ b/x/evm/keeper/state_transition_test.go @@ -792,7 +792,7 @@ func (suite *KeeperTestSuite) TestConsistency() { suite.T().Logf("Tracer (%v): %s", tracer.Len(), tracer.String()) // Write tracer contents to file - err := os.WriteFile(fmt.Sprintf("ctx-state-%v-%x.log", time.Now().Unix(), res.Data), tracer.Bytes(), 0644) + err := os.WriteFile(fmt.Sprintf("tracer-ctx-setstate-%v-%x.log", time.Now().Unix(), res.Data), tracer.Bytes(), 0644) suite.Require().NoError(err) expectedHash := common.Hex2Bytes("87ade13f1a5f21f0346ce822a210caf29f22ffcc501e9969dddebf5b382f926a") From dd184c8d7c3816b225dfa6e4945b06c220493002 Mon Sep 17 00:00:00 2001 From: drklee3 Date: Wed, 6 Mar 2024 11:43:14 -0800 Subject: [PATCH 03/22] add journal based statedb for comparison testing --- x/evm/statedb_legacy/access_list.go | 118 +++++++ x/evm/statedb_legacy/config.go | 62 ++++ x/evm/statedb_legacy/interfaces.go | 49 +++ x/evm/statedb_legacy/journal.go | 243 ++++++++++++++ x/evm/statedb_legacy/state_object.go | 244 ++++++++++++++ x/evm/statedb_legacy/statedb.go | 479 +++++++++++++++++++++++++++ 6 files changed, 1195 insertions(+) create mode 100644 x/evm/statedb_legacy/access_list.go create mode 100644 x/evm/statedb_legacy/config.go create mode 100644 x/evm/statedb_legacy/interfaces.go create mode 100644 x/evm/statedb_legacy/journal.go create mode 100644 x/evm/statedb_legacy/state_object.go create mode 100644 x/evm/statedb_legacy/statedb.go diff --git a/x/evm/statedb_legacy/access_list.go b/x/evm/statedb_legacy/access_list.go new file mode 100644 index 0000000000..21e34adcff --- /dev/null +++ b/x/evm/statedb_legacy/access_list.go @@ -0,0 +1,118 @@ +// Copyright 2020 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package statedb_legacy + +import ( + "github.com/ethereum/go-ethereum/common" +) + +type accessList struct { + addresses map[common.Address]int + slots []map[common.Hash]struct{} +} + +// ContainsAddress returns true if the address is in the access list. +func (al *accessList) ContainsAddress(address common.Address) bool { + _, ok := al.addresses[address] + return ok +} + +// Contains checks if a slot within an account is present in the access list, returning +// separate flags for the presence of the account and the slot respectively. +func (al *accessList) Contains(address common.Address, slot common.Hash) (addressPresent bool, slotPresent bool) { + idx, ok := al.addresses[address] + if !ok { + // no such address (and hence zero slots) + return false, false + } + if idx == -1 { + // address yes, but no slots + return true, false + } + _, slotPresent = al.slots[idx][slot] + return true, slotPresent +} + +// newAccessList creates a new accessList. +func newAccessList() *accessList { + return &accessList{ + addresses: make(map[common.Address]int), + } +} + +// AddAddress adds an address to the access list, and returns 'true' if the operation +// caused a change (addr was not previously in the list). +func (al *accessList) AddAddress(address common.Address) bool { + if _, present := al.addresses[address]; present { + return false + } + al.addresses[address] = -1 + return true +} + +// AddSlot adds the specified (addr, slot) combo to the access list. +// Return values are: +// - address added +// - slot added +// For any 'true' value returned, a corresponding journal entry must be made. +func (al *accessList) AddSlot(address common.Address, slot common.Hash) (addrChange bool, slotChange bool) { + idx, addrPresent := al.addresses[address] + if !addrPresent || idx == -1 { + // Address not present, or addr present but no slots there + al.addresses[address] = len(al.slots) + slotmap := map[common.Hash]struct{}{slot: {}} + al.slots = append(al.slots, slotmap) + return !addrPresent, true + } + // There is already an (address,slot) mapping + slotmap := al.slots[idx] + if _, ok := slotmap[slot]; !ok { + slotmap[slot] = struct{}{} + // Journal add slot change + return false, true + } + // No changes required + return false, false +} + +// DeleteSlot removes an (address, slot)-tuple from the access list. +// This operation needs to be performed in the same order as the addition happened. +// This method is meant to be used by the journal, which maintains ordering of +// operations. +func (al *accessList) DeleteSlot(address common.Address, slot common.Hash) { + idx, addrOk := al.addresses[address] + if !addrOk { + panic("reverting slot change, address not present in list") + } + slotmap := al.slots[idx] + delete(slotmap, slot) + // If that was the last (first) slot, remove it + // Since additions and rollbacks are always performed in order, + // we can delete the item without worrying about screwing up later indices + if len(slotmap) == 0 { + al.slots = al.slots[:idx] + al.addresses[address] = -1 + } +} + +// DeleteAddress removes an address from the access list. This operation +// needs to be performed in the same order as the addition happened. +// This method is meant to be used by the journal, which maintains ordering of +// operations. +func (al *accessList) DeleteAddress(address common.Address) { + delete(al.addresses, address) +} diff --git a/x/evm/statedb_legacy/config.go b/x/evm/statedb_legacy/config.go new file mode 100644 index 0000000000..0e7dd60036 --- /dev/null +++ b/x/evm/statedb_legacy/config.go @@ -0,0 +1,62 @@ +// Copyright 2021 Evmos Foundation +// This file is part of Evmos' Ethermint library. +// +// The Ethermint library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The Ethermint library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the Ethermint library. If not, see https://github.com/evmos/ethermint/blob/main/LICENSE +package statedb_legacy + +import ( + "math/big" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/params" + "github.com/evmos/ethermint/x/evm/types" +) + +// TxConfig encapulates the readonly information of current tx for `StateDB`. +type TxConfig struct { + BlockHash common.Hash // hash of current block + TxHash common.Hash // hash of current tx + TxIndex uint // the index of current transaction + LogIndex uint // the index of next log within current block +} + +// NewTxConfig returns a TxConfig +func NewTxConfig(bhash, thash common.Hash, txIndex, logIndex uint) TxConfig { + return TxConfig{ + BlockHash: bhash, + TxHash: thash, + TxIndex: txIndex, + LogIndex: logIndex, + } +} + +// NewEmptyTxConfig construct an empty TxConfig, +// used in context where there's no transaction, e.g. `eth_call`/`eth_estimateGas`. +func NewEmptyTxConfig(bhash common.Hash) TxConfig { + return TxConfig{ + BlockHash: bhash, + TxHash: common.Hash{}, + TxIndex: 0, + LogIndex: 0, + } +} + +// EVMConfig encapsulates common parameters needed to create an EVM to execute a message +// It's mainly to reduce the number of method parameters +type EVMConfig struct { + Params types.Params + ChainConfig *params.ChainConfig + CoinBase common.Address + BaseFee *big.Int +} diff --git a/x/evm/statedb_legacy/interfaces.go b/x/evm/statedb_legacy/interfaces.go new file mode 100644 index 0000000000..f0d63dcad2 --- /dev/null +++ b/x/evm/statedb_legacy/interfaces.go @@ -0,0 +1,49 @@ +// Copyright 2021 Evmos Foundation +// This file is part of Evmos' Ethermint library. +// +// The Ethermint library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The Ethermint library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the Ethermint library. If not, see https://github.com/evmos/ethermint/blob/main/LICENSE +package statedb_legacy + +import ( + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/vm" + "github.com/evmos/ethermint/x/evm/statedb" +) + +// ExtStateDB defines an extension to the interface provided by the go-ethereum +// codebase to support additional state transition functionalities. In particular +// it supports appending a new entry to the state journal through +// AppendJournalEntry so that the state can be reverted after running +// stateful precompiled contracts. +type ExtStateDB interface { + vm.StateDB + AppendJournalEntry(JournalEntry) +} + +// Keeper provide underlying storage of StateDB +type Keeper interface { + // Read methods + GetAccount(ctx sdk.Context, addr common.Address) *statedb.Account + GetState(ctx sdk.Context, addr common.Address, key common.Hash) common.Hash + GetCode(ctx sdk.Context, codeHash common.Hash) []byte + // the callback returns false to break early + ForEachStorage(ctx sdk.Context, addr common.Address, cb func(key, value common.Hash) bool) + + // Write methods, only called by `StateDB.Commit()` + SetAccount(ctx sdk.Context, addr common.Address, account statedb.Account) error + SetState(ctx sdk.Context, addr common.Address, key common.Hash, value common.Hash) + SetCode(ctx sdk.Context, codeHash []byte, code []byte) + DeleteAccount(ctx sdk.Context, addr common.Address) error +} diff --git a/x/evm/statedb_legacy/journal.go b/x/evm/statedb_legacy/journal.go new file mode 100644 index 0000000000..da9f90da3a --- /dev/null +++ b/x/evm/statedb_legacy/journal.go @@ -0,0 +1,243 @@ +// Copyright 2016 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package statedb_legacy + +import ( + "bytes" + "math/big" + "sort" + + "github.com/ethereum/go-ethereum/common" +) + +// JournalEntry is a modification entry in the state change journal that can be +// Reverted on demand. +type JournalEntry interface { + // Revert undoes the changes introduced by this journal entry. + Revert(*StateDB) + + // Dirtied returns the Ethereum address modified by this journal entry. + Dirtied() *common.Address +} + +// journal contains the list of state modifications applied since the last state +// commit. These are tracked to be able to be reverted in the case of an execution +// exception or request for reversal. +type journal struct { + entries []JournalEntry // Current changes tracked by the journal + dirties map[common.Address]int // Dirty accounts and the number of changes +} + +// newJournal creates a new initialized journal. +func newJournal() *journal { + return &journal{ + dirties: make(map[common.Address]int), + } +} + +// sortedDirties sort the dirty addresses for deterministic iteration +func (j *journal) sortedDirties() []common.Address { + keys := make([]common.Address, len(j.dirties)) + i := 0 + for k := range j.dirties { + keys[i] = k + i++ + } + sort.Slice(keys, func(i, j int) bool { + return bytes.Compare(keys[i].Bytes(), keys[j].Bytes()) < 0 + }) + return keys +} + +// append inserts a new modification entry to the end of the change journal. +func (j *journal) append(entry JournalEntry) { + j.entries = append(j.entries, entry) + if addr := entry.Dirtied(); addr != nil { + j.dirties[*addr]++ + } +} + +// Revert undoes a batch of journalled modifications along with any Reverted +// dirty handling too. +func (j *journal) Revert(statedb *StateDB, snapshot int) { + for i := len(j.entries) - 1; i >= snapshot; i-- { + // Undo the changes made by the operation + j.entries[i].Revert(statedb) + + // Drop any dirty tracking induced by the change + if addr := j.entries[i].Dirtied(); addr != nil { + if j.dirties[*addr]--; j.dirties[*addr] == 0 { + delete(j.dirties, *addr) + } + } + } + j.entries = j.entries[:snapshot] +} + +// length returns the current number of entries in the journal. +func (j *journal) length() int { + return len(j.entries) +} + +type ( + // Changes to the account trie. + createObjectChange struct { + account *common.Address + } + resetObjectChange struct { + prev *stateObject + } + suicideChange struct { + account *common.Address + prev bool // whether account had already suicided + prevbalance *big.Int + } + + // Changes to individual accounts. + balanceChange struct { + account *common.Address + prev *big.Int + } + nonceChange struct { + account *common.Address + prev uint64 + } + storageChange struct { + account *common.Address + key, prevalue common.Hash + } + codeChange struct { + account *common.Address + prevcode, prevhash []byte + } + + // Changes to other state values. + refundChange struct { + prev uint64 + } + addLogChange struct{} + + // Changes to the access list + accessListAddAccountChange struct { + address *common.Address + } + accessListAddSlotChange struct { + address *common.Address + slot *common.Hash + } +) + +func (ch createObjectChange) Revert(s *StateDB) { + delete(s.stateObjects, *ch.account) +} + +func (ch createObjectChange) Dirtied() *common.Address { + return ch.account +} + +func (ch resetObjectChange) Revert(s *StateDB) { + s.setStateObject(ch.prev) +} + +func (ch resetObjectChange) Dirtied() *common.Address { + return nil +} + +func (ch suicideChange) Revert(s *StateDB) { + obj := s.getStateObject(*ch.account) + if obj != nil { + obj.suicided = ch.prev + obj.setBalance(ch.prevbalance) + } +} + +func (ch suicideChange) Dirtied() *common.Address { + return ch.account +} + +func (ch balanceChange) Revert(s *StateDB) { + s.getStateObject(*ch.account).setBalance(ch.prev) +} + +func (ch balanceChange) Dirtied() *common.Address { + return ch.account +} + +func (ch nonceChange) Revert(s *StateDB) { + s.getStateObject(*ch.account).setNonce(ch.prev) +} + +func (ch nonceChange) Dirtied() *common.Address { + return ch.account +} + +func (ch codeChange) Revert(s *StateDB) { + s.getStateObject(*ch.account).setCode(common.BytesToHash(ch.prevhash), ch.prevcode) +} + +func (ch codeChange) Dirtied() *common.Address { + return ch.account +} + +func (ch storageChange) Revert(s *StateDB) { + s.getStateObject(*ch.account).setState(ch.key, ch.prevalue) +} + +func (ch storageChange) Dirtied() *common.Address { + return ch.account +} + +func (ch refundChange) Revert(s *StateDB) { + s.refund = ch.prev +} + +func (ch refundChange) Dirtied() *common.Address { + return nil +} + +func (ch addLogChange) Revert(s *StateDB) { + s.logs = s.logs[:len(s.logs)-1] +} + +func (ch addLogChange) Dirtied() *common.Address { + return nil +} + +func (ch accessListAddAccountChange) Revert(s *StateDB) { + /* + One important invariant here, is that whenever a (addr, slot) is added, if the + addr is not already present, the add causes two journal entries: + - one for the address, + - one for the (address,slot) + Therefore, when unrolling the change, we can always blindly delete the + (addr) at this point, since no storage adds can remain when come upon + a single (addr) change. + */ + s.accessList.DeleteAddress(*ch.address) +} + +func (ch accessListAddAccountChange) Dirtied() *common.Address { + return nil +} + +func (ch accessListAddSlotChange) Revert(s *StateDB) { + s.accessList.DeleteSlot(*ch.address, *ch.slot) +} + +func (ch accessListAddSlotChange) Dirtied() *common.Address { + return nil +} diff --git a/x/evm/statedb_legacy/state_object.go b/x/evm/statedb_legacy/state_object.go new file mode 100644 index 0000000000..ece880f01a --- /dev/null +++ b/x/evm/statedb_legacy/state_object.go @@ -0,0 +1,244 @@ +// Copyright 2021 Evmos Foundation +// This file is part of Evmos' Ethermint library. +// +// The Ethermint library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The Ethermint library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the Ethermint library. If not, see https://github.com/evmos/ethermint/blob/main/LICENSE +package statedb_legacy + +import ( + "bytes" + "math/big" + "sort" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/crypto" + "github.com/evmos/ethermint/x/evm/statedb" +) + +var emptyCodeHash = crypto.Keccak256(nil) + +// Account is the Ethereum consensus representation of accounts. +// These objects are stored in the storage of auth module. +type Account = statedb.Account + +// NewEmptyAccount returns an empty account. +func NewEmptyAccount() *Account { + return &Account{ + Balance: new(big.Int), + CodeHash: emptyCodeHash, + } +} + +// Storage represents in-memory cache/buffer of contract storage. +type Storage map[common.Hash]common.Hash + +// SortedKeys sort the keys for deterministic iteration +func (s Storage) SortedKeys() []common.Hash { + keys := make([]common.Hash, len(s)) + i := 0 + for k := range s { + keys[i] = k + i++ + } + sort.Slice(keys, func(i, j int) bool { + return bytes.Compare(keys[i].Bytes(), keys[j].Bytes()) < 0 + }) + return keys +} + +// stateObject is the state of an acount +type stateObject struct { + db *StateDB + + account Account + code []byte + + // state storage + originStorage Storage + dirtyStorage Storage + + address common.Address + + // flags + dirtyCode bool + suicided bool +} + +// newObject creates a state object. +func newObject(db *StateDB, address common.Address, account Account) *stateObject { + if account.Balance == nil { + account.Balance = new(big.Int) + } + if account.CodeHash == nil { + account.CodeHash = emptyCodeHash + } + return &stateObject{ + db: db, + address: address, + account: account, + originStorage: make(Storage), + dirtyStorage: make(Storage), + } +} + +// empty returns whether the account is considered empty. +func (s *stateObject) empty() bool { + return s.account.Nonce == 0 && s.account.Balance.Sign() == 0 && bytes.Equal(s.account.CodeHash, emptyCodeHash) +} + +func (s *stateObject) markSuicided() { + s.suicided = true +} + +// AddBalance adds amount to s's balance. +// It is used to add funds to the destination account of a transfer. +func (s *stateObject) AddBalance(amount *big.Int) { + if amount.Sign() == 0 { + return + } + s.SetBalance(new(big.Int).Add(s.Balance(), amount)) +} + +// SubBalance removes amount from s's balance. +// It is used to remove funds from the origin account of a transfer. +func (s *stateObject) SubBalance(amount *big.Int) { + if amount.Sign() == 0 { + return + } + s.SetBalance(new(big.Int).Sub(s.Balance(), amount)) +} + +// SetBalance update account balance. +func (s *stateObject) SetBalance(amount *big.Int) { + s.db.journal.append(balanceChange{ + account: &s.address, + prev: new(big.Int).Set(s.account.Balance), + }) + s.setBalance(amount) +} + +func (s *stateObject) setBalance(amount *big.Int) { + s.account.Balance = amount +} + +// +// Attribute accessors +// + +// Returns the address of the contract/account +func (s *stateObject) Address() common.Address { + return s.address +} + +// Code returns the contract code associated with this object, if any. +func (s *stateObject) Code() []byte { + if s.code != nil { + return s.code + } + if bytes.Equal(s.CodeHash(), emptyCodeHash) { + return nil + } + code := s.db.keeper.GetCode(s.db.ctx, common.BytesToHash(s.CodeHash())) + s.code = code + return code +} + +// CodeSize returns the size of the contract code associated with this object, +// or zero if none. +func (s *stateObject) CodeSize() int { + return len(s.Code()) +} + +// SetCode set contract code to account +func (s *stateObject) SetCode(codeHash common.Hash, code []byte) { + prevcode := s.Code() + s.db.journal.append(codeChange{ + account: &s.address, + prevhash: s.CodeHash(), + prevcode: prevcode, + }) + s.setCode(codeHash, code) +} + +func (s *stateObject) setCode(codeHash common.Hash, code []byte) { + s.code = code + s.account.CodeHash = codeHash[:] + s.dirtyCode = true +} + +// SetCode set nonce to account +func (s *stateObject) SetNonce(nonce uint64) { + s.db.journal.append(nonceChange{ + account: &s.address, + prev: s.account.Nonce, + }) + s.setNonce(nonce) +} + +func (s *stateObject) setNonce(nonce uint64) { + s.account.Nonce = nonce +} + +// CodeHash returns the code hash of account +func (s *stateObject) CodeHash() []byte { + return s.account.CodeHash +} + +// Balance returns the balance of account +func (s *stateObject) Balance() *big.Int { + return s.account.Balance +} + +// Nonce returns the nonce of account +func (s *stateObject) Nonce() uint64 { + return s.account.Nonce +} + +// GetCommittedState query the committed state +func (s *stateObject) GetCommittedState(key common.Hash) common.Hash { + if value, cached := s.originStorage[key]; cached { + return value + } + // If no live objects are available, load it from keeper + value := s.db.keeper.GetState(s.db.ctx, s.Address(), key) + s.originStorage[key] = value + return value +} + +// GetState query the current state (including dirty state) +func (s *stateObject) GetState(key common.Hash) common.Hash { + if value, dirty := s.dirtyStorage[key]; dirty { + return value + } + return s.GetCommittedState(key) +} + +// SetState sets the contract state +func (s *stateObject) SetState(key common.Hash, value common.Hash) { + // If the new value is the same as old, don't set + prev := s.GetState(key) + if prev == value { + return + } + // New value is different, update and journal the change + s.db.journal.append(storageChange{ + account: &s.address, + key: key, + prevalue: prev, + }) + s.setState(key, value) +} + +func (s *stateObject) setState(key, value common.Hash) { + s.dirtyStorage[key] = value +} diff --git a/x/evm/statedb_legacy/statedb.go b/x/evm/statedb_legacy/statedb.go new file mode 100644 index 0000000000..6b87daaf49 --- /dev/null +++ b/x/evm/statedb_legacy/statedb.go @@ -0,0 +1,479 @@ +// Copyright 2021 Evmos Foundation +// This file is part of Evmos' Ethermint library. +// +// The Ethermint library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The Ethermint library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the Ethermint library. If not, see https://github.com/evmos/ethermint/blob/main/LICENSE +package statedb_legacy + +import ( + "fmt" + "math/big" + "sort" + + errorsmod "cosmossdk.io/errors" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/ethereum/go-ethereum/common" + ethtypes "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/core/vm" + "github.com/ethereum/go-ethereum/crypto" + "github.com/evmos/ethermint/x/evm/statedb" +) + +// revision is the identifier of a version of state. +// it consists of an auto-increment id and a journal index. +// it's safer to use than using journal index alone. +type revision struct { + id int + journalIndex int +} + +var _ vm.StateDB = &StateDB{} + +// StateDB structs within the ethereum protocol are used to store anything +// within the merkle trie. StateDBs take care of caching and storing +// nested states. It's the general query interface to retrieve: +// * Contracts +// * Accounts +type StateDB struct { + keeper Keeper + ctx sdk.Context + + // Journal of state modifications. This is the backbone of + // Snapshot and RevertToSnapshot. + journal *journal + validRevisions []revision + nextRevisionID int + + stateObjects map[common.Address]*stateObject + + txConfig statedb.TxConfig + + // The refund counter, also used by state transitioning. + refund uint64 + + // Per-transaction logs + logs []*ethtypes.Log + + // Per-transaction access list + accessList *accessList +} + +// New creates a new state from a given trie. +func New(ctx sdk.Context, keeper Keeper, txConfig statedb.TxConfig) *StateDB { + return &StateDB{ + keeper: keeper, + ctx: ctx, + stateObjects: make(map[common.Address]*stateObject), + journal: newJournal(), + accessList: newAccessList(), + + txConfig: txConfig, + } +} + +// Keeper returns the underlying `Keeper` +func (s *StateDB) Keeper() Keeper { + return s.keeper +} + +// AddLog adds a log, called by evm. +func (s *StateDB) AddLog(log *ethtypes.Log) { + s.journal.append(addLogChange{}) + + log.TxHash = s.txConfig.TxHash + log.BlockHash = s.txConfig.BlockHash + log.TxIndex = s.txConfig.TxIndex + log.Index = s.txConfig.LogIndex + uint(len(s.logs)) + s.logs = append(s.logs, log) +} + +// Logs returns the logs of current transaction. +func (s *StateDB) Logs() []*ethtypes.Log { + return s.logs +} + +// AddRefund adds gas to the refund counter +func (s *StateDB) AddRefund(gas uint64) { + s.journal.append(refundChange{prev: s.refund}) + s.refund += gas +} + +// SubRefund removes gas from the refund counter. +// This method will panic if the refund counter goes below zero +func (s *StateDB) SubRefund(gas uint64) { + s.journal.append(refundChange{prev: s.refund}) + if gas > s.refund { + panic(fmt.Sprintf("Refund counter below zero (gas: %d > refund: %d)", gas, s.refund)) + } + s.refund -= gas +} + +// Exist reports whether the given account address exists in the state. +// Notably this also returns true for suicided accounts. +func (s *StateDB) Exist(addr common.Address) bool { + return s.getStateObject(addr) != nil +} + +// Empty returns whether the state object is either non-existent +// or empty according to the EIP161 specification (balance = nonce = code = 0) +func (s *StateDB) Empty(addr common.Address) bool { + so := s.getStateObject(addr) + return so == nil || so.empty() +} + +// GetBalance retrieves the balance from the given address or 0 if object not found +func (s *StateDB) GetBalance(addr common.Address) *big.Int { + stateObject := s.getStateObject(addr) + if stateObject != nil { + return stateObject.Balance() + } + return common.Big0 +} + +// GetNonce returns the nonce of account, 0 if not exists. +func (s *StateDB) GetNonce(addr common.Address) uint64 { + stateObject := s.getStateObject(addr) + if stateObject != nil { + return stateObject.Nonce() + } + + return 0 +} + +// GetCode returns the code of account, nil if not exists. +func (s *StateDB) GetCode(addr common.Address) []byte { + stateObject := s.getStateObject(addr) + if stateObject != nil { + return stateObject.Code() + } + return nil +} + +// GetCodeSize returns the code size of account. +func (s *StateDB) GetCodeSize(addr common.Address) int { + stateObject := s.getStateObject(addr) + if stateObject != nil { + return stateObject.CodeSize() + } + return 0 +} + +// GetCodeHash returns the code hash of account. +func (s *StateDB) GetCodeHash(addr common.Address) common.Hash { + stateObject := s.getStateObject(addr) + if stateObject == nil { + return common.Hash{} + } + return common.BytesToHash(stateObject.CodeHash()) +} + +// GetState retrieves a value from the given account's storage trie. +func (s *StateDB) GetState(addr common.Address, hash common.Hash) common.Hash { + stateObject := s.getStateObject(addr) + if stateObject != nil { + return stateObject.GetState(hash) + } + return common.Hash{} +} + +// GetCommittedState retrieves a value from the given account's committed storage trie. +func (s *StateDB) GetCommittedState(addr common.Address, hash common.Hash) common.Hash { + stateObject := s.getStateObject(addr) + if stateObject != nil { + return stateObject.GetCommittedState(hash) + } + return common.Hash{} +} + +// GetRefund returns the current value of the refund counter. +func (s *StateDB) GetRefund() uint64 { + return s.refund +} + +// HasSuicided returns if the contract is suicided in current transaction. +func (s *StateDB) HasSuicided(addr common.Address) bool { + stateObject := s.getStateObject(addr) + if stateObject != nil { + return stateObject.suicided + } + return false +} + +// AddPreimage records a SHA3 preimage seen by the VM. +// AddPreimage performs a no-op since the EnablePreimageRecording flag is disabled +// on the vm.Config during state transitions. No store trie preimages are written +// to the database. +func (s *StateDB) AddPreimage(hash common.Hash, preimage []byte) {} //nolint: revive + +// getStateObject retrieves a state object given by the address, returning nil if +// the object is not found. +func (s *StateDB) getStateObject(addr common.Address) *stateObject { + // Prefer live objects if any is available + if obj := s.stateObjects[addr]; obj != nil { + return obj + } + // If no live objects are available, load it from keeper + account := s.keeper.GetAccount(s.ctx, addr) + if account == nil { + return nil + } + // Insert into the live set + obj := newObject(s, addr, *account) + s.setStateObject(obj) + return obj +} + +// getOrNewStateObject retrieves a state object or create a new state object if nil. +func (s *StateDB) getOrNewStateObject(addr common.Address) *stateObject { + stateObject := s.getStateObject(addr) + if stateObject == nil { + stateObject, _ = s.createObject(addr) + } + return stateObject +} + +// createObject creates a new state object. If there is an existing account with +// the given address, it is overwritten and returned as the second return value. +func (s *StateDB) createObject(addr common.Address) (newobj, prev *stateObject) { + prev = s.getStateObject(addr) + + newobj = newObject(s, addr, Account{}) + if prev == nil { + s.journal.append(createObjectChange{account: &addr}) + } else { + s.journal.append(resetObjectChange{prev: prev}) + } + s.setStateObject(newobj) + if prev != nil { + return newobj, prev + } + return newobj, nil +} + +// CreateAccount explicitly creates a state object. If a state object with the address +// already exists the balance is carried over to the new account. +// +// CreateAccount is called during the EVM CREATE operation. The situation might arise that +// a contract does the following: +// +// 1. sends funds to sha(account ++ (nonce + 1)) +// 2. tx_create(sha(account ++ nonce)) (note that this gets the address of 1) +// +// Carrying over the balance ensures that Ether doesn't disappear. +func (s *StateDB) CreateAccount(addr common.Address) { + newObj, prev := s.createObject(addr) + if prev != nil { + newObj.setBalance(prev.account.Balance) + } +} + +// ForEachStorage iterate the contract storage, the iteration order is not defined. +func (s *StateDB) ForEachStorage(addr common.Address, cb func(key, value common.Hash) bool) error { + so := s.getStateObject(addr) + if so == nil { + return nil + } + s.keeper.ForEachStorage(s.ctx, addr, func(key, value common.Hash) bool { + if value, dirty := so.dirtyStorage[key]; dirty { + return cb(key, value) + } + if len(value) > 0 { + return cb(key, value) + } + return true + }) + return nil +} + +func (s *StateDB) setStateObject(object *stateObject) { + s.stateObjects[object.Address()] = object +} + +/* + * SETTERS + */ + +// AddBalance adds amount to the account associated with addr. +func (s *StateDB) AddBalance(addr common.Address, amount *big.Int) { + stateObject := s.getOrNewStateObject(addr) + if stateObject != nil { + stateObject.AddBalance(amount) + } +} + +// SubBalance subtracts amount from the account associated with addr. +func (s *StateDB) SubBalance(addr common.Address, amount *big.Int) { + stateObject := s.getOrNewStateObject(addr) + if stateObject != nil { + stateObject.SubBalance(amount) + } +} + +// SetNonce sets the nonce of account. +func (s *StateDB) SetNonce(addr common.Address, nonce uint64) { + stateObject := s.getOrNewStateObject(addr) + if stateObject != nil { + stateObject.SetNonce(nonce) + } +} + +// SetCode sets the code of account. +func (s *StateDB) SetCode(addr common.Address, code []byte) { + stateObject := s.getOrNewStateObject(addr) + if stateObject != nil { + stateObject.SetCode(crypto.Keccak256Hash(code), code) + } +} + +// SetState sets the contract state. +func (s *StateDB) SetState(addr common.Address, key, value common.Hash) { + stateObject := s.getOrNewStateObject(addr) + if stateObject != nil { + stateObject.SetState(key, value) + } +} + +// Suicide marks the given account as suicided. +// This clears the account balance. +// +// The account's state object is still available until the state is committed, +// getStateObject will return a non-nil account after Suicide. +func (s *StateDB) Suicide(addr common.Address) bool { + stateObject := s.getStateObject(addr) + if stateObject == nil { + return false + } + s.journal.append(suicideChange{ + account: &addr, + prev: stateObject.suicided, + prevbalance: new(big.Int).Set(stateObject.Balance()), + }) + stateObject.markSuicided() + stateObject.account.Balance = new(big.Int) + + return true +} + +// PrepareAccessList handles the preparatory steps for executing a state transition with +// regards to both EIP-2929 and EIP-2930: +// +// - Add sender to access list (2929) +// - Add destination to access list (2929) +// - Add precompiles to access list (2929) +// - Add the contents of the optional tx access list (2930) +// +// This method should only be called if Yolov3/Berlin/2929+2930 is applicable at the current number. +func (s *StateDB) PrepareAccessList(sender common.Address, dst *common.Address, precompiles []common.Address, list ethtypes.AccessList) { + s.AddAddressToAccessList(sender) + if dst != nil { + s.AddAddressToAccessList(*dst) + // If it's a create-tx, the destination will be added inside evm.create + } + for _, addr := range precompiles { + s.AddAddressToAccessList(addr) + } + for _, el := range list { + s.AddAddressToAccessList(el.Address) + for _, key := range el.StorageKeys { + s.AddSlotToAccessList(el.Address, key) + } + } +} + +// AddAddressToAccessList adds the given address to the access list +func (s *StateDB) AddAddressToAccessList(addr common.Address) { + if s.accessList.AddAddress(addr) { + s.journal.append(accessListAddAccountChange{&addr}) + } +} + +// AddSlotToAccessList adds the given (address, slot)-tuple to the access list +func (s *StateDB) AddSlotToAccessList(addr common.Address, slot common.Hash) { + addrMod, slotMod := s.accessList.AddSlot(addr, slot) + if addrMod { + // In practice, this should not happen, since there is no way to enter the + // scope of 'address' without having the 'address' become already added + // to the access list (via call-variant, create, etc). + // Better safe than sorry, though + s.journal.append(accessListAddAccountChange{&addr}) + } + if slotMod { + s.journal.append(accessListAddSlotChange{ + address: &addr, + slot: &slot, + }) + } +} + +// AddressInAccessList returns true if the given address is in the access list. +func (s *StateDB) AddressInAccessList(addr common.Address) bool { + return s.accessList.ContainsAddress(addr) +} + +// SlotInAccessList returns true if the given (address, slot)-tuple is in the access list. +func (s *StateDB) SlotInAccessList(addr common.Address, slot common.Hash) (addressPresent bool, slotPresent bool) { + return s.accessList.Contains(addr, slot) +} + +// Snapshot returns an identifier for the current revision of the state. +func (s *StateDB) Snapshot() int { + id := s.nextRevisionID + s.nextRevisionID++ + s.validRevisions = append(s.validRevisions, revision{id, s.journal.length()}) + return id +} + +// RevertToSnapshot reverts all state changes made since the given revision. +func (s *StateDB) RevertToSnapshot(revid int) { + // Find the snapshot in the stack of valid snapshots. + idx := sort.Search(len(s.validRevisions), func(i int) bool { + return s.validRevisions[i].id >= revid + }) + if idx == len(s.validRevisions) || s.validRevisions[idx].id != revid { + panic(fmt.Errorf("revision id %v cannot be reverted", revid)) + } + snapshot := s.validRevisions[idx].journalIndex + + // Replay the journal to undo changes and remove invalidated snapshots + s.journal.Revert(s, snapshot) + s.validRevisions = s.validRevisions[:idx] +} + +// Commit writes the dirty states to keeper +// the StateDB object should be discarded after committed. +func (s *StateDB) Commit() error { + for _, addr := range s.journal.sortedDirties() { + obj := s.stateObjects[addr] + if obj.suicided { + if err := s.keeper.DeleteAccount(s.ctx, obj.Address()); err != nil { + return errorsmod.Wrap(err, "failed to delete account") + } + } else { + if obj.code != nil && obj.dirtyCode { + s.keeper.SetCode(s.ctx, obj.CodeHash(), obj.code) + } + if err := s.keeper.SetAccount(s.ctx, obj.Address(), obj.account); err != nil { + return errorsmod.Wrap(err, "failed to set account") + } + for _, key := range obj.dirtyStorage.SortedKeys() { + value := obj.dirtyStorage[key] + // Skip noop changes, persist actual changes + if value == obj.originStorage[key] { + continue + } + s.keeper.SetState(s.ctx, obj.Address(), key, value) + } + } + } + return nil +} From 827a16fbe18f3ac068d7900fab5fadd63730158e Mon Sep 17 00:00:00 2001 From: drklee3 Date: Wed, 6 Mar 2024 11:43:39 -0800 Subject: [PATCH 04/22] test: Add test for comparing statedb output iavl hashes --- app/app.go | 7 + x/evm/keeper/state_transition_test.go | 181 ++++++++++++++++++++++++++ 2 files changed, 188 insertions(+) diff --git a/app/app.go b/app/app.go index 7b5f98d319..09aa67a55b 100644 --- a/app/app.go +++ b/app/app.go @@ -756,6 +756,13 @@ func (app *EthermintApp) InterfaceRegistry() types.InterfaceRegistry { return app.interfaceRegistry } +// GetKeys returns all KVStoreKeys +// +// NOTE: This is solely to be used for testing purposes. +func (app *EthermintApp) GetKeys() map[string]*storetypes.KVStoreKey { + return app.keys +} + // GetKey returns the KVStoreKey for the provided store key. // // NOTE: This is solely to be used for testing purposes. diff --git a/x/evm/keeper/state_transition_test.go b/x/evm/keeper/state_transition_test.go index 6b71d675d6..d0dac9a32f 100644 --- a/x/evm/keeper/state_transition_test.go +++ b/x/evm/keeper/state_transition_test.go @@ -6,22 +6,30 @@ import ( "math" "math/big" "os" + "strings" "time" codectypes "github.com/cosmos/cosmos-sdk/codec/types" + "github.com/cosmos/cosmos-sdk/store/iavl" + storetypes "github.com/cosmos/cosmos-sdk/store/types" sdk "github.com/cosmos/cosmos-sdk/types" + authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core" ethtypes "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/core/vm" "github.com/ethereum/go-ethereum/params" "github.com/evmos/ethermint/tests" "github.com/evmos/ethermint/x/evm/keeper" "github.com/evmos/ethermint/x/evm/statedb" + "github.com/evmos/ethermint/x/evm/statedb_legacy" "github.com/evmos/ethermint/x/evm/types" "github.com/tendermint/tendermint/crypto/tmhash" tmproto "github.com/tendermint/tendermint/proto/tendermint/types" tmtypes "github.com/tendermint/tendermint/types" + + cosmosiavl "github.com/cosmos/iavl" ) func (suite *KeeperTestSuite) TestGetHashFn() { @@ -804,3 +812,176 @@ func (suite *KeeperTestSuite) TestConsistency() { res.Data, ) } + +func (suite *KeeperTestSuite) TestStateDBConsistency() { + // evm store keys prefixes: + // Code = 1 + // Storage = 2 + addr1 := common.BigToAddress(big.NewInt(1)) + addr2 := common.BigToAddress(big.NewInt(2)) + + tests := []struct { + name string + maleate func(vmdb vm.StateDB) + }{ + { + "noop", + func(vmdb vm.StateDB) { + }, + }, + { + "SetState", + func(vmdb vm.StateDB) { + vmdb.SetState(addr2, common.BigToHash(big.NewInt(1)), common.BigToHash(big.NewInt(2))) + }, + }, + { + "SetCode", + func(vmdb vm.StateDB) { + vmdb.SetCode(addr2, []byte{1, 2, 3}) + }, + }, + { + "SetState", + func(vmdb vm.StateDB) { + vmdb.SetState(addr2, common.BytesToHash([]byte{1, 2, 3}), common.BytesToHash([]byte{4, 5, 6})) + }, + }, + { + "SetState + SetCode", + func(vmdb vm.StateDB) { + vmdb.SetCode(addr1, []byte{10}) + vmdb.SetState(addr2, common.BytesToHash([]byte{1, 2, 3}), common.BytesToHash([]byte{4, 5, 6})) + }, + }, + // FAILS: + { + "SetState + SetCode, reverse address", + func(vmdb vm.StateDB) { + vmdb.SetCode(addr2, []byte{10}) + vmdb.SetState(addr1, common.BytesToHash([]byte{1, 2, 3}), common.BytesToHash([]byte{4, 5, 6})) + }, + }, + } + + for _, tt := range tests { + suite.Run(tt.name, func() { + var tracer bytes.Buffer + suite.App.SetCommitMultiStoreTracer(&tracer) + + suite.SetupTest() + suite.Commit() + + // Cache CTX statedb + ctxDB := statedb.New(suite.Ctx, suite.App.EvmKeeper, emptyTxConfig) + tt.maleate(ctxDB) + suite.Require().NoError(ctxDB.Commit()) + + newRes := suite.Commit() + + cacheNodes := suite.exportIAVLStoreNodes(suite.App.GetKey(authtypes.StoreKey)) + cacheHashes := suite.GetStoreHashes() + + // Reset state + suite.SetupTest() + suite.Commit() + + legacyDB := statedb_legacy.New(suite.Ctx, suite.App.EvmKeeper, emptyTxConfig) + tt.maleate(legacyDB) + suite.Require().NoError(legacyDB.Commit()) + + legacyRes := suite.Commit() + + suite.T().Logf("newRes: %x", newRes.Data) + suite.T().Logf("legacyRes: %x", legacyRes.Data) + + suite.Equalf( + common.Bytes2Hex(newRes.Data), + common.Bytes2Hex(legacyRes.Data), + "commitID.Hash should match between statedb versions, old %x, new %x", + legacyRes.Data, + newRes.Data, + ) + + // Don't log any additional info if the test passed + if !suite.T().Failed() { + return + } + + journalNodes := suite.exportIAVLStoreNodes(suite.App.GetKey(authtypes.StoreKey)) + journalHashes := suite.GetStoreHashes() + + suite.Equal(cacheHashes, journalHashes) + + fmt.Printf("----------------------------------------\n") + fmt.Printf("CTX NODES:\n") + fmt.Printf("%v\n\n", cacheNodes) + fmt.Printf("----------------------------------------\n") + fmt.Printf("JOURNAL NODES:\n") + fmt.Printf("%v\n\n", journalNodes) + }) + } +} + +// GetStoreHashes returns the IAVL hashes of all the stores in the multistore +func (suite *KeeperTestSuite) GetStoreHashes() map[string]string { + cms := suite.App.CommitMultiStore() + storeKeys := suite.App.GetKeys() + storeHashes := make(map[string]string) + + for _, key := range storeKeys { + store := cms.GetStore(key) + iavlStore := store.(*iavl.Store) + storeHashes[key.Name()] = common.Bytes2Hex(iavlStore.LastCommitID().Hash) + } + + return storeHashes +} + +func (suite *KeeperTestSuite) exportIAVLStoreNodes( + storeKey *storetypes.KVStoreKey, +) string { + cms := suite.App.CommitMultiStore() + store := cms.GetStore(storeKey) + authIavlStore := store.(*iavl.Store) + + lastVersion := authIavlStore.LastCommitID().Version + + exporter, err := authIavlStore.Export(lastVersion) + suite.Require().NoError(err) + defer exporter.Close() + + nodes := []*cosmosiavl.ExportNode{} + for { + node, err := exporter.Next() + if err != nil || node == nil { + break + } + + nodes = append(nodes, node) + } + + s := "" + s += fmt.Sprintf("%v store nodes @ version %v\n", len(nodes), lastVersion) + + for _, node := range nodes { + indent := strings.Repeat(" ", int(node.Height)) + + valueStr := fmt.Sprintf("%x", node.Value) + + if len(node.Value) == 0 { + valueStr = "nil" + } + + s += fmt.Sprintf( + "%v[%v-%v] %x -> %s\n", + indent, + node.Height, + node.Version, + node.Key, + valueStr, + ) + } + + return s +} From 5694ac8628c0fb496f55bdea4d0cfec6a331fec7 Mon Sep 17 00:00:00 2001 From: drklee3 Date: Wed, 6 Mar 2024 12:07:29 -0800 Subject: [PATCH 05/22] Remove tracing --- x/evm/keeper/state_transition_test.go | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/x/evm/keeper/state_transition_test.go b/x/evm/keeper/state_transition_test.go index d0dac9a32f..a2221a6d41 100644 --- a/x/evm/keeper/state_transition_test.go +++ b/x/evm/keeper/state_transition_test.go @@ -854,8 +854,10 @@ func (suite *KeeperTestSuite) TestStateDBConsistency() { vmdb.SetState(addr2, common.BytesToHash([]byte{1, 2, 3}), common.BytesToHash([]byte{4, 5, 6})) }, }, - // FAILS: { + // Fails due to different account numbers due to different SetAccount ordering + // Journal -> SetAccount ordered by address @ Commit() -> addr1, addr2 + // CacheCtx -> Ordered by first SetCode/SetState call -> addr2, addr1 "SetState + SetCode, reverse address", func(vmdb vm.StateDB) { vmdb.SetCode(addr2, []byte{10}) @@ -866,9 +868,6 @@ func (suite *KeeperTestSuite) TestStateDBConsistency() { for _, tt := range tests { suite.Run(tt.name, func() { - var tracer bytes.Buffer - suite.App.SetCommitMultiStoreTracer(&tracer) - suite.SetupTest() suite.Commit() @@ -882,7 +881,8 @@ func (suite *KeeperTestSuite) TestStateDBConsistency() { cacheNodes := suite.exportIAVLStoreNodes(suite.App.GetKey(authtypes.StoreKey)) cacheHashes := suite.GetStoreHashes() - // Reset state + // -------------------------------------------- + // Reset state for legacy journal based StateDB suite.SetupTest() suite.Commit() From 760aa3ad50902c5ed8a6b326d66e83e421e34944 Mon Sep 17 00:00:00 2001 From: drklee3 Date: Fri, 8 Mar 2024 15:39:20 -0800 Subject: [PATCH 06/22] feat: Add test for noop SetState unmodified iavl hash --- x/evm/keeper/state_transition_test.go | 140 +++++++++++++++++--------- 1 file changed, 95 insertions(+), 45 deletions(-) diff --git a/x/evm/keeper/state_transition_test.go b/x/evm/keeper/state_transition_test.go index a2221a6d41..3759655e4d 100644 --- a/x/evm/keeper/state_transition_test.go +++ b/x/evm/keeper/state_transition_test.go @@ -1,13 +1,10 @@ package keeper_test import ( - "bytes" "fmt" "math" "math/big" - "os" "strings" - "time" codectypes "github.com/cosmos/cosmos-sdk/codec/types" "github.com/cosmos/cosmos-sdk/store/iavl" @@ -760,60 +757,59 @@ func sortTraces(buf bytes.Buffer) { } */ -func (suite *KeeperTestSuite) TestConsistency() { - var tracer bytes.Buffer - suite.App.SetCommitMultiStoreTracer(&tracer) - // Commit so the ctx is updated with the tracer - suite.Commit() +func (suite *KeeperTestSuite) TestNoopStateChange_UnmodifiedIAVLTree() { + // On StateDB.Commit(), if there is a dirty state change that matches the + // committed state, it should be skipped. Only state changes that are + // different from committed state should be applied to the underlying store. + // Corresponding journal based StateDB code: + // https://github.com/ethereum/go-ethereum/blob/e31709db6570e302557a9bccd681034ea0dcc246/core/state/state_object.go#L302-L305 + // https://github.com/Kava-Labs/ethermint/blob/877e8fd1bd140c37ad05ed613f31e28f0130c0c4/x/evm/statedb/statedb.go#L469-L472 - suite.Require().True( - suite.Ctx.MultiStore().TracingEnabled(), - "tracer should be enabled", - ) + // Even with store.Set() on the same pre-existing key and value, it will + // update the underlying iavl.Node version and thus the node hash, parent + // hashes, and the commitID - // evm store keys prefixes: - // Code = 1 - // Storage = 2 - // addr1 := common.BigToAddress(big.NewInt(1)) - addr2 := common.BigToAddress(big.NewInt(2)) + // E.g. SetState(A, B) -> xxx -> SetState(A, B) should not be applied to + // the underlying store. + // xxx could be 0 or more state changes to the same key. It can be different + // values since the only value that actually matters is the last one when + // Commit() is called. - // Suspect: Inconsistent write orders to underlying ctx store: - // Journal based - sorted by addresses then inserted/deleted/etc (ascending order) - // - addr1 then addr2 - // CacheCtx based - sorted by cosmos store keys which have prefixes and different orders - // - code then storage - db := statedb.New(suite.Ctx, suite.App.EvmKeeper, emptyTxConfig) - // db.SetCode(addr1, []byte{1, 2, 3}) - db.SetState(addr2, common.BigToHash(big.NewInt(1)), common.BigToHash(big.NewInt(2))) + addr := common.BigToAddress(big.NewInt(1)) + key := common.BigToHash(big.NewInt(10)) + value := common.BigToHash(big.NewInt(20)) + db := statedb.New(suite.Ctx, suite.App.EvmKeeper, emptyTxConfig) + db.SetState(addr, key, value) suite.Require().NoError(db.Commit()) - res := suite.Commit() - suite.T().Logf("commitID.Hash: %x", res.Data) + suite.Commit() - // acc1 := suite.App.AccountKeeper.GetAccount(suite.Ctx, sdk.AccAddress(addr1.Bytes())) - // suite.Require().NotNil(acc1) + store := suite.App.CommitMultiStore().GetStore(suite.App.GetKey(types.StoreKey)) + iavlStore := store.(*iavl.Store) + commitID1 := iavlStore.LastCommitID() - // suite.T().Logf("AccNumber: %v", acc1.GetAccountNumber()) + // Set the same state again + db = statedb.New(suite.Ctx, suite.App.EvmKeeper, emptyTxConfig) + db.SetState(addr, key, value) + suite.Require().NoError(db.Commit()) - // Log the tracer contents - suite.T().Logf("Tracer (%v): %s", tracer.Len(), tracer.String()) + suite.Commit() - // Write tracer contents to file - err := os.WriteFile(fmt.Sprintf("tracer-ctx-setstate-%v-%x.log", time.Now().Unix(), res.Data), tracer.Bytes(), 0644) - suite.Require().NoError(err) + commitID2 := iavlStore.LastCommitID() - expectedHash := common.Hex2Bytes("87ade13f1a5f21f0346ce822a210caf29f22ffcc501e9969dddebf5b382f926a") - suite.Require().Equalf( - expectedHash, - res.Data, - "commitID.Hash should match, expected %x, got %x", - expectedHash, - res.Data, + // We can compare the commitIDs since this is *only* the x/evm store which + // doesn't change between blocks without state changes. Any version change, + // e.g. no-op change that was written when it shouldn't, will modify the + // hash. + suite.Require().Equal( + common.Bytes2Hex(commitID1.Hash), + common.Bytes2Hex(commitID2.Hash), + "evm store should be unchanged", ) } -func (suite *KeeperTestSuite) TestStateDBConsistency() { +func (suite *KeeperTestSuite) TestStateDB_IAVLConsistency() { // evm store keys prefixes: // Code = 1 // Storage = 2 @@ -821,31 +817,36 @@ func (suite *KeeperTestSuite) TestStateDBConsistency() { addr2 := common.BigToAddress(big.NewInt(2)) tests := []struct { - name string - maleate func(vmdb vm.StateDB) + name string + maleate func(vmdb vm.StateDB) + shouldSkip bool }{ { "noop", func(vmdb vm.StateDB) { }, + false, }, { "SetState", func(vmdb vm.StateDB) { vmdb.SetState(addr2, common.BigToHash(big.NewInt(1)), common.BigToHash(big.NewInt(2))) }, + false, }, { "SetCode", func(vmdb vm.StateDB) { vmdb.SetCode(addr2, []byte{1, 2, 3}) }, + false, }, { "SetState", func(vmdb vm.StateDB) { vmdb.SetState(addr2, common.BytesToHash([]byte{1, 2, 3}), common.BytesToHash([]byte{4, 5, 6})) }, + false, }, { "SetState + SetCode", @@ -853,6 +854,7 @@ func (suite *KeeperTestSuite) TestStateDBConsistency() { vmdb.SetCode(addr1, []byte{10}) vmdb.SetState(addr2, common.BytesToHash([]byte{1, 2, 3}), common.BytesToHash([]byte{4, 5, 6})) }, + false, }, { // Fails due to different account numbers due to different SetAccount ordering @@ -863,11 +865,27 @@ func (suite *KeeperTestSuite) TestStateDBConsistency() { vmdb.SetCode(addr2, []byte{10}) vmdb.SetState(addr1, common.BytesToHash([]byte{1, 2, 3}), common.BytesToHash([]byte{4, 5, 6})) }, + true, + }, + { + "hmm", + func(vmdb vm.StateDB) { + vmdb.SetState(addr1, common.BigToHash(big.NewInt(1)), common.BigToHash(big.NewInt(2))) + suite.Require().NoError(vmdb.(*statedb.StateDB).Commit()) + suite.Commit() + + vmdb.SetState(addr1, common.BigToHash(big.NewInt(1)), common.BigToHash(big.NewInt(2))) + }, + false, }, } for _, tt := range tests { suite.Run(tt.name, func() { + if tt.shouldSkip { + suite.T().Skip("skipping test - state incompatible") + } + suite.SetupTest() suite.Commit() @@ -913,6 +931,11 @@ func (suite *KeeperTestSuite) TestStateDBConsistency() { suite.Equal(cacheHashes, journalHashes) + hashDiff := storeHashDiff(cacheHashes, journalHashes) + for k, v := range hashDiff { + suite.T().Logf("%v (cache -> journal): %v", k, v) + } + fmt.Printf("----------------------------------------\n") fmt.Printf("CTX NODES:\n") fmt.Printf("%v\n\n", cacheNodes) @@ -923,6 +946,33 @@ func (suite *KeeperTestSuite) TestStateDBConsistency() { } } +func storeHashDiff( + aHashes map[string]string, + bHashes map[string]string, +) map[string]string { + diff := make(map[string]string) + + for k, aHash := range aHashes { + bHash, ok := bHashes[k] + if !ok { + diff[k] = fmt.Sprintf("%v -> nil", aHash) + continue + } + + if aHash != bHash { + diff[k] = fmt.Sprintf("%v -> %v", aHash, bHash) + } + } + + for k, bHash := range bHashes { + if _, ok := aHashes[k]; !ok { + diff[k] = fmt.Sprintf("nil -> %v", bHash) + } + } + + return diff +} + // GetStoreHashes returns the IAVL hashes of all the stores in the multistore func (suite *KeeperTestSuite) GetStoreHashes() map[string]string { cms := suite.App.CommitMultiStore() From f00bfc5a59cf6b44b576a5e319e8dfb74a4cc65b Mon Sep 17 00:00:00 2001 From: drklee3 Date: Fri, 8 Mar 2024 15:55:15 -0800 Subject: [PATCH 07/22] test: Skip noop state change --- x/evm/keeper/state_transition_test.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/x/evm/keeper/state_transition_test.go b/x/evm/keeper/state_transition_test.go index 3759655e4d..3ed55be804 100644 --- a/x/evm/keeper/state_transition_test.go +++ b/x/evm/keeper/state_transition_test.go @@ -758,6 +758,8 @@ func sortTraces(buf bytes.Buffer) { */ func (suite *KeeperTestSuite) TestNoopStateChange_UnmodifiedIAVLTree() { + suite.T().Skip("CacheCtx StateDB does not currently skip noop state changes") + // On StateDB.Commit(), if there is a dirty state change that matches the // committed state, it should be skipped. Only state changes that are // different from committed state should be applied to the underlying store. From 78aefce149e318b04fceb39487e99cc7df6aba60 Mon Sep 17 00:00:00 2001 From: drklee3 Date: Fri, 8 Mar 2024 16:02:42 -0800 Subject: [PATCH 08/22] fix: Re-add SetAccount on SetState --- x/evm/keeper/state_transition_test.go | 31 --------------------------- x/evm/statedb/statedb.go | 5 +++++ 2 files changed, 5 insertions(+), 31 deletions(-) diff --git a/x/evm/keeper/state_transition_test.go b/x/evm/keeper/state_transition_test.go index 3ed55be804..66c3156e2b 100644 --- a/x/evm/keeper/state_transition_test.go +++ b/x/evm/keeper/state_transition_test.go @@ -737,26 +737,6 @@ var ( emptyTxConfig statedb.TxConfig = statedb.NewEmptyTxConfig(blockHash) ) -/* -func sortTraces(buf bytes.Buffer) { - // Iterate over buff by each newline - // module -> - traces := make(map[string][]string) - - scanner := bufio.NewScanner(strings.NewReader(buf.String())) - for scanner.Scan() { - s := scanner.Text() - // parse as json - var lineJson interface{} - json.Unmarshal([]byte(s), &lineJson) - } - - if err := scanner.Err(); err != nil { - panic(err) - } -} -*/ - func (suite *KeeperTestSuite) TestNoopStateChange_UnmodifiedIAVLTree() { suite.T().Skip("CacheCtx StateDB does not currently skip noop state changes") @@ -869,17 +849,6 @@ func (suite *KeeperTestSuite) TestStateDB_IAVLConsistency() { }, true, }, - { - "hmm", - func(vmdb vm.StateDB) { - vmdb.SetState(addr1, common.BigToHash(big.NewInt(1)), common.BigToHash(big.NewInt(2))) - suite.Require().NoError(vmdb.(*statedb.StateDB).Commit()) - suite.Commit() - - vmdb.SetState(addr1, common.BigToHash(big.NewInt(1)), common.BigToHash(big.NewInt(2))) - }, - false, - }, } for _, tt := range tests { diff --git a/x/evm/statedb/statedb.go b/x/evm/statedb/statedb.go index 634c7a5dfa..cbc7f4f17c 100644 --- a/x/evm/statedb/statedb.go +++ b/x/evm/statedb/statedb.go @@ -307,6 +307,11 @@ func (s *StateDB) SetCode(addr common.Address, code []byte) { // SetState sets the contract state. func (s *StateDB) SetState(addr common.Address, key, value common.Hash) { + acc := s.getOrNewAccount(addr) + if err := s.keeper.SetAccount(s.ctx.CurrentCtx(), addr, *acc); err != nil { + s.SetError(fmt.Errorf("failed to set account for state: %w", err)) + } + // We cannot attempt to skip noop changes by just checking committed state // Example: // 1. With committed state to 0x0 From 4f5da0701b144b5e6ae218de21db07325f9ceb4c Mon Sep 17 00:00:00 2001 From: drklee3 Date: Mon, 11 Mar 2024 16:58:17 -0700 Subject: [PATCH 09/22] test: Add additional test cases for noop SetState changes --- x/evm/keeper/state_transition_test.go | 99 ++++++++++++++++++++------- 1 file changed, 76 insertions(+), 23 deletions(-) diff --git a/x/evm/keeper/state_transition_test.go b/x/evm/keeper/state_transition_test.go index 66c3156e2b..b14ab2254e 100644 --- a/x/evm/keeper/state_transition_test.go +++ b/x/evm/keeper/state_transition_test.go @@ -738,7 +738,7 @@ var ( ) func (suite *KeeperTestSuite) TestNoopStateChange_UnmodifiedIAVLTree() { - suite.T().Skip("CacheCtx StateDB does not currently skip noop state changes") + // suite.T().Skip("CacheCtx StateDB does not currently skip noop state changes") // On StateDB.Commit(), if there is a dirty state change that matches the // committed state, it should be skipped. Only state changes that are @@ -761,34 +761,87 @@ func (suite *KeeperTestSuite) TestNoopStateChange_UnmodifiedIAVLTree() { key := common.BigToHash(big.NewInt(10)) value := common.BigToHash(big.NewInt(20)) - db := statedb.New(suite.Ctx, suite.App.EvmKeeper, emptyTxConfig) - db.SetState(addr, key, value) - suite.Require().NoError(db.Commit()) + tests := []struct { + name string + initializeState func(vmdb vm.StateDB) + maleate func(vmdb vm.StateDB) + }{ + { + "SetState - key/value same as committed", + func(vmdb vm.StateDB) { + vmdb.SetState(addr, key, value) + }, + func(vmdb vm.StateDB) { + vmdb.SetState(addr, key, value) + }, + }, + { + "SetState - multiple snapshots, same value", + func(vmdb vm.StateDB) { + vmdb.SetState(addr, key, value) + }, + func(vmdb vm.StateDB) { + // A -> A + vmdb.SetState(addr, key, value) + + // Same value, just different snapshot. Should be skipped in all + // snapshots. + _ = vmdb.Snapshot() + vmdb.SetState(addr, key, value) + }, + }, + { + "SetState - multiple snapshots, different value", + func(vmdb vm.StateDB) { + vmdb.SetState(addr, key, value) + }, + func(vmdb vm.StateDB) { + // A -> B -> A - suite.Commit() + // Different value in 1st snapshot + value2 := common.BigToHash(big.NewInt(30)) + vmdb.SetState(addr, key, value2) - store := suite.App.CommitMultiStore().GetStore(suite.App.GetKey(types.StoreKey)) - iavlStore := store.(*iavl.Store) - commitID1 := iavlStore.LastCommitID() + // Back to original value in 2nd snapshot + _ = vmdb.Snapshot() + vmdb.SetState(addr, key, value) + }, + }, + } - // Set the same state again - db = statedb.New(suite.Ctx, suite.App.EvmKeeper, emptyTxConfig) - db.SetState(addr, key, value) - suite.Require().NoError(db.Commit()) + for _, tt := range tests { + suite.Run(tt.name, func() { + db := statedb.New(suite.Ctx, suite.App.EvmKeeper, emptyTxConfig) + tt.initializeState(db) - suite.Commit() + suite.Require().NoError(db.Commit()) + suite.Commit() - commitID2 := iavlStore.LastCommitID() + store := suite.App.CommitMultiStore().GetStore(suite.App.GetKey(types.StoreKey)) + iavlStore := store.(*iavl.Store) + commitID1 := iavlStore.LastCommitID() - // We can compare the commitIDs since this is *only* the x/evm store which - // doesn't change between blocks without state changes. Any version change, - // e.g. no-op change that was written when it shouldn't, will modify the - // hash. - suite.Require().Equal( - common.Bytes2Hex(commitID1.Hash), - common.Bytes2Hex(commitID2.Hash), - "evm store should be unchanged", - ) + // New statedb that should not modify the underlying store + db = statedb.New(suite.Ctx, suite.App.EvmKeeper, emptyTxConfig) + tt.maleate(db) + + suite.Require().NoError(db.Commit()) + suite.Commit() + + commitID2 := iavlStore.LastCommitID() + + // We can compare the commitIDs since this is *only* the x/evm store which + // doesn't change between blocks without state changes. Any version change, + // e.g. no-op change that was written when it shouldn't, will modify the + // hash. + suite.Require().Equal( + common.Bytes2Hex(commitID1.Hash), + common.Bytes2Hex(commitID2.Hash), + "evm store should be unchanged", + ) + + }) + } } func (suite *KeeperTestSuite) TestStateDB_IAVLConsistency() { From ed834a0bb612b456d2668e1de68e695cb5f4faef Mon Sep 17 00:00:00 2001 From: drklee3 Date: Mon, 11 Mar 2024 18:58:11 -0700 Subject: [PATCH 10/22] test: Add account number tests, extend hash test --- x/evm/keeper/state_transition_test.go | 197 +++++++++++++++++++++++++- 1 file changed, 194 insertions(+), 3 deletions(-) diff --git a/x/evm/keeper/state_transition_test.go b/x/evm/keeper/state_transition_test.go index b14ab2254e..34558ed72e 100644 --- a/x/evm/keeper/state_transition_test.go +++ b/x/evm/keeper/state_transition_test.go @@ -1,9 +1,12 @@ package keeper_test import ( + "bytes" "fmt" "math" "math/big" + "math/rand" + "sort" "strings" codectypes "github.com/cosmos/cosmos-sdk/codec/types" @@ -737,6 +740,166 @@ var ( emptyTxConfig statedb.TxConfig = statedb.NewEmptyTxConfig(blockHash) ) +func (suite *KeeperTestSuite) TestAccountNumberOrder() { + // New accounts should have account numbers in sorted order on Commit(), + // NOT when they were first touched. + + // Relevant journal based StateDB code: + // https://github.com/Kava-Labs/ethermint/blob/877e8fd1bd140c37ad05ed613f31e28f0130c0c4/x/evm/statedb/statedb.go#L464-L466 + // - New accounts are persisted in journal without an account number until + // Commit() is called, which iterates new accounts sorted by address and + // assigns account numbers in order. + + // This test ensures all the specified StateDB methods that should create + // accounts do so in a way that results in sorted account numbers in the + // following cases: + // - When accounts are touched in ascending order (by address) + // - When accounts are touched in descending order (by address) + // - When accounts are touched in random order + + type SingleTest struct { + name string + touch func(vmdb vm.StateDB, addr common.Address) + } + + tests := []SingleTest{ + { + "SetState", + func(vmdb vm.StateDB, addr common.Address) { + key := common.BytesToHash([]byte{1}) + value := common.BytesToHash([]byte{1}) + vmdb.SetState(addr, key, value) + }, + }, + { + "SetCode", + func(vmdb vm.StateDB, addr common.Address) { + vmdb.SetCode(addr, []byte{1}) + }, + }, + { + "SetNonce", + func(vmdb vm.StateDB, addr common.Address) { + vmdb.SetNonce(addr, 2) + }, + }, + { + "AddBalance", + func(vmdb vm.StateDB, addr common.Address) { + vmdb.AddBalance(addr, big.NewInt(5)) + }, + }, + { + "SubBalance", + func(vmdb vm.StateDB, addr common.Address) { + vmdb.SubBalance(addr, big.NewInt(0)) + }, + }, + } + + orderedAddrs := []common.Address{} + for i := 0; i < 5; i++ { + num := big.NewInt(int64(i + 1)) + addr := common.BigToAddress(num) + orderedAddrs = append(orderedAddrs, addr) + } + + testFn := func(addrs []common.Address, tt SingleTest) { + for _, addr := range addrs { + acc := suite.App.AccountKeeper.GetAccount(suite.Ctx, addr.Bytes()) + suite.Require().Nil(acc, "account should not exist yet") + } + + db := statedb.New(suite.Ctx, suite.App.EvmKeeper, emptyTxConfig) + + for _, addr := range addrs { + // Run the corresponding StateDB method that should touch an account + tt.touch(db, addr) + } + + suite.Require().NoError(db.Commit()) + suite.Commit() + + // Check account numbers + accounts := make([]authtypes.AccountI, len(addrs)) + for i, addr := range addrs { + acc := suite.App.AccountKeeper.GetAccount(suite.Ctx, addr.Bytes()) + suite.Require().NotNil(acc, "account should exist") + accounts[i] = acc + } + + sort.Slice(accounts, func(i, j int) bool { + return bytes.Compare(accounts[i].GetAddress().Bytes(), accounts[j].GetAddress().Bytes()) < 0 + }) + + // Ensure account numbers are in order + for i := 0; i < len(accounts)-1; i++ { + accNum1 := accounts[i].GetAccountNumber() + accNum2 := accounts[i+1].GetAccountNumber() + + suite.Require().Less( + accNum1, + accNum2, + "account numbers should be acending order", + ) + suite.Require().Equal( + accNum1+1, + accNum2, + "account numbers should be in order", + ) + } + } + + // Run the tests + for _, tt := range tests { + suite.Run(tt.name, func() { + suite.SetupTest() + + testFn(orderedAddrs, tt) + }) + + // Now do the same but reversed addresses + suite.Run(tt.name+"_reversed", func() { + suite.SetupTest() + + reversedAddrs := make([]common.Address, len(orderedAddrs)) + copy(reversedAddrs, orderedAddrs) + + for i := len(reversedAddrs)/2 - 1; i >= 0; i-- { + opp := len(reversedAddrs) - 1 - i + reversedAddrs[i], reversedAddrs[opp] = reversedAddrs[opp], reversedAddrs[i] + } + + // Make sure it's actually reversed now, descending order + for i := 0; i < len(reversedAddrs)-1; i++ { + suite.Require().True( + bytes.Compare(reversedAddrs[i].Bytes(), reversedAddrs[i+1].Bytes()) > 0, + "addresses should be in descending order", + ) + } + + testFn(reversedAddrs, tt) + }) + + // And again! but with random order + suite.Run(tt.name+"_random", func() { + suite.SetupTest() + + randomAddrs := make([]common.Address, len(orderedAddrs)) + copy(randomAddrs, orderedAddrs) + + // Shuffle + addrsShuffled := make([]common.Address, len(randomAddrs)) + perm := rand.Perm(len(randomAddrs)) + for i, v := range perm { + addrsShuffled[v] = randomAddrs[i] + } + + testFn(addrsShuffled, tt) + }) + } +} + func (suite *KeeperTestSuite) TestNoopStateChange_UnmodifiedIAVLTree() { // suite.T().Skip("CacheCtx StateDB does not currently skip noop state changes") @@ -767,7 +930,7 @@ func (suite *KeeperTestSuite) TestNoopStateChange_UnmodifiedIAVLTree() { maleate func(vmdb vm.StateDB) }{ { - "SetState - key/value same as committed", + "SetState - no extra snapshots", func(vmdb vm.StateDB) { vmdb.SetState(addr, key, value) }, @@ -776,7 +939,7 @@ func (suite *KeeperTestSuite) TestNoopStateChange_UnmodifiedIAVLTree() { }, }, { - "SetState - multiple snapshots, same value", + "SetState - 2nd snapshot, same value", func(vmdb vm.StateDB) { vmdb.SetState(addr, key, value) }, @@ -791,7 +954,7 @@ func (suite *KeeperTestSuite) TestNoopStateChange_UnmodifiedIAVLTree() { }, }, { - "SetState - multiple snapshots, different value", + "SetState - 2nd snapshot, different value", func(vmdb vm.StateDB) { vmdb.SetState(addr, key, value) }, @@ -807,10 +970,38 @@ func (suite *KeeperTestSuite) TestNoopStateChange_UnmodifiedIAVLTree() { vmdb.SetState(addr, key, value) }, }, + { + "SetState - multiple snapshots, different value", + func(vmdb vm.StateDB) { + vmdb.SetState(addr, key, value) + }, + func(vmdb vm.StateDB) { + // A -> B -> C -> A + + // Different value in 1st snapshot + value2 := common.BigToHash(big.NewInt(30)) + value3 := common.BigToHash(big.NewInt(40)) + _ = vmdb.Snapshot() + vmdb.SetState(addr, key, value2) + + // Extra empty snapshot for good measure + _ = vmdb.Snapshot() + + _ = vmdb.Snapshot() + vmdb.SetState(addr, key, value3) + + // Back to original value in last snapshot + _ = vmdb.Snapshot() + vmdb.SetState(addr, key, value) + }, + }, } for _, tt := range tests { suite.Run(tt.name, func() { + // reset + suite.SetupTest() + db := statedb.New(suite.Ctx, suite.App.EvmKeeper, emptyTxConfig) tt.initializeState(db) From e99425f2162ec05d38ab61432abe9e6f87ac7902 Mon Sep 17 00:00:00 2001 From: drklee3 Date: Mon, 11 Mar 2024 19:02:08 -0700 Subject: [PATCH 11/22] feat: Skip no-op contract state changes missing cosmos store changes still --- x/evm/keeper/statedb.go | 31 +++++++++++++++++++++++++ x/evm/statedb/ctx.go | 2 +- x/evm/statedb/interfaces.go | 2 ++ x/evm/statedb/statedb.go | 40 ++++++++++++++++++++++++++++----- x/evm/statedb/store.go | 45 +++++++++++++++++++++++++++++++++++++ 5 files changed, 113 insertions(+), 7 deletions(-) diff --git a/x/evm/keeper/statedb.go b/x/evm/keeper/statedb.go index 5dcefc0066..0f4ff48a3f 100644 --- a/x/evm/keeper/statedb.go +++ b/x/evm/keeper/statedb.go @@ -22,6 +22,8 @@ import ( sdkmath "cosmossdk.io/math" errorsmod "cosmossdk.io/errors" + "github.com/cosmos/cosmos-sdk/store/cachekv" + "github.com/cosmos/cosmos-sdk/store/gaskv" "github.com/cosmos/cosmos-sdk/store/prefix" sdk "github.com/cosmos/cosmos-sdk/types" authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" @@ -186,6 +188,35 @@ func (k *Keeper) SetState(ctx sdk.Context, addr common.Address, key, value commo ) } +func (k *Keeper) UnsetState(ctx sdk.Context, addr common.Address, key common.Hash) error { + ctxStore := ctx.KVStore(k.storeKey) + gasKVStore, ok := ctxStore.(*gaskv.Store) + if !ok { + return fmt.Errorf("expected gaskv.Store, got %T", ctxStore) + } + + // Use parent of store and try as cachekv.Store + ctxStore = gasKVStore.GetParent() + + cacheKVStore, ok := ctxStore.(*cachekv.Store) + if !ok { + return fmt.Errorf("expected cachekv.Store, got %T", ctxStore) + } + + storeKey := types.AddressStoragePrefix(addr) + storeKey = append(storeKey, key.Bytes()...) + + cacheKVStore.Unset(storeKey) + + k.Logger(ctx).Debug( + "state unset", + "ethereum-address", addr.Hex(), + "key", key.Hex(), + ) + + return nil +} + // SetCode set contract code, delete if code is empty. func (k *Keeper) SetCode(ctx sdk.Context, codeHash, code []byte) { store := prefix.NewStore(ctx.KVStore(k.storeKey), types.KeyPrefixCode) diff --git a/x/evm/statedb/ctx.go b/x/evm/statedb/ctx.go index 3439cf34b9..685221eae0 100644 --- a/x/evm/statedb/ctx.go +++ b/x/evm/statedb/ctx.go @@ -29,7 +29,7 @@ func NewSnapshotCtx(initialCtx sdk.Context) *SnapshotCommitCtx { // Create an initial snapshot of the initialCtx so no state is written until // Commit() is called. The ID is -1 but disregarded along with the // StoreRevertKey indices as this is only to branch the ctx. - _ = sCtx.Snapshot(0, StoreRevertKey{0, 0, 0}) + _ = sCtx.Snapshot(0, StoreRevertKey{0, 0, 0, 0}) return sCtx } diff --git a/x/evm/statedb/interfaces.go b/x/evm/statedb/interfaces.go index bdf0ef55e8..2fcdcacc4e 100644 --- a/x/evm/statedb/interfaces.go +++ b/x/evm/statedb/interfaces.go @@ -45,6 +45,8 @@ type Keeper interface { // Write methods, only called by `StateDB.Commit()` SetAccount(ctx sdk.Context, addr common.Address, account Account) error SetState(ctx sdk.Context, addr common.Address, key, value common.Hash) + UnsetState(ctx sdk.Context, addr common.Address, key common.Hash) error + SetCode(ctx sdk.Context, codeHash []byte, code []byte) SetBalance(ctx sdk.Context, addr common.Address, amount *big.Int) error DeleteAccount(ctx sdk.Context, addr common.Address) error diff --git a/x/evm/statedb/statedb.go b/x/evm/statedb/statedb.go index cbc7f4f17c..8ecc197198 100644 --- a/x/evm/statedb/statedb.go +++ b/x/evm/statedb/statedb.go @@ -253,12 +253,9 @@ func (s *StateDB) ForEachStorage(addr common.Address, cb func(key, value common. // AddBalance adds amount to the account associated with addr. func (s *StateDB) AddBalance(addr common.Address, amount *big.Int) { - // Only allow positive amounts. - // TODO: Geth apparently allows negative amounts, but can cause negative - // balance which is not allowed in bank keeper - if amount.Sign() != 1 { - return - } + // Geth apparently allows negative amounts, but can cause negative + // balance which is not allowed in bank keeper. However, we need to create + // the account still. account := s.getOrNewAccount(addr) @@ -321,6 +318,9 @@ func (s *StateDB) SetState(addr common.Address, key, value common.Hash) { // // End result: 0x0, but we cannot skip step 3 or it will be incorrectly 0x1 s.keeper.SetState(s.ctx.CurrentCtx(), addr, key, value) + + // Keep track of the key that had a state change + s.ephemeralStore.AddContractStateKey(addr, key) } // Suicide marks the given account as suicided. @@ -447,6 +447,34 @@ func (s *StateDB) Commit() error { } } + // Check for any state no-op changes and skip committing + for _, change := range s.ephemeralStore.GetContractStateKeys() { + committedValue := s.keeper.GetState(s.ctx.initialCtx, change.Addr, change.Key) + dirtyValue := s.keeper.GetState(s.ctx.CurrentCtx(), change.Addr, change.Key) + + // Different committed and dirty value, so we need to commit + if committedValue != dirtyValue { + continue + } + + // Remove no-op change from ALL snapshot contexts to prevent writing it + // to the store. This is necessary since the same key/value will still + // update the internal IAVL node version and thus the hash. + + // We can't just remove it from the latest snapshot context, since all + // snapshots are written to the store. A snapshot in the "middle" may + // contain a state change that isn't just a no-op change but a + // completely different value. + // Example: A -(snapshot)-> B -(snapshot)-> A + // -> We need to remove BOTH the B -> A change and the A -> B change, + // otherwise B will be written to the store. + for _, snapshot := range s.ctx.snapshots { + if err := s.keeper.UnsetState(snapshot.ctx, change.Addr, change.Key); err != nil { + return err + } + } + } + // Commit after account deletions s.ctx.Commit() diff --git a/x/evm/statedb/store.go b/x/evm/statedb/store.go index 6ec6c08822..d18ea0af59 100644 --- a/x/evm/statedb/store.go +++ b/x/evm/statedb/store.go @@ -12,6 +12,13 @@ type StoreRevertKey struct { RefundIndex int SuicidedAccountsIndex int LogsIndex int + ContractStatesIndex int +} + +// ContractStateKey represents the state key of a contract. +type ContractStateKey struct { + Addr common.Address + Key common.Hash } // EphemeralStore provides in-memory state of the refund and suicided accounts @@ -20,6 +27,7 @@ type EphemeralStore struct { RefundStates []uint64 SuicidedAccountStates []common.Address Logs []*ethtypes.Log + ContractStates []ContractStateKey } // NewEphemeralStore creates a new EphemeralStore. @@ -28,6 +36,7 @@ func NewEphemeralStore() *EphemeralStore { RefundStates: nil, SuicidedAccountStates: nil, Logs: nil, + ContractStates: nil, } } @@ -37,6 +46,7 @@ func (es *EphemeralStore) GetRevertKey() StoreRevertKey { RefundIndex: len(es.RefundStates), SuicidedAccountsIndex: len(es.SuicidedAccountStates), LogsIndex: len(es.Logs), + ContractStatesIndex: len(es.ContractStates), } } @@ -49,6 +59,7 @@ func (es *EphemeralStore) Revert(key StoreRevertKey) { es.RefundStates = es.RefundStates[:key.RefundIndex] es.SuicidedAccountStates = es.SuicidedAccountStates[:key.SuicidedAccountsIndex] es.Logs = es.Logs[:key.LogsIndex] + es.ContractStates = es.ContractStates[:key.ContractStatesIndex] } func (es *EphemeralStore) ValidateRevertKey(key StoreRevertKey) error { @@ -73,6 +84,13 @@ func (es *EphemeralStore) ValidateRevertKey(key StoreRevertKey) error { ) } + if key.ContractStatesIndex > len(es.ContractStates) { + return fmt.Errorf( + "invalid ContractStatesIndex, %d is greater than the length of the contract states (%d)", + key.ContractStatesIndex, len(es.ContractStates), + ) + } + return nil } @@ -147,3 +165,30 @@ func (es *EphemeralStore) GetAccountSuicided(addr common.Address) bool { func (es *EphemeralStore) GetAllSuicided() []common.Address { return es.SuicidedAccountStates } + +// ----------------------------------------------------------------------------- +// Contract states + +// AddContractState adds a contract state to the store. +func (es *EphemeralStore) AddContractStateKey(addr common.Address, key common.Hash) { + if es.HasContractStateKey(addr, key) { + return + } + + es.ContractStates = append(es.ContractStates, ContractStateKey{Addr: addr, Key: key}) +} + +func (es *EphemeralStore) HasContractStateKey(addr common.Address, key common.Hash) bool { + for _, state := range es.ContractStates { + if state.Addr == addr && state.Key == key { + return true + } + } + + return false +} + +// GetContractStateKeys returns all contract state keys. +func (es *EphemeralStore) GetContractStateKeys() []ContractStateKey { + return es.ContractStates +} From 94856c105038c280510a687b0628e9aacf2dbfb1 Mon Sep 17 00:00:00 2001 From: drklee3 Date: Tue, 12 Mar 2024 12:21:40 -0700 Subject: [PATCH 12/22] fix: Avoid skip on 0 value SubBalance --- x/evm/statedb/statedb.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/x/evm/statedb/statedb.go b/x/evm/statedb/statedb.go index 8ecc197198..c926e43de4 100644 --- a/x/evm/statedb/statedb.go +++ b/x/evm/statedb/statedb.go @@ -267,10 +267,10 @@ func (s *StateDB) AddBalance(addr common.Address, amount *big.Int) { // SubBalance subtracts amount from the account associated with addr. func (s *StateDB) SubBalance(addr common.Address, amount *big.Int) { - if amount.Sign() == 0 { - return - } - + // Avoid returning on 0 value to allow for account to be created still. + // This should be a non-issue if state clearing is implemented, as if there + // is an non-existent account and 0 balance is added, an account isn't + // created. account := s.getOrNewAccount(addr) account.Balance = new(big.Int).Sub(account.Balance, amount) From 6cf2664b7f3cb2f09d735b3895ed04ab4fe52480 Mon Sep 17 00:00:00 2001 From: drklee3 Date: Tue, 12 Mar 2024 12:22:25 -0700 Subject: [PATCH 13/22] test: Add account number legacy statedb tests for test correctness --- x/evm/keeper/state_transition_test.go | 129 ++++++++++++++++++-------- 1 file changed, 91 insertions(+), 38 deletions(-) diff --git a/x/evm/keeper/state_transition_test.go b/x/evm/keeper/state_transition_test.go index 34558ed72e..82c61a48d5 100644 --- a/x/evm/keeper/state_transition_test.go +++ b/x/evm/keeper/state_transition_test.go @@ -740,6 +740,11 @@ var ( emptyTxConfig statedb.TxConfig = statedb.NewEmptyTxConfig(blockHash) ) +type StateDBCommit interface { + vm.StateDB + Commit() error +} + func (suite *KeeperTestSuite) TestAccountNumberOrder() { // New accounts should have account numbers in sorted order on Commit(), // NOT when they were first touched. @@ -749,6 +754,8 @@ func (suite *KeeperTestSuite) TestAccountNumberOrder() { // - New accounts are persisted in journal without an account number until // Commit() is called, which iterates new accounts sorted by address and // assigns account numbers in order. + // - Every method that calls getOrNewStateObject will have the account + // created in Commit() if it doesn't exist yet. // This test ensures all the specified StateDB methods that should create // accounts do so in a way that results in sorted account numbers in the @@ -757,12 +764,12 @@ func (suite *KeeperTestSuite) TestAccountNumberOrder() { // - When accounts are touched in descending order (by address) // - When accounts are touched in random order - type SingleTest struct { + type MethodTest struct { name string touch func(vmdb vm.StateDB, addr common.Address) } - tests := []SingleTest{ + tests := []MethodTest{ { "SetState", func(vmdb vm.StateDB, addr common.Address) { @@ -797,6 +804,12 @@ func (suite *KeeperTestSuite) TestAccountNumberOrder() { }, } + // ------------------------------------------------------------------------- + // Addresses setup + // - Ascending order + // - Descending order + // - Random order + orderedAddrs := []common.Address{} for i := 0; i < 5; i++ { num := big.NewInt(int64(i + 1)) @@ -804,13 +817,51 @@ func (suite *KeeperTestSuite) TestAccountNumberOrder() { orderedAddrs = append(orderedAddrs, addr) } - testFn := func(addrs []common.Address, tt SingleTest) { + reversedAddrs := make([]common.Address, len(orderedAddrs)) + copy(reversedAddrs, orderedAddrs) + + for i := len(reversedAddrs)/2 - 1; i >= 0; i-- { + opp := len(reversedAddrs) - 1 - i + reversedAddrs[i], reversedAddrs[opp] = reversedAddrs[opp], reversedAddrs[i] + } + + // Make sure it's actually reversed now, descending order + for i := 0; i < len(reversedAddrs)-1; i++ { + suite.Require().True( + bytes.Compare(reversedAddrs[i].Bytes(), reversedAddrs[i+1].Bytes()) > 0, + "addresses should be in descending order", + ) + } + + randomAddrs := make([]common.Address, len(orderedAddrs)) + copy(randomAddrs, orderedAddrs) + + // Shuffle + addrsShuffled := make([]common.Address, len(randomAddrs)) + perm := rand.Perm(len(randomAddrs)) + for i, v := range perm { + addrsShuffled[v] = randomAddrs[i] + } + + // ------------------------------------------------------------------------- + // Shared test logic + + testFn := func( + stateDBConstructor func() StateDBCommit, + addrs []common.Address, + tt MethodTest, + ) { + // Ensure evm account already exists - otherwise it will be created on + // the first balance change in an account and have an account number gap + // in the user accounts (SetAccount -> SetBalance -> MintCoins) + _ = suite.App.AccountKeeper.GetModuleAccount(suite.Ctx, types.ModuleName) + for _, addr := range addrs { acc := suite.App.AccountKeeper.GetAccount(suite.Ctx, addr.Bytes()) suite.Require().Nil(acc, "account should not exist yet") } - db := statedb.New(suite.Ctx, suite.App.EvmKeeper, emptyTxConfig) + db := stateDBConstructor() for _, addr := range addrs { // Run the corresponding StateDB method that should touch an account @@ -840,62 +891,64 @@ func (suite *KeeperTestSuite) TestAccountNumberOrder() { suite.Require().Less( accNum1, accNum2, - "account numbers should be acending order", + "account numbers should be ascending order", ) - suite.Require().Equal( + suite.Require().Equalf( accNum1+1, accNum2, - "account numbers should be in order", + "account numbers should be in order, %v", + accounts, ) } } + ctxStateDBConstructor := func() StateDBCommit { + return statedb.New(suite.Ctx, suite.App.EvmKeeper, emptyTxConfig) + } + legacyStateDBConstructor := func() StateDBCommit { + return statedb_legacy.New(suite.Ctx, suite.App.EvmKeeper, emptyTxConfig) + } + // Run the tests for _, tt := range tests { - suite.Run(tt.name, func() { + // First run the tests against legacy statedb to ensure the correct + // behavior is expected + suite.Run(tt.name+"_legacy", func() { suite.SetupTest() - - testFn(orderedAddrs, tt) + testFn(legacyStateDBConstructor, orderedAddrs, tt) }) + continue - // Now do the same but reversed addresses - suite.Run(tt.name+"_reversed", func() { + suite.Run(tt.name+"_reversed_legacy", func() { suite.SetupTest() + testFn(legacyStateDBConstructor, reversedAddrs, tt) + }) - reversedAddrs := make([]common.Address, len(orderedAddrs)) - copy(reversedAddrs, orderedAddrs) + suite.Run(tt.name+"_random_legacy", func() { + suite.SetupTest() + testFn(legacyStateDBConstructor, addrsShuffled, tt) + }) + } - for i := len(reversedAddrs)/2 - 1; i >= 0; i-- { - opp := len(reversedAddrs) - 1 - i - reversedAddrs[i], reversedAddrs[opp] = reversedAddrs[opp], reversedAddrs[i] - } + return - // Make sure it's actually reversed now, descending order - for i := 0; i < len(reversedAddrs)-1; i++ { - suite.Require().True( - bytes.Compare(reversedAddrs[i].Bytes(), reversedAddrs[i+1].Bytes()) > 0, - "addresses should be in descending order", - ) - } + // CacheCtx statedb + for _, tt := range tests { + suite.Run(tt.name, func() { + suite.SetupTest() + testFn(ctxStateDBConstructor, orderedAddrs, tt) + }) - testFn(reversedAddrs, tt) + // Now do the same but reversed addresses + suite.Run(tt.name+"_reversed", func() { + suite.SetupTest() + testFn(ctxStateDBConstructor, reversedAddrs, tt) }) // And again! but with random order suite.Run(tt.name+"_random", func() { suite.SetupTest() - - randomAddrs := make([]common.Address, len(orderedAddrs)) - copy(randomAddrs, orderedAddrs) - - // Shuffle - addrsShuffled := make([]common.Address, len(randomAddrs)) - perm := rand.Perm(len(randomAddrs)) - for i, v := range perm { - addrsShuffled[v] = randomAddrs[i] - } - - testFn(addrsShuffled, tt) + testFn(ctxStateDBConstructor, addrsShuffled, tt) }) } } From 1279ba93c4e64e3f0db3b9f9fef648ad691cae19 Mon Sep 17 00:00:00 2001 From: drklee3 Date: Tue, 12 Mar 2024 12:24:08 -0700 Subject: [PATCH 14/22] test: Enable reverse/random addr tests --- x/evm/keeper/state_transition_test.go | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/x/evm/keeper/state_transition_test.go b/x/evm/keeper/state_transition_test.go index 82c61a48d5..c4ebda2214 100644 --- a/x/evm/keeper/state_transition_test.go +++ b/x/evm/keeper/state_transition_test.go @@ -805,7 +805,7 @@ func (suite *KeeperTestSuite) TestAccountNumberOrder() { } // ------------------------------------------------------------------------- - // Addresses setup + // Addresses setup -> All of these should have the same account numbers // - Ascending order // - Descending order // - Random order @@ -851,6 +851,9 @@ func (suite *KeeperTestSuite) TestAccountNumberOrder() { addrs []common.Address, tt MethodTest, ) { + // Reset app state + suite.SetupTest() + // Ensure evm account already exists - otherwise it will be created on // the first balance change in an account and have an account number gap // in the user accounts (SetAccount -> SetBalance -> MintCoins) @@ -914,18 +917,14 @@ func (suite *KeeperTestSuite) TestAccountNumberOrder() { // First run the tests against legacy statedb to ensure the correct // behavior is expected suite.Run(tt.name+"_legacy", func() { - suite.SetupTest() testFn(legacyStateDBConstructor, orderedAddrs, tt) }) - continue suite.Run(tt.name+"_reversed_legacy", func() { - suite.SetupTest() testFn(legacyStateDBConstructor, reversedAddrs, tt) }) suite.Run(tt.name+"_random_legacy", func() { - suite.SetupTest() testFn(legacyStateDBConstructor, addrsShuffled, tt) }) } @@ -935,19 +934,16 @@ func (suite *KeeperTestSuite) TestAccountNumberOrder() { // CacheCtx statedb for _, tt := range tests { suite.Run(tt.name, func() { - suite.SetupTest() testFn(ctxStateDBConstructor, orderedAddrs, tt) }) // Now do the same but reversed addresses suite.Run(tt.name+"_reversed", func() { - suite.SetupTest() testFn(ctxStateDBConstructor, reversedAddrs, tt) }) // And again! but with random order suite.Run(tt.name+"_random", func() { - suite.SetupTest() testFn(ctxStateDBConstructor, addrsShuffled, tt) }) } From 7bc076208be0382b79574fb1f097a3eba5081054 Mon Sep 17 00:00:00 2001 From: drklee3 Date: Tue, 12 Mar 2024 12:27:06 -0700 Subject: [PATCH 15/22] test: Skip account number cachectx tests --- x/evm/keeper/state_transition_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/x/evm/keeper/state_transition_test.go b/x/evm/keeper/state_transition_test.go index c4ebda2214..93507157eb 100644 --- a/x/evm/keeper/state_transition_test.go +++ b/x/evm/keeper/state_transition_test.go @@ -929,7 +929,7 @@ func (suite *KeeperTestSuite) TestAccountNumberOrder() { }) } - return + suite.T().Skip("CacheCtx StateDB does not currently support account number ordering") // CacheCtx statedb for _, tt := range tests { From 7f736258e2ed94c1afd582b1745646fb37c8ca75 Mon Sep 17 00:00:00 2001 From: drklee3 Date: Tue, 12 Mar 2024 15:36:54 -0700 Subject: [PATCH 16/22] deps: Update cosmos-sdk version with additional store methods --- go.mod | 49 ++++++++++++------------ go.sum | 117 ++++++++++++++++++++++++++++++++++----------------------- 2 files changed, 97 insertions(+), 69 deletions(-) diff --git a/go.mod b/go.mod index b4c276a53b..ec1d3de95e 100644 --- a/go.mod +++ b/go.mod @@ -12,6 +12,7 @@ require ( github.com/cosmos/cosmos-sdk v0.46.11 github.com/cosmos/go-bip39 v1.0.0 github.com/cosmos/gogoproto v1.4.3 + github.com/cosmos/iavl v0.19.5 github.com/cosmos/ibc-go/v6 v6.1.0 github.com/davecgh/go-spew v1.1.1 github.com/ethereum/go-ethereum v1.10.26 @@ -35,18 +36,18 @@ require ( github.com/tendermint/tendermint v0.34.27 github.com/tendermint/tm-db v0.6.7 github.com/tyler-smith/go-bip39 v1.1.0 - golang.org/x/net v0.8.0 - google.golang.org/genproto v0.0.0-20221227171554-f9683d7f8bef - google.golang.org/grpc v1.52.0 + golang.org/x/net v0.17.0 + google.golang.org/genproto/googleapis/api v0.0.0-20230920204549-e6e6cdab5c13 + google.golang.org/grpc v1.58.3 sigs.k8s.io/yaml v1.3.0 ) require ( - cloud.google.com/go v0.105.0 // indirect - cloud.google.com/go/compute v1.14.0 // indirect + cloud.google.com/go v0.110.8 // indirect + cloud.google.com/go/compute v1.23.0 // indirect cloud.google.com/go/compute/metadata v0.2.3 // indirect - cloud.google.com/go/iam v0.8.0 // indirect - cloud.google.com/go/storage v1.27.0 // indirect + cloud.google.com/go/iam v1.1.2 // indirect + cloud.google.com/go/storage v1.30.1 // indirect filippo.io/edwards25519 v1.0.0-rc.1 // indirect github.com/99designs/keyring v1.2.1 // indirect github.com/ChainSafe/go-schnorrkel v0.0.0-20200405005733-88cbf1b4c40d // indirect @@ -61,7 +62,7 @@ require ( github.com/btcsuite/btcd/chaincfg/chainhash v1.0.1 // indirect github.com/cenkalti/backoff/v4 v4.1.3 // indirect github.com/cespare/xxhash v1.1.0 // indirect - github.com/cespare/xxhash/v2 v2.1.2 // indirect + github.com/cespare/xxhash/v2 v2.2.0 // indirect github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e // indirect github.com/cockroachdb/apd/v2 v2.0.2 // indirect github.com/coinbase/rosetta-sdk-go v0.7.9 // indirect @@ -69,8 +70,7 @@ require ( github.com/confio/ics23/go v0.9.0 // indirect github.com/cosmos/btcutil v1.0.5 // indirect github.com/cosmos/gorocksdb v1.2.0 // indirect - github.com/cosmos/iavl v0.19.5 // indirect - github.com/cosmos/ledger-cosmos-go v0.12.2 // indirect + github.com/cosmos/ledger-cosmos-go v0.13.1 // indirect github.com/creachadair/taskgroup v0.3.2 // indirect github.com/danieljoos/wincred v1.1.2 // indirect github.com/deckarep/golang-set v1.8.0 // indirect @@ -97,16 +97,17 @@ require ( github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0 // indirect github.com/godbus/dbus v0.0.0-20190726142602-4481cbc300e2 // indirect github.com/gogo/gateway v1.1.0 // indirect - github.com/golang/glog v1.0.0 // indirect + github.com/golang/glog v1.1.0 // indirect github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect github.com/golang/snappy v0.0.4 // indirect github.com/google/btree v1.1.2 // indirect github.com/google/go-cmp v0.5.9 // indirect github.com/google/orderedcode v0.0.1 // indirect github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38 // indirect + github.com/google/s2a-go v0.1.4 // indirect github.com/google/uuid v1.3.0 // indirect - github.com/googleapis/enterprise-certificate-proxy v0.2.1 // indirect - github.com/googleapis/gax-go/v2 v2.7.0 // indirect + github.com/googleapis/enterprise-certificate-proxy v0.2.4 // indirect + github.com/googleapis/gax-go/v2 v2.12.0 // indirect github.com/gorilla/handlers v1.5.1 // indirect github.com/grpc-ecosystem/go-grpc-middleware v1.3.0 // indirect github.com/gsterjov/go-libsecret v0.0.0-20161001094733-a6f4afe4910c // indirect @@ -170,21 +171,23 @@ require ( github.com/tklauser/numcpus v0.4.0 // indirect github.com/ulikunitz/xz v0.5.8 // indirect github.com/zondax/hid v0.9.1 // indirect - github.com/zondax/ledger-go v0.14.1 // indirect + github.com/zondax/ledger-go v0.14.2 // indirect go.etcd.io/bbolt v1.3.6 // indirect go.opencensus.io v0.24.0 // indirect - golang.org/x/crypto v0.5.0 // indirect + golang.org/x/crypto v0.14.0 // indirect golang.org/x/exp v0.0.0-20230131160201-f062dba9d201 // indirect - golang.org/x/oauth2 v0.4.0 // indirect - golang.org/x/sync v0.1.0 // indirect - golang.org/x/sys v0.6.0 // indirect - golang.org/x/term v0.6.0 // indirect - golang.org/x/text v0.8.0 // indirect + golang.org/x/oauth2 v0.10.0 // indirect + golang.org/x/sync v0.3.0 // indirect + golang.org/x/sys v0.13.0 // indirect + golang.org/x/term v0.13.0 // indirect + golang.org/x/text v0.13.0 // indirect golang.org/x/tools v0.7.0 // indirect golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 // indirect - google.golang.org/api v0.107.0 // indirect + google.golang.org/api v0.128.0 // indirect google.golang.org/appengine v1.6.7 // indirect - google.golang.org/protobuf v1.28.2-0.20220831092852-f930b1dc76e8 // indirect + google.golang.org/genproto v0.0.0-20231002182017-d307bd883b97 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20231009173412-8bfb1ae86b6c // indirect + google.golang.org/protobuf v1.31.0 // indirect gopkg.in/ini.v1 v1.67.0 // indirect gopkg.in/natefinch/npipe.v2 v2.0.0-20160621034901-c1b8fa8bdcce // indirect gopkg.in/yaml.v2 v2.4.0 // indirect @@ -196,7 +199,7 @@ replace ( // use cosmos keyring github.com/99designs/keyring => github.com/cosmos/keyring v1.1.7-0.20210622111912-ef00f8ac3d76 // Use cosmos-sdk fork with staking transfer events, and custom tally handler support - github.com/cosmos/cosmos-sdk => github.com/kava-labs/cosmos-sdk v0.46.11-kava.1 + github.com/cosmos/cosmos-sdk => github.com/kava-labs/cosmos-sdk v0.46.12-0.20240312221657-2a8bd3a2ccc4 // Fix upstream GHSA-h395-qcrw-5vmq vulnerability. // TODO Remove it: https://github.com/cosmos/cosmos-sdk/issues/10409 github.com/gin-gonic/gin => github.com/gin-gonic/gin v1.7.0 diff --git a/go.sum b/go.sum index 45c94faa47..8c7ff0941e 100644 --- a/go.sum +++ b/go.sum @@ -19,8 +19,8 @@ cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHOb cloud.google.com/go v0.72.0/go.mod h1:M+5Vjvlc2wnp6tjzE102Dw08nGShTscUx2nZMufOKPI= cloud.google.com/go v0.74.0/go.mod h1:VV1xSbzvo+9QJOxLDaJfTjx5e+MePCpCWwvftOeQmWk= cloud.google.com/go v0.75.0/go.mod h1:VGuuCn7PG0dwsd5XPVm2Mm3wlh3EL55/79EKB6hlPTY= -cloud.google.com/go v0.105.0 h1:DNtEKRBAAzeS4KyIory52wWHuClNaXJ5x1F7xa4q+5Y= -cloud.google.com/go v0.105.0/go.mod h1:PrLgOJNe5nfE9UMxKxgXj4mD3voiP+YQ6gdt6KMFOKM= +cloud.google.com/go v0.110.8 h1:tyNdfIxjzaWctIiLYOTalaLKZ17SI44SKFW26QbOhME= +cloud.google.com/go v0.110.8/go.mod h1:Iz8AkXJf1qmxC3Oxoep8R1T36w8B92yU29PcBhHO5fk= cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE= cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc= @@ -28,15 +28,14 @@ cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUM cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc= cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ= cloud.google.com/go/bigtable v1.2.0/go.mod h1:JcVAOl45lrTmQfLj7T6TxyMzIN/3FGGcFm+2xVAli2o= -cloud.google.com/go/compute v1.14.0 h1:hfm2+FfxVmnRlh6LpB7cg1ZNU+5edAHmW679JePztk0= -cloud.google.com/go/compute v1.14.0/go.mod h1:YfLtxrj9sU4Yxv+sXzZkyPjEyPBZfXHUvjxega5vAdo= +cloud.google.com/go/compute v1.23.0 h1:tP41Zoavr8ptEqaW6j+LQOnyBBhO7OkOMAGrgLopTwY= +cloud.google.com/go/compute v1.23.0/go.mod h1:4tCnrn48xsqlwSAiLf1HXMQk8CONslYbdiEZc9FEIbM= cloud.google.com/go/compute/metadata v0.2.3 h1:mg4jlk7mCAj6xXp9UJ4fjI9VUI5rubuGBW5aJ7UnBMY= cloud.google.com/go/compute/metadata v0.2.3/go.mod h1:VAV5nSsACxMJvgaAuX6Pk2AawlZn8kiOGuCv6gTkwuA= cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk= -cloud.google.com/go/iam v0.8.0 h1:E2osAkZzxI/+8pZcxVLcDtAQx/u+hZXVryUaYQ5O0Kk= -cloud.google.com/go/iam v0.8.0/go.mod h1:lga0/y3iH6CX7sYqypWJ33hf7kkfXJag67naqGESjkE= -cloud.google.com/go/longrunning v0.3.0 h1:NjljC+FYPV3uh5/OwWT6pVU+doBqMg2x/rZlE+CamDs= +cloud.google.com/go/iam v1.1.2 h1:gacbrBdWcoVmGLozRuStX45YKvJtzIjJdAolzUs1sm4= +cloud.google.com/go/iam v1.1.2/go.mod h1:A5avdyVL2tCppe4unb0951eI9jreack+RJ0/d+KUZOU= cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I= cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw= cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA= @@ -47,8 +46,8 @@ cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohl cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs= cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0= cloud.google.com/go/storage v1.14.0/go.mod h1:GrKmX003DSIwi9o29oFT7YDnHYwZoctc3fOKtUw0Xmo= -cloud.google.com/go/storage v1.27.0 h1:YOO045NZI9RKfCj1c5A/ZtuuENUc8OAW+gHdGnDgyMQ= -cloud.google.com/go/storage v1.27.0/go.mod h1:x9DOL8TK/ygDUMieqwfhdpQryTeEkhGKMi80i/iqR2s= +cloud.google.com/go/storage v1.30.1 h1:uOdMxAs8HExqBlnLtnQyP0YkvbiDpdGShGKtx6U/oNM= +cloud.google.com/go/storage v1.30.1/go.mod h1:NfxhC0UJE1aXSx7CIIbCf7y9HKT7BiccwkR7+P7gN8E= collectd.org v0.3.0/go.mod h1:A/8DzQBkF6abtvrT2j/AU/4tiBgJWYyh0y/oB/4MlWE= cosmossdk.io/errors v1.0.0-beta.7 h1:gypHW76pTQGVnHKo6QBkb4yFOJjC+sUGRc5Al3Odj1w= cosmossdk.io/errors v1.0.0-beta.7/go.mod h1:mz6FQMJRku4bY7aqS/Gwfcmr/ue91roMEKAmDUDpBfE= @@ -180,8 +179,9 @@ github.com/cespare/cp v0.1.0/go.mod h1:SOGHArjBr4JWaSDEVpWpo/hNg6RoKrls6Oh40hiwW github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko= github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= -github.com/cespare/xxhash/v2 v2.1.2 h1:YRXhKfTDauu4ajMg1TPgFO5jnlC2HCbmLXMcTG5cbYE= github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44= +github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/cheggaaa/pb v1.0.27/go.mod h1:pQciLPpbU0oxA0h+VJYYLxO+XeDQb5pZijXscXHm81s= github.com/chzyer/logex v1.1.10 h1:Swpa1K6QvQznwJRcfTfQJmTE72DqScAa40E+fbHEXEE= github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= @@ -197,6 +197,10 @@ github.com/cloudflare/cloudflare-go v0.14.0/go.mod h1:EnwdgGMaFOruiPZRFSgn+TsQ3h github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= +github.com/cncf/udpa/go v0.0.0-20210930031921-04548b0d99d4/go.mod h1:6pvJx4me5XPnfI9Z40ddWsdw2W/uZgQLFXToKeRcDiI= +github.com/cncf/xds/go v0.0.0-20210805033703-aa0b78936158/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +github.com/cncf/xds/go v0.0.0-20210922020428-25de7278fc84/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= github.com/cockroachdb/apd/v2 v2.0.2 h1:weh8u7Cneje73dDh+2tEVLUvyBc89iwepWCD8b8034E= github.com/cockroachdb/apd/v2 v2.0.2/go.mod h1:DDxRlzC2lo3/vSlmSoS7JkqbbrARPuFOGr0B9pvN3Gw= github.com/cockroachdb/datadriven v0.0.0-20190809214429-80d97fb3cbaa/go.mod h1:zn76sxSg3SzpJ0PPJaLDCu+Bu0Lg3sKTORVIj19EIF8= @@ -238,8 +242,8 @@ github.com/cosmos/ibc-go/v6 v6.1.0 h1:o7oXws2vKkKfOFzJI+oNylRn44PCNt5wzHd/zKQKbv github.com/cosmos/ibc-go/v6 v6.1.0/go.mod h1:CY3zh2HLfetRiW8LY6kVHMATe90Wj/UOoY8T6cuB0is= github.com/cosmos/keyring v1.1.7-0.20210622111912-ef00f8ac3d76 h1:DdzS1m6o/pCqeZ8VOAit/gyATedRgjvkVI+UCrLpyuU= github.com/cosmos/keyring v1.1.7-0.20210622111912-ef00f8ac3d76/go.mod h1:0mkLWIoZuQ7uBoospo5Q9zIpqq6rYCPJDSUdeCJvPM8= -github.com/cosmos/ledger-cosmos-go v0.12.2 h1:/XYaBlE2BJxtvpkHiBm97gFGSGmYGKunKyF3nNqAXZA= -github.com/cosmos/ledger-cosmos-go v0.12.2/go.mod h1:ZcqYgnfNJ6lAXe4HPtWgarNEY+B74i+2/8MhZw4ziiI= +github.com/cosmos/ledger-cosmos-go v0.13.1 h1:12ac9+GwBb9BjP7X5ygpFk09Itwzjzfmg6A2CWFjoVs= +github.com/cosmos/ledger-cosmos-go v0.13.1/go.mod h1:5tv2RVJEd2+Y38TIQN4CRjJeQGyqOEiKJDfqhk5UjqE= github.com/cpuguy83/go-md2man v1.0.10 h1:BSKMNlYxDvnunlTymqtgONjNnaRV1sTpcovwwjF22jk= github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE= github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= @@ -310,6 +314,7 @@ github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.m github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= github.com/envoyproxy/go-control-plane v0.9.7/go.mod h1:cwu0lG7PUMfa9snN8LXBig5ynNVH9qI8YYLbd1fK2po= github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= +github.com/envoyproxy/go-control-plane v0.9.10-0.20210907150352-cf90f659a021/go.mod h1:AFq3mo9L8Lqqiid3OhADV3RfLJnjiw63cSpi+fDTRC0= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= github.com/ethereum/go-ethereum v1.10.17/go.mod h1:Lt5WzjM07XlXc95YzrhosmR4J9Ahd6X2wyEV2SvGhk0= github.com/ethereum/go-ethereum v1.10.26 h1:i/7d9RBBwiXCEuyduBQzJw/mKmnvzsN14jqBmytw72s= @@ -402,8 +407,8 @@ github.com/golang-jwt/jwt/v4 v4.3.0/go.mod h1:/xlHOz8bRuivTWchD4jCa+NbatV+wEUSzw github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0/go.mod h1:E/TSTwGwJL78qG/PmXZO1EjYhfJinVAhrmmHX6Z8B9k= github.com/golang/geo v0.0.0-20190916061304-5b978397cfec/go.mod h1:QZ0nwyI2jOfgRAoBvP+ab5aRr7c9x7lhGEJrKvBwjWI= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= -github.com/golang/glog v1.0.0 h1:nfP3RFugxnNRyKgeWd4oI1nYvXpxrx8ck8ZrcizshdQ= -github.com/golang/glog v1.0.0/go.mod h1:EWib/APOK0SL3dFbYqvxE3UYd8E6s1ouQ7iEp/0LWV4= +github.com/golang/glog v1.1.0 h1:/d3pCKDPWNnvIWe0vVUpNP32qc8U3PDVxySP/y360qE= +github.com/golang/glog v1.1.0/go.mod h1:pfYeQZ3JWZoXTV5sFc986z3HTpwQs9At6P4ImfuP3NQ= github.com/golang/groupcache v0.0.0-20160516000752-02826c3e7903/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= @@ -468,7 +473,7 @@ github.com/google/martian v2.1.0+incompatible h1:/CP5g8u/VJHijgedC/Legn3BAbAaWPg github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= github.com/google/martian/v3 v3.1.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= -github.com/google/martian/v3 v3.2.1 h1:d8MncMlErDFTwQGBK1xhv026j9kqhvw1Qv9IbWT1VLQ= +github.com/google/martian/v3 v3.3.2 h1:IqNFLAmvJOgVlpdEBiQbDc2EwKW77amAycfTuWKdfvw= github.com/google/orderedcode v0.0.1 h1:UzfcAexk9Vhv8+9pNOgRu41f16lHq725vPwnSeiG/Us= github.com/google/orderedcode v0.0.1/go.mod h1:iVyU4/qPKHY5h/wSd6rZZCDcLJNxiWO6dvsYES2Sb20= github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= @@ -484,18 +489,20 @@ github.com/google/pprof v0.0.0-20201218002935-b9804c9f04c2/go.mod h1:kpwsk12EmLe github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38 h1:yAJXTCF9TqKcTiHJAE8dj7HMvPfh66eeA2JYW7eFpSE= github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= +github.com/google/s2a-go v0.1.4 h1:1kZ/sQM3srePvKs3tXAvQzo66XfcReoqFpIpIccE7Oc= +github.com/google/s2a-go v0.1.4/go.mod h1:Ej+mSEMGRnqRzjc7VtF+jdBwYG5fuJfiZ8ELkjEwM0A= github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.2.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/googleapis/enterprise-certificate-proxy v0.2.1 h1:RY7tHKZcRlk788d5WSo/e83gOyyy742E8GSs771ySpg= -github.com/googleapis/enterprise-certificate-proxy v0.2.1/go.mod h1:AwSRAtLfXpU5Nm3pW+v7rGDHp09LsPtGY9MduiEsR9k= +github.com/googleapis/enterprise-certificate-proxy v0.2.4 h1:uGy6JWR/uMIILU8wbf+OkstIrNiMjGpEIyhx8f6W7s4= +github.com/googleapis/enterprise-certificate-proxy v0.2.4/go.mod h1:AwSRAtLfXpU5Nm3pW+v7rGDHp09LsPtGY9MduiEsR9k= github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= -github.com/googleapis/gax-go/v2 v2.7.0 h1:IcsPKeInNvYi7eqSaDjiZqDDKu5rsmunY0Y1YupQSSQ= -github.com/googleapis/gax-go/v2 v2.7.0/go.mod h1:TEop28CZZQ2y+c0VxMUmu1lV+fQx57QpBWsYpwqHJx8= +github.com/googleapis/gax-go/v2 v2.12.0 h1:A+gCJKdRfqXkr+BIRGtZLibNXf0m1f9E4HG56etFpas= +github.com/googleapis/gax-go/v2 v2.12.0/go.mod h1:y+aIqrI5eb1YGMVJfuV3185Ts/D7qKpsEkdD5+I6QGU= github.com/googleapis/google-cloud-go-testing v0.0.0-20200911160855-bcd43fbb19e8/go.mod h1:dvDLG8qkwmyD9a/MJJN3XJcT3xFxOKAvTZGvuZmac9g= github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg= @@ -633,8 +640,8 @@ github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8 github.com/jung-kurt/gofpdf v1.0.3-0.20190309125859-24315acbbda5/go.mod h1:7Id9E/uU8ce6rXgefFLlgrJj/GYY22cpxn+r32jIOes= github.com/jwilder/encoding v0.0.0-20170811194829-b4e1701a28ef/go.mod h1:Ct9fl0F6iIOGgxJ5npU/IUOhOhqlVrGjyIZc8/MagT0= github.com/karalabe/usb v0.0.2/go.mod h1:Od972xHfMJowv7NGVDiWVxk2zxnWgjLlJzE+F4F7AGU= -github.com/kava-labs/cosmos-sdk v0.46.11-kava.1 h1:3VRpm4zf/gQgmpRVd1p99/2P8ZecAu2FVAXHru5caIo= -github.com/kava-labs/cosmos-sdk v0.46.11-kava.1/go.mod h1:bG4AkW9bqc8ycrryyKGQEl3YV9BY2wr6HggGq8kvcgM= +github.com/kava-labs/cosmos-sdk v0.46.12-0.20240312221657-2a8bd3a2ccc4 h1:0mmBdpPOyj4oyxVHkgLh8eVWB2X0/PhNdrAsbCOYT5M= +github.com/kava-labs/cosmos-sdk v0.46.12-0.20240312221657-2a8bd3a2ccc4/go.mod h1:bSUUbmVwWkv1ZNVTWrQHa/i+73xIUvYYPsCvl5doiCs= github.com/keybase/go-keychain v0.0.0-20190712205309-48d3d31d256d h1:Z+RDyXzjKE0i2sTjZ/b1uxiGtPhFy34Ou/Tk0qwN0kM= github.com/keybase/go-keychain v0.0.0-20190712205309-48d3d31d256d/go.mod h1:JJNrCn9otv/2QP4D7SMJBgaleKpOf66PnW6F5WGNRIc= github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= @@ -1018,10 +1025,11 @@ github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9de github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= +github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= github.com/zondax/hid v0.9.1 h1:gQe66rtmyZ8VeGFcOpbuH3r7erYtNEAezCAYu8LdkJo= github.com/zondax/hid v0.9.1/go.mod h1:l5wttcP0jwtdLjqjMMWFVEE7d1zO0jvSPA9OPZxWpEM= -github.com/zondax/ledger-go v0.14.1 h1:Pip65OOl4iJ84WTpA4BKChvOufMhhbxED3BaihoZN4c= -github.com/zondax/ledger-go v0.14.1/go.mod h1:fZ3Dqg6qcdXWSOJFKMG8GCTnD7slO/RL2feOQv8K320= +github.com/zondax/ledger-go v0.14.2 h1:NDaba434N7JUVKg4P4nFv6TOjSkUosYb9sdiw3c61Zk= +github.com/zondax/ledger-go v0.14.2/go.mod h1:fZ3Dqg6qcdXWSOJFKMG8GCTnD7slO/RL2feOQv8K320= go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= go.etcd.io/bbolt v1.3.6 h1:/ecaJf0sk1l4l6V4awd65v2C3ILy7MSj+s/x1ADCIMU= go.etcd.io/bbolt v1.3.6/go.mod h1:qXsaaIqmgQH0T+OPdb99Bf+PKfBBQVAdyD6TY9G8XM4= @@ -1036,6 +1044,7 @@ go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk= go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0= go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo= +go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI= go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/atomic v1.5.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= @@ -1066,9 +1075,11 @@ golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad/go.mod h1:jdWPYTVW3xRLrWP golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20211108221036-ceb1ce70b4fa/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/crypto v0.5.0 h1:U/0M97KRkSFvyD/3FSmdP5W5swImpNgle/EHFhOsQPE= -golang.org/x/crypto v0.5.0/go.mod h1:NK/OQwhpMQP3MwtdjgLlYHnH9ebylxKWv3e0fK+mkQU= +golang.org/x/crypto v0.0.0-20220314234659-1baeb1ce4c0b/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= +golang.org/x/crypto v0.14.0 h1:wBqGXzWJW6m1XrIKlAH0Hs1JJ7+9KBwnIO8v66Q9cHc= +golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4= golang.org/x/exp v0.0.0-20180321215751-8460e604b9de/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20180807140117-3d87b88a115f/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= @@ -1110,6 +1121,7 @@ golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= golang.org/x/mod v0.9.0 h1:KENHtAZL2y3NLMYZeHY9DW8HW8V+kQyJsY/V9JlKvCs= golang.org/x/net v0.0.0-20180719180050-a680a1efc54d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -1164,10 +1176,12 @@ golang.org/x/net v0.0.0-20210610132358-84b48f89b13b/go.mod h1:9nx3DQGgdP8bBQD5qx golang.org/x/net v0.0.0-20210614182718-04defd469f4e/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= -golang.org/x/net v0.8.0 h1:Zrh2ngAOFYneWTAIAPethzeaQLuHwhuBkuV6ZiRnUaQ= -golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc= +golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= +golang.org/x/net v0.17.0 h1:pVaXccu2ozPjCXewfr1S7xza/zcXTity9cCdXQYSjIM= +golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= @@ -1179,8 +1193,8 @@ golang.org/x/oauth2 v0.0.0-20201208152858-08078c50e5b5/go.mod h1:KelEdhl1UZF7XfJ golang.org/x/oauth2 v0.0.0-20210218202405-ba52d332ba99/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20220223155221-ee480838109b/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc= -golang.org/x/oauth2 v0.4.0 h1:NF0gk8LVPg1Ml7SSbGyySuoxdsXitj7TvgvuRxIMc/M= -golang.org/x/oauth2 v0.4.0/go.mod h1:RznEsdpjGAINPTOF0UH/t+xJ75L18YO3Ho6Pyn+uRec= +golang.org/x/oauth2 v0.10.0 h1:zHCpF2Khkwy4mMB4bv0U37YtJdTGW8jI0glAApi0Kh8= +golang.org/x/oauth2 v0.10.0/go.mod h1:kTpgurOux7LqtuxjuyZa4Gj2gdezIt/jQtGnNFfypQI= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -1192,8 +1206,9 @@ golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o= -golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.3.0 h1:ftCYgMx6zT/asHUrPw8BLLscYtGznsLAnjq5RH9P66E= +golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -1277,15 +1292,17 @@ golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220128215802-99c3d69c2c27/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220517195934-5e4e11fc645e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.6.0 h1:MVltZSvRTcU2ljQOhs94SXPftV6DCNnZViHeQps87pQ= -golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.13.0 h1:Af8nKPmuFypiUBjVoU9V20FiaFXOcuZI21p0ycVYYGE= +golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= -golang.org/x/term v0.6.0 h1:clScbb1cHjoCkyRbWwBEUZ5H/tIFu5TAXIqaZD0Gcjw= -golang.org/x/term v0.6.0/go.mod h1:m6U89DPEgQRMq3DNkDClhWw02AUbt2daBVO4cn4Hv9U= +golang.org/x/term v0.13.0 h1:bb+I9cTfFazGW51MZqBVmZy7+JEJMouUHTUSKVQLBek= +golang.org/x/term v0.13.0/go.mod h1:LTmsnFJwVN6bCy1rVCoS+qHT1HhALEFxKncY3WNNh4U= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -1295,8 +1312,9 @@ golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= -golang.org/x/text v0.8.0 h1:57P1ETyNKtuIjB4SRd15iJxuhj8Gc416Y78H3qgMh68= -golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= +golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ= +golang.org/x/text v0.13.0 h1:ablQoSUd0tRdKxZewP80B+BaqeKJuVhuRxj/dkrun3k= +golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= @@ -1363,6 +1381,7 @@ golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4f golang.org/x/tools v0.0.0-20210108195828-e2f9c7f1fc8e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= +golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= golang.org/x/tools v0.7.0 h1:W4OVu8VVOaIO0yzWMNdepAulS7YfoS3Zabrm8DOXXU4= golang.org/x/tools v0.7.0/go.mod h1:4pg6aUX35JBAogB10C9AtvVL+qowtN4pT3CGSQex14s= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= @@ -1397,8 +1416,8 @@ google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz513 google.golang.org/api v0.35.0/go.mod h1:/XrVsuzM0rZmrsbjJutiuftIzeuTQcEeaYcSk/mQ1dg= google.golang.org/api v0.36.0/go.mod h1:+z5ficQTmoYpPn8LCUNVpK5I7hwkpjbcgqA7I34qYtE= google.golang.org/api v0.40.0/go.mod h1:fYKFpnQN0DsDSKRVRcQSDQNtqWPfM9i+zNPxepjRCQ8= -google.golang.org/api v0.107.0 h1:I2SlFjD8ZWabaIFOfeEDg3pf0BHJDh6iYQ1ic3Yu/UU= -google.golang.org/api v0.107.0/go.mod h1:2Ts0XTHNVWxypznxWOYUeI4g3WdP9Pk2Qk58+a/O9MY= +google.golang.org/api v0.128.0 h1:RjPESny5CnQRn9V6siglged+DZCgfu9l6mO9dkX9VOg= +google.golang.org/api v0.128.0/go.mod h1:Y611qgqaE92On/7g65MQgxYul3c0rEB894kniWLY750= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= google.golang.org/appengine v1.2.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= @@ -1452,8 +1471,12 @@ google.golang.org/genproto v0.0.0-20201214200347-8c77b98c765d/go.mod h1:FWY/as6D google.golang.org/genproto v0.0.0-20210108203827-ffc7fda8c3d7/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20210126160654-44e461bb6506/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20210226172003-ab064af71705/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20221227171554-f9683d7f8bef h1:uQ2vjV/sHTsWSqdKeLqmwitzgvjMl7o4IdtHwUDXSJY= -google.golang.org/genproto v0.0.0-20221227171554-f9683d7f8bef/go.mod h1:RGgjbofJ8xD9Sq1VVhDM1Vok1vRONV+rg+CjzG4SZKM= +google.golang.org/genproto v0.0.0-20231002182017-d307bd883b97 h1:SeZZZx0cP0fqUyA+oRzP9k7cSwJlvDFiROO72uwD6i0= +google.golang.org/genproto v0.0.0-20231002182017-d307bd883b97/go.mod h1:t1VqOqqvce95G3hIDCT5FeO3YUc6Q4Oe24L/+rNMxRk= +google.golang.org/genproto/googleapis/api v0.0.0-20230920204549-e6e6cdab5c13 h1:U7+wNaVuSTaUqNvK2+osJ9ejEZxbjHHk8F2b6Hpx0AE= +google.golang.org/genproto/googleapis/api v0.0.0-20230920204549-e6e6cdab5c13/go.mod h1:RdyHbowztCGQySiCvQPgWQWgWhGnouTdCflKoDBt32U= +google.golang.org/genproto/googleapis/rpc v0.0.0-20231009173412-8bfb1ae86b6c h1:jHkCUWkseRf+W+edG5hMzr/Uh1xkDREY4caybAq4dpY= +google.golang.org/genproto/googleapis/rpc v0.0.0-20231009173412-8bfb1ae86b6c/go.mod h1:4cYg8o5yUbm77w8ZX00LhMVNl/YVBFJRYWDc0uYWMs0= google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.19.1/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= @@ -1478,8 +1501,10 @@ google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTp google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc= google.golang.org/grpc v1.34.0/go.mod h1:WotjhfgOW/POjDeRt8vscBtXq+2VjORFy659qA51WJ8= google.golang.org/grpc v1.35.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= -google.golang.org/grpc v1.52.0 h1:kd48UiU7EHsV4rnLyOJRuP/Il/UHE7gdDAQ+SZI7nZk= -google.golang.org/grpc v1.52.0/go.mod h1:pu6fVzoFb+NBYNAvQL08ic+lvB2IojljRYuun5vorUY= +google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= +google.golang.org/grpc v1.45.0/go.mod h1:lN7owxKUQEqMfSyQikvvk5tf/6zMPsrK+ONuO11+0rQ= +google.golang.org/grpc v1.58.3 h1:BjnpXut1btbtgN/6sp+brB2Kbm2LjNXnidYujAVbSoQ= +google.golang.org/grpc v1.58.3/go.mod h1:tgX3ZQDlNJGU96V6yHh1T/JeoBQ2TXdr43YbYSsCJk0= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= @@ -1492,8 +1517,8 @@ google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGj google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= -google.golang.org/protobuf v1.28.2-0.20220831092852-f930b1dc76e8 h1:KR8+MyP7/qOlV+8Af01LtjL04bu7on42eVsxT4EyBQk= -google.golang.org/protobuf v1.28.2-0.20220831092852-f930b1dc76e8/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= +google.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8= +google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= @@ -1551,4 +1576,4 @@ rsc.io/tmplfunc v0.0.3/go.mod h1:AG3sTPzElb1Io3Yg4voV9AGZJuleGAwaVRxL9M49PhA= sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o= sigs.k8s.io/yaml v1.3.0 h1:a2VclLzOGrwOHDiV8EfBGhvjHvP46CtW5j6POvhYGGo= sigs.k8s.io/yaml v1.3.0/go.mod h1:GeOyir5tyXNByN85N/dRIT9es5UQNerPYEKK56eTBm8= -sourcegraph.com/sourcegraph/appdash v0.0.0-20190731080439-ebfcffb1b5c0/go.mod h1:hI742Nqp5OhwiqlzhgfbWU4mW4yO10fP+LoT9WOswdU= \ No newline at end of file +sourcegraph.com/sourcegraph/appdash v0.0.0-20190731080439-ebfcffb1b5c0/go.mod h1:hI742Nqp5OhwiqlzhgfbWU4mW4yO10fP+LoT9WOswdU= From cd77f54bb508f9af67a7764ebf80daf52bf89ebb Mon Sep 17 00:00:00 2001 From: drklee3 Date: Tue, 12 Mar 2024 17:19:07 -0700 Subject: [PATCH 17/22] refactor: Rename legacy statedb package --- x/evm/keeper/state_transition_test.go | 24 +++++++++++-------- .../access_list.go | 2 +- .../config.go | 2 +- .../interfaces.go | 2 +- .../journal.go | 2 +- .../state_object.go | 2 +- .../statedb.go | 2 +- x/evm/statedb/statedb.go | 2 +- 8 files changed, 21 insertions(+), 17 deletions(-) rename x/evm/{statedb_legacy => legacystatedb}/access_list.go (99%) rename x/evm/{statedb_legacy => legacystatedb}/config.go (98%) rename x/evm/{statedb_legacy => legacystatedb}/interfaces.go (98%) rename x/evm/{statedb_legacy => legacystatedb}/journal.go (99%) rename x/evm/{statedb_legacy => legacystatedb}/state_object.go (99%) rename x/evm/{statedb_legacy => legacystatedb}/statedb.go (99%) diff --git a/x/evm/keeper/state_transition_test.go b/x/evm/keeper/state_transition_test.go index 93507157eb..e4351986b5 100644 --- a/x/evm/keeper/state_transition_test.go +++ b/x/evm/keeper/state_transition_test.go @@ -22,8 +22,8 @@ import ( "github.com/ethereum/go-ethereum/params" "github.com/evmos/ethermint/tests" "github.com/evmos/ethermint/x/evm/keeper" + "github.com/evmos/ethermint/x/evm/legacystatedb" "github.com/evmos/ethermint/x/evm/statedb" - "github.com/evmos/ethermint/x/evm/statedb_legacy" "github.com/evmos/ethermint/x/evm/types" "github.com/tendermint/tendermint/crypto/tmhash" tmproto "github.com/tendermint/tendermint/proto/tendermint/types" @@ -791,7 +791,13 @@ func (suite *KeeperTestSuite) TestAccountNumberOrder() { }, }, { - "AddBalance", + "AddBalance-0", + func(vmdb vm.StateDB, addr common.Address) { + vmdb.AddBalance(addr, big.NewInt(0)) + }, + }, + { + "AddBalance-NonZero", func(vmdb vm.StateDB, addr common.Address) { vmdb.AddBalance(addr, big.NewInt(5)) }, @@ -905,11 +911,8 @@ func (suite *KeeperTestSuite) TestAccountNumberOrder() { } } - ctxStateDBConstructor := func() StateDBCommit { - return statedb.New(suite.Ctx, suite.App.EvmKeeper, emptyTxConfig) - } legacyStateDBConstructor := func() StateDBCommit { - return statedb_legacy.New(suite.Ctx, suite.App.EvmKeeper, emptyTxConfig) + return legacystatedb.New(suite.Ctx, suite.App.EvmKeeper, emptyTxConfig) } // Run the tests @@ -931,6 +934,10 @@ func (suite *KeeperTestSuite) TestAccountNumberOrder() { suite.T().Skip("CacheCtx StateDB does not currently support account number ordering") + ctxStateDBConstructor := func() StateDBCommit { + return statedb.New(suite.Ctx, suite.App.EvmKeeper, emptyTxConfig) + } + // CacheCtx statedb for _, tt := range tests { suite.Run(tt.name, func() { @@ -1085,9 +1092,6 @@ func (suite *KeeperTestSuite) TestNoopStateChange_UnmodifiedIAVLTree() { } func (suite *KeeperTestSuite) TestStateDB_IAVLConsistency() { - // evm store keys prefixes: - // Code = 1 - // Storage = 2 addr1 := common.BigToAddress(big.NewInt(1)) addr2 := common.BigToAddress(big.NewInt(2)) @@ -1168,7 +1172,7 @@ func (suite *KeeperTestSuite) TestStateDB_IAVLConsistency() { suite.SetupTest() suite.Commit() - legacyDB := statedb_legacy.New(suite.Ctx, suite.App.EvmKeeper, emptyTxConfig) + legacyDB := legacystatedb.New(suite.Ctx, suite.App.EvmKeeper, emptyTxConfig) tt.maleate(legacyDB) suite.Require().NoError(legacyDB.Commit()) diff --git a/x/evm/statedb_legacy/access_list.go b/x/evm/legacystatedb/access_list.go similarity index 99% rename from x/evm/statedb_legacy/access_list.go rename to x/evm/legacystatedb/access_list.go index 21e34adcff..31e4458426 100644 --- a/x/evm/statedb_legacy/access_list.go +++ b/x/evm/legacystatedb/access_list.go @@ -14,7 +14,7 @@ // You should have received a copy of the GNU Lesser General Public License // along with the go-ethereum library. If not, see . -package statedb_legacy +package legacystatedb import ( "github.com/ethereum/go-ethereum/common" diff --git a/x/evm/statedb_legacy/config.go b/x/evm/legacystatedb/config.go similarity index 98% rename from x/evm/statedb_legacy/config.go rename to x/evm/legacystatedb/config.go index 0e7dd60036..1e3ca62af2 100644 --- a/x/evm/statedb_legacy/config.go +++ b/x/evm/legacystatedb/config.go @@ -13,7 +13,7 @@ // // You should have received a copy of the GNU Lesser General Public License // along with the Ethermint library. If not, see https://github.com/evmos/ethermint/blob/main/LICENSE -package statedb_legacy +package legacystatedb import ( "math/big" diff --git a/x/evm/statedb_legacy/interfaces.go b/x/evm/legacystatedb/interfaces.go similarity index 98% rename from x/evm/statedb_legacy/interfaces.go rename to x/evm/legacystatedb/interfaces.go index f0d63dcad2..534bc776ec 100644 --- a/x/evm/statedb_legacy/interfaces.go +++ b/x/evm/legacystatedb/interfaces.go @@ -13,7 +13,7 @@ // // You should have received a copy of the GNU Lesser General Public License // along with the Ethermint library. If not, see https://github.com/evmos/ethermint/blob/main/LICENSE -package statedb_legacy +package legacystatedb import ( sdk "github.com/cosmos/cosmos-sdk/types" diff --git a/x/evm/statedb_legacy/journal.go b/x/evm/legacystatedb/journal.go similarity index 99% rename from x/evm/statedb_legacy/journal.go rename to x/evm/legacystatedb/journal.go index da9f90da3a..f34b41625f 100644 --- a/x/evm/statedb_legacy/journal.go +++ b/x/evm/legacystatedb/journal.go @@ -14,7 +14,7 @@ // You should have received a copy of the GNU Lesser General Public License // along with the go-ethereum library. If not, see . -package statedb_legacy +package legacystatedb import ( "bytes" diff --git a/x/evm/statedb_legacy/state_object.go b/x/evm/legacystatedb/state_object.go similarity index 99% rename from x/evm/statedb_legacy/state_object.go rename to x/evm/legacystatedb/state_object.go index ece880f01a..0305b24dcb 100644 --- a/x/evm/statedb_legacy/state_object.go +++ b/x/evm/legacystatedb/state_object.go @@ -13,7 +13,7 @@ // // You should have received a copy of the GNU Lesser General Public License // along with the Ethermint library. If not, see https://github.com/evmos/ethermint/blob/main/LICENSE -package statedb_legacy +package legacystatedb import ( "bytes" diff --git a/x/evm/statedb_legacy/statedb.go b/x/evm/legacystatedb/statedb.go similarity index 99% rename from x/evm/statedb_legacy/statedb.go rename to x/evm/legacystatedb/statedb.go index 6b87daaf49..325323c23e 100644 --- a/x/evm/statedb_legacy/statedb.go +++ b/x/evm/legacystatedb/statedb.go @@ -13,7 +13,7 @@ // // You should have received a copy of the GNU Lesser General Public License // along with the Ethermint library. If not, see https://github.com/evmos/ethermint/blob/main/LICENSE -package statedb_legacy +package legacystatedb import ( "fmt" diff --git a/x/evm/statedb/statedb.go b/x/evm/statedb/statedb.go index c926e43de4..ef6d419a53 100644 --- a/x/evm/statedb/statedb.go +++ b/x/evm/statedb/statedb.go @@ -452,7 +452,7 @@ func (s *StateDB) Commit() error { committedValue := s.keeper.GetState(s.ctx.initialCtx, change.Addr, change.Key) dirtyValue := s.keeper.GetState(s.ctx.CurrentCtx(), change.Addr, change.Key) - // Different committed and dirty value, so we need to commit + // Different committed and dirty value, keep it for commit if committedValue != dirtyValue { continue } From 00f53e72caad661a597656ca80df5aab7efbced0 Mon Sep 17 00:00:00 2001 From: drklee3 Date: Tue, 12 Mar 2024 17:20:10 -0700 Subject: [PATCH 18/22] remove key logging --- app/utils.go | 2 -- 1 file changed, 2 deletions(-) diff --git a/app/utils.go b/app/utils.go index 1115013318..611187843a 100644 --- a/app/utils.go +++ b/app/utils.go @@ -17,7 +17,6 @@ package app import ( "encoding/json" - "fmt" "time" "github.com/cosmos/cosmos-sdk/codec" @@ -119,7 +118,6 @@ func NewTestGenesisState(codec codec.Codec) simapp.GenesisState { senderPrivKey := secp256k1.PrivKey{ Key: common.Hex2Bytes("6b65793a225c745c3337355c33363627365c333735476c5c3331375f5c323530305c3031375c303237375c3337365c3234315c3336355c323136247d475c3237316a3c5c3336365f785c3337353b5c3336305c3031362220"), } - fmt.Printf("senderPrivKey: %x\n", senderPrivKey) acc := authtypes.NewBaseAccount(senderPrivKey.PubKey().Address().Bytes(), senderPrivKey.PubKey(), 0, 0) balance := banktypes.Balance{ Address: acc.GetAddress().String(), From 3613d6305793c30ca9c75b9ef3d1820d14395e4d Mon Sep 17 00:00:00 2001 From: drklee3 Date: Wed, 28 Feb 2024 11:40:49 -0800 Subject: [PATCH 19/22] style: Fix lint issues --- app/utils.go | 2 +- x/evm/testutil/suite.go | 5 ++++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/app/utils.go b/app/utils.go index 611187843a..f584f40490 100644 --- a/app/utils.go +++ b/app/utils.go @@ -116,7 +116,7 @@ func NewTestGenesisState(codec codec.Codec) simapp.GenesisState { // generate genesis account senderPrivKey := secp256k1.PrivKey{ - Key: common.Hex2Bytes("6b65793a225c745c3337355c33363627365c333735476c5c3331375f5c323530305c3031375c303237375c3337365c3234315c3336355c323136247d475c3237316a3c5c3336365f785c3337353b5c3336305c3031362220"), + Key: common.Hex2Bytes("6b65793a225c745c3337355c33363627365c333735476c5c3331375f5c323530305c3031375c303237375c3337365c3234315c3336355c323136247d475c3237316a3c5c3336365f785c3337353b5c3336305c3031362220"), //nolint:lll } acc := authtypes.NewBaseAccount(senderPrivKey.PubKey().Address().Bytes(), senderPrivKey.PubKey(), 0, 0) balance := banktypes.Balance{ diff --git a/x/evm/testutil/suite.go b/x/evm/testutil/suite.go index 26ac7ff0c6..00d639a77a 100644 --- a/x/evm/testutil/suite.go +++ b/x/evm/testutil/suite.go @@ -313,7 +313,10 @@ func (suite *TestSuite) MustTransferERC20Token(t require.TestingT, contractAddr, return ercTransferTx } -func (suite *TestSuite) TransferERC20Token(contractAddr, from, to common.Address, amount *big.Int) (*types.MsgEthereumTx, *types.MsgEthereumTxResponse, error) { +func (suite *TestSuite) TransferERC20Token( + contractAddr, from, to common.Address, + amount *big.Int, +) (*types.MsgEthereumTx, *types.MsgEthereumTxResponse, error) { ctx := sdk.WrapSDKContext(suite.Ctx) chainID := suite.App.EvmKeeper.ChainID() From 9b2d90a79d8f7b1f540c47c55954454613723a00 Mon Sep 17 00:00:00 2001 From: drklee3 Date: Wed, 13 Mar 2024 12:56:09 -0700 Subject: [PATCH 20/22] test: Run unmodified IAVL tree test on legacy statedb --- x/evm/keeper/state_transition_test.go | 38 +++++++++++++++++++++++++-- 1 file changed, 36 insertions(+), 2 deletions(-) diff --git a/x/evm/keeper/state_transition_test.go b/x/evm/keeper/state_transition_test.go index e4351986b5..4ef3c719fb 100644 --- a/x/evm/keeper/state_transition_test.go +++ b/x/evm/keeper/state_transition_test.go @@ -897,10 +897,11 @@ func (suite *KeeperTestSuite) TestAccountNumberOrder() { accNum1 := accounts[i].GetAccountNumber() accNum2 := accounts[i+1].GetAccountNumber() - suite.Require().Less( + suite.Require().Lessf( accNum1, accNum2, - "account numbers should be ascending order", + "account numbers should be ascending order, %v", + accounts, ) suite.Require().Equalf( accNum1+1, @@ -1086,7 +1087,40 @@ func (suite *KeeperTestSuite) TestNoopStateChange_UnmodifiedIAVLTree() { common.Bytes2Hex(commitID2.Hash), "evm store should be unchanged", ) + }) + + suite.Run(tt.name+"_legacy", func() { + // reset + suite.SetupTest() + + db := legacystatedb.New(suite.Ctx, suite.App.EvmKeeper, emptyTxConfig) + tt.initializeState(db) + + suite.Require().NoError(db.Commit()) + suite.Commit() + + store := suite.App.CommitMultiStore().GetStore(suite.App.GetKey(types.StoreKey)) + iavlStore := store.(*iavl.Store) + commitID1 := iavlStore.LastCommitID() + + // New statedb that should not modify the underlying store + db = legacystatedb.New(suite.Ctx, suite.App.EvmKeeper, emptyTxConfig) + tt.maleate(db) + + suite.Require().NoError(db.Commit()) + suite.Commit() + + commitID2 := iavlStore.LastCommitID() + // We can compare the commitIDs since this is *only* the x/evm store which + // doesn't change between blocks without state changes. Any version change, + // e.g. no-op change that was written when it shouldn't, will modify the + // hash. + suite.Require().Equal( + common.Bytes2Hex(commitID1.Hash), + common.Bytes2Hex(commitID2.Hash), + "evm store should be unchanged", + ) }) } } From 6958d5a7e4a00f38d45575312751a871a71b890b Mon Sep 17 00:00:00 2001 From: drklee3 Date: Wed, 13 Mar 2024 12:56:56 -0700 Subject: [PATCH 21/22] feat: Prototype to align account numbers behavior with legacy statedb --- x/evm/keeper/state_transition_test.go | 4 +- x/evm/keeper/statedb.go | 55 +++++++++++++++++++++++++++ x/evm/statedb/ctx.go | 2 +- x/evm/statedb/interfaces.go | 2 + x/evm/statedb/statedb.go | 8 ++++ x/evm/statedb/statedb_test.go | 2 + x/evm/statedb/store.go | 39 +++++++++++++++++++ 7 files changed, 108 insertions(+), 4 deletions(-) diff --git a/x/evm/keeper/state_transition_test.go b/x/evm/keeper/state_transition_test.go index 4ef3c719fb..ee153de14c 100644 --- a/x/evm/keeper/state_transition_test.go +++ b/x/evm/keeper/state_transition_test.go @@ -933,7 +933,7 @@ func (suite *KeeperTestSuite) TestAccountNumberOrder() { }) } - suite.T().Skip("CacheCtx StateDB does not currently support account number ordering") + /// suite.T().Skip("CacheCtx StateDB does not currently support account number ordering") ctxStateDBConstructor := func() StateDBCommit { return statedb.New(suite.Ctx, suite.App.EvmKeeper, emptyTxConfig) @@ -958,8 +958,6 @@ func (suite *KeeperTestSuite) TestAccountNumberOrder() { } func (suite *KeeperTestSuite) TestNoopStateChange_UnmodifiedIAVLTree() { - // suite.T().Skip("CacheCtx StateDB does not currently skip noop state changes") - // On StateDB.Commit(), if there is a dirty state change that matches the // committed state, it should be skipped. Only state changes that are // different from committed state should be applied to the underlying store. diff --git a/x/evm/keeper/statedb.go b/x/evm/keeper/statedb.go index 0f4ff48a3f..a212d6a1c9 100644 --- a/x/evm/keeper/statedb.go +++ b/x/evm/keeper/statedb.go @@ -16,8 +16,10 @@ package keeper import ( + "bytes" "fmt" "math/big" + "sort" sdkmath "cosmossdk.io/math" @@ -169,6 +171,59 @@ func (k *Keeper) SetAccount(ctx sdk.Context, addr common.Address, account stated return nil } +func (k *Keeper) ReassignAccountNumbers(ctx sdk.Context, addrs []common.Address) error { + if len(addrs) == 0 { + return nil + } + + accounts := make([]authtypes.AccountI, len(addrs)) + for i, addr := range addrs { + cosmosAddr := sdk.AccAddress(addr.Bytes()) + acct := k.accountKeeper.GetAccount(ctx, cosmosAddr) + if acct == nil { + return fmt.Errorf("account not found: %s", addr) + } + + accounts[i] = acct + } + + // Sort accounts by account number + sort.Slice(accounts, func(i, j int) bool { + return accounts[i].GetAccountNumber() < accounts[j].GetAccountNumber() + }) + + accountNumberStart := accounts[0].GetAccountNumber() + + // Ensure there are no number gaps + for i, acct := range accounts { + if acct.GetAccountNumber() != accountNumberStart+uint64(i) { + return fmt.Errorf( + "account number mismatch: expected %d, got %d", + accountNumberStart+uint64(i), acct.GetAccountNumber(), + ) + } + } + + // Sort accounts by address + sort.Slice(accounts, func(i, j int) bool { + return bytes.Compare(accounts[i].GetAddress(), accounts[j].GetAddress()) < 0 + }) + + // Reassign account numbers + for i, acct := range accounts { + ethAcct, ok := acct.(ethermint.EthAccountI) + if !ok { + return fmt.Errorf("invalid account type: %T", acct) + } + + // set account number + ethAcct.SetAccountNumber(accountNumberStart + uint64(i)) + k.accountKeeper.SetAccount(ctx, acct) + } + + return nil +} + // SetState update contract storage, delete if value is empty. func (k *Keeper) SetState(ctx sdk.Context, addr common.Address, key, value common.Hash) { store := prefix.NewStore(ctx.KVStore(k.storeKey), types.AddressStoragePrefix(addr)) diff --git a/x/evm/statedb/ctx.go b/x/evm/statedb/ctx.go index 685221eae0..5fe1bebfbb 100644 --- a/x/evm/statedb/ctx.go +++ b/x/evm/statedb/ctx.go @@ -29,7 +29,7 @@ func NewSnapshotCtx(initialCtx sdk.Context) *SnapshotCommitCtx { // Create an initial snapshot of the initialCtx so no state is written until // Commit() is called. The ID is -1 but disregarded along with the // StoreRevertKey indices as this is only to branch the ctx. - _ = sCtx.Snapshot(0, StoreRevertKey{0, 0, 0, 0}) + _ = sCtx.Snapshot(0, StoreRevertKey{0, 0, 0, 0, 0}) return sCtx } diff --git a/x/evm/statedb/interfaces.go b/x/evm/statedb/interfaces.go index 2fcdcacc4e..0e108160ee 100644 --- a/x/evm/statedb/interfaces.go +++ b/x/evm/statedb/interfaces.go @@ -47,6 +47,8 @@ type Keeper interface { SetState(ctx sdk.Context, addr common.Address, key, value common.Hash) UnsetState(ctx sdk.Context, addr common.Address, key common.Hash) error + ReassignAccountNumbers(ctx sdk.Context, addrs []common.Address) error + SetCode(ctx sdk.Context, codeHash []byte, code []byte) SetBalance(ctx sdk.Context, addr common.Address, amount *big.Int) error DeleteAccount(ctx sdk.Context, addr common.Address) error diff --git a/x/evm/statedb/statedb.go b/x/evm/statedb/statedb.go index ef6d419a53..f85dcc3f06 100644 --- a/x/evm/statedb/statedb.go +++ b/x/evm/statedb/statedb.go @@ -202,6 +202,7 @@ func (s *StateDB) getOrNewAccount(addr common.Address) *Account { account := s.keeper.GetAccount(s.ctx.CurrentCtx(), addr) if account == nil { account = NewEmptyAccount() + s.ephemeralStore.AddCreatedAccount(addr) } return account @@ -475,6 +476,13 @@ func (s *StateDB) Commit() error { } } + // Re-assign account numbers for newly created accounts in this tx to be + // ordered by address. + createdAddrs := s.ephemeralStore.GetAllCreatedAccounts() + if err := s.keeper.ReassignAccountNumbers(s.ctx.CurrentCtx(), createdAddrs); err != nil { + return err + } + // Commit after account deletions s.ctx.Commit() diff --git a/x/evm/statedb/statedb_test.go b/x/evm/statedb/statedb_test.go index 3cbedae2bb..518b05f297 100644 --- a/x/evm/statedb/statedb_test.go +++ b/x/evm/statedb/statedb_test.go @@ -12,6 +12,7 @@ import ( "github.com/evmos/ethermint/x/evm/keeper" "github.com/evmos/ethermint/x/evm/statedb" "github.com/evmos/ethermint/x/evm/testutil" + "github.com/evmos/ethermint/x/evm/types" "github.com/stretchr/testify/suite" ) @@ -382,6 +383,7 @@ func (suite *StateDBTestSuite) TestRevertSnapshot() { for _, tc := range testCases { suite.Run(tc.name, func() { suite.SetupTest() + suite.App.AccountKeeper.GetModuleAccount(suite.Ctx, types.ModuleName) ctx := suite.Ctx keeper := suite.App.EvmKeeper diff --git a/x/evm/statedb/store.go b/x/evm/statedb/store.go index d18ea0af59..c6d98f72e4 100644 --- a/x/evm/statedb/store.go +++ b/x/evm/statedb/store.go @@ -13,6 +13,7 @@ type StoreRevertKey struct { SuicidedAccountsIndex int LogsIndex int ContractStatesIndex int + CreatedAccountsIndex int } // ContractStateKey represents the state key of a contract. @@ -28,6 +29,7 @@ type EphemeralStore struct { SuicidedAccountStates []common.Address Logs []*ethtypes.Log ContractStates []ContractStateKey + CreatedAccounts []common.Address } // NewEphemeralStore creates a new EphemeralStore. @@ -37,6 +39,7 @@ func NewEphemeralStore() *EphemeralStore { SuicidedAccountStates: nil, Logs: nil, ContractStates: nil, + CreatedAccounts: nil, } } @@ -47,6 +50,7 @@ func (es *EphemeralStore) GetRevertKey() StoreRevertKey { SuicidedAccountsIndex: len(es.SuicidedAccountStates), LogsIndex: len(es.Logs), ContractStatesIndex: len(es.ContractStates), + CreatedAccountsIndex: len(es.CreatedAccounts), } } @@ -60,6 +64,7 @@ func (es *EphemeralStore) Revert(key StoreRevertKey) { es.SuicidedAccountStates = es.SuicidedAccountStates[:key.SuicidedAccountsIndex] es.Logs = es.Logs[:key.LogsIndex] es.ContractStates = es.ContractStates[:key.ContractStatesIndex] + es.CreatedAccounts = es.CreatedAccounts[:key.CreatedAccountsIndex] } func (es *EphemeralStore) ValidateRevertKey(key StoreRevertKey) error { @@ -91,6 +96,13 @@ func (es *EphemeralStore) ValidateRevertKey(key StoreRevertKey) error { ) } + if key.CreatedAccountsIndex > len(es.CreatedAccounts) { + return fmt.Errorf( + "invalid CreatedAccountsIndex, %d is greater than the length of the created accounts (%d)", + key.CreatedAccountsIndex, len(es.CreatedAccounts), + ) + } + return nil } @@ -192,3 +204,30 @@ func (es *EphemeralStore) HasContractStateKey(addr common.Address, key common.Ha func (es *EphemeralStore) GetContractStateKeys() []ContractStateKey { return es.ContractStates } + +// ----------------------------------------------------------------------------- +// Created accounts + +// AddCreatedAccount adds a created account to the store. +func (es *EphemeralStore) AddCreatedAccount(addr common.Address) { + if es.HasCreatedAccount(addr) { + return + } + + es.CreatedAccounts = append(es.CreatedAccounts, addr) +} + +func (es *EphemeralStore) HasCreatedAccount(addr common.Address) bool { + for _, a := range es.CreatedAccounts { + if a == addr { + return true + } + } + + return false +} + +// GetAllCreatedAccounts returns all created accounts. +func (es *EphemeralStore) GetAllCreatedAccounts() []common.Address { + return es.CreatedAccounts +} From 08cd82118c66e9fe01a568e13a1692a535a5097e Mon Sep 17 00:00:00 2001 From: drklee3 Date: Wed, 13 Mar 2024 15:00:05 -0700 Subject: [PATCH 22/22] feat: Update ReassignAccountNumbers docs --- x/evm/keeper/statedb.go | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/x/evm/keeper/statedb.go b/x/evm/keeper/statedb.go index a212d6a1c9..cefe5135cb 100644 --- a/x/evm/keeper/statedb.go +++ b/x/evm/keeper/statedb.go @@ -171,11 +171,16 @@ func (k *Keeper) SetAccount(ctx sdk.Context, addr common.Address, account stated return nil } +// ReassignAccountNumbers reassign account numbers for the given addresses, +// ensuring they are incrementing in order by address and that there are no +// account number gaps, e.g. Accounts that have been created in side effects +// during a transaction and not passed to this method. func (k *Keeper) ReassignAccountNumbers(ctx sdk.Context, addrs []common.Address) error { if len(addrs) == 0 { return nil } + // Get all the accounts accounts := make([]authtypes.AccountI, len(addrs)) for i, addr := range addrs { cosmosAddr := sdk.AccAddress(addr.Bytes()) @@ -192,9 +197,10 @@ func (k *Keeper) ReassignAccountNumbers(ctx sdk.Context, addrs []common.Address) return accounts[i].GetAccountNumber() < accounts[j].GetAccountNumber() }) + // Min account number as start accountNumberStart := accounts[0].GetAccountNumber() - // Ensure there are no number gaps + // Ensure there are no gaps in account numbers for i, acct := range accounts { if acct.GetAccountNumber() != accountNumberStart+uint64(i) { return fmt.Errorf( @@ -209,7 +215,7 @@ func (k *Keeper) ReassignAccountNumbers(ctx sdk.Context, addrs []common.Address) return bytes.Compare(accounts[i].GetAddress(), accounts[j].GetAddress()) < 0 }) - // Reassign account numbers + // Reassign account numbers in order of account address for i, acct := range accounts { ethAcct, ok := acct.(ethermint.EthAccountI) if !ok {