區(qū)塊鏈原理與實(shí)踐- 課件 第10章以太坊_第1頁(yè)
區(qū)塊鏈原理與實(shí)踐- 課件 第10章以太坊_第2頁(yè)
區(qū)塊鏈原理與實(shí)踐- 課件 第10章以太坊_第3頁(yè)
區(qū)塊鏈原理與實(shí)踐- 課件 第10章以太坊_第4頁(yè)
區(qū)塊鏈原理與實(shí)踐- 課件 第10章以太坊_第5頁(yè)
已閱讀5頁(yè),還剩116頁(yè)未讀, 繼續(xù)免費(fèi)閱讀

下載本文檔

版權(quán)說(shuō)明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請(qǐng)進(jìn)行舉報(bào)或認(rèn)領(lǐng)

文檔簡(jiǎn)介

以太坊基礎(chǔ)及應(yīng)用開(kāi)發(fā)主講:XXX輔導(dǎo):XXX課程簡(jiǎn)介01.以太坊詳解03.以太坊智能合約開(kāi)發(fā)02.以太坊開(kāi)發(fā)環(huán)境04.應(yīng)用系統(tǒng)開(kāi)發(fā)實(shí)例05.總結(jié)1以太坊詳解一以太坊詳解什么是以太坊?以太坊的本質(zhì)就是一個(gè)基于交易的狀態(tài)機(jī)(transaction-basedstatemachine)。全球就存在一臺(tái)以分布式形式存在的單機(jī),其系統(tǒng)狀態(tài)在不停的改變;這臺(tái)單機(jī)的系統(tǒng)狀態(tài)主要由區(qū)塊鏈組成,區(qū)塊鏈上保存著狀態(tài)和交易;當(dāng)用戶與以太坊交互時(shí),其實(shí)就是在執(zhí)行交易、改變系統(tǒng)狀態(tài)。一以太坊詳解如圖所示,以太坊架構(gòu)分為7層,由下至上依次是存儲(chǔ)層、數(shù)據(jù)層、網(wǎng)絡(luò)層、協(xié)議層、共識(shí)層、合約層、應(yīng)用層:1.以太坊體系結(jié)構(gòu)一以太坊詳解

(1)存儲(chǔ)層主要用于存儲(chǔ)以太坊系統(tǒng)運(yùn)行中的日志數(shù)據(jù)及區(qū)塊鏈元數(shù)據(jù),存儲(chǔ)技術(shù)主要使用文件系統(tǒng)和LevelDB。(2)數(shù)據(jù)層主要用于處理以太坊交易中的各類數(shù)據(jù),如將數(shù)據(jù)打包成區(qū)塊,將區(qū)塊維護(hù)成鏈?zhǔn)浇Y(jié)構(gòu),區(qū)塊中內(nèi)容的加密與哈希計(jì)算,區(qū)塊內(nèi)容的數(shù)字簽名及增加時(shí)間戳印記,將交易數(shù)據(jù)構(gòu)建成Merkle樹(shù),并計(jì)算Merkle樹(shù)根節(jié)點(diǎn)的hash值等。與比特幣的不同之處在于以太坊引入了交易和交易池的概念。交易指的是一個(gè)賬戶向另一個(gè)賬戶發(fā)送被簽名的數(shù)據(jù)包的過(guò)程。而交易池則存放通過(guò)節(jié)點(diǎn)驗(yàn)證的交易,這些交易會(huì)放在礦工挖出的新區(qū)塊里。以太坊的Event(事件)指的是和以太坊虛擬機(jī)提供的日志接口,當(dāng)事件被調(diào)用時(shí),對(duì)應(yīng)的日志信息被保存在日志文件中。

(3)與比特幣一樣,以太坊的系統(tǒng)也是基于P2P網(wǎng)絡(luò)的,在網(wǎng)絡(luò)中每個(gè)節(jié)點(diǎn)既有客戶端角色,又有服務(wù)端角色。1.以太坊體系結(jié)構(gòu)一以太坊詳解

(4)協(xié)議層是以太坊提供的供系統(tǒng)各模塊相互調(diào)用的協(xié)議支持,主要有HTTP、RPC協(xié)議、LES、ETH協(xié)議、Whipser協(xié)議等。以太坊基于HTTPClient實(shí)現(xiàn)了對(duì)HTTP的支持,實(shí)現(xiàn)了GET、POST等HTTP方法。外部程序通過(guò)JSONRPC調(diào)用以太坊的API時(shí)需通過(guò)RPC(遠(yuǎn)程過(guò)程調(diào)用)協(xié)議。Whisper協(xié)議用于DApp間通信。LES的全稱是輕量級(jí)以太坊子協(xié)議(LightEthereumSub-protocol),允許以太坊節(jié)點(diǎn)同步獲取區(qū)塊時(shí)僅下載區(qū)塊的頭部,在需要時(shí)再獲取區(qū)塊的其他部分。(5)共識(shí)層在以太坊系統(tǒng)中有PoW(ProofofWork)和PoS(ProofofStake)兩種共識(shí)算法。(6)合約層分為兩層,底層是EVM(EthereumVirtualMachine,即以太坊虛擬機(jī)),上層的智能合約運(yùn)行在EVM中。智能合約是運(yùn)行在以太坊上的代碼的統(tǒng)稱,一個(gè)智能合約往往包含數(shù)據(jù)和代碼兩部分。智能合約系統(tǒng)將約定或合同代碼化,由特定事件驅(qū)動(dòng)觸發(fā)執(zhí)行。因此,在原理上適用于對(duì)安全性、信任性、長(zhǎng)期性的約定或合同場(chǎng)景。在以太坊系統(tǒng)中,智能合約的默認(rèn)編程語(yǔ)言是Solidity,一般學(xué)過(guò)JavaScript語(yǔ)言的讀者很容易上手Solidity。(7)應(yīng)用層有DApp(DecentralizedApplication,分布式應(yīng)用)、以太坊錢包等多種衍生應(yīng)用,是目前開(kāi)發(fā)者最活躍的一層。1.以太坊體系結(jié)構(gòu)一以太坊詳解2.以太坊工作流程及運(yùn)行原理①以太坊區(qū)塊鏈的范式闡述

以太坊的本質(zhì)就是一個(gè)基于交易的狀態(tài)機(jī)(transaction-basedstatemachine)。在計(jì)算機(jī)科學(xué)中,狀態(tài)機(jī)是指可以讀取一系列的輸入,然后根據(jù)這些輸入,會(huì)轉(zhuǎn)換成一個(gè)新的狀態(tài)出來(lái)的東西,如圖所示。

根據(jù)以太坊的狀態(tài)機(jī),我們從創(chuàng)世紀(jì)狀態(tài)(genesisstate)開(kāi)始。這差不多類似于一片空白的石板,在網(wǎng)絡(luò)中還沒(méi)有任何交易的產(chǎn)生狀態(tài)。當(dāng)交易被執(zhí)行后,這個(gè)創(chuàng)世紀(jì)狀態(tài)就會(huì)轉(zhuǎn)變成最終狀態(tài)。在任何時(shí)刻,這個(gè)最終狀態(tài)都代表著以太坊當(dāng)前的狀態(tài)。一以太坊詳解2.以太坊工作流程及運(yùn)行原理

以太坊的狀態(tài)有數(shù)百萬(wàn)個(gè)交易,這些交易都被打包到以太坊區(qū)塊中。一個(gè)區(qū)塊包含了一系列的交易,每個(gè)區(qū)塊都與它的前一個(gè)區(qū)塊鏈接起來(lái)。

為了讓一個(gè)狀態(tài)轉(zhuǎn)換成下一個(gè)狀態(tài),交易必須是有效的。為了讓一個(gè)交易被認(rèn)為是有效的,它必須要經(jīng)過(guò)一個(gè)驗(yàn)證過(guò)程,此過(guò)程也就是挖礦。挖礦就是一組節(jié)點(diǎn)(即電腦)用它們的計(jì)算資源來(lái)創(chuàng)建一個(gè)包含有效交易的區(qū)塊出來(lái)。任何在網(wǎng)絡(luò)上宣稱自己是礦工的節(jié)點(diǎn)都可以嘗試創(chuàng)建和驗(yàn)證區(qū)塊。世界各地的很多礦工都在同一時(shí)間創(chuàng)建和驗(yàn)證區(qū)塊。每個(gè)礦工在提交一個(gè)區(qū)塊到區(qū)塊鏈上的時(shí)候都會(huì)提供一個(gè)數(shù)學(xué)機(jī)制的“證明”,這個(gè)證明就像一個(gè)保證:如果這個(gè)證明存在,那么這個(gè)區(qū)塊一定是有效的。為了讓一個(gè)區(qū)塊添加到主鏈上,一個(gè)礦工必須要比其他礦工更快的提供出這個(gè)“證明”。通過(guò)礦工提供的一個(gè)數(shù)學(xué)機(jī)制的“證明”來(lái)證實(shí)每個(gè)區(qū)塊的過(guò)程稱之為工作量證明(proofofwork)。證實(shí)了一個(gè)新區(qū)塊的礦工都會(huì)被獎(jiǎng)勵(lì)一定價(jià)值的獎(jiǎng)賞。以太坊使用一種內(nèi)在數(shù)字代幣--以太幣(Ether)作為獎(jiǎng)賞。每次礦工證明了一個(gè)新區(qū)塊,那么就會(huì)產(chǎn)生一個(gè)新的以太幣并被獎(jiǎng)勵(lì)給礦工。一以太坊詳解2.以太坊工作流程及運(yùn)行原理

前面,我們定義了區(qū)塊鏈就是一個(gè)具有共享狀態(tài)的交易單機(jī)。使用這個(gè)定義,我們可以知道正確的當(dāng)前狀態(tài)是一個(gè)全球真相,所有人都必須要接受它。擁有多個(gè)狀態(tài)(或多個(gè)鏈)會(huì)摧毀這個(gè)系統(tǒng),因?yàn)樗谀膫€(gè)是正確狀態(tài)的問(wèn)題上不可能得到統(tǒng)一結(jié)果。如果鏈分叉了,你有可能在一條鏈上擁有10個(gè)幣,一條鏈上擁有20個(gè)幣,另一條鏈上擁有40個(gè)幣。在這種場(chǎng)景下,是沒(méi)有辦法確定哪個(gè)鏈才是最“有效的”。

不論什么時(shí)候只要多個(gè)路徑產(chǎn)生了,一個(gè)“分叉”就會(huì)出現(xiàn)。我們通常都想避免分叉,因?yàn)樗鼈儠?huì)破壞系統(tǒng),強(qiáng)制人們?nèi)ミx擇哪條鏈?zhǔn)撬麄兿嘈诺逆?。一以太坊詳?.以太坊工作流程及運(yùn)行原理為了確定哪個(gè)路徑才是最有效的以及防止多條鏈的產(chǎn)生,以太坊使用了一個(gè)叫做“GHOST協(xié)議”(GHOSTprotocol)的數(shù)學(xué)機(jī)制。簡(jiǎn)單來(lái)說(shuō),GHOST協(xié)議就是讓我們必須選擇一個(gè)在其上完成計(jì)算最多的路徑。一個(gè)方法確定路徑就是使用最近一個(gè)區(qū)塊(葉子區(qū)塊)的區(qū)塊號(hào),區(qū)塊號(hào)代表著當(dāng)前路徑上總的區(qū)塊數(shù)(不包含創(chuàng)世紀(jì)區(qū)塊)。區(qū)塊號(hào)越大,路徑就會(huì)越長(zhǎng),就說(shuō)明越多的挖礦算力被消耗在此路徑上以達(dá)到葉子區(qū)塊。使用這種推理就可以允許我們贊同當(dāng)前狀態(tài)的權(quán)威版本。一以太坊詳解2.以太坊工作流程及運(yùn)行原理②以太坊系統(tǒng)的主要組件以太坊系統(tǒng)的主要組件包括:賬戶、賬戶狀態(tài)、世界狀態(tài)、gas和費(fèi)用、交易、區(qū)塊、交易執(zhí)行、挖礦、PoW等。

