以太坊源码分析(3)Accounts源码分析与逻辑结构之交易源码

Eth Transfer 原理 阅读详情

Accounts源码分析之交易源码

listAccounts源码分析

位于internal/ethapi/api.go中的ListAccounts方法
// ListAccounts will return a list of addresses for accounts this node manages.
func (s *PrivateAccountAPI) ListAccounts() []common.Address {
   addresses := make([]common.Address, 0) // return [] instead of nil if empty
   for _, wallet := range s.am.Wallets() {
      for _, account := range wallet.Accounts() {
         addresses = append(addresses, account.Address)
      }
   }
   return addresses
}

通过accounts/account/下的Wallets钱包管理,遍历钱包中存储的账户,将addresses数组返回控制台

sendTransaction 源码分析

在了解sendTransaction之前我们先看一下Transaction 的数据模型

在core/types/tracsaction.go文件下的Transaction结构体

type Transaction struct {
   data txdata
   // caches
   hash atomic.Value
   size atomic.Value
   from atomic.Value
}

hash:交易的hash

size:交易数据的大小

from:发起交易的地址

data:交易数据

type txdata struct {
   AccountNonce uint64          `json:"nonce"    gencodec:"required"`
   Price        *big.Int        `json:"gasPrice" gencodec:"required"`
   GasLimit     uint64          `json:"gas"      gencodec:"required"`
   Recipient    *common.Address `json:"to"       rlp:"nil"` // nil means contract creation
   Amount       *big.Int        `json:"value"    gencodec:"required"`
   Payload      []byte          `json:"input"    gencodec:"required"`

   // Signature values
   V *big.Int `json:"v" gencodec:"required"`
   R *big.Int `json:"r" gencodec:"required"`
   S *big.Int `json:"s" gencodec:"required"`

   // This is only used when marshaling to JSON.
   Hash *common.Hash `json:"hash" rlp:"-"`
}

AccountNonce:发起者发起的交易总数量

Price:此次交易的gas 价格

GasLimit:此次交易允许消耗的最大gas数

Recipient:交易接收者的地址

Amount:此次交易的以太币数量

Payload:对应的虚拟机指令

V:签名数据

R:签名数据

S:签名数据

SendTxArgs:交易的一些参数

type SendTxArgs struct {
   From     common.Address  `json:"from"`
   To       *common.Address `json:"to"`
   Gas      *hexutil.Uint64 `json:"gas"`
   GasPrice *hexutil.Big    `json:"gasPrice"`
   Value    *hexutil.Big    `json:"value"`
   Nonce    *hexutil.Uint64 `json:"nonce"`
   // We accept "data" and "input" for backwards-compatibility reasons. "input" is the
   // newer name and should be preferred by clients.
   Data  *hexutil.Bytes `json:"data"`
   Input *hexutil.Bytes `json:"input"`
}
了解了交易的数据模型之后,我们再来具体执行的源码:

sendTransaction经过RPC调用后,会调用internal/ethapi/api.go中的sendTransaction方法

// SendTransaction为给定的参数创建一个事务,签名并提交给事务池
func (s *PublicTransactionPoolAPI) SendTransaction(ctx context.Context, args SendTxArgs) (common.Hash, error) {
	//通过From地址构造一个account
   // Look up the wallet containing the requested signer
   account := accounts.Account{Address: args.From}
	//调用账户管理器获得该account的钱包,Find方法会从账户管理系统中对钱包进行遍历,找到包含这个        //account的钱包
   wallet, err := s.b.AccountManager().Find(account)
   if err != nil {
      return common.Hash{}, err
   }

   if args.Nonce == nil {
       //保持地址的互斥锁在签名附近,以防止并发分配同样的方法可以多次使用
       //对于每一个账户,nonce会随着转账的增加而增加,以防止双花攻击
      // Hold the addresse's mutex around signing to prevent concurrent assignment of
      // the same nonce to multiple accounts.
      s.nonceLock.LockAddr(args.From)
      defer s.nonceLock.UnlockAddr(args.From)
   }
	//设置交易参数的默认值
   // Set some sanity defaults and terminate on failure
   if err := args.setDefaults(ctx, s.b); err != nil {
      return common.Hash{}, err
   }
    //利用toTransaction方法创建一笔交易
   // Assemble the transaction and sign with the wallet
   tx := args.toTransaction()
	//此时我们已经创建好了一笔交易,接着我们获取区块链的配置信息,检查是否是EIP155的配置,并获取链ID。
   var chainID *big.Int
   if config := s.b.ChainConfig(); config.IsEIP155(s.b.CurrentBlock().Number()) {
      chainID = config.ChainID
   }
    //对交易进行签名 参数是账户,交易信息 ,链ID
   signed, err := wallet.SignTx(account, tx, chainID)
   if err != nil {
      return common.Hash{}, err
   }
   return submitTransaction(ctx, s.b, signed)
}

