比特币全节点Go语言实现BTCD之双重支付代码解析

双重支付是成功支付了1次以上的情况。比特币通过对添加到区块中的每笔交易进行验证来防止双重支付,确保交易的输入没有被支付过

区块中的交易采用的是UTXO模型,每个节点进行打包的时候都会对交易进行验证,交易的输入是否在之前区块存在过,每个节点收到区块时也会对区块及区块中的交易进行合法性校验。

OK,看代码:

func btcdMain(serverChan chan<- *server) error {

主入口

func (s *server) Start() {

启动server对象

go s.peerHandler()

启动节点操作

s.syncManager.Start()

启动同步管理器,负责同步区块、交易等

go s.connManager.Start()
启动连接管理器,负责建立节点的连接,并监听消息

启动syncManager,会调用

go sm.blockHandler()

此方法负责同步各种消息的处理,代码:

func (sm *SyncManager) blockHandler() {
out:
   for {
      select {
      case m := <-sm.msgChan:
         switch msg := m.(type) {
         case *newPeerMsg:
            sm.handleNewPeerMsg(msg.peer)

         case *txMsg:
            sm.handleTxMsg(msg)
            msg.reply <- struct{}{}

         case *blockMsg:
            sm.handleBlockMsg(msg)
            msg.reply <- struct{}{}

         case *invMsg:
            sm.handleInvMsg(msg)

         case *headersMsg:
            sm.handleHeadersMsg(msg)

         case *donePeerMsg:
            sm.handleDonePeerMsg(msg.peer)

         case getSyncPeerMsg:
            var peerID int32
            if sm.syncPeer != nil {
               peerID = sm.syncPeer.ID()
            }
            msg.reply <- peerID

         case processBlockMsg:
            _, isOrphan, err := sm.chain.ProcessBlock(
               msg.block, msg.flags)
            if err != nil {
               msg.reply <- processBlockResponse{
                  isOrphan: false,
                  err:      err,
               }
            }

            msg.reply <- processBlockResponse{
               isOrphan: isOrphan,
               err:      nil,
            }

         case isCurrentMsg:
            msg.reply <- sm.current()

         case pauseMsg:
            // Wait until the sender unpauses the manager.
            <-msg.unpause

         default:
            log.Warnf("Invalid message type in block "+
               "handler: %T", msg)
         }

      case <-sm.quit:
         break out
      }
   }

   sm.wg.Done()
   log.Trace("Block handler done")
}
sm.handleTxMsg(msg)

即是处理交易信息,主要代码逻辑:

func (sm *SyncManager) handleTxMsg(tmsg *txMsg) {
   peer := tmsg.peer
   state, exists := sm.peerStates[peer]
   txHash := tmsg.tx.Hash()

   acceptedTxs, err := sm.txMemPool.ProcessTransaction(tmsg.tx,
      true, true, mempool.Tag(peer.ID()))

   delete(state.requestedTxns, *txHash)
   delete(sm.requestedTxns, *txHash)

   sm.peerNotifier.AnnounceNewTransactions(acceptedTxs)
}
acceptedTxs, err := sm.txMemPool.ProcessTransaction(tmsg.tx, true, true, mempool.Tag(peer.ID()))

这一行代码交给交易内存池处理消息带的交易。

func (mp *TxPool) ProcessTransaction(tx *btcutil.Tx, allowOrphan, rateLimit bool, tag Tag) ([]*TxDesc, error) {

   // Potentially accept the transaction to the memory pool.
   missingParents, txD, err := mp.maybeAcceptTransaction(tx, true, rateLimit, true)
   if err != nil {
      return nil, err
   }

   if len(missingParents) == 0 {
      newTxs := mp.processOrphans(tx)
      acceptedTxs := make([]*TxDesc, len(newTxs)+1)

      // Add the parent transaction first so remote nodes
      // do not add orphans.
      acceptedTxs[0] = txD
      copy(acceptedTxs[1:], newTxs)

      return acceptedTxs, nil
   }

   // The transaction is an orphan (has inputs missing).  Reject
   // it if the flag to allow orphans is not set.
   if !allowOrphan {
      str := fmt.Sprintf("orphan transaction %v references "+
         "outputs of unknown or fully-spent "+
         "transaction %v", tx.Hash(), missingParents[0])
      return nil, txRuleError(wire.RejectDuplicate, str)
   }

   // Potentially add the orphan transaction to the orphan pool.
   err = mp.maybeAddOrphan(tx, tag)
   return nil, err
}
missingParents, txD, err := mp.maybeAcceptTransaction(tx, true, rateLimit, true)

maybeAcceptTransaction方法主要负责验证交易的合法性,处理逻辑很多,缩减后主要逻辑:

func (mp *TxPool) maybeAcceptTransaction(tx *btcutil.Tx, isNew, rateLimit, rejectDupOrphans bool) ([]*chainhash.Hash, *TxDesc, error) {
   txHash := tx.Hash()

   if tx.MsgTx().HasWitness() { // 是否是见证隔离交易
      segwitActive, err := mp.cfg.IsDeploymentActive(chaincfg.DeploymentSegwit)
      if err != nil {
         return nil, nil, err
      }

      if !segwitActive { //若不支持见证隔离则返回
         str := fmt.Sprintf("transaction %v has witness data, "+
            "but segwit isn't active yet", txHash)
         return nil, nil, txRuleError(wire.RejectNonstandard, str)
      }
   }

   if mp.isTransactionInPool(txHash) || (rejectDupOrphans && mp.isOrphanInPool(txHash)) { //是否已存在交易池中
      str := fmt.Sprintf("already have transaction %v", txHash)
      return nil, nil, txRuleError(wire.RejectDuplicate, str)
   }

   err := blockchain.CheckTransactionSanity(tx) //检查交易合法性,代码下面会详细分析
   if err != nil {
      if cerr, ok := err.(blockchain.RuleError); ok {
         return nil, nil, chainRuleError(cerr)
      }
      return nil, nil, err
   }

   if blockchain.IsCoinBase(tx) { //不能是coinbase交易,coinbase交易单独处理
      str := fmt.Sprintf("transaction %v is an individual coinbase",
         txHash)
      return nil, nil, txRuleError(wire.RejectInvalid, str)
   }

   bestHeight := mp.cfg.BestHeight()
   nextBlockHeight := bestHeight + 1

   medianTimePast := mp.cfg.MedianTimePast()

   if !mp.cfg.Policy.AcceptNonStd { //如不接受非标准交易,则需要对交易进行标准化验证
      err = checkTransactionStandard(tx, nextBlockHeight,
         medianTimePast, mp.cfg.Policy.MinRelayTxFee,
         mp.cfg.Policy.MaxTxVersion)
   }

   err = mp.checkPoolDoubleSpend(tx) //在交易池中检查输入是否已经被花费,代码在下方
   if err != nil {
      return nil, nil, err
   }

   utxoView, err := mp.fetchInputUtxos(tx) //取这个交易相关的区块上的所有utxo

   prevOut := wire.OutPoint{Hash: *txHash}
   for txOutIdx := range tx.MsgTx().TxOut {
      prevOut.Index = uint32(txOutIdx)
      entry := utxoView.LookupEntry(prevOut)
      if entry != nil && !entry.IsSpent() { //检查是否已经被花费
         return nil, nil, txRuleError(wire.RejectDuplicate,
            "transaction already exists")
      }
      utxoView.RemoveEntry(prevOut)
   }

   // Transaction is an orphan
   var missingParents []*chainhash.Hash
   for outpoint, entry := range utxoView.Entries() {
      if entry == nil || entry.IsSpent() {
         // Must make a copy of the hash here since the iterator
         // is replaced and taking its address directly would
         // result in all of the entries pointing to the same
         // memory location and thus all be the final hash.
         hashCopy := outpoint.Hash
         missingParents = append(missingParents, &hashCopy)
      }
   }
   if len(missingParents) > 0 {
      return missingParents, nil, nil //孤立交易,父交易还不存在
   }

   sequenceLock, err := mp.cfg.CalcSequenceLock(tx, utxoView) //计算交易锁定时间
   if err != nil {
      if cerr, ok := err.(blockchain.RuleError); ok {
         return nil, nil, chainRuleError(cerr)
      }
      return nil, nil, err
   }
   if !blockchain.SequenceLockActive(sequenceLock, nextBlockHeight,
      medianTimePast) { //交易是否还需要等待
      return nil, nil, txRuleError(wire.RejectNonstandard,
         "transaction's sequence locks on inputs not met")
   }

   txFee, err := blockchain.CheckTransactionInputs(tx, nextBlockHeight, utxoView, mp.cfg.ChainParams)

   if !mp.cfg.Policy.AcceptNonStd {
      err := checkInputsStandard(tx, utxoView) //检查输入标准化
   }

   sigOpCost, err := blockchain.GetSigOpCost(tx, false, utxoView, true, true)
   if err != nil {
      if cerr, ok := err.(blockchain.RuleError); ok {
         return nil, nil, chainRuleError(cerr)
      }
      return nil, nil, err
   }
   if sigOpCost > mp.cfg.Policy.MaxSigOpCostPerTx { //检查多签数量
      str := fmt.Sprintf("transaction %v sigop cost is too high: %d > %d",
         txHash, sigOpCost, mp.cfg.Policy.MaxSigOpCostPerTx)
      return nil, nil, txRuleError(wire.RejectNonstandard, str)
   }

   serializedSize := GetTxVirtualSize(tx)
   minFee := calcMinRequiredTxRelayFee(serializedSize,
      mp.cfg.Policy.MinRelayTxFee)
   if serializedSize >= (DefaultBlockPrioritySize-1000) && txFee < minFee { //检查交易大小及交易费用是否优先处理
      str := fmt.Sprintf("transaction %v has %d fees which is under "+
         "the required amount of %d", txHash, txFee,
         minFee)
      return nil, nil, txRuleError(wire.RejectInsufficientFee, str)
   }

   if isNew && !mp.cfg.Policy.DisableRelayPriority && txFee < minFee {
      currentPriority := mining.CalcPriority(tx.MsgTx(), utxoView, nextBlockHeight)
      if currentPriority <= mining.MinHighPriority {
         str := fmt.Sprintf("transaction %v has insufficient "+ "priority (%g <= %g)", txHash,
            currentPriority, mining.MinHighPriority)
         return nil, nil, txRuleError(wire.RejectInsufficientFee, str)
      }
   }

   if rateLimit && txFee < minFee {
      nowUnix := time.Now().Unix()
      // Decay passed data with an exponentially decaying ~10 minute
      // window - matches bitcoind handling.
      mp.pennyTotal *= math.Pow(1.0-1.0/600.0,
         float64(nowUnix-mp.lastPennyUnix))
      mp.lastPennyUnix = nowUnix

      // Are we still over the limit?
      if mp.pennyTotal >= mp.cfg.Policy.FreeTxRelayLimit*10*1000 {
         str := fmt.Sprintf("transaction %v has been rejected "+
            "by the rate limiter due to low fees", txHash)
         return nil, nil, txRuleError(wire.RejectInsufficientFee, str)
      }
      oldTotal := mp.pennyTotal

      mp.pennyTotal += float64(serializedSize)
      log.Tracef("rate limit: curTotal %v, nextTotal: %v, "+
         "limit %v", oldTotal, mp.pennyTotal,
         mp.cfg.Policy.FreeTxRelayLimit*10*1000)
   }

   err = blockchain.ValidateTransactionScripts(tx, utxoView, txscript.StandardVerifyFlags, mp.cfg.SigCache,
      mp.cfg.HashCache) //验证交易签名脚本
   if err != nil {
      if cerr, ok := err.(blockchain.RuleError); ok {
         return nil, nil, chainRuleError(cerr)
      }
      return nil, nil, err
   }

   txD := mp.addTransaction(utxoView, tx, bestHeight, txFee) //合格交易加入到交易池

   log.Debugf("Accepted transaction %v (pool size: %v)", txHash, len(mp.pool))

   return nil, txD, nil
}
func CheckTransactionSanity(tx *btcutil.Tx) error { //检查交易的合法性
   msgTx := tx.MsgTx()
   if len(msgTx.TxIn) == 0 { //需要有输入
      return ruleError(ErrNoTxInputs, "transaction has no inputs")
   }

   // A transaction must have at least one output.
   if len(msgTx.TxOut) == 0 { //至少一个输出
      return ruleError(ErrNoTxOutputs, "transaction has no outputs")
   }

   serializedTxSize := tx.MsgTx().SerializeSizeStripped()
   if serializedTxSize > MaxBlockBaseSize { //交易不能超过允许的最大块大小
      str := fmt.Sprintf("serialized transaction is too big - got "+
         "%d, max %d", serializedTxSize, MaxBlockBaseSize)
      return ruleError(ErrTxTooBig, str)
   }

   var totalSatoshi int64
   for _, txOut := range msgTx.TxOut {
      satoshi := txOut.Value
      if satoshi < 0 { //输出不能为负
         str := fmt.Sprintf("transaction output has negative "+
            "value of %v", satoshi)
         return ruleError(ErrBadTxOutValue, str)
      }
      if satoshi > btcutil.MaxSatoshi { //不能超过最大值
         str := fmt.Sprintf("transaction output value of %v is "+
            "higher than max allowed value of %v", satoshi,
            btcutil.MaxSatoshi)
         return ruleError(ErrBadTxOutValue, str)
      }

   }

   // Check for duplicate transaction inputs.
   existingTxOut := make(map[wire.OutPoint]struct{}) //输入不能重复
   for _, txIn := range msgTx.TxIn {
      if _, exists := existingTxOut[txIn.PreviousOutPoint]; exists {
         return ruleError(ErrDuplicateTxInputs, "transaction "+ "contains duplicate inputs")
      }
      existingTxOut[txIn.PreviousOutPoint] = struct{}{}
   }

   // Coinbase script length must be between min and max length.
   if IsCoinBase(tx) { //coinbase交易
      slen := len(msgTx.TxIn[0].SignatureScript)
      if slen < MinCoinbaseScriptLen || slen > MaxCoinbaseScriptLen { //验证脚本长度
         str := fmt.Sprintf("coinbase transaction script length "+
            "of %d is out of range (min: %d, max: %d)",
            slen, MinCoinbaseScriptLen, MaxCoinbaseScriptLen)
         return ruleError(ErrBadCoinbaseScriptLen, str)
      }
   } else {
      for _, txIn := range msgTx.TxIn {
         if isNullOutpoint(&txIn.PreviousOutPoint) {
            return ruleError(ErrBadTxInput, "transaction "+
               "input refers to previous output that "+
               "is null")
         }
      }
   }

   return nil
}
func (mp *TxPool) checkPoolDoubleSpend(tx *btcutil.Tx) error {
   for _, txIn := range tx.MsgTx().TxIn {
      if txR, exists := mp.outpoints[txIn.PreviousOutPoint]; exists {
         str := fmt.Sprintf("output %v already spent by "+ "transaction %v in the memory pool",
            txIn.PreviousOutPoint, txR.Hash())
         return txRuleError(wire.RejectDuplicate, str)
      }
   }

   return nil
}


本文作者:architect.bian,欢迎收藏,转载请保留原文地址并保留版权声明!谢谢~
还没完!往下看!!!














猜你喜欢

转载自blog.csdn.net/vohyeah/article/details/80704066