【原文】在geth控制台使用如下命令来发起转账交易:
-
personal.unlockAccount(eth.accounts[0]) -
eth.sendTransaction({from:eth.accounts[0],to:eth.accounts[1],value:web3.toWei(200,"ether")})
将执行到txpool.go的validateTx()函数进行交易验证,验证项目包括交易大小、交易Gas、账户余额检查等。
-
func (pool *TxPool) validateTx(tx *types.Transaction, local bool) error { -
// Heuristic limit, reject transactions over 32KB to prevent DOS attacks -
if tx.Size() > 320*1024 {//lzj change from 32 to 320 -
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 -
}
然后在tx_pool.go中的promoteExecutables()函数中进行进一步检查:
-
func (pool *TxPool) promoteExecutables(accounts []common.Address) { -
// 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) -
delete(pool.all, 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) -
delete(pool.all, 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() -
log.Trace("Promoting queued transaction", "hash", hash) -
pool.promoteTx(addr, hash, 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() -
delete(pool.all, hash) -
pool.priced.Removed() -
queuedRateLimitCounter.Inc(1) -
log.Trace("Removed cap-exceeding queued transaction", "hash", hash) -
} -
}.....
其中重要的是pool.promoteTx(addr,hash,tx),从这里进入promoteTx函数:
-
func (pool *TxPool) promoteTx(addr common.Address, hash common.Hash, tx *types.Transaction) { -
// Try to insert the transaction into the pending queue -
if pool.pending[addr] == nil { -
pool.pending[addr] = newTxList(true) -
} -
list := pool.pending[addr] -
inserted, old := list.Add(tx, pool.config.PriceBump) -
if !inserted { -
// An older transaction was better, discard this -
delete(pool.all, hash) -
pool.priced.Removed() -
pendingDiscardCounter.Inc(1) -
return -
} -
// Otherwise discard any previous transaction and mark this -
if old != nil { -
delete(pool.all, old.Hash()) -
pool.priced.Removed() -
pendingReplaceCounter.Inc(1) -
} -
// Failsafe to work around direct pending inserts (tests) -
if pool.all[hash] == nil { -
pool.all[hash] = tx -
pool.priced.Put(tx) -
} -
// Set the potentially new pending nonce and notify any subsystems of the new tx -
pool.beats[addr] = time.Now() -
pool.pendingState.SetNonce(addr, tx.Nonce()+1) -
go pool.txFeed.Send(TxPreEvent{tx}) -
}
promoteTx函数最后用
go pool.txFeed.Send(TxPreEvent{tx})
将交易发布发布到TxPreEvent类型的监听通道上去。接受该交易的通道定义在miner/worker.go中:
-
func newWorker(config *params.ChainConfig, engine consensus.Engine, coinbase common.Address, eth Backend, mux *event.TypeMux) *worker { -
worker := &worker{ -
config: config, -
engine: engine, -
eth: eth, -
mux: mux, -
txCh: make(chan core.TxPreEvent, txChanSize), -
chainHeadCh: make(chan core.ChainHeadEvent, chainHeadChanSize), -
chainSideCh: make(chan core.ChainSideEvent, chainSideChanSize), -
chainDb: eth.ChainDb(), -
recv: make(chan *Result, resultQueueSize), -
chain: eth.BlockChain(), -
proc: eth.BlockChain().Validator(), -
possibleUncles: make(map[common.Hash]*types.Block), -
coinbase: coinbase, -
agents: make(map[Agent]struct{}), -
unconfirmed: newUnconfirmedBlocks(eth.BlockChain(), miningLogAtDepth), -
} -
// Subscribe TxPreEvent for tx pool -
worker.txSub = eth.TxPool().SubscribeTxPreEvent(worker.txCh) -
// Subscribe events for blockchain -
worker.chainHeadSub = eth.BlockChain().SubscribeChainHeadEvent(worker.chainHeadCh) -
worker.chainSideSub = eth.BlockChain().SubscribeChainSideEvent(worker.chainSideCh) -
go worker.update() -
go worker.wait() -
worker.commitNewWork() -
return worker -
}
接受交易的通道是worker.txCh,通过
-
// Subscribe TxPreEvent for tx pool -
worker.txSub = eth.TxPool().SubscribeTxPreEvent(worker.txCh)
来注册对TxPreEvent事件的监听。go-ethereum中的事件监听机制见这篇文章。txCh通道的监听执行函数在worker.go中的update()函数:
-
func (self *worker) update() { -
defer self.txSub.Unsubscribe() -
defer self.chainHeadSub.Unsubscribe() -
defer self.chainSideSub.Unsubscribe() -
for { -
// A real event arrived, process interesting content -
select { -
// Handle ChainHeadEvent -
case <-self.chainHeadCh: -
self.commitNewWork() -
// Handle ChainSideEvent -
case ev := <-self.chainSideCh: -
self.uncleMu.Lock() -
self.possibleUncles[ev.Block.Hash()] = ev.Block -
self.uncleMu.Unlock() -
// Handle TxPreEvent -
case ev := <-self.txCh: -
log.Info("worker self.txch now") -
// Apply transaction to the pending state if we're not mining -
if atomic.LoadInt32(&self.mining) == 0 { -
self.currentMu.Lock() -
acc, _ := types.Sender(self.current.signer, ev.Tx) -
txs := map[common.Address]types.Transactions{acc: {ev.Tx}} -
txset := types.NewTransactionsByPriceAndNonce(self.current.signer, txs) -
self.current.commitTransactions(self.mux, txset, self.chain, self.coinbase) -
self.updateSnapshot() -
self.currentMu.Unlock() -
} else { -
// If we're mining, but nothing is being processed, wake on new transactions -
if self.config.Clique != nil && self.config.Clique.Period == 0 { -
self.commitNewWork() -
} -
} -
// System stopped -
case <-self.txSub.Err(): -
return -
case <-self.chainHeadSub.Err(): -
return -
case <-self.chainSideSub.Err(): -
return -
} -
} -
}
在通过commitTransactions()函数将交易提交给区块链执行。commitTransactions()函数中遍历每一笔交易,然后通过
commitTransaction()函数提交并执行交易:
-
func (env *Work) commitTransaction(tx *types.Transaction, bc *core.BlockChain, coinbase common.Address, gp *core.GasPool) (error, []*types.Log) { -
snap := env.state.Snapshot() -
receipt, _, err := core.ApplyTransaction(env.config, bc, &coinbase, gp, env.state, env.header, tx, &env.header.GasUsed, vm.Config{}) -
if err != nil { -
log.Info("worker commitTransaction err","err",err) -
env.state.RevertToSnapshot(snap) -
return err, nil -
} -
env.txs = append(env.txs, tx) -
env.receipts = append(env.receipts, receipt) -
return nil, receipt.Logs -
}
commitTransaction()函数先创建区块链状态的快照,然后通过core.ApplyTransaction()函数来执行交易,如果执行出错,就又回退交易到快照状态。ApplyTransaction()的核心函数是ApplyMessage()。基本调用流程是:
state_processor.go的ApplyTransaction()-->state_transition.go中的ApplyMessage()-->TransitionDb()-->vm/evm.go中的Call()函数。
本文深入解析了在以太坊平台中发起转账交易的过程,从解锁账户、交易验证、执行交易到最终提交至区块链的详细步骤。文章重点介绍了交易在txpool中的验证流程,包括交易大小、Gas限制、账户余额检查等关键环节,并阐述了如何通过Go-Ethereum的TxPreEvent监听交易,最终应用到区块链的状态更新。
644

被折叠的 条评论
为什么被折叠?