Find方法会从账户管理系统中对钱包进行遍历,找到包含这个account的钱包

func (am *Manager) Find(account Account) (Wallet, error) {
    //将当前的账户管理器进行读锁
   am.lock.RLock()
    //调用栈结束解锁
   defer am.lock.RUnlock()
	//遍历账户管理器的所有钱包
   for _, wallet := range am.wallets {
       //找到这个account对应的钱包
      if wallet.Contains(account) {
          //返回找到的钱包
         return wallet, nil
      }
   }
    //没有找到返回对应错误
   return nil, ErrUnknownAccount
}

获得钱包以后对交易参数中的账户nonce进行上锁,以防止双花攻击,然后调用setDefaults方法对交易参数设置一些默认参数值

func (args *SendTxArgs) setDefaults(ctx context.Context, b Backend) error {
    //参数中Gas是否为空
   if args.Gas == nil {
       //为空设置默认值
      args.Gas = new(hexutil.Uint64)
      *(*uint64)(args.Gas) = 90000
   }
    //参数中GasPrice是否为空
   if args.GasPrice == nil {
       //获取建议的市场价格
      price, err := b.SuggestPrice(ctx)
      if err != nil {
         return err
      }
       //设置价格
      args.GasPrice = (*hexutil.Big)(price)
   }
    //参数中Value是否为空
   if args.Value == nil {
      args.Value = new(hexutil.Big)
   }
    //参数中Nonce是否为空 
   if args.Nonce == nil {
       //通过账户获取账户的nonce
      nonce, err := b.GetPoolNonce(ctx, args.From)
      if err != nil {
         return err
      }
       //设置nonce
      args.Nonce = (*hexutil.Uint64)(&nonce)
   }
   if args.Data != nil && args.Input != nil && !bytes.Equal(*args.Data, *args.Input) {
      return errors.New(`Both "data" and "input" are set and not equal. Please use "input" to pass transaction call data.`)
   }
    //如果参数中To为空
   if args.To == nil {
      // Contract creation
      var input []byte
      if args.Data != nil {
         input = *args.Data
      } else if args.Input != nil {
         input = *args.Input
      }
      if len(input) == 0 {
         return errors.New(`contract creation without any data provided`)
      }
   }
   return nil
}

toTransaction方法使用SendTxArgs参数创建一笔交易,将新的交易信息返回

func (args *SendTxArgs) toTransaction() *types.Transaction {
    //一个字节数组
   var input []byte
    //如果参数Data不为空
   if args.Data != nil {
       //字节数组的值=Data
      input = *args.Data
       //如果参数Input不为空
   } else if args.Input != nil {
       //字节数组的值=Input
      input = *args.Input
   }
    //这里会对传入的交易信息的to参数进行判断。如果没有to值,那么这是一笔合约转账;而如果有to值,那么就		是发起的一笔转账。最终,代码会调用NewTransaction创建一笔交易信息
   if args.To == nil {
      return types.NewContractCreation(uint64(*args.Nonce), (*big.Int)(args.Value), uint64(*args.Gas), (*big.Int)(args.GasPrice), input)
   }
   return types.NewTransaction(uint64(*args.Nonce), *args.To, (*big.Int)(args.Value), uint64(*args.Gas), (*big.Int)(args.GasPrice), input)
}

事实上NewContractCreation也是调用newTransaction,知识to参数为nil

func NewContractCreation(nonce uint64, amount *big.Int, gasLimit uint64, gasPrice *big.Int, data []byte) *Transaction {
   return newTransaction(nonce, nil, amount, gasLimit, gasPrice, data)
}

通过newTransaction构建一个完整的Transaction交易数据结构,将数据机构返回

func newTransaction(nonce uint64, to *common.Address, amount *big.Int, gasLimit uint64, gasPrice *big.Int, data []byte) *Transaction {
   if len(data) > 0 {
      data = common.CopyBytes(data)
   }
   d := txdata{
      AccountNonce: nonce,
      Recipient:    to,
      Payload:      data,
      Amount:       new(big.Int),
      GasLimit:     gasLimit,
      Price:        new(big.Int),
      V:            new(big.Int),
      R:            new(big.Int),
      S:            new(big.Int),
   }
   if amount != nil {
      d.Amount.Set(amount)
   }
   if gasPrice != nil {
      d.Price.Set(gasPrice)
   }

   return &Transaction{data: d}
}

这里就是填充了交易结构体中的一些参数,来创建一个交易。到这里,一笔交易就已经创建成功了。

回到sendTransaction方法中,此时我们已经创建好了一笔交易,接着我们获取区块链的配置信息,检查是否是EIP155的配置,并获取链ID。

