github.com/n1ghtfa1l/go-vnt@v0.6.4-alpha.6/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/vntchain/go-vnt/common" 25 "github.com/vntchain/go-vnt/consensus/mock" 26 "github.com/vntchain/go-vnt/core" 27 "github.com/vntchain/go-vnt/core/rawdb" 28 "github.com/vntchain/go-vnt/core/types" 29 "github.com/vntchain/go-vnt/params" 30 "github.com/vntchain/go-vnt/vntdb" 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 vntdb.Database, seed int) []*types.Header { 41 blocks, _ := core.GenerateChain(params.TestChainConfig, types.NewBlockWithHeader(parent), mock.NewMock(), 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) (vntdb.Database, *LightChain, error) { 55 db := vntdb.NewMemDatabase() 56 gspec := core.Genesis{Config: params.TestChainConfig} 57 genesis := gspec.MustCommit(db) 58 blockchain, _ := NewLightChain(&dummyOdr{db: db}, gspec.Config, mock.NewMock()) 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 := vntdb.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, mock.NewMock()) 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 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 vntdb.Database 268 } 269 270 func (odr *dummyOdr) Database() vntdb.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, 1, 1}, []int{1, 1, 1, 1}, 4) 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, 1, 1, 1}, []int{1, 1}, 4) 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, 11), 1) 296 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, 1, 1}, 10) 318 core.BadHashes[headers[2].Hash()] = true 319 defer func() { delete(core.BadHashes, headers[2].Hash()) }() 320 if _, err = bc.InsertHeaderChain(headers, 1); err != core.ErrBlacklistedHash { 321 t.Errorf("error mismatch: have: %v, want %v", err, core.ErrBlacklistedHash) 322 } 323 } 324 325 // Tests that bad hashes are detected on boot, and the chan rolled back to a 326 // good state prior to the bad hash. 327 func TestReorgBadHeaderHashes(t *testing.T) { 328 bc := newTestLightChain() 329 330 // Create a chain, import and ban aferwards 331 headers := makeHeaderChainWithDiff(bc.genesisBlock, []int{1, 1, 1, 1}, 10) 332 333 if _, err := bc.InsertHeaderChain(headers, 1); err != nil { 334 t.Fatalf("failed to import headers: %v", err) 335 } 336 if bc.CurrentHeader().Hash() != headers[3].Hash() { 337 t.Errorf("last header hash mismatch: have: %x, want %x", bc.CurrentHeader().Hash(), headers[3].Hash()) 338 } 339 core.BadHashes[headers[3].Hash()] = true 340 defer func() { delete(core.BadHashes, headers[3].Hash()) }() 341 342 // Create a new LightChain and check that it rolled back the state. 343 ncm, err := NewLightChain(&dummyOdr{db: bc.chainDb}, params.TestChainConfig, mock.NewMock()) 344 if err != nil { 345 t.Fatalf("failed to create new chain manager: %v", err) 346 } 347 if ncm.CurrentHeader().Hash() != headers[2].Hash() { 348 t.Errorf("last header hash mismatch: have: %x, want %x", ncm.CurrentHeader().Hash(), headers[2].Hash()) 349 } 350 }