以太坊交易的前世今生


今天我要分享的是有关ETH的程序架构,我会深入浅出的解释整个交易跟挖矿的流程,希望大家能够从程序代码大致的架构了解整个项目。

ETH 是一个设计相当精妙的项目,如果说比特币是现代虚拟货币的信仰中心,那个ETH 就是现代虚拟货币的服务中心,他扩展了整个比特币交易系统的设计,近一步地让整个交易扩展到变成服务,进而希望透过服务来解决大家的问题。

现代的网络系统本质上都是一种资讯的交换,然后透过这个交换,形成一种服务,进而解决大家的问题。也就是说任何产生资讯交换的系统都有机会转成有商业价值的服务。

打个比方,当我们在使用uber eats,你透过app 去传递你需要食物的讯息,然后外送员得到这个讯息,他也释放出想要接单的讯息,这个时候你们在网络上达成一个交易,然后你付钱,他将餐点送到你家,整个服务就此完成。这一切媒合完全透过程序来执行。而以太坊恰好就是在这个转变的十字路口上,而透过的就是我们以前有提过的VM 这个技术。

比特币系统其中一个有趣的地方在于,他将交易抽象化成一组动作,让这个动作可以进行传递,就像是一个lambda function,然后这个lambda function的执行环境是客制化的VM ,如此一来,每个节点都可以去验证这个动作。

以太坊将这个lambda function可以做的事情加以扩展,让他从简单的语言变成一个图灵完备的语言,不仅表达能力变成强了,他能够执行更复杂的指令,连带着也将交易的定义变成了一种服务。

共识

目前来说,整个eth 还是使用PoW共识(ETHASH) ,但未来整个方向是朝向PoS 共识前进的交易流程,如果用抵押去取代算力挖矿,可能会让整个挖矿变得更简单,因为相当于存钱有利息,这样的话,很可能会让整个挖矿变得更普及。

执行流程

发起交易

使用web3.js API 对以太坊进行操作,我们可以从

func (s *PrivateAccountAPI) SendTransaction(ctx context.Context, args SendTxArgs, passwd string) (common.Hash, error)

开始看起,这个API 主要就是发起交易,在这个实作里面,我们可以看到他会先拿取解锁后的私钥去作成签名,再组成一个交易,验证为合法交易后,将交易缴交到交易池里面。

交易池

当刚刚的交易被缴交出来后,这些交易如果合法会被放到交易池里面,当放到交易池里面的时候,此时,交易池会对channel 发出一个交易的讯号,然后这个讯号会被挖矿的gorutine 所捕捉,然后会将池子里面的未打包交易取出,然后将其打包起来

func (pool *TxPool) addTxs(txs []*types.Transaction, local, sync bool) []error

挖矿

矿工会根据交易费来决定处理的顺序,基本上,他会将交易费较高的部分排在前面,确保他所拿到的钱是最多的,你可以看到他自定义less 方法,然后使用container heap 传进去后,他会使用这个less 来进行整个heads 的排序,确保交易费高的交易项目在前面。

基本上,在

miner/worker.go

里面我们可以看到整个挖矿的可以分为四个loop 来执行挖矿的动作,分别是mainLoop 、newWorkLoop 、resultLoop、taskLoop,我们这边以mainLoop里面的txsCh 讯号来探讨,如上所述,如果交易被加入上述的交易池,交易池会透过这个通道来发出讯号,此时在这边会收到这个讯号,然后将这个交易打包,然后进行PoW 共识挖矿。

func NewTransactionsByPriceAndNonce(signer Signer, txs map[common.Address]Transactions) *TransactionsByPriceAndNonce

channel/gorutine model

在这边我们特别提这几个channel ,这些程序负责挖矿的处理,从讯号近来,从交易池取出交易打包,到进行ethash 计算,最后以广播送出block 都在这里面。

miner/worker.go 的func (w *worker) mainLoop() 、newWorkLoop()、resultLoop() 、taskLoop() 里面有用到很多channel的应用case req := <-w.newWorkCh: 
case ev := <-w.chainSideCh: 
case ev := <-w.txsCh:

比如说在mainLoop里面,有三个主要的通道:有新的mining 任务、有旁支链的产生、有新的交易进来,当收到这些通道的讯号时,分别去做对应的任务,有兴趣写golang的朋友可以在这边好好的研究一下。

执行交易

接下来我们会以newWorkEvent 为例,来描述执行交易跟打包区块的部分。当程序代码执行到commitTransaction 时,此时会对每一个交易进行状态树的修改,也就是将这些交易执行。

这边我们就会看到EVM 这个virtual machine 的实作,也可以说整个以太坊的精华就在这边,以太坊的架构来说,他可以分成3颗树来储存整个交易的资料,分别是世界状态数、交易树、交易收据树,然后这些资料的root 会储存在那个block 的header 里面,借此让client 可以更轻松的查找。我们现在会看到的就是世界状态树的修改。

大致上的流程是这样的,我们先从commitTransaction 去执行某一段交易,然后在使用ApplyTransaction 去创建EVM 的实体。

func (w *worker) commitTransaction(tx *types.Transaction, coinbase common.Address) ([]*types.Log, error)func ApplyTransaction(config *params.ChainConfig, bc ChainContext,...)

然后在ApplyMessage 里面透过EVM 去执行交易,包含外部账户以及内部帐户的程序,这边的内部帐户也就是智慧合约的帐户,同时间他会计算奖励, 最后在使用IntermediateRoot 去更新整个世界状态树得到root值,这就是后面我们要写入block header 的值。

result, err := ApplyMessage(evm, msg, gp)func (st *StateTransition) TransitionDb() (*ExecutionResult, error)root = statedb.IntermediateRoot(config.IsEIP158(header.Number)).Bytes()

在此我们就暂时更新完整个状态树的暂存档,接着我们回到刚刚miner 的部分,将上面的暂存档写入硬盘。

_, err := w.chain.WriteBlockWithState(block, receipts, logs, task.state, true)

最后再用广播传送给其他节点,如果其他节点也认可的话,最终这个区块就会在链上

w.mux.Post(core.NewMinedBlockEvent{Block: block})

结论:

写了好久终于把这篇文章写完了,我已经尽力去考察github 程序代码,如果有错请海涵,不得不说以太坊的设计比起比特币更加模组化,整体的设计也往整个程序的生态系走起,Dapp 之所以能够兴起不是完全是运气,它的确把合约抽象的很好,也让开发者能够很容易跟公链整合。这次就到这边,我们下次见。

本文链接地址:https://www.wwsww.cn/ytf/7857.html
郑重声明:本文版权归原作者所有,转载文章仅为传播更多信息之目的,如作者信息标记有误,请第一时间联系我们修改或删除,多谢。