//获取链的一些配置信息
type ChainConfig struct {
   ChainID *big.Int `json:"chainId"` // chainId identifies the current chain and is used for replay protection

   HomesteadBlock *big.Int `json:"homesteadBlock,omitempty"` // Homestead switch block (nil = no fork, 0 = already homestead)

   DAOForkBlock   *big.Int `json:"daoForkBlock,omitempty"`   // TheDAO hard-fork switch block (nil = no fork)
   DAOForkSupport bool     `json:"daoForkSupport,omitempty"` // Whether the nodes supports or opposes the DAO hard-fork

   // EIP150 implements the Gas price changes (https://github.com/ethereum/EIPs/issues/150)
   EIP150Block *big.Int    `json:"eip150Block,omitempty"` // EIP150 HF block (nil = no fork)
   EIP150Hash  common.Hash `json:"eip150Hash,omitempty"`  // EIP150 HF hash (needed for header only clients as only gas pricing changed)

   EIP155Block *big.Int `json:"eip155Block,omitempty"` // EIP155 HF block
   EIP158Block *big.Int `json:"eip158Block,omitempty"` // EIP158 HF block

   ByzantiumBlock      *big.Int `json:"byzantiumBlock,omitempty"`      // Byzantium switch block (nil = no fork, 0 = already on byzantium)
   ConstantinopleBlock *big.Int `json:"constantinopleBlock,omitempty"` // Constantinople switch block (nil = no fork, 0 = already activated)
   EWASMBlock          *big.Int `json:"ewasmBlock,omitempty"`          // EWASM switch block (nil = no fork, 0 = already activated)

   // Various consensus engines
   Ethash *EthashConfig `json:"ethash,omitempty"`
   Clique *CliqueConfig `json:"clique,omitempty"`
}

为了保证交易的真实有效,我们需要对交易进行签名,调用SingTx方法对交易签名

func (ks *KeyStore) SignTx(account *Account, tx *Transaction, chainID *BigInt) (*Transaction, error) {
   if chainID == nil { // Null passed from mobile app
      chainID = new(BigInt)
   }
   signed, err := ks.keystore.SignTx(account.account, tx.tx, chainID.bigint)
   if err != nil {
      return nil, err
   }
    //返回签名后的交易信息
   return &Transaction{signed}, nil
}

回到sendTransaction方法中这个时候需要提交交易,调用submitTransaction方法会将交易发送给backend进行处理,返回经过签名后的交易的hash值。这里主要是SendTx方法对交易进行处理。

sendTx方法会将参数转给txpool的Addlocal方法进行处理,而AddLocal方法会将该笔交易放入到交易池中进行等待。这里我们看将交易放入到交易池中的方法。

func submitTransaction(ctx context.Context, b Backend, tx *types.Transaction) (common.Hash, error) {
   if err := b.SendTx(ctx, tx); err != nil {
      return common.Hash{}, err
   }
   if tx.To() == nil {
      signer := types.MakeSigner(b.ChainConfig(), b.CurrentBlock().Number())
      from, err := types.Sender(signer, tx)
      if err != nil {
         return common.Hash{}, err
      }
      addr := crypto.CreateAddress(from, tx.Nonce())
      log.Info("Submitted contract creation", "fullhash", tx.Hash().Hex(), "contract", addr.Hex())
   } else {
      log.Info("Submitted transaction", "fullhash", tx.Hash().Hex(), "recipient", tx.To())
   }
   return tx.Hash(), nil
}
func (b *EthAPIBackend) SendTx(ctx context.Context, signedTx *types.Transaction) error {
   return b.eth.txPool.AddLocal(signedTx)
}
func (pool *TxPool) AddLocal(tx *types.Transaction) error {
   return pool.addTx(tx, !pool.config.NoLocals)
}
func (pool *TxPool) addTx(tx *types.Transaction, local bool) error {
   pool.mu.Lock()
   defer pool.mu.Unlock()

   // Try to inject the transaction and update any state
   replace, err := pool.add(tx, local)
   if err != nil {
      return err
   }
   // If we added a new transaction, run promotion checks and return
   if !replace {
      from, _ := types.Sender(pool.signer, tx) // already validated
      pool.promoteExecutables([]common.Address{from})
   }
   return nil
}

这里一共有两部操作,第一步操作是调用add方法将交易放入到交易池中,第二步是判断replace参数。如果该笔交易合法并且交易原来不存在在交易池中,则执行promoteExecutables方法,将可处理的交易变为待处理(pending)。

首先看第一步add方法。

