From 25f11a7e61ad5ad04776a621698f038b75a462ac Mon Sep 17 00:00:00 2001 From: Devon Bear Date: Wed, 1 Nov 2023 14:14:51 -0400 Subject: [PATCH 01/15] bing bong --- cosmos/x/evm/keeper/abci.go | 4 +- cosmos/x/evm/keeper/genesis.go | 5 +- cosmos/x/evm/keeper/keeper.go | 7 +- eth/core/chain.go | 19 ++-- eth/core/chain_reader.go | 1 - eth/core/chain_resources.go | 21 ++++ eth/core/chain_writer.go | 196 ++++++++++++++++++++------------- eth/polar/api_backend.go | 1 - 8 files changed, 156 insertions(+), 98 deletions(-) diff --git a/cosmos/x/evm/keeper/abci.go b/cosmos/x/evm/keeper/abci.go index 811cc4ba5..3b75d036d 100644 --- a/cosmos/x/evm/keeper/abci.go +++ b/cosmos/x/evm/keeper/abci.go @@ -35,7 +35,9 @@ func (k *Keeper) Precommit(ctx context.Context) error { block := k.chain.GetBlockByNumber(blockNum) if block == nil { panic( - fmt.Sprintf("EVM BLOCK FAILURE AT BLOCK %d", blockNum), + fmt.Sprintf( + "EVM BLOCK %d FAILED TO PROCESS - hash: %s", blockNum, block.Hash(), + ), ) } else if block.NumberU64() != blockNum { panic( diff --git a/cosmos/x/evm/keeper/genesis.go b/cosmos/x/evm/keeper/genesis.go index 1928a5205..0ebdb80a4 100644 --- a/cosmos/x/evm/keeper/genesis.go +++ b/cosmos/x/evm/keeper/genesis.go @@ -44,9 +44,8 @@ func (k *Keeper) InitGenesis(ctx sdk.Context, genState *core.Genesis) error { } // Insert to chain. - k.chain. - PreparePlugins(ctx.WithEventManager(sdk.NewEventManager())) - return k.chain.InsertBlockWithoutSetHead(genState.ToBlock()) + k.chain.PreparePlugins(ctx.WithEventManager(sdk.NewEventManager())) + return k.chain.InsertGenesisBlock(genState.ToBlock()) } // ExportGenesis returns the exported genesis state. diff --git a/cosmos/x/evm/keeper/keeper.go b/cosmos/x/evm/keeper/keeper.go index ec6024841..31843bd91 100644 --- a/cosmos/x/evm/keeper/keeper.go +++ b/cosmos/x/evm/keeper/keeper.go @@ -31,16 +31,17 @@ import ( "pkg.berachain.dev/polaris/cosmos/config" "pkg.berachain.dev/polaris/cosmos/x/evm/plugins/state" "pkg.berachain.dev/polaris/cosmos/x/evm/types" - "pkg.berachain.dev/polaris/eth/core" ethprecompile "pkg.berachain.dev/polaris/eth/core/precompile" + coretypes "pkg.berachain.dev/polaris/eth/core/types" "pkg.berachain.dev/polaris/eth/params" ) type Blockchain interface { PreparePlugins(context.Context) Config() *params.ChainConfig - core.ChainWriter - core.ChainReader + InsertGenesisBlock(*coretypes.Block) error + InsertBlockWithoutSetHead(*coretypes.Block) error + GetBlockByNumber(uint64) *coretypes.Block } type Keeper struct { diff --git a/eth/core/chain.go b/eth/core/chain.go index f419e17ff..9ead12d5a 100644 --- a/eth/core/chain.go +++ b/eth/core/chain.go @@ -66,6 +66,7 @@ type blockchain struct { engine consensus.Engine processor core.Processor + validator core.Validator // statedb is the state database that is used to mange state during transactions. statedb state.StateDB @@ -81,8 +82,6 @@ type blockchain struct { finalizedBlock atomic.Pointer[types.Block] // currentReceipts is the current/pending receipts. currentReceipts atomic.Value - // currentLogs is the current/pending logs. - currentLogs atomic.Value // receiptsCache is a cache of the receipts for the last `defaultCacheSizeBytes` bytes of // blocks. blockHash -> receipts @@ -98,14 +97,13 @@ type blockchain struct { txLookupCache *lru.Cache[common.Hash, *types.TxLookupEntry] // subscription event feeds - scope event.SubscriptionScope - chainFeed event.Feed - chainHeadFeed event.Feed - logsFeed event.Feed - pendingLogsFeed event.Feed - rmLogsFeed event.Feed // currently never used - chainSideFeed event.Feed // currently never used - logger log.Logger + scope event.SubscriptionScope + chainFeed event.Feed + chainHeadFeed event.Feed + logsFeed event.Feed + rmLogsFeed event.Feed // currently never used + chainSideFeed event.Feed // currently never used + logger log.Logger } // ========================================================================= @@ -134,6 +132,7 @@ func NewChain( } bc.statedb = state.NewStateDB(bc.sp, bc.pp) bc.processor = core.NewStateProcessor(bc.config, bc, bc.engine) + bc.validator = core.NewBlockValidator(bc.config, bc, bc.engine) // TODO: hmm... bc.currentBlock.Store( types.NewBlock(&types.Header{Time: 0, Number: big.NewInt(0), diff --git a/eth/core/chain_reader.go b/eth/core/chain_reader.go index cccaba8d8..b284e0076 100644 --- a/eth/core/chain_reader.go +++ b/eth/core/chain_reader.go @@ -97,7 +97,6 @@ func (bc *blockchain) CurrentFinalBlock() *types.Header { // CurrentSafeBlock retrieves the current safe block of the canonical // chain. The block is retrieved from the blockchain's internal cache. func (bc *blockchain) CurrentSafeBlock() *types.Header { - // TODO: determine the difference between safe and final in polaris. return bc.CurrentFinalBlock() } diff --git a/eth/core/chain_resources.go b/eth/core/chain_resources.go index 95b67cb42..cd14c21ed 100644 --- a/eth/core/chain_resources.go +++ b/eth/core/chain_resources.go @@ -54,6 +54,27 @@ func (bc *blockchain) StateAtBlockNumber(number uint64) (state.StateDB, error) { return state.NewStateDB(sp, bc.pp), nil } +// HasBlockAndState checks if the blockchain has a block and its state at +// a given hash and number. +func (bc *blockchain) HasBlockAndState(hash common.Hash, number uint64) bool { + // Check for State. + if sdb, err := bc.StateAt(hash); sdb == nil || err == nil { + sdb, err = bc.StateAtBlockNumber(number) + if sdb == nil || err != nil { + return false + } + } + + // Check for Block. + if block := bc.GetBlockByNumber(number); block == nil { + block = bc.GetBlockByHash(hash) + if block == nil { + return false + } + } + return true +} + // GetVMConfig returns the vm.Config for the current chain. func (bc *blockchain) GetVMConfig() *vm.Config { return bc.vmConfig diff --git a/eth/core/chain_writer.go b/eth/core/chain_writer.go index e96ae7b97..a13e270f0 100644 --- a/eth/core/chain_writer.go +++ b/eth/core/chain_writer.go @@ -22,83 +22,157 @@ package core import ( "context" - "time" + "errors" "github.com/ethereum/go-ethereum/core" "pkg.berachain.dev/polaris/eth/core/state" "pkg.berachain.dev/polaris/eth/core/types" + "pkg.berachain.dev/polaris/eth/log" ) // ChainWriter defines methods that are used to perform state and block transitions. type ChainWriter interface { LoadLastState(context.Context, uint64) error - InsertBlock(block *types.Block, receipts types.Receipts, logs []*types.Log) error + InsertGenesisBlock(block *types.Block) error InsertBlockWithoutSetHead(block *types.Block) error WriteBlockAndSetHead(block *types.Block, receipts []*types.Receipt, logs []*types.Log, state state.StateDB, emitHeadEvent bool) (status core.WriteStatus, err error) } -// WriteBlockAndSetHead is a no-op in the current implementation. Potentially usable later. -func (*blockchain) WriteBlockAndSetHead( - _ *types.Block, _ []*types.Receipt, _ []*types.Log, _ state.StateDB, - _ bool) (core.WriteStatus, error) { - return core.NonStatTy, nil +// InsertGenesisBlock inserts the genesis block into the blockchain. +func (bc *blockchain) InsertGenesisBlock(block *types.Block) error { + // TODO: add more validation here. + if block.NumberU64() != 0 { + return errors.New("not the genesis block") + } + return bc.InsertBlockWithoutSetHead(block) } +// InsertBlockWithoutSetHead inserts a block into the blockchain without setting the head. +// For now, it is a huge lie. It does infact set the head. func (bc *blockchain) InsertBlockWithoutSetHead(block *types.Block) error { - // Retrieve the parent block and it's state to execute on top - // parent := bc.GetBlock(block.ParentHash(), block.NumberU64()-1) - // if parent == nil { - // return fmt.Errorf("parent block not found") - // } - - // Process block using the parent state as reference point - pstart := time.Now() + // Validate that we are about to insert a valid block. + if block.NumberU64() > 0 { + if err := bc.validator.ValidateBody(block); err != nil { + log.Error("invalid block body", "err", err) + return err + } + } + + // Process the incoming EVM block. receipts, logs, _, err := bc.processor.Process(block, bc.statedb, *bc.vmConfig) if err != nil { return err } - ptime := time.Since(pstart) - bc.logger.Info("processed block in", "time", ptime) - return bc.InsertBlock(block, receipts, logs) -} -// InsertBlock inserts a block into the canonical chain and updates the state of the blockchain. -func (bc *blockchain) InsertBlock( - block *types.Block, - receipts types.Receipts, - logs []*types.Log, -) error { - var err error - if _, err = bc.statedb.Commit( - block.NumberU64(), - bc.config.IsEIP158(block.Header().Number), - ); err != nil { + // We can just immediately finalize the block. It's okay in this context. + var status core.WriteStatus + if status, err = bc.WriteBlockAndSetHead( + block, receipts, logs, nil, true); err != nil { return err } - // TODO: prepare historical plugin here? - // TBH still think we should deprecate it and run in another routine as indexer. + // todo use status for something + _ = status - // ***************************************** // - // TODO: add safety check for canonicallness // - // ***************************************** // + return err +} - // *********************************************** // - // TODO: restructure this function / flow it sucks // - // *********************************************** // - blockHash, blockNum := block.Hash(), block.Number().Uint64() - bc.logger.Info( - "finalizing evm block", "hash", blockHash.Hex(), "num_txs", len(receipts)) +// SetHeadAndFinalize sets the head of the blockchain to the given block and finalizes the block. +func (bc *blockchain) WriteBlockAndSetHead( + block *types.Block, receipts []*types.Receipt, logs []*types.Log, + _ state.StateDB, emitHeadEvent bool, +) (core.WriteStatus, error) { + // Write the block to the store. + if err := bc.writeBlockWithState(block, receipts); err != nil { + return core.NonStatTy, err + } + currentBlock := bc.currentBlock.Load() + + // We need to error if the parent is not the head block. + if block.NumberU64() > 0 && block.ParentHash() != currentBlock.Hash() { + log.Error("canonical chain broken", + "block-number", block.NumberU64(), "block-hash", block.ParentHash().Hex()) + return core.NonStatTy, errors.New("canonical chain broken") + } + + // Set the current block. + bc.currentBlock.Store(block) + + // TODO: this is fine to do here but not really semantically correct + // and is very confusing. + // For clarity reasons, we should make the cosmos chain make a separate call + // to finalize the block. + bc.finalizedBlock.Store(block) + + // Write the receipts cache. + // TODO deprecate this cache? + if receipts != nil { + bc.currentReceipts.Store(receipts) + bc.receiptsCache.Add(block.Hash(), receipts) + } + + // Fire off the feeds. + bc.chainFeed.Send(ChainEvent{Block: block, Hash: block.Hash(), Logs: logs}) + if len(logs) > 0 { + bc.logsFeed.Send(logs) + } + + // In theory, we should fire a ChainHeadEvent when we inject + // a canonical block, but sometimes we can insert a batch of + // canonical blocks. Avoid firing too many ChainHeadEvents, + // we will fire an accumulated ChainHeadEvent and disable fire + // event here. + if emitHeadEvent { + bc.chainHeadFeed.Send(ChainHeadEvent{Block: block}) + } + + return core.CanonStatTy, nil +} - // store the block header on the host chain - err = bc.bp.StoreHeader(block.Header()) +// writeBlockWithState writes the block along with its state (receipts and logs) +// into the blockchain. +func (bc *blockchain) writeBlockWithState( + block *types.Block, receipts []*types.Receipt, +) error { + // In Polaris since we are using single block finality. + // Finalized == Current == Safe. All are the same. + // Store the header as well as update all the finalized stuff. + err := bc.bp.StoreHeader(block.Header()) if err != nil { bc.logger.Error("failed to store block header", "err", err) return err } + // Irrelevant of the canonical status, write the block itself to the database. + // TODO THIS NEEDS TO WRITE TO EXTERNAL DB. + if err = bc.writeHistoricalData(block, receipts); err != nil { + return err + } + + // Commit all cached state changes into underlying memory database. + // In Polaris this is a no-op. + _, err = bc.statedb.Commit(block.NumberU64(), bc.config.IsEIP158(block.Number())) + if err != nil { + return err + } + + bc.logger.Info( + "finalizing evm block", "hash", block.Hash().Hex(), "num_txs", len(receipts)) + + return nil +} + +// InsertBlock inserts a block into the canonical chain and updates the state of the blockchain. +// TODO: WRITE TO EXTERNAL STORE +func (bc *blockchain) writeHistoricalData( + block *types.Block, + receipts types.Receipts, +) error { + var err error + blockHash, blockNum := block.Hash(), block.Number().Uint64() + // store the block, receipts, and txs on the host chain if historical plugin is supported if bc.hp != nil { if err = bc.hp.StoreBlock(block); err != nil { @@ -115,41 +189,5 @@ func (bc *blockchain) InsertBlock( } } - // mark the current block, receipts, and logs - if block != nil { - bc.currentBlock.Store(block) - bc.finalizedBlock.Store(block) - - bc.blockNumCache.Add(blockNum, block) - bc.blockHashCache.Add(blockHash, block) - - for txIndex, tx := range block.Transactions() { - bc.txLookupCache.Add( - tx.Hash(), - &types.TxLookupEntry{ - Tx: tx, - TxIndex: uint64(txIndex), - BlockNum: blockNum, - BlockHash: blockHash, - }, - ) - } - } - if receipts != nil { - bc.currentReceipts.Store(receipts) - bc.receiptsCache.Add(blockHash, receipts) - } - if logs != nil { - bc.pendingLogsFeed.Send(logs) - bc.currentLogs.Store(logs) - if len(logs) > 0 { - bc.logsFeed.Send(logs) - } - } - - // Send chain events. - bc.chainFeed.Send(ChainEvent{Block: block, Hash: blockHash, Logs: logs}) - bc.chainHeadFeed.Send(ChainHeadEvent{Block: block}) - return nil } diff --git a/eth/polar/api_backend.go b/eth/polar/api_backend.go index 97ae4a33c..5d55dc253 100644 --- a/eth/polar/api_backend.go +++ b/eth/polar/api_backend.go @@ -556,7 +556,6 @@ func (b *backend) SubscribeRemovedLogsEvent(ch chan<- core.RemovedLogsEvent) eve } func (b *backend) SubscribeLogsEvent(ch chan<- []*types.Log) event.Subscription { - b.logger.Debug("called eth.rpc.backend.SubscribeLogsEvent", "ch", ch) return b.polar.blockchain.SubscribeLogsEvent(ch) } From 7fa779469ff03ea63c1cfad34c05b1c44b0c17f8 Mon Sep 17 00:00:00 2001 From: Devon Bear Date: Wed, 1 Nov 2023 14:20:14 -0400 Subject: [PATCH 02/15] bing bong --- eth/core/chain_writer.go | 27 +++++++++++++++------------ 1 file changed, 15 insertions(+), 12 deletions(-) diff --git a/eth/core/chain_writer.go b/eth/core/chain_writer.go index a13e270f0..e2f2ae1fe 100644 --- a/eth/core/chain_writer.go +++ b/eth/core/chain_writer.go @@ -46,36 +46,39 @@ func (bc *blockchain) InsertGenesisBlock(block *types.Block) error { if block.NumberU64() != 0 { return errors.New("not the genesis block") } - return bc.InsertBlockWithoutSetHead(block) + _, err := bc.WriteBlockAndSetHead(block, nil, nil, nil, true) + return err } // InsertBlockWithoutSetHead inserts a block into the blockchain without setting the head. // For now, it is a huge lie. It does infact set the head. func (bc *blockchain) InsertBlockWithoutSetHead(block *types.Block) error { // Validate that we are about to insert a valid block. - if block.NumberU64() > 0 { - if err := bc.validator.ValidateBody(block); err != nil { - log.Error("invalid block body", "err", err) - return err - } + if err := bc.validator.ValidateBody(block); err != nil { + log.Error("invalid block body", "err", err) + return err } // Process the incoming EVM block. - receipts, logs, _, err := bc.processor.Process(block, bc.statedb, *bc.vmConfig) + receipts, logs, usedGas, err := bc.processor.Process(block, bc.statedb, *bc.vmConfig) if err != nil { + log.Error("failed to process block", "num", block.NumberU64()) + return err + } + + // ValidateState validates the statedb post block processing. + if err = bc.validator.ValidateState(block, bc.statedb, receipts, usedGas); err != nil { + log.Error("invalid state after processing block", "num", block.NumberU64()) return err } // We can just immediately finalize the block. It's okay in this context. - var status core.WriteStatus - if status, err = bc.WriteBlockAndSetHead( + if _, err = bc.WriteBlockAndSetHead( block, receipts, logs, nil, true); err != nil { + log.Error("failed to write block", "num", block.NumberU64()) return err } - // todo use status for something - _ = status - return err } From c7a36fc60780990ea3af57b0f520dc100690498b Mon Sep 17 00:00:00 2001 From: Devon Bear Date: Wed, 1 Nov 2023 14:22:43 -0400 Subject: [PATCH 03/15] bing bong --- cosmos/x/evm/keeper/keeper.go | 2 +- cosmos/x/evm/keeper/processor.go | 2 +- eth/core/chain_writer.go | 6 +++--- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/cosmos/x/evm/keeper/keeper.go b/cosmos/x/evm/keeper/keeper.go index 31843bd91..5d58e04e2 100644 --- a/cosmos/x/evm/keeper/keeper.go +++ b/cosmos/x/evm/keeper/keeper.go @@ -40,7 +40,7 @@ type Blockchain interface { PreparePlugins(context.Context) Config() *params.ChainConfig InsertGenesisBlock(*coretypes.Block) error - InsertBlockWithoutSetHead(*coretypes.Block) error + InsertBlockAndSetHead(*coretypes.Block) error GetBlockByNumber(uint64) *coretypes.Block } diff --git a/cosmos/x/evm/keeper/processor.go b/cosmos/x/evm/keeper/processor.go index da949bc79..38d3037ac 100644 --- a/cosmos/x/evm/keeper/processor.go +++ b/cosmos/x/evm/keeper/processor.go @@ -61,7 +61,7 @@ func (k *Keeper) ProcessPayloadEnvelope( // Prepare should be moved to the blockchain? THIS IS VERY HOOD YES NEEDS TO BE MOVED. k.chain.PreparePlugins(ctx) - if err = k.chain.InsertBlockWithoutSetHead(block); err != nil { + if err = k.chain.InsertBlockAndSetHead(block); err != nil { return nil, err } diff --git a/eth/core/chain_writer.go b/eth/core/chain_writer.go index e2f2ae1fe..0a36f9edf 100644 --- a/eth/core/chain_writer.go +++ b/eth/core/chain_writer.go @@ -35,7 +35,7 @@ import ( type ChainWriter interface { LoadLastState(context.Context, uint64) error InsertGenesisBlock(block *types.Block) error - InsertBlockWithoutSetHead(block *types.Block) error + InsertBlockAndSetHead(block *types.Block) error WriteBlockAndSetHead(block *types.Block, receipts []*types.Receipt, logs []*types.Log, state state.StateDB, emitHeadEvent bool) (status core.WriteStatus, err error) } @@ -50,9 +50,9 @@ func (bc *blockchain) InsertGenesisBlock(block *types.Block) error { return err } -// InsertBlockWithoutSetHead inserts a block into the blockchain without setting the head. +// InsertBlockAndSetHead inserts a block into the blockchain without setting the head. // For now, it is a huge lie. It does infact set the head. -func (bc *blockchain) InsertBlockWithoutSetHead(block *types.Block) error { +func (bc *blockchain) InsertBlockAndSetHead(block *types.Block) error { // Validate that we are about to insert a valid block. if err := bc.validator.ValidateBody(block); err != nil { log.Error("invalid block body", "err", err) From f31c9dc414414e6099810537bcbe32fe8c31b504 Mon Sep 17 00:00:00 2001 From: Devon Bear Date: Wed, 1 Nov 2023 14:25:25 -0400 Subject: [PATCH 04/15] bing bong --- cosmos/x/evm/keeper/genesis.go | 2 +- cosmos/x/evm/keeper/keeper.go | 2 +- eth/core/chain_writer.go | 6 +++--- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/cosmos/x/evm/keeper/genesis.go b/cosmos/x/evm/keeper/genesis.go index 0ebdb80a4..22816847e 100644 --- a/cosmos/x/evm/keeper/genesis.go +++ b/cosmos/x/evm/keeper/genesis.go @@ -45,7 +45,7 @@ func (k *Keeper) InitGenesis(ctx sdk.Context, genState *core.Genesis) error { // Insert to chain. k.chain.PreparePlugins(ctx.WithEventManager(sdk.NewEventManager())) - return k.chain.InsertGenesisBlock(genState.ToBlock()) + return k.chain.WriteGenesisBlock(genState.ToBlock()) } // ExportGenesis returns the exported genesis state. diff --git a/cosmos/x/evm/keeper/keeper.go b/cosmos/x/evm/keeper/keeper.go index 5d58e04e2..f1bd832d3 100644 --- a/cosmos/x/evm/keeper/keeper.go +++ b/cosmos/x/evm/keeper/keeper.go @@ -39,7 +39,7 @@ import ( type Blockchain interface { PreparePlugins(context.Context) Config() *params.ChainConfig - InsertGenesisBlock(*coretypes.Block) error + WriteGenesisBlock(*coretypes.Block) error InsertBlockAndSetHead(*coretypes.Block) error GetBlockByNumber(uint64) *coretypes.Block } diff --git a/eth/core/chain_writer.go b/eth/core/chain_writer.go index 0a36f9edf..2b59697f4 100644 --- a/eth/core/chain_writer.go +++ b/eth/core/chain_writer.go @@ -34,14 +34,14 @@ import ( // ChainWriter defines methods that are used to perform state and block transitions. type ChainWriter interface { LoadLastState(context.Context, uint64) error - InsertGenesisBlock(block *types.Block) error + WriteGenesisBlock(block *types.Block) error InsertBlockAndSetHead(block *types.Block) error WriteBlockAndSetHead(block *types.Block, receipts []*types.Receipt, logs []*types.Log, state state.StateDB, emitHeadEvent bool) (status core.WriteStatus, err error) } -// InsertGenesisBlock inserts the genesis block into the blockchain. -func (bc *blockchain) InsertGenesisBlock(block *types.Block) error { +// WriteGenesisBlock inserts the genesis block into the blockchain. +func (bc *blockchain) WriteGenesisBlock(block *types.Block) error { // TODO: add more validation here. if block.NumberU64() != 0 { return errors.New("not the genesis block") From 9544b606f19dccd242a0c6a5b83a5ff28950bec5 Mon Sep 17 00:00:00 2001 From: Devon Bear Date: Wed, 1 Nov 2023 14:30:02 -0400 Subject: [PATCH 05/15] remove unused cache --- eth/core/chain.go | 2 -- eth/core/chain_writer.go | 1 - 2 files changed, 3 deletions(-) diff --git a/eth/core/chain.go b/eth/core/chain.go index 9ead12d5a..53fcf2816 100644 --- a/eth/core/chain.go +++ b/eth/core/chain.go @@ -80,8 +80,6 @@ type blockchain struct { currentBlock atomic.Pointer[types.Block] // finalizedBlock is the finalized/latest block. finalizedBlock atomic.Pointer[types.Block] - // currentReceipts is the current/pending receipts. - currentReceipts atomic.Value // receiptsCache is a cache of the receipts for the last `defaultCacheSizeBytes` bytes of // blocks. blockHash -> receipts diff --git a/eth/core/chain_writer.go b/eth/core/chain_writer.go index 2b59697f4..6edb47b09 100644 --- a/eth/core/chain_writer.go +++ b/eth/core/chain_writer.go @@ -112,7 +112,6 @@ func (bc *blockchain) WriteBlockAndSetHead( // Write the receipts cache. // TODO deprecate this cache? if receipts != nil { - bc.currentReceipts.Store(receipts) bc.receiptsCache.Add(block.Hash(), receipts) } From 5f24220ffa6d4d3cfd00b864696597069ab76cef Mon Sep 17 00:00:00 2001 From: Devon Bear Date: Wed, 1 Nov 2023 14:30:25 -0400 Subject: [PATCH 06/15] reee --- eth/core/chain_writer.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/eth/core/chain_writer.go b/eth/core/chain_writer.go index 6edb47b09..74657e077 100644 --- a/eth/core/chain_writer.go +++ b/eth/core/chain_writer.go @@ -82,7 +82,7 @@ func (bc *blockchain) InsertBlockAndSetHead(block *types.Block) error { return err } -// SetHeadAndFinalize sets the head of the blockchain to the given block and finalizes the block. +// WriteBlockAndSetHead sets the head of the blockchain to the given block and finalizes the block. func (bc *blockchain) WriteBlockAndSetHead( block *types.Block, receipts []*types.Receipt, logs []*types.Log, _ state.StateDB, emitHeadEvent bool, From 13594da0fa129e1a111a1ed1b4a83defaaf2f433 Mon Sep 17 00:00:00 2001 From: Devon Bear Date: Wed, 1 Nov 2023 14:34:02 -0400 Subject: [PATCH 07/15] remove unused cache --- eth/core/chain_writer.go | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/eth/core/chain_writer.go b/eth/core/chain_writer.go index 74657e077..47eedf87f 100644 --- a/eth/core/chain_writer.go +++ b/eth/core/chain_writer.go @@ -109,10 +109,21 @@ func (bc *blockchain) WriteBlockAndSetHead( // to finalize the block. bc.finalizedBlock.Store(block) - // Write the receipts cache. - // TODO deprecate this cache? - if receipts != nil { - bc.receiptsCache.Add(block.Hash(), receipts) + // Store txLookup entries for all transactions in the block. + blockNum := block.NumberU64() + blockHash := block.Hash() + bc.blockNumCache.Add(blockNum, block) + bc.blockHashCache.Add(blockHash, block) + for txIndex, tx := range block.Transactions() { + bc.txLookupCache.Add( + tx.Hash(), + &types.TxLookupEntry{ + Tx: tx, + TxIndex: uint64(txIndex), + BlockNum: blockNum, + BlockHash: blockHash, + }, + ) } // Fire off the feeds. From cb37dfa5050c8a56681d7cad8b3c0eedb4cf5f58 Mon Sep 17 00:00:00 2001 From: Devon Bear Date: Wed, 1 Nov 2023 14:36:50 -0400 Subject: [PATCH 08/15] keep receipts cache --- eth/core/chain_writer.go | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/eth/core/chain_writer.go b/eth/core/chain_writer.go index 47eedf87f..dc5459bfe 100644 --- a/eth/core/chain_writer.go +++ b/eth/core/chain_writer.go @@ -126,6 +126,12 @@ func (bc *blockchain) WriteBlockAndSetHead( ) } + // Write the receipts cache. + // TODO deprecate this cache? + if receipts != nil { + bc.receiptsCache.Add(block.Hash(), receipts) + } + // Fire off the feeds. bc.chainFeed.Send(ChainEvent{Block: block, Hash: block.Hash(), Logs: logs}) if len(logs) > 0 { From 4a1b524e56551f60aa02aefd6d7b35ce5f29dda9 Mon Sep 17 00:00:00 2001 From: Devon Bear Date: Wed, 1 Nov 2023 14:47:53 -0400 Subject: [PATCH 09/15] remove unused cache --- eth/core/chain_writer.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/eth/core/chain_writer.go b/eth/core/chain_writer.go index dc5459bfe..acf772129 100644 --- a/eth/core/chain_writer.go +++ b/eth/core/chain_writer.go @@ -62,20 +62,20 @@ func (bc *blockchain) InsertBlockAndSetHead(block *types.Block) error { // Process the incoming EVM block. receipts, logs, usedGas, err := bc.processor.Process(block, bc.statedb, *bc.vmConfig) if err != nil { - log.Error("failed to process block", "num", block.NumberU64()) + log.Error("failed to process block", "num", block.NumberU64(), "err", err) return err } // ValidateState validates the statedb post block processing. if err = bc.validator.ValidateState(block, bc.statedb, receipts, usedGas); err != nil { - log.Error("invalid state after processing block", "num", block.NumberU64()) + log.Error("invalid state after processing block", "num", block.NumberU64(), "err", err) return err } // We can just immediately finalize the block. It's okay in this context. if _, err = bc.WriteBlockAndSetHead( block, receipts, logs, nil, true); err != nil { - log.Error("failed to write block", "num", block.NumberU64()) + log.Error("failed to write block", "num", block.NumberU64(), "err", err) return err } From 57ff73912ae7fa0e4b0a9f803cceb1c0012fe02f Mon Sep 17 00:00:00 2001 From: Devon Bear Date: Wed, 1 Nov 2023 15:47:13 -0400 Subject: [PATCH 10/15] run begin blocker in miner --- cosmos/miner/miner.go | 23 ++++++++++++++++++- cosmos/runtime/runtime.go | 11 +++++---- cosmos/x/evm/keeper/abci.go | 2 +- .../distribution/distribution_test.go | 4 ++-- e2e/testapp/app.go | 2 +- eth/core/chain_writer.go | 8 ++++--- 6 files changed, 38 insertions(+), 12 deletions(-) diff --git a/cosmos/miner/miner.go b/cosmos/miner/miner.go index 39a6ab583..de78cf390 100644 --- a/cosmos/miner/miner.go +++ b/cosmos/miner/miner.go @@ -32,6 +32,7 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/miner" + evmkeeper "pkg.berachain.dev/polaris/cosmos/x/evm/keeper" "pkg.berachain.dev/polaris/eth" "pkg.berachain.dev/polaris/eth/core/types" ) @@ -45,17 +46,30 @@ type EnvelopeSerializer interface { ToSdkTxBytes(*engine.ExecutionPayloadEnvelope, uint64) ([]byte, error) } +type App interface { + BeginBlocker(ctx sdk.Context) (sdk.BeginBlock, error) +} + +// EVMKeeper is an interface that defines the methods needed for the EVM setup. +type EVMKeeper interface { + // Setup initializes the EVM keeper. + Setup(evmkeeper.Blockchain) error + PrepareCheckState(context.Context) error +} + // Miner implements the baseapp.TxSelector interface. type Miner struct { eth.Miner + app App serializer EnvelopeSerializer currentPayload *miner.Payload } // New produces a cosmos miner from a geth miner. -func New(gm eth.Miner) *Miner { +func New(gm eth.Miner, app App) *Miner { return &Miner{ Miner: gm, + app: app, } } @@ -70,6 +84,13 @@ func (m *Miner) PrepareProposal( ) (*abci.ResponsePrepareProposal, error) { var payloadEnvelopeBz []byte var err error + + // We have to run the BeginBlocker to get the chain into the state it'll + // be in when the EVM transaction actually runs. + if _, err = m.app.BeginBlocker(ctx); err != nil { + return nil, err + } + if payloadEnvelopeBz, err = m.buildBlock(ctx); err != nil { return nil, err } diff --git a/cosmos/runtime/runtime.go b/cosmos/runtime/runtime.go index 324386800..3cfb2683b 100644 --- a/cosmos/runtime/runtime.go +++ b/cosmos/runtime/runtime.go @@ -21,6 +21,7 @@ package runtime import ( + "context" "time" "cosmossdk.io/log" @@ -50,6 +51,7 @@ import ( type EVMKeeper interface { // Setup initializes the EVM keeper. Setup(evmkeeper.Blockchain) error + PrepareCheckState(context.Context) error } // CosmosApp is an interface that defines the methods needed for the Cosmos setup. @@ -57,6 +59,7 @@ type CosmosApp interface { SetPrepareProposal(sdk.PrepareProposalHandler) SetMempool(mempool.Mempool) SetAnteHandler(sdk.AnteHandler) + miner.App } // Polaris is a struct that wraps the Polaris struct from the polar package. @@ -92,10 +95,6 @@ func New( panic(err) } - // Wrap the geth miner and txpool with the cosmos miner and txpool. - p.WrappedTxPool = txpool.New(p.Blockchain(), p.TxPool()) - p.WrappedMiner = miner.New(p.Miner()) - return p } @@ -103,6 +102,10 @@ func New( // It takes a BaseApp and an EVMKeeper as arguments. // It returns an error if the setup fails. func (p *Polaris) Build(app CosmosApp, ek EVMKeeper) error { + // Wrap the geth miner and txpool with the cosmos miner and txpool. + p.WrappedTxPool = txpool.New(p.Blockchain(), p.TxPool()) + p.WrappedMiner = miner.New(p.Miner(), app) + app.SetMempool(p.WrappedTxPool) app.SetPrepareProposal(p.WrappedMiner.PrepareProposal) diff --git a/cosmos/x/evm/keeper/abci.go b/cosmos/x/evm/keeper/abci.go index 3b75d036d..888f27b47 100644 --- a/cosmos/x/evm/keeper/abci.go +++ b/cosmos/x/evm/keeper/abci.go @@ -36,7 +36,7 @@ func (k *Keeper) Precommit(ctx context.Context) error { if block == nil { panic( fmt.Sprintf( - "EVM BLOCK %d FAILED TO PROCESS - hash: %s", blockNum, block.Hash(), + "EVM BLOCK %d FAILED TO PROCESS", blockNum, ), ) } else if block.NumberU64() != blockNum { diff --git a/e2e/precompile/contracts/distribution/distribution_test.go b/e2e/precompile/contracts/distribution/distribution_test.go index a79517c97..68db70c7e 100644 --- a/e2e/precompile/contracts/distribution/distribution_test.go +++ b/e2e/precompile/contracts/distribution/distribution_test.go @@ -121,8 +121,8 @@ var _ = Describe("Distribution Precompile", func() { Expect(err).ToNot(HaveOccurred()) ExpectSuccessReceipt(tf.EthClient(), tx) - // Wait for 2 blocks to be produced, to make sure there are rewards. - for i := 0; i < 2; i++ { + // Wait for 5 blocks to be produced, to make sure there are rewards. + for i := 0; i < 5; i++ { Expect(tf.WaitForNextBlock()).To(Succeed()) } diff --git a/e2e/testapp/app.go b/e2e/testapp/app.go index 25231c7e2..fa5d851e9 100644 --- a/e2e/testapp/app.go +++ b/e2e/testapp/app.go @@ -191,7 +191,7 @@ func NewPolarisApp( ) // Setup Polaris Runtime. - if err := app.Polaris.Build(app.BaseApp, app.EVMKeeper); err != nil { + if err := app.Polaris.Build(app, app.EVMKeeper); err != nil { panic(err) } diff --git a/eth/core/chain_writer.go b/eth/core/chain_writer.go index acf772129..7f4ec9357 100644 --- a/eth/core/chain_writer.go +++ b/eth/core/chain_writer.go @@ -54,9 +54,11 @@ func (bc *blockchain) WriteGenesisBlock(block *types.Block) error { // For now, it is a huge lie. It does infact set the head. func (bc *blockchain) InsertBlockAndSetHead(block *types.Block) error { // Validate that we are about to insert a valid block. - if err := bc.validator.ValidateBody(block); err != nil { - log.Error("invalid block body", "err", err) - return err + if block.NumberU64() > 1 { // TODO DIAGNOSE + if err := bc.validator.ValidateBody(block); err != nil { + log.Error("invalid block body", "err", err) + return err + } } // Process the incoming EVM block. From ed2fd90ace70f6922dd6d6d74095ea8a2bbaa902 Mon Sep 17 00:00:00 2001 From: Devon Bear Date: Wed, 1 Nov 2023 15:52:17 -0400 Subject: [PATCH 11/15] run begin blocker in miner --- cosmos/miner/miner.go | 17 ++++++++++++----- cosmos/runtime/runtime.go | 2 +- 2 files changed, 13 insertions(+), 6 deletions(-) diff --git a/cosmos/miner/miner.go b/cosmos/miner/miner.go index de78cf390..9ed4bd31b 100644 --- a/cosmos/miner/miner.go +++ b/cosmos/miner/miner.go @@ -61,15 +61,17 @@ type EVMKeeper interface { type Miner struct { eth.Miner app App + keeper EVMKeeper serializer EnvelopeSerializer currentPayload *miner.Payload } // New produces a cosmos miner from a geth miner. -func New(gm eth.Miner, app App) *Miner { +func New(gm eth.Miner, app App, keeper EVMKeeper) *Miner { return &Miner{ - Miner: gm, - app: app, + Miner: gm, + keeper: keeper, + app: app, } } @@ -82,9 +84,14 @@ func (m *Miner) Init(serializer EnvelopeSerializer) { func (m *Miner) PrepareProposal( ctx sdk.Context, _ *abci.RequestPrepareProposal, ) (*abci.ResponsePrepareProposal, error) { - var payloadEnvelopeBz []byte - var err error + var ( + payloadEnvelopeBz []byte + err error + ) + if err = m.keeper.PrepareCheckState(ctx); err != nil { + return nil, err + } // We have to run the BeginBlocker to get the chain into the state it'll // be in when the EVM transaction actually runs. if _, err = m.app.BeginBlocker(ctx); err != nil { diff --git a/cosmos/runtime/runtime.go b/cosmos/runtime/runtime.go index 3cfb2683b..6b30c8188 100644 --- a/cosmos/runtime/runtime.go +++ b/cosmos/runtime/runtime.go @@ -104,7 +104,7 @@ func New( func (p *Polaris) Build(app CosmosApp, ek EVMKeeper) error { // Wrap the geth miner and txpool with the cosmos miner and txpool. p.WrappedTxPool = txpool.New(p.Blockchain(), p.TxPool()) - p.WrappedMiner = miner.New(p.Miner(), app) + p.WrappedMiner = miner.New(p.Miner(), app, ek) app.SetMempool(p.WrappedTxPool) app.SetPrepareProposal(p.WrappedMiner.PrepareProposal) From 63229e6267fd0e0a2305a312a7331677875372f4 Mon Sep 17 00:00:00 2001 From: Devon Bear Date: Wed, 1 Nov 2023 16:01:19 -0400 Subject: [PATCH 12/15] bing bong --- cosmos/miner/miner.go | 9 +++++++-- cosmos/runtime/runtime.go | 2 +- cosmos/x/evm/keeper/abci.go | 4 ++-- cosmos/x/evm/module.go | 4 ++-- 4 files changed, 12 insertions(+), 7 deletions(-) diff --git a/cosmos/miner/miner.go b/cosmos/miner/miner.go index 9ed4bd31b..90f7e4237 100644 --- a/cosmos/miner/miner.go +++ b/cosmos/miner/miner.go @@ -54,7 +54,7 @@ type App interface { type EVMKeeper interface { // Setup initializes the EVM keeper. Setup(evmkeeper.Blockchain) error - PrepareCheckState(context.Context) error + SetLatestQueryContext(context.Context) error } // Miner implements the baseapp.TxSelector interface. @@ -89,18 +89,23 @@ func (m *Miner) PrepareProposal( err error ) - if err = m.keeper.PrepareCheckState(ctx); err != nil { + // We have to prime the state plugin. + if err = m.keeper.SetLatestQueryContext(ctx); err != nil { return nil, err } + // We have to run the BeginBlocker to get the chain into the state it'll // be in when the EVM transaction actually runs. if _, err = m.app.BeginBlocker(ctx); err != nil { return nil, err } + // Trigger the geth miner to build a block. if payloadEnvelopeBz, err = m.buildBlock(ctx); err != nil { return nil, err } + + // Return the payload as a transaction in the proposal. return &abci.ResponsePrepareProposal{Txs: [][]byte{payloadEnvelopeBz}}, err } diff --git a/cosmos/runtime/runtime.go b/cosmos/runtime/runtime.go index 6b30c8188..d37058df2 100644 --- a/cosmos/runtime/runtime.go +++ b/cosmos/runtime/runtime.go @@ -51,7 +51,7 @@ import ( type EVMKeeper interface { // Setup initializes the EVM keeper. Setup(evmkeeper.Blockchain) error - PrepareCheckState(context.Context) error + SetLatestQueryContext(context.Context) error } // CosmosApp is an interface that defines the methods needed for the Cosmos setup. diff --git a/cosmos/x/evm/keeper/abci.go b/cosmos/x/evm/keeper/abci.go index 888f27b47..c5fc838b3 100644 --- a/cosmos/x/evm/keeper/abci.go +++ b/cosmos/x/evm/keeper/abci.go @@ -49,8 +49,8 @@ func (k *Keeper) Precommit(ctx context.Context) error { return nil } -// PrepareCheckState runs on the Cosmos-SDK lifecycle PrepareCheckState(). -func (k *Keeper) PrepareCheckState(ctx context.Context) error { +// SetLatestQueryContext runs on the Cosmos-SDK lifecycle SetLatestQueryContext(). +func (k *Keeper) SetLatestQueryContext(ctx context.Context) error { k.sp.Prepare(ctx) return nil } diff --git a/cosmos/x/evm/module.go b/cosmos/x/evm/module.go index 5969ad3dd..07d2d653a 100644 --- a/cosmos/x/evm/module.go +++ b/cosmos/x/evm/module.go @@ -126,9 +126,9 @@ func (am AppModule) RegisterServices(registrar grpc.ServiceRegistrar) error { // ConsensusVersion implements AppModule/ConsensusVersion. func (AppModule) ConsensusVersion() uint64 { return ConsensusVersion } -// PrepareCheckState prepares the application state for a check. +// SetLatestQueryContext prepares the application state for a check. func (am AppModule) PrepareCheckState(ctx context.Context) error { - return am.keeper.PrepareCheckState(ctx) + return am.keeper.SetLatestQueryContext(ctx) } // Precommit performs precommit operations. From f981084a5dbe5dff97fdfa81d2b231540ec78a3f Mon Sep 17 00:00:00 2001 From: Devon Bear Date: Wed, 1 Nov 2023 16:08:46 -0400 Subject: [PATCH 13/15] add preblock --- cosmos/miner/miner.go | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/cosmos/miner/miner.go b/cosmos/miner/miner.go index 90f7e4237..e28c1d32f 100644 --- a/cosmos/miner/miner.go +++ b/cosmos/miner/miner.go @@ -47,7 +47,8 @@ type EnvelopeSerializer interface { } type App interface { - BeginBlocker(ctx sdk.Context) (sdk.BeginBlock, error) + BeginBlocker(sdk.Context) (sdk.BeginBlock, error) + PreBlocker(sdk.Context, *abci.RequestFinalizeBlock) (*sdk.ResponsePreBlock, error) } // EVMKeeper is an interface that defines the methods needed for the EVM setup. @@ -94,6 +95,12 @@ func (m *Miner) PrepareProposal( return nil, err } + // We have to run the PreBlocker to get the chain into the state it'll + // be in when the EVM transaction actually runs. + if _, err = m.app.PreBlocker(ctx, nil); err != nil { + return nil, err + } + // We have to run the BeginBlocker to get the chain into the state it'll // be in when the EVM transaction actually runs. if _, err = m.app.BeginBlocker(ctx); err != nil { From 08e11ad1597cacdb029ec1ad9aa3f48851efcd19 Mon Sep 17 00:00:00 2001 From: Devon Bear Date: Wed, 1 Nov 2023 16:09:39 -0400 Subject: [PATCH 14/15] add preblock --- cosmos/miner/miner.go | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/cosmos/miner/miner.go b/cosmos/miner/miner.go index e28c1d32f..10bccbc69 100644 --- a/cosmos/miner/miner.go +++ b/cosmos/miner/miner.go @@ -95,15 +95,11 @@ func (m *Miner) PrepareProposal( return nil, err } - // We have to run the PreBlocker to get the chain into the state it'll - // be in when the EVM transaction actually runs. + // We have to run the PreBlocker && BeginBlocker to get the chain into the state + // it'll be in when the EVM transaction actually runs. if _, err = m.app.PreBlocker(ctx, nil); err != nil { return nil, err - } - - // We have to run the BeginBlocker to get the chain into the state it'll - // be in when the EVM transaction actually runs. - if _, err = m.app.BeginBlocker(ctx); err != nil { + } else if _, err = m.app.BeginBlocker(ctx); err != nil { return nil, err } From a3b3d43a84c99fc3e410f592936cd37c0fd8d29e Mon Sep 17 00:00:00 2001 From: Devon Bear Date: Wed, 1 Nov 2023 16:19:32 -0400 Subject: [PATCH 15/15] fix comment --- eth/core/chain_writer.go | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/eth/core/chain_writer.go b/eth/core/chain_writer.go index 7f4ec9357..fd2e196ef 100644 --- a/eth/core/chain_writer.go +++ b/eth/core/chain_writer.go @@ -50,8 +50,7 @@ func (bc *blockchain) WriteGenesisBlock(block *types.Block) error { return err } -// InsertBlockAndSetHead inserts a block into the blockchain without setting the head. -// For now, it is a huge lie. It does infact set the head. +// InsertBlockAndSetHead inserts a block into the blockchain and sets the head. func (bc *blockchain) InsertBlockAndSetHead(block *types.Block) error { // Validate that we are about to insert a valid block. if block.NumberU64() > 1 { // TODO DIAGNOSE