(1)賬戶:

以太坊的全球“共享狀態(tài)”是由很多小的對(duì)象(賬戶)組成,這些賬戶通過(guò)消息傳遞框架實(shí)現(xiàn)彼此交互。每個(gè)賬戶都有一個(gè)與之關(guān)聯(lián)的狀態(tài)以及一個(gè)20字節(jié)的地址。以太坊中的地址是160位比特的標(biāo)識(shí)符,用于標(biāo)識(shí)任何賬戶。有兩種類型的賬戶:外部賬戶(由私鑰控制,沒(méi)有與之關(guān)聯(lián)的代碼)和合約賬戶(由合約代碼控制,有與之關(guān)聯(lián)的代碼)。一以太坊詳解2.以太坊工作流程及運(yùn)行原理

理解外部賬戶和合約賬戶之間的根本區(qū)別是非常重要的。通過(guò)創(chuàng)建及使用其私鑰簽名一個(gè)交易,外部賬戶能夠給其他外部賬戶或其他合約賬戶發(fā)送消息。兩個(gè)外部賬戶之間的消息只是簡(jiǎn)單的價(jià)值傳輸。但從外部賬戶發(fā)送到合約賬戶的消息可以激活合約賬戶的代碼,允許它執(zhí)行各種操作(例如,轉(zhuǎn)移代幣、寫入內(nèi)部存儲(chǔ)、發(fā)新幣、執(zhí)行計(jì)算、創(chuàng)建新合約等。)

