github.com/jimmyx0x/go-ethereum@v1.10.28/eth/downloader/testchain_test.go (about) 1 // Copyright 2018 The go-ethereum Authors 2 // This file is part of the go-ethereum library. 3 // 4 // The go-ethereum library is free software: you can redistribute it and/or modify 5 // it under the terms of the GNU Lesser General Public License as published by 6 // the Free Software Foundation, either version 3 of the License, or 7 // (at your option) any later version. 8 // 9 // The go-ethereum library is distributed in the hope that it will be useful, 10 // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 // GNU Lesser General Public License for more details. 13 // 14 // You should have received a copy of the GNU Lesser General Public License 15 // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. 16 17 package downloader 18 19 import ( 20 "fmt" 21 "math/big" 22 "sync" 23 "time" 24 25 "github.com/ethereum/go-ethereum/common" 26 "github.com/ethereum/go-ethereum/consensus/ethash" 27 "github.com/ethereum/go-ethereum/core" 28 "github.com/ethereum/go-ethereum/core/rawdb" 29 "github.com/ethereum/go-ethereum/core/types" 30 "github.com/ethereum/go-ethereum/core/vm" 31 "github.com/ethereum/go-ethereum/crypto" 32 "github.com/ethereum/go-ethereum/params" 33 ) 34 35 // Test chain parameters. 36 var ( 37 testKey, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291") 38 testAddress = crypto.PubkeyToAddress(testKey.PublicKey) 39 testDB = rawdb.NewMemoryDatabase() 40 41 testGspec = &core.Genesis{ 42 Config: params.TestChainConfig, 43 Alloc: core.GenesisAlloc{testAddress: {Balance: big.NewInt(1000000000000000)}}, 44 BaseFee: big.NewInt(params.InitialBaseFee), 45 } 46 testGenesis = testGspec.MustCommit(testDB) 47 ) 48 49 // The common prefix of all test chains: 50 var testChainBase *testChain 51 52 // Different forks on top of the base chain: 53 var testChainForkLightA, testChainForkLightB, testChainForkHeavy *testChain 54 55 var pregenerated bool 56 57 func init() { 58 // Reduce some of the parameters to make the tester faster 59 fullMaxForkAncestry = 10000 60 lightMaxForkAncestry = 10000 61 blockCacheMaxItems = 1024 62 fsHeaderSafetyNet = 256 63 fsHeaderContCheck = 500 * time.Millisecond 64 65 testChainBase = newTestChain(blockCacheMaxItems+200, testGenesis) 66 67 var forkLen = int(fullMaxForkAncestry + 50) 68 var wg sync.WaitGroup 69 70 // Generate the test chains to seed the peers with 71 wg.Add(3) 72 go func() { testChainForkLightA = testChainBase.makeFork(forkLen, false, 1); wg.Done() }() 73 go func() { testChainForkLightB = testChainBase.makeFork(forkLen, false, 2); wg.Done() }() 74 go func() { testChainForkHeavy = testChainBase.makeFork(forkLen, true, 3); wg.Done() }() 75 wg.Wait() 76 77 // Generate the test peers used by the tests to avoid overloading during testing. 78 // These seemingly random chains are used in various downloader tests. We're just 79 // pre-generating them here. 80 chains := []*testChain{ 81 testChainBase, 82 testChainForkLightA, 83 testChainForkLightB, 84 testChainForkHeavy, 85 testChainBase.shorten(1), 86 testChainBase.shorten(blockCacheMaxItems - 15), 87 testChainBase.shorten((blockCacheMaxItems - 15) / 2), 88 testChainBase.shorten(blockCacheMaxItems - 15 - 5), 89 testChainBase.shorten(MaxHeaderFetch), 90 testChainBase.shorten(800), 91 testChainBase.shorten(800 / 2), 92 testChainBase.shorten(800 / 3), 93 testChainBase.shorten(800 / 4), 94 testChainBase.shorten(800 / 5), 95 testChainBase.shorten(800 / 6), 96 testChainBase.shorten(800 / 7), 97 testChainBase.shorten(800 / 8), 98 testChainBase.shorten(3*fsHeaderSafetyNet + 256 + fsMinFullBlocks), 99 testChainBase.shorten(fsMinFullBlocks + 256 - 1), 100 testChainForkLightA.shorten(len(testChainBase.blocks) + 80), 101 testChainForkLightB.shorten(len(testChainBase.blocks) + 81), 102 testChainForkLightA.shorten(len(testChainBase.blocks) + MaxHeaderFetch), 103 testChainForkLightB.shorten(len(testChainBase.blocks) + MaxHeaderFetch), 104 testChainForkHeavy.shorten(len(testChainBase.blocks) + 79), 105 } 106 wg.Add(len(chains)) 107 for _, chain := range chains { 108 go func(blocks []*types.Block) { 109 newTestBlockchain(blocks) 110 wg.Done() 111 }(chain.blocks[1:]) 112 } 113 wg.Wait() 114 115 // Mark the chains pregenerated. Generating a new one will lead to a panic. 116 pregenerated = true 117 } 118 119 type testChain struct { 120 blocks []*types.Block 121 } 122 123 // newTestChain creates a blockchain of the given length. 124 func newTestChain(length int, genesis *types.Block) *testChain { 125 tc := &testChain{ 126 blocks: []*types.Block{genesis}, 127 } 128 tc.generate(length-1, 0, genesis, false) 129 return tc 130 } 131 132 // makeFork creates a fork on top of the test chain. 133 func (tc *testChain) makeFork(length int, heavy bool, seed byte) *testChain { 134 fork := tc.copy(len(tc.blocks) + length) 135 fork.generate(length, seed, tc.blocks[len(tc.blocks)-1], heavy) 136 return fork 137 } 138 139 // shorten creates a copy of the chain with the given length. It panics if the 140 // length is longer than the number of available blocks. 141 func (tc *testChain) shorten(length int) *testChain { 142 if length > len(tc.blocks) { 143 panic(fmt.Errorf("can't shorten test chain to %d blocks, it's only %d blocks long", length, len(tc.blocks))) 144 } 145 return tc.copy(length) 146 } 147 148 func (tc *testChain) copy(newlen int) *testChain { 149 if newlen > len(tc.blocks) { 150 newlen = len(tc.blocks) 151 } 152 cpy := &testChain{ 153 blocks: append([]*types.Block{}, tc.blocks[:newlen]...), 154 } 155 return cpy 156 } 157 158 // generate creates a chain of n blocks starting at and including parent. 159 // the returned hash chain is ordered head->parent. In addition, every 22th block 160 // contains a transaction and every 5th an uncle to allow testing correct block 161 // reassembly. 162 func (tc *testChain) generate(n int, seed byte, parent *types.Block, heavy bool) { 163 blocks, _ := core.GenerateChain(testGspec.Config, parent, ethash.NewFaker(), testDB, n, func(i int, block *core.BlockGen) { 164 block.SetCoinbase(common.Address{seed}) 165 // If a heavy chain is requested, delay blocks to raise difficulty 166 if heavy { 167 block.OffsetTime(-9) 168 } 169 // Include transactions to the miner to make blocks more interesting. 170 if parent == tc.blocks[0] && i%22 == 0 { 171 signer := types.MakeSigner(params.TestChainConfig, block.Number()) 172 tx, err := types.SignTx(types.NewTransaction(block.TxNonce(testAddress), common.Address{seed}, big.NewInt(1000), params.TxGas, block.BaseFee(), nil), signer, testKey) 173 if err != nil { 174 panic(err) 175 } 176 block.AddTx(tx) 177 } 178 // if the block number is a multiple of 5, add a bonus uncle to the block 179 if i > 0 && i%5 == 0 { 180 block.AddUncle(&types.Header{ 181 ParentHash: block.PrevBlock(i - 2).Hash(), 182 Number: big.NewInt(block.Number().Int64() - 1), 183 }) 184 } 185 }) 186 tc.blocks = append(tc.blocks, blocks...) 187 } 188 189 var ( 190 testBlockchains = make(map[common.Hash]*testBlockchain) 191 testBlockchainsLock sync.Mutex 192 ) 193 194 type testBlockchain struct { 195 chain *core.BlockChain 196 gen sync.Once 197 } 198 199 // newTestBlockchain creates a blockchain database built by running the given blocks, 200 // either actually running them, or reusing a previously created one. The returned 201 // chains are *shared*, so *do not* mutate them. 202 func newTestBlockchain(blocks []*types.Block) *core.BlockChain { 203 // Retrieve an existing database, or create a new one 204 head := testGenesis.Hash() 205 if len(blocks) > 0 { 206 head = blocks[len(blocks)-1].Hash() 207 } 208 testBlockchainsLock.Lock() 209 if _, ok := testBlockchains[head]; !ok { 210 testBlockchains[head] = new(testBlockchain) 211 } 212 tbc := testBlockchains[head] 213 testBlockchainsLock.Unlock() 214 215 // Ensure that the database is generated 216 tbc.gen.Do(func() { 217 if pregenerated { 218 panic("Requested chain generation outside of init") 219 } 220 chain, err := core.NewBlockChain(rawdb.NewMemoryDatabase(), nil, testGspec, nil, ethash.NewFaker(), vm.Config{}, nil, nil) 221 if err != nil { 222 panic(err) 223 } 224 if n, err := chain.InsertChain(blocks); err != nil { 225 panic(fmt.Sprintf("block %d: %v", n, err)) 226 } 227 tbc.chain = chain 228 }) 229 return tbc.chain 230 }