github.com/linapex/ethereum-go-chinese@v0.0.0-20190316121929-f8b7a73c3fa1/light/txpool.go (about)

     1  
     2  //<developer>
     3  //    <name>linapex 曹一峰</name>
     4  //    <email>linapex@163.com</email>
     5  //    <wx>superexc</wx>
     6  //    <qqgroup>128148617</qqgroup>
     7  //    <url>https://jsq.ink</url>
     8  //    <role>pku engineer</role>
     9  //    <date>2019-03-16 19:16:39</date>
    10  //</624450096854994944>
    11  
    12  
    13  package light
    14  
    15  import (
    16  	"context"
    17  	"fmt"
    18  	"sync"
    19  	"time"
    20  
    21  	"github.com/ethereum/go-ethereum/common"
    22  	"github.com/ethereum/go-ethereum/core"
    23  	"github.com/ethereum/go-ethereum/core/rawdb"
    24  	"github.com/ethereum/go-ethereum/core/state"
    25  	"github.com/ethereum/go-ethereum/core/types"
    26  	"github.com/ethereum/go-ethereum/ethdb"
    27  	"github.com/ethereum/go-ethereum/event"
    28  	"github.com/ethereum/go-ethereum/log"
    29  	"github.com/ethereum/go-ethereum/params"
    30  	"github.com/ethereum/go-ethereum/rlp"
    31  )
    32  
    33  const (
    34  //ChainHeadChansize是侦听ChainHeadEvent的通道的大小。
    35  	chainHeadChanSize = 10
    36  )
    37  
    38  //TxPermanent是挖掘事务之后挖掘的块数
    39  //被认为是永久的,不需要回滚
    40  var txPermanent = uint64(500)
    41  
    42  //TxPool为轻型客户机实现事务池,从而跟踪
    43  //本地创建的事务的状态,检测是否包含这些事务
    44  //在一个块(矿)或回卷。自从我们
    45  //始终以相同的顺序接收所有本地签名的事务
    46  //创建。
    47  type TxPool struct {
    48  	config       *params.ChainConfig
    49  	signer       types.Signer
    50  	quit         chan bool
    51  	txFeed       event.Feed
    52  	scope        event.SubscriptionScope
    53  	chainHeadCh  chan core.ChainHeadEvent
    54  	chainHeadSub event.Subscription
    55  	mu           sync.RWMutex
    56  	chain        *LightChain
    57  	odr          OdrBackend
    58  	chainDb      ethdb.Database
    59  	relay        TxRelayBackend
    60  	head         common.Hash
    61  nonce        map[common.Address]uint64            //“待定”
    62  pending      map[common.Hash]*types.Transaction   //按Tx哈希排序的挂起事务
    63  mined        map[common.Hash][]*types.Transaction //按块哈希挖掘的事务
    64  clearIdx     uint64                               //可包含已开采Tx信息的最早区块编号
    65  
    66  	homestead bool
    67  }
    68  
    69  //txtrelaybackend提供了一个接口,用于转发Transacion的机制
    70  //到ETH网络。函数的实现应该是非阻塞的。
    71  //
    72  //发送指示后端转发新事务
    73  //new head在tx池处理后通知后端有关新头的信息,
    74  //包括自上次事件以来的已挖掘和回滚事务
    75  //Discard通知后端应该丢弃的事务
    76  //因为它们被重新发送或被挖掘所取代
    77  //很久以前,不需要回滚
    78  type TxRelayBackend interface {
    79  	Send(txs types.Transactions)
    80  	NewHead(head common.Hash, mined []common.Hash, rollback []common.Hash)
    81  	Discard(hashes []common.Hash)
    82  }
    83  
    84  //newtxpool创建新的轻型事务池
    85  func NewTxPool(config *params.ChainConfig, chain *LightChain, relay TxRelayBackend) *TxPool {
    86  	pool := &TxPool{
    87  		config:      config,
    88  		signer:      types.NewEIP155Signer(config.ChainID),
    89  		nonce:       make(map[common.Address]uint64),
    90  		pending:     make(map[common.Hash]*types.Transaction),
    91  		mined:       make(map[common.Hash][]*types.Transaction),
    92  		quit:        make(chan bool),
    93  		chainHeadCh: make(chan core.ChainHeadEvent, chainHeadChanSize),
    94  		chain:       chain,
    95  		relay:       relay,
    96  		odr:         chain.Odr(),
    97  		chainDb:     chain.Odr().Database(),
    98  		head:        chain.CurrentHeader().Hash(),
    99  		clearIdx:    chain.CurrentHeader().Number.Uint64(),
   100  	}
   101  //从区块链订阅事件
   102  	pool.chainHeadSub = pool.chain.SubscribeChainHeadEvent(pool.chainHeadCh)
   103  	go pool.eventLoop()
   104  
   105  	return pool
   106  }
   107  
   108  //currentState返回当前头段的灯状态
   109  func (pool *TxPool) currentState(ctx context.Context) *state.StateDB {
   110  	return NewState(ctx, pool.chain.CurrentHeader(), pool.odr)
   111  }
   112  
   113  //getnonce返回给定地址的“挂起”nonce。它总是询问
   114  //也属于最新标题的nonce,以便检测是否有其他标题
   115  //使用相同密钥的客户端发送了一个事务。
   116  func (pool *TxPool) GetNonce(ctx context.Context, addr common.Address) (uint64, error) {
   117  	state := pool.currentState(ctx)
   118  	nonce := state.GetNonce(addr)
   119  	if state.Error() != nil {
   120  		return 0, state.Error()
   121  	}
   122  	sn, ok := pool.nonce[addr]
   123  	if ok && sn > nonce {
   124  		nonce = sn
   125  	}
   126  	if !ok || sn < nonce {
   127  		pool.nonce[addr] = nonce
   128  	}
   129  	return nonce, nil
   130  }
   131  
   132  //TxStateChanges存储的挂起/挖掘状态之间的最近更改
   133  //交易。“真”表示已开采,“假”表示已回退,“不进入”表示无变化。
   134  type txStateChanges map[common.Hash]bool
   135  
   136  //setState将Tx的状态设置为“最近开采”或“最近回滚”
   137  func (txc txStateChanges) setState(txHash common.Hash, mined bool) {
   138  	val, ent := txc[txHash]
   139  	if ent && (val != mined) {
   140  		delete(txc, txHash)
   141  	} else {
   142  		txc[txHash] = mined
   143  	}
   144  }
   145  
   146  //GetLists创建挖掘和回滚的Tx哈希列表
   147  func (txc txStateChanges) getLists() (mined []common.Hash, rollback []common.Hash) {
   148  	for hash, val := range txc {
   149  		if val {
   150  			mined = append(mined, hash)
   151  		} else {
   152  			rollback = append(rollback, hash)
   153  		}
   154  	}
   155  	return
   156  }
   157  
   158  //checkminedTxs为当前挂起的事务检查新添加的块
   159  //必要时标记为已开采。它还将块位置存储在数据库中
   160  //并将它们添加到接收到的txtStateChanges映射中。
   161  func (pool *TxPool) checkMinedTxs(ctx context.Context, hash common.Hash, number uint64, txc txStateChanges) error {
   162  //如果没有交易悬而未决,我们什么都不在乎
   163  	if len(pool.pending) == 0 {
   164  		return nil
   165  	}
   166  	block, err := GetBlock(ctx, pool.odr, hash, number)
   167  	if err != nil {
   168  		return err
   169  	}
   170  //收集在此块中挖掘的所有本地事务
   171  	list := pool.mined[hash]
   172  	for _, tx := range block.Transactions() {
   173  		if _, ok := pool.pending[tx.Hash()]; ok {
   174  			list = append(list, tx)
   175  		}
   176  	}
   177  //如果挖掘了某些事务,请将所需数据写入磁盘并更新
   178  	if list != nil {
   179  //检索属于此块的所有收据并写入循环表
   180  if _, err := GetBlockReceipts(ctx, pool.odr, hash, number); err != nil { //ODR缓存,忽略结果
   181  			return err
   182  		}
   183  		rawdb.WriteTxLookupEntries(pool.chainDb, block)
   184  
   185  //更新事务池的状态
   186  		for _, tx := range list {
   187  			delete(pool.pending, tx.Hash())
   188  			txc.setState(tx.Hash(), true)
   189  		}
   190  		pool.mined[hash] = list
   191  	}
   192  	return nil
   193  }
   194  
   195  //RollbackTXS标记最近回滚块中包含的事务
   196  //像卷回一样。它还删除任何位置查找条目。
   197  func (pool *TxPool) rollbackTxs(hash common.Hash, txc txStateChanges) {
   198  	batch := pool.chainDb.NewBatch()
   199  	if list, ok := pool.mined[hash]; ok {
   200  		for _, tx := range list {
   201  			txHash := tx.Hash()
   202  			rawdb.DeleteTxLookupEntry(batch, txHash)
   203  			pool.pending[txHash] = tx
   204  			txc.setState(txHash, false)
   205  		}
   206  		delete(pool.mined, hash)
   207  	}
   208  	batch.Write()
   209  }
   210  
   211  //重新定位new head设置新的head header,处理(必要时回滚)
   212  //从上一个已知头开始的块,并返回包含
   213  //最近挖掘和回滚的事务哈希。如果出现错误(上下文
   214  //超时)在检查新块时发生,它离开本地已知的头
   215  //在最近选中的块上,仍然返回有效的txtStateChanges,使其
   216  //可以在下一个链头事件中继续检查丢失的块
   217  func (pool *TxPool) reorgOnNewHead(ctx context.Context, newHeader *types.Header) (txStateChanges, error) {
   218  	txc := make(txStateChanges)
   219  	oldh := pool.chain.GetHeaderByHash(pool.head)
   220  	newh := newHeader
   221  //查找公共祖先,创建回滚和新块哈希的列表
   222  	var oldHashes, newHashes []common.Hash
   223  	for oldh.Hash() != newh.Hash() {
   224  		if oldh.Number.Uint64() >= newh.Number.Uint64() {
   225  			oldHashes = append(oldHashes, oldh.Hash())
   226  			oldh = pool.chain.GetHeader(oldh.ParentHash, oldh.Number.Uint64()-1)
   227  		}
   228  		if oldh.Number.Uint64() < newh.Number.Uint64() {
   229  			newHashes = append(newHashes, newh.Hash())
   230  			newh = pool.chain.GetHeader(newh.ParentHash, newh.Number.Uint64()-1)
   231  			if newh == nil {
   232  //当CHT同步时发生,无需执行任何操作
   233  				newh = oldh
   234  			}
   235  		}
   236  	}
   237  	if oldh.Number.Uint64() < pool.clearIdx {
   238  		pool.clearIdx = oldh.Number.Uint64()
   239  	}
   240  //回滚旧块
   241  	for _, hash := range oldHashes {
   242  		pool.rollbackTxs(hash, txc)
   243  	}
   244  	pool.head = oldh.Hash()
   245  //检查新块的挖掘Txs(数组顺序相反)
   246  	for i := len(newHashes) - 1; i >= 0; i-- {
   247  		hash := newHashes[i]
   248  		if err := pool.checkMinedTxs(ctx, hash, newHeader.Number.Uint64()-uint64(i), txc); err != nil {
   249  			return txc, err
   250  		}
   251  		pool.head = hash
   252  	}
   253  
   254  //清除旧块的旧挖掘Tx条目
   255  	if idx := newHeader.Number.Uint64(); idx > pool.clearIdx+txPermanent {
   256  		idx2 := idx - txPermanent
   257  		if len(pool.mined) > 0 {
   258  			for i := pool.clearIdx; i < idx2; i++ {
   259  				hash := rawdb.ReadCanonicalHash(pool.chainDb, i)
   260  				if list, ok := pool.mined[hash]; ok {
   261  					hashes := make([]common.Hash, len(list))
   262  					for i, tx := range list {
   263  						hashes[i] = tx.Hash()
   264  					}
   265  					pool.relay.Discard(hashes)
   266  					delete(pool.mined, hash)
   267  				}
   268  			}
   269  		}
   270  		pool.clearIdx = idx2
   271  	}
   272  
   273  	return txc, nil
   274  }
   275  
   276  //BlockCheckTimeout是检查挖掘的新块的时间限制
   277  //交易。如果超时,则在下一个链头事件时检查恢复。
   278  const blockCheckTimeout = time.Second * 3
   279  
   280  //EventLoop处理链头事件并通知Tx中继后端
   281  //关于新头哈希和Tx状态更改
   282  func (pool *TxPool) eventLoop() {
   283  	for {
   284  		select {
   285  		case ev := <-pool.chainHeadCh:
   286  			pool.setNewHead(ev.Block.Header())
   287  //为了避免锁被霸占,这部分会
   288  //替换为后续的pr。
   289  			time.Sleep(time.Millisecond)
   290  
   291  //系统停止
   292  		case <-pool.chainHeadSub.Err():
   293  			return
   294  		}
   295  	}
   296  }
   297  
   298  func (pool *TxPool) setNewHead(head *types.Header) {
   299  	pool.mu.Lock()
   300  	defer pool.mu.Unlock()
   301  
   302  	ctx, cancel := context.WithTimeout(context.Background(), blockCheckTimeout)
   303  	defer cancel()
   304  
   305  	txc, _ := pool.reorgOnNewHead(ctx, head)
   306  	m, r := txc.getLists()
   307  	pool.relay.NewHead(pool.head, m, r)
   308  	pool.homestead = pool.config.IsHomestead(head.Number)
   309  	pool.signer = types.MakeSigner(pool.config, head.Number)
   310  }
   311  
   312  //停止停止轻型事务处理池
   313  func (pool *TxPool) Stop() {
   314  //取消订阅从txpool注册的所有订阅
   315  	pool.scope.Close()
   316  //取消订阅从区块链注册的订阅
   317  	pool.chainHeadSub.Unsubscribe()
   318  	close(pool.quit)
   319  	log.Info("Transaction pool stopped")
   320  }
   321  
   322  //subscripeWtxEvent注册core.newtxSevent和的订阅
   323  //开始向给定通道发送事件。
   324  func (pool *TxPool) SubscribeNewTxsEvent(ch chan<- core.NewTxsEvent) event.Subscription {
   325  	return pool.scope.Track(pool.txFeed.Subscribe(ch))
   326  }
   327  
   328  //stats返回当前挂起(本地创建)的事务数
   329  func (pool *TxPool) Stats() (pending int) {
   330  	pool.mu.RLock()
   331  	defer pool.mu.RUnlock()
   332  
   333  	pending = len(pool.pending)
   334  	return
   335  }
   336  
   337  //validatetx根据共识规则检查交易是否有效。
   338  func (pool *TxPool) validateTx(ctx context.Context, tx *types.Transaction) error {
   339  //验证发送器
   340  	var (
   341  		from common.Address
   342  		err  error
   343  	)
   344  
   345  //验证事务发送方及其SIG。投掷
   346  //如果“发件人”字段无效。
   347  	if from, err = types.Sender(pool.signer, tx); err != nil {
   348  		return core.ErrInvalidSender
   349  	}
   350  //最后但不是最不重要的检查非关键错误
   351  	currentState := pool.currentState(ctx)
   352  	if n := currentState.GetNonce(from); n > tx.Nonce() {
   353  		return core.ErrNonceTooLow
   354  	}
   355  
   356  //检查交易不超过当前
   357  //阻塞限制气体。
   358  	header := pool.chain.GetHeaderByHash(pool.head)
   359  	if header.GasLimit < tx.Gas() {
   360  		return core.ErrGasLimit
   361  	}
   362  
   363  //交易记录不能为负数。这可能永远不会发生
   364  //使用RLP解码的事务,但如果创建
   365  //例如,使用RPC的事务。
   366  	if tx.Value().Sign() < 0 {
   367  		return core.ErrNegativeValue
   368  	}
   369  
   370  //交易人应该有足够的资金来支付费用。
   371  //成本==V+gp*gl
   372  	if b := currentState.GetBalance(from); b.Cmp(tx.Cost()) < 0 {
   373  		return core.ErrInsufficientFunds
   374  	}
   375  
   376  //应提供足够的固有气体
   377  	gas, err := core.IntrinsicGas(tx.Data(), tx.To() == nil, pool.homestead)
   378  	if err != nil {
   379  		return err
   380  	}
   381  	if tx.Gas() < gas {
   382  		return core.ErrIntrinsicGas
   383  	}
   384  	return currentState.Error()
   385  }
   386  
   387  //添加将验证新事务,并将其状态设置为挂起(如果可处理)。
   388  //如果需要,它还会更新本地存储的nonce。
   389  func (self *TxPool) add(ctx context.Context, tx *types.Transaction) error {
   390  	hash := tx.Hash()
   391  
   392  	if self.pending[hash] != nil {
   393  		return fmt.Errorf("Known transaction (%x)", hash[:4])
   394  	}
   395  	err := self.validateTx(ctx, tx)
   396  	if err != nil {
   397  		return err
   398  	}
   399  
   400  	if _, ok := self.pending[hash]; !ok {
   401  		self.pending[hash] = tx
   402  
   403  		nonce := tx.Nonce() + 1
   404  
   405  		addr, _ := types.Sender(self.signer, tx)
   406  		if nonce > self.nonce[addr] {
   407  			self.nonce[addr] = nonce
   408  		}
   409  
   410  //通知订户。此事件在Goroutine中发布
   411  //因为在“删除事务”后的某个位置
   412  //调用,然后等待全局Tx池锁定和死锁。
   413  		go self.txFeed.Send(core.NewTxsEvent{Txs: types.Transactions{tx}})
   414  	}
   415  
   416  //如果设置了足够低的级别,则打印日志消息
   417  	log.Debug("Pooled new transaction", "hash", hash, "from", log.Lazy{Fn: func() common.Address { from, _ := types.Sender(self.signer, tx); return from }}, "to", tx.To())
   418  	return nil
   419  }
   420  
   421  //添加将事务添加到池中(如果有效)并将其传递到Tx中继
   422  //后端
   423  func (self *TxPool) Add(ctx context.Context, tx *types.Transaction) error {
   424  	self.mu.Lock()
   425  	defer self.mu.Unlock()
   426  
   427  	data, err := rlp.EncodeToBytes(tx)
   428  	if err != nil {
   429  		return err
   430  	}
   431  
   432  	if err := self.add(ctx, tx); err != nil {
   433  		return err
   434  	}
   435  //fmt.println(“发送”,tx.hash())
   436  	self.relay.Send(types.Transactions{tx})
   437  
   438  	self.chainDb.Put(tx.Hash().Bytes(), data)
   439  	return nil
   440  }
   441  
   442  //addTransactions将所有有效事务添加到池中,并将它们传递给
   443  //Tx中继后端
   444  func (self *TxPool) AddBatch(ctx context.Context, txs []*types.Transaction) {
   445  	self.mu.Lock()
   446  	defer self.mu.Unlock()
   447  	var sendTx types.Transactions
   448  
   449  	for _, tx := range txs {
   450  		if err := self.add(ctx, tx); err == nil {
   451  			sendTx = append(sendTx, tx)
   452  		}
   453  	}
   454  	if len(sendTx) > 0 {
   455  		self.relay.Send(sendTx)
   456  	}
   457  }
   458  
   459  //GetTransaction返回包含在池中的事务
   460  //否则为零。
   461  func (tp *TxPool) GetTransaction(hash common.Hash) *types.Transaction {
   462  //先检查TXS
   463  	if tx, ok := tp.pending[hash]; ok {
   464  		return tx
   465  	}
   466  	return nil
   467  }
   468  
   469  //GetTransactions返回所有当前可处理的事务。
   470  //调用程序可以修改返回的切片。
   471  func (self *TxPool) GetTransactions() (txs types.Transactions, err error) {
   472  	self.mu.RLock()
   473  	defer self.mu.RUnlock()
   474  
   475  	txs = make(types.Transactions, len(self.pending))
   476  	i := 0
   477  	for _, tx := range self.pending {
   478  		txs[i] = tx
   479  		i++
   480  	}
   481  	return txs, nil
   482  }
   483  
   484  //Content检索事务池的数据内容,并返回
   485  //挂起和排队的事务,按帐户和nonce分组。
   486  func (self *TxPool) Content() (map[common.Address]types.Transactions, map[common.Address]types.Transactions) {
   487  	self.mu.RLock()
   488  	defer self.mu.RUnlock()
   489  
   490  //检索所有挂起的事务,并按帐户和当前排序
   491  	pending := make(map[common.Address]types.Transactions)
   492  	for _, tx := range self.pending {
   493  		account, _ := types.Sender(self.signer, tx)
   494  		pending[account] = append(pending[account], tx)
   495  	}
   496  //光池中没有排队的事务,只返回一个空映射
   497  	queued := make(map[common.Address]types.Transactions)
   498  	return pending, queued
   499  }
   500  
   501  //removeTransactions从池中删除所有给定的事务。
   502  func (self *TxPool) RemoveTransactions(txs types.Transactions) {
   503  	self.mu.Lock()
   504  	defer self.mu.Unlock()
   505  
   506  	var hashes []common.Hash
   507  	batch := self.chainDb.NewBatch()
   508  	for _, tx := range txs {
   509  		hash := tx.Hash()
   510  		delete(self.pending, hash)
   511  		batch.Delete(hash.Bytes())
   512  		hashes = append(hashes, hash)
   513  	}
   514  	batch.Write()
   515  	self.relay.Discard(hashes)
   516  }
   517  
   518  //removetx从池中删除具有给定哈希的事务。
   519  func (pool *TxPool) RemoveTx(hash common.Hash) {
   520  	pool.mu.Lock()
   521  	defer pool.mu.Unlock()
   522  //从挂起池中删除
   523  	delete(pool.pending, hash)
   524  	pool.chainDb.Delete(hash[:])
   525  	pool.relay.Discard([]common.Hash{hash})
   526  }
   527