github.com/codysnider/go-ethereum@v1.10.18-0.20220420071915-14f4ae99222a/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 testGenesis = core.GenesisBlockForTesting(testDB, testAddress, big.NewInt(1000000000000000)) 41 ) 42 43 // The common prefix of all test chains: 44 var testChainBase *testChain 45 46 // Different forks on top of the base chain: 47 var testChainForkLightA, testChainForkLightB, testChainForkHeavy *testChain 48 49 var pregenerated bool 50 51 func init() { 52 // Reduce some of the parameters to make the tester faster 53 fullMaxForkAncestry = 10000 54 lightMaxForkAncestry = 10000 55 blockCacheMaxItems = 1024 56 fsHeaderSafetyNet = 256 57 fsHeaderContCheck = 500 * time.Millisecond 58 59 testChainBase = newTestChain(blockCacheMaxItems+200, testGenesis) 60 61 var forkLen = int(fullMaxForkAncestry + 50) 62 var wg sync.WaitGroup 63 64 // Generate the test chains to seed the peers with 65 wg.Add(3) 66 go func() { testChainForkLightA = testChainBase.makeFork(forkLen, false, 1); wg.Done() }() 67 go func() { testChainForkLightB = testChainBase.makeFork(forkLen, false, 2); wg.Done() }() 68 go func() { testChainForkHeavy = testChainBase.makeFork(forkLen, true, 3); wg.Done() }() 69 wg.Wait() 70 71 // Generate the test peers used by the tests to avoid overloading during testing. 72 // These seemingly random chains are used in various downloader tests. We're just 73 // pre-generating them here. 74 chains := []*testChain{ 75 testChainBase, 76 testChainForkLightA, 77 testChainForkLightB, 78 testChainForkHeavy, 79 testChainBase.shorten(1), 80 testChainBase.shorten(blockCacheMaxItems - 15), 81 testChainBase.shorten((blockCacheMaxItems - 15) / 2), 82 testChainBase.shorten(blockCacheMaxItems - 15 - 5), 83 testChainBase.shorten(MaxHeaderFetch), 84 testChainBase.shorten(800), 85 testChainBase.shorten(800 / 2), 86 testChainBase.shorten(800 / 3), 87 testChainBase.shorten(800 / 4), 88 testChainBase.shorten(800 / 5), 89 testChainBase.shorten(800 / 6), 90 testChainBase.shorten(800 / 7), 91 testChainBase.shorten(800 / 8), 92 testChainBase.shorten(3*fsHeaderSafetyNet + 256 + fsMinFullBlocks), 93 testChainBase.shorten(fsMinFullBlocks + 256 - 1), 94 testChainForkLightA.shorten(len(testChainBase.blocks) + 80), 95 testChainForkLightB.shorten(len(testChainBase.blocks) + 81), 96 testChainForkLightA.shorten(len(testChainBase.blocks) + MaxHeaderFetch), 97 testChainForkLightB.shorten(len(testChainBase.blocks) + MaxHeaderFetch), 98 testChainForkHeavy.shorten(len(testChainBase.blocks) + 79), 99 } 100 wg.Add(len(chains)) 101 for _, chain := range chains { 102 go func(blocks []*types.Block) { 103 newTestBlockchain(blocks) 104 wg.Done() 105 }(chain.blocks[1:]) 106 } 107 wg.Wait() 108 109 // Mark the chains pregenerated. Generating a new one will lead to a panic. 110 pregenerated = true 111 } 112 113 type testChain struct { 114 blocks []*types.Block 115 } 116 117 // newTestChain creates a blockchain of the given length. 118 func newTestChain(length int, genesis *types.Block) *testChain { 119 tc := &testChain{ 120 blocks: []*types.Block{genesis}, 121 } 122 tc.generate(length-1, 0, genesis, false) 123 return tc 124 } 125 126 // makeFork creates a fork on top of the test chain. 127 func (tc *testChain) makeFork(length int, heavy bool, seed byte) *testChain { 128 fork := tc.copy(len(tc.blocks) + length) 129 fork.generate(length, seed, tc.blocks[len(tc.blocks)-1], heavy) 130 return fork 131 } 132 133 // shorten creates a copy of the chain with the given length. It panics if the 134 // length is longer than the number of available blocks. 135 func (tc *testChain) shorten(length int) *testChain { 136 if length > len(tc.blocks) { 137 panic(fmt.Errorf("can't shorten test chain to %d blocks, it's only %d blocks long", length, len(tc.blocks))) 138 } 139 return tc.copy(length) 140 } 141 142 func (tc *testChain) copy(newlen int) *testChain { 143 if newlen > len(tc.blocks) { 144 newlen = len(tc.blocks) 145 } 146 cpy := &testChain{ 147 blocks: append([]*types.Block{}, tc.blocks[:newlen]...), 148 } 149 return cpy 150 } 151 152 // generate creates a chain of n blocks starting at and including parent. 153 // the returned hash chain is ordered head->parent. In addition, every 22th block 154 // contains a transaction and every 5th an uncle to allow testing correct block 155 // reassembly. 156 func (tc *testChain) generate(n int, seed byte, parent *types.Block, heavy bool) { 157 blocks, _ := core.GenerateChain(params.TestChainConfig, parent, ethash.NewFaker(), testDB, n, func(i int, block *core.BlockGen) { 158 block.SetCoinbase(common.Address{seed}) 159 // If a heavy chain is requested, delay blocks to raise difficulty 160 if heavy { 161 block.OffsetTime(-9) 162 } 163 // Include transactions to the miner to make blocks more interesting. 164 if parent == tc.blocks[0] && i%22 == 0 { 165 signer := types.MakeSigner(params.TestChainConfig, block.Number()) 166 tx, err := types.SignTx(types.NewTransaction(block.TxNonce(testAddress), common.Address{seed}, big.NewInt(1000), params.TxGas, block.BaseFee(), nil), signer, testKey) 167 if err != nil { 168 panic(err) 169 } 170 block.AddTx(tx) 171 } 172 // if the block number is a multiple of 5, add a bonus uncle to the block 173 if i > 0 && i%5 == 0 { 174 block.AddUncle(&types.Header{ 175 ParentHash: block.PrevBlock(i - 2).Hash(), 176 Number: big.NewInt(block.Number().Int64() - 1), 177 }) 178 } 179 }) 180 tc.blocks = append(tc.blocks, blocks...) 181 } 182 183 var ( 184 testBlockchains = make(map[common.Hash]*testBlockchain) 185 testBlockchainsLock sync.Mutex 186 ) 187 188 type testBlockchain struct { 189 chain *core.BlockChain 190 gen sync.Once 191 } 192 193 // newTestBlockchain creates a blockchain database built by running the given blocks, 194 // either actually running them, or reusing a previously created one. The returned 195 // chains are *shared*, so *do not* mutate them. 196 func newTestBlockchain(blocks []*types.Block) *core.BlockChain { 197 // Retrieve an existing database, or create a new one 198 head := testGenesis.Hash() 199 if len(blocks) > 0 { 200 head = blocks[len(blocks)-1].Hash() 201 } 202 testBlockchainsLock.Lock() 203 if _, ok := testBlockchains[head]; !ok { 204 testBlockchains[head] = new(testBlockchain) 205 } 206 tbc := testBlockchains[head] 207 testBlockchainsLock.Unlock() 208 209 // Ensure that the database is generated 210 tbc.gen.Do(func() { 211 if pregenerated { 212 panic("Requested chain generation outside of init") 213 } 214 db := rawdb.NewMemoryDatabase() 215 core.GenesisBlockForTesting(db, testAddress, big.NewInt(1000000000000000)) 216 217 chain, err := core.NewBlockChain(db, nil, params.TestChainConfig, ethash.NewFaker(), vm.Config{}, nil, nil) 218 if err != nil { 219 panic(err) 220 } 221 if n, err := chain.InsertChain(blocks); err != nil { 222 panic(fmt.Sprintf("block %d: %v", n, err)) 223 } 224 tbc.chain = chain 225 }) 226 return tbc.chain 227 }