gitlab.com/flarenetwork/coreth@v0.1.1/chain/chain_test.go (about)

     1  // (c) 2019-2020, Ava Labs, Inc. All rights reserved.
     2  // See the file LICENSE for licensing terms.
     3  
     4  package chain
     5  
     6  import (
     7  	cryptorand "crypto/rand"
     8  	"math/big"
     9  	"math/rand"
    10  	"testing"
    11  
    12  	"github.com/ethereum/go-ethereum/common"
    13  	"github.com/ethereum/go-ethereum/common/hexutil"
    14  	"github.com/ethereum/go-ethereum/log"
    15  	"github.com/ethereum/go-ethereum/rlp"
    16  	"gitlab.com/flarenetwork/coreth/accounts/keystore"
    17  	"gitlab.com/flarenetwork/coreth/consensus/dummy"
    18  	"gitlab.com/flarenetwork/coreth/core"
    19  	"gitlab.com/flarenetwork/coreth/core/rawdb"
    20  	"gitlab.com/flarenetwork/coreth/core/state"
    21  	"gitlab.com/flarenetwork/coreth/core/types"
    22  	"gitlab.com/flarenetwork/coreth/eth"
    23  	"gitlab.com/flarenetwork/coreth/eth/ethconfig"
    24  	"gitlab.com/flarenetwork/coreth/node"
    25  	"gitlab.com/flarenetwork/coreth/params"
    26  )
    27  
    28  type testChain struct {
    29  	t          *testing.T
    30  	name       string
    31  	hasBlock   map[common.Hash]struct{}
    32  	blocks     []common.Hash
    33  	blkCount   uint32
    34  	chain      *ETHChain
    35  	outBlockCh chan<- []byte
    36  	inAckCh    <-chan struct{}
    37  }
    38  
    39  func (tc *testChain) insertBlock(block *types.Block) {
    40  	if _, ok := tc.hasBlock[block.Hash()]; !ok {
    41  		tc.hasBlock[block.Hash()] = struct{}{}
    42  		tc.blocks = append(tc.blocks, block.Hash())
    43  	}
    44  }
    45  
    46  func newTestChain(name string, config *eth.Config,
    47  	inBlockCh <-chan []byte, outBlockCh chan<- []byte,
    48  	inAckCh <-chan struct{}, outAckCh chan<- struct{},
    49  	t *testing.T) *testChain {
    50  	chain, err := NewETHChain(
    51  		config,
    52  		&node.Config{},
    53  		rawdb.NewMemoryDatabase(),
    54  		eth.DefaultSettings,
    55  		&dummy.ConsensusCallbacks{
    56  			OnFinalizeAndAssemble: func(head *types.Header, _ *state.StateDB, _ []*types.Transaction) ([]byte, error) {
    57  				randData := make([]byte, 32)
    58  				_, err := rand.Read(randData)
    59  				if err != nil {
    60  					t.Fatal(err)
    61  				}
    62  				return randData, nil
    63  			},
    64  		},
    65  		common.Hash{},
    66  	)
    67  	if err != nil {
    68  		t.Fatal(err)
    69  	}
    70  	tc := &testChain{
    71  		t:          t,
    72  		name:       name,
    73  		hasBlock:   make(map[common.Hash]struct{}),
    74  		blocks:     make([]common.Hash, 0),
    75  		blkCount:   0,
    76  		chain:      chain,
    77  		outBlockCh: outBlockCh,
    78  		inAckCh:    inAckCh,
    79  	}
    80  	tc.insertBlock(tc.chain.GetGenesisBlock())
    81  	// Start a goroutine to deserialize and insert each block received from [inBlockCh]
    82  	// and send an acknowledgement via [outAckCh]
    83  	go func() {
    84  		for serialized := range inBlockCh {
    85  			block := new(types.Block)
    86  			err := rlp.DecodeBytes(serialized, block)
    87  			if err != nil {
    88  				panic(err)
    89  			}
    90  			if block.Hash() != tc.chain.GetGenesisBlock().Hash() {
    91  				if err = tc.chain.InsertBlock(block); err != nil {
    92  					tc.t.Fatalf("Failed to insert block for chain %q: %s", name, err)
    93  				}
    94  			}
    95  			tc.insertBlock(block)
    96  			outAckCh <- struct{}{}
    97  		}
    98  	}()
    99  	return tc
   100  }
   101  
   102  func (tc *testChain) start() {
   103  	tc.chain.Start()
   104  }
   105  
   106  func (tc *testChain) stop() {
   107  	tc.chain.Stop()
   108  }
   109  
   110  func (tc *testChain) GenRandomTree(genBlocks int) {
   111  	for i := 0; i < genBlocks; i++ {
   112  		numBlocks := len(tc.blocks)
   113  		parentIndex := rand.Intn(numBlocks)
   114  		parentBlockHash := tc.blocks[parentIndex]
   115  		parentBlock := tc.chain.GetBlockByHash(parentBlockHash)
   116  		if parentBlock == nil {
   117  			tc.t.Fatalf("Failed to get parent block by hash %s, %d", parentBlockHash, numBlocks)
   118  		}
   119  		if err := tc.chain.SetPreference(parentBlock); err != nil {
   120  			tc.t.Fatal(err)
   121  		}
   122  		block, err := tc.chain.GenerateBlock()
   123  		if err != nil {
   124  			tc.t.Fatalf("chain %s failed to generate block: %s", tc.name, err)
   125  		}
   126  		if err := tc.chain.InsertBlock(block); err != nil {
   127  			tc.t.Fatal(err)
   128  		}
   129  
   130  		tc.blkCount++
   131  		if len(block.Uncles()) != 0 {
   132  			tc.t.Fatal("#uncles should be zero")
   133  		}
   134  		tc.insertBlock(block)
   135  		if tc.outBlockCh != nil {
   136  			serialized, err := rlp.EncodeToBytes(block)
   137  			if err != nil {
   138  				tc.t.Fatal(err)
   139  			}
   140  			tc.outBlockCh <- serialized
   141  			<-tc.inAckCh
   142  		}
   143  	}
   144  }
   145  
   146  func run(config *eth.Config, size1, size2 int, t *testing.T) {
   147  	aliceBlk := make(chan []byte)
   148  	bobBlk := make(chan []byte)
   149  	aliceAck := make(chan struct{})
   150  	bobAck := make(chan struct{})
   151  	alice := newTestChain("alice", config, bobBlk, aliceBlk, bobAck, aliceAck, t)
   152  	bob := newTestChain("bob", config, aliceBlk, bobBlk, aliceAck, bobAck, t)
   153  	alice.start()
   154  	bob.start()
   155  	log.Info("alice genesis", "block", alice.chain.GetGenesisBlock().Hash().Hex())
   156  	log.Info("bob genesis", "block", bob.chain.GetGenesisBlock().Hash().Hex())
   157  	alice.GenRandomTree(size1)
   158  	log.Info("alice finished generating the tree")
   159  
   160  	bob.outBlockCh = nil
   161  	bob.GenRandomTree(size2)
   162  	for i := range bob.blocks {
   163  		serialized, err := rlp.EncodeToBytes(bob.chain.GetBlockByHash(bob.blocks[i]))
   164  		if err != nil {
   165  			t.Fatal(err)
   166  		}
   167  		bobBlk <- serialized
   168  		<-aliceAck
   169  	}
   170  	log.Info("bob finished generating the tree")
   171  
   172  	log.Info("comparing two trees")
   173  	if len(alice.blocks) != len(bob.blocks) {
   174  		t.Fatalf("mismatching tree size %d != %d", len(alice.blocks), len(bob.blocks))
   175  	}
   176  	gn := big.NewInt(0)
   177  	for i := range alice.blocks {
   178  		ablk := alice.chain.GetBlockByHash(alice.blocks[i])
   179  		bblk := bob.chain.GetBlockByHash(alice.blocks[i])
   180  		for ablk.Number().Cmp(gn) > 0 && bblk.Number().Cmp(gn) > 0 {
   181  			result := ablk.Hash() == bblk.Hash()
   182  			if !result {
   183  				t.Fatal("mismatching path")
   184  			}
   185  			ablk = alice.chain.GetBlockByHash(ablk.ParentHash())
   186  			bblk = bob.chain.GetBlockByHash(bblk.ParentHash())
   187  		}
   188  	}
   189  	alice.stop()
   190  	bob.stop()
   191  }
   192  
   193  // TestChain randomly generates a chain (tree of blocks) on each of two
   194  // entities ("Alice" and "Bob") and lets them exchange each other's blocks via
   195  // a go channel and finally checks if they have the identical chain structure.
   196  func TestChain(t *testing.T) {
   197  	// configure the chain
   198  	config := ethconfig.DefaultConfig
   199  	chainConfig := &params.ChainConfig{
   200  		ChainID:                     big.NewInt(1),
   201  		HomesteadBlock:              big.NewInt(0),
   202  		DAOForkBlock:                big.NewInt(0),
   203  		DAOForkSupport:              true,
   204  		EIP150Block:                 big.NewInt(0),
   205  		EIP150Hash:                  common.HexToHash("0x2086799aeebeae135c246c65021c82b4e15a2c451340993aacfd2751886514f0"),
   206  		EIP155Block:                 big.NewInt(0),
   207  		EIP158Block:                 big.NewInt(0),
   208  		ByzantiumBlock:              big.NewInt(0),
   209  		ConstantinopleBlock:         big.NewInt(0),
   210  		PetersburgBlock:             big.NewInt(0),
   211  		IstanbulBlock:               big.NewInt(0),
   212  		ApricotPhase1BlockTimestamp: big.NewInt(0),
   213  	}
   214  
   215  	// configure the genesis block
   216  	genBalance := big.NewInt(100000000000000000)
   217  	genKey, _ := keystore.NewKey(cryptorand.Reader)
   218  
   219  	config.Genesis = &core.Genesis{
   220  		Config:     chainConfig,
   221  		Nonce:      0,
   222  		Number:     0,
   223  		ExtraData:  hexutil.MustDecode("0x00"),
   224  		GasLimit:   100000000,
   225  		Difficulty: big.NewInt(0),
   226  		Alloc:      core.GenesisAlloc{genKey.Address: {Balance: genBalance}},
   227  	}
   228  
   229  	run(&config, 20, 20, t)
   230  }