與外部賬戶不同,合約賬戶無(wú)法自行啟動(dòng)新的交易。相反,合約賬戶僅能夠通過(guò)響應(yīng)它們收到的交易來(lái)觸發(fā)自身的交易,比如從外部賬戶或從其他的合約賬戶的交易來(lái)觸發(fā)。因此,任何以太坊區(qū)塊鏈上發(fā)生的操作始終由外部賬戶所觸發(fā)的交易來(lái)啟動(dòng)。一以太坊詳解2.以太坊工作流程及運(yùn)行原理(2)賬戶狀態(tài):賬戶狀態(tài)由四個(gè)部分組成:1)Nonce隨機(jī)數(shù):如果該賬戶是外部賬戶,這個(gè)數(shù)代表從這個(gè)賬戶地址發(fā)出來(lái)的交易數(shù)。如果該賬戶是合約賬戶,則該nonce是該賬戶創(chuàng)建的合約數(shù),每創(chuàng)建一次會(huì)加1。2)Balance賬戶余額:該地址擁有的Wei數(shù),每個(gè)Ether有10^18Wei。3)StorageRoot:MerklePatricia樹(shù)的根節(jié)點(diǎn)的哈希值。Merkletree對(duì)該賬戶的存儲(chǔ)內(nèi)容的哈希進(jìn)行編碼,默認(rèn)情況下為空。4)CodeHash:該賬戶EVM代碼的哈希。對(duì)于合約賬戶,就是被Hash的代碼并作為codeHash保存。對(duì)于外部賬戶而言,Codehash字段是空字符串的哈希值。一以太坊詳解2.以太坊工作流程及運(yùn)行原理(3)世界狀態(tài):以太坊的全局狀態(tài)就是由賬戶地址和賬戶狀態(tài)的一個(gè)映射組成。這個(gè)映射被保存在一個(gè)叫做MerklePatricia樹(shù)的數(shù)據(jù)結(jié)構(gòu)中。MerkleTree是一種由一系列節(jié)點(diǎn)組成的二叉樹(shù),這些節(jié)點(diǎn)包括:1)在樹(shù)底的包含了源數(shù)據(jù)的大量葉子節(jié)點(diǎn)。2)一系列的中間的節(jié)點(diǎn),這些節(jié)點(diǎn)是兩個(gè)子節(jié)點(diǎn)的Hash值。3)一個(gè)根節(jié)點(diǎn),同樣是兩個(gè)子節(jié)點(diǎn)的Hash值,代表著整棵樹(shù)。一以太坊詳解2.以太坊工作流程及運(yùn)行原理樹(shù)底的數(shù)據(jù)是通過(guò)分開(kāi)我們想要保存到chunks的數(shù)據(jù)產(chǎn)生的,然后將chunks分成buckets,再然后再獲取每個(gè)bucket的hash值并一直重復(fù)直到最后只剩下一個(gè)Hash:根Hash。一以太坊詳解2.以太坊工作流程及運(yùn)行原理這棵樹(shù)要求存在里面的值(value)都有一個(gè)對(duì)應(yīng)的key。從樹(shù)的根節(jié)點(diǎn)開(kāi)始,key會(huì)告訴你順著哪個(gè)子節(jié)點(diǎn)可以獲得對(duì)應(yīng)的值,這個(gè)值存在葉子節(jié)點(diǎn)。在以太坊中,key/value是地址和與地址相關(guān)聯(lián)的賬戶之間狀態(tài)的映射,包括每個(gè)賬戶的balance,nonce,codeHash和storageRoot(storageRoot自己就是一顆樹(shù))。一以太坊詳解2.以太坊工作流程及運(yùn)行原理同樣的樹(shù)結(jié)構(gòu)也用來(lái)存儲(chǔ)交易和收據(jù)。更具體的說(shuō),每個(gè)塊都有一個(gè)頭(header),保存了三個(gè)不同Merkletrie結(jié)構(gòu)的根節(jié)點(diǎn)的Hash,包括:1)狀態(tài)樹(shù)。2)交易樹(shù)。3)收據(jù)樹(shù)。一以太坊詳解2.以太坊工作流程及運(yùn)行原理區(qū)塊鏈?zhǔn)强恳蝗汗?jié)點(diǎn)來(lái)維持的,有兩種節(jié)點(diǎn)類型:全節(jié)點(diǎn)和輕節(jié)點(diǎn)。全節(jié)點(diǎn)通過(guò)下載整條鏈來(lái)進(jìn)行同步,一個(gè)全節(jié)點(diǎn)包含了整個(gè)鏈,從創(chuàng)世紀(jì)塊到當(dāng)前塊,執(zhí)行其中包含的所有交易。通常,礦工會(huì)存儲(chǔ)全節(jié)點(diǎn),因?yàn)樗麄冊(cè)谕诘V過(guò)程中需要全節(jié)點(diǎn)。一個(gè)節(jié)點(diǎn)如果不需要執(zhí)行所有的交易或輕松訪問(wèn)歷史數(shù)據(jù),就沒(méi)必要保存整條鏈,這就是輕節(jié)點(diǎn)概念的來(lái)源。比起下載和存儲(chǔ)整個(gè)鏈以及執(zhí)行其中所有的交易,輕節(jié)點(diǎn)僅僅下載鏈的頭,從創(chuàng)世紀(jì)塊到當(dāng)前塊的頭,不執(zhí)行任何的交易或檢索任何相關(guān)聯(lián)的狀態(tài)。由于輕節(jié)點(diǎn)可以訪問(wèn)塊的頭,而頭中包含了3個(gè)tries的Hash,所有輕節(jié)點(diǎn)依然可以很容易生成和接收關(guān)于交易、事件、余額等可驗(yàn)證的答案,因?yàn)樵贛erkle樹(shù)中hash值是向上傳播的。如果一個(gè)惡意用戶試圖用一個(gè)假交易來(lái)交換Merkle樹(shù)底的交易,這個(gè)會(huì)改變它上面節(jié)點(diǎn)的hash值,而它上面節(jié)點(diǎn)的值的改變也會(huì)導(dǎo)致上上一個(gè)節(jié)點(diǎn)Hash值的改變,以此類推,一直到樹(shù)的根節(jié)點(diǎn)。如圖。一以太坊詳解2.以太坊工作流程及運(yùn)行原理任何節(jié)點(diǎn)想要驗(yàn)證一些數(shù)據(jù)都可以通過(guò)Merkle證明來(lái)進(jìn)行驗(yàn)證,Merkle證明的組成如下:1)一塊需要驗(yàn)證的數(shù)據(jù)。2)樹(shù)的根節(jié)點(diǎn)Hash。3)一個(gè)“分支”(從chunk到根這個(gè)路徑上所有的hash值)。任何可以讀取證明的人都可以驗(yàn)證分支的hash是連貫的,因此給出的塊在樹(shù)中實(shí)際的位置就是在此處。使用MerklePatricia樹(shù)的好處就是該結(jié)構(gòu)的根節(jié)點(diǎn)加密取決于存儲(chǔ)在樹(shù)中的數(shù)據(jù),而且根據(jù)點(diǎn)的hash還可以作為該數(shù)據(jù)的安全標(biāo)識(shí)。由于塊的頭包含了狀態(tài)、交易、收據(jù)樹(shù)的根hash,所有任何節(jié)點(diǎn)都可以驗(yàn)證以太坊的一小部分狀態(tài)而不用保存整個(gè)狀態(tài),而這整個(gè)狀態(tài)的大小可能是非常大的。一以太坊詳解2.以太坊工作流程及運(yùn)行原理(4)Gas和支付:以太坊有一個(gè)非常重要的概念是費(fèi)用的概念。在以太坊網(wǎng)絡(luò)上交易而消耗的計(jì)算都會(huì)產(chǎn)生費(fèi)用,支付的費(fèi)用以“gas”來(lái)計(jì)算。gas是用于衡量特定計(jì)算所需費(fèi)用的單位。gas價(jià)格是你愿意花費(fèi)在每單位gas上的Ether總量,用“gwei”來(lái)衡量?!癢ei”是Ether的最小單位,1018Wei代表1Ether。1gwei是1,000,000,000Wei。一以太坊詳解2.以太坊工作流程及運(yùn)行原理每次交易,交易發(fā)送人(轉(zhuǎn)賬人)都會(huì)設(shè)置gas的limit和gas價(jià)格。gas價(jià)格和gaslimit代表了發(fā)送人愿意為交易支付的最大數(shù)量的Wei。例如,我們假設(shè)發(fā)送人設(shè)置gaslimit是50,000,gas價(jià)格是20gwei。這意味著交易發(fā)送者愿意支付最多50,000*20gwei=1,000,000,000,000,000Wei,也就是0.001Ether用來(lái)執(zhí)行該交易。gaslimit代表了交易發(fā)送人愿意支付的最大費(fèi)用。如果交易發(fā)送人賬戶余額可以覆蓋這個(gè)最大值,就不會(huì)有問(wèn)題。交易結(jié)束時(shí),發(fā)送人會(huì)收到未被使用的gas資金退款,并按最初價(jià)格交易。如果交易發(fā)送人沒(méi)有提供足夠的gas來(lái)執(zhí)行交易,交易會(huì)用光gas,并且該交易無(wú)效。在這種情況下,交易過(guò)程中止,發(fā)生的任何狀態(tài)更改都會(huì)被逆轉(zhuǎn),這樣交易會(huì)結(jié)束,并回到交易前的以太坊狀態(tài)。此外,還會(huì)記錄交易失敗,顯示什么交易試圖發(fā)起并在哪里失敗。同時(shí),既然在用光gas之前,機(jī)器已經(jīng)花費(fèi)了努力進(jìn)行計(jì)算,邏輯上來(lái)說(shuō),這些花費(fèi)的gas不會(huì)再退還給交易發(fā)送人。交易發(fā)送人花費(fèi)的所有g(shù)as資金都被發(fā)送到“受益人”地址,這通常是礦工的地址。既然礦工花費(fèi)努力來(lái)計(jì)算和驗(yàn)證交易,礦工收取gas費(fèi)用作為獎(jiǎng)勵(lì)。通常,交易發(fā)送人愿意支付的gas價(jià)格越高,礦工從交易中獲得價(jià)值越大。因此,礦工也會(huì)選擇價(jià)格高的交易。這樣,礦工自由選擇他們?cè)敢怛?yàn)證的交易。為了引導(dǎo)交易發(fā)送者設(shè)置gas價(jià)格,礦工可以選擇宣傳他們會(huì)執(zhí)行交易的最低gas價(jià)格。一以太坊詳解2.以太坊工作流程及運(yùn)行原理通常,交易發(fā)送人愿意支付的gas價(jià)格越高,礦工從交易中獲得價(jià)值越大。因此,礦工也會(huì)選擇價(jià)格高的交易。這樣,礦工自由選擇他們?cè)敢怛?yàn)證的交易。為了引導(dǎo)交易發(fā)送者設(shè)置gas價(jià)格,礦工可以選擇宣傳他們會(huì)執(zhí)行交易的最低gas價(jià)格。gas不僅用于支付計(jì)算步驟,也用于支付存儲(chǔ)費(fèi)用。存儲(chǔ)所需的總體費(fèi)用跟使用32字節(jié)的最小倍數(shù)成正比。存儲(chǔ)的費(fèi)用有一些細(xì)微差別。比如,既然不斷增加的存儲(chǔ)增大了所有節(jié)點(diǎn)的以太坊狀態(tài)數(shù)據(jù)庫(kù)的大小,那么有動(dòng)機(jī)來(lái)保持小的數(shù)據(jù)存儲(chǔ)量。因此,如果交易具有可以清除存儲(chǔ)中的條目的步驟,則免除執(zhí)行該操作的費(fèi)用,并且為了釋放存儲(chǔ)空間還可以退還費(fèi)用。以太坊是圖靈完備的語(yǔ)言,智能合約引入了循環(huán)語(yǔ)句,這使得以太坊容易受到停頓問(wèn)題的影響,可能會(huì)使程序無(wú)限運(yùn)行下去。如果沒(méi)有費(fèi)用,惡意行為者可以通過(guò)在交易中執(zhí)行無(wú)限循環(huán),而輕易地破壞網(wǎng)絡(luò)。因此,計(jì)費(fèi)機(jī)制可以保護(hù)網(wǎng)絡(luò)免受惡意攻擊。以太坊網(wǎng)絡(luò)上的存儲(chǔ)也有成本,因此存儲(chǔ)也是需要付費(fèi)的。一以太坊詳解2.以太坊工作流程及運(yùn)行原理(5)交易和消息:以太坊是基于交易的狀態(tài)機(jī),發(fā)生在不同賬戶之間的交易推動(dòng)著以太坊的全球狀態(tài)從一個(gè)狀態(tài)轉(zhuǎn)換到另外一個(gè)狀態(tài)。從最根本的意義上來(lái)說(shuō),交易是加密簽名的指令,它由外部賬戶生成,并序列化,然后提交到區(qū)塊鏈上。有兩類交易:消息調(diào)用和合約創(chuàng)建(即創(chuàng)建新的以太坊合約的交易)。一以太坊詳解2.以太坊工作流程及運(yùn)行原理所有類型的交易均包含以下部分:1)Nonce(隨機(jī)數(shù)):交易發(fā)送人發(fā)送的交易數(shù)量的計(jì)數(shù)。(跟比特幣的Nonce概念不同。)2)Gasprice:交易發(fā)送人愿意為執(zhí)行交易所需的每單位gas支付的Wei的數(shù)量。3)Gaslimit:交易發(fā)送人愿意為執(zhí)行交易支付的最大gas數(shù)量。數(shù)量是設(shè)置并預(yù)付的,在任何計(jì)算完成之前確定。4)To:接收人的地址。如是創(chuàng)建合約的交易,合約賬戶地址還不存在,所以使用的是空值。5)Value:從發(fā)送人轉(zhuǎn)移到接收人的Wei總量。在創(chuàng)建合約的交易中,這個(gè)值作為新創(chuàng)建合約賬戶的初始余額。6)v,r,s:用于生成簽名,該簽名可以標(biāo)識(shí)交易的發(fā)送人。7)Init:僅用于創(chuàng)建合約的交易。它是EVM代碼片段,可用來(lái)初始化新的合約賬戶。Init只允許一次,然后被拋棄。首次運(yùn)行init時(shí),它會(huì)返回賬戶代碼的正文,這段代碼與合約賬戶產(chǎn)生永久關(guān)聯(lián)關(guān)系。8)數(shù)據(jù):僅用于消息調(diào)用的可選字段。它是指消息調(diào)用的輸入數(shù)據(jù)(即參數(shù))。比如,如果智能合約充當(dāng)域名注冊(cè)的服務(wù),對(duì)合約的調(diào)用可能需要輸入字段如域名或IP地址。一以太坊詳解2.以太坊工作流程及運(yùn)行原理(6)區(qū)塊:區(qū)塊就是交易的集合,公鏈或者聯(lián)盟鏈將交易打包成區(qū)塊以后會(huì)進(jìn)行持久化存儲(chǔ)。(7)交易執(zhí)行:所有交易的執(zhí)行必須滿足以下條件:1)交易必須是格式正確的RLP?!癛LP”代表“遞歸長(zhǎng)度前綴”,是一種數(shù)據(jù)格式,它用于編碼二進(jìn)制數(shù)據(jù)的嵌套數(shù)組。RLP是以太坊用于序列化對(duì)象的格式。2)有效的交易簽名。3)有效交易nonce。一個(gè)賬戶的nonce是從該賬戶發(fā)送過(guò)來(lái)交易計(jì)數(shù)。為了有效,交易nonce必須等于發(fā)送者賬戶的nonce。一以太坊詳解2.以太坊工作流程及運(yùn)行原理4)交易的gaslimit必須等于或大于交易使用的固有g(shù)as,發(fā)送人的賬戶余額必須有足夠的Ether覆蓋“預(yù)定”gas成本。固有g(shù)as包括:為執(zhí)行交易預(yù)先定義的成本gas;與交易一起發(fā)送的數(shù)據(jù)的gas費(fèi)用(每字節(jié)數(shù)據(jù)或代碼相當(dāng)于零時(shí)則是4gas的費(fèi)用,每非零字節(jié)的數(shù)據(jù)或代碼是68gas費(fèi)用);如果交易是創(chuàng)建合約的交易,則額外增加32000gas?!邦A(yù)定”gas成本包括兩個(gè)方面:最大的gas成本(交易的gaslimit乘以交易的gas價(jià)格);從發(fā)送者轉(zhuǎn)移到接收者的總價(jià)值(總值)。如果交易滿足以上4個(gè)條件,交易才會(huì)繼續(xù)執(zhí)行。在交易執(zhí)行之前,先從發(fā)送人的余額中扣除預(yù)定的執(zhí)行成本,并將發(fā)送人的賬戶的nonce增加1以計(jì)入當(dāng)前的交易。此時(shí),可以算出剩余的gas,它們作為交易的總gaslimit減去使用過(guò)的固有g(shù)as。一以太坊詳解2.以太坊工作流程及運(yùn)行原理接下來(lái)交易開(kāi)始執(zhí)行。在交易的執(zhí)行過(guò)程中,以太坊跟蹤“子狀態(tài)”,該子狀態(tài)是記錄交易過(guò)程中產(chǎn)生的信息的方法,主要包括:1)自毀集:在交易完成后被拋棄的一組賬戶。2)日志系列:虛擬機(jī)代碼執(zhí)行的歸檔及可索引的檢查點(diǎn)。3)退還余額:交易后退還給發(fā)送人賬戶的金額。接下來(lái),處理交易要求的各種計(jì)算。一旦交易要求的所有步驟都被處理完畢,假定沒(méi)有無(wú)效狀態(tài),則通過(guò)確定要退還給發(fā)送人的未使用的gas金額來(lái)實(shí)現(xiàn)最終狀態(tài)。除了未使用的gas,發(fā)送人還可以從上面提到的“退款余額”中獲得一些補(bǔ)貼。一旦發(fā)送人獲得退款:1)gas的Ether已經(jīng)給到礦工。2)交易使用的gas被添加到區(qū)塊gas計(jì)數(shù)器(它跟蹤區(qū)塊中所有交易使用的總gas,并在驗(yàn)證區(qū)塊時(shí)使用)。3)在自毀集中的所有賬戶都將被刪除。最后,留下新的狀態(tài)和一組交易創(chuàng)建的日志。一以太坊詳解2.以太坊工作流程及運(yùn)行原理③運(yùn)行原理(1)合約創(chuàng)建:為了創(chuàng)建新的合約賬戶,首先使用特殊公式聲明新賬戶地址,然后通過(guò)如下方式初始化新賬戶:1)把nonce(隨機(jī)數(shù))設(shè)置為零。2)如果發(fā)送人用交易發(fā)送一定量的Ether作為價(jià)值,則設(shè)置該價(jià)值為賬戶余額。3)從發(fā)送人的余額中扣除發(fā)送給新賬戶的價(jià)值。4)把存儲(chǔ)設(shè)置為空。5)把合約的codeHash設(shè)置為空字符串的哈希。一旦初始化賬戶,使用跟隨交易發(fā)送的init代碼實(shí)際上能夠創(chuàng)建賬戶。在執(zhí)行這個(gè)初始化代碼時(shí)會(huì)產(chǎn)生多樣的變化。根據(jù)合約的構(gòu)造函數(shù),它可能會(huì)升級(jí)賬戶的存儲(chǔ)、創(chuàng)建其他合約賬戶、進(jìn)行其他消息調(diào)用等。一以太坊詳解2.以太坊工作流程及運(yùn)行原理當(dāng)執(zhí)行初始化合約的代碼時(shí),它使用gas。交易不允許使用比剩余gas更多的gas。如果遇到這樣的情況,執(zhí)行導(dǎo)致gas不足的異常,然后退出。如果交易因?yàn)間as不足異常而退出,那么,狀態(tài)會(huì)立刻復(fù)原到交易之前到那個(gè)點(diǎn),而發(fā)送人不會(huì)收到gas退款,該gas已在之前執(zhí)行中用完。但是,如果發(fā)送人用交易發(fā)送任何Ether價(jià)值,即使合約創(chuàng)建失敗,Ether價(jià)值也會(huì)被退回。如果初始化代碼執(zhí)行成功,會(huì)支付最終的成功創(chuàng)建合約的成本。這是存儲(chǔ)成本,支付費(fèi)用跟所創(chuàng)建合約代碼的大小成正比。如果沒(méi)有足夠的剩余gas來(lái)支付最終的成本,交易再次出現(xiàn)gas不足的異常,并中止。如果在交易執(zhí)行過(guò)程中沒(méi)有發(fā)生異常,則剩余的未使用的gas會(huì)退還給交易的最初發(fā)送人,由此被改變的狀態(tài)也允許持續(xù)存在。一以太坊詳解2.以太坊工作流程及運(yùn)行原理(2)消息調(diào)用:消息調(diào)用的執(zhí)行跟合約創(chuàng)建的執(zhí)行類似,不過(guò)存在一些差異。消息調(diào)用執(zhí)行并不包括任何init代碼,因?yàn)闆](méi)有創(chuàng)建新的賬戶。如果數(shù)據(jù)由交易發(fā)送人提供,則它可以包含輸入數(shù)據(jù)。一旦執(zhí)行,消息調(diào)用還有額外的組件,組件包含了輸出數(shù)據(jù),如果接下來(lái)的執(zhí)行需要該數(shù)據(jù),它會(huì)被使用。跟合約創(chuàng)建一樣,如果因?yàn)間as不足或交易無(wú)效(如堆棧溢出、無(wú)效跳轉(zhuǎn)目標(biāo)或無(wú)效指令),消息調(diào)用退出,并且所使用的gas不會(huì)退還給最初的調(diào)用者。相反,所有剩余的未使用的gas被消耗,并且狀態(tài)會(huì)復(fù)原到余額轉(zhuǎn)移之前的點(diǎn)。一以太坊詳解2.以太坊工作流程及運(yùn)行原理(3)執(zhí)行模式:交易是在VM上執(zhí)行的,實(shí)際處理交易流程的部分協(xié)議是以太坊自己的虛擬機(jī),也就是所謂的EVM。EVM是圖靈完備的虛擬機(jī),跟其他圖靈完備的機(jī)器相比,EVM唯一的限制是它跟gas是內(nèi)在綁定的。也就是說(shuō),它的計(jì)算量是受gas量?jī)?nèi)在約束的。EVM有基于堆棧的架構(gòu),堆棧計(jì)算機(jī)使用后進(jìn)先出堆棧來(lái)保存臨時(shí)值。EVM中每個(gè)堆棧項(xiàng)的大小是256位,堆棧最大的大小為1024。EVM有內(nèi)存,其中的item存儲(chǔ)為字尋址字節(jié)數(shù)組。內(nèi)存不穩(wěn)定,意味著它不是持久的。EVM還有存儲(chǔ)空間。跟內(nèi)存不同,存儲(chǔ)是穩(wěn)定的,同時(shí)它作為系統(tǒng)狀態(tài)的一部分進(jìn)行維持。EVM在虛擬ROM中分別存儲(chǔ)程序代碼,這些代碼只能通過(guò)特別指令訪問(wèn)。通過(guò)這種方式,EVM跟經(jīng)典的馮·諾依曼架構(gòu)不同,其中的程序代碼存儲(chǔ)在內(nèi)存或存儲(chǔ)器中。EVM也有它自己的語(yǔ)言:“EVM字節(jié)代碼”。當(dāng)一個(gè)程序員編寫以太坊智能合約時(shí),一般來(lái)說(shuō),會(huì)使用高階語(yǔ)言如Solidity。之后,可以將其編譯為EVM可以理解的EVM字節(jié)代碼。一以太坊詳解3.以太坊區(qū)塊結(jié)構(gòu)及鏈結(jié)構(gòu)區(qū)塊就是交易的集合,公鏈或者聯(lián)盟鏈將交易打包成區(qū)塊以后會(huì)進(jìn)行持久化存儲(chǔ)。如圖是經(jīng)典的以太坊區(qū)塊結(jié)構(gòu),從中可以看到以太坊區(qū)塊結(jié)構(gòu)由兩部分組成,分別是區(qū)塊頭(header)和區(qū)塊體(body)兩部分。一以太坊詳解3.以太坊區(qū)塊結(jié)構(gòu)及鏈結(jié)構(gòu)區(qū)塊頭(header):區(qū)塊頭存儲(chǔ)了區(qū)塊的元信息,用來(lái)對(duì)區(qū)塊內(nèi)容進(jìn)行一些標(biāo)識(shí)、校驗(yàn)、說(shuō)明等。(1)通用字段:ParentHash:父區(qū)塊的哈希值。Root:世界狀態(tài)的哈希,stateDB的RLP編碼后的哈希值。TxHash(transactionroothash):交易字典樹(shù)的根哈希,由本區(qū)塊所有交易的交易哈希算出。ReceptHash:收據(jù)樹(shù)的哈希。Time:區(qū)塊產(chǎn)生出來(lái)的Unix時(shí)間戳。Number:區(qū)塊號(hào)。Bloom:布隆過(guò)濾器,快速定位日志是否在這個(gè)區(qū)塊中。一以太坊詳解3.以太坊區(qū)塊結(jié)構(gòu)及鏈結(jié)構(gòu)(2)公鏈場(chǎng)景:Coinbase:挖出這個(gè)塊的礦工地址,挖出區(qū)塊所獎(jiǎng)勵(lì)的ETH就會(huì)發(fā)放到這個(gè)地址。Difficulty:當(dāng)前工作量證明算法的復(fù)雜度。GasLimit:每個(gè)區(qū)塊Gas的消耗上線。GasUsed:當(dāng)前區(qū)塊所有交易使用的Gas之和。MixDigest:挖礦得到的Pow算法證明的摘要,也就是挖礦的工作量證明。nonce:挖礦找到的滿足條件的值。Uncle:叔塊,和以太坊的共識(shí)算法相關(guān)。一般而言一個(gè)類以太坊的聯(lián)盟鏈?zhǔn)切枰厦娼榻B的通用字段的,但是也不絕對(duì),還可能與選擇的共識(shí)算法,隱私保護(hù)策略,設(shè)計(jì)偏好有關(guān)。區(qū)塊體(Body):區(qū)塊體包括這個(gè)區(qū)塊打包的所有交易,在一些鏈的設(shè)計(jì)中,并不像以太坊區(qū)分header和body,而是整合在一起。(1)區(qū)塊頭存儲(chǔ)以太坊通過(guò)如下方式將區(qū)塊頭轉(zhuǎn)換成鍵值對(duì)存儲(chǔ)在LevelDB中:headerPrefix+num+hash->rlp(header)(2)區(qū)塊體存儲(chǔ)區(qū)塊體的存儲(chǔ)方式也是類似:bodyPrefix+num+hash->rlp(block)最初系統(tǒng)產(chǎn)生一個(gè)空白的區(qū)塊(無(wú)交易信息),每個(gè)新產(chǎn)生的區(qū)塊的ParentHash指向上一塊區(qū)塊,這樣就形成了區(qū)塊鏈。2以太坊開(kāi)發(fā)環(huán)境二以太坊開(kāi)發(fā)環(huán)境當(dāng)開(kāi)發(fā)者使用Solidity語(yǔ)言編寫智能合約時(shí),智能合約實(shí)際上就是由Solidity代碼編譯后的程序,也就是說(shuō),智能合約的編譯環(huán)境就是Solidity的編譯環(huán)境。而這段程序(智能合約)的執(zhí)行環(huán)境就是EVM。在以太坊中,Solidity編寫的智能合約經(jīng)過(guò)編譯后會(huì)生成一串十六進(jìn)制字節(jié)碼,創(chuàng)建后進(jìn)行調(diào)用時(shí),也需要將調(diào)用的函數(shù)(function)名稱和參數(shù)轉(zhuǎn)化成一串十六進(jìn)制字節(jié)碼寫進(jìn)交易中。當(dāng)用戶通過(guò)發(fā)起ethsendTransaction或者ethcall創(chuàng)建或者調(diào)用智能合約時(shí),就要在交易(Transaction)的data字段填入這個(gè)十六進(jìn)制碼。創(chuàng)建智能合約時(shí),EVM會(huì)將這段字節(jié)碼解析成相應(yīng)的指令符序列,存儲(chǔ)到一個(gè)新建的智能合約地址下。當(dāng)用戶調(diào)用這個(gè)智能合約時(shí),以太坊本身會(huì)根據(jù)交易里的to宇段先獲取到這個(gè)智能合約的信息,EVM先根據(jù)data字段里解析出的具體函數(shù)和參數(shù)生成具體的指令,再依次執(zhí)行這些指令得到執(zhí)行結(jié)果,這些操作會(huì)涉及對(duì)賬戶狀態(tài)數(shù)據(jù)進(jìn)行更改。二以太坊開(kāi)發(fā)環(huán)境1.以太坊開(kāi)發(fā)工具及框架流行的以太坊開(kāi)發(fā)框架:(1)Truffle:最為流行的智能合約開(kāi)發(fā)、測(cè)試和部署框架,經(jīng)常與Ganache(也是由Truffle團(tuán)隊(duì)開(kāi)發(fā))一起搭配使用。(2)Embark:和廣泛使用的Truffle類似,Embark也是一個(gè)功能強(qiáng)大的DApp開(kāi)發(fā)框架,幫助開(kāi)發(fā)者快速構(gòu)建和部署DApp。Embark不單可以與以太坊區(qū)塊鏈通信,還集成了IPFS/Swarm去中心化存儲(chǔ)和Whisper網(wǎng)絡(luò)通信功能。(3)Populus:用Python語(yǔ)言寫的智能合約開(kāi)發(fā)框架。(4)Etherlime:基于ethers.js的DApp開(kāi)發(fā)框架。二以太坊開(kāi)發(fā)環(huán)境1.以太坊開(kāi)發(fā)工具及框架集成開(kāi)發(fā)環(huán)境(IDE):(1)Remix:一個(gè)基于solidity語(yǔ)言的在線智能合約開(kāi)發(fā)IDE,它提供從編譯,調(diào)試到部署的全流程支持。(2)AtomAtom:AtomAtom編輯器可以結(jié)合AtomSolidityLinter,Etheratom等插件進(jìn)行智能合約開(kāi)發(fā)。(3)Pragma:一個(gè)非常簡(jiǎn)單的solidity合約在線IDE,提供合約的編譯、部署與調(diào)用支持。(4)SuperblocksStudio:SuperblocksStudio可以幫助你在線編寫、編譯與部署智能合約,目前處于beta版本。(5)Vimsolidity:有了這個(gè),使用vim也可以寫愉快地寫solidity了。(6)VisualStudioCode:VSCode是日常用的最多的工具。(7)IntellijSolidityPlugin:JetBrainsIntelliJIdeaIDE上用的solidity插件,支持語(yǔ)法高亮,格式化與代碼自動(dòng)補(bǔ)全。本文選擇Truffle這個(gè)最為流行的智能合約開(kāi)發(fā)、測(cè)試和部署框架進(jìn)行介紹。本節(jié)介紹用于啟動(dòng)以太坊的以太坊客戶端Geth、編譯智能合約的Solidity編譯器和流行的智能合約開(kāi)發(fā)框架Truffle。二以太坊開(kāi)發(fā)環(huán)境1.以太坊開(kāi)發(fā)工具及框架1.GethGeth是GoEthereum開(kāi)源項(xiàng)目的簡(jiǎn)稱,它是使用Go語(yǔ)言編寫且實(shí)現(xiàn)了Ethereum協(xié)議的客戶端軟件,也是目前用戶最多,使用最廣泛的客戶端。通過(guò)Geth客戶端與以太坊網(wǎng)絡(luò)進(jìn)行連接和交互可以實(shí)現(xiàn)賬戶管理、合約部署、挖礦等眾多有趣且實(shí)用的功能。Geth啟動(dòng)時(shí)需要對(duì)創(chuàng)世區(qū)塊文件和相關(guān)參數(shù)進(jìn)行配置。創(chuàng)世區(qū)塊文件genesis.json是區(qū)塊鏈最重要的識(shí)別標(biāo)志之一,每一條區(qū)塊鏈都有唯一識(shí)別的創(chuàng)世區(qū)塊文件,如果兩臺(tái)機(jī)器啟動(dòng)Geth時(shí)所選用的創(chuàng)世區(qū)塊文件不同,就無(wú)法被識(shí)別為同一條區(qū)塊鏈的成員。因此,同一條聯(lián)盟鏈中的所有節(jié)點(diǎn)必須使用同一份創(chuàng)世區(qū)塊文件進(jìn)行初始化配置。二以太坊開(kāi)發(fā)環(huán)境1.以太坊開(kāi)發(fā)工具及框架下面是創(chuàng)世區(qū)塊文件genesis.json的一個(gè)示例:{"config":{"chainId":11,"homesteadBlock":0,"eip150Block":0,"eip150Hash":"0x0000000000000000000000000000000000000000000000000000000000000000","eip155Block":0,"eip158Block":0,"byzantiumBlock":0,"constantinopleBlock":0,"petersburgBlock":0,"ethash":{}},"nonce":"0x0","timestamp":"0x5d5cdc87","extraData":"0x0000000000000000000000000000000000000000000000000000000000000000","gasLimit":"0x47b760","difficulty":"0x80000","mixHash":"0x0000000000000000000000000000000000000000000000000000000000000000","coinbase":"0x0000000000000000000000000000000000000000","alloc":{"0000000000000000000000000000000000000000":{"balance":"0x1"}},"number":"0x0","gasUsed":"0x0","parentHash":"0x0000000000000000000000000000000000000000000000000000000000000000"}二以太坊開(kāi)發(fā)環(huán)境1.以太坊開(kāi)發(fā)工具及框架根據(jù)配置用途可分為三大類:(1)鏈配置。config項(xiàng)是定義鏈配置,會(huì)影響共識(shí)協(xié)議,雖然鏈配置對(duì)創(chuàng)世影響不大,但新區(qū)塊的出塊規(guī)則均依賴鏈配置。(2)創(chuàng)世區(qū)塊頭信息配置。nonce:隨機(jī)數(shù),對(duì)應(yīng)創(chuàng)世區(qū)塊Nonce字段。timestamp:UTC時(shí)間戳,對(duì)應(yīng)創(chuàng)世區(qū)塊Time字段。extraData:額外數(shù)據(jù),對(duì)應(yīng)創(chuàng)世區(qū)塊Extra字段。gasLimit:必填,燃料上限,對(duì)應(yīng)創(chuàng)世區(qū)塊GasLimit字段。difficulty:必填,難度系數(shù),對(duì)應(yīng)創(chuàng)世區(qū)塊Difficulty字段。搭建私有鏈時(shí),需要根據(jù)情況選擇合適的難度值,以便調(diào)整出塊。minHash:一個(gè)哈希值,對(duì)應(yīng)創(chuàng)世區(qū)塊的MixDigest字段。和nonce值一起證明在區(qū)塊上已經(jīng)進(jìn)行了足夠的計(jì)算。coinbase:一個(gè)地址,對(duì)應(yīng)創(chuàng)世區(qū)塊的Coinbase字段。(3)初始賬戶資產(chǎn)配置。alloc項(xiàng)是創(chuàng)世中初始賬戶資產(chǎn)配置。在生成創(chuàng)世區(qū)塊時(shí),將此數(shù)據(jù)集中的賬戶資產(chǎn)寫入?yún)^(qū)塊中,相當(dāng)于預(yù)挖礦。這對(duì)開(kāi)發(fā)測(cè)試和私有鏈非常好用,不需要挖礦就可以直接為任意多個(gè)賬戶分配資產(chǎn)。二以太坊開(kāi)發(fā)環(huán)境1.以太坊開(kāi)發(fā)工具及框架Geth常用命令:(1)初始化創(chuàng)世區(qū)塊:geth--datadir/path/to/datadirinit/path/to/genesis.json(2)啟動(dòng)私有鏈:geth--identity"TestNode"--rpc--rpcport"8545"--datadir/path/to/datadir--port "30303"--nodiscoverconsole--identity:指定節(jié)點(diǎn)ID。--rpc:表示開(kāi)啟HTTP-RPC服務(wù)。--rpcport:指定HTTP-RPC服務(wù)監(jiān)聽(tīng)端口號(hào)(默認(rèn)為8545)。--datadir:指定區(qū)塊鏈數(shù)據(jù)的存儲(chǔ)位置。--port:指定和其他節(jié)點(diǎn)連接所用的端口號(hào)(默認(rèn)為30303)。--nodiscover:關(guān)閉節(jié)點(diǎn)發(fā)現(xiàn)機(jī)制,防止加入同樣配置的陌生節(jié)點(diǎn)。(3)快速同步模式:geth--fastconsole2>network_sync.log(4)瀏覽日志:tail-fnetwork_sync.log二以太坊開(kāi)發(fā)環(huán)境1.以太坊開(kāi)發(fā)工具及框架(5)賬戶操作:創(chuàng)建賬戶:gethaccountnew查看賬戶:gethaccountlist查看賬戶余額:eth.getBalance(eth.accounts[])解鎖賬戶:personal.unlockAccount(eth.accounts[],<password>)轉(zhuǎn)賬:eth.sendTransaction({from:sender,to:receiver,value:amount,gas:gasAmount})(6)挖礦:設(shè)置挖礦進(jìn)程數(shù)目:geth--mine--minerthreads=4開(kāi)始挖礦:miner.start(8)結(jié)束挖礦:miner.stop()查看挖礦速率:miner.getHashrate()查看區(qū)塊高度:eth.blockNumber查看挖礦賬戶:eth.coinbase設(shè)置挖礦賬戶:miner.setEtherbase(eth.accounts[0])二以太坊開(kāi)發(fā)環(huán)境1.以太坊開(kāi)發(fā)工具及框架(7)檢查連接狀態(tài):檢查是否連接:net.listening連接到的節(jié)點(diǎn)個(gè)數(shù):net.peerCount添加節(jié)點(diǎn):admin.addPeer()查看添加的節(jié)點(diǎn)的信息:admin.peers(8)查看:查看區(qū)塊信息:eth.getBlock(n)交易信息:eth.getTransaction()查看正在等待確認(rèn)的交易:eth.getBlock("pending",true).transactions二以太坊開(kāi)發(fā)環(huán)境1.以太坊開(kāi)發(fā)工具及框架2.Solidity編譯器以太坊官方社區(qū)提供了Solidity語(yǔ)言的編譯開(kāi)發(fā)工具Solidity項(xiàng)目(/ethereum/solidity)。該項(xiàng)目是用C++編寫的,使用者可以根據(jù)自己的操作系統(tǒng)下載相應(yīng)發(fā)布版的二進(jìn)制可執(zhí)行文件。如果想使用最新的版本,可以同步最新的代碼自行編譯生成可執(zhí)行文件。Solidity項(xiàng)目還提供了一個(gè)命令行工具:Solc。Solc不僅提供將Solidity編譯成字節(jié)碼的功能,也提供一些智能合約相關(guān)的信息,比如可以生成函數(shù)的簽名(調(diào)用智能合約各個(gè)函數(shù)時(shí)的依據(jù))、估算每個(gè)函數(shù)消耗的Gas等。下面我們展示一些常用的Sole使用方法。Solc命令行工具基本的使用模板:solc[options][input_file...]其中options可以是各種參數(shù),用于指定輸出的文件的格式和輸出路徑等。二以太坊開(kāi)發(fā)環(huán)境1.以太坊開(kāi)發(fā)工具及框架Solc最常使用的例子如下:solc--bin-otmp/solcoutputcontract.sol其中,--bin是指將Solidity智能合約編譯成十六進(jìn)制字節(jié)碼。執(zhí)行完以上命令后,會(huì)在/tmp/solcoutput目錄下生成一個(gè)以代碼中定義的合約名(非文件名)命名的.bin的文件,里面存著編譯出來(lái)的字節(jié)碼。除了--bin,Solc也支持其他各種相關(guān)格式的輸出:--ast:所有源文件的抽象語(yǔ)法樹(shù)。--ast-json:json格式的抽象語(yǔ)法樹(shù)。--ast-compact-on:壓縮(去空格、空行)過(guò)后的json格式抽象語(yǔ)法樹(shù)。--asm:EVM的匯編語(yǔ)言。--asm-json:json格式的EVM匯編語(yǔ)言。--opcodes:操作碼(和--asm作用類似,區(qū)別在于asm會(huì)有一些對(duì)應(yīng)到源文件的注釋,而opcodes只有操作碼)。--bin:十六進(jìn)制字節(jié)碼。--bin-runtime:運(yùn)行時(shí)部分的十六進(jìn)制碼(沒(méi)有構(gòu)造函數(shù)部分)。二以太坊開(kāi)發(fā)環(huán)境1.以太坊開(kāi)發(fā)工具及框架--clone-bin:克隆合約的十六進(jìn)制字節(jié)碼。--abi:應(yīng)用程序二進(jìn)制接口規(guī)范。--hashes:各個(gè)函數(shù)的簽名(十六進(jìn)制名稱,用于調(diào)用智能合約時(shí)識(shí)別指定的函數(shù))。--userdoc:用戶使用說(shuō)明文檔。--devdoc:開(kāi)發(fā)者文檔。--metadata:編譯源文件的元數(shù)據(jù)(包括編譯器版本、abi、userdoc、devdoc、設(shè)置、源文件hash等,以json格式組織在一起)。例子里的-o是用來(lái)指定輸出文件路徑的。還有一些常用的選項(xiàng):--optimize:編譯字節(jié)碼時(shí)進(jìn)行優(yōu)化。--optimize-runsn(=200)在激活優(yōu)化功能時(shí),為了進(jìn)行優(yōu)化,試執(zhí)行合約的次數(shù)--add-std:添加標(biāo)準(zhǔn)合約。--librarieslibs:指定合約依賴的庫(kù)。--overwrite:在指定目錄里覆蓋已有的輸出文件。--pretty-json:當(dāng)用戶指定輸出的格式為json時(shí),以可讀性更好的形式輸出。--gas:執(zhí)行編譯時(shí)打印出各個(gè)函數(shù)估測(cè)消耗的Gas數(shù)量。二以太坊開(kāi)發(fā)環(huán)境1.以太坊開(kāi)發(fā)工具及框架3.TruffleTruffle(/)是現(xiàn)在比較流行的Solidity智能合約開(kāi)發(fā)框架,功能十分強(qiáng)大,可以幫助開(kāi)發(fā)者迅速搭建起一個(gè)DApp。Truffle具體的特性有:(1)內(nèi)建智能合約編譯、鏈接、部署和二進(jìn)制包管理功能;(2)支持對(duì)智能合約的自動(dòng)測(cè)試;(3)支持自動(dòng)化部署、移植;(4)支持公有鏈和私有鏈,可以輕松部署到不同的網(wǎng)絡(luò)中;(5)支持訪問(wèn)外部包,可以方便地基于NPM和EthPM引入其他智能合約的依賴;(6)可以使用Truffle命令行工具執(zhí)行外部腳本。二以太坊開(kāi)發(fā)環(huán)境2.以太坊開(kāi)發(fā)環(huán)境搭建本節(jié)介紹以太坊客戶端Geth、編程語(yǔ)言Solidity和智能合約開(kāi)發(fā)框架Truffle的安裝。1.Geth安裝Go-ethereum又稱Geth,是當(dāng)前最為成熟的以太坊客戶端,采用Go語(yǔ)言編寫,以下是Geth的安裝部署過(guò)程。(1)在Windows系統(tǒng)中一鍵安裝Geth安裝包下載地址為:/downloads/。下載完成后打開(kāi)并安裝,默認(rèn)路徑為C:\ProgramFiles\Geth。安裝完成后,打開(kāi)命令行,輸入如命令gethhelp顯示如圖所示表示安裝成功。二以太坊開(kāi)發(fā)環(huán)境2.以太坊開(kāi)發(fā)環(huán)境搭建(2)在Linux系統(tǒng)中安裝Geth在Linux系統(tǒng)中,Geth可以從指定源直接安裝,也可以下載源代碼井編譯安裝。下面以Ubuntu16.04為例說(shuō)明其安裝部署過(guò)程。需要注意的是,在Linux環(huán)境下部分命令需要root權(quán)限執(zhí)行,如apt命令等,本章的命令代碼中以“#”開(kāi)頭的命令表示需要root權(quán)限執(zhí)行,以“$”開(kāi)頭表示僅需在普通用戶權(quán)限下執(zhí)行。使用PPA安裝:#apt-getinstall-ysoftware-properties-common#add-apt-repository-yppa:ethereum/ethereum#add-apt-repository-yppa:ethereum/ethereum-dev#apt-getupdate#apt-getinstallethereum編譯完成后的二進(jìn)制文件為./build/bin/geth。二以太坊開(kāi)發(fā)環(huán)境2.以太坊開(kāi)發(fā)環(huán)境搭建2.Solidity安裝(1)Windows系統(tǒng)中安裝Solidity一鍵安裝Nodejs。Nodejs官方長(zhǎng)期支持的版本為8.10.0LTS,其下載地址為/dist/v8.10.0/node-v8.10.0-x64.msi。安裝完成后,在命令行或者終端中使用命令:npminstallsolc進(jìn)行安裝。(2)在Linux系統(tǒng)中安裝Solidity使用NPM進(jìn)行安裝:使用命令#apt-getinstallnodejs安裝nodejs。安裝完成后使用命令npminstallsolc進(jìn)行安裝,再按如下方法進(jìn)行源代碼編譯。首先使用如下命令加入相關(guān)的包依賴:#sudoapt-get-yupdate#sudoapt-get-yinstalllanguage-pack-en-base#sudodpkg-reconfigurelocales#sudoapt-get-yinstallsoftware-properties-common#sudoadd-apt-repository-yppa:ethereum/ethereum#sudoadd-apt-repository-yppa:ethereum/ethereum-dev#sudoapt-get-yupdate#sudoapt-get-yupgrade二以太坊開(kāi)發(fā)環(huán)境2.以太坊開(kāi)發(fā)環(huán)境搭建然后使用如下命令下載并編譯Solidity:#gitclone--recursive/ethereum/webthree-umbrella.git#cdwebthree-umbrella#更新Solidity庫(kù)#./webthree-helpers/scripts/ethupdate.sh--no-push--simple-pull--projectsolidity#編譯Solidity#./webthree-helpers/scripts/ethbuild.sh--no-git--projectsolidity--all--cores4-DEVMJIT=0二以太坊開(kāi)發(fā)環(huán)境2.以太坊開(kāi)發(fā)環(huán)境搭建3.安裝Truffle(1)Windows系統(tǒng)中安裝Truffle在命令行或者終端中使用命令:#npminstall--global--productionwindows-build-tools安裝依賴,安裝完成后使用#npminstall-gtruffle安裝Truffle。(2)Linux系統(tǒng)中安裝Truffle在命令行或者終端中使用命令:#npminstall-gtruffle。3以太坊智能合約開(kāi)發(fā)三以太坊智能合約開(kāi)發(fā)1.智能合約運(yùn)行環(huán)境

