




版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請進行舉報或認領(lǐng)
文檔簡介
以太坊基礎(chǔ)及應(yīng)用開發(fā)主講:XXX輔導(dǎo):XXX課程簡介01.以太坊詳解03.以太坊智能合約開發(fā)02.以太坊開發(fā)環(huán)境04.應(yīng)用系統(tǒng)開發(fā)實例05.總結(jié)1以太坊詳解一以太坊詳解什么是以太坊?以太坊的本質(zhì)就是一個基于交易的狀態(tài)機(transaction-basedstatemachine)。全球就存在一臺以分布式形式存在的單機,其系統(tǒng)狀態(tài)在不停的改變;這臺單機的系統(tǒng)狀態(tài)主要由區(qū)塊鏈組成,區(qū)塊鏈上保存著狀態(tài)和交易;當(dāng)用戶與以太坊交互時,其實就是在執(zhí)行交易、改變系統(tǒng)狀態(tài)。一以太坊詳解如圖所示,以太坊架構(gòu)分為7層,由下至上依次是存儲層、數(shù)據(jù)層、網(wǎng)絡(luò)層、協(xié)議層、共識層、合約層、應(yīng)用層:1.以太坊體系結(jié)構(gòu)一以太坊詳解
(1)存儲層主要用于存儲以太坊系統(tǒng)運行中的日志數(shù)據(jù)及區(qū)塊鏈元數(shù)據(jù),存儲技術(shù)主要使用文件系統(tǒng)和LevelDB。(2)數(shù)據(jù)層主要用于處理以太坊交易中的各類數(shù)據(jù),如將數(shù)據(jù)打包成區(qū)塊,將區(qū)塊維護成鏈?zhǔn)浇Y(jié)構(gòu),區(qū)塊中內(nèi)容的加密與哈希計算,區(qū)塊內(nèi)容的數(shù)字簽名及增加時間戳印記,將交易數(shù)據(jù)構(gòu)建成Merkle樹,并計算Merkle樹根節(jié)點的hash值等。與比特幣的不同之處在于以太坊引入了交易和交易池的概念。交易指的是一個賬戶向另一個賬戶發(fā)送被簽名的數(shù)據(jù)包的過程。而交易池則存放通過節(jié)點驗證的交易,這些交易會放在礦工挖出的新區(qū)塊里。以太坊的Event(事件)指的是和以太坊虛擬機提供的日志接口,當(dāng)事件被調(diào)用時,對應(yīng)的日志信息被保存在日志文件中。
(3)與比特幣一樣,以太坊的系統(tǒng)也是基于P2P網(wǎng)絡(luò)的,在網(wǎng)絡(luò)中每個節(jié)點既有客戶端角色,又有服務(wù)端角色。1.以太坊體系結(jié)構(gòu)一以太坊詳解
(4)協(xié)議層是以太坊提供的供系統(tǒng)各模塊相互調(diào)用的協(xié)議支持,主要有HTTP、RPC協(xié)議、LES、ETH協(xié)議、Whipser協(xié)議等。以太坊基于HTTPClient實現(xiàn)了對HTTP的支持,實現(xiàn)了GET、POST等HTTP方法。外部程序通過JSONRPC調(diào)用以太坊的API時需通過RPC(遠程過程調(diào)用)協(xié)議。Whisper協(xié)議用于DApp間通信。LES的全稱是輕量級以太坊子協(xié)議(LightEthereumSub-protocol),允許以太坊節(jié)點同步獲取區(qū)塊時僅下載區(qū)塊的頭部,在需要時再獲取區(qū)塊的其他部分。(5)共識層在以太坊系統(tǒng)中有PoW(ProofofWork)和PoS(ProofofStake)兩種共識算法。(6)合約層分為兩層,底層是EVM(EthereumVirtualMachine,即以太坊虛擬機),上層的智能合約運行在EVM中。智能合約是運行在以太坊上的代碼的統(tǒng)稱,一個智能合約往往包含數(shù)據(jù)和代碼兩部分。智能合約系統(tǒng)將約定或合同代碼化,由特定事件驅(qū)動觸發(fā)執(zhí)行。因此,在原理上適用于對安全性、信任性、長期性的約定或合同場景。在以太坊系統(tǒng)中,智能合約的默認編程語言是Solidity,一般學(xué)過JavaScript語言的讀者很容易上手Solidity。(7)應(yīng)用層有DApp(DecentralizedApplication,分布式應(yīng)用)、以太坊錢包等多種衍生應(yīng)用,是目前開發(fā)者最活躍的一層。1.以太坊體系結(jié)構(gòu)一以太坊詳解2.以太坊工作流程及運行原理①以太坊區(qū)塊鏈的范式闡述
以太坊的本質(zhì)就是一個基于交易的狀態(tài)機(transaction-basedstatemachine)。在計算機科學(xué)中,狀態(tài)機是指可以讀取一系列的輸入,然后根據(jù)這些輸入,會轉(zhuǎn)換成一個新的狀態(tài)出來的東西,如圖所示。
根據(jù)以太坊的狀態(tài)機,我們從創(chuàng)世紀(jì)狀態(tài)(genesisstate)開始。這差不多類似于一片空白的石板,在網(wǎng)絡(luò)中還沒有任何交易的產(chǎn)生狀態(tài)。當(dāng)交易被執(zhí)行后,這個創(chuàng)世紀(jì)狀態(tài)就會轉(zhuǎn)變成最終狀態(tài)。在任何時刻,這個最終狀態(tài)都代表著以太坊當(dāng)前的狀態(tài)。一以太坊詳解2.以太坊工作流程及運行原理
以太坊的狀態(tài)有數(shù)百萬個交易,這些交易都被打包到以太坊區(qū)塊中。一個區(qū)塊包含了一系列的交易,每個區(qū)塊都與它的前一個區(qū)塊鏈接起來。
為了讓一個狀態(tài)轉(zhuǎn)換成下一個狀態(tài),交易必須是有效的。為了讓一個交易被認為是有效的,它必須要經(jīng)過一個驗證過程,此過程也就是挖礦。挖礦就是一組節(jié)點(即電腦)用它們的計算資源來創(chuàng)建一個包含有效交易的區(qū)塊出來。任何在網(wǎng)絡(luò)上宣稱自己是礦工的節(jié)點都可以嘗試創(chuàng)建和驗證區(qū)塊。世界各地的很多礦工都在同一時間創(chuàng)建和驗證區(qū)塊。每個礦工在提交一個區(qū)塊到區(qū)塊鏈上的時候都會提供一個數(shù)學(xué)機制的“證明”,這個證明就像一個保證:如果這個證明存在,那么這個區(qū)塊一定是有效的。為了讓一個區(qū)塊添加到主鏈上,一個礦工必須要比其他礦工更快的提供出這個“證明”。通過礦工提供的一個數(shù)學(xué)機制的“證明”來證實每個區(qū)塊的過程稱之為工作量證明(proofofwork)。證實了一個新區(qū)塊的礦工都會被獎勵一定價值的獎賞。以太坊使用一種內(nèi)在數(shù)字代幣--以太幣(Ether)作為獎賞。每次礦工證明了一個新區(qū)塊,那么就會產(chǎn)生一個新的以太幣并被獎勵給礦工。一以太坊詳解2.以太坊工作流程及運行原理
前面,我們定義了區(qū)塊鏈就是一個具有共享狀態(tài)的交易單機。使用這個定義,我們可以知道正確的當(dāng)前狀態(tài)是一個全球真相,所有人都必須要接受它。擁有多個狀態(tài)(或多個鏈)會摧毀這個系統(tǒng),因為它在哪個是正確狀態(tài)的問題上不可能得到統(tǒng)一結(jié)果。如果鏈分叉了,你有可能在一條鏈上擁有10個幣,一條鏈上擁有20個幣,另一條鏈上擁有40個幣。在這種場景下,是沒有辦法確定哪個鏈才是最“有效的”。
不論什么時候只要多個路徑產(chǎn)生了,一個“分叉”就會出現(xiàn)。我們通常都想避免分叉,因為它們會破壞系統(tǒng),強制人們?nèi)ミx擇哪條鏈?zhǔn)撬麄兿嘈诺逆?。一以太坊詳?.以太坊工作流程及運行原理為了確定哪個路徑才是最有效的以及防止多條鏈的產(chǎn)生,以太坊使用了一個叫做“GHOST協(xié)議”(GHOSTprotocol)的數(shù)學(xué)機制。簡單來說,GHOST協(xié)議就是讓我們必須選擇一個在其上完成計算最多的路徑。一個方法確定路徑就是使用最近一個區(qū)塊(葉子區(qū)塊)的區(qū)塊號,區(qū)塊號代表著當(dāng)前路徑上總的區(qū)塊數(shù)(不包含創(chuàng)世紀(jì)區(qū)塊)。區(qū)塊號越大,路徑就會越長,就說明越多的挖礦算力被消耗在此路徑上以達到葉子區(qū)塊。使用這種推理就可以允許我們贊同當(dāng)前狀態(tài)的權(quán)威版本。一以太坊詳解2.以太坊工作流程及運行原理②以太坊系統(tǒng)的主要組件以太坊系統(tǒng)的主要組件包括:賬戶、賬戶狀態(tài)、世界狀態(tài)、gas和費用、交易、區(qū)塊、交易執(zhí)行、挖礦、PoW等。
(1)賬戶:
以太坊的全球“共享狀態(tài)”是由很多小的對象(賬戶)組成,這些賬戶通過消息傳遞框架實現(xiàn)彼此交互。每個賬戶都有一個與之關(guān)聯(lián)的狀態(tài)以及一個20字節(jié)的地址。以太坊中的地址是160位比特的標(biāo)識符,用于標(biāo)識任何賬戶。有兩種類型的賬戶:外部賬戶(由私鑰控制,沒有與之關(guān)聯(lián)的代碼)和合約賬戶(由合約代碼控制,有與之關(guān)聯(lián)的代碼)。一以太坊詳解2.以太坊工作流程及運行原理
理解外部賬戶和合約賬戶之間的根本區(qū)別是非常重要的。通過創(chuàng)建及使用其私鑰簽名一個交易,外部賬戶能夠給其他外部賬戶或其他合約賬戶發(fā)送消息。兩個外部賬戶之間的消息只是簡單的價值傳輸。但從外部賬戶發(fā)送到合約賬戶的消息可以激活合約賬戶的代碼,允許它執(zhí)行各種操作(例如,轉(zhuǎn)移代幣、寫入內(nèi)部存儲、發(fā)新幣、執(zhí)行計算、創(chuàng)建新合約等。)
與外部賬戶不同,合約賬戶無法自行啟動新的交易。相反,合約賬戶僅能夠通過響應(yīng)它們收到的交易來觸發(fā)自身的交易,比如從外部賬戶或從其他的合約賬戶的交易來觸發(fā)。因此,任何以太坊區(qū)塊鏈上發(fā)生的操作始終由外部賬戶所觸發(fā)的交易來啟動。一以太坊詳解2.以太坊工作流程及運行原理(2)賬戶狀態(tài):賬戶狀態(tài)由四個部分組成:1)Nonce隨機數(shù):如果該賬戶是外部賬戶,這個數(shù)代表從這個賬戶地址發(fā)出來的交易數(shù)。如果該賬戶是合約賬戶,則該nonce是該賬戶創(chuàng)建的合約數(shù),每創(chuàng)建一次會加1。2)Balance賬戶余額:該地址擁有的Wei數(shù),每個Ether有10^18Wei。3)StorageRoot:MerklePatricia樹的根節(jié)點的哈希值。Merkletree對該賬戶的存儲內(nèi)容的哈希進行編碼,默認情況下為空。4)CodeHash:該賬戶EVM代碼的哈希。對于合約賬戶,就是被Hash的代碼并作為codeHash保存。對于外部賬戶而言,Codehash字段是空字符串的哈希值。一以太坊詳解2.以太坊工作流程及運行原理(3)世界狀態(tài):以太坊的全局狀態(tài)就是由賬戶地址和賬戶狀態(tài)的一個映射組成。這個映射被保存在一個叫做MerklePatricia樹的數(shù)據(jù)結(jié)構(gòu)中。MerkleTree是一種由一系列節(jié)點組成的二叉樹,這些節(jié)點包括:1)在樹底的包含了源數(shù)據(jù)的大量葉子節(jié)點。2)一系列的中間的節(jié)點,這些節(jié)點是兩個子節(jié)點的Hash值。3)一個根節(jié)點,同樣是兩個子節(jié)點的Hash值,代表著整棵樹。一以太坊詳解2.以太坊工作流程及運行原理樹底的數(shù)據(jù)是通過分開我們想要保存到chunks的數(shù)據(jù)產(chǎn)生的,然后將chunks分成buckets,再然后再獲取每個bucket的hash值并一直重復(fù)直到最后只剩下一個Hash:根Hash。一以太坊詳解2.以太坊工作流程及運行原理這棵樹要求存在里面的值(value)都有一個對應(yīng)的key。從樹的根節(jié)點開始,key會告訴你順著哪個子節(jié)點可以獲得對應(yīng)的值,這個值存在葉子節(jié)點。在以太坊中,key/value是地址和與地址相關(guān)聯(lián)的賬戶之間狀態(tài)的映射,包括每個賬戶的balance,nonce,codeHash和storageRoot(storageRoot自己就是一顆樹)。一以太坊詳解2.以太坊工作流程及運行原理同樣的樹結(jié)構(gòu)也用來存儲交易和收據(jù)。更具體的說,每個塊都有一個頭(header),保存了三個不同Merkletrie結(jié)構(gòu)的根節(jié)點的Hash,包括:1)狀態(tài)樹。2)交易樹。3)收據(jù)樹。一以太坊詳解2.以太坊工作流程及運行原理區(qū)塊鏈?zhǔn)强恳蝗汗?jié)點來維持的,有兩種節(jié)點類型:全節(jié)點和輕節(jié)點。全節(jié)點通過下載整條鏈來進行同步,一個全節(jié)點包含了整個鏈,從創(chuàng)世紀(jì)塊到當(dāng)前塊,執(zhí)行其中包含的所有交易。通常,礦工會存儲全節(jié)點,因為他們在挖礦過程中需要全節(jié)點。一個節(jié)點如果不需要執(zhí)行所有的交易或輕松訪問歷史數(shù)據(jù),就沒必要保存整條鏈,這就是輕節(jié)點概念的來源。比起下載和存儲整個鏈以及執(zhí)行其中所有的交易,輕節(jié)點僅僅下載鏈的頭,從創(chuàng)世紀(jì)塊到當(dāng)前塊的頭,不執(zhí)行任何的交易或檢索任何相關(guān)聯(lián)的狀態(tài)。由于輕節(jié)點可以訪問塊的頭,而頭中包含了3個tries的Hash,所有輕節(jié)點依然可以很容易生成和接收關(guān)于交易、事件、余額等可驗證的答案,因為在Merkle樹中hash值是向上傳播的。如果一個惡意用戶試圖用一個假交易來交換Merkle樹底的交易,這個會改變它上面節(jié)點的hash值,而它上面節(jié)點的值的改變也會導(dǎo)致上上一個節(jié)點Hash值的改變,以此類推,一直到樹的根節(jié)點。如圖。一以太坊詳解2.以太坊工作流程及運行原理任何節(jié)點想要驗證一些數(shù)據(jù)都可以通過Merkle證明來進行驗證,Merkle證明的組成如下:1)一塊需要驗證的數(shù)據(jù)。2)樹的根節(jié)點Hash。3)一個“分支”(從chunk到根這個路徑上所有的hash值)。任何可以讀取證明的人都可以驗證分支的hash是連貫的,因此給出的塊在樹中實際的位置就是在此處。使用MerklePatricia樹的好處就是該結(jié)構(gòu)的根節(jié)點加密取決于存儲在樹中的數(shù)據(jù),而且根據(jù)點的hash還可以作為該數(shù)據(jù)的安全標(biāo)識。由于塊的頭包含了狀態(tài)、交易、收據(jù)樹的根hash,所有任何節(jié)點都可以驗證以太坊的一小部分狀態(tài)而不用保存整個狀態(tài),而這整個狀態(tài)的大小可能是非常大的。一以太坊詳解2.以太坊工作流程及運行原理(4)Gas和支付:以太坊有一個非常重要的概念是費用的概念。在以太坊網(wǎng)絡(luò)上交易而消耗的計算都會產(chǎn)生費用,支付的費用以“gas”來計算。gas是用于衡量特定計算所需費用的單位。gas價格是你愿意花費在每單位gas上的Ether總量,用“gwei”來衡量?!癢ei”是Ether的最小單位,1018Wei代表1Ether。1gwei是1,000,000,000Wei。一以太坊詳解2.以太坊工作流程及運行原理每次交易,交易發(fā)送人(轉(zhuǎn)賬人)都會設(shè)置gas的limit和gas價格。gas價格和gaslimit代表了發(fā)送人愿意為交易支付的最大數(shù)量的Wei。例如,我們假設(shè)發(fā)送人設(shè)置gaslimit是50,000,gas價格是20gwei。這意味著交易發(fā)送者愿意支付最多50,000*20gwei=1,000,000,000,000,000Wei,也就是0.001Ether用來執(zhí)行該交易。gaslimit代表了交易發(fā)送人愿意支付的最大費用。如果交易發(fā)送人賬戶余額可以覆蓋這個最大值,就不會有問題。交易結(jié)束時,發(fā)送人會收到未被使用的gas資金退款,并按最初價格交易。如果交易發(fā)送人沒有提供足夠的gas來執(zhí)行交易,交易會用光gas,并且該交易無效。在這種情況下,交易過程中止,發(fā)生的任何狀態(tài)更改都會被逆轉(zhuǎn),這樣交易會結(jié)束,并回到交易前的以太坊狀態(tài)。此外,還會記錄交易失敗,顯示什么交易試圖發(fā)起并在哪里失敗。同時,既然在用光gas之前,機器已經(jīng)花費了努力進行計算,邏輯上來說,這些花費的gas不會再退還給交易發(fā)送人。交易發(fā)送人花費的所有g(shù)as資金都被發(fā)送到“受益人”地址,這通常是礦工的地址。既然礦工花費努力來計算和驗證交易,礦工收取gas費用作為獎勵。通常,交易發(fā)送人愿意支付的gas價格越高,礦工從交易中獲得價值越大。因此,礦工也會選擇價格高的交易。這樣,礦工自由選擇他們愿意驗證的交易。為了引導(dǎo)交易發(fā)送者設(shè)置gas價格,礦工可以選擇宣傳他們會執(zhí)行交易的最低gas價格。一以太坊詳解2.以太坊工作流程及運行原理通常,交易發(fā)送人愿意支付的gas價格越高,礦工從交易中獲得價值越大。因此,礦工也會選擇價格高的交易。這樣,礦工自由選擇他們愿意驗證的交易。為了引導(dǎo)交易發(fā)送者設(shè)置gas價格,礦工可以選擇宣傳他們會執(zhí)行交易的最低gas價格。gas不僅用于支付計算步驟,也用于支付存儲費用。存儲所需的總體費用跟使用32字節(jié)的最小倍數(shù)成正比。存儲的費用有一些細微差別。比如,既然不斷增加的存儲增大了所有節(jié)點的以太坊狀態(tài)數(shù)據(jù)庫的大小,那么有動機來保持小的數(shù)據(jù)存儲量。因此,如果交易具有可以清除存儲中的條目的步驟,則免除執(zhí)行該操作的費用,并且為了釋放存儲空間還可以退還費用。以太坊是圖靈完備的語言,智能合約引入了循環(huán)語句,這使得以太坊容易受到停頓問題的影響,可能會使程序無限運行下去。如果沒有費用,惡意行為者可以通過在交易中執(zhí)行無限循環(huán),而輕易地破壞網(wǎng)絡(luò)。因此,計費機制可以保護網(wǎng)絡(luò)免受惡意攻擊。以太坊網(wǎng)絡(luò)上的存儲也有成本,因此存儲也是需要付費的。一以太坊詳解2.以太坊工作流程及運行原理(5)交易和消息:以太坊是基于交易的狀態(tài)機,發(fā)生在不同賬戶之間的交易推動著以太坊的全球狀態(tài)從一個狀態(tài)轉(zhuǎn)換到另外一個狀態(tài)。從最根本的意義上來說,交易是加密簽名的指令,它由外部賬戶生成,并序列化,然后提交到區(qū)塊鏈上。有兩類交易:消息調(diào)用和合約創(chuàng)建(即創(chuàng)建新的以太坊合約的交易)。一以太坊詳解2.以太坊工作流程及運行原理所有類型的交易均包含以下部分:1)Nonce(隨機數(shù)):交易發(fā)送人發(fā)送的交易數(shù)量的計數(shù)。(跟比特幣的Nonce概念不同。)2)Gasprice:交易發(fā)送人愿意為執(zhí)行交易所需的每單位gas支付的Wei的數(shù)量。3)Gaslimit:交易發(fā)送人愿意為執(zhí)行交易支付的最大gas數(shù)量。數(shù)量是設(shè)置并預(yù)付的,在任何計算完成之前確定。4)To:接收人的地址。如是創(chuàng)建合約的交易,合約賬戶地址還不存在,所以使用的是空值。5)Value:從發(fā)送人轉(zhuǎn)移到接收人的Wei總量。在創(chuàng)建合約的交易中,這個值作為新創(chuàng)建合約賬戶的初始余額。6)v,r,s:用于生成簽名,該簽名可以標(biāo)識交易的發(fā)送人。7)Init:僅用于創(chuàng)建合約的交易。它是EVM代碼片段,可用來初始化新的合約賬戶。Init只允許一次,然后被拋棄。首次運行init時,它會返回賬戶代碼的正文,這段代碼與合約賬戶產(chǎn)生永久關(guān)聯(lián)關(guān)系。8)數(shù)據(jù):僅用于消息調(diào)用的可選字段。它是指消息調(diào)用的輸入數(shù)據(jù)(即參數(shù))。比如,如果智能合約充當(dāng)域名注冊的服務(wù),對合約的調(diào)用可能需要輸入字段如域名或IP地址。一以太坊詳解2.以太坊工作流程及運行原理(6)區(qū)塊:區(qū)塊就是交易的集合,公鏈或者聯(lián)盟鏈將交易打包成區(qū)塊以后會進行持久化存儲。(7)交易執(zhí)行:所有交易的執(zhí)行必須滿足以下條件:1)交易必須是格式正確的RLP?!癛LP”代表“遞歸長度前綴”,是一種數(shù)據(jù)格式,它用于編碼二進制數(shù)據(jù)的嵌套數(shù)組。RLP是以太坊用于序列化對象的格式。2)有效的交易簽名。3)有效交易nonce。一個賬戶的nonce是從該賬戶發(fā)送過來交易計數(shù)。為了有效,交易nonce必須等于發(fā)送者賬戶的nonce。一以太坊詳解2.以太坊工作流程及運行原理4)交易的gaslimit必須等于或大于交易使用的固有g(shù)as,發(fā)送人的賬戶余額必須有足夠的Ether覆蓋“預(yù)定”gas成本。固有g(shù)as包括:為執(zhí)行交易預(yù)先定義的成本gas;與交易一起發(fā)送的數(shù)據(jù)的gas費用(每字節(jié)數(shù)據(jù)或代碼相當(dāng)于零時則是4gas的費用,每非零字節(jié)的數(shù)據(jù)或代碼是68gas費用);如果交易是創(chuàng)建合約的交易,則額外增加32000gas。“預(yù)定”gas成本包括兩個方面:最大的gas成本(交易的gaslimit乘以交易的gas價格);從發(fā)送者轉(zhuǎn)移到接收者的總價值(總值)。如果交易滿足以上4個條件,交易才會繼續(xù)執(zhí)行。在交易執(zhí)行之前,先從發(fā)送人的余額中扣除預(yù)定的執(zhí)行成本,并將發(fā)送人的賬戶的nonce增加1以計入當(dāng)前的交易。此時,可以算出剩余的gas,它們作為交易的總gaslimit減去使用過的固有g(shù)as。一以太坊詳解2.以太坊工作流程及運行原理接下來交易開始執(zhí)行。在交易的執(zhí)行過程中,以太坊跟蹤“子狀態(tài)”,該子狀態(tài)是記錄交易過程中產(chǎn)生的信息的方法,主要包括:1)自毀集:在交易完成后被拋棄的一組賬戶。2)日志系列:虛擬機代碼執(zhí)行的歸檔及可索引的檢查點。3)退還余額:交易后退還給發(fā)送人賬戶的金額。接下來,處理交易要求的各種計算。一旦交易要求的所有步驟都被處理完畢,假定沒有無效狀態(tài),則通過確定要退還給發(fā)送人的未使用的gas金額來實現(xiàn)最終狀態(tài)。除了未使用的gas,發(fā)送人還可以從上面提到的“退款余額”中獲得一些補貼。一旦發(fā)送人獲得退款:1)gas的Ether已經(jīng)給到礦工。2)交易使用的gas被添加到區(qū)塊gas計數(shù)器(它跟蹤區(qū)塊中所有交易使用的總gas,并在驗證區(qū)塊時使用)。3)在自毀集中的所有賬戶都將被刪除。最后,留下新的狀態(tài)和一組交易創(chuàng)建的日志。一以太坊詳解2.以太坊工作流程及運行原理③運行原理(1)合約創(chuàng)建:為了創(chuàng)建新的合約賬戶,首先使用特殊公式聲明新賬戶地址,然后通過如下方式初始化新賬戶:1)把nonce(隨機數(shù))設(shè)置為零。2)如果發(fā)送人用交易發(fā)送一定量的Ether作為價值,則設(shè)置該價值為賬戶余額。3)從發(fā)送人的余額中扣除發(fā)送給新賬戶的價值。4)把存儲設(shè)置為空。5)把合約的codeHash設(shè)置為空字符串的哈希。一旦初始化賬戶,使用跟隨交易發(fā)送的init代碼實際上能夠創(chuàng)建賬戶。在執(zhí)行這個初始化代碼時會產(chǎn)生多樣的變化。根據(jù)合約的構(gòu)造函數(shù),它可能會升級賬戶的存儲、創(chuàng)建其他合約賬戶、進行其他消息調(diào)用等。一以太坊詳解2.以太坊工作流程及運行原理當(dāng)執(zhí)行初始化合約的代碼時,它使用gas。交易不允許使用比剩余gas更多的gas。如果遇到這樣的情況,執(zhí)行導(dǎo)致gas不足的異常,然后退出。如果交易因為gas不足異常而退出,那么,狀態(tài)會立刻復(fù)原到交易之前到那個點,而發(fā)送人不會收到gas退款,該gas已在之前執(zhí)行中用完。但是,如果發(fā)送人用交易發(fā)送任何Ether價值,即使合約創(chuàng)建失敗,Ether價值也會被退回。如果初始化代碼執(zhí)行成功,會支付最終的成功創(chuàng)建合約的成本。這是存儲成本,支付費用跟所創(chuàng)建合約代碼的大小成正比。如果沒有足夠的剩余gas來支付最終的成本,交易再次出現(xiàn)gas不足的異常,并中止。如果在交易執(zhí)行過程中沒有發(fā)生異常,則剩余的未使用的gas會退還給交易的最初發(fā)送人,由此被改變的狀態(tài)也允許持續(xù)存在。一以太坊詳解2.以太坊工作流程及運行原理(2)消息調(diào)用:消息調(diào)用的執(zhí)行跟合約創(chuàng)建的執(zhí)行類似,不過存在一些差異。消息調(diào)用執(zhí)行并不包括任何init代碼,因為沒有創(chuàng)建新的賬戶。如果數(shù)據(jù)由交易發(fā)送人提供,則它可以包含輸入數(shù)據(jù)。一旦執(zhí)行,消息調(diào)用還有額外的組件,組件包含了輸出數(shù)據(jù),如果接下來的執(zhí)行需要該數(shù)據(jù),它會被使用。跟合約創(chuàng)建一樣,如果因為gas不足或交易無效(如堆棧溢出、無效跳轉(zhuǎn)目標(biāo)或無效指令),消息調(diào)用退出,并且所使用的gas不會退還給最初的調(diào)用者。相反,所有剩余的未使用的gas被消耗,并且狀態(tài)會復(fù)原到余額轉(zhuǎn)移之前的點。一以太坊詳解2.以太坊工作流程及運行原理(3)執(zhí)行模式:交易是在VM上執(zhí)行的,實際處理交易流程的部分協(xié)議是以太坊自己的虛擬機,也就是所謂的EVM。EVM是圖靈完備的虛擬機,跟其他圖靈完備的機器相比,EVM唯一的限制是它跟gas是內(nèi)在綁定的。也就是說,它的計算量是受gas量內(nèi)在約束的。EVM有基于堆棧的架構(gòu),堆棧計算機使用后進先出堆棧來保存臨時值。EVM中每個堆棧項的大小是256位,堆棧最大的大小為1024。EVM有內(nèi)存,其中的item存儲為字尋址字節(jié)數(shù)組。內(nèi)存不穩(wěn)定,意味著它不是持久的。EVM還有存儲空間。跟內(nèi)存不同,存儲是穩(wěn)定的,同時它作為系統(tǒng)狀態(tài)的一部分進行維持。EVM在虛擬ROM中分別存儲程序代碼,這些代碼只能通過特別指令訪問。通過這種方式,EVM跟經(jīng)典的馮·諾依曼架構(gòu)不同,其中的程序代碼存儲在內(nèi)存或存儲器中。EVM也有它自己的語言:“EVM字節(jié)代碼”。當(dāng)一個程序員編寫以太坊智能合約時,一般來說,會使用高階語言如Solidity。之后,可以將其編譯為EVM可以理解的EVM字節(jié)代碼。一以太坊詳解3.以太坊區(qū)塊結(jié)構(gòu)及鏈結(jié)構(gòu)區(qū)塊就是交易的集合,公鏈或者聯(lián)盟鏈將交易打包成區(qū)塊以后會進行持久化存儲。如圖是經(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ū)塊頭存儲了區(qū)塊的元信息,用來對區(qū)塊內(nèi)容進行一些標(biāo)識、校驗、說明等。(1)通用字段:ParentHash:父區(qū)塊的哈希值。Root:世界狀態(tài)的哈希,stateDB的RLP編碼后的哈希值。TxHash(transactionroothash):交易字典樹的根哈希,由本區(qū)塊所有交易的交易哈希算出。ReceptHash:收據(jù)樹的哈希。Time:區(qū)塊產(chǎn)生出來的Unix時間戳。Number:區(qū)塊號。Bloom:布隆過濾器,快速定位日志是否在這個區(qū)塊中。一以太坊詳解3.以太坊區(qū)塊結(jié)構(gòu)及鏈結(jié)構(gòu)(2)公鏈場景:Coinbase:挖出這個塊的礦工地址,挖出區(qū)塊所獎勵的ETH就會發(fā)放到這個地址。Difficulty:當(dāng)前工作量證明算法的復(fù)雜度。GasLimit:每個區(qū)塊Gas的消耗上線。GasUsed:當(dāng)前區(qū)塊所有交易使用的Gas之和。MixDigest:挖礦得到的Pow算法證明的摘要,也就是挖礦的工作量證明。nonce:挖礦找到的滿足條件的值。Uncle:叔塊,和以太坊的共識算法相關(guān)。一般而言一個類以太坊的聯(lián)盟鏈?zhǔn)切枰厦娼榻B的通用字段的,但是也不絕對,還可能與選擇的共識算法,隱私保護策略,設(shè)計偏好有關(guān)。區(qū)塊體(Body):區(qū)塊體包括這個區(qū)塊打包的所有交易,在一些鏈的設(shè)計中,并不像以太坊區(qū)分header和body,而是整合在一起。(1)區(qū)塊頭存儲以太坊通過如下方式將區(qū)塊頭轉(zhuǎn)換成鍵值對存儲在LevelDB中:headerPrefix+num+hash->rlp(header)(2)區(qū)塊體存儲區(qū)塊體的存儲方式也是類似:bodyPrefix+num+hash->rlp(block)最初系統(tǒng)產(chǎn)生一個空白的區(qū)塊(無交易信息),每個新產(chǎn)生的區(qū)塊的ParentHash指向上一塊區(qū)塊,這樣就形成了區(qū)塊鏈。2以太坊開發(fā)環(huán)境二以太坊開發(fā)環(huán)境當(dāng)開發(fā)者使用Solidity語言編寫智能合約時,智能合約實際上就是由Solidity代碼編譯后的程序,也就是說,智能合約的編譯環(huán)境就是Solidity的編譯環(huán)境。而這段程序(智能合約)的執(zhí)行環(huán)境就是EVM。在以太坊中,Solidity編寫的智能合約經(jīng)過編譯后會生成一串十六進制字節(jié)碼,創(chuàng)建后進行調(diào)用時,也需要將調(diào)用的函數(shù)(function)名稱和參數(shù)轉(zhuǎn)化成一串十六進制字節(jié)碼寫進交易中。當(dāng)用戶通過發(fā)起ethsendTransaction或者ethcall創(chuàng)建或者調(diào)用智能合約時,就要在交易(Transaction)的data字段填入這個十六進制碼。創(chuàng)建智能合約時,EVM會將這段字節(jié)碼解析成相應(yīng)的指令符序列,存儲到一個新建的智能合約地址下。當(dāng)用戶調(diào)用這個智能合約時,以太坊本身會根據(jù)交易里的to宇段先獲取到這個智能合約的信息,EVM先根據(jù)data字段里解析出的具體函數(shù)和參數(shù)生成具體的指令,再依次執(zhí)行這些指令得到執(zhí)行結(jié)果,這些操作會涉及對賬戶狀態(tài)數(shù)據(jù)進行更改。二以太坊開發(fā)環(huán)境1.以太坊開發(fā)工具及框架流行的以太坊開發(fā)框架:(1)Truffle:最為流行的智能合約開發(fā)、測試和部署框架,經(jīng)常與Ganache(也是由Truffle團隊開發(fā))一起搭配使用。(2)Embark:和廣泛使用的Truffle類似,Embark也是一個功能強大的DApp開發(fā)框架,幫助開發(fā)者快速構(gòu)建和部署DApp。Embark不單可以與以太坊區(qū)塊鏈通信,還集成了IPFS/Swarm去中心化存儲和Whisper網(wǎng)絡(luò)通信功能。(3)Populus:用Python語言寫的智能合約開發(fā)框架。(4)Etherlime:基于ethers.js的DApp開發(fā)框架。二以太坊開發(fā)環(huán)境1.以太坊開發(fā)工具及框架集成開發(fā)環(huán)境(IDE):(1)Remix:一個基于solidity語言的在線智能合約開發(fā)IDE,它提供從編譯,調(diào)試到部署的全流程支持。(2)AtomAtom:AtomAtom編輯器可以結(jié)合AtomSolidityLinter,Etheratom等插件進行智能合約開發(fā)。(3)Pragma:一個非常簡單的solidity合約在線IDE,提供合約的編譯、部署與調(diào)用支持。(4)SuperblocksStudio:SuperblocksStudio可以幫助你在線編寫、編譯與部署智能合約,目前處于beta版本。(5)Vimsolidity:有了這個,使用vim也可以寫愉快地寫solidity了。(6)VisualStudioCode:VSCode是日常用的最多的工具。(7)IntellijSolidityPlugin:JetBrainsIntelliJIdeaIDE上用的solidity插件,支持語法高亮,格式化與代碼自動補全。本文選擇Truffle這個最為流行的智能合約開發(fā)、測試和部署框架進行介紹。本節(jié)介紹用于啟動以太坊的以太坊客戶端Geth、編譯智能合約的Solidity編譯器和流行的智能合約開發(fā)框架Truffle。二以太坊開發(fā)環(huán)境1.以太坊開發(fā)工具及框架1.GethGeth是GoEthereum開源項目的簡稱,它是使用Go語言編寫且實現(xiàn)了Ethereum協(xié)議的客戶端軟件,也是目前用戶最多,使用最廣泛的客戶端。通過Geth客戶端與以太坊網(wǎng)絡(luò)進行連接和交互可以實現(xiàn)賬戶管理、合約部署、挖礦等眾多有趣且實用的功能。Geth啟動時需要對創(chuàng)世區(qū)塊文件和相關(guān)參數(shù)進行配置。創(chuàng)世區(qū)塊文件genesis.json是區(qū)塊鏈最重要的識別標(biāo)志之一,每一條區(qū)塊鏈都有唯一識別的創(chuàng)世區(qū)塊文件,如果兩臺機器啟動Geth時所選用的創(chuàng)世區(qū)塊文件不同,就無法被識別為同一條區(qū)塊鏈的成員。因此,同一條聯(lián)盟鏈中的所有節(jié)點必須使用同一份創(chuàng)世區(qū)塊文件進行初始化配置。二以太坊開發(fā)環(huán)境1.以太坊開發(fā)工具及框架下面是創(chuàng)世區(qū)塊文件genesis.json的一個示例:{"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"}二以太坊開發(fā)環(huán)境1.以太坊開發(fā)工具及框架根據(jù)配置用途可分為三大類:(1)鏈配置。config項是定義鏈配置,會影響共識協(xié)議,雖然鏈配置對創(chuàng)世影響不大,但新區(qū)塊的出塊規(guī)則均依賴鏈配置。(2)創(chuàng)世區(qū)塊頭信息配置。nonce:隨機數(shù),對應(yīng)創(chuàng)世區(qū)塊Nonce字段。timestamp:UTC時間戳,對應(yīng)創(chuàng)世區(qū)塊Time字段。extraData:額外數(shù)據(jù),對應(yīng)創(chuàng)世區(qū)塊Extra字段。gasLimit:必填,燃料上限,對應(yīng)創(chuàng)世區(qū)塊GasLimit字段。difficulty:必填,難度系數(shù),對應(yīng)創(chuàng)世區(qū)塊Difficulty字段。搭建私有鏈時,需要根據(jù)情況選擇合適的難度值,以便調(diào)整出塊。minHash:一個哈希值,對應(yīng)創(chuàng)世區(qū)塊的MixDigest字段。和nonce值一起證明在區(qū)塊上已經(jīng)進行了足夠的計算。coinbase:一個地址,對應(yīng)創(chuàng)世區(qū)塊的Coinbase字段。(3)初始賬戶資產(chǎn)配置。alloc項是創(chuàng)世中初始賬戶資產(chǎn)配置。在生成創(chuàng)世區(qū)塊時,將此數(shù)據(jù)集中的賬戶資產(chǎn)寫入?yún)^(qū)塊中,相當(dāng)于預(yù)挖礦。這對開發(fā)測試和私有鏈非常好用,不需要挖礦就可以直接為任意多個賬戶分配資產(chǎn)。二以太坊開發(fā)環(huán)境1.以太坊開發(fā)工具及框架Geth常用命令:(1)初始化創(chuàng)世區(qū)塊:geth--datadir/path/to/datadirinit/path/to/genesis.json(2)啟動私有鏈:geth--identity"TestNode"--rpc--rpcport"8545"--datadir/path/to/datadir--port "30303"--nodiscoverconsole--identity:指定節(jié)點ID。--rpc:表示開啟HTTP-RPC服務(wù)。--rpcport:指定HTTP-RPC服務(wù)監(jiān)聽端口號(默認為8545)。--datadir:指定區(qū)塊鏈數(shù)據(jù)的存儲位置。--port:指定和其他節(jié)點連接所用的端口號(默認為30303)。--nodiscover:關(guān)閉節(jié)點發(fā)現(xiàn)機制,防止加入同樣配置的陌生節(jié)點。(3)快速同步模式:geth--fastconsole2>network_sync.log(4)瀏覽日志:tail-fnetwork_sync.log二以太坊開發(fā)環(huán)境1.以太坊開發(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è)置挖礦進程數(shù)目:geth--mine--minerthreads=4開始挖礦:miner.start(8)結(jié)束挖礦:miner.stop()查看挖礦速率:miner.getHashrate()查看區(qū)塊高度:eth.blockNumber查看挖礦賬戶:eth.coinbase設(shè)置挖礦賬戶:miner.setEtherbase(eth.accounts[0])二以太坊開發(fā)環(huán)境1.以太坊開發(fā)工具及框架(7)檢查連接狀態(tài):檢查是否連接:net.listening連接到的節(jié)點個數(shù):net.peerCount添加節(jié)點:admin.addPeer()查看添加的節(jié)點的信息:admin.peers(8)查看:查看區(qū)塊信息:eth.getBlock(n)交易信息:eth.getTransaction()查看正在等待確認的交易:eth.getBlock("pending",true).transactions二以太坊開發(fā)環(huán)境1.以太坊開發(fā)工具及框架2.Solidity編譯器以太坊官方社區(qū)提供了Solidity語言的編譯開發(fā)工具Solidity項目(/ethereum/solidity)。該項目是用C++編寫的,使用者可以根據(jù)自己的操作系統(tǒng)下載相應(yīng)發(fā)布版的二進制可執(zhí)行文件。如果想使用最新的版本,可以同步最新的代碼自行編譯生成可執(zhí)行文件。Solidity項目還提供了一個命令行工具:Solc。Solc不僅提供將Solidity編譯成字節(jié)碼的功能,也提供一些智能合約相關(guān)的信息,比如可以生成函數(shù)的簽名(調(diào)用智能合約各個函數(shù)時的依據(jù))、估算每個函數(shù)消耗的Gas等。下面我們展示一些常用的Sole使用方法。Solc命令行工具基本的使用模板:solc[options][input_file...]其中options可以是各種參數(shù),用于指定輸出的文件的格式和輸出路徑等。二以太坊開發(fā)環(huán)境1.以太坊開發(fā)工具及框架Solc最常使用的例子如下:solc--bin-otmp/solcoutputcontract.sol其中,--bin是指將Solidity智能合約編譯成十六進制字節(jié)碼。執(zhí)行完以上命令后,會在/tmp/solcoutput目錄下生成一個以代碼中定義的合約名(非文件名)命名的.bin的文件,里面存著編譯出來的字節(jié)碼。除了--bin,Solc也支持其他各種相關(guān)格式的輸出:--ast:所有源文件的抽象語法樹。--ast-json:json格式的抽象語法樹。--ast-compact-on:壓縮(去空格、空行)過后的json格式抽象語法樹。--asm:EVM的匯編語言。--asm-json:json格式的EVM匯編語言。--opcodes:操作碼(和--asm作用類似,區(qū)別在于asm會有一些對應(yīng)到源文件的注釋,而opcodes只有操作碼)。--bin:十六進制字節(jié)碼。--bin-runtime:運行時部分的十六進制碼(沒有構(gòu)造函數(shù)部分)。二以太坊開發(fā)環(huán)境1.以太坊開發(fā)工具及框架--clone-bin:克隆合約的十六進制字節(jié)碼。--abi:應(yīng)用程序二進制接口規(guī)范。--hashes:各個函數(shù)的簽名(十六進制名稱,用于調(diào)用智能合約時識別指定的函數(shù))。--userdoc:用戶使用說明文檔。--devdoc:開發(fā)者文檔。--metadata:編譯源文件的元數(shù)據(jù)(包括編譯器版本、abi、userdoc、devdoc、設(shè)置、源文件hash等,以json格式組織在一起)。例子里的-o是用來指定輸出文件路徑的。還有一些常用的選項:--optimize:編譯字節(jié)碼時進行優(yōu)化。--optimize-runsn(=200)在激活優(yōu)化功能時,為了進行優(yōu)化,試執(zhí)行合約的次數(shù)--add-std:添加標(biāo)準(zhǔn)合約。--librarieslibs:指定合約依賴的庫。--overwrite:在指定目錄里覆蓋已有的輸出文件。--pretty-json:當(dāng)用戶指定輸出的格式為json時,以可讀性更好的形式輸出。--gas:執(zhí)行編譯時打印出各個函數(shù)估測消耗的Gas數(shù)量。二以太坊開發(fā)環(huán)境1.以太坊開發(fā)工具及框架3.TruffleTruffle(/)是現(xiàn)在比較流行的Solidity智能合約開發(fā)框架,功能十分強大,可以幫助開發(fā)者迅速搭建起一個DApp。Truffle具體的特性有:(1)內(nèi)建智能合約編譯、鏈接、部署和二進制包管理功能;(2)支持對智能合約的自動測試;(3)支持自動化部署、移植;(4)支持公有鏈和私有鏈,可以輕松部署到不同的網(wǎng)絡(luò)中;(5)支持訪問外部包,可以方便地基于NPM和EthPM引入其他智能合約的依賴;(6)可以使用Truffle命令行工具執(zhí)行外部腳本。二以太坊開發(fā)環(huán)境2.以太坊開發(fā)環(huán)境搭建本節(jié)介紹以太坊客戶端Geth、編程語言Solidity和智能合約開發(fā)框架Truffle的安裝。1.Geth安裝Go-ethereum又稱Geth,是當(dāng)前最為成熟的以太坊客戶端,采用Go語言編寫,以下是Geth的安裝部署過程。(1)在Windows系統(tǒng)中一鍵安裝Geth安裝包下載地址為:/downloads/。下載完成后打開并安裝,默認路徑為C:\ProgramFiles\Geth。安裝完成后,打開命令行,輸入如命令gethhelp顯示如圖所示表示安裝成功。二以太坊開發(fā)環(huán)境2.以太坊開發(fā)環(huán)境搭建(2)在Linux系統(tǒng)中安裝Geth在Linux系統(tǒng)中,Geth可以從指定源直接安裝,也可以下載源代碼井編譯安裝。下面以Ubuntu16.04為例說明其安裝部署過程。需要注意的是,在Linux環(huán)境下部分命令需要root權(quán)限執(zhí)行,如apt命令等,本章的命令代碼中以“#”開頭的命令表示需要root權(quán)限執(zhí)行,以“$”開頭表示僅需在普通用戶權(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編譯完成后的二進制文件為./build/bin/geth。二以太坊開發(fā)環(huán)境2.以太坊開發(fā)環(huán)境搭建2.Solidity安裝(1)Windows系統(tǒng)中安裝Solidity一鍵安裝Nodejs。Nodejs官方長期支持的版本為8.10.0LTS,其下載地址為/dist/v8.10.0/node-v8.10.0-x64.msi。安裝完成后,在命令行或者終端中使用命令:npminstallsolc進行安裝。(2)在Linux系統(tǒng)中安裝Solidity使用NPM進行安裝:使用命令#apt-getinstallnodejs安裝nodejs。安裝完成后使用命令npminstallsolc進行安裝,再按如下方法進行源代碼編譯。首先使用如下命令加入相關(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二以太坊開發(fā)環(huán)境2.以太坊開發(fā)環(huán)境搭建然后使用如下命令下載并編譯Solidity:#gitclone--recursive/ethereum/webthree-umbrella.git#cdwebthree-umbrella#更新Solidity庫#./webthree-helpers/scripts/ethupdate.sh--no-push--simple-pull--projectsolidity#編譯Solidity#./webthree-helpers/scripts/ethbuild.sh--no-git--projectsolidity--all--cores4-DEVMJIT=0二以太坊開發(fā)環(huán)境2.以太坊開發(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以太坊智能合約開發(fā)三以太坊智能合約開發(fā)1.智能合約運行環(huán)境
以太坊虛擬機(EVM)是以太坊中智能合約的運行環(huán)境,它不僅被沙箱封裝起來,事實上它被完全隔離運行,也就是說運行在EVM內(nèi)部的代碼不能接觸到網(wǎng)絡(luò)、文件系統(tǒng)或者其它進程,甚至智能合約之間也只有有限的調(diào)用。1.業(yè)務(wù)流程以太坊虛擬機,簡稱EVM,是用來執(zhí)行以太坊上的交易的。業(yè)務(wù)流程如圖所示:輸入一筆交易,內(nèi)部會轉(zhuǎn)換成一個Message對象,傳入EVM執(zhí)行。如果是一筆普通轉(zhuǎn)賬交易,那么直接修改StateDB中對應(yīng)的賬戶余額即可。如果是智能合約的創(chuàng)建或者調(diào)用,則通過EVM中的解釋器加載和執(zhí)行字節(jié)碼,執(zhí)行過程中可能會查詢或者修改StateDB。三以太坊智能合約開發(fā)1.智能合約運行環(huán)境2.固定油費(IntrinsicGas)
對于每筆交易,先需要收取一筆固定油費,計算方法如下:如果交易不帶額外數(shù)據(jù)(Payload),比如普通轉(zhuǎn)賬,那么需要收取21000的油費。如果交易攜帶額外數(shù)據(jù),那么額外攜帶的數(shù)據(jù)也是需要收費的。具體來說是按字節(jié)收費:字節(jié)為0的收4塊,字節(jié)不為0收68塊,所以會看到很多做合約優(yōu)化的,目的就是減少數(shù)據(jù)中不為0的字節(jié)數(shù)量,從而降低油費gas消耗。三以太坊智能合約開發(fā)1.智能合約運行環(huán)境3.生成Contract對象
交易會被轉(zhuǎn)換成一個Message對象傳入EVM,而EVM則會根據(jù)Message生成一個Contract對象以便后續(xù)執(zhí)行:可以看到,Contract中會根據(jù)合約地址,從StateDB中加載對應(yīng)的代碼,然后送入解釋器執(zhí)行。執(zhí)行合約能夠消耗的油費有一個上限,就是節(jié)點配置的每個區(qū)塊能夠容納的GasLimit。三以太坊智能合約開發(fā)1.智能合約運行環(huán)境4.送入解釋器執(zhí)行代碼跟輸入都有了,就可以送入解釋器執(zhí)行了。EVM是基于棧的虛擬機,解釋器中需要操作四大組件:PC:類似于CPU中的PC寄存器,指向當(dāng)前執(zhí)行的指令。Stack:執(zhí)行堆棧,位寬為256bits,最大深度為1024。Memory:內(nèi)存空間。Gas:油費池,耗光油費則交易執(zhí)行失敗。三以太坊智能合約開發(fā)1.智能合約運行環(huán)境具體解釋執(zhí)行的流程參見:EVM的每條指令稱為一個OpCode,占用一個字節(jié),所以指令集最多不超過256,具體描述參見:https://ethervm.io。首先PC會從合約代碼中讀取一個OpCode,然后從一個JumpTable中檢索出對應(yīng)的operation,也就是與其相關(guān)聯(lián)的函數(shù)集合。接下來會計算該操作需要消耗的油費,如果油費耗光則執(zhí)行失敗,返回ErrOutOfGas錯誤。如果油費充足,則調(diào)用execute()執(zhí)行該指令,根據(jù)指令類型的不同,會分別對Stack、Memory或者StateDB進行讀寫操作。三以太坊智能合約開發(fā)1.智能合約運行環(huán)境5.調(diào)用合約函數(shù)
前面分析完了EVM解釋執(zhí)行的主要流程,那么EVM怎么知道交易想調(diào)用的是合約里的哪個函數(shù)呢?前面提到跟合約代碼一起送到解釋器里的還有一個Input,而這個Input數(shù)據(jù)是由交易提供的。三以太坊智能合約開發(fā)1.智能合約運行環(huán)境Input數(shù)據(jù)通常分為兩個部分:前面4個字節(jié)被稱為“4-bytesignature”,是某個函數(shù)簽名的Keccak哈希值的前4個字節(jié),作為該函數(shù)的唯一標(biāo)識。后面跟的就是調(diào)用該函數(shù)需要提供的參數(shù)了,長度不定。例如:在部署完A合約后,調(diào)用add(1)對應(yīng)的Input數(shù)據(jù)是:0x87db03b70000000000000000000000000000000000000000000000000000000000000001。而在編譯智能合約的時候,編譯器會自動在生成的字節(jié)碼的最前面增加一段函數(shù)選擇邏輯:首先通過CALLDATALOAD(把輸入數(shù)據(jù)加載到Stack中)指令將“4-bytesignature”壓入堆棧中,然后依次跟該合約中包含的函數(shù)進行比對,如果匹配則調(diào)用JUMPI指令跳入該段代碼繼續(xù)執(zhí)行。三以太坊智能合約開發(fā)1.智能合約運行環(huán)境6.合約調(diào)用合約合約內(nèi)部調(diào)用另外一個合約,有4種調(diào)用方式:1)CALL。2)CALLCODE。3)DELEGATECALL。4)STATICALL。這里先以最簡單的CALL為例,調(diào)用流程如下圖所示:三以太坊智能合約開發(fā)1.智能合約運行環(huán)境6.合約調(diào)用合約合約內(nèi)部調(diào)用另外一個合約,有4種調(diào)用方式:1)CALL。2)CALLCODE。3)DELEGATECALL。4)STATICALL。三以太坊智能合約開發(fā)1.智能合約運行環(huán)境這里先以最簡單的CALL為例,調(diào)用流程如下圖所示:可以看到,調(diào)用者把調(diào)用參數(shù)存儲在內(nèi)存中,然后執(zhí)行CALL指令。CALL指令執(zhí)行時會創(chuàng)建新的Contract對象,并以內(nèi)存中的調(diào)用參數(shù)作為其Input。解釋器會為新合約的執(zhí)行創(chuàng)建新的Stack和Memory,從而不會破環(huán)原合約的執(zhí)行環(huán)境。新合約執(zhí)行完成后,通過RETURN指令把執(zhí)行結(jié)果寫入之前指定的內(nèi)存地址,然后原合約繼續(xù)向后執(zhí)行。三以太坊智能合約開發(fā)1.智能合約運行環(huán)境7.創(chuàng)建合約如果某一筆交易的to地址為nil,則表明該交易是用于創(chuàng)建智能合約的。首先需要創(chuàng)建合約地址,采用下面的計算公式:Keccak(RLP(call_addr,nonce))[:12]。也就是說,對交易發(fā)起人的地址和nonce進行RLP編碼,再算出Keccak哈希值,取后20個字節(jié)作為該合約的地址。下一步就是根據(jù)合約地址創(chuàng)建對應(yīng)的stateObject,然后存儲交易中包含的合約代碼。該合約的所有狀態(tài)變化會存儲在一個storagetrie中,最終以Key-Value的形式存儲到StateDB中。代碼一經(jīng)存儲則無法改變,而storagetrie中的內(nèi)容則是可以通過調(diào)用合約進行修改的,比如通過SSTORE指令。三以太坊智能合約開發(fā)2.智能合約開發(fā)語言1結(jié)構(gòu)Solidity中的合約與面向?qū)ο缶幊陶Z言中的類(Class)很相似,在一個合約中可以聲明多種成員,包括狀態(tài)變量、函數(shù)、函數(shù)修改器、事件等。同時,一個合約可以繼承另一個合約。本節(jié)將簡單介紹各種成員的形式和作用。狀態(tài)變量是永久存儲在合約賬戶存儲中的值,用于保存合約的狀態(tài)。Solidity語言提供了多種類型的變量,下面的代碼在合約中聲明了一個無符號整數(shù)類型的狀態(tài)變量:contractSimpleStorage{uintsomeData;//狀態(tài)變量}函數(shù)是合約代碼的執(zhí)行單位,一個合約中可能包含許許多多提供各種功能的函數(shù),它們相互調(diào)用,共同組成合約的工作邏輯。合約中還有一些特殊的函數(shù),比如合約創(chuàng)建時執(zhí)行的構(gòu)造函數(shù)、想要調(diào)用一個不存在的函數(shù)時自動執(zhí)行的fallback函數(shù)等。下面的代碼在合約中聲明了一個不做任何操作的函數(shù):contractSimpleAction{functiondoNothing(){//函數(shù)}}三以太坊智能合約開發(fā)2.智能合約開發(fā)語言函數(shù)修改器可用于改變函數(shù)的行為,在函數(shù)執(zhí)行前或執(zhí)行后插入其他邏輯,比如在函數(shù)執(zhí)行前進行參數(shù)檢查等。下面的代碼演示了如何使用一個函數(shù)修改器確保一個函數(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ù)修改器}}三以太坊智能合約開發(fā)2.智能合約開發(fā)語言事件是以太坊日志協(xié)議的高層次抽象,用于記錄合約執(zhí)行過程中發(fā)生的各種事件和狀態(tài)變化。在下面的代碼中,當(dāng)donate函數(shù)被調(diào)用時會自動記錄調(diào)用者的地址和以太幣數(shù)量,以供將來查看:contractFunding{eventDeposit(addressfrom,uintamount);//事件functiondonate()payable{Deposit(msg.sender,msg.value);//觸發(fā)事件}}三以太坊智能合約開發(fā)2.智能合約開發(fā)語言2變量類型在計算機程序中需要使用變量來存儲值。值有多種類型,比如整數(shù)、小數(shù)、字符串等,不同類型的值需要存儲在不同類型的變量中。Solidity是一門靜態(tài)類型語言,每一個變量都必須指定變量的類型,否則無法正確編譯。Solidity提供了一些基礎(chǔ)的變量類型,可以使用這些基礎(chǔ)類型組合形成復(fù)雜的類型。變量類型根據(jù)參數(shù)傳遞方式的不同可以分為兩類:值類型和引用類型。值類型在每次賦值或者作為參數(shù)傳遞時都會創(chuàng)建一份拷貝,引用類型則有兩種存儲地點,即賬戶存儲和內(nèi)存。狀態(tài)變量與部分類型的局部變量(數(shù)組、結(jié)構(gòu)體等復(fù)雜類型)是默認保存在賬戶存儲中的,而函數(shù)的參數(shù)和其他簡單類型的局部變量則保存在內(nèi)存中。必要時還可以在聲明變量時加上memory或者storage修飾詞來強制限定變量的存儲地點。數(shù)據(jù)的存儲地點非常重要,引用類型在不同的存儲位置賦值,其產(chǎn)生的結(jié)果完全不同。值類型包括布爾類型、整數(shù)類型、地址類型、固定長度字節(jié)數(shù)組等,引用類型包括數(shù)組、結(jié)構(gòu)體等。三以太坊智能合約開發(fā)2.智能合約開發(fā)語言1.值類型(1)布爾類型布爾類型(bool)可能的取值是常量true和false。支持!(邏輯非)、&&(邏輯與)、||(邏輯或)、==(等于)、!=(不等于)等運算符。(2)整數(shù)類型int表示有符號整數(shù),uint表示元符號整數(shù)。變量支持通過后綴指明變量使用多少位進行存儲,后綴必須是8~256范圍內(nèi)8的整數(shù)倍,比如int8、intl6、int256。如果沒有顯式指明后綴,int默認表示int256,uint默認表示uint256。(3)枚舉類型枚舉類型(enums)是一種用戶自定義的類型,用于聲明一些命名的常數(shù)。下面的代碼演示了如何聲明和使用枚舉類型。枚舉類型可以與整數(shù)類型之間顯式地進行類型轉(zhuǎn)換,但是不能自動進行隱式轉(zhuǎn)換。枚舉類型的成員默認從0開始,依次遞增,在下面的例子中DEFAULT、ONE、TWO分別對應(yīng)整數(shù)0、1、2。contractSirnpleEnurn{enurnSorneEnurn{DEFAULT,ONE,TWO};//聲明一個枚舉類型}三以太坊智能合約開發(fā)2.智能合約開發(fā)語言(4)地址類型地址類型(address)的長度為20字節(jié)(與以太坊賬戶地址長度一致),其是合約的基類,擁有-些成員方法和變量。從Solidity0.5.0版本開始,合約不再繼承自地址類型,但是開發(fā)者仍可以通過顯式類型轉(zhuǎn)換將合約轉(zhuǎn)換為地址類型。<address>.balance:類型為uint,表示賬戶的余額,單位是wei。<address>.transfer(uint256amount):發(fā)送amount數(shù)量的以太幣給address表示的賬戶,單位是wei,失敗會拋出異常。<address>.send(uint256amount)returns(bool):與<address>.transfer類似,同樣是進行以太幣的轉(zhuǎn)賬。兩者的區(qū)別是如果執(zhí)行失敗<address>.transfer會拋出異常并且終止代碼,<address>.send則是返回false,代碼繼續(xù)執(zhí)行。需要注意的是,以太幣的轉(zhuǎn)賬會有失敗的風(fēng)險,為了確保以太幣轉(zhuǎn)賬的安全,一定要檢查<address>.send的返回值,或者使用<address>.transfer。三以太坊智能合約開發(fā)2.智能合約開發(fā)語言<address>.call(...)returns(bool):發(fā)起底層的CALL指令,失敗將返回false。<address>.callcode(...)returns(bool):發(fā)起底層的CALLCODE指令,失敗將返回false。<address>.delegatecall(...)returns(bool):發(fā)起底層的DELETECALL指令,失敗將返回false。以上三個函數(shù)提供了一個底層的、靈活的方式與合約進行交互,<address>.call(...)可以接受任何長度、任何類型的參數(shù),每個參數(shù)將被填充到32字節(jié)并拼接在一起。但有一種例外情況,當(dāng)?shù)谝粋€參數(shù)的長度恰好是4字節(jié)時,該參數(shù)不會被打包成32字節(jié),而是被作為指定函數(shù)的簽名。在下面的代碼中,第一個參數(shù)bytes4(keccak256("fun(uint256)"))為長度4字節(jié)的函數(shù)簽名,表示調(diào)用一個函數(shù)簽名為fun(uint256)的函數(shù),4則是實際傳給fun函數(shù)的參數(shù):addressnameReg=0x72ba7d8e73fe8eb666ea66babc8116a4lbfb10e2;nameReg.call(bytes4(keccak256("fun(uint256)")),4);函數(shù)簽名使用基本類型的典型格式定義,如果有多個參數(shù)要使用逗號隔開,并且要去掉表達式中的所有空格。<address>.delegatecall與<address>.call的區(qū)別是調(diào)用delegatecall時僅執(zhí)行代碼,而諸如賬戶存儲、余額等其他方面都是用當(dāng)前合約的數(shù)據(jù),這是為了使用另一個合約中的庫代碼。為了代碼能夠順利執(zhí)行,調(diào)用者必須確保本合約中的存儲變量與delegatecall執(zhí)行的代碼相兼容。<address>.callcode是早期使用的接口,比起call擁有更低的權(quán)限,無法訪問msg.sender、msg.value等變量。以上三個函數(shù)是非常底層的函數(shù)調(diào)用。在實際情況中建議開發(fā)者還是盡量不要使用,因為它們破壞了Solidity語言的類型安全。三以太坊智能合約開發(fā)2.智能合約開發(fā)語言2.引用類型(1)數(shù)組Solidity中的數(shù)組包括固定長度的數(shù)組,以及運行時可動態(tài)改變長度的動態(tài)數(shù)組。對于賬戶存儲中的數(shù)組,數(shù)組元素可以是任何類型,而內(nèi)存中的數(shù)組,其數(shù)組元素不可以是映射。一個包含固定k個T類型數(shù)據(jù)的數(shù)組可以用T[k]語句來聲明,一個動態(tài)長度的數(shù)組用T[]來聲明。下面來了解數(shù)組的成員變量和函數(shù)。1)length:數(shù)組可以通過訪問length成員獲取數(shù)組的長度。對于賬戶存儲中的數(shù)組,可以通過修改數(shù)組的length成員動態(tài)地改變數(shù)組的長度,而內(nèi)存中的數(shù)組在創(chuàng)建之后,其length成員就已經(jīng)完全確定了,無法修改。2)push:賬戶存儲中的動態(tài)數(shù)組以及bytes類型的變量,可以通過調(diào)用push方法在數(shù)組尾部添加元素,返回值為數(shù)組新的長度。3)bytes和string:一種特殊的數(shù)組。bytes通常用于表示任意長度的字節(jié)數(shù)據(jù),而string用于表示任意長度的字符數(shù)據(jù)(UTF-8編碼)。但是string不支持length成員和下標(biāo)訪問。兩者之間可以互相轉(zhuǎn)換,比如bytes(s)可以將字符串s轉(zhuǎn)換成bytes類型。但是需要注意字符串中的字符是以UTF-8編碼保存的,轉(zhuǎn)換成bytes類型時會將多字節(jié)的字符展開,此時如果使用下標(biāo)的方式訪問bytes,有可能只訪問到一個字符的部分編碼。如果想要訪問一個字符串的長度,可以使用bytes(s).length,但是要注意這樣獲取到的長度同樣是以UTF-8編碼計算的長度,而不是以單個字符計算的長度。還有一點需要注意,因為EVM的限制,外部函數(shù)調(diào)用無法返回一個動態(tài)長度的數(shù)組,唯一的做法是將需要返回的內(nèi)容放在一個足夠長的定長數(shù)組中。三以太坊智能合約開發(fā)2.智能合約開發(fā)語言(2)結(jié)構(gòu)體Solidity語言中的結(jié)構(gòu)體(struct)與C語言中很相似,允許開發(fā)者根據(jù)需要自定義變量類型。下面的代碼展示了如何聲明一個結(jié)構(gòu)體:contractTest{structStudent{stringname;uintage;uintscore;stringsex;}}結(jié)構(gòu)體可以作為映射或者數(shù)組中的元素,其本身也可以包含映射和數(shù)組等類型,但是不能將一個結(jié)構(gòu)體用作其本身的成員,因為結(jié)構(gòu)體嵌套自身會導(dǎo)致無限循環(huán)。三以太坊智能合約開發(fā)2.智能合約開發(fā)語言(3)映射映射(Mapping)是一種鍵值對映射關(guān)系的存儲結(jié)構(gòu),我們使用mapping(KeyType=>ValueType)來聲明一個映射,其中KeyType可以是除了映射、動態(tài)數(shù)組、合約、枚舉類型、結(jié)構(gòu)體以外的任何類型,ValueType則可以是任意類型,包括映射本身。映射可以看作一個散列表,其中所有可能存在的鍵都有一個默認值,默認值的二進制編碼全為0,所以映射并沒有長度的概念。同時,映射并不存儲鍵的數(shù)據(jù),而僅僅存儲它的Keccak-256散列值。三以太坊智能合約開發(fā)2.智能合約開發(fā)語言3.類型轉(zhuǎn)換如果一個運算符作用于兩個類型不同的變量,編譯器會自動嘗試將一個變量類型轉(zhuǎn)換為另一個變量的類型,這是隱式類型轉(zhuǎn)換。通常,在語義合理并且不會造成信息損失的情況下允許進行隱式類型轉(zhuǎn)換,比如uint8轉(zhuǎn)換為uint16或者uint32,但是int8不能轉(zhuǎn)換成uintl6,這是因為uintl6不能表示負數(shù)。任何可以轉(zhuǎn)換為uintl6的變量都可以轉(zhuǎn)換為address類型。有時在編譯器不能進行隱式類型轉(zhuǎn)換的情況下可以強行進行類型轉(zhuǎn)換,這叫做顯式類型轉(zhuǎn)換。但是請注意,進行顯式類型轉(zhuǎn)換前必須知道你在進行什么操作并且確定操作的結(jié)果是你想要的,否則會造成很多異常情況。uint32a=0x12345678;uintl6b=uintl6(a);以上代碼將uint32類型轉(zhuǎn)換為uint16類型,這導(dǎo)致了數(shù)值的高16位被截斷。三以太坊智能合約開發(fā)2.智能合約開發(fā)語言4.運算符Solidity語言中也包括算術(shù)運算符、比較運算符、位運算等。運算符的優(yōu)先順序如下圖所示。三以太坊智能合約開發(fā)2.智能合約開發(fā)語言在以上運算符中,需要特別注意delete運算符。在其他編程語言中,delete經(jīng)常作為一種與new相反的內(nèi)存管理操作,用于釋放內(nèi)存。但是在Solidity中,delete僅僅是一項賦值運算,它用作給變量賦初始值。例如,deletea與a=0是等效的;delete用于數(shù)組表示將該數(shù)據(jù)變成一個長度為0的空數(shù)組;當(dāng)作用于固定長度數(shù)組時,該數(shù)組將變?yōu)橐粋€長度不變但每個元素都被賦值為默認值的數(shù)組;當(dāng)作用于結(jié)構(gòu)體時,delete將遞歸作用于除映射外的所有成員;delete對映射無效。下面的代碼展示了delete對復(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}}三以太坊智能合約開發(fā)2.智能合約開發(fā)語言5.類型推斷Solidity語言中,var關(guān)鍵字和C++語言中的auto關(guān)鍵字類似,用于類型推斷。uint24x=0xl23;vary=x;var聲明的變量將會擁有第一個賦值變量的類型,比如上面一段代碼中,y的類型是uint24。在使用中有時需要小心,比如for(vari=0;i<2000;i++){…}將是一個無限循環(huán),因為根據(jù)類型推斷i的類型為unit8,所有i永遠都不會滿足跳出循環(huán)的條件(i>=2000)。三以太坊智能合約開發(fā)2.智能合約開發(fā)語言3內(nèi)置單位、全局變量和函數(shù)Solidity包含一些內(nèi)置單位、全局變量和函數(shù)以供使用。(1)貨幣單位一個字面量的數(shù)字可以使用wei、finney、szabo和ether等后綴表示不同的額度,不加任何后綴則默認單位為wei。比如“2ether==2000finney”的結(jié)果是true。(2)時間單位與貨幣單位相似,不同的時間單位可以以秒為基本單位進行轉(zhuǎn)換,不同的單位定義如下:1==1seconds1minutes==60seconds1hours==60minutes1days==24hours1weeks==7days1years==365days特別注意,如果想要使用這些單位進行時間計算必須特別小心,因為一年并不總是有365天;同時因為閏秒的存在,一天也并不總是24小時。因為閏秒的計算難以預(yù)測,因此為保證日歷庫的精確性,需要由外部供應(yīng)商定期更新。三以太坊智能合約開發(fā)2.智能合約開發(fā)語言(3)區(qū)塊和交易屬性有一些方法和變量可以用于獲取區(qū)塊和交易的屬性。block.blockhash(uintblo
溫馨提示
- 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
- 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
- 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會有圖紙預(yù)覽,若沒有圖紙預(yù)覽就沒有圖紙。
- 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
- 5. 人人文庫網(wǎng)僅提供信息存儲空間,僅對用戶上傳內(nèi)容的表現(xiàn)方式做保護處理,對用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對任何下載內(nèi)容負責(zé)。
- 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請與我們聯(lián)系,我們立即糾正。
- 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時也不承擔(dān)用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。
最新文檔
- 創(chuàng)意行業(yè)月度個人工作計劃
- 2025-2030中國鍛鋁合金行業(yè)市場發(fā)展趨勢與前景展望戰(zhàn)略研究報告
- 2025-2030中國鋰鎳錳鈷氧化物(NMC)行業(yè)市場發(fā)展趨勢與前景展望戰(zhàn)略研究報告
- 2025-2030中國鑄造鞋行業(yè)市場發(fā)展趨勢與前景展望戰(zhàn)略研究報告
- 2025-2030中國鋁鑄件行業(yè)市場發(fā)展趨勢與前景展望戰(zhàn)略研究報告
- 2025-2030中國鋁中間合金行業(yè)市場發(fā)展趨勢與前景展望戰(zhàn)略研究報告
- 2025-2030中國鐵路電力電氣化行業(yè)發(fā)展分析及發(fā)展趨勢與投資前景預(yù)測研究報告
- 2025-2030中國鋼制啤酒桶行業(yè)市場發(fā)展趨勢與前景展望戰(zhàn)略研究報告
- 2025-2030中國金融數(shù)據(jù)處理行業(yè)市場深度調(diào)研及競爭格局與投資研究報告
- 2025-2030中國酒店行業(yè)市場發(fā)展分析及發(fā)展趨勢與投資前景研究報告
- 建筑節(jié)能新路徑:嚴(yán)寒地區(qū)老舊建筑改造
- 2024年廣州農(nóng)村商業(yè)銀行招聘筆試真題
- 2024年寧波樞智交通科技有限公司招聘考試真題
- 數(shù)學(xué)丨湖北省八市2025屆高三下學(xué)期3月聯(lián)考數(shù)學(xué)試卷及答案
- 2024年貴州省普通高中學(xué)業(yè)水平選擇性考試地理試題
- 2024年中國工商銀行遠程銀行中心招聘考試真題
- 2025年我的師德小故事標(biāo)準(zhǔn)教案21
- 3 學(xué)會反思第二課時 養(yǎng)成反思好習(xí)慣 教學(xué)設(shè)計-2023-2024學(xué)年道德與法治六年級下冊統(tǒng)編版
- 二零二五年度汽車銷售業(yè)務(wù)員勞動合同(新車與二手車)
- 護理人員中醫(yī)技術(shù)使用手冊(2024版)
- 滬教版(五四學(xué)制)(2024)六年級下冊單詞表+默寫單
評論
0/150
提交評論