github.com/aergoio/aergo@v1.3.1/chain/signVerifier.go (about)

     1  package chain
     2  
     3  import (
     4  	"errors"
     5  	"time"
     6  
     7  	"github.com/aergoio/aergo-actor/actor"
     8  	"github.com/aergoio/aergo/account/key"
     9  	"github.com/aergoio/aergo/contract/name"
    10  	"github.com/aergoio/aergo/internal/enc"
    11  	"github.com/aergoio/aergo/message"
    12  	"github.com/aergoio/aergo/pkg/component"
    13  	"github.com/aergoio/aergo/state"
    14  	"github.com/aergoio/aergo/types"
    15  )
    16  
    17  type SignVerifier struct {
    18  	comm component.IComponentRequester
    19  
    20  	sdb *state.ChainStateDB
    21  
    22  	workerCnt int
    23  	workCh    chan verifyWork
    24  	doneCh    chan verifyWorkRes
    25  	resultCh  chan *VerifyResult
    26  
    27  	useMempool  bool
    28  	skipMempool bool /* when sync */
    29  	totalHit    int
    30  }
    31  
    32  type verifyWork struct {
    33  	idx        int
    34  	tx         *types.Tx
    35  	useMempool bool // not to use aop for performance
    36  }
    37  
    38  type verifyWorkRes struct {
    39  	work *verifyWork
    40  	err  error
    41  	hit  bool
    42  }
    43  
    44  type VerifyResult struct {
    45  	failed bool
    46  	errs   []error
    47  }
    48  
    49  var (
    50  	ErrTxFormatInvalid = errors.New("tx invalid format")
    51  	dfltUseMempool     = true
    52  	//logger = log.NewLogger("signverifier")
    53  )
    54  
    55  func NewSignVerifier(comm component.IComponentRequester, sdb *state.ChainStateDB, workerCnt int, useMempool bool) *SignVerifier {
    56  	sv := &SignVerifier{
    57  		comm:       comm,
    58  		sdb:        sdb,
    59  		workerCnt:  workerCnt,
    60  		workCh:     make(chan verifyWork, workerCnt),
    61  		doneCh:     make(chan verifyWorkRes, workerCnt),
    62  		resultCh:   make(chan *VerifyResult, 1),
    63  		useMempool: useMempool,
    64  	}
    65  
    66  	for i := 0; i < workerCnt; i++ {
    67  		go sv.verifyTxLoop(i)
    68  	}
    69  
    70  	return sv
    71  }
    72  
    73  func (sv *SignVerifier) Stop() {
    74  	close(sv.workCh)
    75  	close(sv.doneCh)
    76  }
    77  
    78  func (sv *SignVerifier) verifyTxLoop(workerNo int) {
    79  	logger.Debug().Int("worker", workerNo).Msg("verify worker run")
    80  
    81  	for txWork := range sv.workCh {
    82  		//logger.Debug().Int("worker", workerNo).Int("idx", txWork.idx).Msg("get work to verify tx")
    83  		hit, err := sv.verifyTx(sv.comm, txWork.tx, txWork.useMempool)
    84  
    85  		if err != nil {
    86  			logger.Error().Int("worker", workerNo).Bool("hit", hit).Str("hash", enc.ToString(txWork.tx.GetHash())).
    87  				Err(err).Msg("error verify tx")
    88  		}
    89  
    90  		sv.doneCh <- verifyWorkRes{work: &txWork, err: err, hit: hit}
    91  	}
    92  
    93  	logger.Debug().Int("worker", workerNo).Msg("verify worker stop")
    94  }
    95  
    96  func (sv *SignVerifier) isExistInMempool(comm component.IComponentRequester, tx *types.Tx) (bool, error) {
    97  	if !sv.useMempool {
    98  		return false, nil
    99  	}
   100  
   101  	result, err := comm.RequestToFutureResult(message.MemPoolSvc, &message.MemPoolExist{Hash: tx.GetHash()}, time.Second,
   102  		"chain/signverifier/verifytx")
   103  	if err != nil {
   104  		logger.Error().Err(err).Msg("failed to get verify from mempool")
   105  		if err == actor.ErrTimeout {
   106  			return false, nil
   107  		}
   108  		return false, err
   109  	}
   110  
   111  	msg := result.(*message.MemPoolExistRsp)
   112  	if msg.Tx != nil {
   113  		return true, nil
   114  	}
   115  
   116  	return false, nil
   117  }
   118  
   119  func (sv *SignVerifier) verifyTx(comm component.IComponentRequester, tx *types.Tx, useMempool bool) (hit bool, err error) {
   120  	account := tx.GetBody().GetAccount()
   121  	if account == nil {
   122  		return false, ErrTxFormatInvalid
   123  	}
   124  
   125  	if useMempool {
   126  		if hit, err = sv.isExistInMempool(comm, tx); err != nil {
   127  			return false, err
   128  		}
   129  		if hit {
   130  			return hit, nil
   131  		}
   132  	}
   133  
   134  	if tx.NeedNameVerify() {
   135  		cs, err := sv.sdb.GetStateDB().OpenContractStateAccount(types.ToAccountID([]byte(types.AergoName)))
   136  		if err != nil {
   137  			logger.Error().Err(err).Msg("failed to get verify because of openning contract error")
   138  			return false, err
   139  		}
   140  		address := name.GetOwner(cs, tx.Body.Account)
   141  		err = key.VerifyTxWithAddress(tx, address)
   142  		if err != nil {
   143  			return false, err
   144  		}
   145  	} else {
   146  		err := key.VerifyTx(tx)
   147  		if err != nil {
   148  			return false, err
   149  		}
   150  	}
   151  	return false, nil
   152  }
   153  
   154  func (sv *SignVerifier) RequestVerifyTxs(txlist *types.TxList) {
   155  	txs := txlist.GetTxs()
   156  	txLen := len(txs)
   157  
   158  	if txLen == 0 {
   159  		sv.resultCh <- &VerifyResult{failed: false, errs: nil}
   160  		return
   161  	}
   162  
   163  	errs := make([]error, txLen, txLen)
   164  
   165  	//logger.Debug().Int("txlen", txLen).Msg("verify tx start")
   166  	useMempool := sv.useMempool && !sv.skipMempool
   167  
   168  	go func() {
   169  		for i, tx := range txs {
   170  			//logger.Debug().Int("idx", i).Msg("push tx start")
   171  			sv.workCh <- verifyWork{idx: i, tx: tx, useMempool: useMempool}
   172  		}
   173  	}()
   174  
   175  	go func() {
   176  		var doneCnt = 0
   177  		failed := false
   178  		sv.totalHit = 0
   179  
   180  		start := time.Now()
   181  	LOOP:
   182  		for {
   183  			select {
   184  			case result := <-sv.doneCh:
   185  				doneCnt++
   186  				//logger.Debug().Int("donecnt", doneCnt).Msg("verify tx done")
   187  
   188  				if result.work.idx < 0 || result.work.idx >= txLen {
   189  					logger.Error().Int("idx", result.work.idx).Msg("Invalid Verify Result Index")
   190  					continue
   191  				}
   192  
   193  				errs[result.work.idx] = result.err
   194  
   195  				if result.err != nil {
   196  					logger.Error().Err(result.err).Int("txno", result.work.idx).
   197  						Msg("verifing tx failed")
   198  					failed = true
   199  				}
   200  
   201  				if result.hit {
   202  					sv.totalHit++
   203  				}
   204  
   205  				if doneCnt == txLen {
   206  					break LOOP
   207  				}
   208  			}
   209  		}
   210  		sv.resultCh <- &VerifyResult{failed: failed, errs: errs}
   211  
   212  		end := time.Now()
   213  		avg := end.Sub(start) / time.Duration(txLen)
   214  		newAvg := types.AvgTxVerifyTime.UpdateAverage(avg)
   215  
   216  		logger.Debug().Int("hit", sv.totalHit).Int64("curavg", avg.Nanoseconds()).Int64("newavg", newAvg.Nanoseconds()).Msg("verify tx done")
   217  	}()
   218  	return
   219  }
   220  
   221  func (sv *SignVerifier) WaitDone() (bool, []error) {
   222  	select {
   223  	case res := <-sv.resultCh:
   224  		logger.Debug().Msg("wait verify tx")
   225  		return res.failed, res.errs
   226  	}
   227  }
   228  
   229  func (sv *SignVerifier) verifyTxsInplace(txlist *types.TxList) (bool, []error) {
   230  	txs := txlist.GetTxs()
   231  	txLen := len(txs)
   232  	errs := make([]error, txLen, txLen)
   233  	failed := false
   234  	var hit bool
   235  
   236  	logger.Debug().Int("txlen", txLen).Msg("verify tx inplace start")
   237  
   238  	for i, tx := range txs {
   239  		hit, errs[i] = sv.verifyTx(sv.comm, tx, false)
   240  		failed = true
   241  
   242  		if hit {
   243  			sv.totalHit++
   244  		}
   245  	}
   246  
   247  	logger.Debug().Int("totalhit", sv.totalHit).Msg("verify tx inplace done")
   248  	return failed, errs
   249  }
   250  
   251  func (sv *SignVerifier) SetSkipMempool(val bool) {
   252  	sv.skipMempool = val
   253  }