以太坊虛擬機(jī)(EVM)是以太坊中智能合約的運(yùn)行環(huán)境,它不僅被沙箱封裝起來(lái),事實(shí)上它被完全隔離運(yùn)行,也就是說(shuō)運(yùn)行在EVM內(nèi)部的代碼不能接觸到網(wǎng)絡(luò)、文件系統(tǒng)或者其它進(jìn)程,甚至智能合約之間也只有有限的調(diào)用。1.業(yè)務(wù)流程以太坊虛擬機(jī),簡(jiǎn)稱EVM,是用來(lái)執(zhí)行以太坊上的交易的。業(yè)務(wù)流程如圖所示:輸入一筆交易,內(nèi)部會(huì)轉(zhuǎn)換成一個(gè)Message對(duì)象,傳入EVM執(zhí)行。如果是一筆普通轉(zhuǎn)賬交易,那么直接修改StateDB中對(duì)應(yīng)的賬戶余額即可。如果是智能合約的創(chuàng)建或者調(diào)用,則通過(guò)EVM中的解釋器加載和執(zhí)行字節(jié)碼,執(zhí)行過(guò)程中可能會(huì)查詢或者修改StateDB。三以太坊智能合約開(kāi)發(fā)1.智能合約運(yùn)行環(huán)境2.固定油費(fèi)(IntrinsicGas)

