github.com/flashbots/go-ethereum@v1.9.7/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/ethereum/go-ethereum/common" 25 "github.com/ethereum/go-ethereum/consensus/ethash" 26 "github.com/ethereum/go-ethereum/core" 27 "github.com/ethereum/go-ethereum/core/rawdb" 28 "github.com/ethereum/go-ethereum/core/types" 29 "github.com/ethereum/go-ethereum/ethdb" 30 "github.com/ethereum/go-ethereum/params" 31 ) 32 33 // So we can deterministically seed different blockchains 34 var ( 35 canonicalSeed = 1 36 forkSeed = 2 37 ) 38 39 // makeHeaderChain creates a deterministic chain of headers rooted at parent. 40 func makeHeaderChain(parent *types.Header, n int, db ethdb.Database, seed int) []*types.Header { 41 blocks, _ := core.GenerateChain(params.TestChainConfig, types.NewBlockWithHeader(parent), ethash.NewFaker(), db, n, func(i int, b *core.BlockGen) { 42 b.SetCoinbase(common.Address{0: byte(seed), 19: byte(i)}) 43 }) 44 headers := make([]*types.Header, len(blocks)) 45 for i, block := range blocks { 46 headers[i] = block.Header() 47 } 48 return headers 49 } 50 51 // newCanonical creates a chain database, and injects a deterministic canonical 52 // chain. Depending on the full flag, if creates either a full block chain or a 53 // header only chain. 54 func newCanonical(n int) (ethdb.Database, *LightChain, error) { 55 db := rawdb.NewMemoryDatabase() 56 gspec := core.Genesis{Config: params.TestChainConfig} 57 genesis := gspec.MustCommit(db) 58 blockchain, _ := NewLightChain(&dummyOdr{db: db, indexerConfig: TestClientIndexerConfig}, gspec.Config, ethash.NewFaker(), nil) 59 60 // Create and inject the requested chain 61 if n == 0 { 62 return db, blockchain, nil 63 } 64 // Header-only chain requested 65 headers := makeHeaderChain(genesis.Header(), n, db, canonicalSeed) 66 _, err := blockchain.InsertHeaderChain(headers, 1) 67 return db, blockchain, err 68 } 69 70 // newTestLightChain creates a LightChain that doesn't validate anything. 71 func newTestLightChain() *LightChain { 72 db := rawdb.NewMemoryDatabase() 73 gspec := &core.Genesis{ 74 Difficulty: big.NewInt(1), 75 Config: params.TestChainConfig, 76 } 77 gspec.MustCommit(db) 78 lc, err := NewLightChain(&dummyOdr{db: db}, gspec.Config, ethash.NewFullFaker(), nil) 79 if err != nil { 80 panic(err) 81 } 82 return lc 83 } 84 85 // Test fork of length N starting from block i 86 func testFork(t *testing.T, LightChain *LightChain, i, n int, comparator func(td1, td2 *big.Int)) { 87 // Copy old chain up to #i into a new db 88 db, LightChain2, err := newCanonical(i) 89 if err != nil { 90 t.Fatal("could not make new canonical in testFork", err) 91 } 92 // Assert the chains have the same header/block at #i 93 var hash1, hash2 common.Hash 94 hash1 = LightChain.GetHeaderByNumber(uint64(i)).Hash() 95 hash2 = LightChain2.GetHeaderByNumber(uint64(i)).Hash() 96 if hash1 != hash2 { 97 t.Errorf("chain content mismatch at %d: have hash %v, want hash %v", i, hash2, hash1) 98 } 99 // Extend the newly created chain 100 headerChainB := makeHeaderChain(LightChain2.CurrentHeader(), n, db, forkSeed) 101 if _, err := LightChain2.InsertHeaderChain(headerChainB, 1); err != nil { 102 t.Fatalf("failed to insert forking chain: %v", err) 103 } 104 // Sanity check that the forked chain can be imported into the original 105 var tdPre, tdPost *big.Int 106 107 tdPre = LightChain.GetTdByHash(LightChain.CurrentHeader().Hash()) 108 if err := testHeaderChainImport(headerChainB, LightChain); err != nil { 109 t.Fatalf("failed to import forked header chain: %v", err) 110 } 111 tdPost = LightChain.GetTdByHash(headerChainB[len(headerChainB)-1].Hash()) 112 // Compare the total difficulties of the chains 113 comparator(tdPre, tdPost) 114 } 115 116 // testHeaderChainImport tries to process a chain of header, writing them into 117 // the database if successful. 118 func testHeaderChainImport(chain []*types.Header, lightchain *LightChain) error { 119 for _, header := range chain { 120 // Try and validate the header 121 if err := lightchain.engine.VerifyHeader(lightchain.hc, header, true); err != nil { 122 return err 123 } 124 // Manually insert the header into the database, but don't reorganize (allows subsequent testing) 125 lightchain.chainmu.Lock() 126 rawdb.WriteTd(lightchain.chainDb, header.Hash(), header.Number.Uint64(), new(big.Int).Add(header.Difficulty, lightchain.GetTdByHash(header.ParentHash))) 127 rawdb.WriteHeader(lightchain.chainDb, header) 128 lightchain.chainmu.Unlock() 129 } 130 return nil 131 } 132 133 // Tests that given a starting canonical chain of a given size, it can be extended 134 // with various length chains. 135 func TestExtendCanonicalHeaders(t *testing.T) { 136 length := 5 137 138 // Make first chain starting from genesis 139 _, processor, err := newCanonical(length) 140 if err != nil { 141 t.Fatalf("failed to make new canonical chain: %v", err) 142 } 143 // Define the difficulty comparator 144 better := func(td1, td2 *big.Int) { 145 if td2.Cmp(td1) <= 0 { 146 t.Errorf("total difficulty mismatch: have %v, expected more than %v", td2, td1) 147 } 148 } 149 // Start fork from current height 150 testFork(t, processor, length, 1, better) 151 testFork(t, processor, length, 2, better) 152 testFork(t, processor, length, 5, better) 153 testFork(t, processor, length, 10, better) 154 } 155 156 // Tests that given a starting canonical chain of a given size, creating shorter 157 // forks do not take canonical ownership. 158 func TestShorterForkHeaders(t *testing.T) { 159 length := 10 160 161 // Make first chain starting from genesis 162 _, processor, err := newCanonical(length) 163 if err != nil { 164 t.Fatalf("failed to make new canonical chain: %v", err) 165 } 166 // Define the difficulty comparator 167 worse := func(td1, td2 *big.Int) { 168 if td2.Cmp(td1) >= 0 { 169 t.Errorf("total difficulty mismatch: have %v, expected less than %v", td2, td1) 170 } 171 } 172 // Sum of numbers must be less than `length` for this to be a shorter fork 173 testFork(t, processor, 0, 3, worse) 174 testFork(t, processor, 0, 7, worse) 175 testFork(t, processor, 1, 1, worse) 176 testFork(t, processor, 1, 7, worse) 177 testFork(t, processor, 5, 3, worse) 178 testFork(t, processor, 5, 4, worse) 179 } 180 181 // Tests that given a starting canonical chain of a given size, creating longer 182 // forks do take canonical ownership. 183 func TestLongerForkHeaders(t *testing.T) { 184 length := 10 185 186 // Make first chain starting from genesis 187 _, processor, err := newCanonical(length) 188 if err != nil { 189 t.Fatalf("failed to make new canonical chain: %v", err) 190 } 191 // Define the difficulty comparator 192 better := func(td1, td2 *big.Int) { 193 if td2.Cmp(td1) <= 0 { 194 t.Errorf("total difficulty mismatch: have %v, expected more than %v", td2, td1) 195 } 196 } 197 // Sum of numbers must be greater than `length` for this to be a longer fork 198 testFork(t, processor, 0, 11, better) 199 testFork(t, processor, 0, 15, better) 200 testFork(t, processor, 1, 10, better) 201 testFork(t, processor, 1, 12, better) 202 testFork(t, processor, 5, 6, better) 203 testFork(t, processor, 5, 8, better) 204 } 205 206 // Tests that given a starting canonical chain of a given size, creating equal 207 // forks do take canonical ownership. 208 func TestEqualForkHeaders(t *testing.T) { 209 length := 10 210 211 // Make first chain starting from genesis 212 _, processor, err := newCanonical(length) 213 if err != nil { 214 t.Fatalf("failed to make new canonical chain: %v", err) 215 } 216 // Define the difficulty comparator 217 equal := func(td1, td2 *big.Int) { 218 if td2.Cmp(td1) != 0 { 219 t.Errorf("total difficulty mismatch: have %v, want %v", td2, td1) 220 } 221 } 222 // Sum of numbers must be equal to `length` for this to be an equal fork 223 testFork(t, processor, 0, 10, equal) 224 testFork(t, processor, 1, 9, equal) 225 testFork(t, processor, 2, 8, equal) 226 testFork(t, processor, 5, 5, equal) 227 testFork(t, processor, 6, 4, equal) 228 testFork(t, processor, 9, 1, equal) 229 } 230 231 // Tests that chains missing links do not get accepted by the processor. 232 func TestBrokenHeaderChain(t *testing.T) { 233 // Make chain starting from genesis 234 db, LightChain, err := newCanonical(10) 235 if err != nil { 236 t.Fatalf("failed to make new canonical chain: %v", err) 237 } 238 // Create a forked chain, and try to insert with a missing link 239 chain := makeHeaderChain(LightChain.CurrentHeader(), 5, db, forkSeed)[1:] 240 if err := testHeaderChainImport(chain, LightChain); err == nil { 241 t.Errorf("broken header chain not reported") 242 } 243 } 244 245 func makeHeaderChainWithDiff(genesis *types.Block, d []int, seed byte) []*types.Header { 246 var chain []*types.Header 247 for i, difficulty := range d { 248 header := &types.Header{ 249 Coinbase: common.Address{seed}, 250 Number: big.NewInt(int64(i + 1)), 251 Difficulty: big.NewInt(int64(difficulty)), 252 UncleHash: types.EmptyUncleHash, 253 TxHash: types.EmptyRootHash, 254 ReceiptHash: types.EmptyRootHash, 255 } 256 if i == 0 { 257 header.ParentHash = genesis.Hash() 258 } else { 259 header.ParentHash = chain[i-1].Hash() 260 } 261 chain = append(chain, types.CopyHeader(header)) 262 } 263 return chain 264 } 265 266 type dummyOdr struct { 267 OdrBackend 268 db ethdb.Database 269 indexerConfig *IndexerConfig 270 } 271 272 func (odr *dummyOdr) Database() ethdb.Database { 273 return odr.db 274 } 275 276 func (odr *dummyOdr) Retrieve(ctx context.Context, req OdrRequest) error { 277 return nil 278 } 279 280 func (odr *dummyOdr) IndexerConfig() *IndexerConfig { 281 return odr.indexerConfig 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 afterwards 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, ethash.NewFaker(), nil) 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 }