1. 程式人生 > >以太坊transaction的執行分析

以太坊transaction的執行分析

### eth原始碼交易傳送接收,校驗儲存分析:```建立合約指的是將合約部署到區塊鏈上,這也是通過傳送交易來實現。在建立合約的交易中,to欄位要留空不填,在data欄位中指定合約的二進位制程式碼,from欄位是交易的傳送者也是合約的建立者。執行合約的交易呼叫合約中的方法,需要將交易的to欄位指定為要呼叫的合約的地址,通過data欄位指定要呼叫的方法以及向該方法傳遞的引數。所有對賬戶的變動操作都會先提交到stateDB裡面,這個類似一個行為資料庫,或者是快取,最終執行需要提交到底層的資料庫當中,底層資料庫是levelDB(K,V資料庫)core/interface.go定義了stateDB的介面ProtocolManager主要成員包括:peertSet{}型別成員用來快取相鄰個體列表,peer{}表示網路中的一個遠端個體。通過各種通道(chan)和事件訂閱(subscription)的方式,接收和傳送包括交易和區塊在內的資料更新。當然在應用中,訂閱也往往利用通道來實現事件通知。ProtocolManager用到的這些通道的另一端,可能是其他的個體peer,也可能是系統內單例的資料來源比如txPool,或者是事件訂閱的管理者比如event.Mux。Fetcher型別成員累積所有其他個體傳送來的有關新資料的宣佈訊息,並在自身對照後,安排相應的獲取請求。Downloader型別成員負責所有向相鄰個體主動發起的同步流程。func(pm *ProtocolManager) Start()以上這四段相對獨立的業務流程的邏輯分別是:1.廣播新出現的交易物件。txBroadcastLoop()會在txCh通道的收端持續等待,一旦接收到有關新交易的事件,會立即呼叫BroadcastTx()函式廣播給那些尚無該交易物件的相鄰個體。2.廣播新挖掘出的區塊。minedBroadcastLoop()持續等待本個體的新挖掘出區塊事件,然後立即廣播給需要的相鄰個體。當不再訂閱新挖掘區塊事件時,這個函式才會結束等待並返回。很有意思的是,在收到新挖掘出區塊事件後,minedBroadcastLoop()會連續呼叫兩次BroadcastBlock(),兩次呼叫僅僅一個bool型引數@propagate不一樣,當該引數為true時,會將整個新區塊依次發給相鄰區塊中的一小部分;而當其為false時,僅僅將新區塊的Hash值和Number傳送給所有相鄰列表。3.定時與相鄰個體進行區塊全鏈的強制同步。syncer()首先啟動fetcher成員,然後進入一個無限迴圈,每次迴圈中都會向相鄰peer列表中“最優”的那個peer作一次區塊全鏈同步。發起上述同步的理由分兩種:如果有新登記(加入)的相鄰個體,則在整個peer列表數目大於5時,發起之;如果沒有新peer到達,則以10s為間隔定時的發起之。這裡所謂"最優"指的是peer中所維護區塊鏈的TotalDifficulty(td)最高,由於Td是全鏈中從創世塊到最新頭塊的Difficulty值總和,所以Td值最高就意味著它的區塊鏈是最新的,跟這樣的peer作區塊全鏈同步,顯然改動量是最小的,此即"最優"。4.將新出現的交易物件均勻的同步給相鄰個體。txsyncLoop()主體也是一個無限迴圈,它的邏輯稍微複雜一些:首先有一個數據型別txsync{p, txs},包含peer和tx列表;通道txsyncCh用來接收txsync{}物件;txsyncLoop()每次迴圈時,如果從通道txsyncCh中收到新資料,則將它存入一個本地map[]結構,k為peer.ID,v為txsync{},並將這組tx物件傳送給這個peer;每次向peer傳送tx物件的上限數目100*1024,如果txsync{}物件中有剩餘tx,則該txsync{}物件繼續存入map[]並更新tx數目;如果本次迴圈沒有新到達txsync{},則從map[]結構中隨機找出一個txsync物件,將其中的tx組傳送給相應的peer,重複以上迴圈。以上四段流程就是ProtocolManager向相鄰peer主動發起的通訊過程。儘管上述各函式細節從文字閱讀起來容易模糊,不過最重要的內容還是值得留意下的:本個體(peer)向其他peer主動發起的通訊中,按照資料型別可分兩類:交易tx和區塊block;而按照通訊方式劃分,亦可分為廣播新的單個數據和同步一組同類型資料,這樣簡單的兩兩配對,便可組成上述四段流程。在上文的介紹中,出現了多處有關p2p通訊協議的結構型別,比如eth.peer,p2p.Peer,Server等等。這裡不妨對這些p2p通訊協議族的結構一併作個總解。以太坊中用到的p2p通訊協議族的結構型別,大致可分為三層:第一層處於pkg eth中,可以直接被eth.Ethereum,eth.ProtocolManager等頂層管理模組使用,在型別宣告上也明顯考慮了eth.Ethereum的使用特點。典型的有eth.peer{}, eth.peerSet{},其中peerSet是peer的集合型別,而eth.peer代表了遠端通訊物件和其所有通訊操作,它封裝更底層的p2p.Peer物件以及讀寫通道等。第二層屬於pkg p2p,可認為是泛化的p2p通訊結構,比較典型的結構型別包括代表遠端通訊物件的p2p.Peer{}, 封裝自更底層連線物件的conn{},通訊用通道物件protoRW{}, 以及啟動監聽、處理新加入連線或斷開連線的Server{}。這一層中,各種資料型別的界限比較清晰,儘量不出現揉雜的情況,這也是泛化結構的需求。值得關注的是p2p.Protocol{},它應該是針對上層應用特意開闢的型別,主要作用包括容納應用程式所要求的回撥函式等,並通過p2p.Server{}在新連線建立後,將其傳遞給通訊物件peer。從這個型別所起的作用來看,命名為Protocol還是比較貼切的,儘管不應將其與TCP/IP協議等既有概念混淆。第三層處於golang自帶的網路程式碼包中,也可分為兩部分:第一部分pkg net,包括代表網路連線的<Conn>介面,代表網路地址的<Addr>以及它們的實現類;第二部分pkg syscall,包括更底層的網路相關係統呼叫類等,可視為封裝了網路層(IP)和傳輸層(TCP)協議的系統實現。``````Receiptroot我們剛剛在區塊頭有看到,那他具體包含的是什麼呢?它是一個交易的結果,主要包括了poststate,交易所花費的gas,bloom和logsblockchain無結構化查詢需求,僅hash查詢,key/value資料庫最方便,底層用levelDB儲存,效能好stateDB用來儲存世界狀態Core/state/statedb.go注意:1. StateDB完整記錄Transaction的執行情況; 2. StateDB的重點是StateObjects; 3. StateDB中的 stateObjects,Account的Address為 key,記錄其Balance、nonce、code、codeHash ,以及tire中的 {string:Hash}等資訊;所有的結構湊明朗了,那具體的驗證過程是怎麼樣的呢Core/state_processor.goCore/state_transition.goCore/block_validator.goStateProcessor 1. 呼叫StateTransition,驗證(執行)Transaction; 2. 計算Gas、Recipt、Uncle RewardStateTransition1. 驗證(執行)Transaction;3. 扣除transaction.data.payload計算資料所需要消耗的gas;4. 在vm中執行code(生成contract or 執行contract);vm執 行過程中,其gas會被自動消耗。如果gas不足,vm會自 選退出;5. 將多餘的gas退回到sender.balance中;6. 將消耗的gas換成balance加到當前env.Coinbase()中;BlockValidator1. 驗證UsedGas2. 驗證Bloom3. 驗證receiptSha4. 驗證stateDB.IntermediateRoot/core/vm/evm.go交易的轉帳操作由Context物件中的TransferFunc型別函式來實現,類似的函式型別,還有CanTransferFunc, 和GetHashFunc。core/vm/contract.go合約是evm用來執行指令的結構體入口:/cmd/geth/main.go/main```