日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

以太坊源码学习 -- EVM

發(fā)布時間:2025/3/15 编程问答 35 豆豆
生活随笔 收集整理的這篇文章主要介紹了 以太坊源码学习 -- EVM 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

以太坊源碼學習 – EVM


學習文檔鏈接:here

一、虛擬機外

主要功能:

執(zhí)行前將Transaction類型轉(zhuǎn)化成Message,創(chuàng)建虛擬機(EVM)對象,計算一些Gas消耗,以及執(zhí)行交易完畢后創(chuàng)建收據(jù)(Receipt)對象并返回
  • 1
  • 2

1.1 入口 和 返回值

文件:/core/state_processor.go --- Process()for i, tx := range block.Transactions() {statedb.Prepare(tx.Hash(), block.Hash(), i)receipt, _, err := ApplyTransaction(p.config, p.bc, nil, gp, statedb, header, tx, totalUsedGas, cfg)if err != nil {return nil, nil, nil, err}receipts = append(receipts, receipt)allLogs = append(allLogs, receipt.Logs...) }//將block里面所有的tx逐個遍歷執(zhí)行,ApplyTransaction, 每次執(zhí)行完返回一個收據(jù)(Receipt)對象
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

我們來看下Receipt結(jié)構體:

type Receipt struct {// Consensus fieldsPostState []byte `json:"root"`Failed bool `json:"failed"`CumulativeGasUsed *big.Int `json:"cumulativeGasUsed" gencodec:"required"`Bloom Bloom `json:"logsBloom" gencodec:"required"`Logs []*Log `json:"logs" gencodec:"required"`// Implementation fields (don't reorder!)TxHash common.Hash `json:"transactionHash" gencodec:"required"`ContractAddress common.Address `json:"contractAddress"`GasUsed *big.Int `json:"gasUsed" gencodec:"required"` }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

解釋:

Logs: Log類型的數(shù)組,其中每一個Log對象記錄了Tx中一小步的操作。所以,每一個tx的執(zhí)行結(jié)果,由一個Receipt對象來表示;更詳細的內(nèi)容,由一組Log對象來記錄。這個Log數(shù)組很重要,比如在不同Ethereum節(jié)點(Node)的相互同步過程中,待同步區(qū)塊的Log數(shù)組有助于驗證同步中收到的block是否正確和完整,所以會被單獨同步(傳輸)。PostState: 保存了創(chuàng)建該Receipt對象時,整個Block內(nèi)所有“帳戶”的當時狀態(tài)。Ethereum 里用stateObject來表示一個賬戶Account,這個賬戶可轉(zhuǎn)帳(transfer value), 可執(zhí)行tx, 它的唯一標示符是一個Address類型變量。 這個Receipt.PostState 就是當時所在Block里所有stateObject對象的RLP Hash值。Bloom: Ethereum內(nèi)部實現(xiàn)的一個256bit長Bloom Filter。 Bloom Filter概念定義可見wikipedia,它可用來快速驗證一個新收到的對象是否處于一個已知的大量對象集合之中。這里Receipt的Bloom,被用以驗證某個給定的Log是否處于Receipt已有的Log數(shù)組中。
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

1.2 封裝EVM對象和Message對象

我們來看一下ApplyTransaction():

文件:/core/state_processor.go --- ApplyTransaction()//=====Message對象===== msg, err := tx.AsMessage(types.MakeSigner(config, header.Number)) if err != nil { return nil, nil, err }//=====EVM對象===== context := NewEVMContext(msg, header, bc, author) vmenv := vm.NewEVM(context, statedb, config, cfg)//完成tx的執(zhí)行 _, gas, failed, err := ApplyMessage(vmenv, msg, gp)//創(chuàng)建一個收據(jù)Receipt對象,最后返回該Recetip對象,以及整個tx執(zhí)行過程所消耗Gas數(shù)量。 ...
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

我們來看一下ApplyMessage()

文件:/core/state_transition.go --- ApplyMessage()//發(fā)現(xiàn)調(diào)用了TransitionDb() , _, gasUsed, failed, err := st.TransitionDb()
  • 1
  • 2
  • 3
  • 4

我們來看一下TransitionDb()

文件:/core/state_transition.go --- TransitionDb() //購買gas //計算tx固有gas //EVM執(zhí)行 //計算本次執(zhí)行交易的實際gas消耗 //償退gas //獎勵所屬區(qū)塊的挖掘者
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

二、 虛擬機內(nèi)

包括執(zhí)行轉(zhuǎn)帳,和創(chuàng)建合約并執(zhí)行合約的指令數(shù)組

2.1 EVM結(jié)構體

我們來看一下EVM的結(jié)構體:

文件:/core/vm/evm.gotype EVM struct {Context --攜帶輔助信息:Transaction的信息(GasPrice, GasLimit),Block的信息(Number, Difficulty),以及轉(zhuǎn)帳函數(shù)等StateDB StateDB --為EVM提供statedb的相關操作depth intchainConfig *params.ChainConfigchainRules params.RulesvmConfig Configinterpreter *Interpreter --解釋器,用來解釋執(zhí)行EVM中合約的指令abort int32 }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

2.2 完成轉(zhuǎn)賬

交易的轉(zhuǎn)帳操作由Context對象中的TransferFunc類型函數(shù)來實現(xiàn),類似的函數(shù)類型,還有CanTransferFunc, 和GetHashFunc。
  • 1
  • 2
文件:/core/evm.go --Transfer()db.SubBalance(sender, amount) //轉(zhuǎn)出賬戶減到一定金額以太幣 db.AddBalance(recipient, amount) //轉(zhuǎn)入賬戶增加一定金額以太幣 //注意:轉(zhuǎn)出和轉(zhuǎn)入賬戶的操作不會立即生效,StateDB 并不是真正的數(shù)據(jù)庫,只是一行為類似數(shù)據(jù)庫的結(jié)構體它在內(nèi)部以Trie的數(shù)據(jù)結(jié)構來管理各個基于地址的賬戶,可以理解成一個cache;當該賬戶的信息有變化時,變化先存儲在Trie中。僅當整個Block要被插入到BlockChain時,StateDB 里緩存的所有賬戶的所有改動,才會被真正的提交到底層數(shù)據(jù)庫。
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

2.3 合約的創(chuàng)建、賦值

我們先來看一下contract 結(jié)構體

文件:/core/vm/contract.go type Contract struct {CallerAddress common.Addresscaller ContractRef //轉(zhuǎn)賬轉(zhuǎn)出方地址self ContractRef //轉(zhuǎn)入方地址jumpdests destinations // result of JUMPDEST analysis.Code []byte //指令數(shù)組,其中每一個byte都對應于一個預定義的虛擬機指令CodeHash common.HashCodeAddr *common.AddressInput []byte //數(shù)據(jù)數(shù)組,是指令所操作的數(shù)據(jù)集合Gas uint64value *big.IntArgs []byteDelegateCall bool }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
創(chuàng)建合約: call(),create() -- 二者均在StateProcessor的ApplyTransaction()被調(diào)用以執(zhí)行單個交易,并且都有調(diào)用轉(zhuǎn)帳函數(shù)完成轉(zhuǎn)帳。
  • 1
  • 2

我們來看一下call()

文件:/core/vm/call.go var (to = AccountRef(addr)snapshot = evm.StateDB.Snapshot() ) if !evm.StateDB.Exist(addr) {precompiles := PrecompiledContractsHomesteadif evm.ChainConfig().IsByzantium(evm.BlockNumber) {precompiles = PrecompiledContractsByzantium}if precompiles[addr] == nil && evm.ChainConfig().IsEIP158(evm.BlockNumber) && value.Sign() == 0 {return nil, gas, nil}evm.StateDB.CreateAccount(addr) }//轉(zhuǎn)賬 evm.Transfer(evm.StateDB, caller.Address(), to.Address(), value)//賦值Contract對象 contract := NewContract(caller, to, value, gas) contract.SetCallCode(&addr, evm.StateDB.GetCodeHash(addr), evm.StateDB.GetCode(addr))//調(diào)用run,執(zhí)行該合約的指令 ret, err = run(evm, snapshot, contract, input)if err != nil {evm.StateDB.RevertToSnapshot(snapshot)if err != errExecutionReverted {contract.UseGas(contract.Gas)} } return ret, contract.Gas, err
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34

2.4 預編譯合約

我們來看一下run():

文件:/core/vm/run.go if contract.CodeAddr != nil {precompiles := PrecompiledContractsHomesteadif evm.ChainConfig().IsByzantium(evm.BlockNumber) {precompiles = PrecompiledContractsByzantium}if p := precompiles[*contract.CodeAddr]; p != nil {return RunPrecompiledContract(p, input, contract)} } return evm.interpreter.Run(snapshot, contract, input)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
可見如果待執(zhí)行的Contract對象恰好屬于一組預編譯的合約集合-此時以指令地址CodeAddr為匹配項-那么它可以直接運行;沒有經(jīng)過預編譯的Contract,才會由Interpreter解釋執(zhí)行。這里的"預編譯",可理解為不需要編譯(解釋)指令(Code)。預編譯的合約,其邏輯全部固定且已知,所以執(zhí)行中不再需要Code,僅需Input即可。在代碼實現(xiàn)中,預編譯合約只需實現(xiàn)兩個方法Required()和Run()即可,這兩方法僅需一個入?yún)nput。
  • 1
  • 2
  • 3
  • 4

2.5 解釋器執(zhí)行合約的指令

我們來看一下interpreter.go

可以看到一個Config結(jié)構體

文件:/core/vm/.interpreter.gotype Config struct {Debug boolEnableJit boolForceJit boolTracer TracerNoRecursion boolDisableGasMetering boolEnablePreimageRecording boolJumpTable [256]operation // }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
operation: 每個operation對象正對應一個已定義的虛擬機指令,它所含有的四個函數(shù)變量execute, gasCost, validateStack, memorySize 提供了這個虛擬機指令所代表的所有操作。每個指令長度1byte,Contract對象的成員變量Code類型為[]byte,就是這些虛擬機指令的任意集合。operation對象的函數(shù)操作,主要會用到Stack,Memory, IntPool 這幾個自定義的數(shù)據(jù)結(jié)構。
  • 1
  • 2

然后我們看一下interpreter.run()

文件: 文件:/core/vm/.interpreter.go --run()核心: 逐個byte遍歷入?yún)ontract對象的Code變量,將其解釋為一個已知的operation,然后依次調(diào)用該operation對象的四個函數(shù)operation在操作過程中,會需要幾個數(shù)據(jù)結(jié)構: Stack,實現(xiàn)了標準容器 -棧的行為;Memory,一個字節(jié)數(shù)組,可表示線性排列的任意數(shù)據(jù);還有一個intPool,提供對big.Int數(shù)據(jù)的存儲和讀取。需要特別注意的是LOGn指令操作,它用來創(chuàng)建n個Log對象,這里n最大是4。還記得Log在何時被用到么?每個交易(Transaction,tx)執(zhí)行完成后,會創(chuàng)建一個Receipt對象用來記錄這個交易的執(zhí)行結(jié)果。Receipt攜帶一個Log數(shù)組,用來記錄tx操作過程中的所有變動細節(jié),而這些Log,正是通過合適的LOGn指令-即合約指令數(shù)組(Contract.Code)中的單個byte,在其對應的operation里被創(chuàng)建出來的。每個新創(chuàng)建的Log對象被緩存在StateDB中的相對應的stateObject里,待需要時從StateDB中讀取。
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
版權聲明:本文為博主原創(chuàng)文章,未經(jīng)博主允許不得轉(zhuǎn)載。 http://blog.csdn.net/loy_184548/article/details/78078518

總結(jié)

以上是生活随笔為你收集整理的以太坊源码学习 -- EVM的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。