mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-08-20 10:52:25 +00:00
core: import known blocks if they can be inserted as canonical blocks
This commit is contained in:
parent
9b831d74fb
commit
fa569d5b2b
2 changed files with 271 additions and 114 deletions
|
|
@ -1144,13 +1144,23 @@ func (bc *BlockChain) insertChain(chain types.Blocks, verifySeals bool) (int, []
|
|||
stats.ignored++
|
||||
block, err = it.next()
|
||||
}
|
||||
// First block is still known block, the only scenario here is:
|
||||
// We did a roll-back, and we want to re-import a batch of known blocks while a part
|
||||
// of known blocks are higher than current head block.
|
||||
if err == ErrKnownBlock {
|
||||
block, err = bc.insertKnownChain(block, it)
|
||||
|
||||
if bc.CurrentBlock().NumberU64() != current {
|
||||
lastCanon = bc.CurrentBlock()
|
||||
}
|
||||
}
|
||||
// Falls through to the block import
|
||||
}
|
||||
|
||||
switch {
|
||||
// First block is pruned, insert as sidechain and reorg only if TD grows enough
|
||||
case err == consensus.ErrPrunedAncestor:
|
||||
return bc.insertSidechain(block, it)
|
||||
return bc.insertSideChain(block, it)
|
||||
|
||||
// First block is future, shove it (and all children) to the future queue (unknown ancestor)
|
||||
case err == consensus.ErrFutureBlock || (err == consensus.ErrUnknownAncestor && bc.futureBlocks.Contains(it.first().ParentHash())):
|
||||
|
|
@ -1313,13 +1323,44 @@ func (bc *BlockChain) insertChain(chain types.Blocks, verifySeals bool) (int, []
|
|||
return it.index, events, coalescedLogs, err
|
||||
}
|
||||
|
||||
// insertSidechain is called when an import batch hits upon a pruned ancestor
|
||||
// insertKnownChain inserts a batch of known blocks which are higher than current
|
||||
// head block.
|
||||
func (bc *BlockChain) insertKnownChain(block *types.Block, it *insertIterator) (*types.Block, error) {
|
||||
var (
|
||||
externTd *big.Int
|
||||
knownBlocks []*types.Block
|
||||
|
||||
err = ErrKnownBlock
|
||||
current = bc.CurrentBlock()
|
||||
localTd = bc.GetTd(current.Hash(), current.NumberU64())
|
||||
)
|
||||
for ; block != nil && (err == ErrKnownBlock); block, err = it.next() {
|
||||
if externTd == nil {
|
||||
externTd = bc.GetTd(block.ParentHash(), block.NumberU64()-1)
|
||||
}
|
||||
externTd = new(big.Int).Add(externTd, block.Difficulty())
|
||||
|
||||
// Short circuit if the known block cannot be imported as a new
|
||||
// canonical block.
|
||||
if block.ParentHash() != current.Hash() || externTd.Cmp(localTd) <= 0 {
|
||||
break
|
||||
}
|
||||
knownBlocks = append(knownBlocks, block)
|
||||
localTd, current = new(big.Int).Add(localTd, block.Difficulty()), block
|
||||
}
|
||||
if len(knownBlocks) > 0 {
|
||||
bc.insert(knownBlocks[len(knownBlocks)-1])
|
||||
}
|
||||
return block, err
|
||||
}
|
||||
|
||||
// insertSideChain is called when an import batch hits upon a pruned ancestor
|
||||
// error, which happens when a sidechain with a sufficiently old fork-block is
|
||||
// found.
|
||||
//
|
||||
// The method writes all (header-and-body-valid) blocks to disk, then tries to
|
||||
// switch over to the new chain if the TD exceeded the current chain.
|
||||
func (bc *BlockChain) insertSidechain(block *types.Block, it *insertIterator) (int, []interface{}, []*types.Log, error) {
|
||||
func (bc *BlockChain) insertSideChain(block *types.Block, it *insertIterator) (int, []interface{}, []*types.Log, error) {
|
||||
var (
|
||||
externTd *big.Int
|
||||
current = bc.CurrentBlock()
|
||||
|
|
|
|||
|
|
@ -1564,117 +1564,6 @@ func TestLargeReorgTrieGC(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
// Benchmarks large blocks with value transfers to non-existing accounts
|
||||
func benchmarkLargeNumberOfValueToNonexisting(b *testing.B, numTxs, numBlocks int, recipientFn func(uint64) common.Address, dataFn func(uint64) []byte) {
|
||||
var (
|
||||
signer = types.HomesteadSigner{}
|
||||
testBankKey, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
|
||||
testBankAddress = crypto.PubkeyToAddress(testBankKey.PublicKey)
|
||||
bankFunds = big.NewInt(100000000000000000)
|
||||
gspec = Genesis{
|
||||
Config: params.TestChainConfig,
|
||||
Alloc: GenesisAlloc{
|
||||
testBankAddress: {Balance: bankFunds},
|
||||
common.HexToAddress("0xc0de"): {
|
||||
Code: []byte{0x60, 0x01, 0x50},
|
||||
Balance: big.NewInt(0),
|
||||
}, // push 1, pop
|
||||
},
|
||||
GasLimit: 100e6, // 100 M
|
||||
}
|
||||
)
|
||||
// Generate the original common chain segment and the two competing forks
|
||||
engine := ethash.NewFaker()
|
||||
db := rawdb.NewMemoryDatabase()
|
||||
genesis := gspec.MustCommit(db)
|
||||
|
||||
blockGenerator := func(i int, block *BlockGen) {
|
||||
block.SetCoinbase(common.Address{1})
|
||||
for txi := 0; txi < numTxs; txi++ {
|
||||
uniq := uint64(i*numTxs + txi)
|
||||
recipient := recipientFn(uniq)
|
||||
//recipient := common.BigToAddress(big.NewInt(0).SetUint64(1337 + uniq))
|
||||
tx, err := types.SignTx(types.NewTransaction(uniq, recipient, big.NewInt(1), params.TxGas, big.NewInt(1), nil), signer, testBankKey)
|
||||
if err != nil {
|
||||
b.Error(err)
|
||||
}
|
||||
block.AddTx(tx)
|
||||
}
|
||||
}
|
||||
|
||||
shared, _ := GenerateChain(params.TestChainConfig, genesis, engine, db, numBlocks, blockGenerator)
|
||||
b.StopTimer()
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
// Import the shared chain and the original canonical one
|
||||
diskdb := rawdb.NewMemoryDatabase()
|
||||
gspec.MustCommit(diskdb)
|
||||
|
||||
chain, err := NewBlockChain(diskdb, nil, params.TestChainConfig, engine, vm.Config{}, nil)
|
||||
if err != nil {
|
||||
b.Fatalf("failed to create tester chain: %v", err)
|
||||
}
|
||||
b.StartTimer()
|
||||
if _, err := chain.InsertChain(shared); err != nil {
|
||||
b.Fatalf("failed to insert shared chain: %v", err)
|
||||
}
|
||||
b.StopTimer()
|
||||
if got := chain.CurrentBlock().Transactions().Len(); got != numTxs*numBlocks {
|
||||
b.Fatalf("Transactions were not included, expected %d, got %d", numTxs*numBlocks, got)
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
func BenchmarkBlockChain_1x1000ValueTransferToNonexisting(b *testing.B) {
|
||||
var (
|
||||
numTxs = 1000
|
||||
numBlocks = 1
|
||||
)
|
||||
|
||||
recipientFn := func(nonce uint64) common.Address {
|
||||
return common.BigToAddress(big.NewInt(0).SetUint64(1337 + nonce))
|
||||
}
|
||||
dataFn := func(nonce uint64) []byte {
|
||||
return nil
|
||||
}
|
||||
|
||||
benchmarkLargeNumberOfValueToNonexisting(b, numTxs, numBlocks, recipientFn, dataFn)
|
||||
}
|
||||
func BenchmarkBlockChain_1x1000ValueTransferToExisting(b *testing.B) {
|
||||
var (
|
||||
numTxs = 1000
|
||||
numBlocks = 1
|
||||
)
|
||||
b.StopTimer()
|
||||
b.ResetTimer()
|
||||
|
||||
recipientFn := func(nonce uint64) common.Address {
|
||||
return common.BigToAddress(big.NewInt(0).SetUint64(1337))
|
||||
}
|
||||
dataFn := func(nonce uint64) []byte {
|
||||
return nil
|
||||
}
|
||||
|
||||
benchmarkLargeNumberOfValueToNonexisting(b, numTxs, numBlocks, recipientFn, dataFn)
|
||||
}
|
||||
func BenchmarkBlockChain_1x1000Executions(b *testing.B) {
|
||||
var (
|
||||
numTxs = 1000
|
||||
numBlocks = 1
|
||||
)
|
||||
b.StopTimer()
|
||||
b.ResetTimer()
|
||||
|
||||
recipientFn := func(nonce uint64) common.Address {
|
||||
return common.BigToAddress(big.NewInt(0).SetUint64(0xc0de))
|
||||
}
|
||||
dataFn := func(nonce uint64) []byte {
|
||||
return nil
|
||||
}
|
||||
|
||||
benchmarkLargeNumberOfValueToNonexisting(b, numTxs, numBlocks, recipientFn, dataFn)
|
||||
}
|
||||
|
||||
// Tests that importing a very large side fork, which is larger than the canon chain,
|
||||
// but where the difficulty per block is kept low: this means that it will not
|
||||
// overtake the 'canon' chain until after it's passed canon by about 200 blocks.
|
||||
|
|
@ -1812,6 +1701,181 @@ func TestPrunedImportSide(t *testing.T) {
|
|||
testSideImport(t, 1, -10)
|
||||
}
|
||||
|
||||
func TestInsertKnownHeaders(t *testing.T) { testInsertKnownChainData(t, "headers") }
|
||||
func TestInsertKnownReceiptChain(t *testing.T) { testInsertKnownChainData(t, "receiptChain") }
|
||||
func TestInsertKnownBlocks(t *testing.T) { testInsertKnownChainData(t, "blocks") }
|
||||
|
||||
func testInsertKnownChainData(t *testing.T, typ string) {
|
||||
// Generate the original common chain segment and the two competing forks
|
||||
engine := ethash.NewFaker()
|
||||
|
||||
db := rawdb.NewMemoryDatabase()
|
||||
genesis := new(Genesis).MustCommit(db)
|
||||
|
||||
blocks, receipts := GenerateChain(params.TestChainConfig, genesis, engine, db, 64, func(i int, b *BlockGen) { b.SetCoinbase(common.Address{1}) })
|
||||
blocks2, receipts2 := GenerateChain(params.TestChainConfig, blocks[len(blocks)-1], engine, db, 64, func(i int, b *BlockGen) { b.SetCoinbase(common.Address{1}) })
|
||||
|
||||
// Import the shared chain and the original canonical one
|
||||
diskdb := rawdb.NewMemoryDatabase()
|
||||
new(Genesis).MustCommit(diskdb)
|
||||
|
||||
chain, err := NewBlockChain(diskdb, nil, params.TestChainConfig, engine, vm.Config{}, nil)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to create tester chain: %v", err)
|
||||
}
|
||||
|
||||
var rollback []common.Hash
|
||||
for i := len(blocks) / 2; i < len(blocks); i++ {
|
||||
rollback = append(rollback, blocks[i].Hash())
|
||||
}
|
||||
|
||||
if typ == "blocks" {
|
||||
if _, err := chain.InsertChain(blocks); err != nil {
|
||||
t.Fatalf("failed to insert chain: %v", err)
|
||||
}
|
||||
|
||||
// The imported blocks are all known blocks and not higher than
|
||||
// head block.
|
||||
if _, err := chain.InsertChain(blocks); err != nil {
|
||||
t.Fatalf("failed to insert known chain: %v", err)
|
||||
}
|
||||
|
||||
// A part of imported blocks are known blocks. Besides a part of
|
||||
// known blocks are higher than head blocks.
|
||||
chain.Rollback(rollback)
|
||||
if _, err := chain.InsertChain(append(blocks, blocks2...)); err != nil {
|
||||
t.Fatalf("failed to insert chain with known block as prefix: %v", err)
|
||||
}
|
||||
if chain.CurrentBlock().Hash() != blocks2[len(blocks2)-1].Hash() {
|
||||
t.Fatalf("failed to insert chain with known block as prefix, want head block %v, have head block %v",
|
||||
blocks2[len(blocks2)-1].Hash().Hex(), chain.CurrentBlock().Hash().Hex())
|
||||
}
|
||||
} else if typ == "headers" {
|
||||
headers, headers2 := make([]*types.Header, 0, len(blocks)), make([]*types.Header, 0, len(blocks2))
|
||||
for _, block := range blocks {
|
||||
headers = append(headers, block.Header())
|
||||
}
|
||||
for _, block := range blocks2 {
|
||||
headers2 = append(headers2, block.Header())
|
||||
}
|
||||
if _, err := chain.InsertHeaderChain(headers, 1); err != nil {
|
||||
t.Fatalf("failed to insert header chain: %v", err)
|
||||
}
|
||||
|
||||
// The imported headers are all known headers and not higher than
|
||||
// head block.
|
||||
if _, err := chain.InsertHeaderChain(headers, 1); err != nil {
|
||||
t.Fatalf("failed to insert known header chain: %v", err)
|
||||
}
|
||||
|
||||
// A part of imported headers are known headers. Besides a part of
|
||||
// known headers are higher than head headers.
|
||||
chain.Rollback(rollback)
|
||||
if _, err := chain.InsertHeaderChain(append(headers, headers2...), 1); err != nil {
|
||||
t.Fatalf("failed to insert header chain with known headers as prefix: %v", err)
|
||||
}
|
||||
if chain.CurrentHeader().Hash() != headers2[len(headers2)-1].Hash() {
|
||||
t.Fatalf("failed to insert header chain with known header as prefix, want head header %v, have head header %v",
|
||||
headers2[len(headers2)-1].Hash().Hex(), chain.CurrentBlock().Hash().Hex())
|
||||
}
|
||||
} else {
|
||||
headers, headers2 := make([]*types.Header, 0, len(blocks)), make([]*types.Header, 0, len(blocks2))
|
||||
for _, block := range blocks {
|
||||
headers = append(headers, block.Header())
|
||||
}
|
||||
for _, block := range blocks2 {
|
||||
headers2 = append(headers2, block.Header())
|
||||
}
|
||||
if _, err := chain.InsertHeaderChain(headers, 1); err != nil {
|
||||
t.Fatalf("failed to insert header chain: %v", err)
|
||||
}
|
||||
if _, err := chain.InsertReceiptChain(blocks, receipts); err != nil {
|
||||
t.Fatalf("failed to insert receipt chain: %v", err)
|
||||
}
|
||||
|
||||
// The imported receipt chain are all known and not higher than
|
||||
// head fast block.
|
||||
if _, err := chain.InsertReceiptChain(blocks, receipts); err != nil {
|
||||
t.Fatalf("failed to insert receipt chain: %v", err)
|
||||
}
|
||||
// A part of imported headers are known headers. Besides a part of
|
||||
// known headers are higher than head headers.
|
||||
chain.Rollback(rollback)
|
||||
if _, err := chain.InsertHeaderChain(append(headers, headers2...), 1); err != nil {
|
||||
t.Fatalf("failed to insert header chain with known headers as prefix: %v", err)
|
||||
}
|
||||
if _, err := chain.InsertReceiptChain(append(blocks, blocks2...), append(receipts, receipts2...)); err != nil {
|
||||
t.Fatalf("failed to insert header chain with known headers as prefix: %v", err)
|
||||
}
|
||||
if chain.CurrentFastBlock().Hash() != blocks2[len(blocks2)-1].Hash() {
|
||||
t.Fatalf("failed to insert header chain with known header as prefix, want head header %v, have head header %v",
|
||||
blocks2[len(blocks2)-1].Hash().Hex(), chain.CurrentFastBlock().Hash().Hex())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Benchmarks large blocks with value transfers to non-existing accounts
|
||||
func benchmarkLargeNumberOfValueToNonexisting(b *testing.B, numTxs, numBlocks int, recipientFn func(uint64) common.Address, dataFn func(uint64) []byte) {
|
||||
var (
|
||||
signer = types.HomesteadSigner{}
|
||||
testBankKey, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
|
||||
testBankAddress = crypto.PubkeyToAddress(testBankKey.PublicKey)
|
||||
bankFunds = big.NewInt(100000000000000000)
|
||||
gspec = Genesis{
|
||||
Config: params.TestChainConfig,
|
||||
Alloc: GenesisAlloc{
|
||||
testBankAddress: {Balance: bankFunds},
|
||||
common.HexToAddress("0xc0de"): {
|
||||
Code: []byte{0x60, 0x01, 0x50},
|
||||
Balance: big.NewInt(0),
|
||||
}, // push 1, pop
|
||||
},
|
||||
GasLimit: 100e6, // 100 M
|
||||
}
|
||||
)
|
||||
// Generate the original common chain segment and the two competing forks
|
||||
engine := ethash.NewFaker()
|
||||
db := rawdb.NewMemoryDatabase()
|
||||
genesis := gspec.MustCommit(db)
|
||||
|
||||
blockGenerator := func(i int, block *BlockGen) {
|
||||
block.SetCoinbase(common.Address{1})
|
||||
for txi := 0; txi < numTxs; txi++ {
|
||||
uniq := uint64(i*numTxs + txi)
|
||||
recipient := recipientFn(uniq)
|
||||
//recipient := common.BigToAddress(big.NewInt(0).SetUint64(1337 + uniq))
|
||||
tx, err := types.SignTx(types.NewTransaction(uniq, recipient, big.NewInt(1), params.TxGas, big.NewInt(1), nil), signer, testBankKey)
|
||||
if err != nil {
|
||||
b.Error(err)
|
||||
}
|
||||
block.AddTx(tx)
|
||||
}
|
||||
}
|
||||
|
||||
shared, _ := GenerateChain(params.TestChainConfig, genesis, engine, db, numBlocks, blockGenerator)
|
||||
b.StopTimer()
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
// Import the shared chain and the original canonical one
|
||||
diskdb := rawdb.NewMemoryDatabase()
|
||||
gspec.MustCommit(diskdb)
|
||||
|
||||
chain, err := NewBlockChain(diskdb, nil, params.TestChainConfig, engine, vm.Config{}, nil)
|
||||
if err != nil {
|
||||
b.Fatalf("failed to create tester chain: %v", err)
|
||||
}
|
||||
b.StartTimer()
|
||||
if _, err := chain.InsertChain(shared); err != nil {
|
||||
b.Fatalf("failed to insert shared chain: %v", err)
|
||||
}
|
||||
b.StopTimer()
|
||||
if got := chain.CurrentBlock().Transactions().Len(); got != numTxs*numBlocks {
|
||||
b.Fatalf("Transactions were not included, expected %d, got %d", numTxs*numBlocks, got)
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// getLongAndShortChains returns two chains,
|
||||
// A is longer, B is heavier
|
||||
func getLongAndShortChains() (*BlockChain, []*types.Block, []*types.Block, error) {
|
||||
|
|
@ -1931,3 +1995,55 @@ func TestReorgToShorterRemovesCanonMappingHeaderChain(t *testing.T) {
|
|||
t.Errorf("expected header to be gone: %v", headerByNum.Number.Uint64())
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkBlockChain_1x1000ValueTransferToNonexisting(b *testing.B) {
|
||||
var (
|
||||
numTxs = 1000
|
||||
numBlocks = 1
|
||||
)
|
||||
|
||||
recipientFn := func(nonce uint64) common.Address {
|
||||
return common.BigToAddress(big.NewInt(0).SetUint64(1337 + nonce))
|
||||
}
|
||||
dataFn := func(nonce uint64) []byte {
|
||||
return nil
|
||||
}
|
||||
|
||||
benchmarkLargeNumberOfValueToNonexisting(b, numTxs, numBlocks, recipientFn, dataFn)
|
||||
}
|
||||
|
||||
func BenchmarkBlockChain_1x1000ValueTransferToExisting(b *testing.B) {
|
||||
var (
|
||||
numTxs = 1000
|
||||
numBlocks = 1
|
||||
)
|
||||
b.StopTimer()
|
||||
b.ResetTimer()
|
||||
|
||||
recipientFn := func(nonce uint64) common.Address {
|
||||
return common.BigToAddress(big.NewInt(0).SetUint64(1337))
|
||||
}
|
||||
dataFn := func(nonce uint64) []byte {
|
||||
return nil
|
||||
}
|
||||
|
||||
benchmarkLargeNumberOfValueToNonexisting(b, numTxs, numBlocks, recipientFn, dataFn)
|
||||
}
|
||||
|
||||
func BenchmarkBlockChain_1x1000Executions(b *testing.B) {
|
||||
var (
|
||||
numTxs = 1000
|
||||
numBlocks = 1
|
||||
)
|
||||
b.StopTimer()
|
||||
b.ResetTimer()
|
||||
|
||||
recipientFn := func(nonce uint64) common.Address {
|
||||
return common.BigToAddress(big.NewInt(0).SetUint64(0xc0de))
|
||||
}
|
||||
dataFn := func(nonce uint64) []byte {
|
||||
return nil
|
||||
}
|
||||
|
||||
benchmarkLargeNumberOfValueToNonexisting(b, numTxs, numBlocks, recipientFn, dataFn)
|
||||
}
|
||||
Loading…
Reference in a new issue