eth.sendTransaction()操作解析 - Agzs/geth-pbft-study GitHub Wiki
在执行personal.unlockAccount("0x....")后,解锁账户
然后执行eth.sendTransaction({from:"0x..",to:"0x..",value:"...",data:"0x..."})就创建了一笔交易,具体交易是怎么广播出去?怎么传递的?账户是怎么实现资金流转的?
注:纯函数先后调用关系,请看交易纯函数调用
下面从源代码入手解析这一流程:
1、交易产生 -> 提交交易
eth.sendTransaction({from:"0x..",to:"0x..",value:"...",data:"0x..."})
↓↓↓
func (s *PublicTransactionPoolAPI) SendTransaction(ctx context.Context, args SendTxArgs)
↓↓↓
submitTransaction(ctx, s.b, signed)
↓↓↓
func submitTransaction(ctx context.Context, b Backend, tx *types.Transaction)
↓↓↓
b.SendTx(ctx, tx) >> func (b *EthApiBackend) SendTx((ctx, tx) >>
b.eth.txPool.AddLocal(signedTx) >> pool.addTx(tx, !pool.config.NoLocals) >> pool.add(tx, local)
而执行personal.sendTransaction({from:"0x..",to:"0x..",value:"...",data:"0x..."})可以不用解锁账户
personal.sendTransaction({from:"0x..",to:"0x..",value:"...",data:"0x..."})
↓↓↓
func (s *PrivateAccountAPI) SendTransaction(ctx context.Context, args SendTxArgs, passwd string)
↓↓↓
submitTransaction(ctx, s.b, signed)
从这可以看出,产生交易,并且进行签名后,都会调用submitTransaction(ctx, s.b, signed)
2、提交交易 -> 广播交易
接着上面的提交交易的函数
func submitTransaction(ctx context.Context, b Backend, tx *types.Transaction) >> b.SendTx(ctx, tx)
↓↓↓
func (b *EthApiBackend) SendTx(ctx context.Context, signedTx *types.Transaction)
↓↓↓
b.eth.txPool.AddLocal(signedTx)
↓↓↓
pool.addTx(tx, !pool.config.NoLocals)
↓↓↓
func (pool *TxPool) addTx(tx *types.Transaction, local bool)
↓↓↓
pool.promoteExecutables(state, []common.Address{from})
↓↓↓
func (pool *TxPool) promoteExecutables(state *state.StateDB, accounts []common.Address)
↓↓↓
pool.promoteTx(addr, hash, tx)
↓↓↓
go pool.eventMux.Post(TxPreEvent{tx})
↓↓↓
sub.deliver(event) 将event发送到postC中:s.postC <- event,而 postC 又和 readC 等价
↓↓↓
func (s *TypeMuxSubscription) Chan() <-chan *TypeMuxEvent { return s.readC }
Chan()方法在txBroadcastLoop()中被调用,进一步触发BroadcastTx(),然后将TxMsg广播出去
理一下TxMsg消息的广播
ProtocolManager.Start()
↓↓↓
go pm.txBroadcastLoop()
↓↓↓
self.BroadcastTx(event.Tx.Hash(), event.Tx)
↓↓↓
peer.SendTransactions(types.Transactions{tx})
↓↓↓
p2p.Send(p.rw, TxMsg, txs)
3、收到交易
其他节点会收到交易产生者产生的交易,并广播给与其相连接的peer
func (pm *ProtocolManager) handleMsg(p *peer)的case msg.Code == TxMsg分支,执行pm.txpool.AddRemotes(txs)
↓↓↓
func (pool *TxPool) AddRemotes(txs []*types.Transaction)
↓↓↓
pool.addTxs(txs, false)
func (pool *TxPool) addTxs(txs []*types.Transaction, local bool)
↓↓↓
pool.promoteExecutables(state, addrs)
↓↓↓
func (pool *TxPool) promoteExecutables(state *state.StateDB, accounts []common.Address)
↓↓↓
pool.promoteTx(addr, hash, tx)
↓↓↓
go pool.eventMux.Post(TxPreEvent{tx})
↓↓↓
sub.deliver(event) >> s.postC <- event, postC <=> readC
↓↓↓
func (s *TypeMuxSubscription) Chan() <-chan *TypeMuxEvent { return s.readC }
Chan()方法在txBroadcastLoop()中被调用,进一步触发BroadcastTx(),然后将TxMsg广播出去
另外,Chan()方法在worker.update()中被调用,
在TxPreEvent分支,进一步触发"apply transaction to the pending state if we're not mining"相关操作,
其中包含self.current.commitTransactions(self.mux, txset, self.chain, self.coinbase)
Chan()方法在EventSystem.eventLoop()中被调用,进一步触发es.broadcast(index, ev)
4、处理交易
该过程分析采用倒序方式描述
首先,数据库中账户余额操作由ApplyTransaction()操作层层调用实现
func ApplyTransaction(config *params.ChainConfig, bc *BlockChain, author *common.Address, gp *GasPool, statedb *state.StateDB, header *types.Header, tx *types.Transaction, usedGas *big.Int, cfg vm.Config)
↓↓↓
ApplyMessage(vmenv, msg, gp)
↓↓↓
st.TransitionDb()
↓↓↓
evm.Call(sender, st.to().Address(), st.data, st.gas, st.value)
↓↓↓
evm.Transfer(evm.StateDB, caller.Address(), to.Address(), value)
↓↓↓
// Transfer subtracts amount from sender and adds amount to recipient using the given Db
func Transfer(db vm.StateDB, sender, recipient common.Address, amount *big.Int) {
db.SubBalance(sender, amount)
db.AddBalance(recipient, amount)
}
=> db.SubBalance(sender, amount) >> StateDb.SubBalance(sender, amount) >> stateObject.SubBalance(amount) >> c.SetBalance(new(big.Int).Sub(c.Balance(), amount)) >> stateObject.SetBalance(amount) >> stateObject.setBalance(amount)
=> db.AddBalance(recipient, amount) >> StateDB.AddBalance(recipient, amount) >> stateObject.AddBalance(amount) >> c.SetBalance(new(big.Int).Add(c.Balance(), amount)) >> stateObject.SetBalance(amount) >> stateObject.setBalance(amount)
而ApplyTransaction()的调用有以下几条路径:
4.1、InsertChain()切入
func (bc *BlockChain) InsertChain(chain types.Blocks)
↓↓↓
bc.processor.Process(block, state, bc.vmConfig)
↓↓↓
func (p *StateProcessor) Process(block *types.Block, statedb *state.StateDB, cfg vm.Config)
↓↓↓
ApplyTransaction(p.config, p.bc, nil, gp, statedb, header, tx, totalUsedGas, cfg)
4.2、commitTransactions()切入
func (env *Work) commitTransactions(mux *event.TypeMux, txs *types.TransactionsByPriceAndNonce, bc *core.BlockChain, coinbase common.Address)
↓↓↓
env.commitTransaction(tx, bc, coinbase, gp)
↓↓↓
ApplyTransaction(env.config, bc, &coinbase, gp, env.state, env.header, tx, env.header.GasUsed, vm.Config{})
而commitTransactions()在以下几处中被调用,同时在3中Chan()方法的第二种调用中就包含该方法的调用
4.2.1、fullNode切入
fullNode, err := eth.New(ctx, cfg)
↓↓↓
func New(ctx *node.ServiceContext, config *Config) (*Ethereum, error)
↓↓↓
eth.miner = miner.New(eth, eth.chainConfig, eth.EventMux(), eth.engine)
↓↓↓
newWorker(config, engine, common.Address{}, eth, mux)
↓↓↓
go worker.update()
↓↓↓
self.current.commitTransactions(self.mux, txset, self.chain, self.coinbase)
4.2.2、commitNewWork()切入
func (self *worker) commitNewWork()
↓↓↓
work.commitTransactions(self.mux, txs, self.chain, self.coinbase)
而commitNewWork()的被调用又有好多处:
4.2.2.1、fullNode切入
fullNode, err := eth.New(ctx, cfg)
↓↓↓
func New(ctx *node.ServiceContext, config *Config) (*Ethereum, error)
↓↓↓
eth.miner = miner.New(eth, eth.chainConfig, eth.EventMux(), eth.engine)
↓↓↓
newWorker(config, engine, common.Address{}, eth, mux)
↓↓↓
go worker.update() + go worker.wait() + worker.commitNewWork(),顺序执行操作
↓↓↓
self.commitNewWork()
4.2.2.2、miner.start()切入
func (api *PrivateMinerAPI) Start(threads *int)
↓↓↓
api.e.StartMining(true)
↓↓↓
func (s *Ethereum) StartMining(local bool)
↓↓↓
go s.miner.Start(eb)
↓↓↓
func (self *Miner) Start(coinbase common.Address)
↓↓↓
self.worker.commitNewWork()
纯函数先后调用关系,请看交易纯函数调用