github.com/luckypickle/go-ethereum-vet@v1.14.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 "math/big" 22 "testing" 23 24 "github.com/luckypickle/go-ethereum-vet/common" 25 "github.com/luckypickle/go-ethereum-vet/consensus/ethash" 26 "github.com/luckypickle/go-ethereum-vet/core" 27 "github.com/luckypickle/go-ethereum-vet/core/rawdb" 28 "github.com/luckypickle/go-ethereum-vet/core/types" 29 "github.com/luckypickle/go-ethereum-vet/ethdb" 30 "github.com/luckypickle/go-ethereum-vet/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 := ethdb.NewMemDatabase() 56 gspec := core.Genesis{Config: params.TestChainConfig} 57 genesis := gspec.MustCommit(db) 58 blockchain, _ := NewLightChain(&dummyOdr{db: db}, gspec.Config, ethash.NewFaker()) 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 := ethdb.NewMemDatabase() 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()) 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.mu.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.mu.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 } 270 271 func (odr *dummyOdr) Database() ethdb.Database { 272 return odr.db 273 } 274 275 func (odr *dummyOdr) Retrieve(ctx context.Context, req OdrRequest) error { 276 return nil 277 } 278 279 // Tests that reorganizing a long difficult chain after a short easy one 280 // overwrites the canonical numbers and links in the database. 281 func TestReorgLongHeaders(t *testing.T) { 282 testReorg(t, []int{1, 2, 4}, []int{1, 2, 3, 4}, 10) 283 } 284 285 // Tests that reorganizing a short difficult chain after a long easy one 286 // overwrites the canonical numbers and links in the database. 287 func TestReorgShortHeaders(t *testing.T) { 288 testReorg(t, []int{1, 2, 3, 4}, []int{1, 10}, 11) 289 } 290 291 func testReorg(t *testing.T, first, second []int, td int64) { 292 bc := newTestLightChain() 293 294 // Insert an easy and a difficult chain afterwards 295 bc.InsertHeaderChain(makeHeaderChainWithDiff(bc.genesisBlock, first, 11), 1) 296 bc.InsertHeaderChain(makeHeaderChainWithDiff(bc.genesisBlock, second, 22), 1) 297 // Check that the chain is valid number and link wise 298 prev := bc.CurrentHeader() 299 for header := bc.GetHeaderByNumber(bc.CurrentHeader().Number.Uint64() - 1); header.Number.Uint64() != 0; prev, header = header, bc.GetHeaderByNumber(header.Number.Uint64()-1) { 300 if prev.ParentHash != header.Hash() { 301 t.Errorf("parent header hash mismatch: have %x, want %x", prev.ParentHash, header.Hash()) 302 } 303 } 304 // Make sure the chain total difficulty is the correct one 305 want := new(big.Int).Add(bc.genesisBlock.Difficulty(), big.NewInt(td)) 306 if have := bc.GetTdByHash(bc.CurrentHeader().Hash()); have.Cmp(want) != 0 { 307 t.Errorf("total difficulty mismatch: have %v, want %v", have, want) 308 } 309 } 310 311 // Tests that the insertion functions detect banned hashes. 312 func TestBadHeaderHashes(t *testing.T) { 313 bc := newTestLightChain() 314 315 // Create a chain, ban a hash and try to import 316 var err error 317 headers := makeHeaderChainWithDiff(bc.genesisBlock, []int{1, 2, 4}, 10) 318 core.BadHashes[headers[2].Hash()] = true 319 if _, err = bc.InsertHeaderChain(headers, 1); err != core.ErrBlacklistedHash { 320 t.Errorf("error mismatch: have: %v, want %v", err, core.ErrBlacklistedHash) 321 } 322 } 323 324 // Tests that bad hashes are detected on boot, and the chan rolled back to a 325 // good state prior to the bad hash. 326 func TestReorgBadHeaderHashes(t *testing.T) { 327 bc := newTestLightChain() 328 329 // Create a chain, import and ban afterwards 330 headers := makeHeaderChainWithDiff(bc.genesisBlock, []int{1, 2, 3, 4}, 10) 331 332 if _, err := bc.InsertHeaderChain(headers, 1); err != nil { 333 t.Fatalf("failed to import headers: %v", err) 334 } 335 if bc.CurrentHeader().Hash() != headers[3].Hash() { 336 t.Errorf("last header hash mismatch: have: %x, want %x", bc.CurrentHeader().Hash(), headers[3].Hash()) 337 } 338 core.BadHashes[headers[3].Hash()] = true 339 defer func() { delete(core.BadHashes, headers[3].Hash()) }() 340 341 // Create a new LightChain and check that it rolled back the state. 342 ncm, err := NewLightChain(&dummyOdr{db: bc.chainDb}, params.TestChainConfig, ethash.NewFaker()) 343 if err != nil { 344 t.Fatalf("failed to create new chain manager: %v", err) 345 } 346 if ncm.CurrentHeader().Hash() != headers[2].Hash() { 347 t.Errorf("last header hash mismatch: have: %x, want %x", ncm.CurrentHeader().Hash(), headers[2].Hash()) 348 } 349 }