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