對(duì)于每筆交易,先需要收取一筆固定油費(fèi),計(jì)算方法如下:如果交易不帶額外數(shù)據(jù)(Payload),比如普通轉(zhuǎn)賬,那么需要收取21000的油費(fèi)。如果交易攜帶額外數(shù)據(jù),那么額外攜帶的數(shù)據(jù)也是需要收費(fèi)的。具體來(lái)說(shuō)是按字節(jié)收費(fèi):字節(jié)為0的收4塊,字節(jié)不為0收68塊,所以會(huì)看到很多做合約優(yōu)化的,目的就是減少數(shù)據(jù)中不為0的字節(jié)數(shù)量,從而降低油費(fèi)gas消耗。三以太坊智能合約開(kāi)發(fā)1.智能合約運(yùn)行環(huán)境3.生成Contract對(duì)象

交易會(huì)被轉(zhuǎn)換成一個(gè)Message對(duì)象傳入EVM,而EVM則會(huì)根據(jù)Message生成一個(gè)Contract對(duì)象以便后續(xù)執(zhí)行:可以看到,Contract中會(huì)根據(jù)合約地址,從StateDB中加載對(duì)應(yīng)的代碼,然后送入解釋器執(zhí)行。執(zhí)行合約能夠消耗的油費(fèi)有一個(gè)上限,就是節(jié)點(diǎn)配置的每個(gè)區(qū)塊能夠容納的GasLimit。三以太坊智能合約開(kāi)發(fā)1.智能合約運(yùn)行環(huán)境4.送入解釋器執(zhí)行代碼跟輸入都有了,就可以送入解釋器執(zhí)行了。EVM是基于棧的虛擬機(jī),解釋器中需要操作四大組件:PC:類似于CPU中的PC寄存器,指向當(dāng)前執(zhí)行的指令。Stack:執(zhí)行堆棧,位寬為256bits,最大深度為1024。Memory:內(nèi)存空間。Gas:油費(fèi)池,耗光油費(fèi)則交易執(zhí)行失敗。三以太坊智能合約開(kāi)發(fā)1.智能合約運(yùn)行環(huán)境具體解釋執(zhí)行的流程參見(jiàn):EVM的每條指令稱為一個(gè)OpCode,占用一個(gè)字節(jié),所以指令集最多不超過(guò)256,具體描述參見(jiàn):https://ethervm.io。首先PC會(huì)從合約代碼中讀取一個(gè)OpCode,然后從一個(gè)JumpTable中檢索出對(duì)應(yīng)的operation,也就是與其相關(guān)聯(lián)的函數(shù)集合。接下來(lái)會(huì)計(jì)算該操作需要消耗的油費(fèi),如果油費(fèi)耗光則執(zhí)行失敗,返回ErrOutOfGas錯(cuò)誤。如果油費(fèi)充足,則調(diào)用execute()執(zhí)行該指令,根據(jù)指令類型的不同,會(huì)分別對(duì)Stack、Memory或者StateDB進(jìn)行讀寫操作。三以太坊智能合約開(kāi)發(fā)1.智能合約運(yùn)行環(huán)境5.調(diào)用合約函數(shù)

