github.com/iotexproject/iotex-core@v1.14.1-rc1/actpool/actpool.go (about)

     1  // Copyright (c) 2019 IoTeX Foundation
     2  // This source code is provided 'as is' and no warranties are given as to title or non-infringement, merchantability
     3  // or fitness for purpose and, to the extent permitted by law, all liability for your use of the code is disclaimed.
     4  // This source code is governed by Apache License 2.0 that can be found in the LICENSE file.
     5  
     6  package actpool
     7  
     8  import (
     9  	"context"
    10  	"encoding/hex"
    11  	"sort"
    12  	"sync"
    13  	"sync/atomic"
    14  
    15  	"github.com/pkg/errors"
    16  	"github.com/prometheus/client_golang/prometheus"
    17  	"go.uber.org/zap"
    18  
    19  	"github.com/iotexproject/go-pkgs/cache/ttl"
    20  	"github.com/iotexproject/go-pkgs/hash"
    21  	"github.com/iotexproject/iotex-address/address"
    22  	"github.com/iotexproject/iotex-proto/golang/iotextypes"
    23  
    24  	"github.com/iotexproject/iotex-core/action"
    25  	"github.com/iotexproject/iotex-core/action/protocol"
    26  	accountutil "github.com/iotexproject/iotex-core/action/protocol/account/util"
    27  	"github.com/iotexproject/iotex-core/blockchain/block"
    28  	"github.com/iotexproject/iotex-core/blockchain/genesis"
    29  	"github.com/iotexproject/iotex-core/pkg/log"
    30  	"github.com/iotexproject/iotex-core/pkg/prometheustimer"
    31  	"github.com/iotexproject/iotex-core/pkg/tracer"
    32  )
    33  
    34  const (
    35  	// TODO: move to config
    36  	_numWorker = 16
    37  )
    38  
    39  var (
    40  	_actpoolMtc = prometheus.NewCounterVec(prometheus.CounterOpts{
    41  		Name: "iotex_actpool_rejection_metrics",
    42  		Help: "actpool metrics.",
    43  	}, []string{"type"})
    44  	// ErrGasTooHigh error when the intrinsic gas of an action is too high
    45  	ErrGasTooHigh = errors.New("action gas is too high")
    46  )
    47  
    48  func init() {
    49  	prometheus.MustRegister(_actpoolMtc)
    50  }
    51  
    52  // ActPool is the interface of actpool
    53  type ActPool interface {
    54  	action.SealedEnvelopeValidator
    55  	// Reset resets actpool state
    56  	Reset()
    57  	// PendingActionMap returns an action map with all accepted actions
    58  	PendingActionMap() map[string][]*action.SealedEnvelope
    59  	// Add adds an action into the pool after passing validation
    60  	Add(ctx context.Context, act *action.SealedEnvelope) error
    61  	// GetPendingNonce returns pending nonce in pool given an account address
    62  	GetPendingNonce(addr string) (uint64, error)
    63  	// GetUnconfirmedActs returns unconfirmed actions in pool given an account address
    64  	GetUnconfirmedActs(addr string) []*action.SealedEnvelope
    65  	// GetActionByHash returns the pending action in pool given action's hash
    66  	GetActionByHash(hash hash.Hash256) (*action.SealedEnvelope, error)
    67  	// GetSize returns the act pool size
    68  	GetSize() uint64
    69  	// GetCapacity returns the act pool capacity
    70  	GetCapacity() uint64
    71  	// GetGasSize returns the act pool gas size
    72  	GetGasSize() uint64
    73  	// GetGasCapacity returns the act pool gas capacity
    74  	GetGasCapacity() uint64
    75  	// DeleteAction deletes an invalid action from pool
    76  	DeleteAction(address.Address)
    77  	// ReceiveBlock will be called when a new block is committed
    78  	ReceiveBlock(*block.Block) error
    79  
    80  	AddActionEnvelopeValidators(...action.SealedEnvelopeValidator)
    81  }
    82  
    83  // SortedActions is a slice of actions that implements sort.Interface to sort by Value.
    84  type SortedActions []*action.SealedEnvelope
    85  
    86  func (p SortedActions) Swap(i, j int)      { p[i], p[j] = p[j], p[i] }
    87  func (p SortedActions) Len() int           { return len(p) }
    88  func (p SortedActions) Less(i, j int) bool { return p[i].Nonce() < p[j].Nonce() }
    89  
    90  // Option sets action pool construction parameter
    91  type Option func(pool *actPool) error
    92  
    93  // actPool implements ActPool interface
    94  type actPool struct {
    95  	cfg                      Config
    96  	g                        genesis.Genesis
    97  	sf                       protocol.StateReader
    98  	accountDesActs           *destinationMap
    99  	allActions               *ttl.Cache
   100  	gasInPool                uint64
   101  	actionEnvelopeValidators []action.SealedEnvelopeValidator
   102  	timerFactory             *prometheustimer.TimerFactory
   103  	senderBlackList          map[string]bool
   104  	jobQueue                 []chan workerJob
   105  	worker                   []*queueWorker
   106  }
   107  
   108  // NewActPool constructs a new actpool
   109  func NewActPool(g genesis.Genesis, sf protocol.StateReader, cfg Config, opts ...Option) (ActPool, error) {
   110  	if sf == nil {
   111  		return nil, errors.New("Try to attach a nil state reader")
   112  	}
   113  
   114  	senderBlackList := make(map[string]bool)
   115  	for _, bannedSender := range cfg.BlackList {
   116  		senderBlackList[bannedSender] = true
   117  	}
   118  
   119  	actsMap, _ := ttl.NewCache()
   120  	ap := &actPool{
   121  		cfg:             cfg,
   122  		g:               g,
   123  		sf:              sf,
   124  		senderBlackList: senderBlackList,
   125  		accountDesActs:  &destinationMap{acts: make(map[string]map[hash.Hash256]*action.SealedEnvelope)},
   126  		allActions:      actsMap,
   127  		jobQueue:        make([]chan workerJob, _numWorker),
   128  		worker:          make([]*queueWorker, _numWorker),
   129  	}
   130  	for _, opt := range opts {
   131  		if err := opt(ap); err != nil {
   132  			return nil, err
   133  		}
   134  	}
   135  	timerFactory, err := prometheustimer.New(
   136  		"iotex_action_pool_perf",
   137  		"Performance of action pool",
   138  		[]string{"type"},
   139  		[]string{"default"},
   140  	)
   141  	if err != nil {
   142  		return nil, err
   143  	}
   144  	ap.timerFactory = timerFactory
   145  
   146  	for i := 0; i < _numWorker; i++ {
   147  		ap.jobQueue[i] = make(chan workerJob, ap.cfg.WorkerBufferSize)
   148  		ap.worker[i] = newQueueWorker(ap, ap.jobQueue[i])
   149  		if err := ap.worker[i].Start(); err != nil {
   150  			return nil, err
   151  		}
   152  	}
   153  	return ap, nil
   154  }
   155  
   156  func (ap *actPool) AddActionEnvelopeValidators(fs ...action.SealedEnvelopeValidator) {
   157  	ap.actionEnvelopeValidators = append(ap.actionEnvelopeValidators, fs...)
   158  }
   159  
   160  // Reset resets actpool state
   161  // Step I: remove all the actions in actpool that have already been committed to block
   162  // Step II: update pending balance of each account if it still exists in pool
   163  // Step III: update queue's status in each account and remove invalid actions following queue's update
   164  // Specifically, first reset the pending nonce based on confirmed nonce in order to prevent omitting reevaluation of
   165  // unconfirmed but pending actions in pool after update of pending balance
   166  // Then starting from the current confirmed nonce, iteratively update pending nonce if nonces are consecutive and pending
   167  // balance is sufficient, and remove all the subsequent actions once the pending balance becomes insufficient
   168  func (ap *actPool) Reset() {
   169  	ap.reset()
   170  }
   171  
   172  func (ap *actPool) reset() {
   173  	var (
   174  		wg  sync.WaitGroup
   175  		ctx = ap.context(context.Background())
   176  	)
   177  	for i := range ap.worker {
   178  		wg.Add(1)
   179  		go func(worker *queueWorker) {
   180  			defer wg.Done()
   181  			worker.Reset(ctx)
   182  		}(ap.worker[i])
   183  	}
   184  	wg.Wait()
   185  }
   186  
   187  func (ap *actPool) ReceiveBlock(*block.Block) error {
   188  	ap.reset()
   189  	return nil
   190  }
   191  
   192  // PendingActionMap returns an action interator with all accepted actions
   193  func (ap *actPool) PendingActionMap() map[string][]*action.SealedEnvelope {
   194  	var (
   195  		wg             sync.WaitGroup
   196  		actsFromWorker = make([][]*pendingActions, _numWorker)
   197  		ctx            = ap.context(context.Background())
   198  		totalAccounts  = uint64(0)
   199  	)
   200  	for i := range ap.worker {
   201  		wg.Add(1)
   202  		go func(i int) {
   203  			defer wg.Done()
   204  			actsFromWorker[i] = ap.worker[i].PendingActions(ctx)
   205  			atomic.AddUint64(&totalAccounts, uint64(len(actsFromWorker[i])))
   206  		}(i)
   207  	}
   208  	wg.Wait()
   209  
   210  	ret := make(map[string][]*action.SealedEnvelope, totalAccounts)
   211  	for _, v := range actsFromWorker {
   212  		for _, w := range v {
   213  			ret[w.sender] = w.acts
   214  		}
   215  	}
   216  	return ret
   217  }
   218  
   219  func (ap *actPool) Add(ctx context.Context, act *action.SealedEnvelope) error {
   220  	ctx, span := tracer.NewSpan(ap.context(ctx), "actPool.Add")
   221  	defer span.End()
   222  	ctx = ap.context(ctx)
   223  
   224  	// system action is only added by proposer when creating a block
   225  	if action.IsSystemAction(act) {
   226  		return action.ErrInvalidAct
   227  	}
   228  
   229  	if err := checkSelpData(act); err != nil {
   230  		return err
   231  	}
   232  
   233  	if err := ap.checkSelpWithoutState(ctx, act); err != nil {
   234  		return err
   235  	}
   236  
   237  	intrinsicGas, err := act.IntrinsicGas()
   238  	if err != nil {
   239  		return err
   240  	}
   241  	if intrinsicGas > ap.cfg.MaxGasLimitPerPool {
   242  		_actpoolMtc.WithLabelValues("overMaxGasLimitPerPool").Inc()
   243  		return ErrGasTooHigh
   244  	}
   245  
   246  	return ap.enqueue(
   247  		ctx,
   248  		act,
   249  		atomic.LoadUint64(&ap.gasInPool) > ap.cfg.MaxGasLimitPerPool-intrinsicGas ||
   250  			uint64(ap.allActions.Count()) >= ap.cfg.MaxNumActsPerPool,
   251  	)
   252  }
   253  
   254  func checkSelpData(act *action.SealedEnvelope) error {
   255  	_, err := act.IntrinsicGas()
   256  	if err != nil {
   257  		return err
   258  	}
   259  	_, err = act.Hash()
   260  	if err != nil {
   261  		return err
   262  	}
   263  	_, err = act.Cost()
   264  	if err != nil {
   265  		return err
   266  	}
   267  	if act.SrcPubkey() == nil {
   268  		return action.ErrAddress
   269  	}
   270  	return nil
   271  }
   272  
   273  func (ap *actPool) checkSelpWithoutState(ctx context.Context, selp *action.SealedEnvelope) error {
   274  	span := tracer.SpanFromContext(ctx)
   275  	span.AddEvent("actPool.checkSelpWithoutState")
   276  	defer span.End()
   277  
   278  	hash, _ := selp.Hash()
   279  	// Reject action if it already exists in pool
   280  	if _, exist := ap.allActions.Get(hash); exist {
   281  		_actpoolMtc.WithLabelValues("existedAction").Inc()
   282  		return action.ErrExistedInPool
   283  	}
   284  
   285  	// Reject action if the gas price is lower than the threshold
   286  	if selp.Encoding() != uint32(iotextypes.Encoding_ETHEREUM_UNPROTECTED) && selp.GasPrice().Cmp(ap.cfg.MinGasPrice()) < 0 {
   287  		_actpoolMtc.WithLabelValues("gasPriceLower").Inc()
   288  		actHash, _ := selp.Hash()
   289  		log.L().Debug("action rejected due to low gas price",
   290  			zap.String("actionHash", hex.EncodeToString(actHash[:])),
   291  			zap.String("gasPrice", selp.GasPrice().String()))
   292  		return action.ErrUnderpriced
   293  	}
   294  
   295  	if _, ok := ap.senderBlackList[selp.SenderAddress().String()]; ok {
   296  		_actpoolMtc.WithLabelValues("blacklisted").Inc()
   297  		return errors.Wrap(action.ErrAddress, "action source address is blacklisted")
   298  	}
   299  
   300  	for _, ev := range ap.actionEnvelopeValidators {
   301  		span.AddEvent("ev.Validate")
   302  		if err := ev.Validate(ctx, selp); err != nil {
   303  			return err
   304  		}
   305  	}
   306  	return nil
   307  }
   308  
   309  // GetPendingNonce returns pending nonce in pool or confirmed nonce given an account address
   310  func (ap *actPool) GetPendingNonce(addrStr string) (uint64, error) {
   311  	addr, err := address.FromString(addrStr)
   312  	if err != nil {
   313  		return 0, err
   314  	}
   315  	if pendingNonce, ok := ap.worker[ap.allocatedWorker(addr)].PendingNonce(addr); ok {
   316  		return pendingNonce, nil
   317  	}
   318  	ctx := ap.context(context.Background())
   319  	confirmedState, err := accountutil.AccountState(ctx, ap.sf, addr)
   320  	if err != nil {
   321  		return 0, err
   322  	}
   323  	if protocol.MustGetFeatureCtx(ctx).UseZeroNonceForFreshAccount {
   324  		return confirmedState.PendingNonceConsideringFreshAccount(), nil
   325  	}
   326  	return confirmedState.PendingNonce(), nil
   327  }
   328  
   329  // GetUnconfirmedActs returns unconfirmed actions in pool given an account address
   330  func (ap *actPool) GetUnconfirmedActs(addrStr string) []*action.SealedEnvelope {
   331  	addr, err := address.FromString(addrStr)
   332  	if err != nil {
   333  		return []*action.SealedEnvelope{}
   334  	}
   335  
   336  	var ret []*action.SealedEnvelope
   337  	if actions, ok := ap.worker[ap.allocatedWorker(addr)].AllActions(addr); ok {
   338  		ret = append(ret, actions...)
   339  	}
   340  	ret = append(ret, ap.accountDesActs.actionsByDestination(addrStr)...)
   341  	return ret
   342  }
   343  
   344  // GetActionByHash returns the pending action in pool given action's hash
   345  func (ap *actPool) GetActionByHash(hash hash.Hash256) (*action.SealedEnvelope, error) {
   346  	act, ok := ap.allActions.Get(hash)
   347  	if !ok {
   348  		return nil, errors.Wrapf(action.ErrNotFound, "action hash %x does not exist in pool", hash)
   349  	}
   350  	return act.(*action.SealedEnvelope), nil
   351  }
   352  
   353  // GetSize returns the act pool size
   354  func (ap *actPool) GetSize() uint64 {
   355  	return uint64(ap.allActions.Count())
   356  }
   357  
   358  // GetCapacity returns the act pool capacity
   359  func (ap *actPool) GetCapacity() uint64 {
   360  	return ap.cfg.MaxNumActsPerPool
   361  }
   362  
   363  // GetGasSize returns the act pool gas size
   364  func (ap *actPool) GetGasSize() uint64 {
   365  	return atomic.LoadUint64(&ap.gasInPool)
   366  }
   367  
   368  // GetGasCapacity returns the act pool gas capacity
   369  func (ap *actPool) GetGasCapacity() uint64 {
   370  	return ap.cfg.MaxGasLimitPerPool
   371  }
   372  
   373  func (ap *actPool) Validate(ctx context.Context, selp *action.SealedEnvelope) error {
   374  	return ap.validate(ctx, selp)
   375  }
   376  
   377  func (ap *actPool) DeleteAction(caller address.Address) {
   378  	worker := ap.worker[ap.allocatedWorker(caller)]
   379  	if pendingActs := worker.ResetAccount(caller); len(pendingActs) != 0 {
   380  		ap.removeInvalidActs(pendingActs)
   381  	}
   382  }
   383  
   384  func (ap *actPool) validate(ctx context.Context, selp *action.SealedEnvelope) error {
   385  	span := tracer.SpanFromContext(ctx)
   386  	span.AddEvent("actPool.validate")
   387  	defer span.End()
   388  
   389  	caller := selp.SenderAddress()
   390  	if caller == nil {
   391  		return errors.New("failed to get address")
   392  	}
   393  	if _, ok := ap.senderBlackList[caller.String()]; ok {
   394  		_actpoolMtc.WithLabelValues("blacklisted").Inc()
   395  		return errors.Wrap(action.ErrAddress, "action source address is blacklisted")
   396  	}
   397  	// if already validated
   398  	selpHash, err := selp.Hash()
   399  	if err != nil {
   400  		return err
   401  	}
   402  	if _, ok := ap.allActions.Get(selpHash); ok {
   403  		return nil
   404  	}
   405  	for _, ev := range ap.actionEnvelopeValidators {
   406  		span.AddEvent("ev.Validate")
   407  		if err := ev.Validate(ctx, selp); err != nil {
   408  			return err
   409  		}
   410  	}
   411  
   412  	return nil
   413  }
   414  
   415  func (ap *actPool) removeInvalidActs(acts []*action.SealedEnvelope) {
   416  	for _, act := range acts {
   417  		hash, err := act.Hash()
   418  		if err != nil {
   419  			log.L().Debug("Skipping action due to hash error", zap.Error(err))
   420  			continue
   421  		}
   422  		log.L().Debug("Removed invalidated action.", log.Hex("hash", hash[:]))
   423  		ap.allActions.Delete(hash)
   424  		intrinsicGas, _ := act.IntrinsicGas()
   425  		atomic.AddUint64(&ap.gasInPool, ^uint64(intrinsicGas-1))
   426  		ap.accountDesActs.delete(act)
   427  	}
   428  }
   429  
   430  func (ap *actPool) context(ctx context.Context) context.Context {
   431  	height, _ := ap.sf.Height()
   432  	return protocol.WithFeatureCtx(protocol.WithBlockCtx(
   433  		genesis.WithGenesisContext(ctx, ap.g), protocol.BlockCtx{
   434  			BlockHeight: height + 1,
   435  		}))
   436  }
   437  
   438  func (ap *actPool) enqueue(ctx context.Context, act *action.SealedEnvelope, replace bool) error {
   439  	var errChan = make(chan error, 1) // unused errChan will be garbage-collected
   440  	ap.jobQueue[ap.allocatedWorker(act.SenderAddress())] <- workerJob{
   441  		ctx,
   442  		act,
   443  		replace,
   444  		errChan,
   445  	}
   446  
   447  	for {
   448  		select {
   449  		case <-ctx.Done():
   450  			log.L().Error("enqueue actpool fails", zap.Error(ctx.Err()))
   451  			return ctx.Err()
   452  		case ret := <-errChan:
   453  			return ret
   454  		}
   455  	}
   456  }
   457  
   458  func (ap *actPool) allocatedWorker(senderAddr address.Address) int {
   459  	senderBytes := senderAddr.Bytes()
   460  	var lastByte uint8 = senderBytes[len(senderBytes)-1]
   461  	return int(lastByte) % _numWorker
   462  }
   463  
   464  type destinationMap struct {
   465  	mu   sync.Mutex
   466  	acts map[string]map[hash.Hash256]*action.SealedEnvelope
   467  }
   468  
   469  func (des *destinationMap) addAction(act *action.SealedEnvelope) error {
   470  	des.mu.Lock()
   471  	defer des.mu.Unlock()
   472  	destn, ok := act.Destination()
   473  	if !ok {
   474  		return errors.New("the recipient is empty")
   475  	}
   476  	actHash, err := act.Hash()
   477  	if err != nil {
   478  		return err
   479  	}
   480  	if _, exist := des.acts[destn]; !exist {
   481  		des.acts[destn] = make(map[hash.Hash256]*action.SealedEnvelope)
   482  	}
   483  	des.acts[destn][actHash] = act
   484  	return nil
   485  }
   486  
   487  func (des *destinationMap) actionsByDestination(addr string) []*action.SealedEnvelope {
   488  	des.mu.Lock()
   489  	defer des.mu.Unlock()
   490  	desMap, ok := des.acts[addr]
   491  	if !ok {
   492  		return []*action.SealedEnvelope{}
   493  	}
   494  	sortActions := make(SortedActions, 0)
   495  	for _, v := range desMap {
   496  		sortActions = append(sortActions, v)
   497  	}
   498  	sort.Stable(sortActions)
   499  	return sortActions
   500  }
   501  
   502  func (des *destinationMap) delete(act *action.SealedEnvelope) {
   503  	des.mu.Lock()
   504  	defer des.mu.Unlock()
   505  	desAddress, ok := act.Destination()
   506  	if !ok {
   507  		return
   508  	}
   509  	hash, err := act.Hash()
   510  	if err != nil {
   511  		log.L().Debug("Skipping action due to hash error", zap.Error(err))
   512  		return
   513  	}
   514  	dst, exist := des.acts[desAddress]
   515  	if !exist {
   516  		return
   517  	}
   518  	delete(dst, hash)
   519  	if len(dst) == 0 {
   520  		delete(des.acts, desAddress)
   521  	}
   522  }