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