Skip to content

Commit 0436412

Browse files
authored
Merge pull request ethereum#18988 from holiman/repro18977
core: repro ethereum#18977
2 parents 4f3d22f + 940e317 commit 0436412

File tree

3 files changed

+73
-12
lines changed

3 files changed

+73
-12
lines changed

core/blockchain.go

Lines changed: 17 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -979,20 +979,26 @@ func (bc *BlockChain) writeBlockWithState(block *types.Block, receipts []*types.
979979
triedb.Cap(limit - ethdb.IdealBatchSize)
980980
}
981981
// Find the next state trie we need to commit
982-
header := bc.GetHeaderByNumber(current - triesInMemory)
983-
chosen := header.Number.Uint64()
982+
chosen := current - triesInMemory
984983

985984
// If we exceeded out time allowance, flush an entire trie to disk
986985
if bc.gcproc > bc.cacheConfig.TrieTimeLimit {
987-
// If we're exceeding limits but haven't reached a large enough memory gap,
988-
// warn the user that the system is becoming unstable.
989-
if chosen < lastWrite+triesInMemory && bc.gcproc >= 2*bc.cacheConfig.TrieTimeLimit {
990-
log.Info("State in memory for too long, committing", "time", bc.gcproc, "allowance", bc.cacheConfig.TrieTimeLimit, "optimum", float64(chosen-lastWrite)/triesInMemory)
986+
// If the header is missing (canonical chain behind), we're reorging a low
987+
// diff sidechain. Suspend committing until this operation is completed.
988+
header := bc.GetHeaderByNumber(chosen)
989+
if header == nil {
990+
log.Warn("Reorg in progress, trie commit postponed", "number", chosen)
991+
} else {
992+
// If we're exceeding limits but haven't reached a large enough memory gap,
993+
// warn the user that the system is becoming unstable.
994+
if chosen < lastWrite+triesInMemory && bc.gcproc >= 2*bc.cacheConfig.TrieTimeLimit {
995+
log.Info("State in memory for too long, committing", "time", bc.gcproc, "allowance", bc.cacheConfig.TrieTimeLimit, "optimum", float64(chosen-lastWrite)/triesInMemory)
996+
}
997+
// Flush an entire trie and restart the counters
998+
triedb.Commit(header.Root, true)
999+
lastWrite = chosen
1000+
bc.gcproc = 0
9911001
}
992-
// Flush an entire trie and restart the counters
993-
triedb.Commit(header.Root, true)
994-
lastWrite = chosen
995-
bc.gcproc = 0
9961002
}
9971003
// Garbage collect anything below our required write retention
9981004
for !bc.triegc.Empty() {
@@ -1324,7 +1330,7 @@ func (bc *BlockChain) insertSidechain(block *types.Block, it *insertIterator) (i
13241330
if err := bc.WriteBlockWithoutState(block, externTd); err != nil {
13251331
return it.index, nil, nil, err
13261332
}
1327-
log.Debug("Inserted sidechain block", "number", block.Number(), "hash", block.Hash(),
1333+
log.Debug("Injected sidechain block", "number", block.Number(), "hash", block.Hash(),
13281334
"diff", block.Difficulty(), "elapsed", common.PrettyDuration(time.Since(start)),
13291335
"txs", len(block.Transactions()), "gas", block.GasUsed(), "uncles", len(block.Uncles()),
13301336
"root", block.Root())

core/blockchain_test.go

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1483,3 +1483,58 @@ func BenchmarkBlockChain_1x1000Executions(b *testing.B) {
14831483

14841484
benchmarkLargeNumberOfValueToNonexisting(b, numTxs, numBlocks, recipientFn, dataFn)
14851485
}
1486+
1487+
// Tests that importing a very large side fork, which is larger than the canon chain,
1488+
// but where the difficulty per block is kept low: this means that it will not
1489+
// overtake the 'canon' chain until after it's passed canon by about 200 blocks.
1490+
//
1491+
// Details at:
1492+
// - https://github.com/ethereum/go-ethereum/issues/18977
1493+
// - https://github.com/ethereum/go-ethereum/pull/18988
1494+
func TestLowDiffLongChain(t *testing.T) {
1495+
// Generate a canonical chain to act as the main dataset
1496+
engine := ethash.NewFaker()
1497+
db := ethdb.NewMemDatabase()
1498+
genesis := new(Genesis).MustCommit(db)
1499+
1500+
// We must use a pretty long chain to ensure that the fork doesn't overtake us
1501+
// until after at least 128 blocks post tip
1502+
blocks, _ := GenerateChain(params.TestChainConfig, genesis, engine, db, 6*triesInMemory, func(i int, b *BlockGen) {
1503+
b.SetCoinbase(common.Address{1})
1504+
b.OffsetTime(-9)
1505+
})
1506+
1507+
// Import the canonical chain
1508+
diskdb := ethdb.NewMemDatabase()
1509+
new(Genesis).MustCommit(diskdb)
1510+
1511+
chain, err := NewBlockChain(diskdb, nil, params.TestChainConfig, engine, vm.Config{}, nil)
1512+
if err != nil {
1513+
t.Fatalf("failed to create tester chain: %v", err)
1514+
}
1515+
if n, err := chain.InsertChain(blocks); err != nil {
1516+
t.Fatalf("block %d: failed to insert into chain: %v", n, err)
1517+
}
1518+
// Generate fork chain, starting from an early block
1519+
parent := blocks[10]
1520+
fork, _ := GenerateChain(params.TestChainConfig, parent, engine, db, 8*triesInMemory, func(i int, b *BlockGen) {
1521+
b.SetCoinbase(common.Address{2})
1522+
})
1523+
1524+
// And now import the fork
1525+
if i, err := chain.InsertChain(fork); err != nil {
1526+
t.Fatalf("block %d: failed to insert into chain: %v", i, err)
1527+
}
1528+
head := chain.CurrentBlock()
1529+
if got := fork[len(fork)-1].Hash(); got != head.Hash() {
1530+
t.Fatalf("head wrong, expected %x got %x", head.Hash(), got)
1531+
}
1532+
// Sanity check that all the canonical numbers are present
1533+
header := chain.CurrentHeader()
1534+
for number := head.NumberU64(); number > 0; number-- {
1535+
if hash := chain.GetHeaderByNumber(number).Hash(); hash != header.Hash() {
1536+
t.Fatalf("header %d: canonical hash mismatch: have %x, want %x", number, hash, header.Hash())
1537+
}
1538+
header = chain.GetHeader(header.ParentHash, number-1)
1539+
}
1540+
}

core/chain_makers.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -149,7 +149,7 @@ func (b *BlockGen) PrevBlock(index int) *types.Block {
149149
// associated difficulty. It's useful to test scenarios where forking is not
150150
// tied to chain length directly.
151151
func (b *BlockGen) OffsetTime(seconds int64) {
152-
b.header.Time.Add(b.header.Time, new(big.Int).SetInt64(seconds))
152+
b.header.Time.Add(b.header.Time, big.NewInt(seconds))
153153
if b.header.Time.Cmp(b.parent.Header().Time) <= 0 {
154154
panic("block time out of range")
155155
}

0 commit comments

Comments
 (0)