以太坊 Ethereum trx console發(fā)送交易到打包全流程

開啟Geth node 和console

./build/bin/geth --datadir walker/ --miner.etherbase  0x106cdbb5029d186ae6f659b52fb83179fc153e6a --mine --miner.threads 1   --rpc --rpcvhosts "*" --rpcaddr 0.0.0.0 --rpcport 8545 --rpccorsdomain "*" --rpcapi "db,eth,net,web3,personal,debug" 

在console轉賬

//解鎖account
personal.unlockAccount("0x106cdbb5029d186ae6f659b52fb83179fc153e6a","walker")
true
//轉賬交易
eth.sendTransaction({from:'0x106cdbb5029d186ae6f659b52fb83179fc153e6a',to:'0x3b7bfcffd7a8c1047055edc58e2efa6eb589184f',value:web3.toWei(100,"ether")})
"0x21e5e3e9968d4bcf99402923b168bc172cadcc0fa4b27885d07ff52eac5a4f2b"

node處理流程

通過 console.Interactive() -> c.Evaluate(input)
-> simplechain/internal/ethapi.go

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

// submitTransaction is a helper function that submits tx to txPool and logs a message.
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
}

sendTrx() -> simplechain/core/tx_pool.go

// addTx enqueues a single transaction into the pool if it is valid.
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
}

以上代碼流程比較清晰赏参。至此node已經將交易放入交易池。

向交易池添加交易

1颁糟、添加交易TxPool.add(), add()方法用于將本地或遠端的交易加入到交易池抱环,這個方法的基本邏輯是:
1)檢查交易是否收到過可婶,重復接受的交易直接丟棄;
2)驗證交易是否有效梅忌;
3)如果交易池滿了嘲玫,待插入的交易的價值比交易池中任意一個都低娇钱,則直接丟棄伤柄;
4)如果待插入的交易序號在pending列表中已經存在,且待插入的交易價值大于或等于原交易的110%文搂,則替換原交易适刀;
5)如果待插入的交易序號在pending列表中沒有,則直接放入queue列表煤蹭。如果對應的序號已經有交易了蔗彤,則如果新交易的價值大于或等于原交易的110%,替換原交易疯兼;

注意:這里pool.config.GlobalSlots為所有可執(zhí)行交易的總數(shù)然遏,即pending列表總數(shù),默認4096吧彪;pool.config.GlobalQueue為不可執(zhí)行交易總數(shù)待侵,即queue列表總數(shù),默認1024姨裸;

// add validates a transaction and inserts it into the non-executable queue for
// later pending promotion and execution. If the transaction is a replacement for
// an already pending or queued one, it overwrites the previous and returns this
// so outer code doesn't uselessly call promote.
//
// If a newly added transaction is marked as local, its sending account will be
// whitelisted, preventing any associated transaction from being dropped out of
// the pool due to pricing constraints.
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
    //如果插入pending已有的交易秧倾,必須交易價值大于或等于原交易的110%,方可替換
    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
        //通知其他對交易池增加交易感興趣的子系統(tǒng):廣播和礦工
        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 {
        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
}


TxPool.add()的調用時機:

1)命令行發(fā)送交易

EthAPIBackend.SendTx
      ==> TxPool.AddLocal
          ==>Txpool.addTx
              ==>Txpool.add 

2)收到遠程節(jié)點廣播的交易時

AddRemotes
    ==> TxPool.addRemotes
        ==> TxPool.addTxs
            ==> TxPool.addTxLocked
                ==> TxPool.add

3)交易池重新整理的過程

TxPool.reset
    ==> TxPool.addTxLocked
         ==> TxPool.add

這里的addLocal和addRemote的區(qū)別傀缩,其中有第二個參數(shù)來設定該交易是local還是remote那先,local的交易在打包時有優(yōu)先權,在刪除時有豁免權赡艰,還會以文件的形式保存在磁盤上售淡。

func (pool *TxPool) AddLocal(tx *types.Transaction) error {
    return pool.addTx(tx, !pool.config.NoLocals)
}

func (pool *TxPool) AddRemote(tx *types.Transaction) error {
    return pool.addTx(tx, false)
}

交易加入queue列表:TxPool.enqueueTx

