Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 17 additions & 3 deletions core/bench_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,10 @@ import (
"math/big"
"testing"

"github.com/ava-labs/avalanchego/x/blockdb"
"github.com/ava-labs/coreth/consensus/dummy"
"github.com/ava-labs/coreth/params"
"github.com/ava-labs/coreth/plugin/evm/ethblockdb"
"github.com/ava-labs/libevm/common"
"github.com/ava-labs/libevm/common/math"
"github.com/ava-labs/libevm/core/rawdb"
Expand Down Expand Up @@ -160,16 +162,23 @@ func genTxRing(naccounts int) func(int, *BlockGen) {
func benchInsertChain(b *testing.B, disk bool, gen func(int, *BlockGen)) {
// Create the database in memory or in a temporary directory.
var db ethdb.Database
var blockDb ethblockdb.Database
var err error
if !disk {
db = rawdb.NewMemoryDatabase()
blockDb = ethblockdb.NewMock(db)
} else {
dir := b.TempDir()
db, err = rawdb.NewLevelDBDatabase(dir, 128, 128, "", false)
if err != nil {
b.Fatalf("cannot create temporary database: %v", err)
}
defer db.Close()
store, err := blockdb.New(blockdb.DefaultConfig().WithDir(dir), nil)
if err != nil {
b.Fatalf("cannot create temporary block database: %v", err)
}
blockDb = ethblockdb.New(store, db)
}

// Generate a chain of b.N blocks using the supplied block
Expand All @@ -178,11 +187,11 @@ func benchInsertChain(b *testing.B, disk bool, gen func(int, *BlockGen)) {
Config: params.TestChainConfig,
Alloc: types.GenesisAlloc{benchRootAddr: {Balance: benchRootFunds}},
}
_, chain, _, _ := GenerateChainWithGenesis(gspec, dummy.NewCoinbaseFaker(), b.N, 10, gen)
_, _, chain, _, _ := GenerateChainWithGenesis(gspec, dummy.NewCoinbaseFaker(), b.N, 10, gen)

// Time the insertion of the new chain.
// State and blocks are stored in the same DB.
chainman, _ := NewBlockChain(db, DefaultCacheConfig, gspec, dummy.NewCoinbaseFaker(), vm.Config{}, common.Hash{}, false)
chainman, _ := NewBlockChain(db, blockDb, DefaultCacheConfig, gspec, dummy.NewCoinbaseFaker(), vm.Config{}, common.Hash{}, false)
defer chainman.Stop()
b.ReportAllocs()
b.ResetTimer()
Expand Down Expand Up @@ -283,6 +292,11 @@ func benchReadChain(b *testing.B, full bool, count uint64) {
if err != nil {
b.Fatalf("error opening database at %v: %v", dir, err)
}
store, err := blockdb.New(blockdb.DefaultConfig().WithDir(dir), nil)
if err != nil {
b.Fatalf("cannot create temporary block database: %v", err)
}
blockDb := ethblockdb.New(store, db)
genesis := &Genesis{Config: params.TestChainConfig}
makeChainForBench(db, genesis, full, count)
db.Close()
Expand All @@ -295,7 +309,7 @@ func benchReadChain(b *testing.B, full bool, count uint64) {
if err != nil {
b.Fatalf("error opening database at %v: %v", dir, err)
}
chain, err := NewBlockChain(db, DefaultCacheConfig, genesis, dummy.NewFaker(), vm.Config{}, common.Hash{}, false)
chain, err := NewBlockChain(db, blockDb, DefaultCacheConfig, genesis, dummy.NewFaker(), vm.Config{}, common.Hash{}, false)
if err != nil {
b.Fatalf("error creating chain: %v", err)
}
Expand Down
71 changes: 51 additions & 20 deletions core/blockchain.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ import (
"github.com/ava-labs/coreth/plugin/evm/customlogs"
"github.com/ava-labs/coreth/plugin/evm/customrawdb"
"github.com/ava-labs/coreth/plugin/evm/customtypes"
"github.com/ava-labs/coreth/plugin/evm/ethblockdb"
"github.com/ava-labs/coreth/plugin/evm/upgrade/acp176"
"github.com/ava-labs/coreth/triedb/hashdb"
"github.com/ava-labs/coreth/triedb/pathdb"
Expand All @@ -67,6 +68,7 @@ import (

// Force libevm metrics of the same name to be registered first.
_ "github.com/ava-labs/libevm/core"
_ "github.com/ava-labs/libevm/trie"
)

// ====== If resolving merge conflicts ======
Expand Down Expand Up @@ -269,11 +271,12 @@ type BlockChain struct {
chainConfig *params.ChainConfig // Chain & network configuration
cacheConfig *CacheConfig // Cache configuration for pruning

db ethdb.Database // Low level persistent database to store final content in
snaps *snapshot.Tree // Snapshot tree for fast trie leaf access
triedb *triedb.Database // The database handler for maintaining trie nodes.
stateCache state.Database // State database to reuse between imports (contains state cache)
txIndexer *txIndexer // Transaction indexer, might be nil if not enabled
db ethdb.Database // Low level persistent database to store final content in
blockDb ethblockdb.Database // Block database for canonical blocks
snaps *snapshot.Tree // Snapshot tree for fast trie leaf access
triedb *triedb.Database // The database handler for maintaining trie nodes.
stateCache state.Database // State database to reuse between imports (contains state cache)
txIndexer *txIndexer // Transaction indexer, might be nil if not enabled
stateManager TrieWriter

hc *HeaderChain
Expand Down Expand Up @@ -360,7 +363,7 @@ type BlockChain struct {
// available in the database. It initialises the default Ethereum Validator and
// Processor.
func NewBlockChain(
db ethdb.Database, cacheConfig *CacheConfig, genesis *Genesis, engine consensus.Engine,
db ethdb.Database, blockDb ethblockdb.Database, cacheConfig *CacheConfig, genesis *Genesis, engine consensus.Engine,
vmConfig vm.Config, lastAcceptedHash common.Hash, skipChainConfigCheckCompatible bool,
) (*BlockChain, error) {
if cacheConfig == nil {
Expand All @@ -375,7 +378,7 @@ func NewBlockChain(
// Note: In go-ethereum, the code rewinds the chain on an incompatible config upgrade.
// We don't do this and expect the node operator to always update their node's configuration
// before network upgrades take effect.
chainConfig, _, err := SetupGenesisBlock(db, triedb, genesis, lastAcceptedHash, skipChainConfigCheckCompatible)
chainConfig, _, err := SetupGenesisBlock(db, triedb, blockDb, genesis, lastAcceptedHash, skipChainConfigCheckCompatible)
if err != nil {
return nil, err
}
Expand All @@ -391,6 +394,7 @@ func NewBlockChain(
chainConfig: chainConfig,
cacheConfig: cacheConfig,
db: db,
blockDb: blockDb,
triedb: triedb,
bodyCache: lru.NewCache[common.Hash, *types.Body](bodyCacheLimit),
receiptsCache: lru.NewCache[common.Hash, []*types.Receipt](receiptsCacheLimit),
Expand All @@ -408,7 +412,7 @@ func NewBlockChain(
bc.validator = NewBlockValidator(chainConfig, bc, engine)
bc.processor = NewStateProcessor(chainConfig, bc, engine)

bc.hc, err = NewHeaderChain(db, chainConfig, cacheConfig, engine)
bc.hc, err = NewHeaderChain(db, blockDb, chainConfig, cacheConfig, engine)
if err != nil {
return nil, err
}
Expand Down Expand Up @@ -738,11 +742,7 @@ func (bc *BlockChain) loadLastState(lastAcceptedHash common.Hash) error {

func (bc *BlockChain) loadGenesisState() error {
// Prepare the genesis block and reinitialise the chain
batch := bc.db.NewBatch()
rawdb.WriteBlock(batch, bc.genesisBlock)
if err := batch.Write(); err != nil {
log.Crit("Failed to write genesis block", "err", err)
}
bc.blockDb.WriteBlock(bc.genesisBlock)
bc.writeHeadBlock(bc.genesisBlock)

// Last update all in-memory chain markers
Expand Down Expand Up @@ -1082,6 +1082,26 @@ func (bc *BlockChain) Accept(block *types.Block) error {
}
}

// Since we optimistically store the first block at a height as the canonical block,
// we need to rewrite the canonical block in the blockdb if another block at the same height
// was accepted.
savedBlock := bc.blockDb.ReadBlock(block.NumberU64())
if savedBlock == nil || savedBlock.Hash() != block.Hash() {
bc.blockDb.WriteBlock(block)

// delete the block data from the chaindb since we are now storing it in the blockdb
batch := bc.db.NewBatch()
rawdb.DeleteHeader(batch, block.Hash(), block.NumberU64())
rawdb.DeleteBody(batch, block.Hash(), block.NumberU64())
// add back the hash->number mapping since DeleteHeader will have removed it
rawdb.WriteHeaderNumber(batch, block.Hash(), block.NumberU64())
// save the sidechain block back to the chaindb
rawdb.WriteBlock(batch, savedBlock)
if err := batch.Write(); err != nil {
return fmt.Errorf("failed to write delete block batch: %w", err)
}
}

// Enqueue block in the acceptor
bc.lastAccepted = block
bc.addAcceptorQueue(block)
Expand All @@ -1103,6 +1123,7 @@ func (bc *BlockChain) Accept(block *types.Block) error {
latestGasCapacityGauge.Update(int64(s.Gas.Capacity))
latestGasTargetGauge.Update(int64(s.Target()))
}

return nil
}

Expand Down Expand Up @@ -1188,16 +1209,23 @@ func (bc *BlockChain) writeBlockAndSetHead(block *types.Block, parentRoot common
// writeBlockWithState writes the block and all associated state to the database,
// but it expects the chain mutex to be held.
func (bc *BlockChain) writeBlockWithState(block *types.Block, parentRoot common.Hash, receipts []*types.Receipt, state *state.StateDB) error {
// Irrelevant of the canonical status, write the block itself to the database.
// Irrelevant of the canonical status, write the block to blockdb and related
// data to the chaindb.
//
// Note all the components of block(hash->number map, header, body, receipts)
// Note the block data is written first, then all other components of block(hash->number map, receipts, preimages)
// should be written atomically. BlockBatch is used for containing all components.
blockBatch := bc.db.NewBatch()
rawdb.WriteBlock(blockBatch, block)
blockNumber := block.NumberU64()
if !bc.blockDb.HasBlock(blockNumber) {
bc.blockDb.WriteBlock(block)
} else {
// write block to the chaindb if we already stored a block at the same height
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This seems really confusing, we only use blockDB to store 1 block at any given height? This seems fine after SAE but is pretty incompatible until then.

rawdb.WriteBlock(bc.db, block)
}
rawdb.WriteReceipts(blockBatch, block.Hash(), block.NumberU64(), receipts)
rawdb.WritePreimages(blockBatch, state.Preimages())
if err := blockBatch.Write(); err != nil {
log.Crit("Failed to write block into disk", "err", err)
log.Crit("Failed to write block receipts and preimages into disk", "err", err)
}

// Commit all cached state changes into underlying memory database.
Expand Down Expand Up @@ -1538,12 +1566,15 @@ func (bc *BlockChain) reorg(oldHead *types.Header, newHead *types.Block) error {
// stale lookups are still cached.
bc.txLookupCache.Purge()

// Insert the new chain(except the head block(reverse order)),
// taking care of the proper incremental order.
// Remove non-canonical blocks from the chain db and ensure the canonical
// block are stored in the blockdb
blocksBatch := bc.db.NewBatch()
for i := len(newChain) - 1; i >= 1; i-- {
// Insert the block in the canonical way, re-writing history
bc.writeHeadBlock(newChain[i])
}
if err := blocksBatch.Write(); err != nil {
log.Crit("Failed to write block data during reorg", "err", err)
}

// Delete any canonical number assignments above the new head
indexesBatch := bc.db.NewBatch()
Expand Down
Loading