github.com/palisadeinc/bor@v0.0.0-20230615125219-ab7196213d15/tests/bor/helper.go (about)

     1  //go:build integration
     2  
     3  package bor
     4  
     5  import (
     6  	"context"
     7  	"crypto/ecdsa"
     8  	"encoding/hex"
     9  	"encoding/json"
    10  	"fmt"
    11  	"io/ioutil"
    12  	"math/big"
    13  	"sort"
    14  	"testing"
    15  	"time"
    16  
    17  	"github.com/golang/mock/gomock"
    18  
    19  	"github.com/ethereum/go-ethereum/accounts"
    20  	"github.com/ethereum/go-ethereum/accounts/keystore"
    21  	"github.com/ethereum/go-ethereum/cmd/utils"
    22  	"github.com/ethereum/go-ethereum/common"
    23  	"github.com/ethereum/go-ethereum/consensus"
    24  	"github.com/ethereum/go-ethereum/consensus/bor"
    25  	"github.com/ethereum/go-ethereum/consensus/bor/clerk"
    26  	"github.com/ethereum/go-ethereum/consensus/bor/heimdall" //nolint:typecheck
    27  	"github.com/ethereum/go-ethereum/consensus/bor/heimdall/span"
    28  	"github.com/ethereum/go-ethereum/consensus/bor/valset"
    29  	"github.com/ethereum/go-ethereum/consensus/misc"
    30  	"github.com/ethereum/go-ethereum/core"
    31  	"github.com/ethereum/go-ethereum/core/state"
    32  	"github.com/ethereum/go-ethereum/core/types"
    33  	"github.com/ethereum/go-ethereum/core/vm"
    34  	"github.com/ethereum/go-ethereum/crypto"
    35  	"github.com/ethereum/go-ethereum/crypto/secp256k1"
    36  	"github.com/ethereum/go-ethereum/eth"
    37  	"github.com/ethereum/go-ethereum/eth/downloader"
    38  	"github.com/ethereum/go-ethereum/eth/ethconfig"
    39  	"github.com/ethereum/go-ethereum/ethdb"
    40  	"github.com/ethereum/go-ethereum/miner"
    41  	"github.com/ethereum/go-ethereum/node"
    42  	"github.com/ethereum/go-ethereum/p2p"
    43  	"github.com/ethereum/go-ethereum/p2p/enode"
    44  	"github.com/ethereum/go-ethereum/params"
    45  	"github.com/ethereum/go-ethereum/tests/bor/mocks"
    46  )
    47  
    48  var (
    49  
    50  	// Only this account is a validator for the 0th span
    51  	key, _ = crypto.HexToECDSA(privKey)
    52  	addr   = crypto.PubkeyToAddress(key.PublicKey) // 0x71562b71999873DB5b286dF957af199Ec94617F7
    53  
    54  	// This account is one the validators for 1st span (0-indexed)
    55  	key2, _ = crypto.HexToECDSA(privKey2)
    56  	addr2   = crypto.PubkeyToAddress(key2.PublicKey) // 0x9fB29AAc15b9A4B7F17c3385939b007540f4d791
    57  
    58  	keys = []*ecdsa.PrivateKey{key, key2}
    59  )
    60  
    61  const (
    62  	privKey  = "b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291"
    63  	privKey2 = "9b28f36fbd67381120752d6172ecdcf10e06ab2d9a1367aac00cdcd6ac7855d3"
    64  
    65  	// The genesis for tests was generated with following parameters
    66  	extraSeal = 65 // Fixed number of extra-data suffix bytes reserved for signer seal
    67  
    68  	sprintSize uint64 = 4
    69  	spanSize   uint64 = 8
    70  
    71  	validatorHeaderBytesLength = common.AddressLength + 20 // address + power
    72  )
    73  
    74  type initializeData struct {
    75  	genesis  *core.Genesis
    76  	ethereum *eth.Ethereum
    77  }
    78  
    79  func setupMiner(t *testing.T, n int, genesis *core.Genesis) ([]*node.Node, []*eth.Ethereum, []*enode.Node) {
    80  	t.Helper()
    81  
    82  	// Create an Ethash network based off of the Ropsten config
    83  	var (
    84  		stacks []*node.Node
    85  		nodes  []*eth.Ethereum
    86  		enodes []*enode.Node
    87  	)
    88  
    89  	for i := 0; i < n; i++ {
    90  		// Start the node and wait until it's up
    91  		stack, ethBackend, err := InitMiner(genesis, keys[i], true)
    92  		if err != nil {
    93  			t.Fatal("Error occured while initialising miner", "error", err)
    94  		}
    95  
    96  		for stack.Server().NodeInfo().Ports.Listener == 0 {
    97  			time.Sleep(250 * time.Millisecond)
    98  		}
    99  		// Connect the node to all the previous ones
   100  		for _, n := range enodes {
   101  			stack.Server().AddPeer(n)
   102  		}
   103  		// Start tracking the node and its enode
   104  		stacks = append(stacks, stack)
   105  		nodes = append(nodes, ethBackend)
   106  		enodes = append(enodes, stack.Server().Self())
   107  	}
   108  
   109  	return stacks, nodes, enodes
   110  }
   111  
   112  func buildEthereumInstance(t *testing.T, db ethdb.Database) *initializeData {
   113  	genesisData, err := ioutil.ReadFile("./testdata/genesis.json")
   114  	if err != nil {
   115  		t.Fatalf("%s", err)
   116  	}
   117  
   118  	gen := &core.Genesis{}
   119  
   120  	if err := json.Unmarshal(genesisData, gen); err != nil {
   121  		t.Fatalf("%s", err)
   122  	}
   123  
   124  	ethConf := &eth.Config{
   125  		Genesis: gen,
   126  		BorLogs: true,
   127  	}
   128  
   129  	ethConf.Genesis.MustCommit(db)
   130  
   131  	ethereum := utils.CreateBorEthereum(ethConf)
   132  	if err != nil {
   133  		t.Fatalf("failed to register Ethereum protocol: %v", err)
   134  	}
   135  
   136  	ethConf.Genesis.MustCommit(ethereum.ChainDb())
   137  
   138  	ethereum.Engine().(*bor.Bor).Authorize(addr, func(account accounts.Account, s string, data []byte) ([]byte, error) {
   139  		return crypto.Sign(crypto.Keccak256(data), key)
   140  	})
   141  
   142  	return &initializeData{
   143  		genesis:  gen,
   144  		ethereum: ethereum,
   145  	}
   146  }
   147  
   148  func insertNewBlock(t *testing.T, chain *core.BlockChain, block *types.Block) {
   149  	t.Helper()
   150  
   151  	if _, err := chain.InsertChain([]*types.Block{block}); err != nil {
   152  		t.Fatalf("%s", err)
   153  	}
   154  }
   155  
   156  type Option func(header *types.Header)
   157  
   158  func buildNextBlock(t *testing.T, _bor consensus.Engine, chain *core.BlockChain, parentBlock *types.Block, signer []byte, borConfig *params.BorConfig, txs []*types.Transaction, currentValidators []*valset.Validator, opts ...Option) *types.Block {
   159  	t.Helper()
   160  
   161  	header := &types.Header{
   162  		Number:     big.NewInt(int64(parentBlock.Number().Uint64() + 1)),
   163  		Difficulty: big.NewInt(int64(parentBlock.Difficulty().Uint64())),
   164  		GasLimit:   parentBlock.GasLimit(),
   165  		ParentHash: parentBlock.Hash(),
   166  	}
   167  	number := header.Number.Uint64()
   168  
   169  	if signer == nil {
   170  		signer = getSignerKey(header.Number.Uint64())
   171  	}
   172  
   173  	header.Time = parentBlock.Time() + bor.CalcProducerDelay(header.Number.Uint64(), 0, borConfig)
   174  	header.Extra = make([]byte, 32+65) // vanity + extraSeal
   175  
   176  	isSpanStart := IsSpanStart(number)
   177  	isSprintEnd := IsSprintEnd(number)
   178  
   179  	if isSpanStart {
   180  		header.Difficulty = new(big.Int).SetInt64(int64(len(currentValidators)))
   181  	}
   182  
   183  	if isSprintEnd {
   184  		sort.Sort(valset.ValidatorsByAddress(currentValidators))
   185  
   186  		validatorBytes := make([]byte, len(currentValidators)*validatorHeaderBytesLength)
   187  		header.Extra = make([]byte, 32+len(validatorBytes)+65) // vanity + validatorBytes + extraSeal
   188  
   189  		for i, val := range currentValidators {
   190  			copy(validatorBytes[i*validatorHeaderBytesLength:], val.HeaderBytes())
   191  		}
   192  
   193  		copy(header.Extra[32:], validatorBytes)
   194  	}
   195  
   196  	if chain.Config().IsLondon(header.Number) {
   197  		header.BaseFee = misc.CalcBaseFee(chain.Config(), parentBlock.Header())
   198  
   199  		if !chain.Config().IsLondon(parentBlock.Number()) {
   200  			parentGasLimit := parentBlock.GasLimit() * params.ElasticityMultiplier
   201  			header.GasLimit = core.CalcGasLimit(parentGasLimit, parentGasLimit)
   202  		}
   203  	}
   204  
   205  	for _, opt := range opts {
   206  		opt(header)
   207  	}
   208  
   209  	state, err := chain.State()
   210  	if err != nil {
   211  		t.Fatalf("%s", err)
   212  	}
   213  
   214  	b := &blockGen{header: header}
   215  	for _, tx := range txs {
   216  		b.addTxWithChain(chain, state, tx, addr)
   217  	}
   218  
   219  	ctx := context.Background()
   220  
   221  	// Finalize and seal the block
   222  	block, _ := _bor.FinalizeAndAssemble(ctx, chain, b.header, state, b.txs, nil, b.receipts)
   223  
   224  	// Write state changes to db
   225  	root, err := state.Commit(chain.Config().IsEIP158(b.header.Number))
   226  	if err != nil {
   227  		panic(fmt.Sprintf("state write error: %v", err))
   228  	}
   229  
   230  	if err := state.Database().TrieDB().Commit(root, false, nil); err != nil {
   231  		panic(fmt.Sprintf("trie write error: %v", err))
   232  	}
   233  
   234  	res := make(chan *types.Block, 1)
   235  
   236  	err = _bor.Seal(ctx, chain, block, res, nil)
   237  	if err != nil {
   238  		// an error case - sign manually
   239  		sign(t, header, signer, borConfig)
   240  		return types.NewBlockWithHeader(header)
   241  	}
   242  
   243  	return <-res
   244  }
   245  
   246  type blockGen struct {
   247  	txs      []*types.Transaction
   248  	receipts []*types.Receipt
   249  	gasPool  *core.GasPool
   250  	header   *types.Header
   251  }
   252  
   253  func (b *blockGen) addTxWithChain(bc *core.BlockChain, statedb *state.StateDB, tx *types.Transaction, coinbase common.Address) {
   254  	if b.gasPool == nil {
   255  		b.setCoinbase(coinbase)
   256  	}
   257  
   258  	statedb.Prepare(tx.Hash(), len(b.txs))
   259  
   260  	receipt, err := core.ApplyTransaction(bc.Config(), bc, &b.header.Coinbase, b.gasPool, statedb, b.header, tx, &b.header.GasUsed, vm.Config{}, nil)
   261  	if err != nil {
   262  		panic(err)
   263  	}
   264  
   265  	b.txs = append(b.txs, tx)
   266  	b.receipts = append(b.receipts, receipt)
   267  }
   268  
   269  func (b *blockGen) setCoinbase(addr common.Address) {
   270  	if b.gasPool != nil {
   271  		if len(b.txs) > 0 {
   272  			panic("coinbase must be set before adding transactions")
   273  		}
   274  
   275  		panic("coinbase can only be set once")
   276  	}
   277  
   278  	b.header.Coinbase = addr
   279  	b.gasPool = new(core.GasPool).AddGas(b.header.GasLimit)
   280  }
   281  
   282  func sign(t *testing.T, header *types.Header, signer []byte, c *params.BorConfig) {
   283  	t.Helper()
   284  
   285  	sig, err := secp256k1.Sign(crypto.Keccak256(bor.BorRLP(header, c)), signer)
   286  	if err != nil {
   287  		t.Fatalf("%s", err)
   288  	}
   289  
   290  	copy(header.Extra[len(header.Extra)-extraSeal:], sig)
   291  }
   292  
   293  //nolint:unused,deadcode
   294  func stateSyncEventsPayload(t *testing.T) *heimdall.StateSyncEventsResponse {
   295  	t.Helper()
   296  
   297  	stateData, err := ioutil.ReadFile("./testdata/states.json")
   298  	if err != nil {
   299  		t.Fatalf("%s", err)
   300  	}
   301  
   302  	res := &heimdall.StateSyncEventsResponse{}
   303  	if err := json.Unmarshal(stateData, res); err != nil {
   304  		t.Fatalf("%s", err)
   305  	}
   306  
   307  	return res
   308  }
   309  
   310  //nolint:unused,deadcode
   311  func loadSpanFromFile(t *testing.T) (*heimdall.SpanResponse, *span.HeimdallSpan) {
   312  	t.Helper()
   313  
   314  	spanData, err := ioutil.ReadFile("./testdata/span.json")
   315  	if err != nil {
   316  		t.Fatalf("%s", err)
   317  	}
   318  
   319  	res := &heimdall.SpanResponse{}
   320  
   321  	if err := json.Unmarshal(spanData, res); err != nil {
   322  		t.Fatalf("%s", err)
   323  	}
   324  
   325  	return res, &res.Result
   326  }
   327  
   328  func getSignerKey(number uint64) []byte {
   329  	signerKey := privKey
   330  
   331  	if IsSpanStart(number) {
   332  		// validator set in the new span has changed
   333  		signerKey = privKey2
   334  	}
   335  
   336  	newKey, _ := hex.DecodeString(signerKey)
   337  
   338  	return newKey
   339  }
   340  
   341  func getMockedHeimdallClient(t *testing.T, heimdallSpan *span.HeimdallSpan) (*mocks.MockIHeimdallClient, *gomock.Controller) {
   342  	t.Helper()
   343  
   344  	ctrl := gomock.NewController(t)
   345  	h := mocks.NewMockIHeimdallClient(ctrl)
   346  
   347  	h.EXPECT().Span(gomock.Any(), uint64(1)).Return(heimdallSpan, nil).AnyTimes()
   348  
   349  	h.EXPECT().StateSyncEvents(gomock.Any(), gomock.Any(), gomock.Any()).
   350  		Return([]*clerk.EventRecordWithTime{getSampleEventRecord(t)}, nil).AnyTimes()
   351  
   352  	return h, ctrl
   353  }
   354  
   355  func getMockedSpanner(t *testing.T, validators []*valset.Validator) *bor.MockSpanner {
   356  	t.Helper()
   357  
   358  	spanner := bor.NewMockSpanner(gomock.NewController(t))
   359  	spanner.EXPECT().GetCurrentValidatorsByHash(gomock.Any(), gomock.Any(), gomock.Any()).Return(validators, nil).AnyTimes()
   360  	spanner.EXPECT().GetCurrentValidatorsByBlockNrOrHash(gomock.Any(), gomock.Any(), gomock.Any()).Return(validators, nil).AnyTimes()
   361  	spanner.EXPECT().GetCurrentSpan(gomock.Any(), gomock.Any()).Return(&span.Span{0, 0, 0}, nil).AnyTimes()
   362  	spanner.EXPECT().CommitSpan(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(nil).AnyTimes()
   363  	return spanner
   364  }
   365  
   366  func generateFakeStateSyncEvents(sample *clerk.EventRecordWithTime, count int) []*clerk.EventRecordWithTime {
   367  	events := make([]*clerk.EventRecordWithTime, count)
   368  	event := *sample
   369  	event.ID = 1
   370  	events[0] = &clerk.EventRecordWithTime{}
   371  	*events[0] = event
   372  
   373  	for i := 1; i < count; i++ {
   374  		event.ID = uint64(i + 1)
   375  		event.Time = event.Time.Add(1 * time.Second)
   376  		events[i] = &clerk.EventRecordWithTime{}
   377  		*events[i] = event
   378  	}
   379  
   380  	return events
   381  }
   382  
   383  func buildStateEvent(sample *clerk.EventRecordWithTime, id uint64, timeStamp int64) *clerk.EventRecordWithTime {
   384  	event := *sample
   385  	event.ID = id
   386  	event.Time = time.Unix(timeStamp, 0)
   387  
   388  	return &event
   389  }
   390  
   391  func getSampleEventRecord(t *testing.T) *clerk.EventRecordWithTime {
   392  	t.Helper()
   393  
   394  	eventRecords := stateSyncEventsPayload(t)
   395  	eventRecords.Result[0].Time = time.Unix(1, 0)
   396  
   397  	return eventRecords.Result[0]
   398  }
   399  
   400  func newGwei(n int64) *big.Int {
   401  	return new(big.Int).Mul(big.NewInt(n), big.NewInt(params.GWei))
   402  }
   403  
   404  func IsSpanEnd(number uint64) bool {
   405  	return (number+1)%spanSize == 0
   406  }
   407  
   408  func IsSpanStart(number uint64) bool {
   409  	return number%spanSize == 0
   410  }
   411  
   412  func IsSprintStart(number uint64) bool {
   413  	return number%sprintSize == 0
   414  }
   415  
   416  func IsSprintEnd(number uint64) bool {
   417  	return (number+1)%sprintSize == 0
   418  }
   419  
   420  func InitGenesis(t *testing.T, faucets []*ecdsa.PrivateKey, fileLocation string, sprintSize uint64) *core.Genesis {
   421  	t.Helper()
   422  
   423  	// sprint size = 8 in genesis
   424  	genesisData, err := ioutil.ReadFile(fileLocation)
   425  	if err != nil {
   426  		t.Fatalf("%s", err)
   427  	}
   428  
   429  	genesis := &core.Genesis{}
   430  
   431  	if err := json.Unmarshal(genesisData, genesis); err != nil {
   432  		t.Fatalf("%s", err)
   433  	}
   434  
   435  	genesis.Config.ChainID = big.NewInt(15001)
   436  	genesis.Config.EIP150Hash = common.Hash{}
   437  	genesis.Config.Bor.Sprint["0"] = sprintSize
   438  
   439  	return genesis
   440  }
   441  
   442  func InitMiner(genesis *core.Genesis, privKey *ecdsa.PrivateKey, withoutHeimdall bool) (*node.Node, *eth.Ethereum, error) {
   443  	// Define the basic configurations for the Ethereum node
   444  	datadir, _ := ioutil.TempDir("", "")
   445  
   446  	config := &node.Config{
   447  		Name:    "geth",
   448  		Version: params.Version,
   449  		DataDir: datadir,
   450  		P2P: p2p.Config{
   451  			ListenAddr:  "0.0.0.0:0",
   452  			NoDiscovery: true,
   453  			MaxPeers:    25,
   454  		},
   455  		UseLightweightKDF: true,
   456  	}
   457  	// Create the node and configure a full Ethereum node on it
   458  	stack, err := node.New(config)
   459  	if err != nil {
   460  		return nil, nil, err
   461  	}
   462  
   463  	ethBackend, err := eth.New(stack, &ethconfig.Config{
   464  		Genesis:         genesis,
   465  		NetworkId:       genesis.Config.ChainID.Uint64(),
   466  		SyncMode:        downloader.FullSync,
   467  		DatabaseCache:   256,
   468  		DatabaseHandles: 256,
   469  		TxPool:          core.DefaultTxPoolConfig,
   470  		GPO:             ethconfig.Defaults.GPO,
   471  		Ethash:          ethconfig.Defaults.Ethash,
   472  		Miner: miner.Config{
   473  			Etherbase: crypto.PubkeyToAddress(privKey.PublicKey),
   474  			GasCeil:   genesis.GasLimit * 11 / 10,
   475  			GasPrice:  big.NewInt(1),
   476  			Recommit:  time.Second,
   477  		},
   478  		WithoutHeimdall: withoutHeimdall,
   479  	})
   480  
   481  	if err != nil {
   482  		return nil, nil, err
   483  	}
   484  
   485  	// register backend to account manager with keystore for signing
   486  	keydir := stack.KeyStoreDir()
   487  
   488  	n, p := keystore.StandardScryptN, keystore.StandardScryptP
   489  	kStore := keystore.NewKeyStore(keydir, n, p)
   490  
   491  	_, err = kStore.ImportECDSA(privKey, "")
   492  
   493  	if err != nil {
   494  		return nil, nil, err
   495  	}
   496  
   497  	acc := kStore.Accounts()[0]
   498  	err = kStore.Unlock(acc, "")
   499  
   500  	if err != nil {
   501  		return nil, nil, err
   502  	}
   503  
   504  	// proceed to authorize the local account manager in any case
   505  	ethBackend.AccountManager().AddBackend(kStore)
   506  
   507  	err = stack.Start()
   508  
   509  	return stack, ethBackend, err
   510  }