func (pool *TxPool) add(tx *types.Transaction, local bool) (bool, error) {
   // If the transaction is already known, discard it
   hash := tx.Hash()
    //判断这个交易hash有么有在交易池中,如果交易池中有这笔交易则返回报错
   if pool.all.Get(hash) != nil {
      log.Trace("Discarding already known transaction", "hash", hash)
      return false, fmt.Errorf("known transaction: %x", hash)
   }
    //调用validateTx判断交易是否合法,如果不合法则返回报错
   // If the transaction fails basic validation, discard it
   if err := pool.validateTx(tx, local); err != nil {
      log.Trace("Discarding invalid transaction", "hash", hash, "err", err)
      invalidTxCounter.Inc(1)
      return false, err
   }
    //判断交易池是否超过容量
   // If the transaction pool is full, discard underpriced transactions
   if uint64(pool.all.Count()) >= pool.config.GlobalSlots+pool.config.GlobalQueue {
      // If the new transaction is underpriced, don't accept it
       //如果超过容量,并且该笔交易的费用低于当前交易池中列表的最小值,则拒绝这一笔交易
      if !local && pool.priced.Underpriced(tx, pool.locals) {
         log.Trace("Discarding underpriced transaction", "hash", hash, "price", tx.GasPrice())
         underpricedTxCounter.Inc(1)
         return false, ErrUnderpriced
      }
       //如果超过容量,并且该笔交易的费用比当前交易池中列表最小值高,那么从交易池中移除交易费用最低的交易,为当前这一笔交易留出空间。
      // New transaction is better than our worse ones, make room for it
      drop := pool.priced.Discard(pool.all.Count()-int(pool.config.GlobalSlots+pool.config.GlobalQueue-1), pool.locals)
      for _, tx := range drop {
         log.Trace("Discarding freshly underpriced transaction", "hash", tx.Hash(), "price", tx.GasPrice())
         underpricedTxCounter.Inc(1)
         pool.removeTx(tx.Hash(), false)
      }
   }
    //如果事务正在替换一个已经挂起的事务,请直接执行
   // If the transaction is replacing an already pending one, do directly
   from, _ := types.Sender(pool.signer, tx) // already validated
    //接着继续调用Overlaps方法检查该笔交易的Nonce值,确认该用户下的交易是否存在该笔交易
   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 {
         pendingDiscardCounter.Inc(1)
         return false, ErrReplaceUnderpriced
      }
      // New transaction is better, replace old one
      
      if old != nil {
         pool.all.Remove(old.Hash())
         pool.priced.Removed()
         pendingReplaceCounter.Inc(1)
      }
       //在池里添加新的交易信息
      pool.all.Add(tx)
       //添加新交易的价格
      pool.priced.Put(tx)
       //将交易信息写到日志中,只有是在本地账户的情况下
      pool.journalTx(from, tx)

      log.Trace("Pooled new executable transaction", "hash", hash, "from", from, "to", tx.To())

      // We've directly injected a replacement transaction, notify subsystems
      go pool.txFeed.Send(NewTxsEvent{types.Transactions{tx}})

      return old != nil, nil
   }
    //如果不存在,则调用enqueueTx将该笔交易放入交易池中。如果交易是本地发出的,则将发送者保存在交易池的local中
   // New transaction isn't replacing a pending one, push into queue
   replace, err := pool.enqueueTx(hash, tx)
   if err != nil {
      return false, err
   }
    //标记本地地址并记录本地事务
   // Mark local addresses and journal local transactions
   if local {
      if !pool.locals.contains(from) {
         log.Info("Setting new local account", "address", from)
         pool.locals.add(from)
      }
   }
    //记录本地账户的事务交易信息
   pool.journalTx(from, tx)

   log.Trace("Pooled new future transaction", "hash", hash, "from", from, "to", tx.To())
   return replace, nil
}

总结:add方法执行流程

1.判断这个交易hash有么有在交易池中,如果交易池中有这笔交易则返回报错

2.调用validateTx判断交易是否合法,如果不合法则返回报错

3.判断交易池是否超过容量

4.如果超过容量,并且该笔交易的费用低于当前交易池中列表的最小值,则拒绝这一笔交易

5.如果超过容量,并且该笔交易的费用比当前交易池中列表最小值高,那么从交易池中移除交易费用最低的交易,为当前这一笔交易留出空间。

6.接着继续调用Overlaps方法检查该笔交易的Nonce值,确认该用户下的交易是否存在该笔交易

7.如果已经存在这笔交易,则删除之前的交易,并将该笔交易放入交易池中,然后返回。

8.如果不存在,则调用enqueueTx将该笔交易放入交易池中。如果交易是本地发出的,则将发送者保存在交易池的local中

9.返回执行结果 true /false和错误信息

validateTx方法执行的逻辑

