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