github.com/unicornultrafoundation/go-u2u@v1.0.0-rc1.0.20240205080301-e74a83d3fadc/gossip/common_test.go (about)

     1  package gossip
     2  
     3  import (
     4  	"context"
     5  	"crypto/ecdsa"
     6  	"errors"
     7  	"fmt"
     8  	"math"
     9  	"math/big"
    10  	"sync"
    11  	"time"
    12  
    13  	"github.com/unicornultrafoundation/go-helios/consensus"
    14  	"github.com/unicornultrafoundation/go-helios/hash"
    15  	"github.com/unicornultrafoundation/go-helios/native/dag"
    16  	"github.com/unicornultrafoundation/go-helios/native/idx"
    17  	"github.com/unicornultrafoundation/go-helios/utils/cachescale"
    18  	go_u2u "github.com/unicornultrafoundation/go-u2u"
    19  	"github.com/unicornultrafoundation/go-u2u/accounts/abi/bind"
    20  	"github.com/unicornultrafoundation/go-u2u/common"
    21  	"github.com/unicornultrafoundation/go-u2u/common/hexutil"
    22  	"github.com/unicornultrafoundation/go-u2u/core/state"
    23  	"github.com/unicornultrafoundation/go-u2u/core/types"
    24  	"github.com/unicornultrafoundation/go-u2u/core/vm"
    25  	"github.com/unicornultrafoundation/go-u2u/crypto"
    26  	"github.com/unicornultrafoundation/go-u2u/log"
    27  
    28  	"github.com/unicornultrafoundation/go-u2u/evmcore"
    29  	"github.com/unicornultrafoundation/go-u2u/gossip/blockproc"
    30  	"github.com/unicornultrafoundation/go-u2u/gossip/emitter"
    31  	"github.com/unicornultrafoundation/go-u2u/integration/makefakegenesis"
    32  	"github.com/unicornultrafoundation/go-u2u/native"
    33  	"github.com/unicornultrafoundation/go-u2u/native/iblockproc"
    34  	"github.com/unicornultrafoundation/go-u2u/native/validatorpk"
    35  	"github.com/unicornultrafoundation/go-u2u/u2u"
    36  	"github.com/unicornultrafoundation/go-u2u/utils"
    37  	"github.com/unicornultrafoundation/go-u2u/utils/adapters/vecmt2dagidx"
    38  	"github.com/unicornultrafoundation/go-u2u/valkeystore"
    39  	"github.com/unicornultrafoundation/go-u2u/vecmt"
    40  )
    41  
    42  const (
    43  	gasLimit       = uint64(21000)
    44  	maxGasLimit    = uint64(6000000)
    45  	genesisBalance = 1e18
    46  	genesisStake   = 2 * 4e6
    47  
    48  	maxEpochDuration = time.Hour
    49  	sameEpoch        = maxEpochDuration / 1000
    50  	nextEpoch        = maxEpochDuration
    51  )
    52  
    53  type callbacks struct {
    54  	buildEvent       func(e *native.MutableEventPayload)
    55  	onEventConfirmed func(e native.EventI)
    56  }
    57  
    58  type testEnv struct {
    59  	t        time.Time
    60  	nonces   map[common.Address]uint64
    61  	callback callbacks
    62  	*Service
    63  	signer  valkeystore.SignerI
    64  	pubkeys []validatorpk.PubKey
    65  }
    66  
    67  func panics(name string) func(error) {
    68  	return func(err error) {
    69  		log.Crit(fmt.Sprintf("%s error", name), "err", err)
    70  	}
    71  }
    72  
    73  type testGossipStoreAdapter struct {
    74  	*Store
    75  }
    76  
    77  func (g *testGossipStoreAdapter) GetEvent(id hash.Event) dag.Event {
    78  	e := g.Store.GetEvent(id)
    79  	if e == nil {
    80  		return nil
    81  	}
    82  	return e
    83  }
    84  
    85  func makeTestEngine(gdb *Store) (*consensus.Consensus, *vecmt.Index) {
    86  	cdb := consensus.NewMemStore()
    87  	_ = cdb.ApplyGenesis(&consensus.Genesis{
    88  		Epoch:      gdb.GetEpoch(),
    89  		Validators: gdb.GetValidators(),
    90  	})
    91  	vecClock := vecmt.NewIndex(panics("Vector clock"), vecmt.LiteConfig())
    92  	engine := consensus.NewConsensus(cdb, &testGossipStoreAdapter{gdb}, vecmt2dagidx.Wrap(vecClock), panics("Hashgraph"), consensus.LiteConfig())
    93  	return engine, vecClock
    94  }
    95  
    96  type testEmitterWorldExternal struct {
    97  	emitter.External
    98  	env *testEnv
    99  }
   100  
   101  func (em testEmitterWorldExternal) Build(e *native.MutableEventPayload, onIndexed func()) error {
   102  	e.SetCreationTime(native.Timestamp(em.env.t.UnixNano()))
   103  	if em.env.callback.buildEvent != nil {
   104  		em.env.callback.buildEvent(e)
   105  	}
   106  	return em.External.Build(e, onIndexed)
   107  }
   108  
   109  func (em testEmitterWorldExternal) Broadcast(*native.EventPayload) {}
   110  
   111  type testConfirmedEventsProcessor struct {
   112  	blockproc.ConfirmedEventsProcessor
   113  	env *testEnv
   114  }
   115  
   116  func (p testConfirmedEventsProcessor) ProcessConfirmedEvent(e native.EventI) {
   117  	if p.env.callback.onEventConfirmed != nil {
   118  		p.env.callback.onEventConfirmed(e)
   119  	}
   120  	p.ConfirmedEventsProcessor.ProcessConfirmedEvent(e)
   121  }
   122  
   123  type testConfirmedEventsModule struct {
   124  	blockproc.ConfirmedEventsModule
   125  	env *testEnv
   126  }
   127  
   128  func (m testConfirmedEventsModule) Start(bs iblockproc.BlockState, es iblockproc.EpochState) blockproc.ConfirmedEventsProcessor {
   129  	p := m.ConfirmedEventsModule.Start(bs, es)
   130  	return testConfirmedEventsProcessor{p, m.env}
   131  }
   132  
   133  func newTestEnv(firstEpoch idx.Epoch, validatorsNum idx.Validator) *testEnv {
   134  	rules := u2u.FakeNetRules()
   135  	rules.Epochs.MaxEpochDuration = native.Timestamp(maxEpochDuration)
   136  	rules.Blocks.MaxEmptyBlockSkipPeriod = 0
   137  
   138  	genStore := makefakegenesis.FakeGenesisStoreWithRulesAndStart(validatorsNum, utils.ToU2U(genesisBalance), utils.ToU2U(genesisStake), rules, firstEpoch, 2)
   139  	genesis := genStore.Genesis()
   140  
   141  	store := NewMemStore()
   142  	_, err := store.ApplyGenesis(genesis)
   143  	if err != nil {
   144  		panic(err)
   145  	}
   146  
   147  	// install blockProc callbacks
   148  	env := &testEnv{
   149  		t:      store.GetGenesisTime().Time(),
   150  		nonces: make(map[common.Address]uint64),
   151  	}
   152  	blockProc := DefaultBlockProc()
   153  	blockProc.EventsModule = testConfirmedEventsModule{blockProc.EventsModule, env}
   154  
   155  	engine, vecClock := makeTestEngine(store)
   156  
   157  	// create the service
   158  	txPool := &dummyTxPool{}
   159  	env.Service, err = newService(DefaultConfig(cachescale.Identity), store, blockProc, engine, vecClock, func(_ evmcore.StateReader) TxPool {
   160  		return txPool
   161  	})
   162  	if err != nil {
   163  		panic(err)
   164  	}
   165  	txPool.signer = env.EthAPI.signer
   166  	err = engine.Bootstrap(env.GetConsensusCallbacks())
   167  	if err != nil {
   168  		panic(err)
   169  	}
   170  
   171  	valKeystore := valkeystore.NewDefaultMemKeystore()
   172  	env.signer = valkeystore.NewSigner(valKeystore)
   173  
   174  	// register emitters
   175  	for i := idx.Validator(0); i < validatorsNum; i++ {
   176  		cfg := emitter.DefaultConfig()
   177  		vid := store.GetValidators().GetID(i)
   178  		pubkey := store.GetEpochState().ValidatorProfiles[vid].PubKey
   179  		cfg.Validator = emitter.ValidatorConfig{
   180  			ID:     vid,
   181  			PubKey: pubkey,
   182  		}
   183  		cfg.EmitIntervals = emitter.EmitIntervals{}
   184  		cfg.MaxParents = idx.Event(validatorsNum/2 + 1)
   185  		cfg.MaxTxsPerAddress = 10000000
   186  		_ = valKeystore.Add(pubkey, crypto.FromECDSA(makefakegenesis.FakeKey(vid)), validatorpk.FakePassword)
   187  		_ = valKeystore.Unlock(pubkey, validatorpk.FakePassword)
   188  		world := env.EmitterWorld(env.signer)
   189  		world.External = testEmitterWorldExternal{world.External, env}
   190  		em := emitter.NewEmitter(cfg, world)
   191  		env.RegisterEmitter(em)
   192  		env.pubkeys = append(env.pubkeys, pubkey)
   193  		em.Start()
   194  	}
   195  
   196  	_ = env.store.GenerateSnapshotAt(common.Hash(store.GetBlockState().FinalizedStateRoot), false)
   197  	env.blockProcTasks.Start(1)
   198  	env.verWatcher.Start()
   199  
   200  	return env
   201  }
   202  
   203  func (env *testEnv) Close() {
   204  	env.verWatcher.Stop()
   205  	env.store.Close()
   206  	env.tflusher.Stop()
   207  }
   208  
   209  func (env *testEnv) GetEvmStateReader() *EvmStateReader {
   210  	return &EvmStateReader{
   211  		store: env.store,
   212  	}
   213  }
   214  
   215  func (env *testEnv) ApplyTxs(spent time.Duration, txs ...*types.Transaction) (types.Receipts, error) {
   216  	env.t = env.t.Add(spent)
   217  
   218  	externalReceipts := make(types.Receipts, 0, len(txs))
   219  
   220  	env.txpool.AddRemotes(txs)
   221  	defer env.txpool.(*dummyTxPool).Clear()
   222  	newBlocks := make(chan evmcore.ChainHeadNotify)
   223  	chainHeadSub := env.feed.SubscribeNewBlock(newBlocks)
   224  	mu := &sync.Mutex{}
   225  	go func() {
   226  		for b := range newBlocks {
   227  			if len(b.Block.Transactions) == 0 {
   228  				continue
   229  			}
   230  			receipts := env.store.evm.GetReceipts(idx.Block(b.Block.Number.Uint64()), env.EthAPI.signer, b.Block.Hash, b.Block.Transactions)
   231  			for i, tx := range b.Block.Transactions {
   232  				if r, _, _ := tx.RawSignatureValues(); r.Sign() != 0 {
   233  					mu.Lock()
   234  					externalReceipts = append(externalReceipts, receipts[i])
   235  					mu.Unlock()
   236  					env.txpool.(*dummyTxPool).Delete(tx.Hash())
   237  				}
   238  			}
   239  			if externalReceipts.Len() == len(txs) {
   240  				chainHeadSub.Unsubscribe()
   241  				close(newBlocks)
   242  				break
   243  			}
   244  		}
   245  	}()
   246  	err := env.EmitUntil(func() bool {
   247  		mu.Lock()
   248  		defer mu.Unlock()
   249  		return externalReceipts.Len() == len(txs)
   250  	})
   251  
   252  	return externalReceipts, err
   253  }
   254  
   255  func (env *testEnv) ApplyMPs(spent time.Duration, mps ...native.MisbehaviourProof) error {
   256  	env.t = env.t.Add(spent)
   257  
   258  	// all callbacks are non-async
   259  	lastEpoch := idx.Epoch(0)
   260  	env.callback.buildEvent = func(e *native.MutableEventPayload) {
   261  		if e.Epoch() > lastEpoch {
   262  			e.SetMisbehaviourProofs(mps)
   263  			lastEpoch = e.Epoch()
   264  		}
   265  	}
   266  	confirmed := false
   267  	env.callback.onEventConfirmed = func(_e native.EventI) {
   268  		// ensure that not only MPs were confirmed, but also no new MPs will be confirmed in future
   269  		if _e.AnyMisbehaviourProofs() && _e.Epoch() == lastEpoch {
   270  			confirmed = true
   271  			// sanity check for gas used
   272  			e := env.store.GetEventPayload(_e.ID())
   273  			rule := env.store.GetRules().Economy.Gas
   274  			if e.GasPowerUsed() < rule.EventGas+uint64(len(e.MisbehaviourProofs()))*rule.MisbehaviourProofGas {
   275  				panic("GasPowerUsed calculation doesn't include MisbehaviourProofGas")
   276  			}
   277  		}
   278  	}
   279  	defer func() {
   280  		env.callback.buildEvent = nil
   281  	}()
   282  
   283  	return env.EmitUntil(func() bool {
   284  		return confirmed
   285  	})
   286  }
   287  
   288  func (env *testEnv) EmitUntil(stop func() bool) error {
   289  	t := time.Now()
   290  
   291  	for !stop() {
   292  		for _, em := range env.emitters {
   293  			_, err := em.EmitEvent()
   294  			if err != nil {
   295  				return err
   296  			}
   297  		}
   298  		env.WaitBlockEnd()
   299  		env.t = env.t.Add(time.Second)
   300  		if time.Since(t) > 30*time.Second {
   301  			panic("block doesn't get processed")
   302  		}
   303  	}
   304  	return nil
   305  }
   306  
   307  func (env *testEnv) Transfer(from, to idx.ValidatorID, amount *big.Int) *types.Transaction {
   308  	sender := env.Address(from)
   309  	nonce, _ := env.PendingNonceAt(nil, sender)
   310  	env.incNonce(sender)
   311  	key := env.privateKey(from)
   312  	receiver := env.Address(to)
   313  	gp := new(big.Int).SetUint64(1e12)
   314  	tx := types.NewTransaction(nonce, receiver, amount, gasLimit, gp, nil)
   315  	tx, err := types.SignTx(tx, env.EthAPI.signer, key)
   316  	if err != nil {
   317  		panic(err)
   318  	}
   319  
   320  	return tx
   321  }
   322  
   323  func (env *testEnv) Contract(from idx.ValidatorID, amount *big.Int, hex string) *types.Transaction {
   324  	sender := env.Address(from)
   325  	nonce, _ := env.PendingNonceAt(nil, sender)
   326  	env.incNonce(sender)
   327  	key := env.privateKey(from)
   328  	gp := env.store.GetRules().Economy.MinGasPrice
   329  	data := hexutil.MustDecode(hex)
   330  	tx := types.NewContractCreation(nonce, amount, maxGasLimit, gp, data)
   331  	tx, err := types.SignTx(tx, env.EthAPI.signer, key)
   332  	if err != nil {
   333  		panic(err)
   334  	}
   335  
   336  	return tx
   337  }
   338  
   339  func (env *testEnv) privateKey(n idx.ValidatorID) *ecdsa.PrivateKey {
   340  	key := makefakegenesis.FakeKey(n)
   341  	return key
   342  }
   343  
   344  func (env *testEnv) Address(n idx.ValidatorID) common.Address {
   345  	key := makefakegenesis.FakeKey(n)
   346  	addr := crypto.PubkeyToAddress(key.PublicKey)
   347  	return addr
   348  }
   349  
   350  func (env *testEnv) Payer(n idx.ValidatorID, amounts ...*big.Int) *bind.TransactOpts {
   351  	key := env.privateKey(n)
   352  	t, _ := bind.NewKeyedTransactorWithChainID(key, new(big.Int).SetUint64(env.store.GetRules().NetworkID))
   353  	nonce, _ := env.PendingNonceAt(nil, env.Address(n))
   354  	t.Nonce = big.NewInt(int64(nonce))
   355  	t.Value = big.NewInt(0)
   356  	for _, amount := range amounts {
   357  		t.Value.Add(t.Value, amount)
   358  	}
   359  	t.GasLimit = env.GetEvmStateReader().MaxGasLimit()
   360  	t.GasPrice = env.GetEvmStateReader().MinGasPrice()
   361  
   362  	return t
   363  }
   364  
   365  func (env *testEnv) Pay(n idx.ValidatorID, amounts ...*big.Int) *bind.TransactOpts {
   366  	t := env.Payer(n, amounts...)
   367  	env.incNonce(t.From)
   368  
   369  	return t
   370  }
   371  
   372  func (env *testEnv) ReadOnly() *bind.CallOpts {
   373  	return &bind.CallOpts{}
   374  }
   375  
   376  func (env *testEnv) State() *state.StateDB {
   377  	statedb, _ := env.store.evm.StateDB(env.store.GetBlockState().FinalizedStateRoot)
   378  	return statedb
   379  }
   380  
   381  func (env *testEnv) incNonce(account common.Address) {
   382  	env.nonces[account] += 1
   383  }
   384  
   385  /*
   386   * bind.ContractCaller interface
   387   */
   388  
   389  var (
   390  	errBlockNumberUnsupported = errors.New("simulatedBackend cannot access blocks other than the latest block")
   391  )
   392  
   393  // CodeAt returns the code of the given account. This is needed to differentiate
   394  // between contract internal errors and the local chain being out of sync.
   395  func (env *testEnv) CodeAt(ctx context.Context, contract common.Address, blockNumber *big.Int) ([]byte, error) {
   396  	if blockNumber != nil && idx.Block(blockNumber.Uint64()) != env.store.GetLatestBlockIndex() {
   397  		return nil, errBlockNumberUnsupported
   398  	}
   399  
   400  	code := env.State().GetCode(contract)
   401  	return code, nil
   402  }
   403  
   404  // ContractCall executes an Ethereum contract call with the specified data as the
   405  // input.
   406  func (env *testEnv) CallContract(ctx context.Context, call go_u2u.CallMsg, blockNumber *big.Int) ([]byte, error) {
   407  	if blockNumber != nil && idx.Block(blockNumber.Uint64()) != env.store.GetLatestBlockIndex() {
   408  		return nil, errBlockNumberUnsupported
   409  	}
   410  
   411  	h := env.GetEvmStateReader().GetHeader(common.Hash{}, uint64(env.store.GetLatestBlockIndex()))
   412  	block := &evmcore.EvmBlock{
   413  		EvmHeader: *h,
   414  	}
   415  
   416  	rval, _, _, err := env.callContract(ctx, call, block, env.State())
   417  	return rval, err
   418  }
   419  
   420  func (env *testEnv) HeaderByNumber(ctx context.Context, number *big.Int) (*types.Header, error) {
   421  	var num64 uint64
   422  	if number == nil {
   423  		num64 = uint64(env.store.GetLatestBlockIndex())
   424  	} else {
   425  		num64 = number.Uint64()
   426  	}
   427  	return env.GetEvmStateReader().GetHeader(common.Hash{}, num64).EthHeader(), nil
   428  }
   429  
   430  // callContract implements common code between normal and pending contract calls.
   431  // state is modified during execution, make sure to copy it if necessary.
   432  func (env *testEnv) callContract(
   433  	ctx context.Context, call go_u2u.CallMsg, block *evmcore.EvmBlock, state *state.StateDB,
   434  ) (
   435  	ret []byte, usedGas uint64, failed bool, err error,
   436  ) {
   437  	// Ensure message is initialized properly.
   438  	if call.GasPrice == nil {
   439  		call.GasPrice = big.NewInt(1)
   440  	}
   441  	if call.Gas == 0 {
   442  		call.Gas = 50000000
   443  	}
   444  	if call.Value == nil {
   445  		call.Value = new(big.Int)
   446  	}
   447  	// Set infinite balance to the fake caller account.
   448  	from := state.GetOrNewStateObject(call.From)
   449  	from.SetBalance(big.NewInt(math.MaxInt64))
   450  
   451  	msg := callmsg{call}
   452  
   453  	// Create a new environment which holds all relevant information
   454  	// about the transaction and calling mechanisms.
   455  	txContext := evmcore.NewEVMTxContext(msg)
   456  	context := evmcore.NewEVMBlockContext(block.Header(), env.GetEvmStateReader(), nil)
   457  	vmenv := vm.NewEVM(context, txContext, state, env.store.GetEvmChainConfig(), u2u.DefaultVMConfig)
   458  	gaspool := new(evmcore.GasPool).AddGas(math.MaxUint64)
   459  	res, err := evmcore.NewStateTransition(vmenv, msg, gaspool).TransitionDb()
   460  
   461  	ret, usedGas, failed = res.Return(), res.UsedGas, res.Failed()
   462  	return
   463  }
   464  
   465  /*
   466   * bind.ContractTransactor interface
   467   */
   468  
   469  // PendingCodeAt returns the code of the given account in the pending state.
   470  func (env *testEnv) PendingCodeAt(ctx context.Context, account common.Address) ([]byte, error) {
   471  	code := env.State().GetCode(account)
   472  	return code, nil
   473  }
   474  
   475  // PendingNonceAt retrieves the current pending nonce associated with an account.
   476  func (env *testEnv) PendingNonceAt(ctx context.Context, account common.Address) (uint64, error) {
   477  	nonce := env.nonces[account]
   478  	return nonce, nil
   479  }
   480  
   481  // SuggestGasTipCap retrieves the currently suggested gas price tip to allow a timely
   482  // execution of a transaction.
   483  func (env *testEnv) SuggestGasTipCap(ctx context.Context) (*big.Int, error) {
   484  	return new(big.Int), nil
   485  }
   486  
   487  // SuggestGasTipCap retrieves the currently suggested gas price to allow a timely
   488  // execution of a transaction.
   489  func (env *testEnv) SuggestGasPrice(ctx context.Context) (*big.Int, error) {
   490  	return env.store.GetRules().Economy.MinGasPrice, nil
   491  }
   492  
   493  // EstimateGas tries to estimate the gas needed to execute a specific
   494  // transaction based on the current pending state of the backend blockchain.
   495  // There is no guarantee that this is the true gas limit requirement as other
   496  // transactions may be added or removed by miners, but it should provide a basis
   497  // for setting a reasonable default.
   498  func (env *testEnv) EstimateGas(ctx context.Context, call go_u2u.CallMsg) (gas uint64, err error) {
   499  	if call.To == nil {
   500  		gas = gasLimit * 10000
   501  	} else {
   502  		gas = gasLimit * 10
   503  	}
   504  	return
   505  }
   506  
   507  // SendTransaction injects the transaction into the pending pool for execution.
   508  func (env *testEnv) SendTransaction(ctx context.Context, tx *types.Transaction) error {
   509  	// do nothing to avoid executing by transactor, only generating needed
   510  	return nil
   511  }
   512  
   513  /*
   514   *  bind.ContractFilterer interface
   515   */
   516  
   517  // FilterLogs executes a log filter operation, blocking during execution and
   518  // returning all the results in one batch.
   519  func (env *testEnv) FilterLogs(ctx context.Context, query go_u2u.FilterQuery) ([]types.Log, error) {
   520  	panic("not implemented yet")
   521  	return nil, nil
   522  }
   523  
   524  // SubscribeFilterLogs creates a background log filtering operation, returning
   525  // a subscription immediately, which can be used to stream the found events.
   526  func (env *testEnv) SubscribeFilterLogs(ctx context.Context, query go_u2u.FilterQuery, ch chan<- types.Log) (go_u2u.Subscription, error) {
   527  	panic("not implemented yet")
   528  	return nil, nil
   529  }
   530  
   531  // callmsg implements evmcore.Message to allow passing it as a transaction simulator.
   532  type callmsg struct {
   533  	go_u2u.CallMsg
   534  }
   535  
   536  func (m callmsg) From() common.Address         { return m.CallMsg.From }
   537  func (m callmsg) To() *common.Address          { return m.CallMsg.To }
   538  func (m callmsg) GasPrice() *big.Int           { return m.CallMsg.GasPrice }
   539  func (m callmsg) GasTipCap() *big.Int          { return m.CallMsg.GasTipCap }
   540  func (m callmsg) GasFeeCap() *big.Int          { return m.CallMsg.GasFeeCap }
   541  func (m callmsg) Gas() uint64                  { return m.CallMsg.Gas }
   542  func (m callmsg) Value() *big.Int              { return m.CallMsg.Value }
   543  func (m callmsg) Nonce() uint64                { return 0 }
   544  func (m callmsg) IsFake() bool                 { return true }
   545  func (m callmsg) Data() []byte                 { return m.CallMsg.Data }
   546  func (m callmsg) AccessList() types.AccessList { return nil }