5.5 以太坊源码详解5

交易步骤 
发起交易:制定目标地址和交易金额,以及gas和gaslimit 
交易签名:使用账户的私钥对交易进行签名 
提交交易:把交易添加到交易缓冲池中(会先对签名进行验证) 
广播交易:通知EVM执行,同时把交易广播到其他节点 
具体分析 
1、发起交易

func (s *PublicTransactionPoolAPI) SendTransaction(ctx context.Context, args SendTxArgs) (common.Hash, error) {
    // Look up the wallet containing the requested signer
    account := accounts.Account{Address: args.From}
    // 得到钱包
    wallet, err := s.b.AccountManager().Find(account)
    if err != nil {
        return common.Hash{}, err
    }
    if args.Nonce == nil {
        // 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
    }
    // Assemble the transaction and sign with the wallet
    tx := args.toTransaction()// 创建交易
    var chainID *big.Int
    if config := s.b.ChainConfig(); config.IsEIP155(s.b.CurrentBlock().Number()) {
        chainID = config.ChainID
    }
    signed, err := wallet.SignTx(account, tx, chainID) // 对交易签名(接口函数)
    if err != nil {
        return common.Hash{}, err
    }
    return submitTransaction(ctx, s.b, signed) // 提交交易(到内存池中)
}

2、创建交易

func (args *SendTxArgs) toTransaction() *types.Transaction {
    var input []byte
    if args.Data != nil {
        input = *args.Data
    } else if args.Input != nil {
        input = *args.Input
    }
    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)
}

3、发送的交易结构

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"`
}

4、新建交易的内部实现

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}
}

5、交易签名

func (s *PublicTransactionPoolAPI) SignTransaction(ctx context.Context, args SendTxArgs) (*SignTransactionResult, error) {
    if args.Gas == nil {
        return nil, fmt.Errorf("gas not specified")
    }
    if args.GasPrice == nil {
        return nil, fmt.Errorf("gasPrice not specified")
    }
    if args.Nonce == nil {
        return nil, fmt.Errorf("nonce not specified")
    }
    if err := args.setDefaults(ctx, s.b); err != nil {
        return nil, err
    }
    tx, err := s.sign(args.From, args.toTransaction())
    if err != nil {
        return nil, err
    }
    data, err := rlp.EncodeToBytes(tx)
    if err != nil {
        return nil, err
    }
    return &SignTransactionResult{data, tx}, nil
}
// sign is a helper function that signs a transaction with the private key of the given address.
func (s *PublicTransactionPoolAPI) sign(addr common.Address, tx *types.Transaction) (*types.Transaction, error) {
    // Look up the wallet containing the requested signer
    account := accounts.Account{Address: addr}
    wallet, err := s.b.AccountManager().Find(account)
    if err != nil {
        return nil, err
    }
    // Request the wallet to sign the transaction
    var chainID *big.Int
    if config := s.b.ChainConfig(); config.IsEIP155(s.b.CurrentBlock().Number()) {
        chainID = config.ChainID
    }
    return wallet.SignTx(account, tx, chainID)
}

6、最终完成交易签名

func SignTx(tx *Transaction, s Signer, prv *ecdsa.PrivateKey) (*Transaction, error) {
    h := s.Hash(tx)
    sig, err := crypto.Sign(h[:], prv)
    if err != nil {
        return nil, err
    }
    return tx.WithSignature(s, sig)
}

7、具体步骤 
生成哈希 
根据哈希值和私钥生成签名 
把签名数据填充到交易实例中 
8、提交交易到内存池中

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 //返回交易的哈希
}

9、交易缓存池的结构体

type TxPool struct {
    config       TxPoolConfig
    chainconfig  *params.ChainConfig
    chain        blockChain
    gasPrice     *big.Int
    txFeed       event.Feed
    scope        event.SubscriptionScope
    chainHeadCh  chan ChainHeadEvent
    chainHeadSub event.Subscription
    signer       types.Signer
    mu           sync.RWMutex
    currentState  *state.StateDB      // Current state in the blockchain head
    pendingState  *state.ManagedState // Pending state tracking virtual nonces
    currentMaxGas uint64              // Current gas limit for transaction caps
    locals  *accountSet // Set of local transaction to exempt from eviction rules
    journal *txJournal  // Journal of local transaction to back up to disk
    pending map[common.Address]*txList   // All currently processable transactions 当前等待处理的交易
    queue   map[common.Address]*txList   // Queued but non-processable transactions
    beats   map[common.Address]time.Time // Last heartbeat from each known account
    all     *txLookup                    // All transactions to allow lookups 所有的交易列表
    priced  *txPricedList                // All transactions sorted by price // 通过gas价格进行排序的交易列表,如果交易的gas price一样则按照nonce值从小到大排列
    wg sync.WaitGroup // for shutdown sync
    homestead bool
}

10、默认的交易池配置

var DefaultTxPoolConfig = TxPoolConfig{
    Journal:   "transactions.rlp",
    Rejournal: time.Hour,
    PriceLimit: 1,
    PriceBump:  10,
    AccountSlots: 16, pending中每个账户存储的交易阈值
    GlobalSlots:  4096, // 列表的最大长度
    AccountQueue: 64, // queue中 每个账户允许存储的最大交易树
    GlobalQueue:  1024,
    Lifetime: 3 * time.Hour,
}

11、提交交易的内部调用

func (b *EthAPIBackend) SendTx(ctx context.Context, signedTx *types.Transaction) error {
    return b.eth.txPool.AddLocal(signedTx)
}

12、添加到交易池中

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}) // 选区一部分交易、放到pending中去
    }
    return nil
}

13、add 内部实现

func (pool *TxPool) add(tx *types.Transaction, local bool) (bool, error) {
    // If the transaction is already known, discard it
    // 判断交易是否已经存在、有就直接退出
    hash := tx.Hash()
    if pool.all.Get(hash) != nil {
        log.Trace("Discarding already known transaction", "hash", hash)
        return false, fmt.Errorf("known transaction: %x", hash)
    }
    // 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
    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
    }
    // 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 {
        pool.locals.add(from)
    }
    pool.journalTx(from, tx)
    log.Trace("Pooled new future transaction", "hash", hash, "from", from, "to", tx.To())
    return replace, nil
}

 14、交易有效性检查

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
}

15、具体步骤: 
1:数据量必须小于32KB 
2:交易金额必须非负 
3:交易gas limit必须低于block 的gas limit 
4:签名必须有效,能够解析出发送者的地址 
5:交易的gas price 必须高于最低的gas price 
6:账户余额必须大于交易金额加上交易燃料费 
7:gas limit必须大于所需的gas 水平 
8:交易的nonce值必须高于链上的nonce值,如果低于则说明该交易已经被打包过了

 

  • 学院Go语言视频主页
    https://edu.csdn.net/lecturer/1928

  • 清华团队带你实战区块链开发

  • 扫码获取海量视频及源码   QQ群:721929980

你可能感兴趣的:(区块链)