主要流程:
1)將交易插入queue中,如果待插入的交易序號在queue列表中已經有一個交易,那么待插入的交易價值大于原交易價值的110%揖闸,則替換原交易揍堕;
2)如果新交易替換成功,則從all列表中刪除這個被替換的交易
3)更新all列表

// enqueueTx inserts a new transaction into the non-executable transaction queue.
//
// Note, this method assumes the pool lock is held!
func (pool *TxPool) enqueueTx(hash common.Hash, tx *types.Transaction) (bool, error) {
    // Try to insert the transaction into the future queue
    from, _ := types.Sender(pool.signer, tx) // already validated
    if pool.queue[from] == nil {
        pool.queue[from] = newTxList(false)
    }
    inserted, old := pool.queue[from].Add(tx, pool.config.PriceBump)
    if !inserted {
        // An older transaction was better, discard this
        queuedDiscardCounter.Inc(1)
        return false, ErrReplaceUnderpriced
    }
    // Discard any previous transaction and mark this
    if old != nil {
        pool.all.Remove(old.Hash())
        pool.priced.Removed()
        queuedReplaceCounter.Inc(1)
    }
    if pool.all.Get(hash) == nil {
        pool.all.Add(tx)
        pool.priced.Put(tx)
    }
    return old != nil, nil
}
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末汤纸,一起剝皮案震驚了整個濱河市衩茸,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌贮泞,老刑警劉巖楞慈,帶你破解...
    沈念sama閱讀 217,657評論 6 505
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異啃擦,居然都是意外死亡囊蓝,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,889評論 3 394
  • 文/潘曉璐 我一進店門议惰,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人乡恕,你說我怎么就攤上這事言询。” “怎么了傲宜?”我有些...
    開封第一講書人閱讀 164,057評論 0 354
  • 文/不壞的土叔 我叫張陵运杭,是天一觀的道長。 經常有香客問我函卒,道長辆憔,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,509評論 1 293
  • 正文 為了忘掉前任报嵌,我火速辦了婚禮虱咧,結果婚禮上,老公的妹妹穿的比我還像新娘锚国。我一直安慰自己腕巡,他們只是感情好,可當我...
    茶點故事閱讀 67,562評論 6 392
  • 文/花漫 我一把揭開白布血筑。 她就那樣靜靜地躺著绘沉,像睡著了一般。 火紅的嫁衣襯著肌膚如雪豺总。 梳的紋絲不亂的頭發(fā)上车伞,一...
    開封第一講書人閱讀 51,443評論 1 302
  • 那天,我揣著相機與錄音喻喳,去河邊找鬼另玖。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的日矫。 我是一名探鬼主播赂弓,決...
    沈念sama閱讀 40,251評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼哪轿!你這毒婦竟也來了盈魁?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 39,129評論 0 276
  • 序言:老撾萬榮一對情侶失蹤窃诉,失蹤者是張志新(化名)和其女友劉穎杨耙,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體飘痛,經...
    沈念sama閱讀 45,561評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡珊膜,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 37,779評論 3 335
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了宣脉。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片车柠。...
    茶點故事閱讀 39,902評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖塑猖,靈堂內的尸體忽然破棺而出竹祷,到底是詐尸還是另有隱情,我是刑警寧澤羊苟,帶...
    沈念sama閱讀 35,621評論 5 345
  • 正文 年R本政府宣布塑陵,位于F島的核電站,受9級特大地震影響蜡励,放射性物質發(fā)生泄漏令花。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,220評論 3 328
  • 文/蒙蒙 一凉倚、第九天 我趴在偏房一處隱蔽的房頂上張望兼都。 院中可真熱鬧,春花似錦稽寒、人聲如沸俯抖。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,838評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽芬萍。三九已至,卻和暖如春搔啊,著一層夾襖步出監(jiān)牢的瞬間柬祠,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,971評論 1 269
  • 我被黑心中介騙來泰國打工负芋, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留漫蛔,地道東北人嗜愈。 一個月前我還...
    沈念sama閱讀 48,025評論 2 370
  • 正文 我出身青樓,卻偏偏與公主長得像莽龟,于是被迫代替她去往敵國和親蠕嫁。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 44,843評論 2 354

推薦閱讀更多精彩內容