diff --git a/core/evm.go b/core/evm.go new file mode 100644 index 000000000..fcd294370 --- /dev/null +++ b/core/evm.go @@ -0,0 +1,39 @@ +// Copyright (C) 2023, Berachain Foundation. All rights reserved. +// See the file LICENSE for licensing terms. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +package core + +import ( + "math/big" + + "github.com/berachain/stargazer/core/state" + "github.com/berachain/stargazer/core/vm" + "github.com/berachain/stargazer/lib/common" +) + +// Compile-time interface check. +var _ vm.CanTransferFunc = CanTransfer +var _ vm.TransferFunc = Transfer + +// `CanTransfer` checks whether there are enough funds in the address' account to make a transfer. +// NOTE: This does not take the necessary gas in to account to make the transfer valid. +func CanTransfer(sdb vm.StateDB, addr common.Address, amount *big.Int) bool { + return sdb.GetBalance(addr).Cmp(amount) >= 0 +} + +// `Transfer` subtracts amount from sender and adds amount to recipient using the `vm.StateDB`. +func Transfer(sdb vm.StateDB, sender, recipient common.Address, amount *big.Int) { + // We use `TransferBalance` to use the same logic as the native transfer in x/bank. + sdb.(state.ExtStateDB).TransferBalance(sender, recipient, amount) +} diff --git a/core/state/cache_entries.go b/core/state/cache_entries.go new file mode 100644 index 000000000..06ba5f656 --- /dev/null +++ b/core/state/cache_entries.go @@ -0,0 +1,68 @@ +// Copyright (C) 2022, Berachain Foundation. All rights reserved. +// See the file LICENSE for licensing terms. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +//nolint:ireturn // all `CacheEntries` must adhere to the same interface. +package state + +import ( + "github.com/berachain/stargazer/core/state/store/journal" +) + +var ( + _ journal.CacheEntry = &RefundChange{} + _ journal.CacheEntry = &AddLogChange{} +) + +type ( + AddLogChange struct { + sdb *StateDB + } + RefundChange struct { + sdb *StateDB + prev uint64 + } +) + +// ============================================================================== +// AddLogChange +// ============================================================================== + +// `Revert` implements `journal.CacheEntry`. +func (ce *AddLogChange) Revert() { + ce.sdb.logs = ce.sdb.logs[:len(ce.sdb.logs)-1] +} + +// `Clone` implements `journal.CacheEntry`. +func (ce *AddLogChange) Clone() journal.CacheEntry { + return &AddLogChange{ + sdb: ce.sdb, + } +} + +// ============================================================================== +// RefundChange +// ============================================================================== + +// `Revert` implements `journal.CacheEntry`. +func (ce *RefundChange) Revert() { + ce.sdb.refund = ce.prev +} + +// `Clone` implements `journal.CacheEntry`. +func (ce *RefundChange) Clone() journal.CacheEntry { + return &RefundChange{ + sdb: ce.sdb, + prev: ce.prev, + } +} diff --git a/core/state/cache_entries_test.go b/core/state/cache_entries_test.go new file mode 100644 index 000000000..ec6594483 --- /dev/null +++ b/core/state/cache_entries_test.go @@ -0,0 +1,86 @@ +// Copyright (C) 2023, Berachain Foundation. All rights reserved. +// See the file LICENSE for licensing terms. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +package state + +import ( + "github.com/berachain/stargazer/core/state/store/journal" + coretypes "github.com/berachain/stargazer/core/types" + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" +) + +var _ = Describe("AddLogChange", func() { + var ( + ce *AddLogChange + sdb *StateDB + ) + BeforeEach(func() { + sdb = &StateDB{ + logs: []*coretypes.Log{}, + } + ce = &AddLogChange{ + sdb: sdb, + } + }) + It("implements journal.CacheEntry", func() { + var _ journal.CacheEntry = ce + Expect(ce).To(BeAssignableToTypeOf(&AddLogChange{})) + }) + It("Revert should remove the last log", func() { + sdb.logs = append(sdb.logs, &coretypes.Log{}) + ce.Revert() + Expect(len(sdb.logs)).To(Equal(0)) + }) + It("Clone should return a new AddLogChange with the same sdb", func() { + cloned, ok := ce.Clone().(*AddLogChange) + Expect(ok).To(BeTrue()) + Expect(cloned.sdb).To(Equal(sdb)) + Expect(cloned).ToNot(BeIdenticalTo(ce)) + }) +}) + +var _ = Describe("RefundChange", func() { + var ( + ce *RefundChange + sdb *StateDB + ) + + BeforeEach(func() { + sdb = &StateDB{ + refund: 0, + } + ce = &RefundChange{ + sdb: sdb, + prev: 0, + } + }) + It("implements journal.CacheEntry", func() { + var _ journal.CacheEntry = ce + Expect(ce).To(BeAssignableToTypeOf(&RefundChange{})) + }) + It("Revert should restore the previous refund value", func() { + sdb.refund = 100 + ce.prev = 50 + ce.Revert() + Expect(sdb.refund).To(Equal(uint64(50))) + }) + It("Clone should return a new RefundChange with the same sdb and prev", func() { + cloned, ok := ce.Clone().(*RefundChange) + Expect(ok).To(BeTrue()) + Expect(cloned.sdb).To(Equal(sdb)) + Expect(cloned.prev).To(Equal(ce.prev)) + Expect(cloned).ToNot(BeIdenticalTo(ce)) + }) +}) diff --git a/core/state/interface.go b/core/state/interface.go new file mode 100644 index 000000000..bac1905d2 --- /dev/null +++ b/core/state/interface.go @@ -0,0 +1,34 @@ +// Copyright (C) 2023, Berachain Foundation. All rights reserved. +// See the file LICENSE for licensing terms. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +package state + +import ( + "math/big" + + "github.com/ethereum/go-ethereum/core/vm" + + "github.com/berachain/stargazer/lib/common" +) + +type GethStateDB = vm.StateDB + +// `StargazerStateDB` defines an extension to the interface provided by go-ethereum to +// support additional state transition functionalities that are useful in a Cosmos SDK context. +type StargazerStateDB interface { + GethStateDB + + // TransferBalance transfers the balance from one account to another + TransferBalance(common.Address, common.Address, *big.Int) +} diff --git a/core/state/state_test.go b/core/state/state_test.go new file mode 100644 index 000000000..5549b4d57 --- /dev/null +++ b/core/state/state_test.go @@ -0,0 +1,27 @@ +// Copyright (C) 2023, Berachain Foundation. All rights reserved. +// See the file LICENSE for licensing terms. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +package state_test + +import ( + "testing" + + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" +) + +func TestState(t *testing.T) { + RegisterFailHandler(Fail) + RunSpecs(t, "core/state") +} diff --git a/core/state/statedb.go b/core/state/statedb.go index 2a6d67a76..e104ac318 100644 --- a/core/state/statedb.go +++ b/core/state/statedb.go @@ -1,4 +1,4 @@ -// Copyright (C) 2023, Berachain Foundation. All rights reserved. +// Copyright (C) 2022, Berachain Foundation. All rights reserved. // See the file LICENSE for licensing terms. // // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" @@ -14,4 +14,594 @@ package state -// test codecov +import ( + "bytes" + "fmt" + "math/big" + + storetypes "github.com/cosmos/cosmos-sdk/store/types" + sdk "github.com/cosmos/cosmos-sdk/types" + + "github.com/berachain/stargazer/core/state/store/cachekv" + "github.com/berachain/stargazer/core/state/store/cachemulti" + "github.com/berachain/stargazer/core/state/types" + coretypes "github.com/berachain/stargazer/core/types" + "github.com/berachain/stargazer/lib/common" + "github.com/berachain/stargazer/lib/crypto" +) + +var ( + // EmptyCodeHash is the code hash of an empty code + // 0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470. + emptyCodeHash = crypto.Keccak256Hash(nil) + emptyCodeHashBytes = emptyCodeHash.Bytes() +) + +var _ StargazerStateDB = (*StateDB)(nil) + +// The StateDB is a very fun and interesting part of the EVM implementation. But if you want to +// join circus you need to know the rules. So here thet are: +// +// 1. You must ensure that the StateDB is only ever used in a single thread. This is because the +// StateDB is not thread safe. And there are a bunch of optimizations made that are only safe +// to do in a single thread. +// 2. You must discard the StateDB after use. +// 3. When accessing or mutating the StateDB, you must ensure that the underlying account exists. +// in the AccountKeeper, for performance reasons, this implementation of the StateDB will not +// create accounts that do not exist. Notably calling `SetState()` on an account that does not +// exist is completely possible, and the StateDB will not prevent you doing so. This lazy +// creation improves performance a ton, as it prevents calling into the ak on +// every SSTORE. The only accounts that should ever have `SetState()` called on them are +// accounts that represent smart contracts. Because of this assumption, the only place that we +// explicitly create accounts is in `CreateAccount()`, since `CreateAccount()` is called when +// deploying a smart contract. +// 4. Accounts that are sent `evmDenom` coins during an eth transaction, will have an account +// created for them, automatically by the Bank Module. However, these accounts will have a +// codeHash of 0x000... This is because the Bank Module does not know that the account is an +// EVM account, and so it does not set the codeHash. This is totally fine, we just need to +// check both for both the codeHash being 0x000... as well as the codeHash being 0x567... +type StateDB struct { //nolint: revive // we like the vibe. + // We maintain a context in the StateDB, so that we can pass it with the correctly + // configured multi-store to the precompiled contracts. + ctx sdk.Context + + // Store a reference to the multi-store, in `ctx` so that we can access it directly. + cms *cachemulti.Store + + // eth state stores: required for vm.StateDB + // We store references to these stores, so that we can access them + // directly, without having to go through the MultiStore interface. + ethStore cachekv.StateDBCacheKVStore + + // keepers used for balance and account information. + ak types.AccountKeeper + bk types.BankKeeper + + // Any error that occurs during an sdk module read or write is + // memoized here and is eventually be returned by `Commit`. + savedErr error + + // we load the evm denom in the constructor, to prevent going to + // the params to get it mid interpolation. + evmDenom string // todo: get from params ( we have a store so like why not ) + + // The refund counter, also used by state transitioning. + refund uint64 + + // The storekey used during execution + storeKey storetypes.StoreKey + + // Per-transaction logs + logs []*coretypes.Log + + // Transaction and logging bookkeeping + txHash common.Hash + blockHash common.Hash + txIndex uint + logIndex uint + + // Dirty tracking of suicided accounts, we have to keep track of these manually, in order + // for the code and state to still be accessible even after the account has been deleted. + // We chose to keep track of them in a separate slice, rather than a map, because the + // number of accounts that will be suicided in a single transaction is expected to be + // very low. + suicides []common.Address +} + +// returns a *StateDB using the MultiStore belonging to ctx. +func NewStateDB( + ctx sdk.Context, + ak types.AccountKeeper, + bk types.BankKeeper, + storeKey storetypes.StoreKey, + evmDenom string, +) *StateDB { + sdb := &StateDB{ + ak: ak, + bk: bk, + evmDenom: evmDenom, + storeKey: storeKey, + } + + // Wire up the `CacheMultiStore` & `sdk.Context`. + sdb.cms = cachemulti.NewStoreFrom(ctx.MultiStore()) + sdb.ctx = ctx.WithMultiStore(sdb.cms) + + // Store a reference to the EVM state store for performance reasons. + sdb.ethStore, _ = sdb.cms. + GetKVStore(sdb.storeKey).(cachekv.StateDBCacheKVStore) + + return sdb +} + +// =========================================================================== +// Account +// =========================================================================== + +// CreateAccount implements the GethStateDB interface by creating a new account +// in the account keeper. It will allow accounts to be overridden. +func (sdb *StateDB) CreateAccount(addr common.Address) { + acc := sdb.ak.NewAccountWithAddress(sdb.ctx, addr[:]) + + // save the new account in the account keeper + sdb.ak.SetAccount(sdb.ctx, acc) + + // initialize the code hash to empty + sdb.ethStore.Set(types.CodeHashKeyFor(addr), emptyCodeHashBytes) +} + +// ============================================================================= +// Transaction Handling +// ============================================================================= + +// Prepare sets the current transaction hash and index and block hash which is +// used for logging events. +func (sdb *StateDB) PrepareForTransition(blockHash, txHash common.Hash, ti, li uint) { + sdb.blockHash = blockHash + sdb.txHash = txHash + sdb.txIndex = ti + sdb.logIndex = li +} + +// Reset clears the journal and other state objects. It also clears the +// refund counter and the access list. +func (sdb *StateDB) Reset(ctx sdk.Context) { + // TODO: figure out why not fully reallocating the object causes + // the gas shit to fail + // sdb.MultiStore = cachemulti.NewStoreFrom(ctx.MultiStore()) + // sdb.ctx = ctx.WithMultiStore(sdb.MultiStore) + // // // Must support directly accessing the parent store. + // // sdb.ethStore = sdb.ctx.cms. + // // GetKVStore(sdb.storeKey).(cachekv.StateDBCacheKVStore) + // sdb.savedErr = nil + // sdb.refund = 0 + + // sdb.logs = make([]*coretypes.Log, 0) + // sdb.accessList = newAccessList() + // sdb.suicides = make([]common.Address, 0) + // TODO: unghetto this + *sdb = *NewStateDB(ctx, sdb.ak, sdb.bk, sdb.storeKey, sdb.evmDenom) +} + +// ============================================================================= +// Balance +// ============================================================================= + +// GetBalance implements `GethStateDB` interface. +func (sdb *StateDB) GetBalance(addr common.Address) *big.Int { + // Note: bank keeper will return 0 if account/state_object is not found + return sdb.bk.GetBalance(sdb.ctx, addr[:], sdb.evmDenom).Amount.BigInt() +} + +// AddBalance implements the `GethStateDB` interface by adding the given amount +// from the account associated with addr. If the account does not exist, it will be +// created. +func (sdb *StateDB) AddBalance(addr common.Address, amount *big.Int) { + coins := sdk.NewCoins(sdk.NewCoin(sdb.evmDenom, sdk.NewIntFromBigInt(amount))) + + // Mint the coins to the evm module account + if err := sdb.bk.MintCoins(sdb.ctx, types.EvmNamespace, coins); err != nil { + sdb.setErrorUnsafe(err) + return + } + + // Send the coins from the evm module account to the destination address. + if err := sdb.bk.SendCoinsFromModuleToAccount(sdb.ctx, types.EvmNamespace, addr[:], coins); err != nil { + sdb.setErrorUnsafe(err) + } +} + +// SubBalance implements the `GethStateDB` interface by subtracting the given amount +// from the account associated with addr. +func (sdb *StateDB) SubBalance(addr common.Address, amount *big.Int) { + coins := sdk.NewCoins(sdk.NewCoin(sdb.evmDenom, sdk.NewIntFromBigInt(amount))) + + // Send the coins from the source address to the evm module account. + if err := sdb.bk.SendCoinsFromAccountToModule(sdb.ctx, addr[:], types.EvmNamespace, coins); err != nil { + sdb.setErrorUnsafe(err) + return + } + + // Burn the coins from the evm module account. + if err := sdb.bk.BurnCoins(sdb.ctx, types.EvmNamespace, coins); err != nil { + sdb.setErrorUnsafe(err) + return + } +} + +// `TransferBalance` sends the given amount from one account to another. It will +// error if the sender does not have enough funds to send. +func (sdb *StateDB) TransferBalance(from, to common.Address, amount *big.Int) { + coins := sdk.NewCoins(sdk.NewCoin(sdb.evmDenom, sdk.NewIntFromBigInt(amount))) + + // Send the coins from the source address to the destination address. + if err := sdb.bk.SendCoins(sdb.ctx, from[:], to[:], coins); err != nil { + sdb.setErrorUnsafe(err) + } +} + +// ============================================================================= +// Nonce +// ============================================================================= + +// GetNonce implements the `GethStateDB` interface by returning the nonce +// of an account. +func (sdb *StateDB) GetNonce(addr common.Address) uint64 { + acc := sdb.ak.GetAccount(sdb.ctx, addr[:]) + if acc == nil { + return 0 + } + return acc.GetSequence() +} + +// SetNonce implements the `GethStateDB` interface by setting the nonce +// of an account. +func (sdb *StateDB) SetNonce(addr common.Address, nonce uint64) { + // get the account or create a new one if doesn't exist + acc := sdb.ak.GetAccount(sdb.ctx, addr[:]) + if acc == nil { + acc = sdb.ak.NewAccountWithAddress(sdb.ctx, addr[:]) + } + + if err := acc.SetSequence(nonce); err != nil { + sdb.setErrorUnsafe(err) + } + + sdb.ak.SetAccount(sdb.ctx, acc) +} + +// ============================================================================= +// Code +// ============================================================================= + +// GetCodeHash implements the `GethStateDB` interface by returning +// the code hash of account. +func (sdb *StateDB) GetCodeHash(addr common.Address) common.Hash { + if sdb.ak.HasAccount(sdb.ctx, addr[:]) { + if ch := sdb.ethStore.Get(types.CodeHashKeyFor(addr)); ch != nil { + return common.BytesToHash(ch) + } + return emptyCodeHash + } + // if account at addr does not exist, return ZeroCodeHash + return common.Hash{} +} + +// GetCode implements the `GethStateDB` interface by returning +// the code of account (nil if not exists). +func (sdb *StateDB) GetCode(addr common.Address) []byte { + codeHash := sdb.GetCodeHash(addr) + // if account at addr does not exist, GetCodeHash returns ZeroCodeHash so return nil + // if codeHash is empty, i.e. crypto.Keccak256(nil), also return nil + if (codeHash == common.Hash{}) || codeHash == emptyCodeHash { + return nil + } + return sdb.ethStore.Get(types.CodeKeyFor(codeHash)) +} + +// SetCode implements the `GethStateDB` interface by setting the code hash and +// code for the given account. +func (sdb *StateDB) SetCode(addr common.Address, code []byte) { + codeHash := crypto.Keccak256Hash(code) + + sdb.ethStore.Set(types.CodeHashKeyFor(addr), codeHash[:]) + // store or delete code + if len(code) == 0 { + sdb.ethStore.Delete(types.CodeKeyFor(codeHash)) + } else { + sdb.ethStore.Set(types.CodeKeyFor(codeHash), code) + } +} + +// GetCodeSize implements the `GethStateDB` interface by returning the size of the +// code associated with the given `GethStateDB`. +func (sdb *StateDB) GetCodeSize(addr common.Address) int { + return len(sdb.GetCode(addr)) +} + +// ============================================================================= +// Refund +// ============================================================================= + +// `AddRefund` implements the `GethStateDB` interface by adding gas to the +// refund counter. +func (sdb *StateDB) AddRefund(gas uint64) { + sdb.cms.JournalMgr.Push(&RefundChange{sdb, sdb.refund}) + sdb.refund += gas +} + +// `SubRefund` implements the `GethStateDB` interface by subtracting gas from the +// refund counter. If the gas is greater than the refund counter, it will panic. +func (sdb *StateDB) SubRefund(gas uint64) { + sdb.cms.JournalMgr.Push(&RefundChange{sdb, sdb.refund}) + if gas > sdb.refund { + panic(fmt.Sprintf("Refund counter below zero (gas: %d > refund: %d)", gas, sdb.refund)) + } + sdb.refund -= gas +} + +// `GetRefund` implements the `GethStateDB` interface by returning the current +// value of the refund counter. +func (sdb *StateDB) GetRefund() uint64 { + return sdb.refund +} + +// ============================================================================= +// State +// ============================================================================= + +// `GetCommittedState` implements the `GethStateDB` interface by returning the +// committed state of slot in the given address. +func (sdb *StateDB) GetCommittedState( + addr common.Address, + slot common.Hash, +) common.Hash { + return sdb.getStateFromStore(sdb.ethStore.GetParent(), addr, slot) +} + +// `GetState` implements the `GethStateDB` interface by returning the current state +// of slot in the given address. +func (sdb *StateDB) GetState(addr common.Address, slot common.Hash) common.Hash { + return sdb.getStateFromStore(sdb.ethStore, addr, slot) +} + +// `getStateFromStore` returns the current state of the slot in the given address. +func (sdb *StateDB) getStateFromStore( + store storetypes.KVStore, + addr common.Address, slot common.Hash, +) common.Hash { + if value := store.Get(types.StateKeyFor(addr, slot)); value != nil { + return common.BytesToHash(value) + } + return common.Hash{} +} + +// `SetState` implements the `GethStateDB` interface by setting the state of an +// address. +func (sdb *StateDB) SetState(addr common.Address, key, value common.Hash) { + // For performance reasons, we don't check to ensure the account exists before we execute. + // This is reasonably safe since under normal operation, SetState is only ever called by the + // SSTORE opcode in the EVM, which will only ever be called on an account that exists, since + // it would with 100% certainty have been created by a prior Create, thus setting its code + // hash. + // + // CONTRACT: never manually call SetState outside of `opSstore`, or InitGenesis. + + // If empty value is given, delete the state entry. + if len(value) == 0 || (value == common.Hash{}) { + sdb.ethStore.Delete(types.StateKeyFor(addr, key)) + return + } + + // Set the state entry. + sdb.ethStore.Set(types.StateKeyFor(addr, key), value[:]) +} + +// ============================================================================= +// Suicide +// ============================================================================= + +// Suicide implements the GethStateDB interface by marking the given address as suicided. +// This clears the account balance, but the code and state of the address remains available +// until after Commit is called. +func (sdb *StateDB) Suicide(addr common.Address) bool { + // only smart contracts can commit suicide + ch := sdb.GetCodeHash(addr) + if (ch == common.Hash{}) || ch == emptyCodeHash { + return false + } + + // Reduce it's balance to 0. + bal := sdb.bk.GetBalance(sdb.ctx, addr[:], sdb.evmDenom) + sdb.SubBalance(addr, bal.Amount.BigInt()) + + // Mark the underlying account for deletion in `Commit()`. + sdb.suicides = append(sdb.suicides, addr) + return true +} + +// `HasSuicided` implements the `GethStateDB` interface by returning if the contract was suicided +// in current transaction. +func (sdb *StateDB) HasSuicided(addr common.Address) bool { + for _, suicide := range sdb.suicides { + if bytes.Equal(suicide[:], addr[:]) { + return true + } + } + return false +} + +// ============================================================================= +// Exist & Empty +// ============================================================================= + +// `Exist` implements the `GethStateDB` interface by reporting whether the given account address +// exists in the state. Notably this also returns true for suicided accounts, which is accounted +// for since, `RemoveAccount()` is not called until Commit. +func (sdb *StateDB) Exist(addr common.Address) bool { + return sdb.ak.HasAccount(sdb.ctx, addr[:]) +} + +// `Empty` implements the `GethStateDB` interface by returning whether the state object +// is either non-existent or empty according to the EIP161 specification +// (balance = nonce = code = 0) +// https://github.com/ethereum/EIPs/blob/master/EIPS/eip-161.md +func (sdb *StateDB) Empty(addr common.Address) bool { + ch := sdb.GetCodeHash(addr) + return sdb.GetNonce(addr) == 0 && + (ch == emptyCodeHash || ch == common.Hash{}) && + sdb.GetBalance(addr).Sign() == 0 +} + +// ============================================================================= +// Snapshot +// ============================================================================= + +// `RevertToSnapshot` implements `StateDB`. +func (sdb *StateDB) RevertToSnapshot(id int) { + // revert and discard all journal entries after snapshot id + sdb.cms.JournalMgr.PopToSize(id) +} + +// `Snapshot` implements `StateDB`. +func (sdb *StateDB) Snapshot() int { + return sdb.cms.JournalMgr.Size() +} + +// ============================================================================= +// Logs +// ============================================================================= + +// AddLog implements the GethStateDB interface by adding the given log to the current transaction. +func (sdb *StateDB) AddLog(log *coretypes.Log) { + sdb.cms.JournalMgr.Push(&AddLogChange{sdb}) + log.TxHash = sdb.txHash + log.BlockHash = sdb.blockHash + log.TxIndex = sdb.txIndex + log.Index = sdb.logIndex + sdb.logs = append(sdb.logs, log) + sdb.logIndex++ // erigon intra +} + +// Logs returns the logs of current transaction. +func (sdb *StateDB) Logs() []*coretypes.Log { + return sdb.logs +} + +// ============================================================================= +// ForEachStorage +// ============================================================================= + +// `ForEachStorage` implements the `GethStateDB` interface by iterating through the contract state +// contract storage, the iteration order is not defined. +// +// Note: We do not support iterating through any storage that is modified before calling +// `ForEachStorage`; only committed state is iterated through. +func (sdb *StateDB) ForEachStorage( + addr common.Address, + cb func(key, value common.Hash) bool, +) error { + it := sdk.KVStorePrefixIterator(sdb.ethStore, types.AddressStoragePrefix(addr)) + defer it.Close() + + for ; it.Valid(); it.Next() { + committedValue := it.Value() + if len(committedValue) > 0 { + if !cb(common.BytesToHash(it.Key()), common.BytesToHash(committedValue)) { + // stop iteration + return nil + } + } + } + + return nil +} + +// `Commit` is called when we are complete with the state transition and want to commit the changes +// to the underlying store. +func (sdb *StateDB) Commit() error { + // If we saw an error during the execution, we return it here. + if sdb.savedErr != nil { + return sdb.savedErr + } + + // Manually delete all suicidal accounts. + for _, suicidalAddr := range sdb.suicides { + acct := sdb.ak.GetAccount(sdb.ctx, suicidalAddr[:]) + if acct == nil { + // handles the double suicide case + continue + } + + // clear storage + if err := sdb.ForEachStorage(suicidalAddr, + func(key, _ common.Hash) bool { + sdb.SetState(suicidalAddr, key, common.Hash{}) + return true + }); err != nil { + return err + } + + // clear the codehash from this account + sdb.ethStore.Delete(types.CodeHashKeyFor(suicidalAddr)) + + // remove auth account + sdb.ak.RemoveAccount(sdb.ctx, acct) + } + + // write all cache stores to parent stores, effectively writing temporary state in ctx to + // the underlying parent store. + sdb.cms.CacheMultiStore().Write() + return nil +} + +// `GetSavedErr` implements `StargazerStateDB` +// Any errors that pop up during store operations should be checked here +// called upon the conclusion. +func (sdb *StateDB) GetSavedErr() error { + return sdb.savedErr +} + +// `setErrorUnsafe` sets error but should be called in medhods that already have locks. +func (sdb *StateDB) setErrorUnsafe(err error) { + if sdb.savedErr == nil { + sdb.savedErr = err + } +} + +// ============================================================================= +// AccessList +// ============================================================================= + +func (sdb *StateDB) PrepareAccessList( + sender common.Address, + dst *common.Address, + precompiles []common.Address, + list coretypes.AccessList, +) { + panic("not implemented, as accesslists are not valuable in the Cosmos-SDK context") +} + +func (sdb *StateDB) AddAddressToAccessList(addr common.Address) { + panic("not implemented, as accesslists are not valuable in the Cosmos-SDK context") +} + +func (sdb *StateDB) AddSlotToAccessList(addr common.Address, slot common.Hash) { + panic("not implemented, as accesslists are not valuable in the Cosmos-SDK context") +} + +func (sdb *StateDB) AddressInAccessList(addr common.Address) bool { + return false +} + +func (sdb *StateDB) SlotInAccessList(addr common.Address, slot common.Hash) (bool, bool) { + return false, false +} + +// ============================================================================= +// PreImage +// ============================================================================= + +// AddPreimage implements the the `StateDB“ interface, but currently +// performs a no-op since the EnablePreimageRecording flag is disabled. +func (sdb *StateDB) AddPreimage(hash common.Hash, preimage []byte) {} diff --git a/core/state/statedb_ext.go b/core/state/statedb_ext.go new file mode 100644 index 000000000..881768ec9 --- /dev/null +++ b/core/state/statedb_ext.go @@ -0,0 +1,20 @@ +// Copyright (C) 2023, Berachain Foundation. All rights reserved. +// See the file LICENSE for licensing terms. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +package state + +type ExtStateDB struct { + // StateDB is the underlying state database. + *StateDB +} diff --git a/core/types/types.go b/core/state/statedb_ext_test.go similarity index 91% rename from core/types/types.go rename to core/state/statedb_ext_test.go index f78039b0f..1bd6ea9ca 100644 --- a/core/types/types.go +++ b/core/state/statedb_ext_test.go @@ -12,8 +12,4 @@ // OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -package types - -import "github.com/ethereum/go-ethereum/core/types" - -type Log = types.Log +package state_test diff --git a/core/state/statedb_factory.go b/core/state/statedb_factory.go new file mode 100644 index 000000000..883df4836 --- /dev/null +++ b/core/state/statedb_factory.go @@ -0,0 +1,57 @@ +// Copyright (C) 2022, Berachain Foundation. All rights reserved. +// See the file LICENSE for licensing terms. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +package state + +import ( + storetypes "github.com/cosmos/cosmos-sdk/store/types" + sdk "github.com/cosmos/cosmos-sdk/types" + + // "github.com/berachain/stargazer/params". + "github.com/berachain/stargazer/core/state/types" +) + +type StateDBFactory struct { //nolint:revive // the vibes are good. + // Cosmos Keeper References + ak types.AccountKeeper + bk types.BankKeeper + + // evmStoreKey is the store key for the EVM store. + evmStoreKey storetypes.StoreKey + + // evmDenom is the denom used for EVM transactions. + // evmDenom params.Retriever[params.EVMDenom] +} + +// NewStateDBFactory returns a new StateDBFactory instance. +func NewStateDBFactory( + ak types.AccountKeeper, + bk types.BankKeeper, + evmStoreKey storetypes.StoreKey, + // evmDenom params.Retriever[params.EVMDenom], + logFactory types.EthereumLogFactory, +) *StateDBFactory { + return &StateDBFactory{ + ak: ak, + bk: bk, + evmStoreKey: evmStoreKey, + // evmDenom: evmDenom, + // er: er, + } +} + +// BuildNewStateDB returns a new StateDB instance. +func (sdf *StateDBFactory) BuildStateDB(ctx sdk.Context) *StateDB { + return NewStateDB(ctx, sdf.ak, sdf.bk, sdf.evmStoreKey, "abera") // sdf.evmDenom.Get(ctx), sdf.er) +} diff --git a/core/state/statedb_test.go b/core/state/statedb_test.go new file mode 100644 index 000000000..07b6bd755 --- /dev/null +++ b/core/state/statedb_test.go @@ -0,0 +1,651 @@ +// Copyright (C) 2022, Berachain Foundation. All rights reserved. +// See the file LICENSE for licensing terms. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +package state_test + +import ( + "errors" + "fmt" + "math/big" + + sdk "github.com/cosmos/cosmos-sdk/types" + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" + + "github.com/berachain/stargazer/core/state" + "github.com/berachain/stargazer/core/state/types" + coretypes "github.com/berachain/stargazer/core/types" + "github.com/berachain/stargazer/lib/common" + "github.com/berachain/stargazer/lib/crypto" + "github.com/berachain/stargazer/testutil" +) + +var alice = testutil.Alice +var bob = testutil.Bob + +var _ = Describe("StateDB", func() { + var ak types.AccountKeeper + var bk types.BankKeeper + var ctx sdk.Context + var sdb *state.StateDB + + BeforeEach(func() { + ctx, ak, bk, _ = testutil.SetupMinimalKeepers() + sdb = state.NewStateDB(ctx, ak, bk, testutil.EvmKey, "abera") // todo use lf + }) + + Describe("TestCreateAccount", func() { + AfterEach(func() { + Expect(sdb.GetSavedErr()).To(BeNil()) + }) + It("should create account", func() { + sdb.CreateAccount(alice) + Expect(sdb.Exist(alice)).To(BeTrue()) + }) + }) + + Describe("TestBalance", func() { + It("should have start with zero balance", func() { + Expect(sdb.GetBalance(alice)).To(Equal(new(big.Int))) + }) + Context("TestAddBalance", func() { + It("should be able to add zero", func() { + Expect(sdb.GetBalance(alice)).To(Equal(new(big.Int))) + sdb.AddBalance(alice, new(big.Int)) + Expect(sdb.GetBalance(alice)).To(Equal(new(big.Int))) + }) + It("should have 100 balance", func() { + sdb.AddBalance(alice, big.NewInt(100)) + Expect(sdb.GetBalance(alice)).To(Equal(big.NewInt(100))) + }) + It("should panic if using negative value", func() { + Expect(func() { + sdb.AddBalance(alice, big.NewInt(-100)) + }).To(Panic()) + }) + }) + + Context("TestSubBalance", func() { + It("should not set balance to negative value", func() { + sdb.SubBalance(alice, big.NewInt(100)) + Expect(sdb.GetSavedErr()).To(HaveOccurred()) + Expect(errors.Unwrap(errors.Unwrap(sdb.GetSavedErr()))).To( + Equal(sdkerrors.ErrInsufficientFunds)) + Expect(sdb.GetBalance(alice)).To(Equal(new(big.Int))) + }) + It("should panic if using negative value", func() { + Expect(func() { + sdb.SubBalance(alice, big.NewInt(-100)) + }).To(Panic()) + }) + }) + + It("should handle complex balance updates", func() { + // Initial balance for alice should be 0 + Expect(sdb.GetBalance(alice)).To(Equal(new(big.Int))) + + // Add some balance to alice + sdb.AddBalance(alice, big.NewInt(100)) + Expect(sdb.GetBalance(alice)).To(Equal(big.NewInt(100))) + + // Subtract some balance from alice + sdb.SubBalance(alice, big.NewInt(50)) + Expect(sdb.GetBalance(alice)).To(Equal(big.NewInt(50))) + + // Add some balance to alice + sdb.AddBalance(alice, big.NewInt(100)) + Expect(sdb.GetBalance(alice)).To(Equal(big.NewInt(150))) + + // Subtract some balance from alice + sdb.SubBalance(alice, big.NewInt(200)) + Expect(sdb.GetSavedErr()).To(HaveOccurred()) + Expect(errors.Unwrap(errors.Unwrap(sdb.GetSavedErr()))).To( + Equal(sdkerrors.ErrInsufficientFunds)) + Expect(sdb.GetBalance(alice)).To(Equal(big.NewInt(150))) + + }) + }) + + Describe("TestNonce", func() { + When("account exists", func() { + BeforeEach(func() { + sdb.CreateAccount(alice) + }) + It("should have start with zero nonce", func() { + Expect(sdb.GetNonce(alice)).To(Equal(uint64(0))) + }) + It("should have 100 nonce", func() { + sdb.SetNonce(alice, 100) + Expect(sdb.GetNonce(alice)).To(Equal(uint64(100))) + }) + }) + When("account does not exist", func() { + It("should have start with zero nonce", func() { + Expect(sdb.GetNonce(alice)).To(Equal(uint64(0))) + }) + + It("should have 100 nonce", func() { + sdb.SetNonce(alice, 100) + Expect(sdb.GetNonce(alice)).To(Equal(uint64(100))) + }) + }) + }) + + Describe("TestCode & CodeHash", func() { + When("account does not exist", func() { + It("should have empty code hash", func() { + Expect(sdb.GetCodeHash(alice)).To(Equal(common.Hash{})) + }) + It("should not have code", func() { // ensure account exists + Expect(sdb.GetCode(alice)).To(BeNil()) + Expect(sdb.GetCodeHash(alice)).To(Equal(common.Hash{})) + }) + It("cannot set code", func() { // ensure account exists + sdb.SetCode(alice, []byte("code")) + Expect(sdb.GetCode(alice)).To(BeNil()) + Expect(sdb.GetCodeHash(alice)).To(Equal(common.Hash{})) + }) + }) + When("account exists", func() { + BeforeEach(func() { + sdb.CreateAccount(alice) + }) + It("should have zero code hash", func() { + Expect(sdb.GetCodeHash(alice)).To(Equal(crypto.Keccak256Hash(nil))) + }) + When("account has code", func() { + BeforeEach(func() { + sdb.SetCode(alice, []byte("code")) + }) + It("should have code", func() { + Expect(sdb.GetCode(alice)).To(Equal([]byte("code"))) + Expect(sdb.GetCodeHash(alice)).To(Equal(crypto.Keccak256Hash([]byte("code")))) + }) + It("should have empty code hash", func() { + sdb.SetCode(alice, nil) + Expect(sdb.GetCode(alice)).To(BeNil()) + Expect(sdb.GetCodeHash(alice)).To(Equal(crypto.Keccak256Hash(nil))) + }) + }) + }) + }) + + Describe("TestRefund", func() { + It("should have 0 refund", func() { + Expect(sdb.GetRefund()).To(Equal(uint64(0))) + }) + It("should have 100 refund", func() { + sdb.AddRefund(100) + Expect(sdb.GetRefund()).To(Equal(uint64(100))) + }) + It("should have 0 refund", func() { + sdb.AddRefund(100) + sdb.SubRefund(100) + Expect(sdb.GetRefund()).To(Equal(uint64(0))) + }) + It("should panic and over refund", func() { + Expect(func() { + sdb.SubRefund(200) + }).To(Panic()) + }) + }) + + Describe("TestState", func() { + It("should have empty state", func() { + Expect(sdb.GetState(alice, common.Hash{3})).To(Equal(common.Hash{})) + }) + When("set basic state", func() { + BeforeEach(func() { + sdb.SetState(alice, common.Hash{3}, common.Hash{1}) + }) + + It("should have state", func() { + Expect(sdb.GetState(alice, common.Hash{3})).To(Equal(common.Hash{1})) + }) + + It("should have state changed", func() { + sdb.SetState(alice, common.Hash{3}, common.Hash{2}) + Expect(sdb.GetState(alice, common.Hash{3})).To(Equal(common.Hash{2})) + Expect(sdb.GetCommittedState(alice, common.Hash{3})).To(Equal(common.Hash{})) + }) + + When("state is committed", func() { + BeforeEach(func() { + Expect(sdb.Commit()).Should(BeNil()) + It("should have committed state", func() { + Expect(sdb.GetCommittedState(alice, common.Hash{3})).To(Equal(common.Hash{1})) + }) + It("should maintain committed state", func() { + sdb.SetState(alice, common.Hash{3}, common.Hash{4}) + Expect(sdb.GetCommittedState(alice, common.Hash{3})). + To(Equal(common.Hash{1})) + Expect(sdb.GetState(alice, common.Hash{3})).To(Equal(common.Hash{4})) + }) + }) + }) + }) + + Describe("TestExist", func() { + It("should not exist", func() { + Expect(sdb.Exist(alice)).To(BeFalse()) + }) + When("account is created", func() { + BeforeEach(func() { + sdb.CreateAccount(alice) + }) + It("should exist", func() { + Expect(sdb.Exist(alice)).To(BeTrue()) + }) + When("suicided", func() { + BeforeEach(func() { + // Only contracts can be suicided + sdb.SetCode(alice, []byte("code")) + Expect(sdb.Suicide(alice)).To(BeTrue()) + }) + It("should still exist", func() { + Expect(sdb.Exist(alice)).To(BeTrue()) + }) + When("commit", func() { + BeforeEach(func() { + Expect(sdb.Commit()).To(BeNil()) + }) + It("should not exist", func() { + Expect(sdb.Exist(alice)).To(BeFalse()) + }) + }) + }) + }) + }) + + Describe("TestReset", func() { + BeforeEach(func() { + sdb.AddRefund(1000) + sdb.AddLog(&coretypes.Log{}) + sdb.PrepareForTransition(common.Hash{1}, common.Hash{2}, 3, 4) + + sdb.CreateAccount(alice) + sdb.SetCode(alice, []byte("code")) + sdb.Suicide(alice) + }) + It("should have reset state", func() { + sdb.Reset(ctx) + Expect(sdb.GetNonce(alice)).To(Equal(uint64(0))) + Expect(sdb.Logs()).To(BeNil()) + Expect(sdb.GetRefund()).To(Equal(uint64(0))) + Expect(sdb.GetSavedErr()).To(BeNil()) + Expect(sdb.HasSuicided(alice)).To(BeFalse()) + // todo check the txhash and blockhash stuff + Expect(sdb, state.NewStateDB(ctx, ak, bk, testutil.EvmKey, "bera")) + }) + }) + + Describe("TestEmpty", func() { + When("account does not exist", func() { + It("should return true", func() { + Expect(sdb.Empty(alice)).To(BeTrue()) + }) + }) + When("account exists", func() { + BeforeEach(func() { + sdb.CreateAccount(alice) + }) + It("new account", func() { + Expect(sdb.Empty(alice)).To(BeTrue()) + }) + It("has a balance", func() { + sdb.AddBalance(alice, big.NewInt(1)) + Expect(sdb.Empty(alice)).To(BeFalse()) + }) + It("has a nonce", func() { + sdb.SetNonce(alice, 1) + Expect(sdb.Empty(alice)).To(BeFalse()) + }) + It("has code", func() { + sdb.SetCode(alice, []byte{0x69}) + Expect(sdb.Empty(alice)).To(BeFalse()) + }) + }) + }) + + Describe("TestSuicide", func() { + BeforeEach(func() { + sdb.CreateAccount(alice) + }) + It("cannot suicide eoa", func() { + Expect(sdb.Suicide(alice)).To(BeFalse()) + Expect(sdb.HasSuicided(alice)).To(BeFalse()) + }) + + initialAliceBal := big.NewInt(69) + initialBobBal := big.NewInt(420) + aliceCode := []byte("alicecode") + bobCode := []byte("bobcode") + + When("address has code and balance", func() { + BeforeEach(func() { + sdb.SetCode(alice, aliceCode) + sdb.SetCode(bob, bobCode) + sdb.AddBalance(alice, initialAliceBal) + sdb.AddBalance(bob, initialBobBal) + // Give Alice some state + for i := 0; i < 5; i++ { + sdb.SetState(alice, common.BytesToHash([]byte(fmt.Sprintf("key%d", i))), + common.BytesToHash([]byte(fmt.Sprintf("value%d", i)))) + } + // Give Bob some state + for i := 5; i < 15; i++ { + sdb.SetState(bob, common.BytesToHash([]byte(fmt.Sprintf("key%d", i))), + common.BytesToHash([]byte(fmt.Sprintf("value%d", i)))) + } + }) + When("alice commits suicide", func() { + BeforeEach(func() { + Expect(sdb.Suicide(alice)).To(BeTrue()) + Expect(sdb.HasSuicided(alice)).To(BeTrue()) + }) + It("alice should be marked as suicidal, but not bob", func() { + Expect(sdb.HasSuicided(alice)).To(BeTrue()) + Expect(sdb.HasSuicided(bob)).To(BeFalse()) + }) + It("alice should have her balance set to 0", func() { + Expect(sdb.GetBalance(alice)).To(Equal(new(big.Int))) + Expect(sdb.GetBalance(bob)).To(Equal(initialBobBal)) + }) + It("both alice and bob should have their code and state untouched", func() { + Expect(sdb.GetCode(alice)).To(Equal(aliceCode)) + Expect(sdb.GetCode(bob)).To(Equal(bobCode)) + for i := 0; i < 5; i++ { + Expect(sdb.GetState(alice, + common.BytesToHash([]byte(fmt.Sprintf("key%d", i))))). + To(Equal(common.BytesToHash([]byte(fmt.Sprintf("value%d", i))))) + } + + for i := 5; i < 15; i++ { + Expect(sdb.GetState(bob, + common.BytesToHash([]byte(fmt.Sprintf("key%d", i))))). + To(Equal(common.BytesToHash([]byte(fmt.Sprintf("value%d", i))))) + } + }) + When("commit is called", func() { + BeforeEach(func() { + _ = sdb.Commit() + }) + It("alice should have her code and state wiped, but not bob", func() { + Expect(sdb.GetCode(alice)).To(BeNil()) + Expect(sdb.GetCode(bob)).To(Equal(bobCode)) + var aliceStorage types.Storage + err := sdb.ForEachStorage(alice, + func(key, value common.Hash) bool { + aliceStorage = append(aliceStorage, + types.NewState(key, value)) + return true + }) + Expect(err).To(BeNil()) + Expect(len(aliceStorage)).To(BeZero()) + + var bobStorage types.Storage + err = sdb.ForEachStorage(bob, + func(key, value common.Hash) bool { + bobStorage = append(bobStorage, types.NewState(key, value)) + return true + }) + Expect(err).To(BeNil()) + Expect(len(bobStorage)).To(Equal(10)) + }) + }) + + }) + }) + }) + Describe("TestAccount", func() { + It("account does not exist", func() { + Expect(sdb.Exist(alice)).To(BeFalse()) + Expect(sdb.Empty(alice)).To(BeTrue()) + Expect(sdb.GetBalance(alice)).To(Equal(new(big.Int))) + Expect(sdb.GetNonce(alice)).To(BeZero()) + Expect(sdb.GetCodeHash(alice)).To(Equal(common.Hash{})) + Expect(sdb.GetCode(alice)).To(BeNil()) + Expect(sdb.GetCodeSize(alice)).To(BeZero()) + Expect(sdb.GetState(alice, common.Hash{})).To(Equal(common.Hash{})) + Expect(sdb.GetRefund()).To(BeZero()) + Expect(sdb.GetCommittedState(alice, common.Hash{})).To(Equal(common.Hash{})) + }) + When("account exists", func() { + BeforeEach(func() { + sdb.AddBalance(alice, big.NewInt(56)) + sdb.SetNonce(alice, 59) + }) + It("accidental override account", func() { + // override + sdb.CreateAccount(alice) + + // check balance is not reset + Expect(sdb.GetBalance(alice)).To(Equal(big.NewInt(56))) + }) + }) + + }) + + Describe("TestSnapshot", func() { + key := common.BytesToHash([]byte("key")) + value1 := common.BytesToHash([]byte("value1")) + value2 := common.BytesToHash([]byte("value2")) + It("simple revert", func() { + revision := sdb.Snapshot() + Expect(revision).To(Equal(0)) + sdb.SetState(alice, key, value1) + Expect(sdb.GetState(alice, key)).To(Equal(value1)) + sdb.RevertToSnapshot(revision) + Expect(sdb.GetState(alice, key)).To(Equal(common.Hash{})) + }) + It("nested snapshot & revert", func() { + revision1 := sdb.Snapshot() + Expect(revision1).To(Equal(0)) + sdb.SetState(alice, key, value1) + revision2 := sdb.Snapshot() + Expect(revision2).To(Equal(1)) + sdb.SetState(alice, key, value2) + Expect(sdb.GetState(alice, key)).To(Equal(value2)) + + sdb.RevertToSnapshot(revision2) + Expect(sdb.GetState(alice, key)).To(Equal(value1)) + + sdb.RevertToSnapshot(revision1) + Expect(sdb.GetState(alice, key)).To(Equal(common.Hash{})) + }) + It("jump revert", func() { + revision1 := sdb.Snapshot() + Expect(revision1).To(Equal(0)) + sdb.SetState(alice, key, value1) + sdb.Snapshot() + sdb.SetState(alice, key, value2) + Expect(sdb.GetState(alice, key)).To(Equal(value2)) + sdb.RevertToSnapshot(revision1) + Expect(sdb.GetState(alice, key)).To(Equal(common.Hash{})) + }) + }) + + Describe("Test Refund", func() { + It("simple refund", func() { + sdb.AddRefund(10) + Expect(sdb.GetRefund()).To(Equal(uint64(10))) + sdb.AddRefund(200) + Expect(sdb.GetRefund()).To(Equal(uint64(210))) + }) + + It("nested refund", func() { + sdb.AddRefund(uint64(10)) + sdb.SubRefund(uint64(5)) + Expect(sdb.GetRefund()).To(Equal(uint64(5))) + }) + + It("negative refund", func() { + sdb.AddRefund(5) + Expect(func() { sdb.SubRefund(10) }).To(Panic()) + }) + }) + Describe("Test Log", func() { + txHash := common.BytesToHash([]byte("tx")) + blockHash := common.BytesToHash([]byte("block")) + data := []byte("bing bong bananas") + blockNumber := uint64(13) + + BeforeEach(func() { + sdb.PrepareForTransition(blockHash, txHash, 0, 0) + }) + When("We add a log to the state", func() { + BeforeEach(func() { + + sdb.AddLog(&coretypes.Log{ + Address: alice, + Topics: []common.Hash{}, + Data: data, + BlockNumber: blockNumber, + TxHash: txHash, + TxIndex: 0, + BlockHash: blockHash, + Index: 0, + Removed: false, + }) + }) + It("should have the correct log", func() { + logs := sdb.Logs() + Expect(logs).To(HaveLen(1)) + Expect(logs[0].Address).To(Equal(alice)) + Expect(logs[0].Data).To(Equal(data)) + Expect(logs[0].BlockNumber).To(Equal(blockNumber)) + Expect(logs[0].TxHash).To(Equal(txHash)) + Expect(logs[0].TxIndex).To(Equal(uint(0))) + Expect(logs[0].BlockHash).To(Equal(blockHash)) + Expect(logs[0].Index).To(Equal(uint(0))) + Expect(logs[0].Removed).To(BeFalse()) + }) + When("we add a second log", func() { + BeforeEach(func() { + sdb.AddLog(&coretypes.Log{ + Address: alice, + Topics: []common.Hash{}, + Data: data, + BlockNumber: blockNumber, + TxHash: txHash, + TxIndex: 0, + BlockHash: blockHash, + Index: 1, + Removed: false, + }) + }) + It("should have the correct logs", func() { + logs := sdb.Logs() + Expect(logs).To(HaveLen(2)) + Expect(logs[1].Address).To(Equal(alice)) + Expect(logs[1].Data).To(Equal(data)) + Expect(logs[1].BlockNumber).To(Equal(blockNumber)) + Expect(logs[1].TxHash).To(Equal(txHash)) + Expect(logs[1].TxIndex).To(Equal(uint(0))) + Expect(logs[1].BlockHash).To(Equal(blockHash)) + Expect(logs[1].Index).To(Equal(uint(1))) + Expect(logs[1].Removed).To(BeFalse()) + }) + }) + }) + }) + + Describe("TestSavedErr", func() { + When("if we see an error", func() { + It("should have an error", func() { + sdb.TransferBalance(alice, bob, big.NewInt(100)) + Expect(sdb.GetSavedErr()).To(HaveOccurred()) + }) + }) + + }) + + Describe("TestRevertSnapshot", func() { + key := common.BytesToHash([]byte("key")) + value := common.BytesToHash([]byte("value")) + + When("We make a bunch of arbitrary changes", func() { + BeforeEach(func() { + sdb.SetNonce(alice, 1) + sdb.AddBalance(alice, big.NewInt(100)) + sdb.SetCode(alice, []byte("hello world")) + sdb.SetState(alice, key, value) + sdb.SetNonce(bob, 1) + }) + When("we take a snapshot", func() { + var revision int + BeforeEach(func() { + revision = sdb.Snapshot() + }) + When("we do more changes", func() { + AfterEach(func() { + sdb.RevertToSnapshot(revision) + Expect(sdb.GetNonce(alice)).To(Equal(uint64(1))) + Expect(sdb.GetBalance(alice)).To(Equal(big.NewInt(100))) + Expect(sdb.GetCode(alice)).To(Equal([]byte("hello world"))) + Expect(sdb.GetState(alice, key)).To(Equal(value)) + Expect(sdb.GetNonce(bob)).To(Equal(uint64(1))) + }) + + It("if we change balance", func() { + sdb.AddBalance(alice, big.NewInt(100)) + }) + + It("if we change nonce", func() { + sdb.SetNonce(alice, 2) + }) + + It("if we change code", func() { + sdb.SetCode(alice, []byte("goodbye world")) + }) + + It("if we change state", func() { + sdb.SetState(alice, key, common.Hash{}) + }) + + It("if we change nonce of another account", func() { + sdb.SetNonce(bob, 2) + }) + }) + + When("we make a nested snapshot", func() { + var revision2 int + BeforeEach(func() { + sdb.SetState(alice, key, common.Hash{2}) + revision2 = sdb.Snapshot() + }) + When("we revert to snapshot ", (func() { + It("revision 2", func() { + sdb.RevertToSnapshot(revision2) + Expect(sdb.GetState(alice, key)).To(Equal(common.Hash{2})) + }) + It("revision 1", func() { + sdb.RevertToSnapshot(revision) + Expect(sdb.GetState(alice, key)).To(Equal(value)) + }) + })) + }) + }) + When("we revert to an invalid snapshot", func() { + It("should panic", func() { + Expect(func() { + sdb.RevertToSnapshot(100) + }).To(Panic()) + }) + }) + }) + }) + }) +}) diff --git a/core/state/store/cachekv/cache_value.go b/core/state/store/cachekv/cache_value.go index 2b8de5a4c..2b014f7b3 100644 --- a/core/state/store/cachekv/cache_value.go +++ b/core/state/store/cachekv/cache_value.go @@ -14,10 +14,10 @@ package cachekv -import "github.com/berachain/stargazer/types" +import "github.com/berachain/stargazer/lib/gointerfaces" // Compile-time assertion that `cacheValue` implements `types.Cloneable`. -var _ types.Cloneable[*cacheValue] = (*cacheValue)(nil) +var _ gointerfaces.Cloneable[*cacheValue] = (*cacheValue)(nil) // `cacheValue` represents a cached value in the cachekv store. // If dirty is true, it indicates the cached value is different from the underlying value. diff --git a/core/state/store/cachekv/store.go b/core/state/store/cachekv/store.go index 5c56b3338..0d6cb3100 100644 --- a/core/state/store/cachekv/store.go +++ b/core/state/store/cachekv/store.go @@ -11,8 +11,6 @@ // CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, // OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -// -//nolint:ireturn // cachekv needs to return interfaces. package cachekv import ( diff --git a/core/state/store/cachekv/store_test.go b/core/state/store/cachekv/store_test.go index a29dddad7..e028fc5fd 100644 --- a/core/state/store/cachekv/store_test.go +++ b/core/state/store/cachekv/store_test.go @@ -30,11 +30,11 @@ import ( "github.com/berachain/stargazer/core/state/store/journal" ) -func newParent() types.CacheKVStore { //nolint: ireturn // by design. +func newParent() types.CacheKVStore { return sdkcachekv.NewStore(dbadapter.Store{DB: dbm.NewMemDB()}) } -func newCacheKVStoreFromParent(parent types.CacheKVStore) types.CacheKVStore { //nolint: ireturn // by design. +func newCacheKVStoreFromParent(parent types.CacheKVStore) types.CacheKVStore { return cachekv.NewStore(parent, journal.NewManager()) } diff --git a/core/state/store/cachemulti/store.go b/core/state/store/cachemulti/store.go index e6e90c021..a29708bf8 100644 --- a/core/state/store/cachemulti/store.go +++ b/core/state/store/cachemulti/store.go @@ -11,7 +11,6 @@ // CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, // OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - package cachemulti import ( @@ -41,13 +40,11 @@ func NewStoreFrom(ms storetypes.MultiStore) *Store { } } -// `GetKVStore` shadows the Cosmos SDK `storetypes.MultiStore` function. Routes native module calls -// to read the dirty state during an eth tx. Any state that is modified by evm statedb, and using -// the context passed in to StateDB, will be routed to a tx-specific cache kv store. This function -// always returns a `storetypes.CacheKVStore`. -// -//nolint:nolintlint,ireturn // must return interface. +// `GetKVStore` shadows the SDK's `storetypes.MultiStore` function. Routes native module calls to +// read the dirty state during an eth tx. Any state that is modified by evm statedb, and using the +// context passed in to StateDB, will be routed to a tx-specific cache kv store. func (s *Store) GetKVStore(key storetypes.StoreKey) storetypes.KVStore { + // check if cache kv store already used if cacheKVStore, exists := s.stores[key]; exists { return cacheKVStore } @@ -72,8 +69,6 @@ func (s *Store) Write() { // `newCacheKVStore` returns a new CacheKVStore. If the `key` is an EVM storekey, it will return // an EVM CacheKVStore. -// -//nolint:nolintlint,ireturn // must return interface. func (s *Store) newCacheKVStore( key storetypes.StoreKey, kvstore storetypes.KVStore, diff --git a/core/state/store/journal/cache_entry.go b/core/state/store/journal/cache_entry.go index af18ae540..fc6351491 100644 --- a/core/state/store/journal/cache_entry.go +++ b/core/state/store/journal/cache_entry.go @@ -14,12 +14,12 @@ package journal -import "github.com/berachain/stargazer/types" +import "github.com/berachain/stargazer/lib/gointerfaces" // `CacheEntry` is an interface for journal entries. type CacheEntry interface { // `CacheEntry` implements `Cloneable`. - types.Cloneable[CacheEntry] + gointerfaces.Cloneable[CacheEntry] // `Revert` undoes the changes made by the entry. Revert() diff --git a/core/state/store/journal/manager.go b/core/state/store/journal/manager.go index b846cdf00..6d65ca9ee 100644 --- a/core/state/store/journal/manager.go +++ b/core/state/store/journal/manager.go @@ -15,17 +15,17 @@ package journal import ( "github.com/berachain/stargazer/lib/ds" - "github.com/berachain/stargazer/types" + "github.com/berachain/stargazer/lib/gointerfaces" ) // `ManagerI` is an interface that defines the methods that a journal manager must implement. // Journal managers support holding cache entries and reverting to a certain index. type ManagerI[T any] interface { - // The journal manager is a stack. + // `ManagerI` implements `ds.StackI[CacheEntry]`. ds.StackI[CacheEntry] // `ManagerI` implements `Cloneable`. - types.Cloneable[T] + gointerfaces.Cloneable[T] } // Compile-time check to ensure `Manager` implements `ManagerI`. @@ -33,6 +33,7 @@ var _ ManagerI[*Manager] = (*Manager)(nil) // `Manager` is a struct that holds a slice of CacheEntry instances. type Manager struct { + // The journal manager is a stack. *ds.Stack[CacheEntry] } diff --git a/core/state/interfaces.go b/core/state/types/interfaces.go similarity index 93% rename from core/state/interfaces.go rename to core/state/types/interfaces.go index 8bb39763f..ed6eb7570 100644 --- a/core/state/interfaces.go +++ b/core/state/types/interfaces.go @@ -12,9 +12,10 @@ // OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -package state +package types import ( + coretypes "github.com/berachain/stargazer/core/types" sdk "github.com/cosmos/cosmos-sdk/types" authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" ) @@ -42,3 +43,7 @@ type BankKeeper interface { MintCoins(ctx sdk.Context, moduleName string, amt sdk.Coins) error BurnCoins(ctx sdk.Context, moduleName string, amt sdk.Coins) error } + +type EthereumLogFactory interface { + BuildLog(event *sdk.Event) (*coretypes.Log, error) +} diff --git a/core/state/types/keys.go b/core/state/types/keys.go new file mode 100644 index 000000000..9c7332206 --- /dev/null +++ b/core/state/types/keys.go @@ -0,0 +1,60 @@ +// Copyright (C) 2023, Berachain Foundation. All rights reserved. +// See the file LICENSE for licensing terms. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +package types + +import ( + "github.com/ethereum/go-ethereum/common" +) + +const ( + keyPrefixCode byte = iota + keyPrefixHash + keyPrefixStorage +) + +// NOTE: we use copy to build keys for max performance: https://github.com/golang/go/issues/55905 + +// AddressStoragePrefix returns a prefix to iterate over a given account storage. +func AddressStoragePrefix(address common.Address) []byte { + bz := make([]byte, 1+common.AddressLength) + copy(bz, []byte{keyPrefixStorage}) + copy(bz[1:], address[:]) + return bz +} + +// `StateKeyFor` defines the full key under which an account state is stored. +func StateKeyFor(address common.Address, key common.Hash) []byte { + bz := make([]byte, 1+common.AddressLength+common.HashLength) + copy(bz, []byte{keyPrefixStorage}) + copy(bz[1:], address[:]) + copy(bz[1+common.AddressLength:], key[:]) + return bz +} + +// `CodeHashKeyFor` defines the full key under which an addreses codehash is stored. +func CodeHashKeyFor(address common.Address) []byte { + bz := make([]byte, 1+common.AddressLength) + copy(bz, []byte{keyPrefixCode}) + copy(bz[1:], address[:]) + return bz +} + +// `CodeKeyFor` defines the full key for which a codehash's corresponding code is stored. +func CodeKeyFor(codeHash common.Hash) []byte { + bz := make([]byte, 1+common.HashLength) + copy(bz, []byte{keyPrefixCode}) + copy(bz[1:], codeHash[:]) + return bz +} diff --git a/core/state/types/keys_test.go b/core/state/types/keys_test.go new file mode 100644 index 000000000..4c096416b --- /dev/null +++ b/core/state/types/keys_test.go @@ -0,0 +1,61 @@ +// Copyright (C) 2023, Berachain Foundation. All rights reserved. +// See the file LICENSE for licensing terms. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +package types + +import ( + "github.com/ethereum/go-ethereum/common" + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" +) + +var _ = Describe("AddressStoragePrefix", func() { + It("returns a prefix to iterate over a given account storage", func() { + address := common.HexToAddress("0x1234567890abcdef1234567890abcdef12345678") + prefix := AddressStoragePrefix(address) + Expect(prefix).To(HaveLen(1 + common.AddressLength)) + Expect(prefix[0]).To(Equal(keyPrefixStorage)) + Expect(prefix[1:]).To(Equal(address.Bytes())) + }) +}) + +var _ = Describe("StateKeyFor", func() { + It("returns a storage key for a given account and storage slot", func() { + address := common.HexToAddress("0x1234567890abcdef1234567890abcdef12345678") + slot := common.HexToHash("0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef") + key := StateKeyFor(address, slot) + Expect(key).To(HaveLen(1 + common.AddressLength + common.HashLength)) + Expect(key[0]).To(Equal(keyPrefixStorage)) + Expect(key[1 : 1+common.AddressLength]).To(Equal(address.Bytes())) + Expect(key[1+common.AddressLength:]).To(Equal(slot.Bytes())) + }) +}) + +var _ = Describe("CodeHashKeyFo or a given account", func() { + address := common.HexToAddress("0x1234567890abcdef1234567890abcdef12345678") + key := CodeHashKeyFor(address) + Expect(key).To(HaveLen(1 + common.AddressLength)) + Expect(key[0]).To(Equal(keyPrefixCode)) + Expect(key[1:]).To(Equal(address.Bytes())) +}) + +var _ = Describe("CodeKeyFor", func() { + It("returns a code key for a given account", func() { + address := common.HexToHash("0x1234567890abcdef1234567890abcdef12345678") + key := CodeKeyFor(address) + Expect(key).To(HaveLen(1 + common.HashLength)) + Expect(key[0]).To(Equal(keyPrefixCode)) + Expect(key[1:]).To(Equal(address.Bytes())) + }) +}) diff --git a/core/state/types/state.go b/core/state/types/state.go index f4d6827c1..234268932 100644 --- a/core/state/types/state.go +++ b/core/state/types/state.go @@ -19,12 +19,12 @@ import ( "strings" "cosmossdk.io/errors" - "github.com/berachain/stargazer/types" + "github.com/berachain/stargazer/lib/gointerfaces" "github.com/ethereum/go-ethereum/common" ) // Compile-time interface assertions. -var _ types.Cloneable[State] = &State{} +var _ gointerfaces.Cloneable[State] = &State{} var _ fmt.Stringer = Storage{} // `NewState` creates a new State instance. diff --git a/core/state/types/storage.go b/core/state/types/storage.go index 3a9352dc6..b566c1fae 100644 --- a/core/state/types/storage.go +++ b/core/state/types/storage.go @@ -18,11 +18,11 @@ import ( "fmt" "cosmossdk.io/errors" - "github.com/berachain/stargazer/types" + "github.com/berachain/stargazer/lib/gointerfaces" ) // Compile-time type assertions. -var _ types.Cloneable[Storage] = Storage{} +var _ gointerfaces.Cloneable[Storage] = Storage{} var _ fmt.Stringer = Storage{} // `Storage` represents the account Storage map as a slice of single key value diff --git a/core/state/types/types.go b/core/state/types/types.go index 534832a1b..1177a3189 100644 --- a/core/state/types/types.go +++ b/core/state/types/types.go @@ -15,3 +15,4 @@ package types const EvmStoreKey = "evm" +const EvmNamespace = "evm" diff --git a/core/types/imported.go b/core/types/imported.go new file mode 100644 index 000000000..4513f7502 --- /dev/null +++ b/core/types/imported.go @@ -0,0 +1,24 @@ +// Copyright (C) 2023, Berachain Foundation. All rights reserved. +// See the file LICENSE for licensing terms. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +package types + +import ( + "github.com/ethereum/go-ethereum/core/types" +) + +type ( + AccessList = types.AccessList + Log = types.Log +) diff --git a/core/vm/imported.go b/core/vm/imported.go new file mode 100644 index 000000000..c69d10363 --- /dev/null +++ b/core/vm/imported.go @@ -0,0 +1,29 @@ +// Copyright (C) 2023, Berachain Foundation. All rights reserved. +// See the file LICENSE for licensing terms. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +package vm + +import ( + "github.com/ethereum/go-ethereum/core/vm" +) + +type ( + BlockContext = vm.BlockContext + CanTransferFunc = vm.CanTransferFunc + ContractRef = vm.ContractRef + Config = vm.Config + TransferFunc = vm.TransferFunc + TxContext = vm.TxContext + StateDB = vm.StateDB +) diff --git a/core/vm/interface.go b/core/vm/interface.go new file mode 100644 index 000000000..5605b3626 --- /dev/null +++ b/core/vm/interface.go @@ -0,0 +1,37 @@ +// Copyright (C) 2023, Berachain Foundation. All rights reserved. +// See the file LICENSE for licensing terms. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +package vm + +import ( + "github.com/berachain/stargazer/lib/common" + "github.com/ethereum/go-ethereum/params" + "github.com/holiman/uint256" +) + +type VMInterface interface { //nolint:revive // we like the vibe. + Reset(txCtx TxContext, sdb StateDB) + Create(caller ContractRef, code []byte, + gas uint64, value *uint256.Int, + ) (ret []byte, contractAddr common.Address, leftOverGas uint64, err error) + Call(caller ContractRef, addr common.Address, input []byte, + gas uint64, value *uint256.Int, bailout bool, + ) (ret []byte, leftOverGas uint64, err error) + Config() Config + ChainConfig() *params.ChainConfig + ChainRules() *params.Rules + Context() BlockContext + StateDB() StateDB + TxContext() TxContext +} diff --git a/go.mod b/go.mod index 6f42ec3e8..2c4bc257c 100644 --- a/go.mod +++ b/go.mod @@ -14,6 +14,7 @@ require ( github.com/ethereum/go-ethereum v1.10.26 github.com/golangci/golangci-lint v1.50.1 github.com/google/addlicense v1.1.0 + github.com/holiman/uint256 v1.2.0 github.com/incu6us/goimports-reviser/v3 v3.3.0 github.com/magefile/mage v1.14.0 github.com/onsi/ginkgo/v2 v2.7.0 @@ -77,6 +78,7 @@ require ( github.com/cosmos/btcutil v1.0.5 // indirect github.com/cosmos/cosmos-proto v1.0.0-beta.1 // indirect github.com/cosmos/go-bip39 v1.0.0 // indirect + github.com/cosmos/gogogateway v1.2.0 // indirect github.com/cosmos/gorocksdb v1.2.0 // indirect github.com/cosmos/iavl v0.19.4 // indirect github.com/cosmos/ledger-cosmos-go v0.12.1 // indirect @@ -104,6 +106,7 @@ require ( github.com/ettle/strcase v0.1.1 // indirect github.com/fatih/color v1.13.0 // indirect github.com/fatih/structtag v1.2.0 // indirect + github.com/felixge/httpsnoop v1.0.2 // indirect github.com/firefart/nonamedreturns v1.0.4 // indirect github.com/fjl/memsize v0.0.0-20190710130421-bcb5799ab5e5 // indirect github.com/fsnotify/fsnotify v1.6.0 // indirect @@ -131,10 +134,12 @@ require ( github.com/godbus/dbus v0.0.0-20190726142602-4481cbc300e2 // indirect github.com/gofrs/flock v0.8.1 // indirect github.com/gofrs/uuid v4.2.0+incompatible // indirect + github.com/gogo/googleapis v1.4.1 // indirect github.com/gogo/protobuf v1.3.2 // indirect github.com/golang-jwt/jwt/v4 v4.3.0 // indirect github.com/golang/glog v1.0.0 // indirect github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect + github.com/golang/mock v1.6.0 // indirect github.com/golang/protobuf v1.5.2 // indirect github.com/golang/snappy v0.0.4 // indirect github.com/golangci/check v0.0.0-20180506172741-cfe4005ccda2 // indirect @@ -152,6 +157,8 @@ require ( github.com/google/uuid v1.3.0 // indirect github.com/gookit/color v1.5.2 // indirect github.com/gordonklaus/ineffassign v0.0.0-20210914165742-4cc7213b9bc8 // indirect + github.com/gorilla/handlers v1.5.1 // indirect + github.com/gorilla/mux v1.8.0 // indirect github.com/gorilla/websocket v1.5.0 // indirect github.com/gostaticanalysis/analysisutil v0.7.1 // indirect github.com/gostaticanalysis/comment v1.4.2 // indirect @@ -173,7 +180,6 @@ require ( github.com/hdevalence/ed25519consensus v0.0.0-20220222234857-c00d1f31bab3 // indirect github.com/hexops/gotextdiff v1.0.3 // indirect github.com/holiman/bloomfilter/v2 v2.0.3 // indirect - github.com/holiman/uint256 v1.2.0 // indirect github.com/huandu/skiplist v1.2.0 // indirect github.com/huin/goupnp v1.0.3 // indirect github.com/inconshreveable/mousetrap v1.0.1 // indirect diff --git a/go.sum b/go.sum index e019fd3f0..470d45e7c 100644 --- a/go.sum +++ b/go.sum @@ -179,6 +179,12 @@ github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDk 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-20211001041855-01bcc9b48dfe/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/v3 v3.1.0 h1:MK3Ow7LH0W8zkd5GMKA1PvS9qG3bWFI95WaVNfyZJ/w= github.com/confio/ics23/go v0.9.0 h1:cWs+wdbS2KRPZezoaaj+qBleXgUk5WOQFMP3CQFGTr4= github.com/confio/ics23/go v0.9.0/go.mod h1:4LPZ2NYqnYIVRklaozjNR1FScgDJ2s5Xrp+e/mYVRak= @@ -197,6 +203,8 @@ github.com/cosmos/go-bip39 v0.0.0-20180819234021-555e2067c45d/go.mod h1:tSxLoYXy github.com/cosmos/go-bip39 v1.0.0 h1:pcomnQdrdH22njcAatO0yWojsUnCO3y2tNoV1cb6hHY= github.com/cosmos/go-bip39 v1.0.0/go.mod h1:RNJv0H/pOIVgxw6KS7QeX2a0Uo0aKUlfhZ4xuwvCdJw= github.com/cosmos/gogogateway v1.2.0 h1:Ae/OivNhp8DqBi/sh2A8a1D0y638GpL3tkmLQAiKxTE= +github.com/cosmos/gogogateway v1.2.0/go.mod h1:iQpLkGWxYcnCdz5iAdLcRBSw3h7NXeOkZ4GUkT+tbFI= +github.com/cosmos/gogoproto v1.4.2/go.mod h1:cLxOsn1ljAHSV527CHOtaIP91kK6cCrZETRBrkzItWU= github.com/cosmos/gogoproto v1.4.3 h1:RP3yyVREh9snv/lsOvmsAPQt8f44LgL281X0IOIhhcI= github.com/cosmos/gogoproto v1.4.3/go.mod h1:0hLIG5TR7IvV1fme1HCFKjfzW9X2x0Mo+RooWXCnOWU= github.com/cosmos/gorocksdb v1.2.0 h1:d0l3jJG8M4hBouIZq0mDUHZ+zjOx044J3nGRskwTb4Y= @@ -275,6 +283,8 @@ github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1m 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.9-0.20210217033140-668b12f5399d/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= +github.com/envoyproxy/go-control-plane v0.9.10-0.20210907150352-cf90f659a021/go.mod h1:AFq3mo9L8Lqqiid3OhADV3RfLJnjiw63cSpi+fDTRC0= +github.com/envoyproxy/go-control-plane v0.10.2-0.20220325020618-49ff273808a1/go.mod h1:KJwIaB5Mv44NWtYuAOFCVOjcI94vtpEz2JU/D2v6IjE= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= github.com/esimonov/ifshort v1.0.4 h1:6SID4yGWfRae/M7hkVDVVyppy8q/v9OuxNdmjLQStBA= github.com/esimonov/ifshort v1.0.4/go.mod h1:Pe8zjlRrJ80+q2CxHLfEOfTwxCZ4O+MuhcHcfgNWTk0= @@ -289,7 +299,9 @@ github.com/fatih/color v1.13.0 h1:8LOYc1KYPPmyKMuN8QV2DNRWNbLo6LZ0iLs8+mlH53w= github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk= github.com/fatih/structtag v1.2.0 h1:/OdNE99OxoI/PqaW/SuSK9uxxT3f/tcSZgon/ssNSx4= github.com/fatih/structtag v1.2.0/go.mod h1:mBJUNpUnHmRKrKlQQlmCrh5PuhftFbNv8Ys4/aAZl94= +github.com/felixge/httpsnoop v1.0.1/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= github.com/felixge/httpsnoop v1.0.2 h1:+nS9g82KMXccJ/wp0zyRW9ZBHFETmMGtkk+2CTTrW4o= +github.com/felixge/httpsnoop v1.0.2/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= github.com/firefart/nonamedreturns v1.0.4 h1:abzI1p7mAEPYuR4A+VLKn4eNDOycjYo2phmY9sfv40Y= github.com/firefart/nonamedreturns v1.0.4/go.mod h1:TDhe/tjI1BXo48CmYbUduTV7BdIga8MAO/xbKdcVsGI= github.com/fjl/memsize v0.0.0-20190710130421-bcb5799ab5e5 h1:FtmdgXiUlNeRsoNMFlKLDt+S+6hbjVMEW6RGQ7aUf7c= @@ -375,7 +387,9 @@ github.com/gofrs/flock v0.8.1/go.mod h1:F1TvTiK9OcQqauNUHlbJvyl9Qa1QvF/gOUDKA14j github.com/gofrs/uuid v3.3.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= github.com/gofrs/uuid v4.2.0+incompatible h1:yyYWMnhkhrKwwr8gAOcOCYxOOscHgDS9yZgBrnJfGa0= github.com/gofrs/uuid v4.2.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= +github.com/gogo/googleapis v1.4.1-0.20201022092350-68b0159b7869/go.mod h1:5YRNX2z1oM5gXdAkurHa942MDgEJyk02w4OecKY87+c= github.com/gogo/googleapis v1.4.1 h1:1Yx4Myt7BxzvUr5ldGSbwYiZG6t9wGBZ+8/fX3Wvtq0= +github.com/gogo/googleapis v1.4.1/go.mod h1:2lpHqI5OcWCtVElxXnPt+s8oJvMpySlOyM6xDCrzib4= github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o= github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= @@ -400,6 +414,7 @@ github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4= github.com/golang/mock v1.6.0 h1:ErTB+efbowRARo13NNdxyJji2egdxLGQhRaY+DUumQc= +github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.0/go.mod h1:Qd/q+1AKNOZr9uGQzbzCmRO6sUih6GTPZv6a1/R87v0= github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= @@ -461,6 +476,7 @@ github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= @@ -496,6 +512,7 @@ github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORR github.com/gordonklaus/ineffassign v0.0.0-20210914165742-4cc7213b9bc8 h1:PVRE9d4AQKmbelZ7emNig1+NT27DUmKZn5qXxfio54U= github.com/gordonklaus/ineffassign v0.0.0-20210914165742-4cc7213b9bc8/go.mod h1:Qcp2HIAYhR7mNUVSIxZww3Guk4it82ghYcEXIAk+QT0= github.com/gorilla/handlers v1.5.1 h1:9lRY6j8DEeeBT10CvO9hGW0gmky0BprnvDI5vfhUHH4= +github.com/gorilla/handlers v1.5.1/go.mod h1:t8XrUpc4KVXb7HGyJ4/cEnwQiaxrX/hz1Zv/4g96P1Q= github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI= github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So= github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc= @@ -1068,6 +1085,7 @@ go.opentelemetry.io/otel v1.8.0 h1:zcvBFizPbpa1q7FehvFiHbQwGzmPILebO0tyqIR5Djg= go.opentelemetry.io/otel v1.8.0/go.mod h1:2pkj+iMj0o03Y+cW6/m8Y4WkRdYN3AvCXCnzRMp9yvM= go.opentelemetry.io/otel/trace v1.8.0 h1:cSy0DF9eGI5WIfNwZ1q2iUyGj00tGzP24dE1lOlHrfY= go.opentelemetry.io/otel/trace v1.8.0/go.mod h1:0Bt3PXY8w+3pheS3hQUt+wow8b1ojPaTBoTCh2zIFI4= +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.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= @@ -1292,6 +1310,7 @@ golang.org/x/sys v0.0.0-20211105183446-c75c47738b0c/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 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-20220315194320-039c03cc5b86/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-20220702020025-31831981b65f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= @@ -1494,6 +1513,7 @@ google.golang.org/genproto v0.0.0-20201210142538-e3217bee35cc/go.mod h1:FWY/as6D google.golang.org/genproto v0.0.0-20201214200347-8c77b98c765d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20210108203827-ffc7fda8c3d7/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-20220314164441-57ef72a4c106/go.mod h1:hAL49I2IFola2sVEjAn7MEwsja0xp51I0tlGAf9hz4E= google.golang.org/genproto v0.0.0-20221118155620-16455021b5e6 h1:a2S6M0+660BgMNl++4JPlcAO/CjkqYItDEZwkoDQK7c= google.golang.org/genproto v0.0.0-20221118155620-16455021b5e6/go.mod h1:rZS5c/ZVYMaOGBfO68GWtjOw/eLaZM1X6iVtgjZ+EWg= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= @@ -1513,7 +1533,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.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= google.golang.org/grpc v1.38.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= +google.golang.org/grpc v1.45.0/go.mod h1:lN7owxKUQEqMfSyQikvvk5tf/6zMPsrK+ONuO11+0rQ= +google.golang.org/grpc v1.49.0/go.mod h1:ZgQEeidpAuNRZ8iRrlBKXZQP1ghovWIVhdJRyCDK+GI= google.golang.org/grpc v1.51.0 h1:E1eGv1FTqoLIdnBCZufiSHgKjlqG6fKFf6pPWtMTh8U= google.golang.org/grpc v1.51.0/go.mod h1:wgNDFcnuBGmxLKI/qn4T+m5BtEBYXJPvibbUPsAIPww= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= @@ -1529,6 +1552,7 @@ google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlba 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.27.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= +google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= google.golang.org/protobuf v1.28.1 h1:d0NfwRgPtno5B1Wa6L2DAG+KivqkdutMf1UhdNx175w= google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= gopkg.in/alecthomas/kingpin.v2 v2.2.6 h1:jMFz6MfLP0/4fUyZle81rXUoxOBFi19VUFKVDOQfozc= diff --git a/lib/common/imported.go b/lib/common/imported.go index 2fb567981..ca84d58bc 100644 --- a/lib/common/imported.go +++ b/lib/common/imported.go @@ -26,4 +26,8 @@ type ( var ( BytesToAddress = common.BytesToAddress BytesToHash = common.BytesToHash + Bytes2Hex = common.Bytes2Hex + HexToAddress = common.HexToAddress + Hex2Bytes = common.Hex2Bytes + HexToHash = common.HexToHash ) diff --git a/lib/crypto/imported.go b/lib/crypto/imported.go new file mode 100644 index 000000000..9b7142adc --- /dev/null +++ b/lib/crypto/imported.go @@ -0,0 +1,27 @@ +// Copyright (C) 2022, Berachain Foundation. All rights reserved. +// See the file LICENSE for licensing terms. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +package crypto + +import "github.com/ethereum/go-ethereum/crypto" + +var ( + Keccak256 = crypto.Keccak256 + Keccak256Hash = crypto.Keccak256Hash + CreateAddress = crypto.CreateAddress + ToECDSA = crypto.ToECDSA + FromECDSA = crypto.FromECDSA + HexToECDSA = crypto.HexToECDSA + PubkeyToAddress = crypto.PubkeyToAddress +) diff --git a/types/interfaces.go b/lib/gointerfaces/interfaces.go similarity index 97% rename from types/interfaces.go rename to lib/gointerfaces/interfaces.go index c6da64b46..033f89273 100644 --- a/types/interfaces.go +++ b/lib/gointerfaces/interfaces.go @@ -12,7 +12,7 @@ // OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -package types +package gointerfaces // `Cloneable` is an interface that defines a `Clone` method. type Cloneable[T any] interface { diff --git a/testutil/setup.go b/testutil/setup.go new file mode 100644 index 000000000..fae6cd619 --- /dev/null +++ b/testutil/setup.go @@ -0,0 +1,104 @@ +// Copyright (C) 2022, Berachain Foundation. All rights reserved. +// See the file LICENSE for licensing terms. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +package testutil + +import ( + "github.com/cosmos/cosmos-sdk/store" + storetypes "github.com/cosmos/cosmos-sdk/store/types" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/types/module/testutil" + "github.com/cosmos/cosmos-sdk/x/auth" + authkeeper "github.com/cosmos/cosmos-sdk/x/auth/keeper" + authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" + "github.com/cosmos/cosmos-sdk/x/bank" + bankkeeper "github.com/cosmos/cosmos-sdk/x/bank/keeper" + govtypes "github.com/cosmos/cosmos-sdk/x/gov/types" + "github.com/cosmos/cosmos-sdk/x/staking" + stakingkeeper "github.com/cosmos/cosmos-sdk/x/staking/keeper" + stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" + "github.com/tendermint/tendermint/libs/log" + tmproto "github.com/tendermint/tendermint/proto/tendermint/types" + db "github.com/tendermint/tm-db" + + "github.com/berachain/stargazer/lib/common" +) + +var ( + AccKey = sdk.NewKVStoreKey("acc") + BankKey = sdk.NewKVStoreKey("bank") + EvmKey = sdk.NewKVStoreKey("evm") + StakingKey = sdk.NewKVStoreKey("staking") + Alice = common.BytesToAddress([]byte("alice")) + Bob = common.BytesToAddress([]byte("bob")) +) + +func SetupMinimalKeepers() (sdk.Context, + authkeeper.AccountKeeper, bankkeeper.BaseKeeper, stakingkeeper.Keeper) { + dbm := db.NewMemDB() + ms := store.NewCommitMultiStore(dbm) + + ms.MountStoreWithDB(AccKey, storetypes.StoreTypeIAVL, dbm) + ms.MountStoreWithDB(BankKey, storetypes.StoreTypeIAVL, dbm) + ms.MountStoreWithDB(EvmKey, storetypes.StoreTypeIAVL, dbm) + ms.MountStoreWithDB(StakingKey, storetypes.StoreTypeIAVL, dbm) + + err := ms.LoadLatestVersion() + if err != nil { + panic(err) + } + + ctx := sdk.NewContext(ms, tmproto.Header{}, false, log.TestingLogger()) + + encodingConfig := testutil.MakeTestEncodingConfig( + auth.AppModuleBasic{}, + bank.AppModuleBasic{}, + staking.AppModuleBasic{}, + ) + + ak := authkeeper.NewAccountKeeper( + encodingConfig.Codec, + AccKey, + authtypes.ProtoBaseAccount, + map[string][]string{ + stakingtypes.NotBondedPoolName: {authtypes.Minter, authtypes.Burner}, + stakingtypes.BondedPoolName: {authtypes.Minter, authtypes.Burner}, + "evm": {authtypes.Minter, authtypes.Burner}, + "staking": {authtypes.Minter, authtypes.Burner}, + }, + "bera", + authtypes.NewModuleAddress(govtypes.ModuleName).String(), + ) + + ak.SetModuleAccount(ctx, + authtypes.NewEmptyModuleAccount("evm", authtypes.Minter, authtypes.Burner)) + + bk := bankkeeper.NewBaseKeeper( + encodingConfig.Codec, + BankKey, + ak, + nil, + authtypes.NewModuleAddress(govtypes.ModuleName).String(), + ) + + sk := stakingkeeper.NewKeeper( + encodingConfig.Codec, + StakingKey, + ak, + bk, + authtypes.NewModuleAddress(govtypes.ModuleName).String(), + ) + + return ctx, ak, bk, *sk +}