func (pool *TxPool) validateTx(tx *types.Transaction, local bool) error {
   // Heuristic limit, reject transactions over 32KB to prevent DOS attacks
   if tx.Size() > 32*1024 {
      return ErrOversizedData
   }
   // Transactions can't be negative. This may never happen using RLP decoded
   // transactions but may occur if you create a transaction using the RPC.
   if tx.Value().Sign() < 0 {
      return ErrNegativeValue
   }
   // Ensure the transaction doesn't exceed the current block limit gas.
   if pool.currentMaxGas < tx.Gas() {
      return ErrGasLimit
   }
   // Make sure the transaction is signed properly
   from, err := types.Sender(pool.signer, tx)
   if err != nil {
      return ErrInvalidSender
   }
   // Drop non-local transactions under our own minimal accepted gas price
   local = local || pool.locals.contains(from) // account may be local even if the transaction arrived from the network
   if !local && pool.gasPrice.Cmp(tx.GasPrice()) > 0 {
      return ErrUnderpriced
   }
   // Ensure the transaction adheres to nonce ordering
   if pool.currentState.GetNonce(from) > tx.Nonce() {
      return ErrNonceTooLow
   }
   // Transactor should have enough funds to cover the costs
   // cost == V + GP * GL
   if pool.currentState.GetBalance(from).Cmp(tx.Cost()) < 0 {
      return ErrInsufficientFunds
   }
   intrGas, err := IntrinsicGas(tx.Data(), tx.To() == nil, pool.homestead)
   if err != nil {
      return err
   }
   if tx.Gas() < intrGas {
      return ErrIntrinsicGas
   }
   return nil
}

validateTx会验证一笔交易的以下几个特性:
​ 1.首先验证这笔交易的大小,如果大于32kb则拒绝这笔交易,这样主要是为了防止DDOS攻击。
​ 2.接着验证转账金额。如果金额小于0则拒绝这笔交易。
​ 3.这笔交易的gas不能超过交易池的gas上限。
​ 4.验证这笔交易的签名是否合法。
​ 5.如果这笔交易不是来自本地并且这笔交易的gas小于当前交易池中的gas,则拒绝这笔交易。
​ 6.当前用户的nonce如果大于这笔交易的nonce,则拒绝这笔交易。
​ 7.当前用户的余额是否充足,如果不充足则拒绝该笔交易。
​ 8.验证这笔交易的固有花费,如果小于交易池的gas,则拒绝该笔交易。
以上就是在进行交易验证时所需验证的参数。这一系列的验证操作结束后,回到addTx的第二步。
会判断replace。如果replace是false,则会执行promoteExecutables方法。