前面分析完了EVM解釋執(zhí)行的主要流程,那么EVM怎么知道交易想調(diào)用的是合約里的哪個(gè)函數(shù)呢?前面提到跟合約代碼一起送到解釋器里的還有一個(gè)Input,而這個(gè)Input數(shù)據(jù)是由交易提供的。三以太坊智能合約開(kāi)發(fā)1.智能合約運(yùn)行環(huán)境Input數(shù)據(jù)通常分為兩個(gè)部分:前面4個(gè)字節(jié)被稱為“4-bytesignature”,是某個(gè)函數(shù)簽名的Keccak哈希值的前4個(gè)字節(jié),作為該函數(shù)的唯一標(biāo)識(shí)。后面跟的就是調(diào)用該函數(shù)需要提供的參數(shù)了,長(zhǎng)度不定。例如:在部署完A合約后,調(diào)用add(1)對(duì)應(yīng)的Input數(shù)據(jù)是:0x87db03b70000000000000000000000000000000000000000000000000000000000000001。而在編譯智能合約的時(shí)候,編譯器會(huì)自動(dòng)在生成的字節(jié)碼的最前面增加一段函數(shù)選擇邏輯:首先通過(guò)CALLDATALOAD(把輸入數(shù)據(jù)加載到Stack中)指令將“4-bytesignature”壓入堆棧中,然后依次跟該合約中包含的函數(shù)進(jìn)行比對(duì),如果匹配則調(diào)用JUMPI指令跳入該段代碼繼續(xù)執(zhí)行。三以太坊智能合約開(kāi)發(fā)1.智能合約運(yùn)行環(huán)境6.合約調(diào)用合約合約內(nèi)部調(diào)用另外一個(gè)合約,有4種調(diào)用方式:1)CALL。2)CALLCODE。3)DELEGATECALL。4)STATICALL。這里先以最簡(jiǎn)單的CALL為例,調(diào)用流程如下圖所示:三以太坊智能合約開(kāi)發(fā)1.智能合約運(yùn)行環(huán)境6.合約調(diào)用合約合約內(nèi)部調(diào)用另外一個(gè)合約,有4種調(diào)用方式:1)CALL。2)CALLCODE。3)DELEGATECALL。4)STATICALL。三以太坊智能合約開(kāi)發(fā)1.智能合約運(yùn)行環(huán)境這里先以最簡(jiǎn)單的CALL為例,調(diào)用流程如下圖所示:可以看到,調(diào)用者把調(diào)用參數(shù)存儲(chǔ)在內(nèi)存中,然后執(zhí)行CALL指令。CALL指令執(zhí)行時(shí)會(huì)創(chuàng)建新的Contract對(duì)象,并以內(nèi)存中的調(diào)用參數(shù)作為其Input。解釋器會(huì)為新合約的執(zhí)行創(chuàng)建新的Stack和Memory,從而不會(huì)破環(huán)原合約的執(zhí)行環(huán)境。新合約執(zhí)行完成后,通過(guò)RETURN指令把執(zhí)行結(jié)果寫入之前指定的內(nèi)存地址,然后原合約繼續(xù)向后執(zhí)行。三以太坊智能合約開(kāi)發(fā)1.智能合約運(yùn)行環(huán)境7.創(chuàng)建合約如果某一筆交易的to地址為nil,則表明該交易是用于創(chuàng)建智能合約的。首先需要?jiǎng)?chuàng)建合約地址,采用下面的計(jì)算公式:Keccak(RLP(call_addr,nonce))[:12]。也就是說(shuō),對(duì)交易發(fā)起人的地址和nonce進(jìn)行RLP編碼,再算出Keccak哈希值,取后20個(gè)字節(jié)作為該合約的地址。下一步就是根據(jù)合約地址創(chuàng)建對(duì)應(yīng)的stateObject,然后存儲(chǔ)交易中包含的合約代碼。該合約的所有狀態(tài)變化會(huì)存儲(chǔ)在一個(gè)storagetrie中,最終以Key-Value的形式存儲(chǔ)到StateDB中。代碼一經(jīng)存儲(chǔ)則無(wú)法改變,而storagetrie中的內(nèi)容則是可以通過(guò)調(diào)用合約進(jìn)行修改的,比如通過(guò)SSTORE指令。三以太坊智能合約開(kāi)發(fā)2.智能合約開(kāi)發(fā)語(yǔ)言1結(jié)構(gòu)Solidity中的合約與面向?qū)ο缶幊陶Z(yǔ)言中的類(Class)很相似,在一個(gè)合約中可以聲明多種成員,包括狀態(tài)變量、函數(shù)、函數(shù)修改器、事件等。同時(shí),一個(gè)合約可以繼承另一個(gè)合約。本節(jié)將簡(jiǎn)單介紹各種成員的形式和作用。狀態(tài)變量是永久存儲(chǔ)在合約賬戶存儲(chǔ)中的值,用于保存合約的狀態(tài)。Solidity語(yǔ)言提供了多種類型的變量,下面的代碼在合約中聲明了一個(gè)無(wú)符號(hào)整數(shù)類型的狀態(tài)變量:contractSimpleStorage{uintsomeData;//狀態(tài)變量}函數(shù)是合約代碼的執(zhí)行單位,一個(gè)合約中可能包含許許多多提供各種功能的函數(shù),它們相互調(diào)用,共同組成合約的工作邏輯。合約中還有一些特殊的函數(shù),比如合約創(chuàng)建時(shí)執(zhí)行的構(gòu)造函數(shù)、想要調(diào)用一個(gè)不存在的函數(shù)時(shí)自動(dòng)執(zhí)行的fallback函數(shù)等。下面的代碼在合約中聲明了一個(gè)不做任何操作的函數(shù):contractSimpleAction{functiondoNothing(){//函數(shù)}}三以太坊智能合約開(kāi)發(fā)2.智能合約開(kāi)發(fā)語(yǔ)言函數(shù)修改器可用于改變函數(shù)的行為,在函數(shù)執(zhí)行前或執(zhí)行后插入其他邏輯,比如在函數(shù)執(zhí)行前進(jìn)行參數(shù)檢查等。下面的代碼演示了如何使用一個(gè)函數(shù)修改器確保一個(gè)函數(shù)只能被合約的創(chuàng)建者調(diào)用:contractSimpleContract{addresspubliccreater;functionSimpleContract(){creater=msg.sender;//構(gòu)造函數(shù)中記錄合約創(chuàng)建者}modifieronlyCreater(){//函數(shù)修改器require(msg.sender==creater);_//使用下劃線指代原函數(shù)代碼}functionabort()onlyCreater{//使用函數(shù)修改器}}三以太坊智能合約開(kāi)發(fā)2.智能合約開(kāi)發(fā)語(yǔ)言事件是以太坊日志協(xié)議的高層次抽象,用于記錄合約執(zhí)行過(guò)程中發(fā)生的各種事件和狀態(tài)變化。在下面的代碼中,當(dāng)donate函數(shù)被調(diào)用時(shí)會(huì)自動(dòng)記錄調(diào)用者的地址和以太幣數(shù)量,以供將來(lái)查看:contractFunding{eventDeposit(addressfrom,uintamount);//事件functiondonate()payable{Deposit(msg.sender,msg.value);//觸發(fā)事件}}三以太坊智能合約開(kāi)發(fā)2.智能合約開(kāi)發(fā)語(yǔ)言2變量類型在計(jì)算機(jī)程序中需要使用變量來(lái)存儲(chǔ)值。值有多種類型,比如整數(shù)、小數(shù)、字符串等,不同類型的值需要存儲(chǔ)在不同類型的變量中。Solidity是一門靜態(tài)類型語(yǔ)言,每一個(gè)變量都必須指定變量的類型,否則無(wú)法正確編譯。Solidity提供了一些基礎(chǔ)的變量類型,可以使用這些基礎(chǔ)類型組合形成復(fù)雜的類型。變量類型根據(jù)參數(shù)傳遞方式的不同可以分為兩類:值類型和引用類型。值類型在每次賦值或者作為參數(shù)傳遞時(shí)都會(huì)創(chuàng)建一份拷貝,引用類型則有兩種存儲(chǔ)地點(diǎn),即賬戶存儲(chǔ)和內(nèi)存。狀態(tài)變量與部分類型的局部變量(數(shù)組、結(jié)構(gòu)體等復(fù)雜類型)是默認(rèn)保存在賬戶存儲(chǔ)中的,而函數(shù)的參數(shù)和其他簡(jiǎn)單類型的局部變量則保存在內(nèi)存中。必要時(shí)還可以在聲明變量時(shí)加上memory或者storage修飾詞來(lái)強(qiáng)制限定變量的存儲(chǔ)地點(diǎn)。數(shù)據(jù)的存儲(chǔ)地點(diǎn)非常重要,引用類型在不同的存儲(chǔ)位置賦值,其產(chǎn)生的結(jié)果完全不同。值類型包括布爾類型、整數(shù)類型、地址類型、固定長(zhǎng)度字節(jié)數(shù)組等,引用類型包括數(shù)組、結(jié)構(gòu)體等。三以太坊智能合約開(kāi)發(fā)2.智能合約開(kāi)發(fā)語(yǔ)言1.值類型(1)布爾類型布爾類型(bool)可能的取值是常量true和false。支持!(邏輯非)、&&(邏輯與)、||(邏輯或)、==(等于)、!=(不等于)等運(yùn)算符。(2)整數(shù)類型int表示有符號(hào)整數(shù),uint表示元符號(hào)整數(shù)。變量支持通過(guò)后綴指明變量使用多少位進(jìn)行存儲(chǔ),后綴必須是8~256范圍內(nèi)8的整數(shù)倍,比如int8、intl6、int256。如果沒(méi)有顯式指明后綴,int默認(rèn)表示int256,uint默認(rèn)表示uint256。(3)枚舉類型枚舉類型(enums)是一種用戶自定義的類型,用于聲明一些命名的常數(shù)。下面的代碼演示了如何聲明和使用枚舉類型。枚舉類型可以與整數(shù)類型之間顯式地進(jìn)行類型轉(zhuǎn)換,但是不能自動(dòng)進(jìn)行隱式轉(zhuǎn)換。枚舉類型的成員默認(rèn)從0開(kāi)始,依次遞增,在下面的例子中DEFAULT、ONE、TWO分別對(duì)應(yīng)整數(shù)0、1、2。contractSirnpleEnurn{enurnSorneEnurn{DEFAULT,ONE,TWO};//聲明一個(gè)枚舉類型}三以太坊智能合約開(kāi)發(fā)2.智能合約開(kāi)發(fā)語(yǔ)言(4)地址類型地址類型(address)的長(zhǎng)度為20字節(jié)(與以太坊賬戶地址長(zhǎng)度一致),其是合約的基類,擁有-些成員方法和變量。從Solidity0.5.0版本開(kāi)始,合約不再繼承自地址類型,但是開(kāi)發(fā)者仍可以通過(guò)顯式類型轉(zhuǎn)換將合約轉(zhuǎn)換為地址類型。<address>.balance:類型為uint,表示賬戶的余額,單位是wei。<address>.transfer(uint256amount):發(fā)送amount數(shù)量的以太幣給address表示的賬戶,單位是wei,失敗會(huì)拋出異常。<address>.send(uint256amount)returns(bool):與<address>.transfer類似,同樣是進(jìn)行以太幣的轉(zhuǎn)賬。兩者的區(qū)別是如果執(zhí)行失?。糰ddress>.transfer會(huì)拋出異常并且終止代碼,<address>.send則是返回false,代碼繼續(xù)執(zhí)行。需要注意的是,以太幣的轉(zhuǎn)賬會(huì)有失敗的風(fēng)險(xiǎn),為了確保以太幣轉(zhuǎn)賬的安全,一定要檢查<address>.send的返回值,或者使用<address>.transfer。三以太坊智能合約開(kāi)發(fā)2.智能合約開(kāi)發(fā)語(yǔ)言<address>.call(...)returns(bool):發(fā)起底層的CALL指令,失敗將返回false。<address>.callcode(...)returns(bool):發(fā)起底層的CALLCODE指令,失敗將返回false。<address>.delegatecall(...)returns(bool):發(fā)起底層的DELETECALL指令,失敗將返回false。以上三個(gè)函數(shù)提供了一個(gè)底層的、靈活的方式與合約進(jìn)行交互,<address>.call(...)可以接受任何長(zhǎng)度、任何類型的參數(shù),每個(gè)參數(shù)將被填充到32字節(jié)并拼接在一起。但有一種例外情況,當(dāng)?shù)谝粋€(gè)參數(shù)的長(zhǎng)度恰好是4字節(jié)時(shí),該參數(shù)不會(huì)被打包成32字節(jié),而是被作為指定函數(shù)的簽名。在下面的代碼中,第一個(gè)參數(shù)bytes4(keccak256("fun(uint256)"))為長(zhǎng)度4字節(jié)的函數(shù)簽名,表示調(diào)用一個(gè)函數(shù)簽名為fun(uint256)的函數(shù),4則是實(shí)際傳給fun函數(shù)的參數(shù):addressnameReg=0x72ba7d8e73fe8eb666ea66babc8116a4lbfb10e2;nameReg.call(bytes4(keccak256("fun(uint256)")),4);函數(shù)簽名使用基本類型的典型格式定義,如果有多個(gè)參數(shù)要使用逗號(hào)隔開(kāi),并且要去掉表達(dá)式中的所有空格。<address>.delegatecall與<address>.call的區(qū)別是調(diào)用delegatecall時(shí)僅執(zhí)行代碼,而諸如賬戶存儲(chǔ)、余額等其他方面都是用當(dāng)前合約的數(shù)據(jù),這是為了使用另一個(gè)合約中的庫(kù)代碼。為了代碼能夠順利執(zhí)行,調(diào)用者必須確保本合約中的存儲(chǔ)變量與delegatecall執(zhí)行的代碼相兼容。<address>.callcode是早期使用的接口,比起call擁有更低的權(quán)限,無(wú)法訪問(wèn)msg.sender、msg.value等變量。以上三個(gè)函數(shù)是非常底層的函數(shù)調(diào)用。在實(shí)際情況中建議開(kāi)發(fā)者還是盡量不要使用,因?yàn)樗鼈兤茐牧薙olidity語(yǔ)言的類型安全。三以太坊智能合約開(kāi)發(fā)2.智能合約開(kāi)發(fā)語(yǔ)言2.引用類型(1)數(shù)組Solidity中的數(shù)組包括固定長(zhǎng)度的數(shù)組,以及運(yùn)行時(shí)可動(dòng)態(tài)改變長(zhǎng)度的動(dòng)態(tài)數(shù)組。對(duì)于賬戶存儲(chǔ)中的數(shù)組,數(shù)組元素可以是任何類型,而內(nèi)存中的數(shù)組,其數(shù)組元素不可以是映射。一個(gè)包含固定k個(gè)T類型數(shù)據(jù)的數(shù)組可以用T[k]語(yǔ)句來(lái)聲明,一個(gè)動(dòng)態(tài)長(zhǎng)度的數(shù)組用T[]來(lái)聲明。下面來(lái)了解數(shù)組的成員變量和函數(shù)。1)length:數(shù)組可以通過(guò)訪問(wèn)length成員獲取數(shù)組的長(zhǎng)度。對(duì)于賬戶存儲(chǔ)中的數(shù)組,可以通過(guò)修改數(shù)組的length成員動(dòng)態(tài)地改變數(shù)組的長(zhǎng)度,而內(nèi)存中的數(shù)組在創(chuàng)建之后,其length成員就已經(jīng)完全確定了,無(wú)法修改。2)push:賬戶存儲(chǔ)中的動(dòng)態(tài)數(shù)組以及bytes類型的變量,可以通過(guò)調(diào)用push方法在數(shù)組尾部添加元素,返回值為數(shù)組新的長(zhǎng)度。3)bytes和string:一種特殊的數(shù)組。bytes通常用于表示任意長(zhǎng)度的字節(jié)數(shù)據(jù),而string用于表示任意長(zhǎng)度的字符數(shù)據(jù)(UTF-8編碼)。但是string不支持length成員和下標(biāo)訪問(wèn)。兩者之間可以互相轉(zhuǎn)換,比如bytes(s)可以將字符串s轉(zhuǎn)換成bytes類型。但是需要注意字符串中的字符是以UTF-8編碼保存的,轉(zhuǎn)換成bytes類型時(shí)會(huì)將多字節(jié)的字符展開(kāi),此時(shí)如果使用下標(biāo)的方式訪問(wèn)bytes,有可能只訪問(wèn)到一個(gè)字符的部分編碼。如果想要訪問(wèn)一個(gè)字符串的長(zhǎng)度,可以使用bytes(s).length,但是要注意這樣獲取到的長(zhǎng)度同樣是以UTF-8編碼計(jì)算的長(zhǎng)度,而不是以單個(gè)字符計(jì)算的長(zhǎng)度。還有一點(diǎn)需要注意,因?yàn)镋VM的限制,外部函數(shù)調(diào)用無(wú)法返回一個(gè)動(dòng)態(tài)長(zhǎng)度的數(shù)組,唯一的做法是將需要返回的內(nèi)容放在一個(gè)足夠長(zhǎng)的定長(zhǎng)數(shù)組中。三以太坊智能合約開(kāi)發(fā)2.智能合約開(kāi)發(fā)語(yǔ)言(2)結(jié)構(gòu)體Solidity語(yǔ)言中的結(jié)構(gòu)體(struct)與C語(yǔ)言中很相似,允許開(kāi)發(fā)者根據(jù)需要自定義變量類型。下面的代碼展示了如何聲明一個(gè)結(jié)構(gòu)體:contractTest{structStudent{stringname;uintage;uintscore;stringsex;}}結(jié)構(gòu)體可以作為映射或者數(shù)組中的元素,其本身也可以包含映射和數(shù)組等類型,但是不能將一個(gè)結(jié)構(gòu)體用作其本身的成員,因?yàn)榻Y(jié)構(gòu)體嵌套自身會(huì)導(dǎo)致無(wú)限循環(huán)。三以太坊智能合約開(kāi)發(fā)2.智能合約開(kāi)發(fā)語(yǔ)言(3)映射映射(Mapping)是一種鍵值對(duì)映射關(guān)系的存儲(chǔ)結(jié)構(gòu),我們使用mapping(KeyType=>ValueType)來(lái)聲明一個(gè)映射,其中KeyType可以是除了映射、動(dòng)態(tài)數(shù)組、合約、枚舉類型、結(jié)構(gòu)體以外的任何類型,ValueType則可以是任意類型,包括映射本身。映射可以看作一個(gè)散列表,其中所有可能存在的鍵都有一個(gè)默認(rèn)值,默認(rèn)值的二進(jìn)制編碼全為0,所以映射并沒(méi)有長(zhǎng)度的概念。同時(shí),映射并不存儲(chǔ)鍵的數(shù)據(jù),而僅僅存儲(chǔ)它的Keccak-256散列值。三以太坊智能合約開(kāi)發(fā)2.智能合約開(kāi)發(fā)語(yǔ)言3.類型轉(zhuǎn)換如果一個(gè)運(yùn)算符作用于兩個(gè)類型不同的變量,編譯器會(huì)自動(dòng)嘗試將一個(gè)變量類型轉(zhuǎn)換為另一個(gè)變量的類型,這是隱式類型轉(zhuǎn)換。通常,在語(yǔ)義合理并且不會(huì)造成信息損失的情況下允許進(jìn)行隱式類型轉(zhuǎn)換,比如uint8轉(zhuǎn)換為uint16或者uint32,但是int8不能轉(zhuǎn)換成uintl6,這是因?yàn)閡intl6不能表示負(fù)數(shù)。任何可以轉(zhuǎn)換為uintl6的變量都可以轉(zhuǎn)換為address類型。有時(shí)在編譯器不能進(jìn)行隱式類型轉(zhuǎn)換的情況下可以強(qiáng)行進(jìn)行類型轉(zhuǎn)換,這叫做顯式類型轉(zhuǎn)換。但是請(qǐng)注意,進(jìn)行顯式類型轉(zhuǎn)換前必須知道你在進(jìn)行什么操作并且確定操作的結(jié)果是你想要的,否則會(huì)造成很多異常情況。uint32a=0x12345678;uintl6b=uintl6(a);以上代碼將uint32類型轉(zhuǎn)換為uint16類型,這導(dǎo)致了數(shù)值的高16位被截?cái)?。三以太坊智能合約開(kāi)發(fā)2.智能合約開(kāi)發(fā)語(yǔ)言4.運(yùn)算符Solidity語(yǔ)言中也包括算術(shù)運(yùn)算符、比較運(yùn)算符、位運(yùn)算等。運(yùn)算符的優(yōu)先順序如下圖所示。三以太坊智能合約開(kāi)發(fā)2.智能合約開(kāi)發(fā)語(yǔ)言在以上運(yùn)算符中,需要特別注意delete運(yùn)算符。在其他編程語(yǔ)言中,delete經(jīng)常作為一種與new相反的內(nèi)存管理操作,用于釋放內(nèi)存。但是在Solidity中,delete僅僅是一項(xiàng)賦值運(yùn)算,它用作給變量賦初始值。例如,deletea與a=0是等效的;delete用于數(shù)組表示將該數(shù)據(jù)變成一個(gè)長(zhǎng)度為0的空數(shù)組;當(dāng)作用于固定長(zhǎng)度數(shù)組時(shí),該數(shù)組將變?yōu)橐粋€(gè)長(zhǎng)度不變但每個(gè)元素都被賦值為默認(rèn)值的數(shù)組;當(dāng)作用于結(jié)構(gòu)體時(shí),delete將遞歸作用于除映射外的所有成員;delete對(duì)映射無(wú)效。下面的代碼展示了delete對(duì)復(fù)雜類型變量的效果:contractDeleteExample{functiondeleteArray(){uint[]memorya=newuint[](3);a[0]=l;a[l]=2;a[2]=3;deletea[l];//數(shù)組將變?yōu)閇1,0,3]deletea;//a.length將變?yōu)?}structS{uinta;stringb;bytesc;};functiondeleteStruct(){Ss=S(1,"hello","world");deletes; //刪除s中的所有元素,a、b、c分別被賦值為0、空串、0x0}}三以太坊智能合約開(kāi)發(fā)2.智能合約開(kāi)發(fā)語(yǔ)言5.類型推斷Solidity語(yǔ)言中,var關(guān)鍵字和C++語(yǔ)言中的auto關(guān)鍵字類似,用于類型推斷。uint24x=0xl23;vary=x;var聲明的變量將會(huì)擁有第一個(gè)賦值變量的類型,比如上面一段代碼中,y的類型是uint24。在使用中有時(shí)需要小心,比如for(vari=0;i<2000;i++){…}將是一個(gè)無(wú)限循環(huán),因?yàn)楦鶕?jù)類型推斷i的類型為unit8,所有i永遠(yuǎn)都不會(huì)滿足跳出循環(huán)的條件(i>=2000)。三以太坊智能合約開(kāi)發(fā)2.智能合約開(kāi)發(fā)語(yǔ)言3內(nèi)置單位、全局變量和函數(shù)Solidity包含一些內(nèi)置單位、全局變量和函數(shù)以供使用。(1)貨幣單位一個(gè)字面量的數(shù)字可以使用wei、finney、szabo和ether等后綴表示不同的額度,不加任何后綴則默認(rèn)單位為wei。比如“2ether==2000finney”的結(jié)果是true。(2)時(shí)間單位與貨幣單位相似,不同的時(shí)間單位可以以秒為基本單位進(jìn)行轉(zhuǎn)換,不同的單位定義如下:1==1seconds1minutes==60seconds1hours==60minutes1days==24hours1weeks==7days1years==365days特別注意,如果想要使用這些單位進(jìn)行時(shí)間計(jì)算必須特別小心,因?yàn)橐荒瓴⒉豢偸怯?65天;同時(shí)因?yàn)殚c秒的存在,一天也并不總是24小時(shí)。因?yàn)殚c秒的計(jì)算難以預(yù)測(cè),因此為保證日歷庫(kù)的精確性,需要由外部供應(yīng)商定期更新。三以太坊智能合約開(kāi)發(fā)2.智能合約開(kāi)發(fā)語(yǔ)言(3)區(qū)塊和交易屬性有一些方法和變量可以用于獲取區(qū)塊和交易的屬性。block.blockhash(uintblo

溫馨提示

  • 1. 本站所有資源如無(wú)特殊說(shuō)明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請(qǐng)下載最新的WinRAR軟件解壓。
  • 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請(qǐng)聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
  • 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁(yè)內(nèi)容里面會(huì)有圖紙預(yù)覽,若沒(méi)有圖紙預(yù)覽就沒(méi)有圖紙。
  • 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
  • 5. 人人文庫(kù)網(wǎng)僅提供信息存儲(chǔ)空間,僅對(duì)用戶上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對(duì)用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對(duì)任何下載內(nèi)容負(fù)責(zé)。
  • 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請(qǐng)與我們聯(lián)系,我們立即糾正。
  • 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時(shí)也不承擔(dān)用戶因使用這些下載資源對(duì)自己和他人造成任何形式的傷害或損失。

評(píng)論

0/150

提交評(píng)論