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