func (pool *TxPool) promoteExecutables(accounts []common.Address) {
   // Track the promoted transactions to broadcast them at once
   var promoted []*types.Transaction

   // Gather all the accounts potentially needing updates
   if accounts == nil {
      accounts = make([]common.Address, 0, len(pool.queue))
      for addr := range pool.queue {
         accounts = append(accounts, addr)
      }
   }
   // Iterate over all accounts and promote any executable transactions
   for _, addr := range accounts {
      list := pool.queue[addr]
      if list == nil {
         continue // Just in case someone calls with a non existing account
      }
      // Drop all transactions that are deemed too old (low nonce)
      for _, tx := range list.Forward(pool.currentState.GetNonce(addr)) {
         hash := tx.Hash()
         log.Trace("Removed old queued transaction", "hash", hash)
         pool.all.Remove(hash)
         pool.priced.Removed()
      }
      // Drop all transactions that are too costly (low balance or out of gas)
      drops, _ := list.Filter(pool.currentState.GetBalance(addr), pool.currentMaxGas)
      for _, tx := range drops {
         hash := tx.Hash()
         log.Trace("Removed unpayable queued transaction", "hash", hash)
         pool.all.Remove(hash)
         pool.priced.Removed()
         queuedNofundsCounter.Inc(1)
      }
      // Gather all executable transactions and promote them
      for _, tx := range list.Ready(pool.pendingState.GetNonce(addr)) {
         hash := tx.Hash()
         if pool.promoteTx(addr, hash, tx) {
            log.Trace("Promoting queued transaction", "hash", hash)
            promoted = append(promoted, tx)
         }
      }
      // Drop all transactions over the allowed limit
      if !pool.locals.contains(addr) {
         for _, tx := range list.Cap(int(pool.config.AccountQueue)) {
            hash := tx.Hash()
            pool.all.Remove(hash)
            pool.priced.Removed()
            queuedRateLimitCounter.Inc(1)
            log.Trace("Removed cap-exceeding queued transaction", "hash", hash)
         }
      }
      // Delete the entire queue entry if it became empty.
      if list.Empty() {
         delete(pool.queue, addr)
      }
   }
   // Notify subsystem for new promoted transactions.
   if len(promoted) > 0 {
      go pool.txFeed.Send(NewTxsEvent{promoted})
   }
   // If the pending limit is overflown, start equalizing allowances
   pending := uint64(0)
   for _, list := range pool.pending {
      pending += uint64(list.Len())
   }
   if pending > pool.config.GlobalSlots {
      pendingBeforeCap := pending
      // Assemble a spam order to penalize large transactors first
      spammers := prque.New(nil)
      for addr, list := range pool.pending {
         // Only evict transactions from high rollers
         if !pool.locals.contains(addr) && uint64(list.Len()) > pool.config.AccountSlots {
            spammers.Push(addr, int64(list.Len()))
         }
      }
      // Gradually drop transactions from offenders
      offenders := []common.Address{}
      for pending > pool.config.GlobalSlots && !spammers.Empty() {
         // Retrieve the next offender if not local address
         offender, _ := spammers.Pop()
         offenders = append(offenders, offender.(common.Address))

         // Equalize balances until all the same or below threshold
         if len(offenders) > 1 {
            // Calculate the equalization threshold for all current offenders
            threshold := pool.pending[offender.(common.Address)].Len()

            // Iteratively reduce all offenders until below limit or threshold reached
            for pending > pool.config.GlobalSlots && pool.pending[offenders[len(offenders)-2]].Len() > threshold {
               for i := 0; i < len(offenders)-1; i++ {
                  list := pool.pending[offenders[i]]
                  for _, tx := range list.Cap(list.Len() - 1) {
                     // Drop the transaction from the global pools too
                     hash := tx.Hash()
                     pool.all.Remove(hash)
                     pool.priced.Removed()

                     // Update the account nonce to the dropped transaction
                     if nonce := tx.Nonce(); pool.pendingState.GetNonce(offenders[i]) > nonce {
                        pool.pendingState.SetNonce(offenders[i], nonce)
                     }
                     log.Trace("Removed fairness-exceeding pending transaction", "hash", hash)
                  }
                  pending--
               }
            }
         }
      }
      // If still above threshold, reduce to limit or min allowance
      if pending > pool.config.GlobalSlots && len(offenders) > 0 {
         for pending > pool.config.GlobalSlots && uint64(pool.pending[offenders[len(offenders)-1]].Len()) > pool.config.AccountSlots {
            for _, addr := range offenders {
               list := pool.pending[addr]
               for _, tx := range list.Cap(list.Len() - 1) {
                  // Drop the transaction from the global pools too
                  hash := tx.Hash()
                  pool.all.Remove(hash)
                  pool.priced.Removed()

                  // Update the account nonce to the dropped transaction
                  if nonce := tx.Nonce(); pool.pendingState.GetNonce(addr) > nonce {
                     pool.pendingState.SetNonce(addr, nonce)
                  }
                  log.Trace("Removed fairness-exceeding pending transaction", "hash", hash)
               }
               pending--
            }
         }
      }
      pendingRateLimitCounter.Inc(int64(pendingBeforeCap - pending))
   }
   // If we've queued more transactions than the hard limit, drop oldest ones
   queued := uint64(0)
   for _, list := range pool.queue {
      queued += uint64(list.Len())
   }
   if queued > pool.config.GlobalQueue {
      // Sort all accounts with queued transactions by heartbeat
      addresses := make(addressesByHeartbeat, 0, len(pool.queue))
      for addr := range pool.queue {
         if !pool.locals.contains(addr) { // don't drop locals
            addresses = append(addresses, addressByHeartbeat{addr, pool.beats[addr]})
         }
      }
      sort.Sort(addresses)

      // Drop transactions until the total is below the limit or only locals remain
      for drop := queued - pool.config.GlobalQueue; drop > 0 && len(addresses) > 0; {
         addr := addresses[len(addresses)-1]
         list := pool.queue[addr.address]

         addresses = addresses[:len(addresses)-1]

         // Drop all transactions if they are less than the overflow
         if size := uint64(list.Len()); size <= drop {
            for _, tx := range list.Flatten() {
               pool.removeTx(tx.Hash(), true)
            }
            drop -= size
            queuedRateLimitCounter.Inc(int64(size))
            continue
         }
         // Otherwise drop only last few transactions
         txs := list.Flatten()
         for i := len(txs) - 1; i >= 0 && drop > 0; i-- {
            pool.removeTx(txs[i].Hash(), true)
            drop--
            queuedRateLimitCounter.Inc(1)
         }
      }
   }
}

在promoteExecutable中有一个promoteTx方法,这个方法是将交易防区pending区方法中。在promoteTx方法中,最后一步执行的是一个Send方法。
这个Send方法会同步将pending区的交易广播至它所连接到的节点,并返回通知到的节点的数量。
然后被通知到的节点继续通知到它添加的节点,继而广播至全网。

至此,发送交易就结束了。此时交易池中的交易等待挖矿打包处理

以太坊账户管理源码分析 这篇分析一下以太坊的账户管理。 这部分比较简单,主要分”获取钱包列表“和“订阅钱包事件”两个部分,下面分别介绍。 1. 获取钱包列表 先上一张图,理清组件间的关系: 从图中可以看出wallet、account、address这三者的区别和联系i:wallet中可能包含多个account,而每个account中包含一个address和账户所在路径(URL)。 这里有两个重要接口:Bac... 阅读详情

相关推荐

CS1237卡尔曼滤波实战:如何用24位ADC实现电子秤级精度?

