github.com/bcskill/bcschain/v3@v3.4.9-beta2/light/lightchain_test.go (about) 1 // Copyright 2016 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 light 18 19 import ( 20 "context" 21 "math/big" 22 "testing" 23 24 "github.com/bcskill/bcschain/v3/common" 25 "github.com/bcskill/bcschain/v3/common/hexutil" 26 "github.com/bcskill/bcschain/v3/consensus/clique" 27 "github.com/bcskill/bcschain/v3/core" 28 "github.com/bcskill/bcschain/v3/core/rawdb" 29 "github.com/bcskill/bcschain/v3/core/types" 30 "github.com/bcskill/bcschain/v3/ethdb" 31 "github.com/bcskill/bcschain/v3/params" 32 ) 33 34 // So we can deterministically seed different blockchains 35 var ( 36 canonicalSeed = 1 37 forkSeed = 2 38 ) 39 40 // makeHeaderChain creates a deterministic chain of headers rooted at parent. 41 func makeHeaderChain(parent *types.Header, n int, db common.Database, seed int) []*types.Header { 42 blocks, _ := core.GenerateChain(params.TestChainConfig, types.NewBlockWithHeader(parent), clique.NewFaker(), db, n, func(i int, b *core.BlockGen) { 43 b.SetExtra([]byte{0: byte(seed)}) 44 }) 45 headers := make([]*types.Header, len(blocks)) 46 for i, block := range blocks { 47 headers[i] = block.Header() 48 } 49 return headers 50 } 51 52 // newCanonical creates a chain database, and injects a deterministic canonical 53 // chain. Depending on the full flag, if creates either a full block chain or a 54 // header only chain. 55 func newCanonical(n int) (common.Database, *LightChain, error) { 56 db := ethdb.NewMemDatabase() 57 signer := hexutil.MustDecode("0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000") 58 gspec := core.Genesis{Config: params.TestChainConfig, Signer: signer} 59 genesis := gspec.MustCommit(db) 60 blockchain, _ := NewLightChain(&dummyOdr{db: db}, gspec.Config, clique.NewFaker()) 61 62 // Create and inject the requested chain 63 if n == 0 { 64 return db, blockchain, nil 65 } 66 // Header-only chain requested 67 headers := makeHeaderChain(genesis.Header(), n, db, canonicalSeed) 68 _, err := blockchain.InsertHeaderChain(headers, 1) 69 return db, blockchain, err 70 } 71 72 // newTestLightChain creates a LightChain that doesn't validate anything. 73 func newTestLightChain() *LightChain { 74 db := ethdb.NewMemDatabase() 75 gspec := &core.Genesis{ 76 Difficulty: big.NewInt(1), 77 Config: params.TestChainConfig, 78 } 79 gspec.MustCommit(db) 80 lc, err := NewLightChain(&dummyOdr{db: db}, gspec.Config, clique.NewFullFaker()) 81 if err != nil { 82 panic(err) 83 } 84 return lc 85 } 86 87 // Test fork of length N starting from block i 88 func testFork(t *testing.T, LightChain *LightChain, i, n int, comparator func(td1, td2 *big.Int)) { 89 // Copy old chain up to #i into a new db 90 db, LightChain2, err := newCanonical(i) 91 if err != nil { 92 t.Fatal("could not make new canonical in testFork", err) 93 } 94 // Assert the chains have the same header/block at #i 95 var hash1, hash2 common.Hash 96 hash1 = LightChain.GetHeaderByNumber(uint64(i)).Hash() 97 hash2 = LightChain2.GetHeaderByNumber(uint64(i)).Hash() 98 if hash1 != hash2 { 99 t.Errorf("chain content mismatch at %d: have hash %v, want hash %v", i, hash2, hash1) 100 } 101 // Extend the newly created chain 102 header := LightChain2.CurrentHeader() 103 104 headerChainB := makeHeaderChain(header, n, db, forkSeed) 105 if _, err := LightChain2.InsertHeaderChain(headerChainB, 1); err != nil { 106 t.Fatalf("failed to insert forking chain: %v", err) 107 } 108 // Sanity check that the forked chain can be imported into the original 109 var tdPre, tdPost *big.Int 110 111 tdPre = LightChain.GetTdByHash(LightChain.CurrentHeader().Hash()) 112 if err := testHeaderChainImport(headerChainB, LightChain); err != nil { 113 t.Fatalf("failed to import forked header chain: %v", err) 114 } 115 tdPost = LightChain.GetTdByHash(headerChainB[len(headerChainB)-1].Hash()) 116 // Compare the total difficulties of the chains 117 comparator(tdPre, tdPost) 118 } 119 120 // testHeaderChainImport tries to process a chain of header, writing them into 121 // the database if successful. 122 func testHeaderChainImport(chain []*types.Header, lightchain *LightChain) error { 123 for _, header := range chain { 124 // Try and validate the header 125 if err := lightchain.engine.VerifyHeader(lightchain.hc, header); err != nil { 126 return err 127 } 128 // Manually insert the header into the database, but don't reorganize (allows subsequent testing) 129 lightchain.mu.Lock() 130 rawdb.WriteTd(lightchain.chainDb.GlobalTable(), header.Hash(), header.Number.Uint64(), new(big.Int).Add(header.Difficulty, lightchain.GetTdByHash(header.ParentHash))) 131 rawdb.WriteHeader(lightchain.chainDb.GlobalTable(), lightchain.chainDb.HeaderTable(), header) 132 lightchain.mu.Unlock() 133 } 134 return nil 135 } 136 137 // Tests that given a starting canonical chain of a given size, it can be extended 138 // with various length chains. 139 func TestExtendCanonicalHeaders(t *testing.T) { 140 length := 5 141 142 // Make first chain starting from genesis 143 _, processor, err := newCanonical(length) 144 if err != nil { 145 t.Fatalf("failed to make new canonical chain: %v", err) 146 } 147 // Define the difficulty comparator 148 better := func(td1, td2 *big.Int) { 149 if td2.Cmp(td1) <= 0 { 150 t.Errorf("total difficulty mismatch: have %v, expected more than %v", td2, td1) 151 } 152 } 153 // Start fork from current height 154 testFork(t, processor, length, 1, better) 155 testFork(t, processor, length, 2, better) 156 testFork(t, processor, length, 5, better) 157 testFork(t, processor, length, 10, better) 158 } 159 160 // Tests that given a starting canonical chain of a given size, creating shorter 161 // forks do not take canonical ownership. 162 func TestShorterForkHeaders(t *testing.T) { 163 length := 10 164 165 // Make first chain starting from genesis 166 _, processor, err := newCanonical(length) 167 if err != nil { 168 t.Fatalf("failed to make new canonical chain: %v", err) 169 } 170 // Define the difficulty comparator 171 worse := func(td1, td2 *big.Int) { 172 if td2.Cmp(td1) >= 0 { 173 t.Errorf("total difficulty mismatch: have %v, expected less than %v", td2, td1) 174 } 175 } 176 // Sum of numbers must be less than `length` for this to be a shorter fork 177 testFork(t, processor, 0, 3, worse) 178 testFork(t, processor, 0, 7, worse) 179 testFork(t, processor, 1, 1, worse) 180 testFork(t, processor, 1, 7, worse) 181 testFork(t, processor, 5, 3, worse) 182 testFork(t, processor, 5, 4, worse) 183 } 184 185 // Tests that given a starting canonical chain of a given size, creating longer 186 // forks do take canonical ownership. 187 func TestLongerForkHeaders(t *testing.T) { 188 length := 10 189 190 // Make first chain starting from genesis 191 _, processor, err := newCanonical(length) 192 if err != nil { 193 t.Fatalf("failed to make new canonical chain: %v", err) 194 } 195 // Define the difficulty comparator 196 better := func(td1, td2 *big.Int) { 197 if td2.Cmp(td1) <= 0 { 198 t.Errorf("total difficulty mismatch: have %v, expected more than %v", td2, td1) 199 } 200 } 201 // Sum of numbers must be greater than `length` for this to be a longer fork 202 testFork(t, processor, 0, 11, better) 203 testFork(t, processor, 0, 15, better) 204 testFork(t, processor, 1, 10, better) 205 testFork(t, processor, 1, 12, better) 206 testFork(t, processor, 5, 6, better) 207 testFork(t, processor, 5, 8, better) 208 } 209 210 // Tests that given a starting canonical chain of a given size, creating equal 211 // forks do take canonical ownership. 212 func TestEqualForkHeaders(t *testing.T) { 213 length := 10 214 215 // Make first chain starting from genesis 216 _, processor, err := newCanonical(length) 217 if err != nil { 218 t.Fatalf("failed to make new canonical chain: %v", err) 219 } 220 // Define the difficulty comparator 221 equal := func(td1, td2 *big.Int) { 222 if td2.Cmp(td1) != 0 { 223 t.Errorf("total difficulty mismatch: have %v, want %v", td2, td1) 224 } 225 } 226 // Sum of numbers must be equal to `length` for this to be an equal fork 227 testFork(t, processor, 0, 10, equal) 228 testFork(t, processor, 1, 9, equal) 229 testFork(t, processor, 2, 8, equal) 230 testFork(t, processor, 5, 5, equal) 231 testFork(t, processor, 6, 4, equal) 232 testFork(t, processor, 9, 1, equal) 233 } 234 235 // Tests that chains missing links do not get accepted by the processor. 236 func TestBrokenHeaderChain(t *testing.T) { 237 // Make chain starting from genesis 238 db, LightChain, err := newCanonical(10) 239 if err != nil { 240 t.Fatalf("failed to make new canonical chain: %v", err) 241 } 242 // Create a forked chain, and try to insert with a missing link 243 header := LightChain.CurrentHeader() 244 chain := makeHeaderChain(header, 5, db, forkSeed)[1:] 245 if err := testHeaderChainImport(chain, LightChain); err == nil { 246 t.Errorf("broken header chain not reported") 247 } 248 } 249 250 func makeHeaderChainWithDiff(genesis *types.Block, d []int, seed byte) []*types.Header { 251 var chain []*types.Header 252 for i, difficulty := range d { 253 header := &types.Header{ 254 Coinbase: common.Address{seed}, 255 Number: big.NewInt(int64(i + 1)), 256 Difficulty: big.NewInt(int64(difficulty)), 257 UncleHash: types.EmptyUncleHash, 258 TxHash: types.EmptyRootHash, 259 ReceiptHash: types.EmptyRootHash, 260 } 261 if i == 0 { 262 header.ParentHash = genesis.Hash() 263 } else { 264 header.ParentHash = chain[i-1].Hash() 265 } 266 chain = append(chain, types.CopyHeader(header)) 267 } 268 return chain 269 } 270 271 type dummyOdr struct { 272 OdrBackend 273 db common.Database 274 } 275 276 func (odr *dummyOdr) Database() common.Database { 277 return odr.db 278 } 279 280 func (odr *dummyOdr) Retrieve(ctx context.Context, req OdrRequest) error { 281 return nil 282 } 283 284 // Tests that reorganizing a long difficult chain after a short easy one 285 // overwrites the canonical numbers and links in the database. 286 func TestReorgLongHeaders(t *testing.T) { 287 testReorg(t, []int{1, 2, 4}, []int{1, 2, 3, 4}, 10) 288 } 289 290 // Tests that reorganizing a short difficult chain after a long easy one 291 // overwrites the canonical numbers and links in the database. 292 func TestReorgShortHeaders(t *testing.T) { 293 testReorg(t, []int{1, 2, 3, 4}, []int{1, 10}, 11) 294 } 295 296 func testReorg(t *testing.T, first, second []int, td int64) { 297 bc := newTestLightChain() 298 299 // Insert an easy and a difficult chain afterwards 300 bc.InsertHeaderChain(makeHeaderChainWithDiff(bc.genesisBlock, first, 11), 1) 301 bc.InsertHeaderChain(makeHeaderChainWithDiff(bc.genesisBlock, second, 22), 1) 302 // Check that the chain is valid number and link wise 303 prev := bc.CurrentHeader() 304 for header := bc.GetHeaderByNumber(bc.CurrentHeader().Number.Uint64() - 1); header.Number.Uint64() != 0; prev, header = header, bc.GetHeaderByNumber(header.Number.Uint64()-1) { 305 if prev.ParentHash != header.Hash() { 306 t.Errorf("parent header hash mismatch: have %x, want %x", prev.ParentHash, header.Hash()) 307 } 308 } 309 // Make sure the chain total difficulty is the correct one 310 want := new(big.Int).Add(bc.genesisBlock.Difficulty(), big.NewInt(td)) 311 if have := bc.GetTdByHash(bc.CurrentHeader().Hash()); have.Cmp(want) != 0 { 312 t.Errorf("total difficulty mismatch: have %v, want %v", have, want) 313 } 314 } 315 316 // Tests that the insertion functions detect banned hashes. 317 func TestBadHeaderHashes(t *testing.T) { 318 bc := newTestLightChain() 319 320 // Create a chain, ban a hash and try to import 321 var err error 322 headers := makeHeaderChainWithDiff(bc.genesisBlock, []int{1, 2, 4}, 10) 323 core.BadHashes[headers[2].Hash()] = true 324 if _, err = bc.InsertHeaderChain(headers, 1); err != core.ErrBlacklistedHash { 325 t.Errorf("error mismatch: have: %v, want %v", err, core.ErrBlacklistedHash) 326 } 327 } 328 329 // Tests that bad hashes are detected on boot, and the chan rolled back to a 330 // good state prior to the bad hash. 331 func TestReorgBadHeaderHashes(t *testing.T) { 332 bc := newTestLightChain() 333 334 // Create a chain, import and ban aferwards 335 headers := makeHeaderChainWithDiff(bc.genesisBlock, []int{1, 2, 3, 4}, 10) 336 337 if _, err := bc.InsertHeaderChain(headers, 1); err != nil { 338 t.Fatalf("failed to import headers: %v", err) 339 } 340 if bc.CurrentHeader().Hash() != headers[3].Hash() { 341 t.Errorf("last header hash mismatch: have: %x, want %x", bc.CurrentHeader().Hash(), headers[3].Hash()) 342 } 343 core.BadHashes[headers[3].Hash()] = true 344 defer func() { delete(core.BadHashes, headers[3].Hash()) }() 345 346 // Create a new LightChain and check that it rolled back the state. 347 ncm, err := NewLightChain(&dummyOdr{db: bc.chainDb}, params.TestChainConfig, clique.NewFaker()) 348 if err != nil { 349 t.Fatalf("failed to create new chain manager: %v", err) 350 } 351 if ncm.CurrentHeader().Hash() != headers[2].Hash() { 352 t.Errorf("last header hash mismatch: have: %x, want %x", ncm.CurrentHeader().Hash(), headers[2].Hash()) 353 } 354 }