| 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236 |
- // Copyright 2014 The go-ethereum Authors
- // This file is part of the go-ethereum library.
- //
- // The go-ethereum library is free software: you can redistribute it and/or modify
- // it under the terms of the GNU Lesser General Public License as published by
- // the Free Software Foundation, either version 3 of the License, or
- // (at your option) any later version.
- //
- // The go-ethereum library is distributed in the hope that it will be useful,
- // but WITHOUT ANY WARRANTY; without even the implied warranty of
- // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- // GNU Lesser General Public License for more details.
- //
- // You should have received a copy of the GNU Lesser General Public License
- // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
- package core
- import (
- "fmt"
- "io/ioutil"
- "math/big"
- "math/rand"
- "os"
- "sync"
- "testing"
- "time"
- "github.com/ethereum/go-ethereum/common"
- "github.com/ethereum/go-ethereum/consensus"
- "github.com/ethereum/go-ethereum/consensus/ethash"
- "github.com/ethereum/go-ethereum/core/rawdb"
- "github.com/ethereum/go-ethereum/core/state"
- "github.com/ethereum/go-ethereum/core/types"
- "github.com/ethereum/go-ethereum/core/vm"
- "github.com/ethereum/go-ethereum/crypto"
- "github.com/ethereum/go-ethereum/ethdb"
- "github.com/ethereum/go-ethereum/params"
- )
- // So we can deterministically seed different blockchains
- var (
- canonicalSeed = 1
- forkSeed = 2
- )
- // newCanonical creates a chain database, and injects a deterministic canonical
- // chain. Depending on the full flag, if creates either a full block chain or a
- // header only chain.
- func newCanonical(engine consensus.Engine, n int, full bool) (ethdb.Database, *BlockChain, error) {
- var (
- db = rawdb.NewMemoryDatabase()
- genesis = new(Genesis).MustCommit(db)
- )
- // Initialize a fresh chain with only a genesis block
- blockchain, _ := NewBlockChain(db, nil, params.AllEthashProtocolChanges, engine, vm.Config{}, nil)
- // Create and inject the requested chain
- if n == 0 {
- return db, blockchain, nil
- }
- if full {
- // Full block-chain requested
- blocks := makeBlockChain(genesis, n, engine, db, canonicalSeed)
- _, err := blockchain.InsertChain(blocks)
- return db, blockchain, err
- }
- // Header-only chain requested
- headers := makeHeaderChain(genesis.Header(), n, engine, db, canonicalSeed)
- _, err := blockchain.InsertHeaderChain(headers, 1)
- return db, blockchain, err
- }
- // Test fork of length N starting from block i
- func testFork(t *testing.T, blockchain *BlockChain, i, n int, full bool, comparator func(td1, td2 *big.Int)) {
- // Copy old chain up to #i into a new db
- db, blockchain2, err := newCanonical(ethash.NewFaker(), i, full)
- if err != nil {
- t.Fatal("could not make new canonical in testFork", err)
- }
- defer blockchain2.Stop()
- // Assert the chains have the same header/block at #i
- var hash1, hash2 common.Hash
- if full {
- hash1 = blockchain.GetBlockByNumber(uint64(i)).Hash()
- hash2 = blockchain2.GetBlockByNumber(uint64(i)).Hash()
- } else {
- hash1 = blockchain.GetHeaderByNumber(uint64(i)).Hash()
- hash2 = blockchain2.GetHeaderByNumber(uint64(i)).Hash()
- }
- if hash1 != hash2 {
- t.Errorf("chain content mismatch at %d: have hash %v, want hash %v", i, hash2, hash1)
- }
- // Extend the newly created chain
- var (
- blockChainB []*types.Block
- headerChainB []*types.Header
- )
- if full {
- blockChainB = makeBlockChain(blockchain2.CurrentBlock(), n, ethash.NewFaker(), db, forkSeed)
- if _, err := blockchain2.InsertChain(blockChainB); err != nil {
- t.Fatalf("failed to insert forking chain: %v", err)
- }
- } else {
- headerChainB = makeHeaderChain(blockchain2.CurrentHeader(), n, ethash.NewFaker(), db, forkSeed)
- if _, err := blockchain2.InsertHeaderChain(headerChainB, 1); err != nil {
- t.Fatalf("failed to insert forking chain: %v", err)
- }
- }
- // Sanity check that the forked chain can be imported into the original
- var tdPre, tdPost *big.Int
- if full {
- tdPre = blockchain.GetTdByHash(blockchain.CurrentBlock().Hash())
- if err := testBlockChainImport(blockChainB, blockchain); err != nil {
- t.Fatalf("failed to import forked block chain: %v", err)
- }
- tdPost = blockchain.GetTdByHash(blockChainB[len(blockChainB)-1].Hash())
- } else {
- tdPre = blockchain.GetTdByHash(blockchain.CurrentHeader().Hash())
- if err := testHeaderChainImport(headerChainB, blockchain); err != nil {
- t.Fatalf("failed to import forked header chain: %v", err)
- }
- tdPost = blockchain.GetTdByHash(headerChainB[len(headerChainB)-1].Hash())
- }
- // Compare the total difficulties of the chains
- comparator(tdPre, tdPost)
- }
- // testBlockChainImport tries to process a chain of blocks, writing them into
- // the database if successful.
- func testBlockChainImport(chain types.Blocks, blockchain *BlockChain) error {
- for _, block := range chain {
- // Try and process the block
- err := blockchain.engine.VerifyHeader(blockchain, block.Header(), true)
- if err == nil {
- err = blockchain.validator.ValidateBody(block)
- }
- if err != nil {
- if err == ErrKnownBlock {
- continue
- }
- return err
- }
- statedb, err := state.New(blockchain.GetBlockByHash(block.ParentHash()).Root(), blockchain.stateCache)
- if err != nil {
- return err
- }
- receipts, _, usedGas, err := blockchain.processor.Process(block, statedb, vm.Config{})
- if err != nil {
- blockchain.reportBlock(block, receipts, err)
- return err
- }
- err = blockchain.validator.ValidateState(block, statedb, receipts, usedGas)
- if err != nil {
- blockchain.reportBlock(block, receipts, err)
- return err
- }
- blockchain.chainmu.Lock()
- rawdb.WriteTd(blockchain.db, block.Hash(), block.NumberU64(), new(big.Int).Add(block.Difficulty(), blockchain.GetTdByHash(block.ParentHash())))
- rawdb.WriteBlock(blockchain.db, block)
- statedb.Commit(false)
- blockchain.chainmu.Unlock()
- }
- return nil
- }
- // testHeaderChainImport tries to process a chain of header, writing them into
- // the database if successful.
- func testHeaderChainImport(chain []*types.Header, blockchain *BlockChain) error {
- for _, header := range chain {
- // Try and validate the header
- if err := blockchain.engine.VerifyHeader(blockchain, header, false); err != nil {
- return err
- }
- // Manually insert the header into the database, but don't reorganise (allows subsequent testing)
- blockchain.chainmu.Lock()
- rawdb.WriteTd(blockchain.db, header.Hash(), header.Number.Uint64(), new(big.Int).Add(header.Difficulty, blockchain.GetTdByHash(header.ParentHash)))
- rawdb.WriteHeader(blockchain.db, header)
- blockchain.chainmu.Unlock()
- }
- return nil
- }
- func TestLastBlock(t *testing.T) {
- _, blockchain, err := newCanonical(ethash.NewFaker(), 0, true)
- if err != nil {
- t.Fatalf("failed to create pristine chain: %v", err)
- }
- defer blockchain.Stop()
- blocks := makeBlockChain(blockchain.CurrentBlock(), 1, ethash.NewFullFaker(), blockchain.db, 0)
- if _, err := blockchain.InsertChain(blocks); err != nil {
- t.Fatalf("Failed to insert block: %v", err)
- }
- if blocks[len(blocks)-1].Hash() != rawdb.ReadHeadBlockHash(blockchain.db) {
- t.Fatalf("Write/Get HeadBlockHash failed")
- }
- }
- // Tests that given a starting canonical chain of a given size, it can be extended
- // with various length chains.
- func TestExtendCanonicalHeaders(t *testing.T) { testExtendCanonical(t, false) }
- func TestExtendCanonicalBlocks(t *testing.T) { testExtendCanonical(t, true) }
- func testExtendCanonical(t *testing.T, full bool) {
- length := 5
- // Make first chain starting from genesis
- _, processor, err := newCanonical(ethash.NewFaker(), length, full)
- if err != nil {
- t.Fatalf("failed to make new canonical chain: %v", err)
- }
- defer processor.Stop()
- // Define the difficulty comparator
- better := func(td1, td2 *big.Int) {
- if td2.Cmp(td1) <= 0 {
- t.Errorf("total difficulty mismatch: have %v, expected more than %v", td2, td1)
- }
- }
- // Start fork from current height
- testFork(t, processor, length, 1, full, better)
- testFork(t, processor, length, 2, full, better)
- testFork(t, processor, length, 5, full, better)
- testFork(t, processor, length, 10, full, better)
- }
- // Tests that given a starting canonical chain of a given size, creating shorter
- // forks do not take canonical ownership.
- func TestShorterForkHeaders(t *testing.T) { testShorterFork(t, false) }
- func TestShorterForkBlocks(t *testing.T) { testShorterFork(t, true) }
- func testShorterFork(t *testing.T, full bool) {
- length := 10
- // Make first chain starting from genesis
- _, processor, err := newCanonical(ethash.NewFaker(), length, full)
- if err != nil {
- t.Fatalf("failed to make new canonical chain: %v", err)
- }
- defer processor.Stop()
- // Define the difficulty comparator
- worse := func(td1, td2 *big.Int) {
- if td2.Cmp(td1) >= 0 {
- t.Errorf("total difficulty mismatch: have %v, expected less than %v", td2, td1)
- }
- }
- // Sum of numbers must be less than `length` for this to be a shorter fork
- testFork(t, processor, 0, 3, full, worse)
- testFork(t, processor, 0, 7, full, worse)
- testFork(t, processor, 1, 1, full, worse)
- testFork(t, processor, 1, 7, full, worse)
- testFork(t, processor, 5, 3, full, worse)
- testFork(t, processor, 5, 4, full, worse)
- }
- // Tests that given a starting canonical chain of a given size, creating longer
- // forks do take canonical ownership.
- func TestLongerForkHeaders(t *testing.T) { testLongerFork(t, false) }
- func TestLongerForkBlocks(t *testing.T) { testLongerFork(t, true) }
- func testLongerFork(t *testing.T, full bool) {
- length := 10
- // Make first chain starting from genesis
- _, processor, err := newCanonical(ethash.NewFaker(), length, full)
- if err != nil {
- t.Fatalf("failed to make new canonical chain: %v", err)
- }
- defer processor.Stop()
- // Define the difficulty comparator
- better := func(td1, td2 *big.Int) {
- if td2.Cmp(td1) <= 0 {
- t.Errorf("total difficulty mismatch: have %v, expected more than %v", td2, td1)
- }
- }
- // Sum of numbers must be greater than `length` for this to be a longer fork
- testFork(t, processor, 0, 11, full, better)
- testFork(t, processor, 0, 15, full, better)
- testFork(t, processor, 1, 10, full, better)
- testFork(t, processor, 1, 12, full, better)
- testFork(t, processor, 5, 6, full, better)
- testFork(t, processor, 5, 8, full, better)
- }
- // Tests that given a starting canonical chain of a given size, creating equal
- // forks do take canonical ownership.
- func TestEqualForkHeaders(t *testing.T) { testEqualFork(t, false) }
- func TestEqualForkBlocks(t *testing.T) { testEqualFork(t, true) }
- func testEqualFork(t *testing.T, full bool) {
- length := 10
- // Make first chain starting from genesis
- _, processor, err := newCanonical(ethash.NewFaker(), length, full)
- if err != nil {
- t.Fatalf("failed to make new canonical chain: %v", err)
- }
- defer processor.Stop()
- // Define the difficulty comparator
- equal := func(td1, td2 *big.Int) {
- if td2.Cmp(td1) != 0 {
- t.Errorf("total difficulty mismatch: have %v, want %v", td2, td1)
- }
- }
- // Sum of numbers must be equal to `length` for this to be an equal fork
- testFork(t, processor, 0, 10, full, equal)
- testFork(t, processor, 1, 9, full, equal)
- testFork(t, processor, 2, 8, full, equal)
- testFork(t, processor, 5, 5, full, equal)
- testFork(t, processor, 6, 4, full, equal)
- testFork(t, processor, 9, 1, full, equal)
- }
- // Tests that chains missing links do not get accepted by the processor.
- func TestBrokenHeaderChain(t *testing.T) { testBrokenChain(t, false) }
- func TestBrokenBlockChain(t *testing.T) { testBrokenChain(t, true) }
- func testBrokenChain(t *testing.T, full bool) {
- // Make chain starting from genesis
- db, blockchain, err := newCanonical(ethash.NewFaker(), 10, full)
- if err != nil {
- t.Fatalf("failed to make new canonical chain: %v", err)
- }
- defer blockchain.Stop()
- // Create a forked chain, and try to insert with a missing link
- if full {
- chain := makeBlockChain(blockchain.CurrentBlock(), 5, ethash.NewFaker(), db, forkSeed)[1:]
- if err := testBlockChainImport(chain, blockchain); err == nil {
- t.Errorf("broken block chain not reported")
- }
- } else {
- chain := makeHeaderChain(blockchain.CurrentHeader(), 5, ethash.NewFaker(), db, forkSeed)[1:]
- if err := testHeaderChainImport(chain, blockchain); err == nil {
- t.Errorf("broken header chain not reported")
- }
- }
- }
- // Tests that reorganising a long difficult chain after a short easy one
- // overwrites the canonical numbers and links in the database.
- func TestReorgLongHeaders(t *testing.T) { testReorgLong(t, false) }
- func TestReorgLongBlocks(t *testing.T) { testReorgLong(t, true) }
- func testReorgLong(t *testing.T, full bool) {
- testReorg(t, []int64{0, 0, -9}, []int64{0, 0, 0, -9}, 393280, full)
- }
- // Tests that reorganising a short difficult chain after a long easy one
- // overwrites the canonical numbers and links in the database.
- func TestReorgShortHeaders(t *testing.T) { testReorgShort(t, false) }
- func TestReorgShortBlocks(t *testing.T) { testReorgShort(t, true) }
- func testReorgShort(t *testing.T, full bool) {
- // Create a long easy chain vs. a short heavy one. Due to difficulty adjustment
- // we need a fairly long chain of blocks with different difficulties for a short
- // one to become heavyer than a long one. The 96 is an empirical value.
- easy := make([]int64, 96)
- for i := 0; i < len(easy); i++ {
- easy[i] = 60
- }
- diff := make([]int64, len(easy)-1)
- for i := 0; i < len(diff); i++ {
- diff[i] = -9
- }
- testReorg(t, easy, diff, 12615120, full)
- }
- func testReorg(t *testing.T, first, second []int64, td int64, full bool) {
- // Create a pristine chain and database
- db, blockchain, err := newCanonical(ethash.NewFaker(), 0, full)
- if err != nil {
- t.Fatalf("failed to create pristine chain: %v", err)
- }
- defer blockchain.Stop()
- // Insert an easy and a difficult chain afterwards
- easyBlocks, _ := GenerateChain(params.TestChainConfig, blockchain.CurrentBlock(), ethash.NewFaker(), db, len(first), func(i int, b *BlockGen) {
- b.OffsetTime(first[i])
- })
- diffBlocks, _ := GenerateChain(params.TestChainConfig, blockchain.CurrentBlock(), ethash.NewFaker(), db, len(second), func(i int, b *BlockGen) {
- b.OffsetTime(second[i])
- })
- if full {
- if _, err := blockchain.InsertChain(easyBlocks); err != nil {
- t.Fatalf("failed to insert easy chain: %v", err)
- }
- if _, err := blockchain.InsertChain(diffBlocks); err != nil {
- t.Fatalf("failed to insert difficult chain: %v", err)
- }
- } else {
- easyHeaders := make([]*types.Header, len(easyBlocks))
- for i, block := range easyBlocks {
- easyHeaders[i] = block.Header()
- }
- diffHeaders := make([]*types.Header, len(diffBlocks))
- for i, block := range diffBlocks {
- diffHeaders[i] = block.Header()
- }
- if _, err := blockchain.InsertHeaderChain(easyHeaders, 1); err != nil {
- t.Fatalf("failed to insert easy chain: %v", err)
- }
- if _, err := blockchain.InsertHeaderChain(diffHeaders, 1); err != nil {
- t.Fatalf("failed to insert difficult chain: %v", err)
- }
- }
- // Check that the chain is valid number and link wise
- if full {
- prev := blockchain.CurrentBlock()
- for block := blockchain.GetBlockByNumber(blockchain.CurrentBlock().NumberU64() - 1); block.NumberU64() != 0; prev, block = block, blockchain.GetBlockByNumber(block.NumberU64()-1) {
- if prev.ParentHash() != block.Hash() {
- t.Errorf("parent block hash mismatch: have %x, want %x", prev.ParentHash(), block.Hash())
- }
- }
- } else {
- prev := blockchain.CurrentHeader()
- for header := blockchain.GetHeaderByNumber(blockchain.CurrentHeader().Number.Uint64() - 1); header.Number.Uint64() != 0; prev, header = header, blockchain.GetHeaderByNumber(header.Number.Uint64()-1) {
- if prev.ParentHash != header.Hash() {
- t.Errorf("parent header hash mismatch: have %x, want %x", prev.ParentHash, header.Hash())
- }
- }
- }
- // Make sure the chain total difficulty is the correct one
- want := new(big.Int).Add(blockchain.genesisBlock.Difficulty(), big.NewInt(td))
- if full {
- if have := blockchain.GetTdByHash(blockchain.CurrentBlock().Hash()); have.Cmp(want) != 0 {
- t.Errorf("total difficulty mismatch: have %v, want %v", have, want)
- }
- } else {
- if have := blockchain.GetTdByHash(blockchain.CurrentHeader().Hash()); have.Cmp(want) != 0 {
- t.Errorf("total difficulty mismatch: have %v, want %v", have, want)
- }
- }
- }
- // Tests that the insertion functions detect banned hashes.
- func TestBadHeaderHashes(t *testing.T) { testBadHashes(t, false) }
- func TestBadBlockHashes(t *testing.T) { testBadHashes(t, true) }
- func testBadHashes(t *testing.T, full bool) {
- // Create a pristine chain and database
- db, blockchain, err := newCanonical(ethash.NewFaker(), 0, full)
- if err != nil {
- t.Fatalf("failed to create pristine chain: %v", err)
- }
- defer blockchain.Stop()
- // Create a chain, ban a hash and try to import
- if full {
- blocks := makeBlockChain(blockchain.CurrentBlock(), 3, ethash.NewFaker(), db, 10)
- BadHashes[blocks[2].Header().Hash()] = true
- defer func() { delete(BadHashes, blocks[2].Header().Hash()) }()
- _, err = blockchain.InsertChain(blocks)
- } else {
- headers := makeHeaderChain(blockchain.CurrentHeader(), 3, ethash.NewFaker(), db, 10)
- BadHashes[headers[2].Hash()] = true
- defer func() { delete(BadHashes, headers[2].Hash()) }()
- _, err = blockchain.InsertHeaderChain(headers, 1)
- }
- if err != ErrBlacklistedHash {
- t.Errorf("error mismatch: have: %v, want: %v", err, ErrBlacklistedHash)
- }
- }
- // Tests that bad hashes are detected on boot, and the chain rolled back to a
- // good state prior to the bad hash.
- func TestReorgBadHeaderHashes(t *testing.T) { testReorgBadHashes(t, false) }
- func TestReorgBadBlockHashes(t *testing.T) { testReorgBadHashes(t, true) }
- func testReorgBadHashes(t *testing.T, full bool) {
- // Create a pristine chain and database
- db, blockchain, err := newCanonical(ethash.NewFaker(), 0, full)
- if err != nil {
- t.Fatalf("failed to create pristine chain: %v", err)
- }
- // Create a chain, import and ban afterwards
- headers := makeHeaderChain(blockchain.CurrentHeader(), 4, ethash.NewFaker(), db, 10)
- blocks := makeBlockChain(blockchain.CurrentBlock(), 4, ethash.NewFaker(), db, 10)
- if full {
- if _, err = blockchain.InsertChain(blocks); err != nil {
- t.Errorf("failed to import blocks: %v", err)
- }
- if blockchain.CurrentBlock().Hash() != blocks[3].Hash() {
- t.Errorf("last block hash mismatch: have: %x, want %x", blockchain.CurrentBlock().Hash(), blocks[3].Header().Hash())
- }
- BadHashes[blocks[3].Header().Hash()] = true
- defer func() { delete(BadHashes, blocks[3].Header().Hash()) }()
- } else {
- if _, err = blockchain.InsertHeaderChain(headers, 1); err != nil {
- t.Errorf("failed to import headers: %v", err)
- }
- if blockchain.CurrentHeader().Hash() != headers[3].Hash() {
- t.Errorf("last header hash mismatch: have: %x, want %x", blockchain.CurrentHeader().Hash(), headers[3].Hash())
- }
- BadHashes[headers[3].Hash()] = true
- defer func() { delete(BadHashes, headers[3].Hash()) }()
- }
- blockchain.Stop()
- // Create a new BlockChain and check that it rolled back the state.
- ncm, err := NewBlockChain(blockchain.db, nil, blockchain.chainConfig, ethash.NewFaker(), vm.Config{}, nil)
- if err != nil {
- t.Fatalf("failed to create new chain manager: %v", err)
- }
- if full {
- if ncm.CurrentBlock().Hash() != blocks[2].Header().Hash() {
- t.Errorf("last block hash mismatch: have: %x, want %x", ncm.CurrentBlock().Hash(), blocks[2].Header().Hash())
- }
- if blocks[2].Header().GasLimit != ncm.GasLimit() {
- t.Errorf("last block gasLimit mismatch: have: %d, want %d", ncm.GasLimit(), blocks[2].Header().GasLimit)
- }
- } else {
- if ncm.CurrentHeader().Hash() != headers[2].Hash() {
- t.Errorf("last header hash mismatch: have: %x, want %x", ncm.CurrentHeader().Hash(), headers[2].Hash())
- }
- }
- ncm.Stop()
- }
- // Tests chain insertions in the face of one entity containing an invalid nonce.
- func TestHeadersInsertNonceError(t *testing.T) { testInsertNonceError(t, false) }
- func TestBlocksInsertNonceError(t *testing.T) { testInsertNonceError(t, true) }
- func testInsertNonceError(t *testing.T, full bool) {
- for i := 1; i < 25 && !t.Failed(); i++ {
- // Create a pristine chain and database
- db, blockchain, err := newCanonical(ethash.NewFaker(), 0, full)
- if err != nil {
- t.Fatalf("failed to create pristine chain: %v", err)
- }
- defer blockchain.Stop()
- // Create and insert a chain with a failing nonce
- var (
- failAt int
- failRes int
- failNum uint64
- )
- if full {
- blocks := makeBlockChain(blockchain.CurrentBlock(), i, ethash.NewFaker(), db, 0)
- failAt = rand.Int() % len(blocks)
- failNum = blocks[failAt].NumberU64()
- blockchain.engine = ethash.NewFakeFailer(failNum)
- failRes, err = blockchain.InsertChain(blocks)
- } else {
- headers := makeHeaderChain(blockchain.CurrentHeader(), i, ethash.NewFaker(), db, 0)
- failAt = rand.Int() % len(headers)
- failNum = headers[failAt].Number.Uint64()
- blockchain.engine = ethash.NewFakeFailer(failNum)
- blockchain.hc.engine = blockchain.engine
- failRes, err = blockchain.InsertHeaderChain(headers, 1)
- }
- // Check that the returned error indicates the failure
- if failRes != failAt {
- t.Errorf("test %d: failure (%v) index mismatch: have %d, want %d", i, err, failRes, failAt)
- }
- // Check that all blocks after the failing block have been inserted
- for j := 0; j < i-failAt; j++ {
- if full {
- if block := blockchain.GetBlockByNumber(failNum + uint64(j)); block != nil {
- t.Errorf("test %d: invalid block in chain: %v", i, block)
- }
- } else {
- if header := blockchain.GetHeaderByNumber(failNum + uint64(j)); header != nil {
- t.Errorf("test %d: invalid header in chain: %v", i, header)
- }
- }
- }
- }
- }
- // Tests that fast importing a block chain produces the same chain data as the
- // classical full block processing.
- func TestFastVsFullChains(t *testing.T) {
- // Configure and generate a sample block chain
- var (
- gendb = rawdb.NewMemoryDatabase()
- key, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
- address = crypto.PubkeyToAddress(key.PublicKey)
- funds = big.NewInt(1000000000)
- gspec = &Genesis{
- Config: params.TestChainConfig,
- Alloc: GenesisAlloc{address: {Balance: funds}},
- }
- genesis = gspec.MustCommit(gendb)
- signer = types.NewEIP155Signer(gspec.Config.ChainID)
- )
- blocks, receipts := GenerateChain(gspec.Config, genesis, ethash.NewFaker(), gendb, 1024, func(i int, block *BlockGen) {
- block.SetCoinbase(common.Address{0x00})
- // If the block number is multiple of 3, send a few bonus transactions to the miner
- if i%3 == 2 {
- for j := 0; j < i%4+1; j++ {
- tx, err := types.SignTx(types.NewTransaction(block.TxNonce(address), common.Address{0x00}, big.NewInt(1000), params.TxGas, nil, nil), signer, key)
- if err != nil {
- panic(err)
- }
- block.AddTx(tx)
- }
- }
- // If the block number is a multiple of 5, add a few bonus uncles to the block
- if i%5 == 5 {
- block.AddUncle(&types.Header{ParentHash: block.PrevBlock(i - 1).Hash(), Number: big.NewInt(int64(i - 1))})
- }
- })
- // Import the chain as an archive node for the comparison baseline
- archiveDb := rawdb.NewMemoryDatabase()
- gspec.MustCommit(archiveDb)
- archive, _ := NewBlockChain(archiveDb, nil, gspec.Config, ethash.NewFaker(), vm.Config{}, nil)
- defer archive.Stop()
- if n, err := archive.InsertChain(blocks); err != nil {
- t.Fatalf("failed to process block %d: %v", n, err)
- }
- // Fast import the chain as a non-archive node to test
- fastDb := rawdb.NewMemoryDatabase()
- gspec.MustCommit(fastDb)
- fast, _ := NewBlockChain(fastDb, nil, gspec.Config, ethash.NewFaker(), vm.Config{}, nil)
- defer fast.Stop()
- headers := make([]*types.Header, len(blocks))
- for i, block := range blocks {
- headers[i] = block.Header()
- }
- if n, err := fast.InsertHeaderChain(headers, 1); err != nil {
- t.Fatalf("failed to insert header %d: %v", n, err)
- }
- if n, err := fast.InsertReceiptChain(blocks, receipts, 0); err != nil {
- t.Fatalf("failed to insert receipt %d: %v", n, err)
- }
- // Freezer style fast import the chain.
- frdir, err := ioutil.TempDir("", "")
- if err != nil {
- t.Fatalf("failed to create temp freezer dir: %v", err)
- }
- defer os.Remove(frdir)
- ancientDb, err := rawdb.NewDatabaseWithFreezer(rawdb.NewMemoryDatabase(), frdir, "")
- if err != nil {
- t.Fatalf("failed to create temp freezer db: %v", err)
- }
- gspec.MustCommit(ancientDb)
- ancient, _ := NewBlockChain(ancientDb, nil, gspec.Config, ethash.NewFaker(), vm.Config{}, nil)
- defer ancient.Stop()
- if n, err := ancient.InsertHeaderChain(headers, 1); err != nil {
- t.Fatalf("failed to insert header %d: %v", n, err)
- }
- if n, err := ancient.InsertReceiptChain(blocks, receipts, uint64(len(blocks)/2)); err != nil {
- t.Fatalf("failed to insert receipt %d: %v", n, err)
- }
- // Iterate over all chain data components, and cross reference
- for i := 0; i < len(blocks); i++ {
- num, hash := blocks[i].NumberU64(), blocks[i].Hash()
- if ftd, atd := fast.GetTdByHash(hash), archive.GetTdByHash(hash); ftd.Cmp(atd) != 0 {
- t.Errorf("block #%d [%x]: td mismatch: fastdb %v, archivedb %v", num, hash, ftd, atd)
- }
- if antd, artd := ancient.GetTdByHash(hash), archive.GetTdByHash(hash); antd.Cmp(artd) != 0 {
- t.Errorf("block #%d [%x]: td mismatch: ancientdb %v, archivedb %v", num, hash, antd, artd)
- }
- if fheader, aheader := fast.GetHeaderByHash(hash), archive.GetHeaderByHash(hash); fheader.Hash() != aheader.Hash() {
- t.Errorf("block #%d [%x]: header mismatch: fastdb %v, archivedb %v", num, hash, fheader, aheader)
- }
- if anheader, arheader := ancient.GetHeaderByHash(hash), archive.GetHeaderByHash(hash); anheader.Hash() != arheader.Hash() {
- t.Errorf("block #%d [%x]: header mismatch: ancientdb %v, archivedb %v", num, hash, anheader, arheader)
- }
- if fblock, arblock, anblock := fast.GetBlockByHash(hash), archive.GetBlockByHash(hash), ancient.GetBlockByHash(hash); fblock.Hash() != arblock.Hash() || anblock.Hash() != arblock.Hash() {
- t.Errorf("block #%d [%x]: block mismatch: fastdb %v, ancientdb %v, archivedb %v", num, hash, fblock, anblock, arblock)
- } else if types.DeriveSha(fblock.Transactions()) != types.DeriveSha(arblock.Transactions()) || types.DeriveSha(anblock.Transactions()) != types.DeriveSha(arblock.Transactions()) {
- t.Errorf("block #%d [%x]: transactions mismatch: fastdb %v, ancientdb %v, archivedb %v", num, hash, fblock.Transactions(), anblock.Transactions(), arblock.Transactions())
- } else if types.CalcUncleHash(fblock.Uncles()) != types.CalcUncleHash(arblock.Uncles()) || types.CalcUncleHash(anblock.Uncles()) != types.CalcUncleHash(arblock.Uncles()) {
- t.Errorf("block #%d [%x]: uncles mismatch: fastdb %v, ancientdb %v, archivedb %v", num, hash, fblock.Uncles(), anblock, arblock.Uncles())
- }
- if freceipts, anreceipts, areceipts := rawdb.ReadReceipts(fastDb, hash, *rawdb.ReadHeaderNumber(fastDb, hash), fast.Config()), rawdb.ReadReceipts(ancientDb, hash, *rawdb.ReadHeaderNumber(ancientDb, hash), fast.Config()), rawdb.ReadReceipts(archiveDb, hash, *rawdb.ReadHeaderNumber(archiveDb, hash), fast.Config()); types.DeriveSha(freceipts) != types.DeriveSha(areceipts) {
- t.Errorf("block #%d [%x]: receipts mismatch: fastdb %v, ancientdb %v, archivedb %v", num, hash, freceipts, anreceipts, areceipts)
- }
- }
- // Check that the canonical chains are the same between the databases
- for i := 0; i < len(blocks)+1; i++ {
- if fhash, ahash := rawdb.ReadCanonicalHash(fastDb, uint64(i)), rawdb.ReadCanonicalHash(archiveDb, uint64(i)); fhash != ahash {
- t.Errorf("block #%d: canonical hash mismatch: fastdb %v, archivedb %v", i, fhash, ahash)
- }
- if anhash, arhash := rawdb.ReadCanonicalHash(ancientDb, uint64(i)), rawdb.ReadCanonicalHash(archiveDb, uint64(i)); anhash != arhash {
- t.Errorf("block #%d: canonical hash mismatch: ancientdb %v, archivedb %v", i, anhash, arhash)
- }
- }
- }
- // Tests that various import methods move the chain head pointers to the correct
- // positions.
- func TestLightVsFastVsFullChainHeads(t *testing.T) {
- // Configure and generate a sample block chain
- var (
- gendb = rawdb.NewMemoryDatabase()
- key, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
- address = crypto.PubkeyToAddress(key.PublicKey)
- funds = big.NewInt(1000000000)
- gspec = &Genesis{Config: params.TestChainConfig, Alloc: GenesisAlloc{address: {Balance: funds}}}
- genesis = gspec.MustCommit(gendb)
- )
- height := uint64(1024)
- blocks, receipts := GenerateChain(gspec.Config, genesis, ethash.NewFaker(), gendb, int(height), nil)
- // Configure a subchain to roll back
- remove := []common.Hash{}
- for _, block := range blocks[height/2:] {
- remove = append(remove, block.Hash())
- }
- // Create a small assertion method to check the three heads
- assert := func(t *testing.T, kind string, chain *BlockChain, header uint64, fast uint64, block uint64) {
- if num := chain.CurrentBlock().NumberU64(); num != block {
- t.Errorf("%s head block mismatch: have #%v, want #%v", kind, num, block)
- }
- if num := chain.CurrentFastBlock().NumberU64(); num != fast {
- t.Errorf("%s head fast-block mismatch: have #%v, want #%v", kind, num, fast)
- }
- if num := chain.CurrentHeader().Number.Uint64(); num != header {
- t.Errorf("%s head header mismatch: have #%v, want #%v", kind, num, header)
- }
- }
- // Import the chain as an archive node and ensure all pointers are updated
- archiveDb := rawdb.NewMemoryDatabase()
- gspec.MustCommit(archiveDb)
- archive, _ := NewBlockChain(archiveDb, nil, gspec.Config, ethash.NewFaker(), vm.Config{}, nil)
- if n, err := archive.InsertChain(blocks); err != nil {
- t.Fatalf("failed to process block %d: %v", n, err)
- }
- defer archive.Stop()
- assert(t, "archive", archive, height, height, height)
- archive.Rollback(remove)
- assert(t, "archive", archive, height/2, height/2, height/2)
- // Import the chain as a non-archive node and ensure all pointers are updated
- fastDb := rawdb.NewMemoryDatabase()
- gspec.MustCommit(fastDb)
- fast, _ := NewBlockChain(fastDb, nil, gspec.Config, ethash.NewFaker(), vm.Config{}, nil)
- defer fast.Stop()
- headers := make([]*types.Header, len(blocks))
- for i, block := range blocks {
- headers[i] = block.Header()
- }
- if n, err := fast.InsertHeaderChain(headers, 1); err != nil {
- t.Fatalf("failed to insert header %d: %v", n, err)
- }
- if n, err := fast.InsertReceiptChain(blocks, receipts, 0); err != nil {
- t.Fatalf("failed to insert receipt %d: %v", n, err)
- }
- assert(t, "fast", fast, height, height, 0)
- fast.Rollback(remove)
- assert(t, "fast", fast, height/2, height/2, 0)
- // Import the chain as a ancient-first node and ensure all pointers are updated
- frdir, err := ioutil.TempDir("", "")
- if err != nil {
- t.Fatalf("failed to create temp freezer dir: %v", err)
- }
- defer os.Remove(frdir)
- ancientDb, err := rawdb.NewDatabaseWithFreezer(rawdb.NewMemoryDatabase(), frdir, "")
- if err != nil {
- t.Fatalf("failed to create temp freezer db: %v", err)
- }
- gspec.MustCommit(ancientDb)
- ancient, _ := NewBlockChain(ancientDb, nil, gspec.Config, ethash.NewFaker(), vm.Config{}, nil)
- defer ancient.Stop()
- if n, err := ancient.InsertHeaderChain(headers, 1); err != nil {
- t.Fatalf("failed to insert header %d: %v", n, err)
- }
- if n, err := ancient.InsertReceiptChain(blocks, receipts, uint64(3*len(blocks)/4)); err != nil {
- t.Fatalf("failed to insert receipt %d: %v", n, err)
- }
- assert(t, "ancient", ancient, height, height, 0)
- ancient.Rollback(remove)
- assert(t, "ancient", ancient, height/2, height/2, 0)
- if frozen, err := ancientDb.Ancients(); err != nil || frozen != height/2+1 {
- t.Fatalf("failed to truncate ancient store, want %v, have %v", height/2+1, frozen)
- }
- // Import the chain as a light node and ensure all pointers are updated
- lightDb := rawdb.NewMemoryDatabase()
- gspec.MustCommit(lightDb)
- light, _ := NewBlockChain(lightDb, nil, gspec.Config, ethash.NewFaker(), vm.Config{}, nil)
- if n, err := light.InsertHeaderChain(headers, 1); err != nil {
- t.Fatalf("failed to insert header %d: %v", n, err)
- }
- defer light.Stop()
- assert(t, "light", light, height, 0, 0)
- light.Rollback(remove)
- assert(t, "light", light, height/2, 0, 0)
- }
- // Tests that chain reorganisations handle transaction removals and reinsertions.
- func TestChainTxReorgs(t *testing.T) {
- var (
- key1, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
- key2, _ = crypto.HexToECDSA("8a1f9a8f95be41cd7ccb6168179afb4504aefe388d1e14474d32c45c72ce7b7a")
- key3, _ = crypto.HexToECDSA("49a7b37aa6f6645917e7b807e9d1c00d4fa71f18343b0d4122a4d2df64dd6fee")
- addr1 = crypto.PubkeyToAddress(key1.PublicKey)
- addr2 = crypto.PubkeyToAddress(key2.PublicKey)
- addr3 = crypto.PubkeyToAddress(key3.PublicKey)
- db = rawdb.NewMemoryDatabase()
- gspec = &Genesis{
- Config: params.TestChainConfig,
- GasLimit: 3141592,
- Alloc: GenesisAlloc{
- addr1: {Balance: big.NewInt(1000000)},
- addr2: {Balance: big.NewInt(1000000)},
- addr3: {Balance: big.NewInt(1000000)},
- },
- }
- genesis = gspec.MustCommit(db)
- signer = types.NewEIP155Signer(gspec.Config.ChainID)
- )
- // Create two transactions shared between the chains:
- // - postponed: transaction included at a later block in the forked chain
- // - swapped: transaction included at the same block number in the forked chain
- postponed, _ := types.SignTx(types.NewTransaction(0, addr1, big.NewInt(1000), params.TxGas, nil, nil), signer, key1)
- swapped, _ := types.SignTx(types.NewTransaction(1, addr1, big.NewInt(1000), params.TxGas, nil, nil), signer, key1)
- // Create two transactions that will be dropped by the forked chain:
- // - pastDrop: transaction dropped retroactively from a past block
- // - freshDrop: transaction dropped exactly at the block where the reorg is detected
- var pastDrop, freshDrop *types.Transaction
- // Create three transactions that will be added in the forked chain:
- // - pastAdd: transaction added before the reorganization is detected
- // - freshAdd: transaction added at the exact block the reorg is detected
- // - futureAdd: transaction added after the reorg has already finished
- var pastAdd, freshAdd, futureAdd *types.Transaction
- chain, _ := GenerateChain(gspec.Config, genesis, ethash.NewFaker(), db, 3, func(i int, gen *BlockGen) {
- switch i {
- case 0:
- pastDrop, _ = types.SignTx(types.NewTransaction(gen.TxNonce(addr2), addr2, big.NewInt(1000), params.TxGas, nil, nil), signer, key2)
- gen.AddTx(pastDrop) // This transaction will be dropped in the fork from below the split point
- gen.AddTx(postponed) // This transaction will be postponed till block #3 in the fork
- case 2:
- freshDrop, _ = types.SignTx(types.NewTransaction(gen.TxNonce(addr2), addr2, big.NewInt(1000), params.TxGas, nil, nil), signer, key2)
- gen.AddTx(freshDrop) // This transaction will be dropped in the fork from exactly at the split point
- gen.AddTx(swapped) // This transaction will be swapped out at the exact height
- gen.OffsetTime(9) // Lower the block difficulty to simulate a weaker chain
- }
- })
- // Import the chain. This runs all block validation rules.
- blockchain, _ := NewBlockChain(db, nil, gspec.Config, ethash.NewFaker(), vm.Config{}, nil)
- if i, err := blockchain.InsertChain(chain); err != nil {
- t.Fatalf("failed to insert original chain[%d]: %v", i, err)
- }
- defer blockchain.Stop()
- // overwrite the old chain
- chain, _ = GenerateChain(gspec.Config, genesis, ethash.NewFaker(), db, 5, func(i int, gen *BlockGen) {
- switch i {
- case 0:
- pastAdd, _ = types.SignTx(types.NewTransaction(gen.TxNonce(addr3), addr3, big.NewInt(1000), params.TxGas, nil, nil), signer, key3)
- gen.AddTx(pastAdd) // This transaction needs to be injected during reorg
- case 2:
- gen.AddTx(postponed) // This transaction was postponed from block #1 in the original chain
- gen.AddTx(swapped) // This transaction was swapped from the exact current spot in the original chain
- freshAdd, _ = types.SignTx(types.NewTransaction(gen.TxNonce(addr3), addr3, big.NewInt(1000), params.TxGas, nil, nil), signer, key3)
- gen.AddTx(freshAdd) // This transaction will be added exactly at reorg time
- case 3:
- futureAdd, _ = types.SignTx(types.NewTransaction(gen.TxNonce(addr3), addr3, big.NewInt(1000), params.TxGas, nil, nil), signer, key3)
- gen.AddTx(futureAdd) // This transaction will be added after a full reorg
- }
- })
- if _, err := blockchain.InsertChain(chain); err != nil {
- t.Fatalf("failed to insert forked chain: %v", err)
- }
- // removed tx
- for i, tx := range (types.Transactions{pastDrop, freshDrop}) {
- if txn, _, _, _ := rawdb.ReadTransaction(db, tx.Hash()); txn != nil {
- t.Errorf("drop %d: tx %v found while shouldn't have been", i, txn)
- }
- if rcpt, _, _, _ := rawdb.ReadReceipt(db, tx.Hash(), blockchain.Config()); rcpt != nil {
- t.Errorf("drop %d: receipt %v found while shouldn't have been", i, rcpt)
- }
- }
- // added tx
- for i, tx := range (types.Transactions{pastAdd, freshAdd, futureAdd}) {
- if txn, _, _, _ := rawdb.ReadTransaction(db, tx.Hash()); txn == nil {
- t.Errorf("add %d: expected tx to be found", i)
- }
- if rcpt, _, _, _ := rawdb.ReadReceipt(db, tx.Hash(), blockchain.Config()); rcpt == nil {
- t.Errorf("add %d: expected receipt to be found", i)
- }
- }
- // shared tx
- for i, tx := range (types.Transactions{postponed, swapped}) {
- if txn, _, _, _ := rawdb.ReadTransaction(db, tx.Hash()); txn == nil {
- t.Errorf("share %d: expected tx to be found", i)
- }
- if rcpt, _, _, _ := rawdb.ReadReceipt(db, tx.Hash(), blockchain.Config()); rcpt == nil {
- t.Errorf("share %d: expected receipt to be found", i)
- }
- }
- }
- func TestLogReorgs(t *testing.T) {
- var (
- key1, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
- addr1 = crypto.PubkeyToAddress(key1.PublicKey)
- db = rawdb.NewMemoryDatabase()
- // this code generates a log
- code = common.Hex2Bytes("60606040525b7f24ec1d3ff24c2f6ff210738839dbc339cd45a5294d85c79361016243157aae7b60405180905060405180910390a15b600a8060416000396000f360606040526008565b00")
- gspec = &Genesis{Config: params.TestChainConfig, Alloc: GenesisAlloc{addr1: {Balance: big.NewInt(10000000000000)}}}
- genesis = gspec.MustCommit(db)
- signer = types.NewEIP155Signer(gspec.Config.ChainID)
- )
- blockchain, _ := NewBlockChain(db, nil, gspec.Config, ethash.NewFaker(), vm.Config{}, nil)
- defer blockchain.Stop()
- rmLogsCh := make(chan RemovedLogsEvent)
- blockchain.SubscribeRemovedLogsEvent(rmLogsCh)
- chain, _ := GenerateChain(params.TestChainConfig, genesis, ethash.NewFaker(), db, 2, func(i int, gen *BlockGen) {
- if i == 1 {
- tx, err := types.SignTx(types.NewContractCreation(gen.TxNonce(addr1), new(big.Int), 1000000, new(big.Int), code), signer, key1)
- if err != nil {
- t.Fatalf("failed to create tx: %v", err)
- }
- gen.AddTx(tx)
- }
- })
- if _, err := blockchain.InsertChain(chain); err != nil {
- t.Fatalf("failed to insert chain: %v", err)
- }
- chain, _ = GenerateChain(params.TestChainConfig, genesis, ethash.NewFaker(), db, 3, func(i int, gen *BlockGen) {})
- if _, err := blockchain.InsertChain(chain); err != nil {
- t.Fatalf("failed to insert forked chain: %v", err)
- }
- timeout := time.NewTimer(1 * time.Second)
- select {
- case ev := <-rmLogsCh:
- if len(ev.Logs) == 0 {
- t.Error("expected logs")
- }
- case <-timeout.C:
- t.Fatal("Timeout. There is no RemovedLogsEvent has been sent.")
- }
- }
- func TestLogRebirth(t *testing.T) {
- var (
- key1, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
- addr1 = crypto.PubkeyToAddress(key1.PublicKey)
- db = rawdb.NewMemoryDatabase()
- // this code generates a log
- code = common.Hex2Bytes("60606040525b7f24ec1d3ff24c2f6ff210738839dbc339cd45a5294d85c79361016243157aae7b60405180905060405180910390a15b600a8060416000396000f360606040526008565b00")
- gspec = &Genesis{Config: params.TestChainConfig, Alloc: GenesisAlloc{addr1: {Balance: big.NewInt(10000000000000)}}}
- genesis = gspec.MustCommit(db)
- signer = types.NewEIP155Signer(gspec.Config.ChainID)
- newLogCh = make(chan bool)
- )
- // listenNewLog checks whether the received logs number is equal with expected.
- listenNewLog := func(sink chan []*types.Log, expect int) {
- cnt := 0
- for {
- select {
- case logs := <-sink:
- cnt += len(logs)
- case <-time.NewTimer(5 * time.Second).C:
- // new logs timeout
- newLogCh <- false
- return
- }
- if cnt == expect {
- break
- } else if cnt > expect {
- // redundant logs received
- newLogCh <- false
- return
- }
- }
- select {
- case <-sink:
- // redundant logs received
- newLogCh <- false
- case <-time.NewTimer(100 * time.Millisecond).C:
- newLogCh <- true
- }
- }
- blockchain, _ := NewBlockChain(db, nil, gspec.Config, ethash.NewFaker(), vm.Config{}, nil)
- defer blockchain.Stop()
- logsCh := make(chan []*types.Log)
- blockchain.SubscribeLogsEvent(logsCh)
- rmLogsCh := make(chan RemovedLogsEvent)
- blockchain.SubscribeRemovedLogsEvent(rmLogsCh)
- chain, _ := GenerateChain(params.TestChainConfig, genesis, ethash.NewFaker(), db, 2, func(i int, gen *BlockGen) {
- if i == 1 {
- tx, err := types.SignTx(types.NewContractCreation(gen.TxNonce(addr1), new(big.Int), 1000000, new(big.Int), code), signer, key1)
- if err != nil {
- t.Fatalf("failed to create tx: %v", err)
- }
- gen.AddTx(tx)
- }
- })
- // Spawn a goroutine to receive log events
- go listenNewLog(logsCh, 1)
- if _, err := blockchain.InsertChain(chain); err != nil {
- t.Fatalf("failed to insert chain: %v", err)
- }
- if !<-newLogCh {
- t.Fatalf("failed to receive new log event")
- }
- // Generate long reorg chain
- forkChain, _ := GenerateChain(params.TestChainConfig, genesis, ethash.NewFaker(), db, 2, func(i int, gen *BlockGen) {
- if i == 1 {
- tx, err := types.SignTx(types.NewContractCreation(gen.TxNonce(addr1), new(big.Int), 1000000, new(big.Int), code), signer, key1)
- if err != nil {
- t.Fatalf("failed to create tx: %v", err)
- }
- gen.AddTx(tx)
- // Higher block difficulty
- gen.OffsetTime(-9)
- }
- })
- // Spawn a goroutine to receive log events
- go listenNewLog(logsCh, 1)
- if _, err := blockchain.InsertChain(forkChain); err != nil {
- t.Fatalf("failed to insert forked chain: %v", err)
- }
- if !<-newLogCh {
- t.Fatalf("failed to receive new log event")
- }
- // Ensure removedLog events received
- select {
- case ev := <-rmLogsCh:
- if len(ev.Logs) == 0 {
- t.Error("expected logs")
- }
- case <-time.NewTimer(1 * time.Second).C:
- t.Fatal("Timeout. There is no RemovedLogsEvent has been sent.")
- }
- newBlocks, _ := GenerateChain(params.TestChainConfig, chain[len(chain)-1], ethash.NewFaker(), db, 1, func(i int, gen *BlockGen) {})
- go listenNewLog(logsCh, 1)
- if _, err := blockchain.InsertChain(newBlocks); err != nil {
- t.Fatalf("failed to insert forked chain: %v", err)
- }
- // Ensure removedLog events received
- select {
- case ev := <-rmLogsCh:
- if len(ev.Logs) == 0 {
- t.Error("expected logs")
- }
- case <-time.NewTimer(1 * time.Second).C:
- t.Fatal("Timeout. There is no RemovedLogsEvent has been sent.")
- }
- // Rebirth logs should omit a newLogEvent
- if !<-newLogCh {
- t.Fatalf("failed to receive new log event")
- }
- }
- func TestSideLogRebirth(t *testing.T) {
- var (
- key1, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
- addr1 = crypto.PubkeyToAddress(key1.PublicKey)
- db = rawdb.NewMemoryDatabase()
- // this code generates a log
- code = common.Hex2Bytes("60606040525b7f24ec1d3ff24c2f6ff210738839dbc339cd45a5294d85c79361016243157aae7b60405180905060405180910390a15b600a8060416000396000f360606040526008565b00")
- gspec = &Genesis{Config: params.TestChainConfig, Alloc: GenesisAlloc{addr1: {Balance: big.NewInt(10000000000000)}}}
- genesis = gspec.MustCommit(db)
- signer = types.NewEIP155Signer(gspec.Config.ChainID)
- newLogCh = make(chan bool)
- )
- // listenNewLog checks whether the received logs number is equal with expected.
- listenNewLog := func(sink chan []*types.Log, expect int) {
- cnt := 0
- for {
- select {
- case logs := <-sink:
- cnt += len(logs)
- case <-time.NewTimer(5 * time.Second).C:
- // new logs timeout
- newLogCh <- false
- return
- }
- if cnt == expect {
- break
- } else if cnt > expect {
- // redundant logs received
- newLogCh <- false
- return
- }
- }
- select {
- case <-sink:
- // redundant logs received
- newLogCh <- false
- case <-time.NewTimer(100 * time.Millisecond).C:
- newLogCh <- true
- }
- }
- blockchain, _ := NewBlockChain(db, nil, gspec.Config, ethash.NewFaker(), vm.Config{}, nil)
- defer blockchain.Stop()
- logsCh := make(chan []*types.Log)
- blockchain.SubscribeLogsEvent(logsCh)
- chain, _ := GenerateChain(params.TestChainConfig, genesis, ethash.NewFaker(), db, 2, func(i int, gen *BlockGen) {
- if i == 1 {
- // Higher block difficulty
- gen.OffsetTime(-9)
- }
- })
- if _, err := blockchain.InsertChain(chain); err != nil {
- t.Fatalf("failed to insert forked chain: %v", err)
- }
- // Generate side chain with lower difficulty
- sideChain, _ := GenerateChain(params.TestChainConfig, genesis, ethash.NewFaker(), db, 2, func(i int, gen *BlockGen) {
- if i == 1 {
- tx, err := types.SignTx(types.NewContractCreation(gen.TxNonce(addr1), new(big.Int), 1000000, new(big.Int), code), signer, key1)
- if err != nil {
- t.Fatalf("failed to create tx: %v", err)
- }
- gen.AddTx(tx)
- }
- })
- if _, err := blockchain.InsertChain(sideChain); err != nil {
- t.Fatalf("failed to insert forked chain: %v", err)
- }
- // Generate a new block based on side chain
- newBlocks, _ := GenerateChain(params.TestChainConfig, sideChain[len(sideChain)-1], ethash.NewFaker(), db, 1, func(i int, gen *BlockGen) {})
- go listenNewLog(logsCh, 1)
- if _, err := blockchain.InsertChain(newBlocks); err != nil {
- t.Fatalf("failed to insert forked chain: %v", err)
- }
- // Rebirth logs should omit a newLogEvent
- if !<-newLogCh {
- t.Fatalf("failed to receive new log event")
- }
- }
- func TestReorgSideEvent(t *testing.T) {
- var (
- db = rawdb.NewMemoryDatabase()
- key1, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
- addr1 = crypto.PubkeyToAddress(key1.PublicKey)
- gspec = &Genesis{
- Config: params.TestChainConfig,
- Alloc: GenesisAlloc{addr1: {Balance: big.NewInt(10000000000000)}},
- }
- genesis = gspec.MustCommit(db)
- signer = types.NewEIP155Signer(gspec.Config.ChainID)
- )
- blockchain, _ := NewBlockChain(db, nil, gspec.Config, ethash.NewFaker(), vm.Config{}, nil)
- defer blockchain.Stop()
- chain, _ := GenerateChain(gspec.Config, genesis, ethash.NewFaker(), db, 3, func(i int, gen *BlockGen) {})
- if _, err := blockchain.InsertChain(chain); err != nil {
- t.Fatalf("failed to insert chain: %v", err)
- }
- replacementBlocks, _ := GenerateChain(gspec.Config, genesis, ethash.NewFaker(), db, 4, func(i int, gen *BlockGen) {
- tx, err := types.SignTx(types.NewContractCreation(gen.TxNonce(addr1), new(big.Int), 1000000, new(big.Int), nil), signer, key1)
- if i == 2 {
- gen.OffsetTime(-9)
- }
- if err != nil {
- t.Fatalf("failed to create tx: %v", err)
- }
- gen.AddTx(tx)
- })
- chainSideCh := make(chan ChainSideEvent, 64)
- blockchain.SubscribeChainSideEvent(chainSideCh)
- if _, err := blockchain.InsertChain(replacementBlocks); err != nil {
- t.Fatalf("failed to insert chain: %v", err)
- }
- // first two block of the secondary chain are for a brief moment considered
- // side chains because up to that point the first one is considered the
- // heavier chain.
- expectedSideHashes := map[common.Hash]bool{
- replacementBlocks[0].Hash(): true,
- replacementBlocks[1].Hash(): true,
- chain[0].Hash(): true,
- chain[1].Hash(): true,
- chain[2].Hash(): true,
- }
- i := 0
- const timeoutDura = 10 * time.Second
- timeout := time.NewTimer(timeoutDura)
- done:
- for {
- select {
- case ev := <-chainSideCh:
- block := ev.Block
- if _, ok := expectedSideHashes[block.Hash()]; !ok {
- t.Errorf("%d: didn't expect %x to be in side chain", i, block.Hash())
- }
- i++
- if i == len(expectedSideHashes) {
- timeout.Stop()
- break done
- }
- timeout.Reset(timeoutDura)
- case <-timeout.C:
- t.Fatal("Timeout. Possibly not all blocks were triggered for sideevent")
- }
- }
- // make sure no more events are fired
- select {
- case e := <-chainSideCh:
- t.Errorf("unexpected event fired: %v", e)
- case <-time.After(250 * time.Millisecond):
- }
- }
- // Tests if the canonical block can be fetched from the database during chain insertion.
- func TestCanonicalBlockRetrieval(t *testing.T) {
- _, blockchain, err := newCanonical(ethash.NewFaker(), 0, true)
- if err != nil {
- t.Fatalf("failed to create pristine chain: %v", err)
- }
- defer blockchain.Stop()
- chain, _ := GenerateChain(blockchain.chainConfig, blockchain.genesisBlock, ethash.NewFaker(), blockchain.db, 10, func(i int, gen *BlockGen) {})
- var pend sync.WaitGroup
- pend.Add(len(chain))
- for i := range chain {
- go func(block *types.Block) {
- defer pend.Done()
- // try to retrieve a block by its canonical hash and see if the block data can be retrieved.
- for {
- ch := rawdb.ReadCanonicalHash(blockchain.db, block.NumberU64())
- if ch == (common.Hash{}) {
- continue // busy wait for canonical hash to be written
- }
- if ch != block.Hash() {
- t.Fatalf("unknown canonical hash, want %s, got %s", block.Hash().Hex(), ch.Hex())
- }
- fb := rawdb.ReadBlock(blockchain.db, ch, block.NumberU64())
- if fb == nil {
- t.Fatalf("unable to retrieve block %d for canonical hash: %s", block.NumberU64(), ch.Hex())
- }
- if fb.Hash() != block.Hash() {
- t.Fatalf("invalid block hash for block %d, want %s, got %s", block.NumberU64(), block.Hash().Hex(), fb.Hash().Hex())
- }
- return
- }
- }(chain[i])
- if _, err := blockchain.InsertChain(types.Blocks{chain[i]}); err != nil {
- t.Fatalf("failed to insert block %d: %v", i, err)
- }
- }
- pend.Wait()
- }
- func TestEIP155Transition(t *testing.T) {
- // Configure and generate a sample block chain
- var (
- db = rawdb.NewMemoryDatabase()
- key, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
- address = crypto.PubkeyToAddress(key.PublicKey)
- funds = big.NewInt(1000000000)
- deleteAddr = common.Address{1}
- gspec = &Genesis{
- Config: ¶ms.ChainConfig{ChainID: big.NewInt(1), EIP155Block: big.NewInt(2), HomesteadBlock: new(big.Int)},
- Alloc: GenesisAlloc{address: {Balance: funds}, deleteAddr: {Balance: new(big.Int)}},
- }
- genesis = gspec.MustCommit(db)
- )
- blockchain, _ := NewBlockChain(db, nil, gspec.Config, ethash.NewFaker(), vm.Config{}, nil)
- defer blockchain.Stop()
- blocks, _ := GenerateChain(gspec.Config, genesis, ethash.NewFaker(), db, 4, func(i int, block *BlockGen) {
- var (
- tx *types.Transaction
- err error
- basicTx = func(signer types.Signer) (*types.Transaction, error) {
- return types.SignTx(types.NewTransaction(block.TxNonce(address), common.Address{}, new(big.Int), 21000, new(big.Int), nil), signer, key)
- }
- )
- switch i {
- case 0:
- tx, err = basicTx(types.HomesteadSigner{})
- if err != nil {
- t.Fatal(err)
- }
- block.AddTx(tx)
- case 2:
- tx, err = basicTx(types.HomesteadSigner{})
- if err != nil {
- t.Fatal(err)
- }
- block.AddTx(tx)
- tx, err = basicTx(types.NewEIP155Signer(gspec.Config.ChainID))
- if err != nil {
- t.Fatal(err)
- }
- block.AddTx(tx)
- case 3:
- tx, err = basicTx(types.HomesteadSigner{})
- if err != nil {
- t.Fatal(err)
- }
- block.AddTx(tx)
- tx, err = basicTx(types.NewEIP155Signer(gspec.Config.ChainID))
- if err != nil {
- t.Fatal(err)
- }
- block.AddTx(tx)
- }
- })
- if _, err := blockchain.InsertChain(blocks); err != nil {
- t.Fatal(err)
- }
- block := blockchain.GetBlockByNumber(1)
- if block.Transactions()[0].Protected() {
- t.Error("Expected block[0].txs[0] to not be replay protected")
- }
- block = blockchain.GetBlockByNumber(3)
- if block.Transactions()[0].Protected() {
- t.Error("Expected block[3].txs[0] to not be replay protected")
- }
- if !block.Transactions()[1].Protected() {
- t.Error("Expected block[3].txs[1] to be replay protected")
- }
- if _, err := blockchain.InsertChain(blocks[4:]); err != nil {
- t.Fatal(err)
- }
- // generate an invalid chain id transaction
- config := ¶ms.ChainConfig{ChainID: big.NewInt(2), EIP155Block: big.NewInt(2), HomesteadBlock: new(big.Int)}
- blocks, _ = GenerateChain(config, blocks[len(blocks)-1], ethash.NewFaker(), db, 4, func(i int, block *BlockGen) {
- var (
- tx *types.Transaction
- err error
- basicTx = func(signer types.Signer) (*types.Transaction, error) {
- return types.SignTx(types.NewTransaction(block.TxNonce(address), common.Address{}, new(big.Int), 21000, new(big.Int), nil), signer, key)
- }
- )
- if i == 0 {
- tx, err = basicTx(types.NewEIP155Signer(big.NewInt(2)))
- if err != nil {
- t.Fatal(err)
- }
- block.AddTx(tx)
- }
- })
- _, err := blockchain.InsertChain(blocks)
- if err != types.ErrInvalidChainId {
- t.Error("expected error:", types.ErrInvalidChainId)
- }
- }
- func TestEIP161AccountRemoval(t *testing.T) {
- // Configure and generate a sample block chain
- var (
- db = rawdb.NewMemoryDatabase()
- key, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
- address = crypto.PubkeyToAddress(key.PublicKey)
- funds = big.NewInt(1000000000)
- theAddr = common.Address{1}
- gspec = &Genesis{
- Config: ¶ms.ChainConfig{
- ChainID: big.NewInt(1),
- HomesteadBlock: new(big.Int),
- EIP155Block: new(big.Int),
- EIP158Block: big.NewInt(2),
- },
- Alloc: GenesisAlloc{address: {Balance: funds}},
- }
- genesis = gspec.MustCommit(db)
- )
- blockchain, _ := NewBlockChain(db, nil, gspec.Config, ethash.NewFaker(), vm.Config{}, nil)
- defer blockchain.Stop()
- blocks, _ := GenerateChain(gspec.Config, genesis, ethash.NewFaker(), db, 3, func(i int, block *BlockGen) {
- var (
- tx *types.Transaction
- err error
- signer = types.NewEIP155Signer(gspec.Config.ChainID)
- )
- switch i {
- case 0:
- tx, err = types.SignTx(types.NewTransaction(block.TxNonce(address), theAddr, new(big.Int), 21000, new(big.Int), nil), signer, key)
- case 1:
- tx, err = types.SignTx(types.NewTransaction(block.TxNonce(address), theAddr, new(big.Int), 21000, new(big.Int), nil), signer, key)
- case 2:
- tx, err = types.SignTx(types.NewTransaction(block.TxNonce(address), theAddr, new(big.Int), 21000, new(big.Int), nil), signer, key)
- }
- if err != nil {
- t.Fatal(err)
- }
- block.AddTx(tx)
- })
- // account must exist pre eip 161
- if _, err := blockchain.InsertChain(types.Blocks{blocks[0]}); err != nil {
- t.Fatal(err)
- }
- if st, _ := blockchain.State(); !st.Exist(theAddr) {
- t.Error("expected account to exist")
- }
- // account needs to be deleted post eip 161
- if _, err := blockchain.InsertChain(types.Blocks{blocks[1]}); err != nil {
- t.Fatal(err)
- }
- if st, _ := blockchain.State(); st.Exist(theAddr) {
- t.Error("account should not exist")
- }
- // account musn't be created post eip 161
- if _, err := blockchain.InsertChain(types.Blocks{blocks[2]}); err != nil {
- t.Fatal(err)
- }
- if st, _ := blockchain.State(); st.Exist(theAddr) {
- t.Error("account should not exist")
- }
- }
- // This is a regression test (i.e. as weird as it is, don't delete it ever), which
- // tests that under weird reorg conditions the blockchain and its internal header-
- // chain return the same latest block/header.
- //
- // https://github.com/ethereum/go-ethereum/pull/15941
- func TestBlockchainHeaderchainReorgConsistency(t *testing.T) {
- // Generate a canonical chain to act as the main dataset
- engine := ethash.NewFaker()
- db := rawdb.NewMemoryDatabase()
- genesis := new(Genesis).MustCommit(db)
- blocks, _ := GenerateChain(params.TestChainConfig, genesis, engine, db, 64, func(i int, b *BlockGen) { b.SetCoinbase(common.Address{1}) })
- // Generate a bunch of fork blocks, each side forking from the canonical chain
- forks := make([]*types.Block, len(blocks))
- for i := 0; i < len(forks); i++ {
- parent := genesis
- if i > 0 {
- parent = blocks[i-1]
- }
- fork, _ := GenerateChain(params.TestChainConfig, parent, engine, db, 1, func(i int, b *BlockGen) { b.SetCoinbase(common.Address{2}) })
- forks[i] = fork[0]
- }
- // Import the canonical and fork chain side by side, verifying the current block
- // and current header consistency
- 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)
- }
- for i := 0; i < len(blocks); i++ {
- if _, err := chain.InsertChain(blocks[i : i+1]); err != nil {
- t.Fatalf("block %d: failed to insert into chain: %v", i, err)
- }
- if chain.CurrentBlock().Hash() != chain.CurrentHeader().Hash() {
- t.Errorf("block %d: current block/header mismatch: block #%d [%x…], header #%d [%x…]", i, chain.CurrentBlock().Number(), chain.CurrentBlock().Hash().Bytes()[:4], chain.CurrentHeader().Number, chain.CurrentHeader().Hash().Bytes()[:4])
- }
- if _, err := chain.InsertChain(forks[i : i+1]); err != nil {
- t.Fatalf(" fork %d: failed to insert into chain: %v", i, err)
- }
- if chain.CurrentBlock().Hash() != chain.CurrentHeader().Hash() {
- t.Errorf(" fork %d: current block/header mismatch: block #%d [%x…], header #%d [%x…]", i, chain.CurrentBlock().Number(), chain.CurrentBlock().Hash().Bytes()[:4], chain.CurrentHeader().Number, chain.CurrentHeader().Hash().Bytes()[:4])
- }
- }
- }
- // Tests that importing small side forks doesn't leave junk in the trie database
- // cache (which would eventually cause memory issues).
- func TestTrieForkGC(t *testing.T) {
- // Generate a canonical chain to act as the main dataset
- engine := ethash.NewFaker()
- db := rawdb.NewMemoryDatabase()
- genesis := new(Genesis).MustCommit(db)
- blocks, _ := GenerateChain(params.TestChainConfig, genesis, engine, db, 2*triesInMemory, func(i int, b *BlockGen) { b.SetCoinbase(common.Address{1}) })
- // Generate a bunch of fork blocks, each side forking from the canonical chain
- forks := make([]*types.Block, len(blocks))
- for i := 0; i < len(forks); i++ {
- parent := genesis
- if i > 0 {
- parent = blocks[i-1]
- }
- fork, _ := GenerateChain(params.TestChainConfig, parent, engine, db, 1, func(i int, b *BlockGen) { b.SetCoinbase(common.Address{2}) })
- forks[i] = fork[0]
- }
- // Import the canonical and fork chain side by side, forcing the trie cache to cache both
- 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)
- }
- for i := 0; i < len(blocks); i++ {
- if _, err := chain.InsertChain(blocks[i : i+1]); err != nil {
- t.Fatalf("block %d: failed to insert into chain: %v", i, err)
- }
- if _, err := chain.InsertChain(forks[i : i+1]); err != nil {
- t.Fatalf("fork %d: failed to insert into chain: %v", i, err)
- }
- }
- // Dereference all the recent tries and ensure no past trie is left in
- for i := 0; i < triesInMemory; i++ {
- chain.stateCache.TrieDB().Dereference(blocks[len(blocks)-1-i].Root())
- chain.stateCache.TrieDB().Dereference(forks[len(blocks)-1-i].Root())
- }
- if len(chain.stateCache.TrieDB().Nodes()) > 0 {
- t.Fatalf("stale tries still alive after garbase collection")
- }
- }
- // Tests that doing large reorgs works even if the state associated with the
- // forking point is not available any more.
- func TestLargeReorgTrieGC(t *testing.T) {
- // Generate the original common chain segment and the two competing forks
- engine := ethash.NewFaker()
- db := rawdb.NewMemoryDatabase()
- genesis := new(Genesis).MustCommit(db)
- shared, _ := GenerateChain(params.TestChainConfig, genesis, engine, db, 64, func(i int, b *BlockGen) { b.SetCoinbase(common.Address{1}) })
- original, _ := GenerateChain(params.TestChainConfig, shared[len(shared)-1], engine, db, 2*triesInMemory, func(i int, b *BlockGen) { b.SetCoinbase(common.Address{2}) })
- competitor, _ := GenerateChain(params.TestChainConfig, shared[len(shared)-1], engine, db, 2*triesInMemory+1, func(i int, b *BlockGen) { b.SetCoinbase(common.Address{3}) })
- // 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)
- }
- if _, err := chain.InsertChain(shared); err != nil {
- t.Fatalf("failed to insert shared chain: %v", err)
- }
- if _, err := chain.InsertChain(original); err != nil {
- t.Fatalf("failed to insert original chain: %v", err)
- }
- // Ensure that the state associated with the forking point is pruned away
- if node, _ := chain.stateCache.TrieDB().Node(shared[len(shared)-1].Root()); node != nil {
- t.Fatalf("common-but-old ancestor still cache")
- }
- // Import the competitor chain without exceeding the canonical's TD and ensure
- // we have not processed any of the blocks (protection against malicious blocks)
- if _, err := chain.InsertChain(competitor[:len(competitor)-2]); err != nil {
- t.Fatalf("failed to insert competitor chain: %v", err)
- }
- for i, block := range competitor[:len(competitor)-2] {
- if node, _ := chain.stateCache.TrieDB().Node(block.Root()); node != nil {
- t.Fatalf("competitor %d: low TD chain became processed", i)
- }
- }
- // Import the head of the competitor chain, triggering the reorg and ensure we
- // successfully reprocess all the stashed away blocks.
- if _, err := chain.InsertChain(competitor[len(competitor)-2:]); err != nil {
- t.Fatalf("failed to finalize competitor chain: %v", err)
- }
- for i, block := range competitor[:len(competitor)-triesInMemory] {
- if node, _ := chain.stateCache.TrieDB().Node(block.Root()); node != nil {
- t.Fatalf("competitor %d: competing chain state missing", i)
- }
- }
- }
- func TestBlockchainRecovery(t *testing.T) {
- // Configure and generate a sample block chain
- var (
- gendb = rawdb.NewMemoryDatabase()
- key, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
- address = crypto.PubkeyToAddress(key.PublicKey)
- funds = big.NewInt(1000000000)
- gspec = &Genesis{Config: params.TestChainConfig, Alloc: GenesisAlloc{address: {Balance: funds}}}
- genesis = gspec.MustCommit(gendb)
- )
- height := uint64(1024)
- blocks, receipts := GenerateChain(gspec.Config, genesis, ethash.NewFaker(), gendb, int(height), nil)
- // Import the chain as a ancient-first node and ensure all pointers are updated
- frdir, err := ioutil.TempDir("", "")
- if err != nil {
- t.Fatalf("failed to create temp freezer dir: %v", err)
- }
- defer os.Remove(frdir)
- ancientDb, err := rawdb.NewDatabaseWithFreezer(rawdb.NewMemoryDatabase(), frdir, "")
- if err != nil {
- t.Fatalf("failed to create temp freezer db: %v", err)
- }
- gspec.MustCommit(ancientDb)
- ancient, _ := NewBlockChain(ancientDb, nil, gspec.Config, ethash.NewFaker(), vm.Config{}, nil)
- headers := make([]*types.Header, len(blocks))
- for i, block := range blocks {
- headers[i] = block.Header()
- }
- if n, err := ancient.InsertHeaderChain(headers, 1); err != nil {
- t.Fatalf("failed to insert header %d: %v", n, err)
- }
- if n, err := ancient.InsertReceiptChain(blocks, receipts, uint64(3*len(blocks)/4)); err != nil {
- t.Fatalf("failed to insert receipt %d: %v", n, err)
- }
- ancient.Stop()
- // Destroy head fast block manually
- midBlock := blocks[len(blocks)/2]
- rawdb.WriteHeadFastBlockHash(ancientDb, midBlock.Hash())
- // Reopen broken blockchain again
- ancient, _ = NewBlockChain(ancientDb, nil, gspec.Config, ethash.NewFaker(), vm.Config{}, nil)
- defer ancient.Stop()
- if num := ancient.CurrentBlock().NumberU64(); num != 0 {
- t.Errorf("head block mismatch: have #%v, want #%v", num, 0)
- }
- if num := ancient.CurrentFastBlock().NumberU64(); num != midBlock.NumberU64() {
- t.Errorf("head fast-block mismatch: have #%v, want #%v", num, midBlock.NumberU64())
- }
- if num := ancient.CurrentHeader().Number.Uint64(); num != midBlock.NumberU64() {
- t.Errorf("head header mismatch: have #%v, want #%v", num, midBlock.NumberU64())
- }
- }
- func TestIncompleteAncientReceiptChainInsertion(t *testing.T) {
- // Configure and generate a sample block chain
- var (
- gendb = rawdb.NewMemoryDatabase()
- key, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
- address = crypto.PubkeyToAddress(key.PublicKey)
- funds = big.NewInt(1000000000)
- gspec = &Genesis{Config: params.TestChainConfig, Alloc: GenesisAlloc{address: {Balance: funds}}}
- genesis = gspec.MustCommit(gendb)
- )
- height := uint64(1024)
- blocks, receipts := GenerateChain(gspec.Config, genesis, ethash.NewFaker(), gendb, int(height), nil)
- // Import the chain as a ancient-first node and ensure all pointers are updated
- frdir, err := ioutil.TempDir("", "")
- if err != nil {
- t.Fatalf("failed to create temp freezer dir: %v", err)
- }
- defer os.Remove(frdir)
- ancientDb, err := rawdb.NewDatabaseWithFreezer(rawdb.NewMemoryDatabase(), frdir, "")
- if err != nil {
- t.Fatalf("failed to create temp freezer db: %v", err)
- }
- gspec.MustCommit(ancientDb)
- ancient, _ := NewBlockChain(ancientDb, nil, gspec.Config, ethash.NewFaker(), vm.Config{}, nil)
- defer ancient.Stop()
- headers := make([]*types.Header, len(blocks))
- for i, block := range blocks {
- headers[i] = block.Header()
- }
- if n, err := ancient.InsertHeaderChain(headers, 1); err != nil {
- t.Fatalf("failed to insert header %d: %v", n, err)
- }
- // Abort ancient receipt chain insertion deliberately
- ancient.terminateInsert = func(hash common.Hash, number uint64) bool {
- if number == blocks[len(blocks)/2].NumberU64() {
- return true
- }
- return false
- }
- previousFastBlock := ancient.CurrentFastBlock()
- if n, err := ancient.InsertReceiptChain(blocks, receipts, uint64(3*len(blocks)/4)); err == nil {
- t.Fatalf("failed to insert receipt %d: %v", n, err)
- }
- if ancient.CurrentFastBlock().NumberU64() != previousFastBlock.NumberU64() {
- t.Fatalf("failed to rollback ancient data, want %d, have %d", previousFastBlock.NumberU64(), ancient.CurrentFastBlock().NumberU64())
- }
- if frozen, err := ancient.db.Ancients(); err != nil || frozen != 1 {
- t.Fatalf("failed to truncate ancient data")
- }
- ancient.terminateInsert = nil
- if n, err := ancient.InsertReceiptChain(blocks, receipts, uint64(3*len(blocks)/4)); err != nil {
- t.Fatalf("failed to insert receipt %d: %v", n, err)
- }
- if ancient.CurrentFastBlock().NumberU64() != blocks[len(blocks)-1].NumberU64() {
- t.Fatalf("failed to insert ancient recept chain after rollback")
- }
- }
- // 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.
- //
- // Details at:
- // - https://github.com/ethereum/go-ethereum/issues/18977
- // - https://github.com/ethereum/go-ethereum/pull/18988
- func TestLowDiffLongChain(t *testing.T) {
- // Generate a canonical chain to act as the main dataset
- engine := ethash.NewFaker()
- db := rawdb.NewMemoryDatabase()
- genesis := new(Genesis).MustCommit(db)
- // We must use a pretty long chain to ensure that the fork doesn't overtake us
- // until after at least 128 blocks post tip
- blocks, _ := GenerateChain(params.TestChainConfig, genesis, engine, db, 6*triesInMemory, func(i int, b *BlockGen) {
- b.SetCoinbase(common.Address{1})
- b.OffsetTime(-9)
- })
- // Import the canonical chain
- 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)
- }
- if n, err := chain.InsertChain(blocks); err != nil {
- t.Fatalf("block %d: failed to insert into chain: %v", n, err)
- }
- // Generate fork chain, starting from an early block
- parent := blocks[10]
- fork, _ := GenerateChain(params.TestChainConfig, parent, engine, db, 8*triesInMemory, func(i int, b *BlockGen) {
- b.SetCoinbase(common.Address{2})
- })
- // And now import the fork
- if i, err := chain.InsertChain(fork); err != nil {
- t.Fatalf("block %d: failed to insert into chain: %v", i, err)
- }
- head := chain.CurrentBlock()
- if got := fork[len(fork)-1].Hash(); got != head.Hash() {
- t.Fatalf("head wrong, expected %x got %x", head.Hash(), got)
- }
- // Sanity check that all the canonical numbers are present
- header := chain.CurrentHeader()
- for number := head.NumberU64(); number > 0; number-- {
- if hash := chain.GetHeaderByNumber(number).Hash(); hash != header.Hash() {
- t.Fatalf("header %d: canonical hash mismatch: have %x, want %x", number, hash, header.Hash())
- }
- header = chain.GetHeader(header.ParentHash, number-1)
- }
- }
- // Tests that importing a sidechain (S), where
- // - S is sidechain, containing blocks [Sn...Sm]
- // - C is canon chain, containing blocks [G..Cn..Cm]
- // - A common ancestor is placed at prune-point + blocksBetweenCommonAncestorAndPruneblock
- // - The sidechain S is prepended with numCanonBlocksInSidechain blocks from the canon chain
- func testSideImport(t *testing.T, numCanonBlocksInSidechain, blocksBetweenCommonAncestorAndPruneblock int) {
- // Generate a canonical chain to act as the main dataset
- engine := ethash.NewFaker()
- db := rawdb.NewMemoryDatabase()
- genesis := new(Genesis).MustCommit(db)
- // Generate and import the canonical chain
- blocks, _ := GenerateChain(params.TestChainConfig, genesis, engine, db, 2*triesInMemory, nil)
- 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)
- }
- if n, err := chain.InsertChain(blocks); err != nil {
- t.Fatalf("block %d: failed to insert into chain: %v", n, err)
- }
- lastPrunedIndex := len(blocks) - triesInMemory - 1
- lastPrunedBlock := blocks[lastPrunedIndex]
- firstNonPrunedBlock := blocks[len(blocks)-triesInMemory]
- // Verify pruning of lastPrunedBlock
- if chain.HasBlockAndState(lastPrunedBlock.Hash(), lastPrunedBlock.NumberU64()) {
- t.Errorf("Block %d not pruned", lastPrunedBlock.NumberU64())
- }
- // Verify firstNonPrunedBlock is not pruned
- if !chain.HasBlockAndState(firstNonPrunedBlock.Hash(), firstNonPrunedBlock.NumberU64()) {
- t.Errorf("Block %d pruned", firstNonPrunedBlock.NumberU64())
- }
- // Generate the sidechain
- // First block should be a known block, block after should be a pruned block. So
- // canon(pruned), side, side...
- // Generate fork chain, make it longer than canon
- parentIndex := lastPrunedIndex + blocksBetweenCommonAncestorAndPruneblock
- parent := blocks[parentIndex]
- fork, _ := GenerateChain(params.TestChainConfig, parent, engine, db, 2*triesInMemory, func(i int, b *BlockGen) {
- b.SetCoinbase(common.Address{2})
- })
- // Prepend the parent(s)
- var sidechain []*types.Block
- for i := numCanonBlocksInSidechain; i > 0; i-- {
- sidechain = append(sidechain, blocks[parentIndex+1-i])
- }
- sidechain = append(sidechain, fork...)
- _, err = chain.InsertChain(sidechain)
- if err != nil {
- t.Errorf("Got error, %v", err)
- }
- head := chain.CurrentBlock()
- if got := fork[len(fork)-1].Hash(); got != head.Hash() {
- t.Fatalf("head wrong, expected %x got %x", head.Hash(), got)
- }
- }
- // Tests that importing a sidechain (S), where
- // - S is sidechain, containing blocks [Sn...Sm]
- // - C is canon chain, containing blocks [G..Cn..Cm]
- // - The common ancestor Cc is pruned
- // - The first block in S: Sn, is == Cn
- // That is: the sidechain for import contains some blocks already present in canon chain.
- // So the blocks are
- // [ Cn, Cn+1, Cc, Sn+3 ... Sm]
- // ^ ^ ^ pruned
- func TestPrunedImportSide(t *testing.T) {
- //glogger := log.NewGlogHandler(log.StreamHandler(os.Stdout, log.TerminalFormat(false)))
- //glogger.Verbosity(3)
- //log.Root().SetHandler(log.Handler(glogger))
- testSideImport(t, 3, 3)
- testSideImport(t, 3, -3)
- testSideImport(t, 10, 0)
- testSideImport(t, 1, 10)
- testSideImport(t, 1, -10)
- }
- func TestInsertKnownHeaders(t *testing.T) { testInsertKnownChainData(t, "headers") }
- func TestInsertKnownReceiptChain(t *testing.T) { testInsertKnownChainData(t, "receipts") }
- func TestInsertKnownBlocks(t *testing.T) { testInsertKnownChainData(t, "blocks") }
- func testInsertKnownChainData(t *testing.T, typ string) {
- engine := ethash.NewFaker()
- db := rawdb.NewMemoryDatabase()
- genesis := new(Genesis).MustCommit(db)
- blocks, receipts := GenerateChain(params.TestChainConfig, genesis, engine, db, 32, func(i int, b *BlockGen) { b.SetCoinbase(common.Address{1}) })
- // A longer chain but total difficulty is lower.
- blocks2, receipts2 := GenerateChain(params.TestChainConfig, blocks[len(blocks)-1], engine, db, 65, func(i int, b *BlockGen) { b.SetCoinbase(common.Address{1}) })
- // A shorter chain but total difficulty is higher.
- blocks3, receipts3 := GenerateChain(params.TestChainConfig, blocks[len(blocks)-1], engine, db, 64, func(i int, b *BlockGen) {
- b.SetCoinbase(common.Address{1})
- b.OffsetTime(-9) // A higher difficulty
- })
- // Import the shared chain and the original canonical one
- chaindb := rawdb.NewMemoryDatabase()
- new(Genesis).MustCommit(chaindb)
- chain, err := NewBlockChain(chaindb, nil, params.TestChainConfig, engine, vm.Config{}, nil)
- if err != nil {
- t.Fatalf("failed to create tester chain: %v", err)
- }
- var (
- inserter func(blocks []*types.Block, receipts []types.Receipts) error
- asserter func(t *testing.T, block *types.Block)
- )
- 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 typ == "headers" {
- inserter = func(blocks []*types.Block, receipts []types.Receipts) error {
- headers := make([]*types.Header, 0, len(blocks))
- for _, block := range blocks {
- headers = append(headers, block.Header())
- }
- _, err := chain.InsertHeaderChain(headers, 1)
- return err
- }
- asserter = func(t *testing.T, block *types.Block) {
- if chain.CurrentHeader().Hash() != block.Hash() {
- t.Fatalf("current head header mismatch, have %v, want %v", chain.CurrentHeader().Hash().Hex(), block.Hash().Hex())
- }
- }
- } else if typ == "receipts" {
- inserter = func(blocks []*types.Block, receipts []types.Receipts) error {
- headers := make([]*types.Header, 0, len(blocks))
- for _, block := range blocks {
- headers = append(headers, block.Header())
- }
- _, err := chain.InsertHeaderChain(headers, 1)
- if err != nil {
- return err
- }
- _, err = chain.InsertReceiptChain(blocks, receipts, 0)
- return err
- }
- asserter = func(t *testing.T, block *types.Block) {
- if chain.CurrentFastBlock().Hash() != block.Hash() {
- t.Fatalf("current head fast block mismatch, have %v, want %v", chain.CurrentFastBlock().Hash().Hex(), block.Hash().Hex())
- }
- }
- } else {
- inserter = func(blocks []*types.Block, receipts []types.Receipts) error {
- _, err := chain.InsertChain(blocks)
- return err
- }
- asserter = func(t *testing.T, block *types.Block) {
- if chain.CurrentBlock().Hash() != block.Hash() {
- t.Fatalf("current head block mismatch, have %v, want %v", chain.CurrentBlock().Hash().Hex(), block.Hash().Hex())
- }
- }
- }
- if err := inserter(blocks, receipts); err != nil {
- t.Fatalf("failed to insert chain data: %v", err)
- }
- // Reimport the chain data again. All the imported
- // chain data are regarded "known" data.
- if err := inserter(blocks, receipts); err != nil {
- t.Fatalf("failed to insert chain data: %v", err)
- }
- asserter(t, blocks[len(blocks)-1])
- // Import a long canonical chain with some known data as prefix.
- var rollback []common.Hash
- for i := len(blocks) / 2; i < len(blocks); i++ {
- rollback = append(rollback, blocks[i].Hash())
- }
- chain.Rollback(rollback)
- if err := inserter(append(blocks, blocks2...), append(receipts, receipts2...)); err != nil {
- t.Fatalf("failed to insert chain data: %v", err)
- }
- asserter(t, blocks2[len(blocks2)-1])
- // Import a heavier shorter but higher total difficulty chain with some known data as prefix.
- if err := inserter(append(blocks, blocks3...), append(receipts, receipts3...)); err != nil {
- t.Fatalf("failed to insert chain data: %v", err)
- }
- asserter(t, blocks3[len(blocks3)-1])
- // Import a longer but lower total difficulty chain with some known data as prefix.
- if err := inserter(append(blocks, blocks2...), append(receipts, receipts2...)); err != nil {
- t.Fatalf("failed to insert chain data: %v", err)
- }
- // The head shouldn't change.
- asserter(t, blocks3[len(blocks3)-1])
- if typ != "headers" {
- // Rollback the heavier chain and re-insert the longer chain again
- for i := 0; i < len(blocks3); i++ {
- rollback = append(rollback, blocks3[i].Hash())
- }
- chain.Rollback(rollback)
- if err := inserter(append(blocks, blocks2...), append(receipts, receipts2...)); err != nil {
- t.Fatalf("failed to insert chain data: %v", err)
- }
- asserter(t, blocks2[len(blocks2)-1])
- }
- }
- // getLongAndShortChains returns two chains,
- // A is longer, B is heavier
- func getLongAndShortChains() (*BlockChain, []*types.Block, []*types.Block, error) {
- // Generate a canonical chain to act as the main dataset
- engine := ethash.NewFaker()
- db := rawdb.NewMemoryDatabase()
- genesis := new(Genesis).MustCommit(db)
- // Generate and import the canonical chain,
- // Offset the time, to keep the difficulty low
- longChain, _ := GenerateChain(params.TestChainConfig, genesis, engine, db, 80, func(i int, b *BlockGen) {
- b.SetCoinbase(common.Address{1})
- })
- diskdb := rawdb.NewMemoryDatabase()
- new(Genesis).MustCommit(diskdb)
- chain, err := NewBlockChain(diskdb, nil, params.TestChainConfig, engine, vm.Config{}, nil)
- if err != nil {
- return nil, nil, nil, fmt.Errorf("failed to create tester chain: %v", err)
- }
- // Generate fork chain, make it shorter than canon, with common ancestor pretty early
- parentIndex := 3
- parent := longChain[parentIndex]
- heavyChain, _ := GenerateChain(params.TestChainConfig, parent, engine, db, 75, func(i int, b *BlockGen) {
- b.SetCoinbase(common.Address{2})
- b.OffsetTime(-9)
- })
- // Verify that the test is sane
- var (
- longerTd = new(big.Int)
- shorterTd = new(big.Int)
- )
- for index, b := range longChain {
- longerTd.Add(longerTd, b.Difficulty())
- if index <= parentIndex {
- shorterTd.Add(shorterTd, b.Difficulty())
- }
- }
- for _, b := range heavyChain {
- shorterTd.Add(shorterTd, b.Difficulty())
- }
- if shorterTd.Cmp(longerTd) <= 0 {
- return nil, nil, nil, fmt.Errorf("Test is moot, heavyChain td (%v) must be larger than canon td (%v)", shorterTd, longerTd)
- }
- longerNum := longChain[len(longChain)-1].NumberU64()
- shorterNum := heavyChain[len(heavyChain)-1].NumberU64()
- if shorterNum >= longerNum {
- return nil, nil, nil, fmt.Errorf("Test is moot, heavyChain num (%v) must be lower than canon num (%v)", shorterNum, longerNum)
- }
- return chain, longChain, heavyChain, nil
- }
- // TestReorgToShorterRemovesCanonMapping tests that if we
- // 1. Have a chain [0 ... N .. X]
- // 2. Reorg to shorter but heavier chain [0 ... N ... Y]
- // 3. Then there should be no canon mapping for the block at height X
- func TestReorgToShorterRemovesCanonMapping(t *testing.T) {
- chain, canonblocks, sideblocks, err := getLongAndShortChains()
- if err != nil {
- t.Fatal(err)
- }
- if n, err := chain.InsertChain(canonblocks); err != nil {
- t.Fatalf("block %d: failed to insert into chain: %v", n, err)
- }
- canonNum := chain.CurrentBlock().NumberU64()
- _, err = chain.InsertChain(sideblocks)
- if err != nil {
- t.Errorf("Got error, %v", err)
- }
- head := chain.CurrentBlock()
- if got := sideblocks[len(sideblocks)-1].Hash(); got != head.Hash() {
- t.Fatalf("head wrong, expected %x got %x", head.Hash(), got)
- }
- // We have now inserted a sidechain.
- if blockByNum := chain.GetBlockByNumber(canonNum); blockByNum != nil {
- t.Errorf("expected block to be gone: %v", blockByNum.NumberU64())
- }
- if headerByNum := chain.GetHeaderByNumber(canonNum); headerByNum != nil {
- t.Errorf("expected header to be gone: %v", headerByNum.Number.Uint64())
- }
- }
- // TestReorgToShorterRemovesCanonMappingHeaderChain is the same scenario
- // as TestReorgToShorterRemovesCanonMapping, but applied on headerchain
- // imports -- that is, for fast sync
- func TestReorgToShorterRemovesCanonMappingHeaderChain(t *testing.T) {
- chain, canonblocks, sideblocks, err := getLongAndShortChains()
- if err != nil {
- t.Fatal(err)
- }
- // Convert into headers
- canonHeaders := make([]*types.Header, len(canonblocks))
- for i, block := range canonblocks {
- canonHeaders[i] = block.Header()
- }
- if n, err := chain.InsertHeaderChain(canonHeaders, 0); err != nil {
- t.Fatalf("header %d: failed to insert into chain: %v", n, err)
- }
- canonNum := chain.CurrentHeader().Number.Uint64()
- sideHeaders := make([]*types.Header, len(sideblocks))
- for i, block := range sideblocks {
- sideHeaders[i] = block.Header()
- }
- if n, err := chain.InsertHeaderChain(sideHeaders, 0); err != nil {
- t.Fatalf("header %d: failed to insert into chain: %v", n, err)
- }
- head := chain.CurrentHeader()
- if got := sideblocks[len(sideblocks)-1].Hash(); got != head.Hash() {
- t.Fatalf("head wrong, expected %x got %x", head.Hash(), got)
- }
- // We have now inserted a sidechain.
- if blockByNum := chain.GetBlockByNumber(canonNum); blockByNum != nil {
- t.Errorf("expected block to be gone: %v", blockByNum.NumberU64())
- }
- if headerByNum := chain.GetHeaderByNumber(canonNum); headerByNum != nil {
- t.Errorf("expected header to be gone: %v", headerByNum.Number.Uint64())
- }
- }
- // 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)
- 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)
- }
|