소스 코드에서 이더리움 Replacementtransaction underpriced 이상 분석

3518 단어 이태방
프로젝트에서 이더리움 이체 기능을 사용했는데 어느 날 이더리움 네트워크가 막혀서 이체가 Replacementtransaction underpriced 이상을 보고했습니다. 이 이상 키워드에 따라 이더리움 원본 코드를 검색한 결과 여기에서 오류가 발생했습니다.
func (pool *TxPool) add(tx *types.Transaction, local bool) (replaced bool, err error) {
//---------       ------------------------
   from, _ := types.Sender(pool.signer, tx) // already validated
   if list := pool.pending[from]; list != nil && list.Overlaps(tx) {
      // Nonce already pending, check if required price bump is met
      inserted, old := list.Add(tx, pool.config.PriceBump)
      if !inserted {
         pendingDiscardMeter.Mark(1)
         return false, ErrReplaceUnderpriced          //      
      }
      // New transaction is better, replace old one
      if old != nil {
         pool.all.Remove(old.Hash())
         pool.priced.Removed(1)
         pendingReplaceMeter.Mark(1)
      }
//------------       -------------------

   }
}
func (l *txList) Add(tx *types.Transaction, priceBump uint64) (bool, *types.Transaction) {
   // If there's an older better transaction, abort
   old := l.txs.Get(tx.Nonce())
   if old != nil {
      threshold := new(big.Int).Div(new(big.Int).Mul(old.GasPrice(), big.NewInt(100+int64(priceBump))), big.NewInt(100))
      // Have to ensure that the new gas price is higher than the old gas
      // price as well as checking the percentage threshold to ensure that
      // this is accurate for low (Wei-level) gas price replacements
//pending       nonce    ,        nonce           threshold 
      if old.GasPrice().Cmp(tx.GasPrice()) >= 0 || threshold.Cmp(tx.GasPrice()) > 0 {
         return false, nil
      }
   }
   // Otherwise overwrite the old transaction with the current one
   l.txs.Put(tx)
   if cost := tx.Cost(); l.costcap.Cmp(cost) < 0 {
      l.costcap = cost
   }
   if gas := tx.Gas(); l.gascap < gas {
      l.gascap = gas
   }
   return true, old
}

pending 대기열에는 이미 같은 nonce 값의 거래가 있고 신구 두 거래의 nonce가 같거나 새로운 거래의 값이threshold 값보다 작다. 이것은 오보의 원인이다. 그러나gas의 비용은 자동으로 계산되기 때문에 원인은 nonce 값에 있다. 거래를 보내는 코드를 보면 nonce 값이 들어가지 않고 결점이 거래 소식을 받은 후,전송된 매개 변수 nonce 값이 비어 있으면 기존 pending 대기열에서 같은 nonce를 가져옵니다. nonce 값은 블록이 포장될 때까지 기다려야 거래 메시지를 state DB로 지속할 때 +1 변화가 있습니다.//다음은 기본 nonce 코드를 가져오는 것입니다
// setDefaults is a helper function that fills in default values for unspecified tx fields.
func (args *SendTxArgs) setDefaults(ctx context.Context, b Backend) error {
   //      
   if args.Nonce == nil {
      nonce, err := b.GetPoolNonce(ctx, args.From)
      if err != nil {
         return err
      }
      args.Nonce = (*hexutil.Uint64)(&nonce)
   }
   //      
}

//다음은 거래를 수행할 때 블록에 포장된 코드입니다
// TransitionDb will transition the state by applying the current message and
// returning the result including the used gas. It returns an error if failed.
// An error indicates a consensus issue.
func (st *StateTransition) TransitionDb() (ret []byte, usedGas uint64, failed bool, err error) {

   //       	
   if contractCreation {
      ret, _, st.gas, vmerr = evm.Create(sender, st.data, st.gas, st.value)
   } else {
      // Increment the nonce for the next transaction
      st.state.SetNonce(msg.From(), st.state.GetNonce(sender.Address())+1)  //   +1
      ret, st.gas, vmerr = evm.Call(sender, st.to(), st.data, st.gas, st.value)
   }
   //       
}

이 문제를 해결하려면 두 가지 방식이 있다. 첫째, 호출자가 nonce 값을 유지하고 자신이 매번 거래를 만들 때마다 nonce 값이 변화하고 거래 매개 변수를 전송한다. 2. 상기 거래 비용을 토대로 가스를 많이 넣는다.

좋은 웹페이지 즐겨찾기