本文详细介绍了如何利用CS1237 24位ADC芯片结合卡尔曼滤波算法实现电子秤级精度测量。通过解析CS1237硬件特性、卡尔曼滤波原理及STM32驱动优化,提供了一套完整的解决方案,包括多通道支持、驱动代码实现和性能调优技巧,适用于工业测量和嵌入式开发领域。

linux的博客 1087

区块链常见交易问题-高级

区块链是一种记录保存系统,在将条目添加到数据链之前会有多个源来验证该条目。数据一旦添加,便无法更改,记录随之分布到网络中的多个位置。向区块链序列添加新记录(称为区块)需要由连接到区块链网络的多个成员进行验证。这些数据区块相互链接而形成链。区块链中的全部交易对其中的所有人公开,但所有个人身份都是隐藏的。区块链技术使用算法为每个区块分配加密哈希(包含字母和数字的唯一字符串,有时也称为“数字指纹”)。除哈希以外,每个区块还包含带时间戳的先前交易的记录集,以及前一个区块的哈希。

知识的共享,拓宽视野,深化思考,获得新的启发. 3247

实现飞书机器人推送消息到指定群组或者用户

飞书是一个集成了聊天、协作和办公功能的企业级平台,广泛应用于企业内部沟通和协作。飞书机器人(Feishu Bot)是一种强大的工具,允许开发者通过编程接口与飞书进行互动,从而实现自动化消息推送和任务管理。本文将详细介绍如何通过飞书机器人推送消息给指定的人或者群组,帮助您快速上手。

qq_41898196的博客 5862

web3中eth转账函数

sendTransaction和send都可以转账, sendTransaction是专门用来转账的,无需合约,eth直接能调用,send必须有合约的方法 sendTransaction 比send多一个回调函数。

qq_30505673的博客 2036

用AT7456E给监控摄像头加OSD?5步搞定时间水印和Logo叠加

本文详细解析了如何使用AT7456E视频字符叠加芯片为监控系统添加专业级OSD功能,涵盖硬件设计、字符库编程和系统集成等关键步骤。通过5步操作实现时间水印和Logo叠加,提升监控系统的可用性和专业性,特别适合安防和工业视觉应用。

sea99的博客 1011

【unity游戏开发入门到精通——UGUI】UI控件 —— Toggle开关切换控件

【unity游戏开发入门到精通——UGUI】UI控件 —— Toggle开关切换控件

向宇的博客,专注php/web全栈 unity游戏开发,欢迎大家评论纠错 1844

go语言JSON-RPC API详解之eth_sendTransaction

在以太坊系统下,要使用go语言来实现发送交易功能,是通过JSON-RPC接口来调用节点客户端的eth_sendTransaction方法,他们之间的关系是:eth_sendTransaction是客户端提供给外部调用的方法,JSON-RPC使用json格式的交互数据来远程调用客户端的方法。 JSON-RPC API下调用eth_sendTransaction的说明如下: 在JSON RPC...

qq_30864597的博客 4509

GMSL转eDP芯片MAX9686X(ADI)

MAX96860/MAX96862/MAX96864解串器可以把单路或双路GMSL转换成eDP V1.4a,支持820MHzPCLK的无压缩的24bpp(每个像素的位数)视频,或660MHz的30bpp视频。芯片的GMSL支持NRZ编码的6Gbps/187Mbps的GMSL2,PAM4编码的12Gbps/187Mbps的GMSL3。同时支持单线(COAX模式)双向全双工传输控制数据和video数据。支持DSC压缩的芯片,最大支持像素时钟1080MHz@24bpp或者864MHz@30bpp,传输方式包括同轴

qflook的博客 2835

三种外部调用

外部调用: sendTransaction/call 函数调用一般分外部调用和内部调用两种, 外部调用是通过JSON-RPC接口实现对合约函数的调用, 有3种调用方式: testInstance.testFunc.sendTransaction(); testInstance.testFunc(); testInstance.testFunc.call(); 区别如下: testInstance....

weixin_43832070的博客 1121

SpringBoot + MyBatis-Plus 高效实现数据变更记录:从注解到通用审计方案(附源码)

