github.com/core-coin/go-core/v2@v2.1.9/xcb/downloader/testchain_test.go (about) 1 // Copyright 2018 by the Authors 2 // This file is part of the go-core library. 3 // 4 // The go-core 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-core 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-core library. If not, see <http://www.gnu.org/licenses/>. 16 17 package downloader 18 19 import ( 20 "fmt" 21 "math/big" 22 "sync" 23 24 "github.com/core-coin/go-core/v2/consensus/cryptore" 25 26 "github.com/core-coin/go-core/v2/common" 27 "github.com/core-coin/go-core/v2/core" 28 "github.com/core-coin/go-core/v2/core/rawdb" 29 "github.com/core-coin/go-core/v2/core/types" 30 "github.com/core-coin/go-core/v2/crypto" 31 "github.com/core-coin/go-core/v2/params" 32 ) 33 34 // Test chain parameters. 35 var ( 36 testKey, _ = crypto.UnmarshalPrivateKeyHex("89bdfaa2b6f9c30b94ee98fec96c58ff8507fabf49d36a6267e6cb5516eaa2a9e854eccc041f9f67e109d0eb4f653586855355c5b2b87bb313") 37 testDB = rawdb.NewMemoryDatabase() 38 testGenesis = core.GenesisBlockForTesting(testDB, testKey.Address(), big.NewInt(1000000000)) 39 ) 40 41 // The common prefix of all test chains: 42 var testChainBase = newTestChain(blockCacheMaxItems+200, testGenesis) 43 44 // Different forks on top of the base chain: 45 var testChainForkLightA, testChainForkLightB, testChainForkHeavy *testChain 46 47 func init() { 48 var forkLen = int(fullMaxForkAncestry + 50) 49 var wg sync.WaitGroup 50 wg.Add(3) 51 go func() { testChainForkLightA = testChainBase.makeFork(forkLen, false, 1); wg.Done() }() 52 go func() { testChainForkLightB = testChainBase.makeFork(forkLen, false, 2); wg.Done() }() 53 go func() { testChainForkHeavy = testChainBase.makeFork(forkLen, true, 3); wg.Done() }() 54 wg.Wait() 55 } 56 57 type testChain struct { 58 genesis *types.Block 59 chain []common.Hash 60 headerm map[common.Hash]*types.Header 61 blockm map[common.Hash]*types.Block 62 receiptm map[common.Hash][]*types.Receipt 63 tdm map[common.Hash]*big.Int 64 } 65 66 // newTestChain creates a blockchain of the given length. 67 func newTestChain(length int, genesis *types.Block) *testChain { 68 tc := new(testChain).copy(length) 69 tc.genesis = genesis 70 tc.chain = append(tc.chain, genesis.Hash()) 71 tc.headerm[tc.genesis.Hash()] = tc.genesis.Header() 72 tc.tdm[tc.genesis.Hash()] = tc.genesis.Difficulty() 73 tc.blockm[tc.genesis.Hash()] = tc.genesis 74 tc.generate(length-1, 0, genesis, false) 75 return tc 76 } 77 78 // makeFork creates a fork on top of the test chain. 79 func (tc *testChain) makeFork(length int, heavy bool, seed byte) *testChain { 80 fork := tc.copy(tc.len() + length) 81 fork.generate(length, seed, tc.headBlock(), heavy) 82 return fork 83 } 84 85 // shorten creates a copy of the chain with the given length. It panics if the 86 // length is longer than the number of available blocks. 87 func (tc *testChain) shorten(length int) *testChain { 88 if length > tc.len() { 89 panic(fmt.Errorf("can't shorten test chain to %d blocks, it's only %d blocks long", length, tc.len())) 90 } 91 return tc.copy(length) 92 } 93 94 func (tc *testChain) copy(newlen int) *testChain { 95 cpy := &testChain{ 96 genesis: tc.genesis, 97 headerm: make(map[common.Hash]*types.Header, newlen), 98 blockm: make(map[common.Hash]*types.Block, newlen), 99 receiptm: make(map[common.Hash][]*types.Receipt, newlen), 100 tdm: make(map[common.Hash]*big.Int, newlen), 101 } 102 for i := 0; i < len(tc.chain) && i < newlen; i++ { 103 hash := tc.chain[i] 104 cpy.chain = append(cpy.chain, tc.chain[i]) 105 cpy.tdm[hash] = tc.tdm[hash] 106 cpy.blockm[hash] = tc.blockm[hash] 107 cpy.headerm[hash] = tc.headerm[hash] 108 cpy.receiptm[hash] = tc.receiptm[hash] 109 } 110 return cpy 111 } 112 113 // generate creates a chain of n blocks starting at and including parent. 114 // the returned hash chain is ordered head->parent. In addition, every 22th block 115 // contains a transaction and every 5th an uncle to allow testing correct block 116 // reassembly. 117 func (tc *testChain) generate(n int, seed byte, parent *types.Block, heavy bool) { 118 // start := time.Now() 119 // defer func() { fmt.Printf("test chain generated in %v\n", time.Since(start)) }() 120 121 blocks, receipts := core.GenerateChain(params.MainnetChainConfig, parent, cryptore.NewFaker(), testDB, n, func(i int, block *core.BlockGen) { 122 block.SetCoinbase(common.Address{seed}) 123 // If a heavy chain is requested, delay blocks to raise difficulty 124 if heavy { 125 block.OffsetTime(-1) 126 } 127 // Include transactions to the miner to make blocks more interesting. 128 if parent == tc.genesis && i%22 == 0 { 129 signer := types.MakeSigner(params.MainnetChainConfig.NetworkID) 130 tx, err := types.SignTx(types.NewTransaction(block.TxNonce(testKey.Address()), common.Address{seed}, big.NewInt(1000), params.TxEnergy, nil, nil), signer, testKey) 131 if err != nil { 132 panic(err) 133 } 134 block.AddTx(tx) 135 } 136 // if the block number is a multiple of 5, add a bonus uncle to the block 137 if i > 0 && i%5 == 0 { 138 block.AddUncle(&types.Header{ 139 ParentHash: block.PrevBlock(i - 1).Hash(), 140 Number: big.NewInt(block.Number().Int64() - 1), 141 }) 142 } 143 }) 144 145 // Convert the block-chain into a hash-chain and header/block maps 146 td := new(big.Int).Set(tc.td(parent.Hash())) 147 for i, b := range blocks { 148 td := td.Add(td, b.Difficulty()) 149 hash := b.Hash() 150 tc.chain = append(tc.chain, hash) 151 tc.blockm[hash] = b 152 tc.headerm[hash] = b.Header() 153 tc.receiptm[hash] = receipts[i] 154 tc.tdm[hash] = new(big.Int).Set(td) 155 } 156 } 157 158 // len returns the total number of blocks in the chain. 159 func (tc *testChain) len() int { 160 return len(tc.chain) 161 } 162 163 // headBlock returns the head of the chain. 164 func (tc *testChain) headBlock() *types.Block { 165 return tc.blockm[tc.chain[len(tc.chain)-1]] 166 } 167 168 // td returns the total difficulty of the given block. 169 func (tc *testChain) td(hash common.Hash) *big.Int { 170 return tc.tdm[hash] 171 } 172 173 // headersByHash returns headers in order from the given hash. 174 func (tc *testChain) headersByHash(origin common.Hash, amount int, skip int, reverse bool) []*types.Header { 175 num, _ := tc.hashToNumber(origin) 176 return tc.headersByNumber(num, amount, skip, reverse) 177 } 178 179 // headersByNumber returns headers from the given number. 180 func (tc *testChain) headersByNumber(origin uint64, amount int, skip int, reverse bool) []*types.Header { 181 result := make([]*types.Header, 0, amount) 182 183 if !reverse { 184 for num := origin; num < uint64(len(tc.chain)) && len(result) < amount; num += uint64(skip) + 1 { 185 if header, ok := tc.headerm[tc.chain[int(num)]]; ok { 186 result = append(result, header) 187 } 188 } 189 } else { 190 for num := int64(origin); num >= 0 && len(result) < amount; num -= int64(skip) + 1 { 191 if header, ok := tc.headerm[tc.chain[int(num)]]; ok { 192 result = append(result, header) 193 } 194 } 195 } 196 return result 197 } 198 199 // receipts returns the receipts of the given block hashes. 200 func (tc *testChain) receipts(hashes []common.Hash) [][]*types.Receipt { 201 results := make([][]*types.Receipt, 0, len(hashes)) 202 for _, hash := range hashes { 203 if receipt, ok := tc.receiptm[hash]; ok { 204 results = append(results, receipt) 205 } 206 } 207 return results 208 } 209 210 // bodies returns the block bodies of the given block hashes. 211 func (tc *testChain) bodies(hashes []common.Hash) ([][]*types.Transaction, [][]*types.Header) { 212 transactions := make([][]*types.Transaction, 0, len(hashes)) 213 uncles := make([][]*types.Header, 0, len(hashes)) 214 for _, hash := range hashes { 215 if block, ok := tc.blockm[hash]; ok { 216 transactions = append(transactions, block.Transactions()) 217 uncles = append(uncles, block.Uncles()) 218 } 219 } 220 return transactions, uncles 221 } 222 223 func (tc *testChain) hashToNumber(target common.Hash) (uint64, bool) { 224 for num, hash := range tc.chain { 225 if hash == target { 226 return uint64(num), true 227 } 228 } 229 return 0, false 230 }