github.com/aidoskuneen/adk-node@v0.0.0-20220315131952-2e32567cb7f4/light/lightchain_test.go (about)

     1  // Copyright 2021 The adkgo Authors
     2  // This file is part of the adkgo library (adapted for adkgo from go--ethereum v1.10.8).
     3  //
     4  // the adkgo 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 adkgo 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 adkgo 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/aidoskuneen/adk-node/common"
    26  	"github.com/aidoskuneen/adk-node/consensus/ethash"
    27  	"github.com/aidoskuneen/adk-node/core"
    28  	"github.com/aidoskuneen/adk-node/core/rawdb"
    29  	"github.com/aidoskuneen/adk-node/core/types"
    30  	"github.com/aidoskuneen/adk-node/ethdb"
    31  	"github.com/aidoskuneen/adk-node/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.ErrBannedHash) {
   326  		t.Errorf("error mismatch: have: %v, want %v", err, core.ErrBannedHash)
   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  }