*** 数据变更审计插件* 拦截增删改操作,自动记录变更日志*/@Component@Resource/*** 拦截SQL执行*/@Override/*** 数据变更审计插件* 拦截增删改操作,自动记录变更日志/*** 拦截SQL执行。

2601_94871597的博客 556

运放+MOS恒流源电路仿真分析

利用运放设计一个恒流源

HW-BASE的博客 4081

以太坊源码分析(2)Accounts源码分析与逻辑结构之基本概述与新建账户源码

总所周知以太坊在比特币的基础上加以引用与改进,比特币使用UTXO来表示状态的转移,而以太坊使用账来表示状态的转移。 在以太坊网络中存在两种账户: 外部账户EOA:一般是属于个人或者用户的账户,被私钥控制没有任何代码与之相关 内部账户CA:给智能合约分配的账户,被合约代码控制,且与合约关联 在源码core/state/state_object.go文件下,账户定义如下: // Account is ...

Debug_lll的博客 659

9.6华为OD机试真题 新系统 - 仓储货物有序出库 (Java/Py/C/C++/Js/Go)

9.6华为OD机试真题 新系统 - 仓储货物有序出库 (Java/Py/C/C++/Js/Go)

算法大师! 1196

以太坊源码分析(5)accounts代码分析

## 1.personal.newAccount创建账户方法用户在控制台输入personal.newAccount会创建一个新的账户,会进入到ethapi.api中的newAccount方法中,这个方法会返回一个地址。``` func (s *PrivateAccountAPI) NewAccount(password string) (common.Address, error) { ac...

尹成的技术博客 2934

WINCC7.3破解文件

WINCC7.3硬件够破解文件,有需要的下载!WINCC7.3硬件够破解文件,有需要的下载!

jdk-8u144-linux-x64.tar.gz.

该资源为搭建Linux系统Java环境用,资源的后缀为tar.gz格式的压缩包

以太坊源码分析(4)accounts包简介

accounts包实现了以太坊客户端的钱包和账户管理。账号的数据结构:    typeAccount struct {    Address common.Address `json:"address"` // Ethereum account addressderived from the key     URLURL `json:"url"` // Optional resource loca...

尹成的技术博客 1671

SAP B1中文培訓教材_SDK

公司内部培训资料,对开发工具 ,DIAPI, UIAPI,Add-ON有详细解说

以太坊源码分析(6)accounts账户管理分析

##数据结构分析以太坊的账户管理定义在accounts/manager.go中,其数据结构为:```// Manager is an overarching account manager that can communicate with various// backends for signing transactions. type Manager struct { ba...

尹成的技术博客 2035

Ethereum 源码分析之 accounts

一、Account // Account represents an Ethereum account located at a specific location defined // by the optional URL field. // Account,代表一个位于由可选的URL字段定义的具体位置的以太坊账户 // Address,地址是20个字节,由私钥衍生。。U...

banjuan3729的博客 237

06.以太坊源码分析(6)accounts账户管理分析

数据结构分析 以太坊的账户管理定义在accounts/manager.go中,其数据结构为: // Manager is an overarching account manager that can communicate with various // backends for signing transactions. type Manager struct { ba...

boss2967的博客 485

如何从零构建以太坊账户系统:Geth私钥生成与地址计算实战

以太坊账户系统是区块链交互的基础,本文将带你快速掌握以太坊账户的核心原理,包括私钥生成、公钥提取和地址计算的完整流程。通过Geth源码解析和实战示例,即使是区块链新手也能轻松理解账户安全的底层逻辑。 ## 以太坊账户的核心构成 以太坊账户分为外部账户(EOA)和合约账户两种类型,其中外部账户由用户直接控制,通过私钥进行交易签名。每个账户都拥有唯一的20字节地址,类似于银行账户的账号,而私钥则相

gitblog_01092的博客 375

区块链教程以太源码分析accounts账户管理分析

   兄弟连区块链教程以太源码分析accounts账户管理分析。 数据结构分析 ETH的账户管理定义在accounts/manager.go中,其数据结构为: //Manag...

congdianquan3478的博客 215

兄弟连区块链教程以太源码分析accounts包简介

accounts包实现了eth客户端的钱包和账户管理。 账号的数据结构: typeAccountstruct{ Addresscommon.Address`json:"address"`//...

congdianquan3478的博客 164

以太坊源码分析(10)CMD深入分析

### cmd包分析 #### cmd下面总共有13个子包,除了util包之外,每个子包都有一个主函数,每个主函数的init方法中都定义了该主函数支持的命令,如   ##### geth包下面的:   ``` func init() {     // Initialize the CLI app and start Geth     app.Action = geth     a...

chunqizhi 980

Go-Ethereum:从单节点到安全签名守护的演进之路

Go-Ethereum(Geth)作为以太坊协议的官方Golang实现,已经从一个简单的以太坊客户端演变为一个功能完备的区块链基础设施。本文将带您了解Geth如何从基础节点运行发展到支持复杂安全架构的完整解决方案,特别关注其**安全签名守护进程Clef**的演进历程。 ## 基础起点:搭建您的第一个以太坊节点 对于大多数开发者来说,接触Go-Ethereum的第一步都是从运行一个简单的以太坊节

gitblog_00346的博客 465
上一篇: 以太坊源码分析(2)Accounts源码分析与逻辑结构之基本概述与新建账户源码
下一篇: 以太坊源码解读(6)以太坊VM源码解读之指令集与操作码解释器
Debug_lll
博客等级 码龄8年 10粉丝 16原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值