diff --git a/cmd/util/cmd/checkpoint-collect-stats/cmd.go b/cmd/util/cmd/checkpoint-collect-stats/cmd.go index 2e53fe9528a..3269f4914cf 100644 --- a/cmd/util/cmd/checkpoint-collect-stats/cmd.go +++ b/cmd/util/cmd/checkpoint-collect-stats/cmd.go @@ -19,8 +19,8 @@ import ( "github.com/onflow/flow-go/cmd/util/ledger/reporters" "github.com/onflow/flow-go/cmd/util/ledger/util" + "github.com/onflow/flow-go/fvm/environment" "github.com/onflow/flow-go/fvm/evm/emulator/state" - "github.com/onflow/flow-go/fvm/evm/handler" "github.com/onflow/flow-go/ledger" "github.com/onflow/flow-go/ledger/common/pathfinder" "github.com/onflow/flow-go/ledger/complete" @@ -465,9 +465,9 @@ func getRegisterType(key ledger.Key) string { return "account storage ID" case state.CodesStorageIDKey: return "code storage ID" - case handler.BlockStoreLatestBlockKey: + case environment.BlockStoreLatestBlockKey: return "latest block" - case handler.BlockStoreLatestBlockProposalKey: + case environment.BlockStoreLatestBlockProposalKey: return "latest block proposal" } diff --git a/fvm/environment/env.go b/fvm/environment/env.go index 55f2244e2fd..53727315844 100644 --- a/fvm/environment/env.go +++ b/fvm/environment/env.go @@ -85,6 +85,8 @@ type Environment interface { // Reset resets all stateful environment modules (e.g., ContractUpdater, // EventEmitter) to initial state. Reset() + + EVMBlockStore } // ReusableCadenceRuntime is a wrapper around the cadence runtime and environment that diff --git a/fvm/evm/handler/blockHashList.go b/fvm/environment/evm_block_hash_list.go similarity index 98% rename from fvm/evm/handler/blockHashList.go rename to fvm/environment/evm_block_hash_list.go index 635e1b3fc82..cd85d2f4093 100644 --- a/fvm/evm/handler/blockHashList.go +++ b/fvm/environment/evm_block_hash_list.go @@ -1,4 +1,4 @@ -package handler +package environment import ( "encoding/binary" @@ -7,7 +7,6 @@ import ( gethCommon "github.com/ethereum/go-ethereum/common" - "github.com/onflow/flow-go/fvm/evm/types" "github.com/onflow/flow-go/model/flow" ) @@ -41,7 +40,7 @@ func IsBlockHashListMetaKey(id flow.RegisterID) bool { // smaller fixed size buckets to minimize the // number of bytes read and written during set/get operations. type BlockHashList struct { - backend types.BackendStorage + backend ValueStore rootAddress flow.Address // cached meta data @@ -55,7 +54,7 @@ type BlockHashList struct { // It tries to load the metadata from the backend // and if not exist it creates one func NewBlockHashList( - backend types.BackendStorage, + backend ValueStore, rootAddress flow.Address, capacity int, ) (*BlockHashList, error) { diff --git a/fvm/evm/handler/blockHashList_test.go b/fvm/environment/evm_block_hash_list_test.go similarity index 90% rename from fvm/evm/handler/blockHashList_test.go rename to fvm/environment/evm_block_hash_list_test.go index ebf1b21e1c8..8e731ada078 100644 --- a/fvm/evm/handler/blockHashList_test.go +++ b/fvm/environment/evm_block_hash_list_test.go @@ -1,4 +1,4 @@ -package handler_test +package environment_test import ( "testing" @@ -6,16 +6,16 @@ import ( gethCommon "github.com/ethereum/go-ethereum/common" "github.com/stretchr/testify/require" - "github.com/onflow/flow-go/fvm/evm/handler" + "github.com/onflow/flow-go/fvm/environment" "github.com/onflow/flow-go/fvm/evm/testutils" "github.com/onflow/flow-go/model/flow" ) func TestBlockHashList(t *testing.T) { - testutils.RunWithTestBackend(t, func(backend *testutils.TestBackend) { + testutils.RunWithTestBackend(t, flow.Testnet, func(backend *testutils.TestBackend) { testutils.RunWithTestFlowEVMRootAddress(t, backend, func(root flow.Address) { capacity := 256 - bhl, err := handler.NewBlockHashList(backend, root, capacity) + bhl, err := environment.NewBlockHashList(backend, root, capacity) require.NoError(t, err) require.True(t, bhl.IsEmpty()) @@ -75,7 +75,7 @@ func TestBlockHashList(t *testing.T) { require.Equal(t, gethCommon.Hash{byte(capacity + 2)}, h) // construct a new one and check - bhl, err = handler.NewBlockHashList(backend, root, capacity) + bhl, err = environment.NewBlockHashList(backend, root, capacity) require.NoError(t, err) require.False(t, bhl.IsEmpty()) diff --git a/fvm/environment/evm_block_store.go b/fvm/environment/evm_block_store.go new file mode 100644 index 00000000000..1aab448a129 --- /dev/null +++ b/fvm/environment/evm_block_store.go @@ -0,0 +1,297 @@ +package environment + +import ( + "fmt" + "time" + + gethCommon "github.com/ethereum/go-ethereum/common" + + "github.com/onflow/flow-go/fvm/evm/types" + "github.com/onflow/flow-go/model/flow" +) + +// BlockStore stores the chain of blocks +type EVMBlockStore interface { + // LatestBlock returns the latest appended block + LatestBlock() (*types.Block, error) + + // BlockHash returns the hash of the block at the given height + BlockHash(height uint64) (gethCommon.Hash, error) + + // BlockProposal returns the active block proposal + BlockProposal() (*types.BlockProposal, error) + + // StageBlockProposal updates the in-memory block proposal without writing to + // storage. Persistence happens at transaction end via FlushBlockProposal. + StageBlockProposal(*types.BlockProposal) + + // CommitBlockProposal commits the block proposal and update the chain of blocks + CommitBlockProposal(*types.BlockProposal) error +} + +const ( + BlockHashListCapacity = 256 + BlockStoreLatestBlockKey = "LatestBlock" + BlockStoreLatestBlockProposalKey = "LatestBlockProposal" +) + +// BlockStore manages EVM block storage and block proposal lifecycle during Flow block execution. +// +// Storage Keys: +// - LatestBlock: The last finalized EVM block. Updated only at CommitBlockProposal(). +// - LatestBlockProposal: The in-progress EVM block accumulating transactions. +// Its parent hash must equal hash(LatestBlock) and height must equal LatestBlock.Height + 1. +// +// Each Cadence transaction creates a new BlockStore instance with an empty cache. +// The cache avoids repeated storage reads within a single Cadence transaction. +// +// Flow Block K Execution: +// +// ├── Cadence tx 1 (succeed) +// │ ├── EVM Tx A +// │ │ ├── BlockProposal() +// │ │ │ ├── cache miss +// │ │ │ ├── read LatestBlockProposal from storage +// │ │ │ │ └── (if empty) read LatestBlock from storage (for parent hash, height) +// │ │ │ └── cache it +// │ │ └── StageBlockProposal() → update cache +// │ ├── EVM Tx B +// │ │ ├── BlockProposal() → cache hit +// │ │ └── StageBlockProposal() → update cache +// │ └── [tx end] +// │ └── FlushBlockProposal() → write LatestBlockProposal, cache = nil +// │ +// ├── Cadence tx 2 (failed) +// │ ├── EVM Tx C +// │ │ ├── BlockProposal() +// │ │ │ ├── cache miss +// │ │ │ └── read LatestBlockProposal from storage → cache it +// │ │ └── StageBlockProposal() → update cache +// │ ├── EVM Tx D +// │ │ ├── BlockProposal() → cache hit +// │ │ └── StageBlockProposal() → update cache +// │ └── [tx fail/revert] +// │ └── Reset() → cache = nil, storage unchanged +// │ +// └── System chunk tx (last) +// └── heartbeat() +// └── CommitBlockProposal() +// ├── write LatestBlock +// ├── write new LatestBlockProposal (for next flow block) +// └── cache = nil +type BlockStore struct { + chainID flow.ChainID + storage ValueStore + blockInfo BlockInfo + randGen RandomGenerator + rootAddress flow.Address + cached *types.BlockProposal +} + +var _ EVMBlockStore = &BlockStore{} + +// NewBlockStore constructs a new block store +func NewBlockStore( + chainID flow.ChainID, + storage ValueStore, + blockInfo BlockInfo, + randGen RandomGenerator, + rootAddress flow.Address, +) *BlockStore { + return &BlockStore{ + chainID: chainID, + storage: storage, + blockInfo: blockInfo, + randGen: randGen, + rootAddress: rootAddress, + } +} + +// BlockProposal returns the block proposal to be updated by the handler +func (bs *BlockStore) BlockProposal() (*types.BlockProposal, error) { + if bs.cached != nil { + return bs.cached, nil + } + // first fetch it from the storage + data, err := bs.storage.GetValue(bs.rootAddress[:], []byte(BlockStoreLatestBlockProposalKey)) + if err != nil { + return nil, err + } + if len(data) != 0 { + bp, err := types.NewBlockProposalFromBytes(data) + if err != nil { + return nil, err + } + bs.cached = bp + return bp, nil + } + bp, err := bs.constructBlockProposal() + if err != nil { + return nil, err + } + bs.cached = bp + return bp, nil +} + +func (bs *BlockStore) constructBlockProposal() (*types.BlockProposal, error) { + // if available construct a new one + cadenceHeight, err := bs.blockInfo.GetCurrentBlockHeight() + if err != nil { + return nil, err + } + + cadenceBlock, found, err := bs.blockInfo.GetBlockAtHeight(cadenceHeight) + if err != nil { + return nil, err + } + if !found { + return nil, fmt.Errorf("cadence block not found") + } + + lastExecutedBlock, err := bs.LatestBlock() + if err != nil { + return nil, err + } + + parentHash, err := lastExecutedBlock.Hash() + if err != nil { + return nil, err + } + + // cadence block timestamp is unix nanoseconds but evm blocks + // expect timestamps in unix seconds so we convert here + timestamp := uint64(cadenceBlock.Timestamp / int64(time.Second)) + + // read a random value for block proposal + prevrandao := gethCommon.Hash{} + err = bs.randGen.ReadRandom(prevrandao[:]) + if err != nil { + return nil, err + } + + blockProposal := types.NewBlockProposal( + parentHash, + lastExecutedBlock.Height+1, + timestamp, + lastExecutedBlock.TotalSupply, + prevrandao, + ) + + return blockProposal, nil +} + +// UpdateBlockProposal updates the block proposal +func (bs *BlockStore) updateBlockProposal(bp *types.BlockProposal) error { + blockProposalBytes, err := bp.ToBytes() + if err != nil { + return types.NewFatalError(err) + } + + return bs.storage.SetValue( + bs.rootAddress[:], + []byte(BlockStoreLatestBlockProposalKey), + blockProposalBytes, + ) +} + +// CommitBlockProposal commits the block proposal to the chain +func (bs *BlockStore) CommitBlockProposal(bp *types.BlockProposal) error { + bp.PopulateRoots() + + blockBytes, err := bp.Block.ToBytes() + if err != nil { + return types.NewFatalError(err) + } + + err = bs.storage.SetValue(bs.rootAddress[:], []byte(BlockStoreLatestBlockKey), blockBytes) + if err != nil { + return err + } + + hash, err := bp.Block.Hash() + if err != nil { + return err + } + + bhl, err := bs.getBlockHashList() + if err != nil { + return err + } + err = bhl.Push(bp.Block.Height, hash) + if err != nil { + return err + } + + // Construct and store the new block proposal eagerly to maintain + // state compatibility with the previous implementation. + newBP, err := bs.constructBlockProposal() + if err != nil { + return err + } + err = bs.updateBlockProposal(newBP) + if err != nil { + return err + } + bs.cached = nil + return nil +} + +// LatestBlock returns the latest executed block +func (bs *BlockStore) LatestBlock() (*types.Block, error) { + data, err := bs.storage.GetValue(bs.rootAddress[:], []byte(BlockStoreLatestBlockKey)) + if err != nil { + return nil, err + } + if len(data) == 0 { + return types.GenesisBlock(bs.chainID), nil + } + return types.NewBlockFromBytes(data) +} + +// BlockHash returns the block hash for the last x blocks +func (bs *BlockStore) BlockHash(height uint64) (gethCommon.Hash, error) { + bhl, err := bs.getBlockHashList() + if err != nil { + return gethCommon.Hash{}, err + } + _, hash, err := bhl.BlockHashByHeight(height) + return hash, err +} + +func (bs *BlockStore) getBlockHashList() (*BlockHashList, error) { + bhl, err := NewBlockHashList(bs.storage, bs.rootAddress, BlockHashListCapacity) + if err != nil { + return nil, err + } + + if bhl.IsEmpty() { + err = bhl.Push( + types.GenesisBlock(bs.chainID).Height, + types.GenesisBlockHash(bs.chainID), + ) + if err != nil { + return nil, err + } + } + + return bhl, nil +} + +func (bs *BlockStore) ResetBlockProposal() { + bs.cached = nil +} + +func (bs *BlockStore) StageBlockProposal(bp *types.BlockProposal) { + bs.cached = bp +} + +func (bs *BlockStore) FlushBlockProposal() error { + if bs.cached == nil { + return nil + } + err := bs.updateBlockProposal(bs.cached) + if err != nil { + return err + } + return nil +} diff --git a/fvm/evm/handler/blockstore_benchmark_test.go b/fvm/environment/evm_block_store_benchmark_test.go similarity index 84% rename from fvm/evm/handler/blockstore_benchmark_test.go rename to fvm/environment/evm_block_store_benchmark_test.go index 013b6326b0a..7f7e5170a21 100644 --- a/fvm/evm/handler/blockstore_benchmark_test.go +++ b/fvm/environment/evm_block_store_benchmark_test.go @@ -1,4 +1,4 @@ -package handler_test +package environment_test import ( "testing" @@ -6,7 +6,7 @@ import ( "github.com/stretchr/testify/require" - "github.com/onflow/flow-go/fvm/evm/handler" + "github.com/onflow/flow-go/fvm/environment" "github.com/onflow/flow-go/fvm/evm/testutils" "github.com/onflow/flow-go/model/flow" ) @@ -14,17 +14,16 @@ import ( func BenchmarkProposalGrowth(b *testing.B) { benchmarkBlockProposalGrowth(b, 1000) } func benchmarkBlockProposalGrowth(b *testing.B, txCounts int) { - testutils.RunWithTestBackend(b, func(backend *testutils.TestBackend) { + testutils.RunWithTestBackend(b, flow.Testnet, func(backend *testutils.TestBackend) { testutils.RunWithTestFlowEVMRootAddress(b, backend, func(rootAddr flow.Address) { - bs := handler.NewBlockStore(flow.Testnet, backend, rootAddr) + bs := environment.NewBlockStore(flow.Testnet, backend, backend, backend, rootAddr) for i := 0; i < txCounts; i++ { bp, err := bs.BlockProposal() require.NoError(b, err) res := testutils.RandomResultFixture(b) bp.AppendTransaction(res) - err = bs.UpdateBlockProposal(bp) - require.NoError(b, err) + bs.StageBlockProposal(bp) } // check the impact of updating block proposal after x number of transactions @@ -34,8 +33,7 @@ func benchmarkBlockProposalGrowth(b *testing.B, txCounts int) { require.NoError(b, err) res := testutils.RandomResultFixture(b) bp.AppendTransaction(res) - err = bs.UpdateBlockProposal(bp) - require.NoError(b, err) + bs.StageBlockProposal(bp) b.ReportMetric(float64(time.Since(startTime).Nanoseconds()), "proposal_update_time_ns") b.ReportMetric(float64(backend.TotalBytesRead()), "proposal_update_bytes_read") diff --git a/fvm/environment/evm_block_store_test.go b/fvm/environment/evm_block_store_test.go new file mode 100644 index 00000000000..b7a962db065 --- /dev/null +++ b/fvm/environment/evm_block_store_test.go @@ -0,0 +1,338 @@ +package environment_test + +import ( + "math/big" + "testing" + + gethCommon "github.com/ethereum/go-ethereum/common" + "github.com/stretchr/testify/require" + + "github.com/onflow/flow-go/fvm/environment" + "github.com/onflow/flow-go/fvm/evm/testutils" + "github.com/onflow/flow-go/fvm/evm/types" + "github.com/onflow/flow-go/model/flow" +) + +// TestBlockStoreLifecycle tests the block store lifecycle across two Flow blocks, +// following the execution flow documented in BlockStore comments. +// +// This test verifies: +// - Each Cadence tx creates a new BlockStore instance with empty cache +// - Cache hits/misses work correctly within a Cadence tx +// - FlushBlockProposal persists the proposal for the next Cadence tx +// - Reset discards changes on failed Cadence tx +// - CommitBlockProposal finalizes the block and clears LatestBlockProposal +// - Lazy construction of new proposal in the next Flow block +func TestBlockStoreLifecycle(t *testing.T) { + chainID := flow.Testnet + testutils.RunWithTestBackend(t, chainID, func(backend *testutils.TestBackend) { + testutils.RunWithTestFlowEVMRootAddress(t, backend, func(root flow.Address) { + + // Gas constants for each EVM tx - using distinct digit positions for easy verification + // Each tx uses a different decimal place, so accumulated values are easy to read + const ( + evmTxAGas = uint64(1) // ones place + evmTxBGas = uint64(20) // tens place + evmTxCGas = uint64(300) // hundreds place + evmTxDGas = uint64(4000) // thousands place + evmTxEGas = uint64(50000) // ten-thousands place + evmTxFGas = uint64(600000) // hundred-thousands place + ) + + // TotalSupply changes for each EVM tx - also using distinct digit positions + // TotalSupply represents native token deposited in EVM (can increase via deposits) + var ( + evmTxASupply = big.NewInt(100) // ones place (x100) + evmTxBSupply = big.NewInt(2000) // tens place (x100) + evmTxESupply = big.NewInt(5000000) // ten-thousands place (x100) + evmTxFSupply = big.NewInt(60000000) // hundred-thousands place (x100) + ) + + // ============================================ + // Flow Block K + // ============================================ + + // --- Cadence tx 1 (succeed) --- + // Each Cadence tx creates a new BlockStore instance + bs1 := environment.NewBlockStore(chainID, backend, backend, backend, root) + + // EVM Tx A: BlockProposal() - cache miss, loads from storage + // Since this is the first ever call, LatestBlockProposal is empty, + // so it reads LatestBlock (genesis) to construct proposal + bpA, err := bs1.BlockProposal() + require.NoError(t, err) + require.Equal(t, uint64(1), bpA.Height) + expectedParentHash, err := types.GenesisBlock(chainID).Hash() + require.NoError(t, err) + require.Equal(t, expectedParentHash, bpA.ParentBlockHash) + require.Equal(t, uint64(0), bpA.TotalGasUsed) // starts at 0 + + // Verify TotalSupply is inherited from genesis block (0) + require.Equal(t, big.NewInt(0), bpA.TotalSupply) + + // Verify PrevRandao is set (non-zero) - read from RandomGenerator during construction + require.NotEqual(t, gethCommon.Hash{}, bpA.PrevRandao) + prevRandaoBlockK := bpA.PrevRandao // save for later comparison + + // EVM Tx A: StageBlockProposal() - update cache (accumulate gas and supply) + bpA.TotalGasUsed += evmTxAGas + bpA.TotalSupply = new(big.Int).Add(bpA.TotalSupply, evmTxASupply) + bs1.StageBlockProposal(bpA) + + // EVM Tx B: BlockProposal() - cache hit (same BlockStore instance) + bpB, err := bs1.BlockProposal() + require.NoError(t, err) + require.Equal(t, bpA, bpB) // same pointer, cache hit + require.Equal(t, uint64(1), bpB.TotalGasUsed) // sees Tx A's gas + require.Equal(t, big.NewInt(100), bpB.TotalSupply) // sees Tx A's supply + require.Equal(t, prevRandaoBlockK, bpB.PrevRandao) // PrevRandao unchanged within block + + // EVM Tx B: StageBlockProposal() - update cache (accumulate gas and supply) + bpB.TotalGasUsed += evmTxBGas + bpB.TotalSupply = new(big.Int).Add(bpB.TotalSupply, evmTxBSupply) + bs1.StageBlockProposal(bpB) + + // [tx end]: FlushBlockProposal() - write LatestBlockProposal to storage + // At this point, TotalGasUsed = 1 + 20 = 21, TotalSupply = 100 + 2000 = 2100 + require.Equal(t, uint64(21), bpB.TotalGasUsed) + require.Equal(t, big.NewInt(2100), bpB.TotalSupply) + err = bs1.FlushBlockProposal() + require.NoError(t, err) + + // --- Cadence tx 2 (failed) --- + // New BlockStore instance (simulating new Cadence tx) + bs2 := environment.NewBlockStore(chainID, backend, backend, backend, root) + + // EVM Tx C: BlockProposal() - cache miss, loads from storage + // Should load the flushed proposal from tx 1 with accumulated gas = 21, supply = 2100 + bpC, err := bs2.BlockProposal() + require.NoError(t, err) + require.Equal(t, uint64(21), bpC.TotalGasUsed) // persisted from tx 1 + require.Equal(t, big.NewInt(2100), bpC.TotalSupply) // persisted from tx 1 + require.Equal(t, prevRandaoBlockK, bpC.PrevRandao) // PrevRandao unchanged within block + + // EVM Tx C: StageBlockProposal() - update cache (accumulate gas) + bpC.TotalGasUsed += evmTxCGas + bs2.StageBlockProposal(bpC) + + // EVM Tx D: BlockProposal() - cache hit + bpD, err := bs2.BlockProposal() + require.NoError(t, err) + require.Equal(t, uint64(321), bpD.TotalGasUsed) // sees A+B+C = 1+20+300 + + // EVM Tx D: StageBlockProposal() - update cache (accumulate gas) + bpD.TotalGasUsed += evmTxDGas + bs2.StageBlockProposal(bpD) + + // Verify all 4 txs' gas is accumulated before revert = 1+20+300+4000 = 4321 + require.Equal(t, uint64(4321), bpD.TotalGasUsed) + + // [tx fail/revert]: Reset() - cache = nil, storage unchanged + bs2.ResetBlockProposal() + + // Verify storage is unchanged by creating a new BlockStore and reading + // Should only see gas from Cadence tx 1 (A+B = 21), not from failed tx 2 (C+D) + bs2Check := environment.NewBlockStore(chainID, backend, backend, backend, root) + bpCheck, err := bs2Check.BlockProposal() + require.NoError(t, err) + require.Equal(t, uint64(21), bpCheck.TotalGasUsed) // unchanged from tx 1 + require.Equal(t, big.NewInt(2100), bpCheck.TotalSupply) // unchanged from tx 1 + + // --- System chunk tx (last) --- + // New BlockStore instance for system tx + bsSystem := environment.NewBlockStore(chainID, backend, backend, backend, root) + + // heartbeat() -> CommitBlockProposal() + bpCommit, err := bsSystem.BlockProposal() + require.NoError(t, err) + require.Equal(t, uint64(21), bpCommit.TotalGasUsed) // A+B = 21 + require.Equal(t, big.NewInt(2100), bpCommit.TotalSupply) // A+B supply = 2100 + + // CommitBlockProposal: write LatestBlock, remove LatestBlockProposal + err = bsSystem.CommitBlockProposal(bpCommit) + require.NoError(t, err) + + // Verify LatestBlock is now the committed block with accumulated values + latestBlock, err := bsSystem.LatestBlock() + require.NoError(t, err) + require.Equal(t, uint64(1), latestBlock.Height) + require.Equal(t, uint64(21), latestBlock.TotalGasUsed) + require.Equal(t, big.NewInt(2100), latestBlock.TotalSupply) + require.Equal(t, prevRandaoBlockK, latestBlock.PrevRandao) // PrevRandao preserved in block + + // ============================================ + // Flow Block K+1 + // ============================================ + + // --- Cadence tx 1 --- + // New BlockStore instance (new Flow block, new Cadence tx) + bsK1 := environment.NewBlockStore(chainID, backend, backend, backend, root) + + // EVM Tx E: BlockProposal() - cache miss + // After CommitBlockProposal, LatestBlockProposal was cleared (or new one written) + // This tests lazy construction: reads LatestBlock to construct new proposal + bpE, err := bsK1.BlockProposal() + require.NoError(t, err) + + // Verify the new proposal is a child of the committed block + require.Equal(t, uint64(2), bpE.Height) // height incremented + committedBlockHash, err := latestBlock.Hash() + require.NoError(t, err) + require.Equal(t, committedBlockHash, bpE.ParentBlockHash) // parent is committed block + + // Verify the proposal starts fresh (no accumulated gas from previous block) + require.Equal(t, uint64(0), bpE.TotalGasUsed) + + // Verify TotalSupply is inherited from the committed block (2100) + require.Equal(t, big.NewInt(2100), bpE.TotalSupply) + + // Verify PrevRandao is set and DIFFERENT from block K (new random value for new block) + require.NotEqual(t, gethCommon.Hash{}, bpE.PrevRandao) + require.NotEqual(t, prevRandaoBlockK, bpE.PrevRandao) // different random for new block + prevRandaoBlockK1 := bpE.PrevRandao + + // EVM Tx E: StageBlockProposal() - update cache (accumulate gas and supply) + bpE.TotalGasUsed += evmTxEGas + bpE.TotalSupply = new(big.Int).Add(bpE.TotalSupply, evmTxESupply) + bsK1.StageBlockProposal(bpE) + + // EVM Tx F: BlockProposal() - cache hit + bpF, err := bsK1.BlockProposal() + require.NoError(t, err) + require.Equal(t, uint64(50000), bpF.TotalGasUsed) // sees Tx E's gas + require.Equal(t, big.NewInt(5002100), bpF.TotalSupply) // 2100 + 5000000 + require.Equal(t, prevRandaoBlockK1, bpF.PrevRandao) // PrevRandao unchanged within block + + // EVM Tx F: StageBlockProposal() - update cache (accumulate gas and supply) + bpF.TotalGasUsed += evmTxFGas + bpF.TotalSupply = new(big.Int).Add(bpF.TotalSupply, evmTxFSupply) + bsK1.StageBlockProposal(bpF) + + // [tx end]: FlushBlockProposal() + // Gas: E+F = 50000+600000 = 650000 + // Supply: 2100 + 5000000 + 60000000 = 65002100 + require.Equal(t, uint64(650000), bpF.TotalGasUsed) + require.Equal(t, big.NewInt(65002100), bpF.TotalSupply) + err = bsK1.FlushBlockProposal() + require.NoError(t, err) + + // --- System chunk tx for block K+1 --- + bsSystemK1 := environment.NewBlockStore(chainID, backend, backend, backend, root) + bpCommitK1, err := bsSystemK1.BlockProposal() + require.NoError(t, err) + require.Equal(t, uint64(650000), bpCommitK1.TotalGasUsed) // E+F = 650000 + require.Equal(t, big.NewInt(65002100), bpCommitK1.TotalSupply) // accumulated supply + + err = bsSystemK1.CommitBlockProposal(bpCommitK1) + require.NoError(t, err) + + // Verify LatestBlock is now block 2 with accumulated values + latestBlockK1, err := bsSystemK1.LatestBlock() + require.NoError(t, err) + require.Equal(t, uint64(2), latestBlockK1.Height) + require.Equal(t, uint64(650000), latestBlockK1.TotalGasUsed) + require.Equal(t, big.NewInt(65002100), latestBlockK1.TotalSupply) + require.Equal(t, prevRandaoBlockK1, latestBlockK1.PrevRandao) // PrevRandao preserved + + // Verify block hashes are correct + hash0, err := bsSystemK1.BlockHash(0) + require.NoError(t, err) + require.Equal(t, types.GenesisBlockHash(chainID), hash0) + + hash1, err := bsSystemK1.BlockHash(1) + require.NoError(t, err) + require.Equal(t, committedBlockHash, hash1) + + hash2, err := bsSystemK1.BlockHash(2) + require.NoError(t, err) + expectedHash2, err := latestBlockK1.Hash() + require.NoError(t, err) + require.Equal(t, expectedHash2, hash2) + }) + }) +} + +func TestBlockStore(t *testing.T) { + + var chainID = flow.Testnet + testutils.RunWithTestBackend(t, chainID, func(backend *testutils.TestBackend) { + testutils.RunWithTestFlowEVMRootAddress(t, backend, func(root flow.Address) { + bs := environment.NewBlockStore(chainID, backend, backend, backend, root) + + // check the Genesis block + b, err := bs.LatestBlock() + require.NoError(t, err) + require.Equal(t, types.GenesisBlock(chainID), b) + h, err := bs.BlockHash(0) + require.NoError(t, err) + require.Equal(t, types.GenesisBlockHash(chainID), h) + + // test block proposal construction from the Genesis block + bp, err := bs.BlockProposal() + require.NoError(t, err) + require.Equal(t, uint64(1), bp.Height) + expectedParentHash, err := types.GenesisBlock(chainID).Hash() + require.NoError(t, err) + require.Equal(t, expectedParentHash, bp.ParentBlockHash) + + // if no commit and again block proposal call should return the same + retbp, err := bs.BlockProposal() + require.NoError(t, err) + require.Equal(t, bp, retbp) + + // update the block proposal + bp.TotalGasUsed += 100 + bs.StageBlockProposal(bp) + err = bs.FlushBlockProposal() + require.NoError(t, err) + + bs = environment.NewBlockStore(chainID, backend, backend, backend, root) + retbp, err = bs.BlockProposal() + require.NoError(t, err) + require.Equal(t, bp, retbp) + + // update the block proposal again + supply := big.NewInt(100) + bp.TotalSupply = supply + bs.StageBlockProposal(bp) + err = bs.FlushBlockProposal() + require.NoError(t, err) + // this should still return the genesis block + retb, err := bs.LatestBlock() + require.NoError(t, err) + require.Equal(t, types.GenesisBlock(chainID), retb) + + // commit the changes + err = bs.CommitBlockProposal(bp) + require.NoError(t, err) + retb, err = bs.LatestBlock() + require.NoError(t, err) + require.Equal(t, supply, retb.TotalSupply) + require.Equal(t, uint64(1), retb.Height) + + retbp, err = bs.BlockProposal() + require.NoError(t, err) + require.Equal(t, uint64(2), retbp.Height) + + // check block hashes + // genesis + h, err = bs.BlockHash(0) + require.NoError(t, err) + require.Equal(t, types.GenesisBlockHash(chainID), h) + + // block 1 + h, err = bs.BlockHash(1) + require.NoError(t, err) + expected, err := bp.Hash() + require.NoError(t, err) + require.Equal(t, expected, h) + + // block 2 + h, err = bs.BlockHash(2) + require.NoError(t, err) + require.Equal(t, gethCommon.Hash{}, h) + }) + + }) + +} diff --git a/fvm/environment/facade_env.go b/fvm/environment/facade_env.go index 7129a2ca453..1169f6d4d75 100644 --- a/fvm/environment/facade_env.go +++ b/fvm/environment/facade_env.go @@ -8,6 +8,8 @@ import ( "github.com/onflow/cadence/interpreter" "github.com/onflow/cadence/sema" + "github.com/onflow/flow-go/fvm/evm" + "github.com/onflow/flow-go/fvm/storage" "github.com/onflow/flow-go/fvm/storage/snapshot" "github.com/onflow/flow-go/fvm/storage/state" @@ -51,6 +53,7 @@ type facadeEnvironment struct { *ContractReader ContractUpdater *Programs + *BlockStore accounts Accounts txnState storage.TransactionPreparer @@ -147,6 +150,7 @@ func newFacadeEnvironment( common.Address(sc.Crypto.Address), ), ContractUpdater: NoContractUpdater{}, + Programs: NewPrograms( tracer, meter, @@ -200,6 +204,13 @@ func NewScriptEnv( params.ScriptInfoParams.ID[:], ) env.addParseRestrictedChecks() + env.BlockStore = NewBlockStore( + params.Chain.ChainID(), + env.ValueStore, + env.BlockInfo, + env.RandomGenerator, + evm.StorageAccountAddress(params.Chain.ChainID()), + ) return env } @@ -270,6 +281,14 @@ func NewTransactionEnvironment( params.TransactionInfoParams.RandomSourceHistoryCallAllowed, ) + env.BlockStore = NewBlockStore( + params.Chain.ChainID(), + env.ValueStore, + env.BlockInfo, + env.RandomGenerator, + evm.StorageAccountAddress(params.Chain.ChainID()), + ) + env.addParseRestrictedChecks() return env @@ -331,13 +350,22 @@ func (env *facadeEnvironment) FlushPendingUpdates() ( ContractUpdates, error, ) { - return env.ContractUpdater.Commit() + updates, err := env.ContractUpdater.Commit() + if err != nil { + return ContractUpdates{}, err + } + err = env.BlockStore.FlushBlockProposal() + if err != nil { + return ContractUpdates{}, err + } + return updates, nil } func (env *facadeEnvironment) Reset() { env.ContractUpdater.Reset() env.EventEmitter.Reset() env.Programs.Reset() + env.BlockStore.ResetBlockProposal() } // Miscellaneous Cadence runtime.Interface API diff --git a/fvm/environment/mock/environment.go b/fvm/environment/mock/environment.go index ff7c29bbbc6..54c9e3aa103 100644 --- a/fvm/environment/mock/environment.go +++ b/fvm/environment/mock/environment.go @@ -7,14 +7,16 @@ package mock import ( "time" + "github.com/ethereum/go-ethereum/common" "github.com/onflow/atree" "github.com/onflow/cadence" "github.com/onflow/cadence/ast" - "github.com/onflow/cadence/common" + common0 "github.com/onflow/cadence/common" "github.com/onflow/cadence/interpreter" "github.com/onflow/cadence/runtime" "github.com/onflow/cadence/sema" "github.com/onflow/flow-go/fvm/environment" + "github.com/onflow/flow-go/fvm/evm/types" "github.com/onflow/flow-go/fvm/meter" "github.com/onflow/flow-go/fvm/tracing" "github.com/onflow/flow-go/model/flow" @@ -518,6 +520,123 @@ func (_c *Environment_BLSVerifyPOP_Call) RunAndReturn(run func(publicKey *runtim return _c } +// BlockHash provides a mock function for the type Environment +func (_mock *Environment) BlockHash(height uint64) (common.Hash, error) { + ret := _mock.Called(height) + + if len(ret) == 0 { + panic("no return value specified for BlockHash") + } + + var r0 common.Hash + var r1 error + if returnFunc, ok := ret.Get(0).(func(uint64) (common.Hash, error)); ok { + return returnFunc(height) + } + if returnFunc, ok := ret.Get(0).(func(uint64) common.Hash); ok { + r0 = returnFunc(height) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(common.Hash) + } + } + if returnFunc, ok := ret.Get(1).(func(uint64) error); ok { + r1 = returnFunc(height) + } else { + r1 = ret.Error(1) + } + return r0, r1 +} + +// Environment_BlockHash_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'BlockHash' +type Environment_BlockHash_Call struct { + *mock.Call +} + +// BlockHash is a helper method to define mock.On call +// - height uint64 +func (_e *Environment_Expecter) BlockHash(height interface{}) *Environment_BlockHash_Call { + return &Environment_BlockHash_Call{Call: _e.mock.On("BlockHash", height)} +} + +func (_c *Environment_BlockHash_Call) Run(run func(height uint64)) *Environment_BlockHash_Call { + _c.Call.Run(func(args mock.Arguments) { + var arg0 uint64 + if args[0] != nil { + arg0 = args[0].(uint64) + } + run( + arg0, + ) + }) + return _c +} + +func (_c *Environment_BlockHash_Call) Return(hash common.Hash, err error) *Environment_BlockHash_Call { + _c.Call.Return(hash, err) + return _c +} + +func (_c *Environment_BlockHash_Call) RunAndReturn(run func(height uint64) (common.Hash, error)) *Environment_BlockHash_Call { + _c.Call.Return(run) + return _c +} + +// BlockProposal provides a mock function for the type Environment +func (_mock *Environment) BlockProposal() (*types.BlockProposal, error) { + ret := _mock.Called() + + if len(ret) == 0 { + panic("no return value specified for BlockProposal") + } + + var r0 *types.BlockProposal + var r1 error + if returnFunc, ok := ret.Get(0).(func() (*types.BlockProposal, error)); ok { + return returnFunc() + } + if returnFunc, ok := ret.Get(0).(func() *types.BlockProposal); ok { + r0 = returnFunc() + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*types.BlockProposal) + } + } + if returnFunc, ok := ret.Get(1).(func() error); ok { + r1 = returnFunc() + } else { + r1 = ret.Error(1) + } + return r0, r1 +} + +// Environment_BlockProposal_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'BlockProposal' +type Environment_BlockProposal_Call struct { + *mock.Call +} + +// BlockProposal is a helper method to define mock.On call +func (_e *Environment_Expecter) BlockProposal() *Environment_BlockProposal_Call { + return &Environment_BlockProposal_Call{Call: _e.mock.On("BlockProposal")} +} + +func (_c *Environment_BlockProposal_Call) Run(run func()) *Environment_BlockProposal_Call { + _c.Call.Run(func(args mock.Arguments) { + run() + }) + return _c +} + +func (_c *Environment_BlockProposal_Call) Return(blockProposal *types.BlockProposal, err error) *Environment_BlockProposal_Call { + _c.Call.Return(blockProposal, err) + return _c +} + +func (_c *Environment_BlockProposal_Call) RunAndReturn(run func() (*types.BlockProposal, error)) *Environment_BlockProposal_Call { + _c.Call.Return(run) + return _c +} + // BorrowCadenceRuntime provides a mock function for the type Environment func (_mock *Environment) BorrowCadenceRuntime() environment.ReusableCadenceRuntime { ret := _mock.Called() @@ -638,8 +757,59 @@ func (_c *Environment_CheckPayerBalanceAndGetMaxTxFees_Call) RunAndReturn(run fu return _c } +// CommitBlockProposal provides a mock function for the type Environment +func (_mock *Environment) CommitBlockProposal(blockProposal *types.BlockProposal) error { + ret := _mock.Called(blockProposal) + + if len(ret) == 0 { + panic("no return value specified for CommitBlockProposal") + } + + var r0 error + if returnFunc, ok := ret.Get(0).(func(*types.BlockProposal) error); ok { + r0 = returnFunc(blockProposal) + } else { + r0 = ret.Error(0) + } + return r0 +} + +// Environment_CommitBlockProposal_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'CommitBlockProposal' +type Environment_CommitBlockProposal_Call struct { + *mock.Call +} + +// CommitBlockProposal is a helper method to define mock.On call +// - blockProposal *types.BlockProposal +func (_e *Environment_Expecter) CommitBlockProposal(blockProposal interface{}) *Environment_CommitBlockProposal_Call { + return &Environment_CommitBlockProposal_Call{Call: _e.mock.On("CommitBlockProposal", blockProposal)} +} + +func (_c *Environment_CommitBlockProposal_Call) Run(run func(blockProposal *types.BlockProposal)) *Environment_CommitBlockProposal_Call { + _c.Call.Run(func(args mock.Arguments) { + var arg0 *types.BlockProposal + if args[0] != nil { + arg0 = args[0].(*types.BlockProposal) + } + run( + arg0, + ) + }) + return _c +} + +func (_c *Environment_CommitBlockProposal_Call) Return(err error) *Environment_CommitBlockProposal_Call { + _c.Call.Return(err) + return _c +} + +func (_c *Environment_CommitBlockProposal_Call) RunAndReturn(run func(blockProposal *types.BlockProposal) error) *Environment_CommitBlockProposal_Call { + _c.Call.Return(run) + return _c +} + // ComputationAvailable provides a mock function for the type Environment -func (_mock *Environment) ComputationAvailable(computationUsage common.ComputationUsage) bool { +func (_mock *Environment) ComputationAvailable(computationUsage common0.ComputationUsage) bool { ret := _mock.Called(computationUsage) if len(ret) == 0 { @@ -647,7 +817,7 @@ func (_mock *Environment) ComputationAvailable(computationUsage common.Computati } var r0 bool - if returnFunc, ok := ret.Get(0).(func(common.ComputationUsage) bool); ok { + if returnFunc, ok := ret.Get(0).(func(common0.ComputationUsage) bool); ok { r0 = returnFunc(computationUsage) } else { r0 = ret.Get(0).(bool) @@ -661,16 +831,16 @@ type Environment_ComputationAvailable_Call struct { } // ComputationAvailable is a helper method to define mock.On call -// - computationUsage common.ComputationUsage +// - computationUsage common0.ComputationUsage func (_e *Environment_Expecter) ComputationAvailable(computationUsage interface{}) *Environment_ComputationAvailable_Call { return &Environment_ComputationAvailable_Call{Call: _e.mock.On("ComputationAvailable", computationUsage)} } -func (_c *Environment_ComputationAvailable_Call) Run(run func(computationUsage common.ComputationUsage)) *Environment_ComputationAvailable_Call { +func (_c *Environment_ComputationAvailable_Call) Run(run func(computationUsage common0.ComputationUsage)) *Environment_ComputationAvailable_Call { _c.Call.Run(func(args mock.Arguments) { - var arg0 common.ComputationUsage + var arg0 common0.ComputationUsage if args[0] != nil { - arg0 = args[0].(common.ComputationUsage) + arg0 = args[0].(common0.ComputationUsage) } run( arg0, @@ -684,7 +854,7 @@ func (_c *Environment_ComputationAvailable_Call) Return(b bool) *Environment_Com return _c } -func (_c *Environment_ComputationAvailable_Call) RunAndReturn(run func(computationUsage common.ComputationUsage) bool) *Environment_ComputationAvailable_Call { +func (_c *Environment_ComputationAvailable_Call) RunAndReturn(run func(computationUsage common0.ComputationUsage) bool) *Environment_ComputationAvailable_Call { _c.Call.Return(run) return _c } @@ -736,7 +906,7 @@ func (_c *Environment_ComputationIntensities_Call) RunAndReturn(run func() meter } // ComputationRemaining provides a mock function for the type Environment -func (_mock *Environment) ComputationRemaining(kind common.ComputationKind) uint64 { +func (_mock *Environment) ComputationRemaining(kind common0.ComputationKind) uint64 { ret := _mock.Called(kind) if len(ret) == 0 { @@ -744,7 +914,7 @@ func (_mock *Environment) ComputationRemaining(kind common.ComputationKind) uint } var r0 uint64 - if returnFunc, ok := ret.Get(0).(func(common.ComputationKind) uint64); ok { + if returnFunc, ok := ret.Get(0).(func(common0.ComputationKind) uint64); ok { r0 = returnFunc(kind) } else { r0 = ret.Get(0).(uint64) @@ -758,16 +928,16 @@ type Environment_ComputationRemaining_Call struct { } // ComputationRemaining is a helper method to define mock.On call -// - kind common.ComputationKind +// - kind common0.ComputationKind func (_e *Environment_Expecter) ComputationRemaining(kind interface{}) *Environment_ComputationRemaining_Call { return &Environment_ComputationRemaining_Call{Call: _e.mock.On("ComputationRemaining", kind)} } -func (_c *Environment_ComputationRemaining_Call) Run(run func(kind common.ComputationKind)) *Environment_ComputationRemaining_Call { +func (_c *Environment_ComputationRemaining_Call) Run(run func(kind common0.ComputationKind)) *Environment_ComputationRemaining_Call { _c.Call.Run(func(args mock.Arguments) { - var arg0 common.ComputationKind + var arg0 common0.ComputationKind if args[0] != nil { - arg0 = args[0].(common.ComputationKind) + arg0 = args[0].(common0.ComputationKind) } run( arg0, @@ -781,7 +951,7 @@ func (_c *Environment_ComputationRemaining_Call) Return(v uint64) *Environment_C return _c } -func (_c *Environment_ComputationRemaining_Call) RunAndReturn(run func(kind common.ComputationKind) uint64) *Environment_ComputationRemaining_Call { +func (_c *Environment_ComputationRemaining_Call) RunAndReturn(run func(kind common0.ComputationKind) uint64) *Environment_ComputationRemaining_Call { _c.Call.Return(run) return _c } @@ -1388,7 +1558,7 @@ func (_c *Environment_FlushPendingUpdates_Call) RunAndReturn(run func() (environ } // GenerateAccountID provides a mock function for the type Environment -func (_mock *Environment) GenerateAccountID(address common.Address) (uint64, error) { +func (_mock *Environment) GenerateAccountID(address common0.Address) (uint64, error) { ret := _mock.Called(address) if len(ret) == 0 { @@ -1397,15 +1567,15 @@ func (_mock *Environment) GenerateAccountID(address common.Address) (uint64, err var r0 uint64 var r1 error - if returnFunc, ok := ret.Get(0).(func(common.Address) (uint64, error)); ok { + if returnFunc, ok := ret.Get(0).(func(common0.Address) (uint64, error)); ok { return returnFunc(address) } - if returnFunc, ok := ret.Get(0).(func(common.Address) uint64); ok { + if returnFunc, ok := ret.Get(0).(func(common0.Address) uint64); ok { r0 = returnFunc(address) } else { r0 = ret.Get(0).(uint64) } - if returnFunc, ok := ret.Get(1).(func(common.Address) error); ok { + if returnFunc, ok := ret.Get(1).(func(common0.Address) error); ok { r1 = returnFunc(address) } else { r1 = ret.Error(1) @@ -1419,16 +1589,16 @@ type Environment_GenerateAccountID_Call struct { } // GenerateAccountID is a helper method to define mock.On call -// - address common.Address +// - address common0.Address func (_e *Environment_Expecter) GenerateAccountID(address interface{}) *Environment_GenerateAccountID_Call { return &Environment_GenerateAccountID_Call{Call: _e.mock.On("GenerateAccountID", address)} } -func (_c *Environment_GenerateAccountID_Call) Run(run func(address common.Address)) *Environment_GenerateAccountID_Call { +func (_c *Environment_GenerateAccountID_Call) Run(run func(address common0.Address)) *Environment_GenerateAccountID_Call { _c.Call.Run(func(args mock.Arguments) { - var arg0 common.Address + var arg0 common0.Address if args[0] != nil { - arg0 = args[0].(common.Address) + arg0 = args[0].(common0.Address) } run( arg0, @@ -1442,7 +1612,7 @@ func (_c *Environment_GenerateAccountID_Call) Return(v uint64, err error) *Envir return _c } -func (_c *Environment_GenerateAccountID_Call) RunAndReturn(run func(address common.Address) (uint64, error)) *Environment_GenerateAccountID_Call { +func (_c *Environment_GenerateAccountID_Call) RunAndReturn(run func(address common0.Address) (uint64, error)) *Environment_GenerateAccountID_Call { _c.Call.Return(run) return _c } @@ -1563,7 +1733,7 @@ func (_c *Environment_GetAccount_Call) RunAndReturn(run func(address flow.Addres } // GetAccountAvailableBalance provides a mock function for the type Environment -func (_mock *Environment) GetAccountAvailableBalance(address common.Address) (uint64, error) { +func (_mock *Environment) GetAccountAvailableBalance(address common0.Address) (uint64, error) { ret := _mock.Called(address) if len(ret) == 0 { @@ -1572,15 +1742,15 @@ func (_mock *Environment) GetAccountAvailableBalance(address common.Address) (ui var r0 uint64 var r1 error - if returnFunc, ok := ret.Get(0).(func(common.Address) (uint64, error)); ok { + if returnFunc, ok := ret.Get(0).(func(common0.Address) (uint64, error)); ok { return returnFunc(address) } - if returnFunc, ok := ret.Get(0).(func(common.Address) uint64); ok { + if returnFunc, ok := ret.Get(0).(func(common0.Address) uint64); ok { r0 = returnFunc(address) } else { r0 = ret.Get(0).(uint64) } - if returnFunc, ok := ret.Get(1).(func(common.Address) error); ok { + if returnFunc, ok := ret.Get(1).(func(common0.Address) error); ok { r1 = returnFunc(address) } else { r1 = ret.Error(1) @@ -1594,16 +1764,16 @@ type Environment_GetAccountAvailableBalance_Call struct { } // GetAccountAvailableBalance is a helper method to define mock.On call -// - address common.Address +// - address common0.Address func (_e *Environment_Expecter) GetAccountAvailableBalance(address interface{}) *Environment_GetAccountAvailableBalance_Call { return &Environment_GetAccountAvailableBalance_Call{Call: _e.mock.On("GetAccountAvailableBalance", address)} } -func (_c *Environment_GetAccountAvailableBalance_Call) Run(run func(address common.Address)) *Environment_GetAccountAvailableBalance_Call { +func (_c *Environment_GetAccountAvailableBalance_Call) Run(run func(address common0.Address)) *Environment_GetAccountAvailableBalance_Call { _c.Call.Run(func(args mock.Arguments) { - var arg0 common.Address + var arg0 common0.Address if args[0] != nil { - arg0 = args[0].(common.Address) + arg0 = args[0].(common0.Address) } run( arg0, @@ -1617,13 +1787,13 @@ func (_c *Environment_GetAccountAvailableBalance_Call) Return(value uint64, err return _c } -func (_c *Environment_GetAccountAvailableBalance_Call) RunAndReturn(run func(address common.Address) (uint64, error)) *Environment_GetAccountAvailableBalance_Call { +func (_c *Environment_GetAccountAvailableBalance_Call) RunAndReturn(run func(address common0.Address) (uint64, error)) *Environment_GetAccountAvailableBalance_Call { _c.Call.Return(run) return _c } // GetAccountBalance provides a mock function for the type Environment -func (_mock *Environment) GetAccountBalance(address common.Address) (uint64, error) { +func (_mock *Environment) GetAccountBalance(address common0.Address) (uint64, error) { ret := _mock.Called(address) if len(ret) == 0 { @@ -1632,15 +1802,15 @@ func (_mock *Environment) GetAccountBalance(address common.Address) (uint64, err var r0 uint64 var r1 error - if returnFunc, ok := ret.Get(0).(func(common.Address) (uint64, error)); ok { + if returnFunc, ok := ret.Get(0).(func(common0.Address) (uint64, error)); ok { return returnFunc(address) } - if returnFunc, ok := ret.Get(0).(func(common.Address) uint64); ok { + if returnFunc, ok := ret.Get(0).(func(common0.Address) uint64); ok { r0 = returnFunc(address) } else { r0 = ret.Get(0).(uint64) } - if returnFunc, ok := ret.Get(1).(func(common.Address) error); ok { + if returnFunc, ok := ret.Get(1).(func(common0.Address) error); ok { r1 = returnFunc(address) } else { r1 = ret.Error(1) @@ -1654,16 +1824,16 @@ type Environment_GetAccountBalance_Call struct { } // GetAccountBalance is a helper method to define mock.On call -// - address common.Address +// - address common0.Address func (_e *Environment_Expecter) GetAccountBalance(address interface{}) *Environment_GetAccountBalance_Call { return &Environment_GetAccountBalance_Call{Call: _e.mock.On("GetAccountBalance", address)} } -func (_c *Environment_GetAccountBalance_Call) Run(run func(address common.Address)) *Environment_GetAccountBalance_Call { +func (_c *Environment_GetAccountBalance_Call) Run(run func(address common0.Address)) *Environment_GetAccountBalance_Call { _c.Call.Run(func(args mock.Arguments) { - var arg0 common.Address + var arg0 common0.Address if args[0] != nil { - arg0 = args[0].(common.Address) + arg0 = args[0].(common0.Address) } run( arg0, @@ -1677,13 +1847,13 @@ func (_c *Environment_GetAccountBalance_Call) Return(value uint64, err error) *E return _c } -func (_c *Environment_GetAccountBalance_Call) RunAndReturn(run func(address common.Address) (uint64, error)) *Environment_GetAccountBalance_Call { +func (_c *Environment_GetAccountBalance_Call) RunAndReturn(run func(address common0.Address) (uint64, error)) *Environment_GetAccountBalance_Call { _c.Call.Return(run) return _c } // GetAccountContractCode provides a mock function for the type Environment -func (_mock *Environment) GetAccountContractCode(location common.AddressLocation) ([]byte, error) { +func (_mock *Environment) GetAccountContractCode(location common0.AddressLocation) ([]byte, error) { ret := _mock.Called(location) if len(ret) == 0 { @@ -1692,17 +1862,17 @@ func (_mock *Environment) GetAccountContractCode(location common.AddressLocation var r0 []byte var r1 error - if returnFunc, ok := ret.Get(0).(func(common.AddressLocation) ([]byte, error)); ok { + if returnFunc, ok := ret.Get(0).(func(common0.AddressLocation) ([]byte, error)); ok { return returnFunc(location) } - if returnFunc, ok := ret.Get(0).(func(common.AddressLocation) []byte); ok { + if returnFunc, ok := ret.Get(0).(func(common0.AddressLocation) []byte); ok { r0 = returnFunc(location) } else { if ret.Get(0) != nil { r0 = ret.Get(0).([]byte) } } - if returnFunc, ok := ret.Get(1).(func(common.AddressLocation) error); ok { + if returnFunc, ok := ret.Get(1).(func(common0.AddressLocation) error); ok { r1 = returnFunc(location) } else { r1 = ret.Error(1) @@ -1716,16 +1886,16 @@ type Environment_GetAccountContractCode_Call struct { } // GetAccountContractCode is a helper method to define mock.On call -// - location common.AddressLocation +// - location common0.AddressLocation func (_e *Environment_Expecter) GetAccountContractCode(location interface{}) *Environment_GetAccountContractCode_Call { return &Environment_GetAccountContractCode_Call{Call: _e.mock.On("GetAccountContractCode", location)} } -func (_c *Environment_GetAccountContractCode_Call) Run(run func(location common.AddressLocation)) *Environment_GetAccountContractCode_Call { +func (_c *Environment_GetAccountContractCode_Call) Run(run func(location common0.AddressLocation)) *Environment_GetAccountContractCode_Call { _c.Call.Run(func(args mock.Arguments) { - var arg0 common.AddressLocation + var arg0 common0.AddressLocation if args[0] != nil { - arg0 = args[0].(common.AddressLocation) + arg0 = args[0].(common0.AddressLocation) } run( arg0, @@ -1739,7 +1909,7 @@ func (_c *Environment_GetAccountContractCode_Call) Return(code []byte, err error return _c } -func (_c *Environment_GetAccountContractCode_Call) RunAndReturn(run func(location common.AddressLocation) ([]byte, error)) *Environment_GetAccountContractCode_Call { +func (_c *Environment_GetAccountContractCode_Call) RunAndReturn(run func(location common0.AddressLocation) ([]byte, error)) *Environment_GetAccountContractCode_Call { _c.Call.Return(run) return _c } @@ -2665,6 +2835,61 @@ func (_c *Environment_IsServiceAccountAuthorizer_Call) RunAndReturn(run func() b return _c } +// LatestBlock provides a mock function for the type Environment +func (_mock *Environment) LatestBlock() (*types.Block, error) { + ret := _mock.Called() + + if len(ret) == 0 { + panic("no return value specified for LatestBlock") + } + + var r0 *types.Block + var r1 error + if returnFunc, ok := ret.Get(0).(func() (*types.Block, error)); ok { + return returnFunc() + } + if returnFunc, ok := ret.Get(0).(func() *types.Block); ok { + r0 = returnFunc() + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*types.Block) + } + } + if returnFunc, ok := ret.Get(1).(func() error); ok { + r1 = returnFunc() + } else { + r1 = ret.Error(1) + } + return r0, r1 +} + +// Environment_LatestBlock_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'LatestBlock' +type Environment_LatestBlock_Call struct { + *mock.Call +} + +// LatestBlock is a helper method to define mock.On call +func (_e *Environment_Expecter) LatestBlock() *Environment_LatestBlock_Call { + return &Environment_LatestBlock_Call{Call: _e.mock.On("LatestBlock")} +} + +func (_c *Environment_LatestBlock_Call) Run(run func()) *Environment_LatestBlock_Call { + _c.Call.Run(func(args mock.Arguments) { + run() + }) + return _c +} + +func (_c *Environment_LatestBlock_Call) Return(block *types.Block, err error) *Environment_LatestBlock_Call { + _c.Call.Return(block, err) + return _c +} + +func (_c *Environment_LatestBlock_Call) RunAndReturn(run func() (*types.Block, error)) *Environment_LatestBlock_Call { + _c.Call.Return(run) + return _c +} + // LimitAccountStorage provides a mock function for the type Environment func (_mock *Environment) LimitAccountStorage() bool { ret := _mock.Called() @@ -2853,7 +3078,7 @@ func (_c *Environment_MemoryUsed_Call) RunAndReturn(run func() (uint64, error)) } // MeterComputation provides a mock function for the type Environment -func (_mock *Environment) MeterComputation(usage common.ComputationUsage) error { +func (_mock *Environment) MeterComputation(usage common0.ComputationUsage) error { ret := _mock.Called(usage) if len(ret) == 0 { @@ -2861,7 +3086,7 @@ func (_mock *Environment) MeterComputation(usage common.ComputationUsage) error } var r0 error - if returnFunc, ok := ret.Get(0).(func(common.ComputationUsage) error); ok { + if returnFunc, ok := ret.Get(0).(func(common0.ComputationUsage) error); ok { r0 = returnFunc(usage) } else { r0 = ret.Error(0) @@ -2875,16 +3100,16 @@ type Environment_MeterComputation_Call struct { } // MeterComputation is a helper method to define mock.On call -// - usage common.ComputationUsage +// - usage common0.ComputationUsage func (_e *Environment_Expecter) MeterComputation(usage interface{}) *Environment_MeterComputation_Call { return &Environment_MeterComputation_Call{Call: _e.mock.On("MeterComputation", usage)} } -func (_c *Environment_MeterComputation_Call) Run(run func(usage common.ComputationUsage)) *Environment_MeterComputation_Call { +func (_c *Environment_MeterComputation_Call) Run(run func(usage common0.ComputationUsage)) *Environment_MeterComputation_Call { _c.Call.Run(func(args mock.Arguments) { - var arg0 common.ComputationUsage + var arg0 common0.ComputationUsage if args[0] != nil { - arg0 = args[0].(common.ComputationUsage) + arg0 = args[0].(common0.ComputationUsage) } run( arg0, @@ -2898,7 +3123,7 @@ func (_c *Environment_MeterComputation_Call) Return(err error) *Environment_Mete return _c } -func (_c *Environment_MeterComputation_Call) RunAndReturn(run func(usage common.ComputationUsage) error) *Environment_MeterComputation_Call { +func (_c *Environment_MeterComputation_Call) RunAndReturn(run func(usage common0.ComputationUsage) error) *Environment_MeterComputation_Call { _c.Call.Return(run) return _c } @@ -2955,7 +3180,7 @@ func (_c *Environment_MeterEmittedEvent_Call) RunAndReturn(run func(byteSize uin } // MeterMemory provides a mock function for the type Environment -func (_mock *Environment) MeterMemory(usage common.MemoryUsage) error { +func (_mock *Environment) MeterMemory(usage common0.MemoryUsage) error { ret := _mock.Called(usage) if len(ret) == 0 { @@ -2963,7 +3188,7 @@ func (_mock *Environment) MeterMemory(usage common.MemoryUsage) error { } var r0 error - if returnFunc, ok := ret.Get(0).(func(common.MemoryUsage) error); ok { + if returnFunc, ok := ret.Get(0).(func(common0.MemoryUsage) error); ok { r0 = returnFunc(usage) } else { r0 = ret.Error(0) @@ -2977,16 +3202,16 @@ type Environment_MeterMemory_Call struct { } // MeterMemory is a helper method to define mock.On call -// - usage common.MemoryUsage +// - usage common0.MemoryUsage func (_e *Environment_Expecter) MeterMemory(usage interface{}) *Environment_MeterMemory_Call { return &Environment_MeterMemory_Call{Call: _e.mock.On("MeterMemory", usage)} } -func (_c *Environment_MeterMemory_Call) Run(run func(usage common.MemoryUsage)) *Environment_MeterMemory_Call { +func (_c *Environment_MeterMemory_Call) Run(run func(usage common0.MemoryUsage)) *Environment_MeterMemory_Call { _c.Call.Run(func(args mock.Arguments) { - var arg0 common.MemoryUsage + var arg0 common0.MemoryUsage if args[0] != nil { - arg0 = args[0].(common.MemoryUsage) + arg0 = args[0].(common0.MemoryUsage) } run( arg0, @@ -3000,7 +3225,7 @@ func (_c *Environment_MeterMemory_Call) Return(err error) *Environment_MeterMemo return _c } -func (_c *Environment_MeterMemory_Call) RunAndReturn(run func(usage common.MemoryUsage) error) *Environment_MeterMemory_Call { +func (_c *Environment_MeterMemory_Call) RunAndReturn(run func(usage common0.MemoryUsage) error) *Environment_MeterMemory_Call { _c.Call.Return(run) return _c } @@ -3268,7 +3493,7 @@ func (_c *Environment_RecordTrace_Call) RunAndReturn(run func(operation string, } // RecoverProgram provides a mock function for the type Environment -func (_mock *Environment) RecoverProgram(program *ast.Program, location common.Location) ([]byte, error) { +func (_mock *Environment) RecoverProgram(program *ast.Program, location common0.Location) ([]byte, error) { ret := _mock.Called(program, location) if len(ret) == 0 { @@ -3277,17 +3502,17 @@ func (_mock *Environment) RecoverProgram(program *ast.Program, location common.L var r0 []byte var r1 error - if returnFunc, ok := ret.Get(0).(func(*ast.Program, common.Location) ([]byte, error)); ok { + if returnFunc, ok := ret.Get(0).(func(*ast.Program, common0.Location) ([]byte, error)); ok { return returnFunc(program, location) } - if returnFunc, ok := ret.Get(0).(func(*ast.Program, common.Location) []byte); ok { + if returnFunc, ok := ret.Get(0).(func(*ast.Program, common0.Location) []byte); ok { r0 = returnFunc(program, location) } else { if ret.Get(0) != nil { r0 = ret.Get(0).([]byte) } } - if returnFunc, ok := ret.Get(1).(func(*ast.Program, common.Location) error); ok { + if returnFunc, ok := ret.Get(1).(func(*ast.Program, common0.Location) error); ok { r1 = returnFunc(program, location) } else { r1 = ret.Error(1) @@ -3302,20 +3527,20 @@ type Environment_RecoverProgram_Call struct { // RecoverProgram is a helper method to define mock.On call // - program *ast.Program -// - location common.Location +// - location common0.Location func (_e *Environment_Expecter) RecoverProgram(program interface{}, location interface{}) *Environment_RecoverProgram_Call { return &Environment_RecoverProgram_Call{Call: _e.mock.On("RecoverProgram", program, location)} } -func (_c *Environment_RecoverProgram_Call) Run(run func(program *ast.Program, location common.Location)) *Environment_RecoverProgram_Call { +func (_c *Environment_RecoverProgram_Call) Run(run func(program *ast.Program, location common0.Location)) *Environment_RecoverProgram_Call { _c.Call.Run(func(args mock.Arguments) { var arg0 *ast.Program if args[0] != nil { arg0 = args[0].(*ast.Program) } - var arg1 common.Location + var arg1 common0.Location if args[1] != nil { - arg1 = args[1].(common.Location) + arg1 = args[1].(common0.Location) } run( arg0, @@ -3330,13 +3555,13 @@ func (_c *Environment_RecoverProgram_Call) Return(bytes []byte, err error) *Envi return _c } -func (_c *Environment_RecoverProgram_Call) RunAndReturn(run func(program *ast.Program, location common.Location) ([]byte, error)) *Environment_RecoverProgram_Call { +func (_c *Environment_RecoverProgram_Call) RunAndReturn(run func(program *ast.Program, location common0.Location) ([]byte, error)) *Environment_RecoverProgram_Call { _c.Call.Return(run) return _c } // RemoveAccountContractCode provides a mock function for the type Environment -func (_mock *Environment) RemoveAccountContractCode(location common.AddressLocation) error { +func (_mock *Environment) RemoveAccountContractCode(location common0.AddressLocation) error { ret := _mock.Called(location) if len(ret) == 0 { @@ -3344,7 +3569,7 @@ func (_mock *Environment) RemoveAccountContractCode(location common.AddressLocat } var r0 error - if returnFunc, ok := ret.Get(0).(func(common.AddressLocation) error); ok { + if returnFunc, ok := ret.Get(0).(func(common0.AddressLocation) error); ok { r0 = returnFunc(location) } else { r0 = ret.Error(0) @@ -3358,16 +3583,16 @@ type Environment_RemoveAccountContractCode_Call struct { } // RemoveAccountContractCode is a helper method to define mock.On call -// - location common.AddressLocation +// - location common0.AddressLocation func (_e *Environment_Expecter) RemoveAccountContractCode(location interface{}) *Environment_RemoveAccountContractCode_Call { return &Environment_RemoveAccountContractCode_Call{Call: _e.mock.On("RemoveAccountContractCode", location)} } -func (_c *Environment_RemoveAccountContractCode_Call) Run(run func(location common.AddressLocation)) *Environment_RemoveAccountContractCode_Call { +func (_c *Environment_RemoveAccountContractCode_Call) Run(run func(location common0.AddressLocation)) *Environment_RemoveAccountContractCode_Call { _c.Call.Run(func(args mock.Arguments) { - var arg0 common.AddressLocation + var arg0 common0.AddressLocation if args[0] != nil { - arg0 = args[0].(common.AddressLocation) + arg0 = args[0].(common0.AddressLocation) } run( arg0, @@ -3381,7 +3606,7 @@ func (_c *Environment_RemoveAccountContractCode_Call) Return(err error) *Environ return _c } -func (_c *Environment_RemoveAccountContractCode_Call) RunAndReturn(run func(location common.AddressLocation) error) *Environment_RemoveAccountContractCode_Call { +func (_c *Environment_RemoveAccountContractCode_Call) RunAndReturn(run func(location common0.AddressLocation) error) *Environment_RemoveAccountContractCode_Call { _c.Call.Return(run) return _c } @@ -3488,7 +3713,7 @@ func (_c *Environment_ResolveLocation_Call) RunAndReturn(run func(identifiers [] } // ResourceOwnerChanged provides a mock function for the type Environment -func (_mock *Environment) ResourceOwnerChanged(interpreter1 *interpreter.Interpreter, resource *interpreter.CompositeValue, oldOwner common.Address, newOwner common.Address) { +func (_mock *Environment) ResourceOwnerChanged(interpreter1 *interpreter.Interpreter, resource *interpreter.CompositeValue, oldOwner common0.Address, newOwner common0.Address) { _mock.Called(interpreter1, resource, oldOwner, newOwner) return } @@ -3501,13 +3726,13 @@ type Environment_ResourceOwnerChanged_Call struct { // ResourceOwnerChanged is a helper method to define mock.On call // - interpreter1 *interpreter.Interpreter // - resource *interpreter.CompositeValue -// - oldOwner common.Address -// - newOwner common.Address +// - oldOwner common0.Address +// - newOwner common0.Address func (_e *Environment_Expecter) ResourceOwnerChanged(interpreter1 interface{}, resource interface{}, oldOwner interface{}, newOwner interface{}) *Environment_ResourceOwnerChanged_Call { return &Environment_ResourceOwnerChanged_Call{Call: _e.mock.On("ResourceOwnerChanged", interpreter1, resource, oldOwner, newOwner)} } -func (_c *Environment_ResourceOwnerChanged_Call) Run(run func(interpreter1 *interpreter.Interpreter, resource *interpreter.CompositeValue, oldOwner common.Address, newOwner common.Address)) *Environment_ResourceOwnerChanged_Call { +func (_c *Environment_ResourceOwnerChanged_Call) Run(run func(interpreter1 *interpreter.Interpreter, resource *interpreter.CompositeValue, oldOwner common0.Address, newOwner common0.Address)) *Environment_ResourceOwnerChanged_Call { _c.Call.Run(func(args mock.Arguments) { var arg0 *interpreter.Interpreter if args[0] != nil { @@ -3517,13 +3742,13 @@ func (_c *Environment_ResourceOwnerChanged_Call) Run(run func(interpreter1 *inte if args[1] != nil { arg1 = args[1].(*interpreter.CompositeValue) } - var arg2 common.Address + var arg2 common0.Address if args[2] != nil { - arg2 = args[2].(common.Address) + arg2 = args[2].(common0.Address) } - var arg3 common.Address + var arg3 common0.Address if args[3] != nil { - arg3 = args[3].(common.Address) + arg3 = args[3].(common0.Address) } run( arg0, @@ -3540,7 +3765,7 @@ func (_c *Environment_ResourceOwnerChanged_Call) Return() *Environment_ResourceO return _c } -func (_c *Environment_ResourceOwnerChanged_Call) RunAndReturn(run func(interpreter1 *interpreter.Interpreter, resource *interpreter.CompositeValue, oldOwner common.Address, newOwner common.Address)) *Environment_ResourceOwnerChanged_Call { +func (_c *Environment_ResourceOwnerChanged_Call) RunAndReturn(run func(interpreter1 *interpreter.Interpreter, resource *interpreter.CompositeValue, oldOwner common0.Address, newOwner common0.Address)) *Environment_ResourceOwnerChanged_Call { _c.Run(run) return _c } @@ -4068,6 +4293,46 @@ func (_c *Environment_SetValue_Call) RunAndReturn(run func(owner []byte, key []b return _c } +// StageBlockProposal provides a mock function for the type Environment +func (_mock *Environment) StageBlockProposal(blockProposal *types.BlockProposal) { + _mock.Called(blockProposal) + return +} + +// Environment_StageBlockProposal_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'StageBlockProposal' +type Environment_StageBlockProposal_Call struct { + *mock.Call +} + +// StageBlockProposal is a helper method to define mock.On call +// - blockProposal *types.BlockProposal +func (_e *Environment_Expecter) StageBlockProposal(blockProposal interface{}) *Environment_StageBlockProposal_Call { + return &Environment_StageBlockProposal_Call{Call: _e.mock.On("StageBlockProposal", blockProposal)} +} + +func (_c *Environment_StageBlockProposal_Call) Run(run func(blockProposal *types.BlockProposal)) *Environment_StageBlockProposal_Call { + _c.Call.Run(func(args mock.Arguments) { + var arg0 *types.BlockProposal + if args[0] != nil { + arg0 = args[0].(*types.BlockProposal) + } + run( + arg0, + ) + }) + return _c +} + +func (_c *Environment_StageBlockProposal_Call) Return() *Environment_StageBlockProposal_Call { + _c.Call.Return() + return _c +} + +func (_c *Environment_StageBlockProposal_Call) RunAndReturn(run func(blockProposal *types.BlockProposal)) *Environment_StageBlockProposal_Call { + _c.Run(run) + return _c +} + // StartChildSpan provides a mock function for the type Environment func (_mock *Environment) StartChildSpan(name trace.SpanName, options ...trace0.SpanStartOption) tracing.TracerSpan { // trace0.SpanStartOption @@ -4317,7 +4582,7 @@ func (_c *Environment_TxIndex_Call) RunAndReturn(run func() uint32) *Environment } // UpdateAccountContractCode provides a mock function for the type Environment -func (_mock *Environment) UpdateAccountContractCode(location common.AddressLocation, code []byte) error { +func (_mock *Environment) UpdateAccountContractCode(location common0.AddressLocation, code []byte) error { ret := _mock.Called(location, code) if len(ret) == 0 { @@ -4325,7 +4590,7 @@ func (_mock *Environment) UpdateAccountContractCode(location common.AddressLocat } var r0 error - if returnFunc, ok := ret.Get(0).(func(common.AddressLocation, []byte) error); ok { + if returnFunc, ok := ret.Get(0).(func(common0.AddressLocation, []byte) error); ok { r0 = returnFunc(location, code) } else { r0 = ret.Error(0) @@ -4339,17 +4604,17 @@ type Environment_UpdateAccountContractCode_Call struct { } // UpdateAccountContractCode is a helper method to define mock.On call -// - location common.AddressLocation +// - location common0.AddressLocation // - code []byte func (_e *Environment_Expecter) UpdateAccountContractCode(location interface{}, code interface{}) *Environment_UpdateAccountContractCode_Call { return &Environment_UpdateAccountContractCode_Call{Call: _e.mock.On("UpdateAccountContractCode", location, code)} } -func (_c *Environment_UpdateAccountContractCode_Call) Run(run func(location common.AddressLocation, code []byte)) *Environment_UpdateAccountContractCode_Call { +func (_c *Environment_UpdateAccountContractCode_Call) Run(run func(location common0.AddressLocation, code []byte)) *Environment_UpdateAccountContractCode_Call { _c.Call.Run(func(args mock.Arguments) { - var arg0 common.AddressLocation + var arg0 common0.AddressLocation if args[0] != nil { - arg0 = args[0].(common.AddressLocation) + arg0 = args[0].(common0.AddressLocation) } var arg1 []byte if args[1] != nil { @@ -4368,7 +4633,7 @@ func (_c *Environment_UpdateAccountContractCode_Call) Return(err error) *Environ return _c } -func (_c *Environment_UpdateAccountContractCode_Call) RunAndReturn(run func(location common.AddressLocation, code []byte) error) *Environment_UpdateAccountContractCode_Call { +func (_c *Environment_UpdateAccountContractCode_Call) RunAndReturn(run func(location common0.AddressLocation, code []byte) error) *Environment_UpdateAccountContractCode_Call { _c.Call.Return(run) return _c } diff --git a/fvm/environment/mock/evm_block_store.go b/fvm/environment/mock/evm_block_store.go new file mode 100644 index 00000000000..0c59382f5ea --- /dev/null +++ b/fvm/environment/mock/evm_block_store.go @@ -0,0 +1,301 @@ +// Code generated by mockery; DO NOT EDIT. +// github.com/vektra/mockery +// template: testify + +package mock + +import ( + "github.com/ethereum/go-ethereum/common" + "github.com/onflow/flow-go/fvm/evm/types" + mock "github.com/stretchr/testify/mock" +) + +// NewEVMBlockStore creates a new instance of EVMBlockStore. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +// The first argument is typically a *testing.T value. +func NewEVMBlockStore(t interface { + mock.TestingT + Cleanup(func()) +}) *EVMBlockStore { + mock := &EVMBlockStore{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} + +// EVMBlockStore is an autogenerated mock type for the EVMBlockStore type +type EVMBlockStore struct { + mock.Mock +} + +type EVMBlockStore_Expecter struct { + mock *mock.Mock +} + +func (_m *EVMBlockStore) EXPECT() *EVMBlockStore_Expecter { + return &EVMBlockStore_Expecter{mock: &_m.Mock} +} + +// BlockHash provides a mock function for the type EVMBlockStore +func (_mock *EVMBlockStore) BlockHash(height uint64) (common.Hash, error) { + ret := _mock.Called(height) + + if len(ret) == 0 { + panic("no return value specified for BlockHash") + } + + var r0 common.Hash + var r1 error + if returnFunc, ok := ret.Get(0).(func(uint64) (common.Hash, error)); ok { + return returnFunc(height) + } + if returnFunc, ok := ret.Get(0).(func(uint64) common.Hash); ok { + r0 = returnFunc(height) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(common.Hash) + } + } + if returnFunc, ok := ret.Get(1).(func(uint64) error); ok { + r1 = returnFunc(height) + } else { + r1 = ret.Error(1) + } + return r0, r1 +} + +// EVMBlockStore_BlockHash_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'BlockHash' +type EVMBlockStore_BlockHash_Call struct { + *mock.Call +} + +// BlockHash is a helper method to define mock.On call +// - height uint64 +func (_e *EVMBlockStore_Expecter) BlockHash(height interface{}) *EVMBlockStore_BlockHash_Call { + return &EVMBlockStore_BlockHash_Call{Call: _e.mock.On("BlockHash", height)} +} + +func (_c *EVMBlockStore_BlockHash_Call) Run(run func(height uint64)) *EVMBlockStore_BlockHash_Call { + _c.Call.Run(func(args mock.Arguments) { + var arg0 uint64 + if args[0] != nil { + arg0 = args[0].(uint64) + } + run( + arg0, + ) + }) + return _c +} + +func (_c *EVMBlockStore_BlockHash_Call) Return(hash common.Hash, err error) *EVMBlockStore_BlockHash_Call { + _c.Call.Return(hash, err) + return _c +} + +func (_c *EVMBlockStore_BlockHash_Call) RunAndReturn(run func(height uint64) (common.Hash, error)) *EVMBlockStore_BlockHash_Call { + _c.Call.Return(run) + return _c +} + +// BlockProposal provides a mock function for the type EVMBlockStore +func (_mock *EVMBlockStore) BlockProposal() (*types.BlockProposal, error) { + ret := _mock.Called() + + if len(ret) == 0 { + panic("no return value specified for BlockProposal") + } + + var r0 *types.BlockProposal + var r1 error + if returnFunc, ok := ret.Get(0).(func() (*types.BlockProposal, error)); ok { + return returnFunc() + } + if returnFunc, ok := ret.Get(0).(func() *types.BlockProposal); ok { + r0 = returnFunc() + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*types.BlockProposal) + } + } + if returnFunc, ok := ret.Get(1).(func() error); ok { + r1 = returnFunc() + } else { + r1 = ret.Error(1) + } + return r0, r1 +} + +// EVMBlockStore_BlockProposal_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'BlockProposal' +type EVMBlockStore_BlockProposal_Call struct { + *mock.Call +} + +// BlockProposal is a helper method to define mock.On call +func (_e *EVMBlockStore_Expecter) BlockProposal() *EVMBlockStore_BlockProposal_Call { + return &EVMBlockStore_BlockProposal_Call{Call: _e.mock.On("BlockProposal")} +} + +func (_c *EVMBlockStore_BlockProposal_Call) Run(run func()) *EVMBlockStore_BlockProposal_Call { + _c.Call.Run(func(args mock.Arguments) { + run() + }) + return _c +} + +func (_c *EVMBlockStore_BlockProposal_Call) Return(blockProposal *types.BlockProposal, err error) *EVMBlockStore_BlockProposal_Call { + _c.Call.Return(blockProposal, err) + return _c +} + +func (_c *EVMBlockStore_BlockProposal_Call) RunAndReturn(run func() (*types.BlockProposal, error)) *EVMBlockStore_BlockProposal_Call { + _c.Call.Return(run) + return _c +} + +// CommitBlockProposal provides a mock function for the type EVMBlockStore +func (_mock *EVMBlockStore) CommitBlockProposal(blockProposal *types.BlockProposal) error { + ret := _mock.Called(blockProposal) + + if len(ret) == 0 { + panic("no return value specified for CommitBlockProposal") + } + + var r0 error + if returnFunc, ok := ret.Get(0).(func(*types.BlockProposal) error); ok { + r0 = returnFunc(blockProposal) + } else { + r0 = ret.Error(0) + } + return r0 +} + +// EVMBlockStore_CommitBlockProposal_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'CommitBlockProposal' +type EVMBlockStore_CommitBlockProposal_Call struct { + *mock.Call +} + +// CommitBlockProposal is a helper method to define mock.On call +// - blockProposal *types.BlockProposal +func (_e *EVMBlockStore_Expecter) CommitBlockProposal(blockProposal interface{}) *EVMBlockStore_CommitBlockProposal_Call { + return &EVMBlockStore_CommitBlockProposal_Call{Call: _e.mock.On("CommitBlockProposal", blockProposal)} +} + +func (_c *EVMBlockStore_CommitBlockProposal_Call) Run(run func(blockProposal *types.BlockProposal)) *EVMBlockStore_CommitBlockProposal_Call { + _c.Call.Run(func(args mock.Arguments) { + var arg0 *types.BlockProposal + if args[0] != nil { + arg0 = args[0].(*types.BlockProposal) + } + run( + arg0, + ) + }) + return _c +} + +func (_c *EVMBlockStore_CommitBlockProposal_Call) Return(err error) *EVMBlockStore_CommitBlockProposal_Call { + _c.Call.Return(err) + return _c +} + +func (_c *EVMBlockStore_CommitBlockProposal_Call) RunAndReturn(run func(blockProposal *types.BlockProposal) error) *EVMBlockStore_CommitBlockProposal_Call { + _c.Call.Return(run) + return _c +} + +// LatestBlock provides a mock function for the type EVMBlockStore +func (_mock *EVMBlockStore) LatestBlock() (*types.Block, error) { + ret := _mock.Called() + + if len(ret) == 0 { + panic("no return value specified for LatestBlock") + } + + var r0 *types.Block + var r1 error + if returnFunc, ok := ret.Get(0).(func() (*types.Block, error)); ok { + return returnFunc() + } + if returnFunc, ok := ret.Get(0).(func() *types.Block); ok { + r0 = returnFunc() + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*types.Block) + } + } + if returnFunc, ok := ret.Get(1).(func() error); ok { + r1 = returnFunc() + } else { + r1 = ret.Error(1) + } + return r0, r1 +} + +// EVMBlockStore_LatestBlock_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'LatestBlock' +type EVMBlockStore_LatestBlock_Call struct { + *mock.Call +} + +// LatestBlock is a helper method to define mock.On call +func (_e *EVMBlockStore_Expecter) LatestBlock() *EVMBlockStore_LatestBlock_Call { + return &EVMBlockStore_LatestBlock_Call{Call: _e.mock.On("LatestBlock")} +} + +func (_c *EVMBlockStore_LatestBlock_Call) Run(run func()) *EVMBlockStore_LatestBlock_Call { + _c.Call.Run(func(args mock.Arguments) { + run() + }) + return _c +} + +func (_c *EVMBlockStore_LatestBlock_Call) Return(block *types.Block, err error) *EVMBlockStore_LatestBlock_Call { + _c.Call.Return(block, err) + return _c +} + +func (_c *EVMBlockStore_LatestBlock_Call) RunAndReturn(run func() (*types.Block, error)) *EVMBlockStore_LatestBlock_Call { + _c.Call.Return(run) + return _c +} + +// StageBlockProposal provides a mock function for the type EVMBlockStore +func (_mock *EVMBlockStore) StageBlockProposal(blockProposal *types.BlockProposal) { + _mock.Called(blockProposal) + return +} + +// EVMBlockStore_StageBlockProposal_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'StageBlockProposal' +type EVMBlockStore_StageBlockProposal_Call struct { + *mock.Call +} + +// StageBlockProposal is a helper method to define mock.On call +// - blockProposal *types.BlockProposal +func (_e *EVMBlockStore_Expecter) StageBlockProposal(blockProposal interface{}) *EVMBlockStore_StageBlockProposal_Call { + return &EVMBlockStore_StageBlockProposal_Call{Call: _e.mock.On("StageBlockProposal", blockProposal)} +} + +func (_c *EVMBlockStore_StageBlockProposal_Call) Run(run func(blockProposal *types.BlockProposal)) *EVMBlockStore_StageBlockProposal_Call { + _c.Call.Run(func(args mock.Arguments) { + var arg0 *types.BlockProposal + if args[0] != nil { + arg0 = args[0].(*types.BlockProposal) + } + run( + arg0, + ) + }) + return _c +} + +func (_c *EVMBlockStore_StageBlockProposal_Call) Return() *EVMBlockStore_StageBlockProposal_Call { + _c.Call.Return() + return _c +} + +func (_c *EVMBlockStore_StageBlockProposal_Call) RunAndReturn(run func(blockProposal *types.BlockProposal)) *EVMBlockStore_StageBlockProposal_Call { + _c.Run(run) + return _c +} diff --git a/fvm/environment/mock/reusable_cadence_runtime_interface.go b/fvm/environment/mock/reusable_cadence_runtime_interface.go deleted file mode 100644 index a1de2d9c6d4..00000000000 --- a/fvm/environment/mock/reusable_cadence_runtime_interface.go +++ /dev/null @@ -1,190 +0,0 @@ -// Code generated by mockery. DO NOT EDIT. - -package mock - -import ( - cadence "github.com/onflow/cadence" - common "github.com/onflow/cadence/common" - - environment "github.com/onflow/flow-go/fvm/environment" - - mock "github.com/stretchr/testify/mock" - - runtime "github.com/onflow/cadence/runtime" - - sema "github.com/onflow/cadence/sema" -) - -// ReusableCadenceRuntimeInterface is an autogenerated mock type for the ReusableCadenceRuntimeInterface type -type ReusableCadenceRuntimeInterface struct { - mock.Mock -} - -// CadenceScriptEnv provides a mock function with no fields -func (_m *ReusableCadenceRuntimeInterface) CadenceScriptEnv() runtime.Environment { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for CadenceScriptEnv") - } - - var r0 runtime.Environment - if rf, ok := ret.Get(0).(func() runtime.Environment); ok { - r0 = rf() - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(runtime.Environment) - } - } - - return r0 -} - -// CadenceTXEnv provides a mock function with no fields -func (_m *ReusableCadenceRuntimeInterface) CadenceTXEnv() runtime.Environment { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for CadenceTXEnv") - } - - var r0 runtime.Environment - if rf, ok := ret.Get(0).(func() runtime.Environment); ok { - r0 = rf() - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(runtime.Environment) - } - } - - return r0 -} - -// ExecuteScript provides a mock function with given fields: script, location -func (_m *ReusableCadenceRuntimeInterface) ExecuteScript(script runtime.Script, location common.Location) (cadence.Value, error) { - ret := _m.Called(script, location) - - if len(ret) == 0 { - panic("no return value specified for ExecuteScript") - } - - var r0 cadence.Value - var r1 error - if rf, ok := ret.Get(0).(func(runtime.Script, common.Location) (cadence.Value, error)); ok { - return rf(script, location) - } - if rf, ok := ret.Get(0).(func(runtime.Script, common.Location) cadence.Value); ok { - r0 = rf(script, location) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(cadence.Value) - } - } - - if rf, ok := ret.Get(1).(func(runtime.Script, common.Location) error); ok { - r1 = rf(script, location) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// InvokeContractFunction provides a mock function with given fields: contractLocation, functionName, arguments, argumentTypes -func (_m *ReusableCadenceRuntimeInterface) InvokeContractFunction(contractLocation common.AddressLocation, functionName string, arguments []cadence.Value, argumentTypes []sema.Type) (cadence.Value, error) { - ret := _m.Called(contractLocation, functionName, arguments, argumentTypes) - - if len(ret) == 0 { - panic("no return value specified for InvokeContractFunction") - } - - var r0 cadence.Value - var r1 error - if rf, ok := ret.Get(0).(func(common.AddressLocation, string, []cadence.Value, []sema.Type) (cadence.Value, error)); ok { - return rf(contractLocation, functionName, arguments, argumentTypes) - } - if rf, ok := ret.Get(0).(func(common.AddressLocation, string, []cadence.Value, []sema.Type) cadence.Value); ok { - r0 = rf(contractLocation, functionName, arguments, argumentTypes) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(cadence.Value) - } - } - - if rf, ok := ret.Get(1).(func(common.AddressLocation, string, []cadence.Value, []sema.Type) error); ok { - r1 = rf(contractLocation, functionName, arguments, argumentTypes) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// NewTransactionExecutor provides a mock function with given fields: script, location -func (_m *ReusableCadenceRuntimeInterface) NewTransactionExecutor(script runtime.Script, location common.Location) runtime.Executor { - ret := _m.Called(script, location) - - if len(ret) == 0 { - panic("no return value specified for NewTransactionExecutor") - } - - var r0 runtime.Executor - if rf, ok := ret.Get(0).(func(runtime.Script, common.Location) runtime.Executor); ok { - r0 = rf(script, location) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(runtime.Executor) - } - } - - return r0 -} - -// ReadStored provides a mock function with given fields: address, path -func (_m *ReusableCadenceRuntimeInterface) ReadStored(address common.Address, path cadence.Path) (cadence.Value, error) { - ret := _m.Called(address, path) - - if len(ret) == 0 { - panic("no return value specified for ReadStored") - } - - var r0 cadence.Value - var r1 error - if rf, ok := ret.Get(0).(func(common.Address, cadence.Path) (cadence.Value, error)); ok { - return rf(address, path) - } - if rf, ok := ret.Get(0).(func(common.Address, cadence.Path) cadence.Value); ok { - r0 = rf(address, path) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(cadence.Value) - } - } - - if rf, ok := ret.Get(1).(func(common.Address, cadence.Path) error); ok { - r1 = rf(address, path) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// SetFvmEnvironment provides a mock function with given fields: fvmEnv -func (_m *ReusableCadenceRuntimeInterface) SetFvmEnvironment(fvmEnv environment.Environment) { - _m.Called(fvmEnv) -} - -// NewReusableCadenceRuntimeInterface creates a new instance of ReusableCadenceRuntimeInterface. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. -// The first argument is typically a *testing.T value. -func NewReusableCadenceRuntimeInterface(t interface { - mock.TestingT - Cleanup(func()) -}) *ReusableCadenceRuntimeInterface { - mock := &ReusableCadenceRuntimeInterface{} - mock.Mock.Test(t) - - t.Cleanup(func() { mock.AssertExpectations(t) }) - - return mock -} diff --git a/fvm/evm/types/backend.go b/fvm/evm/backends/backend.go similarity index 93% rename from fvm/evm/types/backend.go rename to fvm/evm/backends/backend.go index 0a60d109324..cc538ea3d6e 100644 --- a/fvm/evm/types/backend.go +++ b/fvm/evm/backends/backend.go @@ -1,4 +1,4 @@ -package types +package backends import ( "github.com/onflow/flow-go/fvm/environment" @@ -23,5 +23,6 @@ type Backend interface { environment.Tracer environment.EVMMetricsReporter environment.LoggerProvider + environment.EVMBlockStore EVMTestOperationsAllowed() bool } diff --git a/fvm/evm/backends/wrappedEnv.go b/fvm/evm/backends/wrappedEnv.go index d63ed8b2f17..ff4a491a931 100644 --- a/fvm/evm/backends/wrappedEnv.go +++ b/fvm/evm/backends/wrappedEnv.go @@ -1,6 +1,7 @@ package backends import ( + gocommon "github.com/ethereum/go-ethereum/common" "github.com/onflow/atree" "github.com/onflow/cadence" "github.com/onflow/cadence/common" @@ -28,7 +29,7 @@ func NewWrappedEnvironment(env environment.Environment) *WrappedEnvironment { return &WrappedEnvironment{env} } -var _ types.Backend = &WrappedEnvironment{} +var _ Backend = &WrappedEnvironment{} // GetValue gets a value from the storage for the given owner and key pair, // if value not found empty slice and no error is returned. @@ -225,3 +226,32 @@ func handleEnvironmentError(err error) error { return types.NewBackendError(err) } + +// BlockHash implements [Backend]. +func (we *WrappedEnvironment) BlockHash(height uint64) (gocommon.Hash, error) { + hash, err := we.env.BlockHash(height) + return hash, handleEnvironmentError(err) +} + +// BlockProposal implements [Backend]. +func (we *WrappedEnvironment) BlockProposal() (*types.BlockProposal, error) { + bp, err := we.env.BlockProposal() + return bp, handleEnvironmentError(err) +} + +// CommitBlockProposal implements [Backend]. +func (we *WrappedEnvironment) CommitBlockProposal(bp *types.BlockProposal) error { + err := we.env.CommitBlockProposal(bp) + return handleEnvironmentError(err) +} + +// LatestBlock implements [Backend]. +func (we *WrappedEnvironment) LatestBlock() (*types.Block, error) { + block, err := we.env.LatestBlock() + return block, handleEnvironmentError(err) +} + +// StageBlockProposal implements [Backend]. +func (we *WrappedEnvironment) StageBlockProposal(bp *types.BlockProposal) { + we.env.StageBlockProposal(bp) +} diff --git a/fvm/evm/emulator/emulator_test.go b/fvm/evm/emulator/emulator_test.go index 9631c821b5d..78f3d73c65e 100644 --- a/fvm/evm/emulator/emulator_test.go +++ b/fvm/evm/emulator/emulator_test.go @@ -50,7 +50,7 @@ func requireSuccessfulExecution(t testing.TB, err error, res *types.Result) { } func TestNativeTokenBridging(t *testing.T) { - testutils.RunWithTestBackend(t, func(backend *testutils.TestBackend) { + testutils.RunWithTestBackend(t, flow.Testnet, func(backend *testutils.TestBackend) { testutils.RunWithTestFlowEVMRootAddress(t, backend, func(rootAddr flow.Address) { originalBalance := types.MakeBigIntInFlow(3) testAccount := types.NewAddressFromString("test") @@ -245,7 +245,7 @@ func TestNativeTokenBridging(t *testing.T) { func TestContractInteraction(t *testing.T) { t.Parallel() - testutils.RunWithTestBackend(t, func(backend *testutils.TestBackend) { + testutils.RunWithTestBackend(t, flow.Testnet, func(backend *testutils.TestBackend) { testutils.RunWithTestFlowEVMRootAddress(t, backend, func(rootAddr flow.Address) { testContract := testutils.GetStorageTestContract(t) @@ -635,7 +635,7 @@ func TestContractInteraction(t *testing.T) { } func TestDeployAtFunctionality(t *testing.T) { - testutils.RunWithTestBackend(t, func(backend *testutils.TestBackend) { + testutils.RunWithTestBackend(t, flow.Testnet, func(backend *testutils.TestBackend) { testutils.RunWithTestFlowEVMRootAddress(t, backend, func(rootAddr flow.Address) { testContract := testutils.GetStorageTestContract(t) testAccount := types.NewAddressFromString("test") @@ -724,7 +724,7 @@ func TestDeployAtFunctionality(t *testing.T) { // EIP 6780 https://eips.ethereum.org/EIPS/eip-6780 in case where the selfdestruct // is not called in the same transaction as deployment. func TestSelfdestruct(t *testing.T) { - testutils.RunWithTestBackend(t, func(backend *testutils.TestBackend) { + testutils.RunWithTestBackend(t, flow.Testnet, func(backend *testutils.TestBackend) { testutils.RunWithTestFlowEVMRootAddress(t, backend, func(rootAddr flow.Address) { testutils.RunWithEOATestAccount(t, backend, rootAddr, func(testAccount *testutils.EOATestAccount) { @@ -808,7 +808,7 @@ func TestSelfdestruct(t *testing.T) { // test factory patterns func TestFactoryPatterns(t *testing.T) { - testutils.RunWithTestBackend(t, func(backend *testutils.TestBackend) { + testutils.RunWithTestBackend(t, flow.Testnet, func(backend *testutils.TestBackend) { testutils.RunWithTestFlowEVMRootAddress(t, backend, func(rootAddr flow.Address) { var factoryAddress types.Address @@ -1067,7 +1067,7 @@ func TestFactoryPatterns(t *testing.T) { } func TestTransfers(t *testing.T) { - testutils.RunWithTestBackend(t, func(backend *testutils.TestBackend) { + testutils.RunWithTestBackend(t, flow.Testnet, func(backend *testutils.TestBackend) { testutils.RunWithTestFlowEVMRootAddress(t, backend, func(rootAddr flow.Address) { testAccount1 := types.NewAddressFromString("test1") @@ -1107,7 +1107,7 @@ func TestTransfers(t *testing.T) { } func TestStorageNoSideEffect(t *testing.T) { - testutils.RunWithTestBackend(t, func(backend *testutils.TestBackend) { + testutils.RunWithTestBackend(t, flow.Testnet, func(backend *testutils.TestBackend) { testutils.RunWithTestFlowEVMRootAddress(t, backend, func(flowEVMRoot flow.Address) { var err error em := emulator.NewEmulator(backend, flowEVMRoot) @@ -1131,7 +1131,7 @@ func TestStorageNoSideEffect(t *testing.T) { } func TestCallingExtraPrecompiles(t *testing.T) { - testutils.RunWithTestBackend(t, func(backend *testutils.TestBackend) { + testutils.RunWithTestBackend(t, flow.Testnet, func(backend *testutils.TestBackend) { testutils.RunWithTestFlowEVMRootAddress(t, backend, func(flowEVMRoot flow.Address) { RunWithNewEmulator(t, backend, flowEVMRoot, func(em *emulator.Emulator) { @@ -1198,7 +1198,7 @@ func TestCallingExtraPrecompiles(t *testing.T) { } func TestTxIndex(t *testing.T) { - testutils.RunWithTestBackend(t, func(backend *testutils.TestBackend) { + testutils.RunWithTestBackend(t, flow.Testnet, func(backend *testutils.TestBackend) { testutils.RunWithTestFlowEVMRootAddress(t, backend, func(rootAddr flow.Address) { RunWithNewEmulator(t, backend, rootAddr, func(em *emulator.Emulator) { ctx := types.NewDefaultBlockContext(blockNumber.Uint64()) diff --git a/fvm/evm/evm_test.go b/fvm/evm/evm_test.go index fbde098c088..f7f89778bdd 100644 --- a/fvm/evm/evm_test.go +++ b/fvm/evm/evm_test.go @@ -170,7 +170,7 @@ func TestEVMRun(t *testing.T) { assert(res.status == EVM.Status.successful, message: "unexpected status") assert(res.errorCode == 0, message: "unexpected error code") - + return res } `, @@ -1465,7 +1465,7 @@ func TestEVMBatchRun(t *testing.T) { prepare(account: &Account) { let coinbase = EVM.EVMAddress(bytes: coinbaseBytes) let batchResults = EVM.batchRun(txs: txs, coinbase: coinbase) - + assert(batchResults.length == txs.length, message: "invalid result length") for res in batchResults { assert(res.status == EVM.Status.successful, message: "unexpected status") @@ -1657,7 +1657,7 @@ func TestEVMBatchRun(t *testing.T) { prepare(account: &Account) { let coinbase = EVM.EVMAddress(bytes: coinbaseBytes) let batchResults = EVM.batchRun(txs: txs, coinbase: coinbase) - + assert(batchResults.length == txs.length, message: "invalid result length") for i, res in batchResults { if i != %d { @@ -2826,7 +2826,7 @@ func TestCadenceOwnedAccountFunctionalities(t *testing.T) { ` import EVM from %s import FlowToken from %s - + access(all) fun main(code: [UInt8]): EVM.Result { let admin = getAuthAccount(%s) @@ -2834,10 +2834,10 @@ func TestCadenceOwnedAccountFunctionalities(t *testing.T) { let minter <- admin.createNewMinter(allowedAmount: 2.34) let vault <- minter.mintTokens(amount: 2.34) destroy minter - + let cadenceOwnedAccount <- EVM.createCadenceOwnedAccount() cadenceOwnedAccount.deposit(from: <-vault) - + let res = cadenceOwnedAccount.deploy( code: code, gasLimit: 2_000_000, @@ -3324,7 +3324,7 @@ func TestDryRun(t *testing.T) { access(all) fun main(tx: [UInt8]): EVM.Result { return EVM.dryRun( - tx: tx, + tx: tx, from: EVM.EVMAddress(bytes: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19]) ) }`, @@ -4019,6 +4019,91 @@ func TestDryRun(t *testing.T) { }, ) }) + + // Regression test: dryRun reads the block proposal into the cache. A subsequent + // EVM.run in the same Cadence transaction must still see a clean proposal (not + // one tainted by the dry-run read) and complete successfully. + t.Run("test EVM.dryRun followed by EVM.run in same transaction", func(t *testing.T) { + RunWithNewEnvironment(t, + chain, func( + ctx fvm.Context, + vm fvm.VM, + snapshot snapshot.SnapshotTree, + testContract *TestContract, + testAccount *EOATestAccount, + ) { + sc := systemcontracts.SystemContractsForChain(chain.ChainID()) + + data := testContract.MakeCallData(t, "store", big.NewInt(42)) + + // The dry-run tx uses nonce 0 (doesn't consume it). + dryTx := gethTypes.NewTransaction( + testAccount.Nonce(), + testContract.DeployedAt.ToCommon(), + big.NewInt(0), + uint64(50_000), + big.NewInt(0), + data, + ) + dryTxBytes, err := dryTx.MarshalBinary() + require.NoError(t, err) + + // The real tx is signed and uses the same nonce. + realTxBytes := testAccount.PrepareSignAndEncodeTx(t, + testContract.DeployedAt.ToCommon(), + data, + big.NewInt(0), + uint64(50_000), + big.NewInt(0), + ) + + code := []byte(fmt.Sprintf(` + import EVM from %s + + transaction(dryTx: [UInt8], realTx: [UInt8], coinbaseBytes: [UInt8; 20]) { + prepare(account: &Account) { + let from = EVM.EVMAddress(bytes: coinbaseBytes) + let coinbase = EVM.EVMAddress(bytes: coinbaseBytes) + + let dryResult = EVM.dryRun(tx: dryTx, from: from) + assert(dryResult.status == EVM.Status.successful, message: "dry run failed") + + let runResult = EVM.run(tx: realTx, coinbase: coinbase) + assert(runResult.status == EVM.Status.successful, message: "run after dry run failed") + } + } + `, sc.EVMContract.Address.HexWithPrefix())) + + dryTxArg := cadence.NewArray( + unittest.BytesToCdcUInt8(dryTxBytes), + ).WithType(stdlib.EVMTransactionBytesCadenceType) + + realTxArg := cadence.NewArray( + unittest.BytesToCdcUInt8(realTxBytes), + ).WithType(stdlib.EVMTransactionBytesCadenceType) + + coinbase := cadence.NewArray( + unittest.BytesToCdcUInt8(testAccount.Address().Bytes()), + ).WithType(stdlib.EVMAddressBytesCadenceType) + + txBody, err := flow.NewTransactionBodyBuilder(). + SetScript(code). + SetPayer(sc.FlowServiceAccount.Address). + AddAuthorizer(sc.FlowServiceAccount.Address). + AddArgument(json.MustEncode(dryTxArg)). + AddArgument(json.MustEncode(realTxArg)). + AddArgument(json.MustEncode(coinbase)). + Build() + require.NoError(t, err) + + tx := fvm.Transaction(txBody, 0) + _, output, err := vm.Run(ctx, tx, snapshot) + require.NoError(t, err) + require.NoError(t, output.Err) + assert.Equal(t, uint64(15), output.ComputationUsed) + }, + ) + }) } func TestDryCall(t *testing.T) { @@ -6830,7 +6915,7 @@ func createAndFundFlowAccount( code := []byte(fmt.Sprintf( ` import FlowToken from %s - import FungibleToken from %s + import FungibleToken from %s transaction { prepare(account: auth(BorrowValue) &Account) { @@ -6900,7 +6985,7 @@ func setupCOA( transaction(amount: UFix64) { prepare(account: auth(Capabilities, Storage) &Account) { let cadenceOwnedAccount1 <- EVM.createCadenceOwnedAccount() - + let vaultRef = account.storage .borrow(from: /storage/flowTokenVault) ?? panic("Could not borrow reference to the owner's Vault!") @@ -6909,7 +6994,7 @@ func setupCOA( let vault <- vaultRef.withdraw(amount: amount) as! @FlowToken.Vault cadenceOwnedAccount1.deposit(from: <-vault) } - + account.storage.save<@EVM.CadenceOwnedAccount>( <-cadenceOwnedAccount1, to: /storage/coa @@ -7101,7 +7186,7 @@ func RunWithNewEnvironment( bootstrapOpts ...fvm.BootstrapProcedureOption, ) { rootAddr := evm.StorageAccountAddress(chain.ChainID()) - RunWithTestBackend(t, func(backend *TestBackend) { + RunWithTestBackend(t, chain.ChainID(), func(backend *TestBackend) { RunWithDeployedContract(t, GetStorageTestContract(t), backend, rootAddr, func(testContract *TestContract) { RunWithEOATestAccount(t, backend, rootAddr, func(testAccount *EOATestAccount) { blocks := new(envMock.Blocks) @@ -7162,7 +7247,7 @@ func RunContractWithNewEnvironment( ) { rootAddr := evm.StorageAccountAddress(chain.ChainID()) - RunWithTestBackend(t, func(backend *TestBackend) { + RunWithTestBackend(t, chain.ChainID(), func(backend *TestBackend) { RunWithDeployedContract(t, tc, backend, rootAddr, func(testContract *TestContract) { RunWithEOATestAccount(t, backend, rootAddr, func(testAccount *EOATestAccount) { diff --git a/fvm/evm/handler/blockstore.go b/fvm/evm/handler/blockstore.go deleted file mode 100644 index 3e1f2581c02..00000000000 --- a/fvm/evm/handler/blockstore.go +++ /dev/null @@ -1,203 +0,0 @@ -package handler - -import ( - "fmt" - "time" - - gethCommon "github.com/ethereum/go-ethereum/common" - - "github.com/onflow/flow-go/fvm/evm/types" - "github.com/onflow/flow-go/model/flow" -) - -const ( - BlockHashListCapacity = 256 - BlockStoreLatestBlockKey = "LatestBlock" - BlockStoreLatestBlockProposalKey = "LatestBlockProposal" -) - -type BlockStore struct { - chainID flow.ChainID - backend types.Backend - rootAddress flow.Address -} - -var _ types.BlockStore = &BlockStore{} - -// NewBlockStore constructs a new block store -func NewBlockStore( - chainID flow.ChainID, - backend types.Backend, - rootAddress flow.Address, -) *BlockStore { - return &BlockStore{ - chainID: chainID, - backend: backend, - rootAddress: rootAddress, - } -} - -// BlockProposal returns the block proposal to be updated by the handler -func (bs *BlockStore) BlockProposal() (*types.BlockProposal, error) { - // first fetch it from the storage - data, err := bs.backend.GetValue(bs.rootAddress[:], []byte(BlockStoreLatestBlockProposalKey)) - if err != nil { - return nil, err - } - if len(data) != 0 { - return types.NewBlockProposalFromBytes(data) - } - bp, err := bs.constructBlockProposal() - if err != nil { - return nil, err - } - // store block proposal - err = bs.UpdateBlockProposal(bp) - if err != nil { - return nil, err - } - return bp, nil -} - -func (bs *BlockStore) constructBlockProposal() (*types.BlockProposal, error) { - // if available construct a new one - cadenceHeight, err := bs.backend.GetCurrentBlockHeight() - if err != nil { - return nil, err - } - - cadenceBlock, found, err := bs.backend.GetBlockAtHeight(cadenceHeight) - if err != nil { - return nil, err - } - if !found { - return nil, fmt.Errorf("cadence block not found") - } - - lastExecutedBlock, err := bs.LatestBlock() - if err != nil { - return nil, err - } - - parentHash, err := lastExecutedBlock.Hash() - if err != nil { - return nil, err - } - - // cadence block timestamp is unix nanoseconds but evm blocks - // expect timestamps in unix seconds so we convert here - timestamp := uint64(cadenceBlock.Timestamp / int64(time.Second)) - - // read a random value for block proposal - prevrandao := gethCommon.Hash{} - err = bs.backend.ReadRandom(prevrandao[:]) - if err != nil { - return nil, err - } - - blockProposal := types.NewBlockProposal( - parentHash, - lastExecutedBlock.Height+1, - timestamp, - lastExecutedBlock.TotalSupply, - prevrandao, - ) - - return blockProposal, nil -} - -// UpdateBlockProposal updates the block proposal -func (bs *BlockStore) UpdateBlockProposal(bp *types.BlockProposal) error { - blockProposalBytes, err := bp.ToBytes() - if err != nil { - return types.NewFatalError(err) - } - - return bs.backend.SetValue( - bs.rootAddress[:], - []byte(BlockStoreLatestBlockProposalKey), - blockProposalBytes, - ) -} - -// CommitBlockProposal commits the block proposal to the chain -func (bs *BlockStore) CommitBlockProposal(bp *types.BlockProposal) error { - bp.PopulateRoots() - - blockBytes, err := bp.Block.ToBytes() - if err != nil { - return types.NewFatalError(err) - } - - err = bs.backend.SetValue(bs.rootAddress[:], []byte(BlockStoreLatestBlockKey), blockBytes) - if err != nil { - return err - } - - hash, err := bp.Block.Hash() - if err != nil { - return err - } - - bhl, err := bs.getBlockHashList() - if err != nil { - return err - } - err = bhl.Push(bp.Block.Height, hash) - if err != nil { - return err - } - - // construct a new block proposal and store - newBP, err := bs.constructBlockProposal() - if err != nil { - return err - } - err = bs.UpdateBlockProposal(newBP) - if err != nil { - return err - } - - return nil -} - -// LatestBlock returns the latest executed block -func (bs *BlockStore) LatestBlock() (*types.Block, error) { - data, err := bs.backend.GetValue(bs.rootAddress[:], []byte(BlockStoreLatestBlockKey)) - if err != nil { - return nil, err - } - if len(data) == 0 { - return types.GenesisBlock(bs.chainID), nil - } - return types.NewBlockFromBytes(data) -} - -// BlockHash returns the block hash for the last x blocks -func (bs *BlockStore) BlockHash(height uint64) (gethCommon.Hash, error) { - bhl, err := bs.getBlockHashList() - if err != nil { - return gethCommon.Hash{}, err - } - _, hash, err := bhl.BlockHashByHeight(height) - return hash, err -} - -func (bs *BlockStore) getBlockHashList() (*BlockHashList, error) { - bhl, err := NewBlockHashList(bs.backend, bs.rootAddress, BlockHashListCapacity) - if err != nil { - return nil, err - } - - if bhl.IsEmpty() { - err = bhl.Push( - types.GenesisBlock(bs.chainID).Height, - types.GenesisBlockHash(bs.chainID), - ) - if err != nil { - return nil, err - } - } - - return bhl, nil -} diff --git a/fvm/evm/handler/blockstore_test.go b/fvm/evm/handler/blockstore_test.go deleted file mode 100644 index c2e40135949..00000000000 --- a/fvm/evm/handler/blockstore_test.go +++ /dev/null @@ -1,98 +0,0 @@ -package handler_test - -import ( - "math/big" - "testing" - - gethCommon "github.com/ethereum/go-ethereum/common" - "github.com/stretchr/testify/require" - - "github.com/onflow/flow-go/fvm/evm/handler" - "github.com/onflow/flow-go/fvm/evm/testutils" - "github.com/onflow/flow-go/fvm/evm/types" - "github.com/onflow/flow-go/model/flow" -) - -func TestBlockStore(t *testing.T) { - - var chainID = flow.Testnet - testutils.RunWithTestBackend(t, func(backend *testutils.TestBackend) { - testutils.RunWithTestFlowEVMRootAddress(t, backend, func(root flow.Address) { - bs := handler.NewBlockStore(chainID, backend, root) - - // check the Genesis block - b, err := bs.LatestBlock() - require.NoError(t, err) - require.Equal(t, types.GenesisBlock(chainID), b) - h, err := bs.BlockHash(0) - require.NoError(t, err) - require.Equal(t, types.GenesisBlockHash(chainID), h) - - // test block proposal construction from the Genesis block - bp, err := bs.BlockProposal() - require.NoError(t, err) - require.Equal(t, uint64(1), bp.Height) - expectedParentHash, err := types.GenesisBlock(chainID).Hash() - require.NoError(t, err) - require.Equal(t, expectedParentHash, bp.ParentBlockHash) - - // if no commit and again block proposal call should return the same - retbp, err := bs.BlockProposal() - require.NoError(t, err) - require.Equal(t, bp, retbp) - - // update the block proposal - bp.TotalGasUsed += 100 - err = bs.UpdateBlockProposal(bp) - require.NoError(t, err) - - // reset the bs and check if it still return the block proposal - bs = handler.NewBlockStore(chainID, backend, root) - retbp, err = bs.BlockProposal() - require.NoError(t, err) - require.Equal(t, bp, retbp) - - // update the block proposal again - supply := big.NewInt(100) - bp.TotalSupply = supply - err = bs.UpdateBlockProposal(bp) - require.NoError(t, err) - // this should still return the genesis block - retb, err := bs.LatestBlock() - require.NoError(t, err) - require.Equal(t, types.GenesisBlock(chainID), retb) - - // commit the changes - err = bs.CommitBlockProposal(bp) - require.NoError(t, err) - retb, err = bs.LatestBlock() - require.NoError(t, err) - require.Equal(t, supply, retb.TotalSupply) - require.Equal(t, uint64(1), retb.Height) - - retbp, err = bs.BlockProposal() - require.NoError(t, err) - require.Equal(t, uint64(2), retbp.Height) - - // check block hashes - // genesis - h, err = bs.BlockHash(0) - require.NoError(t, err) - require.Equal(t, types.GenesisBlockHash(chainID), h) - - // block 1 - h, err = bs.BlockHash(1) - require.NoError(t, err) - expected, err := bp.Hash() - require.NoError(t, err) - require.Equal(t, expected, h) - - // block 2 - h, err = bs.BlockHash(2) - require.NoError(t, err) - require.Equal(t, gethCommon.Hash{}, h) - }) - - }) - -} diff --git a/fvm/evm/handler/handler.go b/fvm/evm/handler/handler.go index 911d9bfac9b..3f935bc87a1 100644 --- a/fvm/evm/handler/handler.go +++ b/fvm/evm/handler/handler.go @@ -12,6 +12,7 @@ import ( "github.com/onflow/flow-go/fvm/environment" fvmErrors "github.com/onflow/flow-go/fvm/errors" "github.com/onflow/flow-go/fvm/evm" + "github.com/onflow/flow-go/fvm/evm/backends" "github.com/onflow/flow-go/fvm/evm/emulator/state" "github.com/onflow/flow-go/fvm/evm/events" "github.com/onflow/flow-go/fvm/evm/handler/coa" @@ -36,9 +37,8 @@ type ContractHandler struct { flowChainID flow.ChainID evmContractAddress flow.Address flowTokenAddress common.Address - blockStore types.BlockStore addressAllocator types.AddressAllocator - backend types.Backend + backend backends.Backend emulator types.Emulator precompiledContracts []types.PrecompiledContract // evmDryCallCache caches EVM drycall results in a transaction. @@ -56,16 +56,14 @@ func NewContractHandler( evmContractAddress flow.Address, flowTokenAddress common.Address, randomBeaconAddress flow.Address, - blockStore types.BlockStore, addressAllocator types.AddressAllocator, - backend types.Backend, + backend backends.Backend, emulator types.Emulator, ) *ContractHandler { return &ContractHandler{ flowChainID: flowChainID, evmContractAddress: evmContractAddress, flowTokenAddress: flowTokenAddress, - blockStore: blockStore, addressAllocator: addressAllocator, backend: backend, emulator: emulator, @@ -215,7 +213,7 @@ func (h *ContractHandler) AccountByAddress(addr types.Address, isAuthorized bool // LastExecutedBlock returns the last executed block func (h *ContractHandler) LastExecutedBlock() *types.Block { - block, err := h.blockStore.LatestBlock() + block, err := h.backend.LatestBlock() panicOnError(err) return block } @@ -408,10 +406,7 @@ func (h *ContractHandler) batchRun(rlpEncodedTxs [][]byte) (_ []*types.Result, e } // update the block proposal - err = h.blockStore.UpdateBlockProposal(bp) - if err != nil { - return nil, err - } + h.backend.StageBlockProposal(bp) return res, nil } @@ -431,13 +426,13 @@ func (h *ContractHandler) commitBlockProposal() (err error) { }() // load latest block proposal - bp, err := h.blockStore.BlockProposal() + bp, err := h.backend.BlockProposal() if err != nil { return err } // commit the proposal - err = h.blockStore.CommitBlockProposal(bp) + err = h.backend.CommitBlockProposal(bp) if err != nil { return err } @@ -531,10 +526,7 @@ func (h *ContractHandler) run(rlpEncodedTx []byte) (_ *types.Result, err error) // step 8 - update the block proposal bp.AppendTransaction(res) - err = h.blockStore.UpdateBlockProposal(bp) - if err != nil { - return nil, err - } + h.backend.StageBlockProposal(bp) // step 9 - emit transaction event err = h.emitEvent( @@ -745,7 +737,7 @@ func (h *ContractHandler) getBlockContext(bp *types.BlockProposal) ( BlockTimestamp: bp.Timestamp, DirectCallBaseGasUsage: types.DefaultDirectCallBaseGasUsage, GetHashFunc: func(n uint64) gethCommon.Hash { - hash, err := h.blockStore.BlockHash(n) + hash, err := h.backend.BlockHash(n) panicOnError(err) // we have to handle it here given we can't continue with it even in try case return hash }, @@ -758,7 +750,7 @@ func (h *ContractHandler) getBlockContext(bp *types.BlockProposal) ( } func (h *ContractHandler) getBlockProposal() (*types.BlockProposal, error) { - return h.blockStore.BlockProposal() + return h.backend.BlockProposal() } func (h *ContractHandler) executeAndHandleCall( @@ -837,10 +829,7 @@ func (h *ContractHandler) executeAndHandleCall( } // update the block proposal - err = h.blockStore.UpdateBlockProposal(bp) - if err != nil { - return nil, err - } + h.backend.StageBlockProposal(bp) // step 8 - emit transaction event encoded, err := call.Encode() diff --git a/fvm/evm/handler/handler_benchmark_test.go b/fvm/evm/handler/handler_benchmark_test.go index 136a431aec7..7fe9326014f 100644 --- a/fvm/evm/handler/handler_benchmark_test.go +++ b/fvm/evm/handler/handler_benchmark_test.go @@ -14,7 +14,7 @@ func BenchmarkStorage(b *testing.B) { benchmarkStorageGrowth(b, 100, 100, 100) } // benchmark func benchmarkStorageGrowth(b *testing.B, accountCount, setupKittyCount, txPerBlock int) { - testutils.RunWithTestBackend(b, func(backend *testutils.TestBackend) { + testutils.RunWithTestBackend(b, flow.Testnet, func(backend *testutils.TestBackend) { testutils.RunWithTestFlowEVMRootAddress(b, backend, func(rootAddr flow.Address) { testutils.RunWithDeployedContract(b, testutils.GetDummyKittyTestContract(b), diff --git a/fvm/evm/handler/handler_test.go b/fvm/evm/handler/handler_test.go index 935dabd4a2b..49435a2d9f7 100644 --- a/fvm/evm/handler/handler_test.go +++ b/fvm/evm/handler/handler_test.go @@ -17,6 +17,7 @@ import ( "github.com/onflow/flow-go/fvm/errors" "github.com/onflow/flow-go/fvm/evm" + "github.com/onflow/flow-go/fvm/evm/backends" "github.com/onflow/flow-go/fvm/evm/emulator" "github.com/onflow/flow-go/fvm/evm/emulator/state" "github.com/onflow/flow-go/fvm/evm/handler" @@ -40,14 +41,12 @@ func TestHandler_TransactionRunOrPanic(t *testing.T) { t.Run("test RunOrPanic run (happy case)", func(t *testing.T) { t.Parallel() - testutils.RunWithTestBackend(t, func(backend *testutils.TestBackend) { + testutils.RunWithTestBackend(t, flow.Testnet, func(backend *testutils.TestBackend) { testutils.RunWithTestFlowEVMRootAddress(t, backend, func(rootAddr flow.Address) { testutils.RunWithEOATestAccount(t, backend, rootAddr, func(eoa *testutils.EOATestAccount) { sc := systemcontracts.SystemContractsForChain(flow.Emulator) - bs := handler.NewBlockStore(defaultChainID, backend, rootAddr) - aa := handler.NewAddressAllocator() result := &types.Result{ @@ -68,7 +67,7 @@ func TestHandler_TransactionRunOrPanic(t *testing.T) { return new(big.Int), nil }, } - handler := handler.NewContractHandler(flow.Emulator, rootAddr, flowTokenAddress, rootAddr, bs, aa, backend, em) + handler := handler.NewContractHandler(flow.Emulator, rootAddr, flowTokenAddress, rootAddr, aa, backend, em) coinbase := types.NewAddress(gethCommon.Address{}) @@ -125,10 +124,9 @@ func TestHandler_TransactionRunOrPanic(t *testing.T) { t.Run("test RunOrPanic (unhappy non-fatal cases)", func(t *testing.T) { t.Parallel() - testutils.RunWithTestBackend(t, func(backend *testutils.TestBackend) { + testutils.RunWithTestBackend(t, flow.Testnet, func(backend *testutils.TestBackend) { testutils.RunWithTestFlowEVMRootAddress(t, backend, func(rootAddr flow.Address) { testutils.RunWithEOATestAccount(t, backend, rootAddr, func(eoa *testutils.EOATestAccount) { - bs := handler.NewBlockStore(defaultChainID, backend, rootAddr) aa := handler.NewAddressAllocator() em := &testutils.TestEmulator{ RunTransactionFunc: func(tx *gethTypes.Transaction) (*types.Result, error) { @@ -141,7 +139,7 @@ func TestHandler_TransactionRunOrPanic(t *testing.T) { return new(big.Int), nil }, } - handler := handler.NewContractHandler(flow.Testnet, rootAddr, flowTokenAddress, rootAddr, bs, aa, backend, em) + handler := handler.NewContractHandler(flow.Testnet, rootAddr, flowTokenAddress, rootAddr, aa, backend, em) coinbase := types.NewAddress(gethCommon.Address{}) @@ -188,10 +186,9 @@ func TestHandler_TransactionRunOrPanic(t *testing.T) { t.Run("test RunOrPanic (fatal cases)", func(t *testing.T) { t.Parallel() - testutils.RunWithTestBackend(t, func(backend *testutils.TestBackend) { + testutils.RunWithTestBackend(t, flow.Testnet, func(backend *testutils.TestBackend) { testutils.RunWithTestFlowEVMRootAddress(t, backend, func(rootAddr flow.Address) { testutils.RunWithEOATestAccount(t, backend, rootAddr, func(eoa *testutils.EOATestAccount) { - bs := handler.NewBlockStore(defaultChainID, backend, rootAddr) aa := handler.NewAddressAllocator() em := &testutils.TestEmulator{ RunTransactionFunc: func(tx *gethTypes.Transaction) (*types.Result, error) { @@ -202,7 +199,7 @@ func TestHandler_TransactionRunOrPanic(t *testing.T) { return new(big.Int), nil }, } - handler := handler.NewContractHandler(flow.Testnet, rootAddr, flowTokenAddress, rootAddr, bs, aa, backend, em) + handler := handler.NewContractHandler(flow.Testnet, rootAddr, flowTokenAddress, rootAddr, aa, backend, em) assertPanic(t, errors.IsFailure, func() { tx := eoa.PrepareSignAndEncodeTx( t, @@ -223,7 +220,7 @@ func TestHandler_TransactionRunOrPanic(t *testing.T) { t.Run("test RunOrPanic (with integrated emulator)", func(t *testing.T) { t.Parallel() - testutils.RunWithTestBackend(t, func(backend *testutils.TestBackend) { + testutils.RunWithTestBackend(t, flow.Testnet, func(backend *testutils.TestBackend) { testutils.RunWithTestFlowEVMRootAddress(t, backend, func(rootAddr flow.Address) { handler := SetupHandler(t, backend, rootAddr) @@ -283,7 +280,7 @@ func TestHandler_OpsWithoutEmulator(t *testing.T) { t.Run("test last executed block call", func(t *testing.T) { t.Parallel() - testutils.RunWithTestBackend(t, func(backend *testutils.TestBackend) { + testutils.RunWithTestBackend(t, flow.Testnet, func(backend *testutils.TestBackend) { testutils.RunWithTestFlowEVMRootAddress(t, backend, func(rootAddr flow.Address) { handler := SetupHandler(t, backend, rootAddr) @@ -308,7 +305,7 @@ func TestHandler_OpsWithoutEmulator(t *testing.T) { t.Run("test address allocation", func(t *testing.T) { t.Parallel() - testutils.RunWithTestBackend(t, func(backend *testutils.TestBackend) { + testutils.RunWithTestBackend(t, flow.Testnet, func(backend *testutils.TestBackend) { testutils.RunWithTestFlowEVMRootAddress(t, backend, func(rootAddr flow.Address) { h := SetupHandler(t, backend, rootAddr) @@ -326,7 +323,7 @@ func TestHandler_COA(t *testing.T) { t.Parallel() t.Run("test deposit/withdraw (with integrated emulator)", func(t *testing.T) { - testutils.RunWithTestBackend(t, func(backend *testutils.TestBackend) { + testutils.RunWithTestBackend(t, flow.Testnet, func(backend *testutils.TestBackend) { testutils.RunWithTestFlowEVMRootAddress(t, backend, func(rootAddr flow.Address) { sc := systemcontracts.SystemContractsForChain(flow.Emulator) @@ -413,7 +410,7 @@ func TestHandler_COA(t *testing.T) { }) t.Run("test coa deployment", func(t *testing.T) { - testutils.RunWithTestBackend(t, func(backend *testutils.TestBackend) { + testutils.RunWithTestBackend(t, flow.Testnet, func(backend *testutils.TestBackend) { testutils.RunWithTestFlowEVMRootAddress(t, backend, func(rootAddr flow.Address) { h := SetupHandler(t, backend, rootAddr) @@ -460,10 +457,9 @@ func TestHandler_COA(t *testing.T) { }) t.Run("test withdraw (unhappy case)", func(t *testing.T) { - testutils.RunWithTestBackend(t, func(backend *testutils.TestBackend) { + testutils.RunWithTestBackend(t, flow.Testnet, func(backend *testutils.TestBackend) { testutils.RunWithTestFlowEVMRootAddress(t, backend, func(rootAddr flow.Address) { testutils.RunWithEOATestAccount(t, backend, rootAddr, func(eoa *testutils.EOATestAccount) { - bs := handler.NewBlockStore(defaultChainID, backend, rootAddr) aa := handler.NewAddressAllocator() // Withdraw calls are only possible within FOA accounts @@ -474,7 +470,7 @@ func TestHandler_COA(t *testing.T) { }, } - handler := handler.NewContractHandler(flow.Testnet, rootAddr, flowTokenAddress, rootAddr, bs, aa, backend, em) + handler := handler.NewContractHandler(flow.Testnet, rootAddr, flowTokenAddress, rootAddr, aa, backend, em) account := handler.AccountByAddress(testutils.RandomAddress(t), false) account.Withdraw(types.NewBalanceFromUFix64(1)) @@ -491,7 +487,7 @@ func TestHandler_COA(t *testing.T) { }, } - handler := handler.NewContractHandler(flow.Testnet, rootAddr, flowTokenAddress, rootAddr, bs, aa, backend, em) + handler := handler.NewContractHandler(flow.Testnet, rootAddr, flowTokenAddress, rootAddr, aa, backend, em) account := handler.AccountByAddress(testutils.RandomAddress(t), true) account.Withdraw(types.NewBalanceFromUFix64(1)) @@ -508,7 +504,7 @@ func TestHandler_COA(t *testing.T) { }, } - handler := handler.NewContractHandler(flow.Testnet, rootAddr, flowTokenAddress, rootAddr, bs, aa, backend, em) + handler := handler.NewContractHandler(flow.Testnet, rootAddr, flowTokenAddress, rootAddr, aa, backend, em) account := handler.AccountByAddress(testutils.RandomAddress(t), true) account.Withdraw(types.NewBalanceFromUFix64(0)) @@ -525,7 +521,7 @@ func TestHandler_COA(t *testing.T) { }, } - handler := handler.NewContractHandler(flow.Testnet, rootAddr, flowTokenAddress, rootAddr, bs, aa, backend, em) + handler := handler.NewContractHandler(flow.Testnet, rootAddr, flowTokenAddress, rootAddr, aa, backend, em) account := handler.AccountByAddress(testutils.RandomAddress(t), true) account.Withdraw(types.NewBalanceFromUFix64(0)) @@ -538,10 +534,9 @@ func TestHandler_COA(t *testing.T) { t.Run("test deposit (unhappy case)", func(t *testing.T) { t.Parallel() - testutils.RunWithTestBackend(t, func(backend *testutils.TestBackend) { + testutils.RunWithTestBackend(t, flow.Testnet, func(backend *testutils.TestBackend) { testutils.RunWithTestFlowEVMRootAddress(t, backend, func(rootAddr flow.Address) { testutils.RunWithEOATestAccount(t, backend, rootAddr, func(eoa *testutils.EOATestAccount) { - bs := handler.NewBlockStore(defaultChainID, backend, rootAddr) aa := handler.NewAddressAllocator() // test non fatal error of emulator @@ -555,7 +550,7 @@ func TestHandler_COA(t *testing.T) { }, } - handler := handler.NewContractHandler(flow.Testnet, rootAddr, flowTokenAddress, rootAddr, bs, aa, backend, em) + handler := handler.NewContractHandler(flow.Testnet, rootAddr, flowTokenAddress, rootAddr, aa, backend, em) account := handler.AccountByAddress(testutils.RandomAddress(t), true) account.Deposit(types.NewFlowTokenVault(types.NewBalanceFromUFix64(1))) @@ -572,7 +567,7 @@ func TestHandler_COA(t *testing.T) { }, } - handler := handler.NewContractHandler(flow.Testnet, rootAddr, flowTokenAddress, rootAddr, bs, aa, backend, em) + handler := handler.NewContractHandler(flow.Testnet, rootAddr, flowTokenAddress, rootAddr, aa, backend, em) account := handler.AccountByAddress(testutils.RandomAddress(t), true) account.Deposit(types.NewFlowTokenVault(types.NewBalanceFromUFix64(1))) @@ -586,7 +581,7 @@ func TestHandler_COA(t *testing.T) { t.Parallel() // TODO update this test with events, gas metering, etc - testutils.RunWithTestBackend(t, func(backend *testutils.TestBackend) { + testutils.RunWithTestBackend(t, flow.Testnet, func(backend *testutils.TestBackend) { testutils.RunWithTestFlowEVMRootAddress(t, backend, func(rootAddr flow.Address) { handler := SetupHandler(t, backend, rootAddr) @@ -628,7 +623,7 @@ func TestHandler_COA(t *testing.T) { t.Run("test call to cadence arch", func(t *testing.T) { t.Parallel() - testutils.RunWithTestBackend(t, func(backend *testutils.TestBackend) { + testutils.RunWithTestBackend(t, flow.Testnet, func(backend *testutils.TestBackend) { blockHeight := uint64(123) backend.GetCurrentBlockHeightFunc = func() (uint64, error) { return blockHeight, nil @@ -682,7 +677,7 @@ func TestHandler_COA(t *testing.T) { t.Run("test block.random call (with integrated emulator)", func(t *testing.T) { t.Parallel() - testutils.RunWithTestBackend(t, func(backend *testutils.TestBackend) { + testutils.RunWithTestBackend(t, flow.Testnet, func(backend *testutils.TestBackend) { random := testutils.RandomCommonHash(t) backend.ReadRandomFunc = func(buffer []byte) error { copy(buffer, random.Bytes()) @@ -726,11 +721,10 @@ func TestHandler_TransactionRun(t *testing.T) { t.Run("test - transaction run (success)", func(t *testing.T) { t.Parallel() - testutils.RunWithTestBackend(t, func(backend *testutils.TestBackend) { + testutils.RunWithTestBackend(t, flow.Testnet, func(backend *testutils.TestBackend) { testutils.RunWithTestFlowEVMRootAddress(t, backend, func(rootAddr flow.Address) { testutils.RunWithEOATestAccount(t, backend, rootAddr, func(eoa *testutils.EOATestAccount) { - bs := handler.NewBlockStore(chainID, backend, rootAddr) aa := handler.NewAddressAllocator() result := &types.Result{ @@ -772,7 +766,7 @@ func TestHandler_TransactionRun(t *testing.T) { }, nil }, } - handler := handler.NewContractHandler(chainID, rootAddr, flowTokenAddress, rootAddr, bs, aa, backend, em) + handler := handler.NewContractHandler(chainID, rootAddr, flowTokenAddress, rootAddr, aa, backend, em) tx := eoa.PrepareSignAndEncodeTx( t, gethCommon.Address{}, @@ -796,11 +790,10 @@ func TestHandler_TransactionRun(t *testing.T) { t.Run("test - transaction run (failed)", func(t *testing.T) { t.Parallel() - testutils.RunWithTestBackend(t, func(backend *testutils.TestBackend) { + testutils.RunWithTestBackend(t, flow.Testnet, func(backend *testutils.TestBackend) { testutils.RunWithTestFlowEVMRootAddress(t, backend, func(rootAddr flow.Address) { testutils.RunWithEOATestAccount(t, backend, rootAddr, func(eoa *testutils.EOATestAccount) { - bs := handler.NewBlockStore(chainID, backend, rootAddr) aa := handler.NewAddressAllocator() result := &types.Result{ @@ -821,7 +814,7 @@ func TestHandler_TransactionRun(t *testing.T) { return new(big.Int), nil }, } - handler := handler.NewContractHandler(chainID, rootAddr, flowTokenAddress, rootAddr, bs, aa, backend, em) + handler := handler.NewContractHandler(chainID, rootAddr, flowTokenAddress, rootAddr, aa, backend, em) tx := eoa.PrepareSignAndEncodeTx( t, @@ -845,10 +838,9 @@ func TestHandler_TransactionRun(t *testing.T) { t.Run("test - transaction run (unhappy cases)", func(t *testing.T) { t.Parallel() - testutils.RunWithTestBackend(t, func(backend *testutils.TestBackend) { + testutils.RunWithTestBackend(t, flow.Testnet, func(backend *testutils.TestBackend) { testutils.RunWithTestFlowEVMRootAddress(t, backend, func(rootAddr flow.Address) { testutils.RunWithEOATestAccount(t, backend, rootAddr, func(eoa *testutils.EOATestAccount) { - bs := handler.NewBlockStore(chainID, backend, rootAddr) aa := handler.NewAddressAllocator() evmErr := fmt.Errorf("%w: next nonce %v, tx nonce %v", gethCore.ErrNonceTooLow, 1, 0) em := &testutils.TestEmulator{ @@ -859,7 +851,7 @@ func TestHandler_TransactionRun(t *testing.T) { return new(big.Int), nil }, } - handler := handler.NewContractHandler(chainID, rootAddr, flowTokenAddress, rootAddr, bs, aa, backend, em) + handler := handler.NewContractHandler(chainID, rootAddr, flowTokenAddress, rootAddr, aa, backend, em) coinbase := types.NewAddress(gethCommon.Address{}) @@ -898,12 +890,11 @@ func TestHandler_TransactionRun(t *testing.T) { t.Run("test - transaction batch run (success)", func(t *testing.T) { t.Parallel() - testutils.RunWithTestBackend(t, func(backend *testutils.TestBackend) { + testutils.RunWithTestBackend(t, flow.Testnet, func(backend *testutils.TestBackend) { testutils.RunWithTestFlowEVMRootAddress(t, backend, func(rootAddr flow.Address) { testutils.RunWithEOATestAccount(t, backend, rootAddr, func(eoa *testutils.EOATestAccount) { sc := systemcontracts.SystemContractsForChain(chainID) - bs := handler.NewBlockStore(chainID, backend, rootAddr) aa := handler.NewAddressAllocator() gasConsumed := testutils.RandomGas(1000) @@ -956,7 +947,7 @@ func TestHandler_TransactionRun(t *testing.T) { }, nil }, } - handler := handler.NewContractHandler(chainID, rootAddr, flowTokenAddress, randomBeaconAddress, bs, aa, backend, em) + handler := handler.NewContractHandler(chainID, rootAddr, flowTokenAddress, randomBeaconAddress, aa, backend, em) gasLimit := uint64(100_000) @@ -1027,10 +1018,9 @@ func TestHandler_TransactionRun(t *testing.T) { t.Run("test - transaction batch run (unhappy case)", func(t *testing.T) { t.Parallel() - testutils.RunWithTestBackend(t, func(backend *testutils.TestBackend) { + testutils.RunWithTestBackend(t, flow.Testnet, func(backend *testutils.TestBackend) { testutils.RunWithTestFlowEVMRootAddress(t, backend, func(rootAddr flow.Address) { testutils.RunWithEOATestAccount(t, backend, rootAddr, func(eoa *testutils.EOATestAccount) { - bs := handler.NewBlockStore(chainID, backend, rootAddr) aa := handler.NewAddressAllocator() gasConsumed := testutils.RandomGas(1000) @@ -1060,7 +1050,7 @@ func TestHandler_TransactionRun(t *testing.T) { return new(big.Int), nil }, } - handler := handler.NewContractHandler(chainID, rootAddr, flowTokenAddress, randomBeaconAddress, bs, aa, backend, em) + handler := handler.NewContractHandler(chainID, rootAddr, flowTokenAddress, randomBeaconAddress, aa, backend, em) coinbase := types.NewAddress(gethCommon.Address{}) // batch run empty transactions @@ -1077,11 +1067,10 @@ func TestHandler_TransactionRun(t *testing.T) { t.Run("test dry run successful", func(t *testing.T) { t.Parallel() - testutils.RunWithTestBackend(t, func(backend *testutils.TestBackend) { + testutils.RunWithTestBackend(t, flow.Testnet, func(backend *testutils.TestBackend) { testutils.RunWithTestFlowEVMRootAddress(t, backend, func(rootAddr flow.Address) { testutils.RunWithEOATestAccount(t, backend, rootAddr, func(eoa *testutils.EOATestAccount) { - bs := handler.NewBlockStore(defaultChainID, backend, rootAddr) aa := handler.NewAddressAllocator() nonce := uint64(1) @@ -1128,7 +1117,7 @@ func TestHandler_TransactionRun(t *testing.T) { }, } - handler := handler.NewContractHandler(chainID, rootAddr, flowTokenAddress, randomBeaconAddress, bs, aa, backend, em) + handler := handler.NewContractHandler(chainID, rootAddr, flowTokenAddress, randomBeaconAddress, aa, backend, em) rs := handler.DryRun(rlpTx, from) require.Equal(t, types.StatusSuccessful, rs.Status) @@ -1143,7 +1132,7 @@ func TestHandler_TransactionRun(t *testing.T) { t.Run("test - open tracing", func(t *testing.T) { t.Parallel() - testutils.RunWithTestBackend(t, func(backend *testutils.TestBackend) { + testutils.RunWithTestBackend(t, flow.Testnet, func(backend *testutils.TestBackend) { testutils.RunWithTestFlowEVMRootAddress(t, backend, func(rootAddr flow.Address) { testutils.RunWithEOATestAccount(t, backend, rootAddr, func(eoa *testutils.EOATestAccount) { @@ -1195,7 +1184,7 @@ func TestHandler_TransactionRun(t *testing.T) { func TestHandler_Metrics(t *testing.T) { t.Parallel() - testutils.RunWithTestBackend(t, func(backend *testutils.TestBackend) { + testutils.RunWithTestBackend(t, flow.Testnet, func(backend *testutils.TestBackend) { testutils.RunWithTestFlowEVMRootAddress(t, backend, func(rootAddr flow.Address) { testutils.RunWithEOATestAccount(t, backend, rootAddr, func(eoa *testutils.EOATestAccount) { gasUsed := testutils.RandomGas(1000) @@ -1226,7 +1215,6 @@ func TestHandler_Metrics(t *testing.T) { rootAddr, flowTokenAddress, rootAddr, - handler.NewBlockStore(defaultChainID, backend, rootAddr), handler.NewAddressAllocator(), backend, em, @@ -1293,17 +1281,16 @@ func TestHandler_Metrics(t *testing.T) { func TestHandler_GetState(t *testing.T) { t.Parallel() - testutils.RunWithTestBackend(t, func(backend *testutils.TestBackend) { + testutils.RunWithTestBackend(t, flow.Testnet, func(backend *testutils.TestBackend) { backend.SetEVMTestOperationsAllowed(true) testutils.RunWithTestFlowEVMRootAddress(t, backend, func(rootAddr flow.Address) { testutils.RunWithEOATestAccount(t, backend, rootAddr, func(eoa *testutils.EOATestAccount) { - bs := handler.NewBlockStore(flow.Emulator, backend, rootAddr) aa := handler.NewAddressAllocator() em := &testutils.TestEmulator{} - handler := handler.NewContractHandler(flow.Emulator, rootAddr, flowTokenAddress, rootAddr, bs, aa, backend, em) + handler := handler.NewContractHandler(flow.Emulator, rootAddr, flowTokenAddress, rootAddr, aa, backend, em) address := types.NewAddressFromString("0x7e093BA1474b79481f9B87D66c99a819F25e82E2") slot := gethCommon.HexToHash("0x5591cf37b43a6cc1c6a0b89114c8779fca21c866d7fc4a827ce040428eb28b78") @@ -1317,17 +1304,16 @@ func TestHandler_GetState(t *testing.T) { func TestHandler_GetState_Disabled(t *testing.T) { t.Parallel() - testutils.RunWithTestBackend(t, func(backend *testutils.TestBackend) { + testutils.RunWithTestBackend(t, flow.Testnet, func(backend *testutils.TestBackend) { backend.SetEVMTestOperationsAllowed(false) testutils.RunWithTestFlowEVMRootAddress(t, backend, func(rootAddr flow.Address) { testutils.RunWithEOATestAccount(t, backend, rootAddr, func(eoa *testutils.EOATestAccount) { - bs := handler.NewBlockStore(flow.Emulator, backend, rootAddr) aa := handler.NewAddressAllocator() em := &testutils.TestEmulator{} - handler := handler.NewContractHandler(flow.Emulator, rootAddr, flowTokenAddress, rootAddr, bs, aa, backend, em) + handler := handler.NewContractHandler(flow.Emulator, rootAddr, flowTokenAddress, rootAddr, aa, backend, em) address := types.NewAddressFromString("0x7e093BA1474b79481f9B87D66c99a819F25e82E2") slot := gethCommon.HexToHash("0x5591cf37b43a6cc1c6a0b89114c8779fca21c866d7fc4a827ce040428eb28b78") @@ -1343,13 +1329,12 @@ func TestHandler_GetState_Disabled(t *testing.T) { func TestHandler_SetState(t *testing.T) { t.Parallel() - testutils.RunWithTestBackend(t, func(backend *testutils.TestBackend) { + testutils.RunWithTestBackend(t, flow.Testnet, func(backend *testutils.TestBackend) { backend.SetEVMTestOperationsAllowed(true) testutils.RunWithTestFlowEVMRootAddress(t, backend, func(rootAddr flow.Address) { testutils.RunWithEOATestAccount(t, backend, rootAddr, func(eoa *testutils.EOATestAccount) { - bs := handler.NewBlockStore(flow.Emulator, backend, rootAddr) aa := handler.NewAddressAllocator() execState, err := state.NewStateDB(backend, evm.StorageAccountAddress(flow.Emulator)) @@ -1366,7 +1351,7 @@ func TestHandler_SetState(t *testing.T) { require.Equal(t, gethCommon.Hash{}, prevValue) em := &testutils.TestEmulator{} - handler := handler.NewContractHandler(flow.Emulator, rootAddr, flowTokenAddress, rootAddr, bs, aa, backend, em) + handler := handler.NewContractHandler(flow.Emulator, rootAddr, flowTokenAddress, rootAddr, aa, backend, em) handler.SetState(address, slot, value) }) @@ -1377,13 +1362,12 @@ func TestHandler_SetState(t *testing.T) { func TestHandler_SetState_Disabled(t *testing.T) { t.Parallel() - testutils.RunWithTestBackend(t, func(backend *testutils.TestBackend) { + testutils.RunWithTestBackend(t, flow.Testnet, func(backend *testutils.TestBackend) { backend.SetEVMTestOperationsAllowed(false) testutils.RunWithTestFlowEVMRootAddress(t, backend, func(rootAddr flow.Address) { testutils.RunWithEOATestAccount(t, backend, rootAddr, func(eoa *testutils.EOATestAccount) { - bs := handler.NewBlockStore(flow.Emulator, backend, rootAddr) aa := handler.NewAddressAllocator() execState, err := state.NewStateDB(backend, evm.StorageAccountAddress(flow.Emulator)) @@ -1400,7 +1384,7 @@ func TestHandler_SetState_Disabled(t *testing.T) { require.Equal(t, gethCommon.Hash{}, prevValue) em := &testutils.TestEmulator{} - handler := handler.NewContractHandler(flow.Emulator, rootAddr, flowTokenAddress, rootAddr, bs, aa, backend, em) + handler := handler.NewContractHandler(flow.Emulator, rootAddr, flowTokenAddress, rootAddr, aa, backend, em) assertPanic(t, types.IsAUnsupportedOperationError, func() { handler.SetState(address, slot, value) @@ -1413,13 +1397,12 @@ func TestHandler_SetState_Disabled(t *testing.T) { func TestHandler_RunTxAs(t *testing.T) { t.Parallel() - testutils.RunWithTestBackend(t, func(backend *testutils.TestBackend) { + testutils.RunWithTestBackend(t, flow.Testnet, func(backend *testutils.TestBackend) { backend.SetEVMTestOperationsAllowed(true) testutils.RunWithTestFlowEVMRootAddress(t, backend, func(rootAddr flow.Address) { testutils.RunWithEOATestAccount(t, backend, rootAddr, func(eoa *testutils.EOATestAccount) { - bs := handler.NewBlockStore(flow.Emulator, backend, rootAddr) aa := handler.NewAddressAllocator() result := &types.Result{ ReturnedData: testutils.RandomData(t), @@ -1438,7 +1421,7 @@ func TestHandler_RunTxAs(t *testing.T) { return result, nil }, } - handler := handler.NewContractHandler(flow.Emulator, rootAddr, flowTokenAddress, rootAddr, bs, aa, backend, em) + handler := handler.NewContractHandler(flow.Emulator, rootAddr, flowTokenAddress, rootAddr, aa, backend, em) from := types.NewAddressFromString("0x7e093BA1474b79481f9B87D66c99a819F25e82E2") to := types.NewAddressFromString("0x7A038Ec292505B94d20004d3761Db0d1623bb45b") @@ -1455,13 +1438,12 @@ func TestHandler_RunTxAs(t *testing.T) { func TestHandler_RunTxAs_Disabled(t *testing.T) { t.Parallel() - testutils.RunWithTestBackend(t, func(backend *testutils.TestBackend) { + testutils.RunWithTestBackend(t, flow.Testnet, func(backend *testutils.TestBackend) { backend.SetEVMTestOperationsAllowed(false) testutils.RunWithTestFlowEVMRootAddress(t, backend, func(rootAddr flow.Address) { testutils.RunWithEOATestAccount(t, backend, rootAddr, func(eoa *testutils.EOATestAccount) { - bs := handler.NewBlockStore(flow.Emulator, backend, rootAddr) aa := handler.NewAddressAllocator() result := &types.Result{ ReturnedData: testutils.RandomData(t), @@ -1480,7 +1462,7 @@ func TestHandler_RunTxAs_Disabled(t *testing.T) { return result, nil }, } - handler := handler.NewContractHandler(flow.Emulator, rootAddr, flowTokenAddress, rootAddr, bs, aa, backend, em) + handler := handler.NewContractHandler(flow.Emulator, rootAddr, flowTokenAddress, rootAddr, aa, backend, em) from := types.NewAddressFromString("0x7e093BA1474b79481f9B87D66c99a819F25e82E2") to := types.NewAddressFromString("0x7A038Ec292505B94d20004d3761Db0d1623bb45b") @@ -1518,13 +1500,12 @@ func assertPanic(t *testing.T, check checkError, f func()) { f() } -func SetupHandler(t testing.TB, backend types.Backend, rootAddr flow.Address) *handler.ContractHandler { +func SetupHandler(t testing.TB, backend backends.Backend, rootAddr flow.Address) *handler.ContractHandler { return handler.NewContractHandler( flow.Emulator, rootAddr, flowTokenAddress, rootAddr, - handler.NewBlockStore(defaultChainID, backend, rootAddr), handler.NewAddressAllocator(), backend, emulator.NewEmulator(backend, rootAddr), diff --git a/fvm/evm/handler/precompiles.go b/fvm/evm/handler/precompiles.go index 10cd3d95701..ae45208e5c5 100644 --- a/fvm/evm/handler/precompiles.go +++ b/fvm/evm/handler/precompiles.go @@ -8,6 +8,7 @@ import ( "github.com/onflow/cadence/sema" "github.com/onflow/flow-go/fvm/environment" + "github.com/onflow/flow-go/fvm/evm/backends" "github.com/onflow/flow-go/fvm/evm/precompiles" "github.com/onflow/flow-go/fvm/evm/types" "github.com/onflow/flow-go/model/flow" @@ -33,7 +34,7 @@ func preparePrecompiledContracts( evmContractAddress flow.Address, randomBeaconAddress flow.Address, addressAllocator types.AddressAllocator, - backend types.Backend, + backend backends.Backend, ) []types.PrecompiledContract { archAddress := addressAllocator.AllocatePrecompileAddress(1) archContract := precompiles.ArchContract( @@ -46,7 +47,7 @@ func preparePrecompiledContracts( return []types.PrecompiledContract{archContract} } -func blockHeightProvider(backend types.Backend) func() (uint64, error) { +func blockHeightProvider(backend backends.Backend) func() (uint64, error) { return func() (uint64, error) { h, err := backend.GetCurrentBlockHeight() if types.IsAFatalError(err) { @@ -58,7 +59,7 @@ func blockHeightProvider(backend types.Backend) func() (uint64, error) { const RandomSourceTypeValueFieldName = "value" -func randomSourceProvider(contractAddress flow.Address, backend types.Backend) func(uint64) ([]byte, error) { +func randomSourceProvider(contractAddress flow.Address, backend backends.Backend) func(uint64) ([]byte, error) { return func(blockHeight uint64) ([]byte, error) { value, err := backend.Invoke( environment.ContractFunctionSpec{ @@ -97,7 +98,7 @@ func randomSourceProvider(contractAddress flow.Address, backend types.Backend) f } } -func revertibleRandomGenerator(backend types.Backend) func() (uint64, error) { +func revertibleRandomGenerator(backend backends.Backend) func() (uint64, error) { return func() (uint64, error) { rand := make([]byte, 8) err := backend.ReadRandom(rand) @@ -111,7 +112,7 @@ func revertibleRandomGenerator(backend types.Backend) func() (uint64, error) { const ValidationResultTypeIsValidFieldName = "isValid" -func coaOwnershipProofValidator(contractAddress flow.Address, backend types.Backend) func(proof *types.COAOwnershipProofInContext) (bool, error) { +func coaOwnershipProofValidator(contractAddress flow.Address, backend backends.Backend) func(proof *types.COAOwnershipProofInContext) (bool, error) { return func(proof *types.COAOwnershipProofInContext) (bool, error) { value, err := backend.Invoke( environment.ContractFunctionSpec{ diff --git a/fvm/evm/offchain/blocks/blocks.go b/fvm/evm/offchain/blocks/blocks.go index 455790b9135..781acd939bf 100644 --- a/fvm/evm/offchain/blocks/blocks.go +++ b/fvm/evm/offchain/blocks/blocks.go @@ -5,7 +5,8 @@ import ( gethCommon "github.com/ethereum/go-ethereum/common" - "github.com/onflow/flow-go/fvm/evm/handler" + "github.com/onflow/flow-go/fvm/environment" + "github.com/onflow/flow-go/fvm/evm/backends" "github.com/onflow/flow-go/fvm/evm/types" "github.com/onflow/flow-go/model/flow" ) @@ -16,9 +17,9 @@ const BlockStoreLatestBlockMetaKey = "LatestBlockMeta" // and also the latest executed block meta data type Blocks struct { chainID flow.ChainID - storage types.BackendStorage + storage backends.BackendStorage rootAddress flow.Address - bhl *handler.BlockHashList + bhl *environment.BlockHashList } var _ types.BlockSnapshot = (*Blocks)(nil) @@ -27,7 +28,7 @@ var _ types.BlockSnapshot = (*Blocks)(nil) func NewBlocks( chainID flow.ChainID, rootAddress flow.Address, - storage types.BackendStorage, + storage backends.BackendStorage, ) (*Blocks, error) { var err error blocks := &Blocks{ @@ -35,10 +36,10 @@ func NewBlocks( storage: storage, rootAddress: rootAddress, } - blocks.bhl, err = handler.NewBlockHashList( + blocks.bhl, err = environment.NewBlockHashList( storage, rootAddress, - handler.BlockHashListCapacity, + environment.BlockHashListCapacity, ) if err != nil { return nil, err diff --git a/fvm/evm/offchain/blocks/provider.go b/fvm/evm/offchain/blocks/provider.go index b9da39bd468..e14b566e6d3 100644 --- a/fvm/evm/offchain/blocks/provider.go +++ b/fvm/evm/offchain/blocks/provider.go @@ -3,8 +3,9 @@ package blocks import ( "fmt" + "github.com/onflow/flow-go/fvm/environment" + "github.com/onflow/flow-go/fvm/evm/backends" "github.com/onflow/flow-go/fvm/evm/events" - "github.com/onflow/flow-go/fvm/evm/handler" "github.com/onflow/flow-go/fvm/evm/types" "github.com/onflow/flow-go/model/flow" ) @@ -17,7 +18,7 @@ type BasicProvider struct { chainID flow.ChainID blks *Blocks rootAddr flow.Address - storage types.BackendStorage + storage backends.BackendStorage latestBlockPayload *events.BlockEventPayload } @@ -25,7 +26,7 @@ var _ types.BlockSnapshotProvider = (*BasicProvider)(nil) func NewBasicProvider( chainID flow.ChainID, - storage types.BackendStorage, + storage backends.BackendStorage, rootAddr flow.Address, ) (*BasicProvider, error) { blks, err := NewBlocks(chainID, rootAddr, storage) @@ -87,7 +88,7 @@ func (p *BasicProvider) OnBlockExecuted( // do the same as handler.CommitBlockProposal err = p.storage.SetValue( p.rootAddr[:], - []byte(handler.BlockStoreLatestBlockKey), + []byte(environment.BlockStoreLatestBlockKey), blockBytes, ) if err != nil { @@ -103,7 +104,7 @@ func (p *BasicProvider) OnBlockExecuted( // update block proposal err = p.storage.SetValue( p.rootAddr[:], - []byte(handler.BlockStoreLatestBlockProposalKey), + []byte(environment.BlockStoreLatestBlockProposalKey), blockProposalBytes, ) if err != nil { diff --git a/fvm/evm/offchain/query/view_test.go b/fvm/evm/offchain/query/view_test.go index ec172f98cce..ba3438fe225 100644 --- a/fvm/evm/offchain/query/view_test.go +++ b/fvm/evm/offchain/query/view_test.go @@ -26,7 +26,7 @@ import ( func TestView(t *testing.T) { const chainID = flow.Emulator - RunWithTestBackend(t, func(backend *TestBackend) { + RunWithTestBackend(t, chainID, func(backend *TestBackend) { RunWithTestFlowEVMRootAddress(t, backend, func(rootAddr flow.Address) { RunWithDeployedContract(t, GetStorageTestContract(t), backend, rootAddr, func(testContract *TestContract) { @@ -189,7 +189,7 @@ func TestView(t *testing.T) { func TestViewStateOverrides(t *testing.T) { const chainID = flow.Emulator - RunWithTestBackend(t, func(backend *TestBackend) { + RunWithTestBackend(t, chainID, func(backend *TestBackend) { RunWithTestFlowEVMRootAddress(t, backend, func(rootAddr flow.Address) { RunWithDeployedContract(t, GetStorageTestContract(t), backend, rootAddr, func(testContract *TestContract) { diff --git a/fvm/evm/offchain/storage/ephemeral.go b/fvm/evm/offchain/storage/ephemeral.go index a1687460526..be5eedc4866 100644 --- a/fvm/evm/offchain/storage/ephemeral.go +++ b/fvm/evm/offchain/storage/ephemeral.go @@ -6,6 +6,7 @@ import ( "github.com/onflow/atree" "github.com/onflow/flow-go/fvm/environment" + "github.com/onflow/flow-go/fvm/evm/backends" "github.com/onflow/flow-go/fvm/evm/types" "github.com/onflow/flow-go/model/flow" ) @@ -14,19 +15,19 @@ import ( // the provided backend storage. It can be used for dry running transaction/calls // or batching updates for atomic operations. type EphemeralStorage struct { - parent types.BackendStorage + parent backends.BackendStorage deltas map[flow.RegisterID]flow.RegisterValue } // NewEphemeralStorage constructs a new EphemeralStorage -func NewEphemeralStorage(parent types.BackendStorage) *EphemeralStorage { +func NewEphemeralStorage(parent backends.BackendStorage) *EphemeralStorage { return &EphemeralStorage{ parent: parent, deltas: make(map[flow.RegisterID]flow.RegisterValue), } } -var _ types.BackendStorage = (*EphemeralStorage)(nil) +var _ backends.BackendStorage = (*EphemeralStorage)(nil) var _ types.ReplayResultCollector = (*EphemeralStorage)(nil) diff --git a/fvm/evm/offchain/storage/readonly.go b/fvm/evm/offchain/storage/readonly.go index 6c66e7c1e43..daecced2bcd 100644 --- a/fvm/evm/offchain/storage/readonly.go +++ b/fvm/evm/offchain/storage/readonly.go @@ -5,6 +5,7 @@ import ( "github.com/onflow/atree" + "github.com/onflow/flow-go/fvm/evm/backends" "github.com/onflow/flow-go/fvm/evm/types" ) @@ -13,7 +14,7 @@ type ReadOnlyStorage struct { snapshot types.BackendStorageSnapshot } -var _ types.BackendStorage = &ReadOnlyStorage{} +var _ backends.BackendStorage = &ReadOnlyStorage{} // NewReadOnlyStorage constructs a new ReadOnlyStorage using the given snapshot func NewReadOnlyStorage(snapshot types.BackendStorageSnapshot) *ReadOnlyStorage { diff --git a/fvm/evm/offchain/sync/replay.go b/fvm/evm/offchain/sync/replay.go index a9fe6b2f955..c83535362cf 100644 --- a/fvm/evm/offchain/sync/replay.go +++ b/fvm/evm/offchain/sync/replay.go @@ -9,6 +9,7 @@ import ( gethTrie "github.com/ethereum/go-ethereum/trie" "github.com/onflow/atree" + "github.com/onflow/flow-go/fvm/evm/backends" "github.com/onflow/flow-go/fvm/evm/emulator" "github.com/onflow/flow-go/fvm/evm/events" "github.com/onflow/flow-go/fvm/evm/precompiles" @@ -24,7 +25,7 @@ var emptyChecksum = [types.ChecksumLength]byte{0, 0, 0, 0} func ReplayBlockExecution( chainID flow.ChainID, rootAddr flow.Address, - storage types.BackendStorage, + storage backends.BackendStorage, blockSnapshot types.BlockSnapshot, tracer *gethTracer.Tracer, transactionEvents []events.TransactionEventPayload, diff --git a/fvm/evm/offchain/sync/replayer_test.go b/fvm/evm/offchain/sync/replayer_test.go index 6297a7e2531..d92a12bb644 100644 --- a/fvm/evm/offchain/sync/replayer_test.go +++ b/fvm/evm/offchain/sync/replayer_test.go @@ -23,7 +23,7 @@ func TestChainReplay(t *testing.T) { const chainID = flow.Emulator var snapshot *TestValueStore - RunWithTestBackend(t, func(backend *TestBackend) { + RunWithTestBackend(t, chainID, func(backend *TestBackend) { RunWithTestFlowEVMRootAddress(t, backend, func(rootAddr flow.Address) { RunWithDeployedContract(t, GetStorageTestContract(t), backend, rootAddr, func(testContract *TestContract) { diff --git a/fvm/evm/testutils/backend.go b/fvm/evm/testutils/backend.go index 31ac2349ce5..428c74504e2 100644 --- a/fvm/evm/testutils/backend.go +++ b/fvm/evm/testutils/backend.go @@ -6,6 +6,7 @@ import ( "fmt" "testing" + gocommon "github.com/ethereum/go-ethereum/common" "github.com/onflow/cadence/stdlib" "github.com/rs/zerolog" otelTrace "go.opentelemetry.io/otel/trace" @@ -19,6 +20,7 @@ import ( "golang.org/x/exp/maps" "github.com/onflow/flow-go/fvm/environment" + "github.com/onflow/flow-go/fvm/evm" "github.com/onflow/flow-go/fvm/evm/types" "github.com/onflow/flow-go/fvm/meter" "github.com/onflow/flow-go/fvm/tracing" @@ -37,17 +39,21 @@ func RunWithTestFlowEVMRootAddress(t testing.TB, backend atree.Ledger, f func(fl f(TestFlowEVMRootAddress) } -func RunWithTestBackend(t testing.TB, f func(*TestBackend)) { +func RunWithTestBackend(t testing.TB, chain flow.ChainID, f func(*TestBackend)) { + vs := GetSimpleValueStore() + bi := getSimpleBlockInfo() + rg := getSimpleRandomGenerator() tb := &TestBackend{ - TestValueStore: GetSimpleValueStore(), + TestValueStore: vs, testEventEmitter: getSimpleEventEmitter(), testMeter: getSimpleMeter(), - TestBlockInfo: getSimpleBlockStore(), - TestRandomGenerator: getSimpleRandomGenerator(), + TestBlockInfo: bi, + TestRandomGenerator: rg, TestContractFunctionInvoker: &TestContractFunctionInvoker{}, TestTracer: &TestTracer{}, TestMetricsReporter: &TestMetricsReporter{}, TestLoggerProvider: &TestLoggerProvider{}, + TestBlockStore: getSimpleBlockStore(chain, vs, bi, rg), } f(tb) } @@ -209,7 +215,7 @@ func getSimpleMeter() *testMeter { } } -func getSimpleBlockStore() *TestBlockInfo { +func getSimpleBlockInfo() *TestBlockInfo { var index int64 = 1 return &TestBlockInfo{ GetCurrentBlockHeightFunc: func() (uint64, error) { @@ -227,6 +233,34 @@ func getSimpleBlockStore() *TestBlockInfo { } } +func getSimpleBlockStore(chain flow.ChainID, vs *TestValueStore, bi *TestBlockInfo, rg *TestRandomGenerator) *TestBlockStore { + bs := environment.NewBlockStore( + chain, + vs, + bi, + rg, + evm.StorageAccountAddress(chain), + ) + + return &TestBlockStore{ + LatestBlockFunc: func() (*types.Block, error) { + return bs.LatestBlock() + }, + BlockHashFunc: func(height uint64) (gocommon.Hash, error) { + return bs.BlockHash(height) + }, + BlockProposalFunc: func() (*types.BlockProposal, error) { + return bs.BlockProposal() + }, + StageBlockProposalFunc: func(_bp *types.BlockProposal) { + bs.StageBlockProposal(_bp) + }, + CommitBlockProposalFunc: func(_bp *types.BlockProposal) error { + return bs.CommitBlockProposal(_bp) + }, + } +} + type TestBackend struct { *TestValueStore *testMeter @@ -238,6 +272,7 @@ type TestBackend struct { *TestTracer *TestMetricsReporter *TestLoggerProvider + *TestBlockStore evmTestOperationsAllowed bool } @@ -245,8 +280,6 @@ func (tb *TestBackend) EVMTestOperationsAllowed() bool { return tb.evmTestOperationsAllowed } -var _ types.Backend = &TestBackend{} - func (tb *TestBackend) TotalStorageSize() int { if tb.TotalStorageSizeFunc == nil { panic("method not set") @@ -699,3 +732,53 @@ func (tlp *TestLoggerProvider) Logger() zerolog.Logger { } return zerolog.Nop() } + +type TestBlockStore struct { + BlockHashFunc func(height uint64) (gocommon.Hash, error) + BlockProposalFunc func() (*types.BlockProposal, error) + CommitBlockProposalFunc func(*types.BlockProposal) error + LatestBlockFunc func() (*types.Block, error) + StageBlockProposalFunc func(*types.BlockProposal) +} + +var _ environment.EVMBlockStore = &TestBlockStore{} + +func (tb *TestBlockStore) BlockHash(height uint64) (gocommon.Hash, error) { + blockHashFunc := tb.BlockHashFunc + if blockHashFunc == nil { + panic("BlockHashFunc method is not set") + } + return blockHashFunc(height) +} + +func (tb *TestBlockStore) BlockProposal() (*types.BlockProposal, error) { + blockProposalFunc := tb.BlockProposalFunc + if blockProposalFunc == nil { + panic("BlockProposalFunc method is not set") + } + return blockProposalFunc() +} + +func (tb *TestBlockStore) CommitBlockProposal(bp *types.BlockProposal) error { + commitBlockProposalFunc := tb.CommitBlockProposalFunc + if commitBlockProposalFunc == nil { + panic("CommitBlockProposalFunc method is not set") + } + return commitBlockProposalFunc(bp) +} + +func (tb *TestBlockStore) LatestBlock() (*types.Block, error) { + latestBlockFunc := tb.LatestBlockFunc + if latestBlockFunc == nil { + panic("LatestBlockFunc method is not set") + } + return latestBlockFunc() +} + +func (tb *TestBlockStore) StageBlockProposal(bp *types.BlockProposal) { + stageBlockProposalFunc := tb.StageBlockProposalFunc + if stageBlockProposalFunc == nil { + panic("StageBlockProposalFunc method is not set") + } + stageBlockProposalFunc(bp) +} diff --git a/fvm/evm/testutils/handler.go b/fvm/evm/testutils/handler.go index 2b1301e1f0e..d266bb57e49 100644 --- a/fvm/evm/testutils/handler.go +++ b/fvm/evm/testutils/handler.go @@ -3,6 +3,7 @@ package testutils import ( "github.com/onflow/cadence/common" + "github.com/onflow/flow-go/fvm/evm/backends" "github.com/onflow/flow-go/fvm/evm/emulator" "github.com/onflow/flow-go/fvm/evm/handler" "github.com/onflow/flow-go/fvm/evm/precompiles" @@ -13,7 +14,7 @@ import ( func SetupHandler( chainID flow.ChainID, - backend types.Backend, + backend backends.Backend, rootAddr flow.Address, ) *handler.ContractHandler { return handler.NewContractHandler( @@ -21,7 +22,6 @@ func SetupHandler( rootAddr, common.Address(systemcontracts.SystemContractsForChain(chainID).FlowToken.Address), rootAddr, - handler.NewBlockStore(chainID, backend, rootAddr), handler.NewAddressAllocator(), backend, emulator.NewEmulator(backend, rootAddr), diff --git a/fvm/evm/testutils/offchain.go b/fvm/evm/testutils/offchain.go index 5a5e675798a..ecd2b97b00d 100644 --- a/fvm/evm/testutils/offchain.go +++ b/fvm/evm/testutils/offchain.go @@ -3,6 +3,7 @@ package testutils import ( "fmt" + "github.com/onflow/flow-go/fvm/evm/backends" "github.com/onflow/flow-go/fvm/evm/offchain/storage" "github.com/onflow/flow-go/fvm/evm/types" ) @@ -11,7 +12,7 @@ import ( // storage provider that only provides // storage for an specific height type TestStorageProvider struct { - storage types.BackendStorage + storage backends.BackendStorage height uint64 } diff --git a/fvm/evm/types/handler.go b/fvm/evm/types/handler.go index 144d44f12a7..b93aece4a70 100644 --- a/fvm/evm/types/handler.go +++ b/fvm/evm/types/handler.go @@ -90,21 +90,3 @@ type AddressAllocator interface { // AllocateAddress allocates an address by index to be used by a precompile contract AllocatePrecompileAddress(index uint64) Address } - -// BlockStore stores the chain of blocks -type BlockStore interface { - // LatestBlock returns the latest appended block - LatestBlock() (*Block, error) - - // BlockHash returns the hash of the block at the given height - BlockHash(height uint64) (gethCommon.Hash, error) - - // BlockProposal returns the active block proposal - BlockProposal() (*BlockProposal, error) - - // UpdateBlockProposal replaces the current block proposal with the ones passed - UpdateBlockProposal(*BlockProposal) error - - // CommitBlockProposal commits the block proposal and update the chain of blocks - CommitBlockProposal(*BlockProposal) error -} diff --git a/fvm/fvm_test.go b/fvm/fvm_test.go index 0021e338232..ce2e11e6d6f 100644 --- a/fvm/fvm_test.go +++ b/fvm/fvm_test.go @@ -44,7 +44,6 @@ import ( envMock "github.com/onflow/flow-go/fvm/environment/mock" "github.com/onflow/flow-go/fvm/errors" "github.com/onflow/flow-go/fvm/evm/events" - "github.com/onflow/flow-go/fvm/evm/handler" "github.com/onflow/flow-go/fvm/evm/stdlib" "github.com/onflow/flow-go/fvm/evm/types" "github.com/onflow/flow-go/fvm/meter" @@ -4240,7 +4239,7 @@ func Test_BlockHashListShouldWriteOnPush(t *testing.T) { chain := flow.Emulator.Chain() sc := systemcontracts.SystemContractsForChain(chain.ChainID()) - push := func(bhl *handler.BlockHashList, height uint64) { + push := func(bhl *environment.BlockHashList, height uint64) { buffer := make([]byte, 32) pos := 0 @@ -4275,7 +4274,7 @@ func Test_BlockHashListShouldWriteOnPush(t *testing.T) { accounts, ) - bhl, err := handler.NewBlockHashList(valueStore, sc.EVMStorage.Address, capacity) + bhl, err := environment.NewBlockHashList(valueStore, sc.EVMStorage.Address, capacity) require.NoError(t, err) // fill the block hash list @@ -4300,7 +4299,7 @@ func Test_BlockHashListShouldWriteOnPush(t *testing.T) { accounts, ) - bhl, err = handler.NewBlockHashList(valueStore, sc.EVMStorage.Address, capacity) + bhl, err = environment.NewBlockHashList(valueStore, sc.EVMStorage.Address, capacity) require.NoError(t, err) // after we push the changes should be applied and the first block hash in the bucket should be capacity+1 instead of 0 diff --git a/fvm/runtime/cadence_function_declarations.go b/fvm/runtime/cadence_function_declarations.go index 636f923dfba..30f1de62cda 100644 --- a/fvm/runtime/cadence_function_declarations.go +++ b/fvm/runtime/cadence_function_declarations.go @@ -93,7 +93,6 @@ func EVMInternalEVMContractValue(chainID flow.ChainID, fvmEnv environment.Enviro evmBackend := backends.NewWrappedEnvironment(fvmEnv) evmEmulator := emulator.NewEmulator(evmBackend, evm.StorageAccountAddress(chainID)) - blockStore := handler.NewBlockStore(chainID, evmBackend, evm.StorageAccountAddress(chainID)) addressAllocator := handler.NewAddressAllocator() evmContractAddress := evm.ContractAccountAddress(chainID) @@ -103,7 +102,6 @@ func EVMInternalEVMContractValue(chainID flow.ChainID, fvmEnv environment.Enviro evmContractAddress, common.Address(flowTokenAddress), randomBeaconAddress, - blockStore, addressAllocator, evmBackend, evmEmulator,