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