本書鏈接:https://github.com/oldnicke/MasteringEthereum
以太坊是一種確定性但實際無界的狀態(tài)機,有兩個基本功能:全局可訪問的單例狀態(tài)和對狀態(tài)進行更改的虛擬機。
進一步說,以太坊是一個開源的、全球的去中心化計算架構(gòu),它執(zhí)行被稱為智能合約的程序,使用區(qū)塊鏈來同步和存儲系統(tǒng)的狀態(tài),使用稱為ether的加密貨幣來計量和約束執(zhí)行資源成本。
狀態(tài)機
狀態(tài)機是有限狀態(tài)自動機的簡稱,是現(xiàn)實事物運行規(guī)則抽象而成的一個數(shù)學模型。
包含四大概念:狀態(tài)、事件、動作、變換
區(qū)塊鏈的組件
- 一個鏈接參與者,傳播交易和包含已驗證交易的區(qū)塊的點對點網(wǎng)絡(基于gossip協(xié)議)
- 狀態(tài)機中實現(xiàn)的一系列共識規(guī)則
- 消息,以交易的形式表示
- 根據(jù)共識規(guī)則處理交易的狀態(tài)機
- 分布式數(shù)據(jù)庫,區(qū)塊鏈,記錄所有狀態(tài)轉(zhuǎn)移的日志
- 共識算法,通過強制參與者競爭并使用共識規(guī)則約束他們,來分散區(qū)塊鏈的控制權
- 上述內(nèi)容的開源軟件實現(xiàn)
gossip
gossip就是一個并行的圖廣度優(yōu)先遍歷算法。假設A得到某些信息,更新了自身的信息,A需要將信息告訴B、C等,然后B、C告訴其他的D、E、F、G,一直遍歷。如果節(jié)點B收到A的消息,發(fā)現(xiàn)自己早就知道這個消息就直接忽略,從而可以防止圖重復遍歷。和路由器使用的路由表同步算法類似,只不過路由器還會維護每個路徑的cost。gossip常用于維護集群的拓撲結(jié)構(gòu)(路由器也是),被redis、consul使用。
gas
以太坊無法預測合約是否會終止,于是通過gas來限制智能合約使用的資源。
每一條指令都以gas為單位的成本,如果實際計算所消耗的gas超過了gas limit,EVM將終止執(zhí)行。
gas是以太坊用于允許圖靈完備計算的機制
從區(qū)塊鏈到DApps
以太坊從作為一種可用于各種用途的通用區(qū)塊鏈開始,其愿景已經(jīng)擴展為編程DApp到平臺。
DApp代表著比智能合約更廣闊的視角。
一個DApp至少是一個智能合約和一個web用戶界面。
進一步說,DApp是一個基于開放的、去中心化的、點對點基礎架構(gòu)服務的web應用程序
此外,DApp還常常包括其他去中心化組件:
去中心化p2p存儲協(xié)議和平臺
去中心化p2p消息傳遞和平臺
DApp常寫作DApps,D是拉丁字母,讀作 ETH
web3
目前web2
DApp的概念旨在將web引入web3,去中心化對等協(xié)議將進入web應用程序的方方面面
web3代表著web應用程序的新愿景和焦點:從集中擁有和管理的應用程序,到去中心化協(xié)議的應用程序。
Ethereum+web3js+JS庫,會在瀏覽器中運行的js應用程序和以太坊區(qū)塊鏈相連接。此外,web3.js庫還提供了一個名為Swarm的P2P存儲網(wǎng)絡接口和一個成文Whisper的P2P消息傳遞服務。根據(jù)以上組件,開發(fā)人員可以使用完整的應用程序開發(fā)套件來構(gòu)建web3 DApp。
以太坊:ethereum
以太坊貨幣:以太:ether:Ξ
賬戶
外部所有賬戶(EOA):擁有私人密鑰的賬戶,可以控制資金或?qū)霞s的訪問
合約賬戶:合約賬戶由以太坊記錄,由EVM執(zhí)行的軟件程序的邏輯擁有和控制
以太坊客戶端主要有六種語言編寫
Go:Geth
Rust:parity
C++:cpp-ethereum
Python:pyethereum
Scala:mantis
Java:harmony
輕量級客戶端可連接現(xiàn)有網(wǎng)絡,如自己的完整節(jié)點、公共區(qū)塊鏈、公開或許可的測試網(wǎng)或本地私有區(qū)塊鏈。
實際上常用輕量級客戶端(metamask)在所有不同節(jié)點選項之間切換。
此外輕量級客戶端常和錢包換用,輕量級客戶端除了提供錢包的交易功能外還提供API。
mainnet&testnet
密碼學是以太坊技術基礎
加密可以用來證明秘密的知識而不泄漏(數(shù)字簽名),或驗證數(shù)據(jù)的真實性(數(shù)字指紋)
加密不是以太坊的重要組成部分,因為起通信和交易數(shù)據(jù)沒有加密也不需要加密
簡介
在EOA中,以太的所有權通過數(shù)字密鑰、以太坊地址和數(shù)字簽名建立
- 公鑰+私鑰(數(shù)字密鑰)
數(shù)字密鑰并不儲存在區(qū)塊鏈上或在區(qū)塊鏈上傳輸,而是儲存在錢包這個簡單的數(shù)據(jù)庫中
錢包中的數(shù)字密鑰完全獨立于以太坊協(xié)議,可以由錢包軟件生成和控制而無需連接區(qū)塊鏈或互聯(lián)網(wǎng)
數(shù)字密鑰可以實現(xiàn)區(qū)塊鏈的去中心化信任和控制、所有權的證明等
?在以太坊的交易中需要包含有效的數(shù)字簽名,該簽名只能由私鑰生成。因此得到了數(shù)字密鑰就意味著成為了該地址的資金所有者
?數(shù)字密鑰成對,為公鑰和私鑰。公鑰類似賬號,私鑰類似密碼。私鑰大部分時候在錢包內(nèi)保存。在交易付款中,收款人由以太坊地址表示,這往往代表公鑰。但也并非所有的以太坊地址都代表公鑰,也可以代表合同。
?公鑰從私鑰中派生而來,公鑰用于接收資金,私鑰用于創(chuàng)建數(shù)字簽名來簽署交易以及支付資金。數(shù)字簽名可用于驗證合同的所有者或者用戶。
?公鑰密碼系統(tǒng)的特點是,很容易在一個方向上計算,但很難在反方向上計算。因此數(shù)字密鑰的安全性和簽名的不可偽造得以保證。
數(shù)學部分
-
以太坊公鑰
以太坊公鑰是橢圓曲線上的點,是一組滿足橢圓曲線方程的XY坐標
簡單來說,公鑰是兩個數(shù)字連接在了一起,這兩個數(shù)字是從一次單向的計算中從私鑰產(chǎn)生,但你不能從公鑰中算出私鑰 -
詳細說明
公鑰使用橢圓曲線乘法和私鑰計算:
其中k是私鑰,G是一個常數(shù)點,K的結(jié)果是公鑰
橢圓曲線乘法被密碼學家稱為一種單向函數(shù) -
一個橢圓曲線
它將被定義在素數(shù)階上,但在數(shù)學上與實數(shù)階的橢圓曲線相同
而以太坊和比特幣使用的橢圓曲線加密算法都是secp256k1 曲線
曲線可視化:https://www.desmos.com/calculator/ialhd71we3?lang=zh-CN
函數(shù)為:
它被定義在素數(shù)階p上,
常數(shù)點G是橢圓曲線上一點,被稱為基點,很大
參數(shù)n,是使得的最小正整數(shù)(此處0指無窮遠點),具體值是
-
計算
定義加法:
P1、P2在橢圓曲線上,P3=P1+P2,P3也在橢圓曲線上。
在幾何上,連接P1、P2,與橢圓曲線相交于P3'=(x,y)。而P3=(x,-y)。
無窮遠點:
大致等于零點,常用(0,0)表示,雖然(0,0)不在橢圓曲線上
P1、P2是同一個點,P1、P2的連線是切線;一些時候,切線垂直,此時的P3為無窮遠點
若P1是無窮遠點,P1+P2=P2;此處看到無窮遠點類似零點
定義乘法: -
生成公鑰
已知錢包生成了一個大整數(shù)k(為私鑰)和一個橢圓曲線上的點生成點G(所有以太坊用戶的生成點相同)
則公鑰(相當于k次切線+反向,最后可以獲得一個點)
密碼庫(OpenSSL、libsecp256k1)可以幫助計算這個點K=(x,y)
連接公鑰的X和Y坐標:
因為以太坊使用的是未壓縮的公鑰,在前面加上04
即可的得到被序列化后的公鑰 -
計算以太坊地址
計算以太坊地址使用的是未加04前的公鑰
用以太坊的哈希加密函數(shù):Keccak-256 處理
保留最后的20個字節(jié)(40位)(大端序中的最低有效字節(jié))
加上0x表示16進制
即可得到以太坊地址
公鑰:64字節(jié) //046e145ccef1033dea239875dd00dfb4fee6e3348b84985c92f103444683bae07b83b5c38e5e2b0c8529d7fa3f64d46daa1ece2d9ac14cab9477d042c84c32ccd0
私鑰:32字節(jié) //f8f8a2f43c8376ccb0871305060d7b27b0554d2cc72bccf41b2705608452f315
地址:20字節(jié) //0x001d3f1ef827552ae1114027bd3ecf1f086ba0f9
如何判斷正在使用那種哈希加密函數(shù)(FIPS-202還是Keccak-256)
給一個預定輸入,看輸出判斷
常用空輸入
互換客戶端地址協(xié)議(ICAP)為以太坊地址提供多功能、校驗和互操作編碼(錢包檢測是否輸入錯誤地址)
由于ICAP活名稱服務器部署緩慢,現(xiàn)在只有幾個錢包支持ICAP。
新標準以太坊改進建議55(EIP-55)
https://github.com/Ethereum/EIPs/blob/master/EIPS/eip-55.md
通過修改十六進制地址的大小寫,EIP-55為以太坊地址提供了向后兼容的校驗和
通過修改地址中字母字符的大小寫,我們可以傳達一個校驗和,用來保護地址的完整性,防止輸入或讀取錯誤。如果錢包不支持EIP-55校驗和,會忽略大寫的事實。但如果錢包支持EIP-55校驗和,就可以驗證它并以99.986%的準確度檢測錯誤。
EIP-55計算方式
簡單來說先計算小寫地址的哈希(不帶0x)
如果哈希的相應數(shù)字大于等于0x8,則地址的字母要大寫
輸入地址的時候只要輸錯一位,計算出來的哈希就會發(fā)生變化,導致大小寫的位置完全對不上
-
錢包只包含密鑰,代幣記錄在區(qū)塊鏈中,用戶通過錢包中的密鑰簽署交易來控制網(wǎng)絡上的代幣。
-
目前有兩種主要類型的錢包,通過他們包含的密鑰是否彼此相關來區(qū)分
一種是 非確定性錢包 JBOK,其中密鑰是從隨機數(shù)中獨立生成的,密鑰之間互不關聯(lián)(不鼓勵使用,麻煩,一般只有簡單測試的時候用)
一種是 確定性錢包 HD,所有密鑰來自單個主密鑰(成為seed)這種錢包中所有段密鑰互相關聯(lián),只要seed還在就可以再次生成。確定性錢包使用了許多不同的密鑰推導方法,最常用的是樹狀結(jié)構(gòu)。
HD錢包是管理許多密鑰和地址的強大機制,并可以將他們與一系列英語單詞相結(jié)合(助記詞),更易于轉(zhuǎn)錄和跨錢包的導入導出。這個標準由BIP-39確定。 -
助記詞和腦錢包不同,區(qū)別在于腦錢包由用戶選擇單詞,而助記次由錢包的隨機性創(chuàng)建并呈現(xiàn)給用戶
-
種子512 bits(64位16進制)
-
種子到助記詞
種子不是私鑰,種子可以生成私鑰。助記詞即是錢包的備份。
助記詞是表示HD的種子的隨機數(shù)的單詞序列,從單詞序列足以重建種子并派生所有的密鑰。
創(chuàng)建一個128到256位的隨機序列(熵)。?不同長度的隨機序列,最后生成的助記詞數(shù)量不同。
通過取其SHA256哈希的第一部分(熵長度/32)來創(chuàng)建隨機序列的校驗和。
將校驗和添加到隨機序列的末尾。
將序列按照11bits劃分。
將每個11bits的值映射到預定義字典中的2048個詞中的一個。
助記詞就是單詞的序列。 -
助記詞到種子
PBKDF2是一個密鑰擴展函數(shù),可以將熵導出成更長的種子,然后使用種子構(gòu)建HD并生成密鑰。
密鑰擴展函數(shù)有兩個參數(shù):助記詞和salt
PBKDF2密鑰擴展函數(shù)的第一個參數(shù)是步驟6產(chǎn)生的助記詞。
PBKDF2密鑰擴展函數(shù)的第二個參數(shù)是鹽。鹽由用戶提供的密碼字符串(在產(chǎn)生種子時指定,會改變助記詞)和“mnemonic”組合起來。
PBKDF2使用2048輪HMAC-SHA512哈希算法,擴展助記詞和鹽,生成512位的種子。 -
128-bit entropy mnemonic code, with passphrase, resulting seed
Entropy input (128 bits):0c1e24e5917779d297e14d45f14e1a1a
Mnemonic (12 words):army van defense carry jealous true garbage claim echo media make crunch
Passphrase:SuperDuperSecret
Seed (512 bits):3b5df16df2157104cfdd22830162a5e170c0161653e3afe6c88defeefb0818c793dbb28ab3ab091897d0 715861dc8a18358f80b79d49acf64142ae57037d1d54 -
BIP-39標準允許在派生種子時使用可選的密碼短語。如果沒有使用密碼短語,助記詞將被一個由常量字符串"mnemonic" 組成的鹽擴展,從任何給定的助記詞中產(chǎn)生一個特定的512位種子。
這里展示了生成助記詞,種子和擴展私鑰的獨立網(wǎng)頁。 -
HD錢包的一個特點是能從公開的父公鑰派生子公鑰
交易是由EOA發(fā)起的簽名消息,由以太坊網(wǎng)絡傳輸并在以太坊區(qū)塊鏈上記錄。
它是唯一可以觸發(fā)狀態(tài)更改或者導致合約在EVM中執(zhí)行的東西。
以太坊是一個全球的單實例狀態(tài)機器,交易是唯一可以讓狀態(tài)機運動、改變狀態(tài)的東西。即合約不會自動運行、以太坊不會自動運行,一切運行都始于交易
交易的基本結(jié)構(gòu)
交易的網(wǎng)絡序列化是交易結(jié)構(gòu)的唯一通用標準
交易是一個序列化的二進制消息,其中包含以下數(shù)據(jù):
- nonce:由始發(fā)EOA(外部所有賬戶)發(fā)出的序列號,用于防止消息重播。
- gas price:發(fā)起人愿意支付的gas價格(以wei為單位)。
- start gas:發(fā)起人愿意支付的最大gas量。
- to:目標以太坊地址。
- value:發(fā)送到目標地址的ether數(shù)量。
- data:變長二進制數(shù)據(jù)。
- v,r,s:始發(fā)EOA的ECDSA簽名的三個組成部分。
交易消息不包含from字段,因為交易發(fā)起者的公鑰可以從ECDSA簽名中計算出來。
交易隨機數(shù)nonce
這是交易中最重要也最少被理解的部分。
nonce:與此地址發(fā)送過的交易數(shù)量相等的標量值,或者,對于具有關聯(lián)代碼的帳戶,表示此帳戶創(chuàng)建的合約數(shù)量。
嚴格地說,nonce是始發(fā)地址的一個屬性(它只在發(fā)送地址的上下文中有意義)。但是,該nonce并未作為賬戶狀態(tài)的一部分顯式存儲在區(qū)塊鏈中。相反,它是根據(jù)來源于此地址的已確認交易的數(shù)量動態(tài)計算的。
nonce也可以被用于防止雙花等。
跟蹤nonce
查詢賬戶已確認交易數(shù)量的最新計數(shù)
最新計數(shù)為40,意味著下一個交易的nonce將是40。
nonce有著確認機制,意味著按照順序創(chuàng)建多個交易后,其中有一個交易未被開采,那么后面等所有交易將被卡住。如果出現(xiàn)兩個nonce相同的交易(并發(fā)時出錯),前面的將進行,后面的會被拒絕。
并發(fā)、交易、nonce
在以太坊這種去中心化/分布式實時系統(tǒng)中,并發(fā)會突然出現(xiàn)。
簡單來說,并發(fā)是多個獨立系統(tǒng)同時計算,這些可以在相同的的程序中(多線程)、同樣的CPU中(多進程)或不同的計算機中(分布式)。而以太坊是一個允許(節(jié)點、客戶端、DApps)并發(fā)多系統(tǒng),但強制實施單一狀態(tài)。
交易gas
gasPrice & startPrice
gasPrice:每單位gas的匯率,1gas=?wei,1ether=10^18wei
startPrice:本次交易愿意付出的gas
gas是以太坊的燃料。gas不是ether,它是獨立的虛擬貨幣,有相對于ether的匯率。gas與ether分離,以保護系統(tǒng)免受隨著ether價值快速變化而產(chǎn)生的波動。
web3界面通過計算幾個區(qū)塊的中間價格來提供gasPrice建議
交易的接收者to
交易的接受人由to指定,包含一個20個字節(jié)的以太坊地址,可以說EOA或合約。
以太坊不會驗證是否有這個地址,任何20個字節(jié)的值都是有效值。
驗證地址是否有效,應該在用戶界面層級完成。
如果發(fā)送到不存在的地址,ehter會燃燒。
實際上也存在著一些專門用于燃燒ether的合約。
交易的價值和數(shù)據(jù)
value&data
value和data可以都有,可以都沒有,可以只有一個
只有value的交易是支付
只有data的交易是調(diào)用
沒有value也沒有data的交易是浪費gas
將value傳給EOA或合約
對于EOA,將把發(fā)送的余額添加到地址的余額中。如果地址之前沒有被觀測過,會創(chuàng)建地址并初始化余額。
對于合約地址,會嘗試調(diào)用data中指定的函數(shù)。如果沒有data,EVM會調(diào)用目標合約的fallback函數(shù),如果該函數(shù)是payable,將執(zhí)行函數(shù)以確定下一步。
合約可以通過在調(diào)用付款功能時立刻拋出異?;蛴蓷l件來拒絕收款。如果付款成功,合約狀態(tài)將更新,ether余額增加。
將數(shù)據(jù)傳到EOA或合約
一般如果交易包含data,大概率是發(fā)到合約上的。
如果發(fā)送到EOA上,多數(shù)錢包會忽視data
如果發(fā)送到合約上,data將被EVM認為是一個函數(shù)調(diào)用,調(diào)用指定的函數(shù)并將任何編碼參數(shù)傳遞給該函數(shù)。
發(fā)送給合約的data是一個十六進制的編碼
函數(shù)選擇器:函數(shù)原型的Keccak256哈希的前4個字節(jié)。這使EVM能夠明確地識別你希望調(diào)用的功能。
函數(shù)的參數(shù):根據(jù)EVM定義的各種基本類型的規(guī)則進行編碼。
?交易中data格式的例子
在Faucet.sol中,我們?yōu)槿】疃x了一個函數(shù):
函數(shù)名稱是withdraw,它只有一個參數(shù)是uint(它是uint256的別名)。所以withdraw的原型將是:
我們來計算這個字符串的Keccak256哈希值(我們可以使用truffle控制臺或任何JavaScript web3控制臺來做到這一點:多數(shù)情況下keccak256和sha3是同義詞):
散列的前4個字節(jié)是 0x2e1a7d4d(一個字節(jié)是8位,表達0-255,代表兩位十六進制)。
這是我們的“函數(shù)選擇器”的值,它會告訴EVM我們想調(diào)用哪個函數(shù)。
接下來,讓我們計算一個值作為參數(shù) withdraw_amount 傳遞。我們要取款0.01 ether。我們將它編碼為一個十六進制序列化的大端序無符號256位整數(shù),以wei為單位:
現(xiàn)在,我們將函數(shù)選擇器添加到這個參數(shù)上(填充為32字節(jié)):
這就是我們的交易的 data,調(diào)用 withdraw 函數(shù)并請求0.01 ether作為 withdraw_amount。
特殊交易:注冊合約
發(fā)送到零地址的,帶有data但是沒有value的交易,表示注冊一個新的合約。
合約注冊交易的to字段包括地址0x0,該地址不代表EOA,也不代表合約,不會花費ether和啟動交易,僅作為注冊合約的目的。
特殊交易:燃燒
燃燒地址
數(shù)字簽名
數(shù)字簽名是用于證明數(shù)字信息或文件真實性的數(shù)學方案。有效的數(shù)字簽名使得收件人有理由相信該信息是由已知的發(fā)件人創(chuàng)建的,發(fā)件人不能否認已發(fā)送信息,信息在傳輸過程中未被更改。(認證+不可否認+完整)
此處討論數(shù)字簽名如何工作,以及如何在不泄露私鑰的情況下提供私鑰所有權的證明。
橢圓曲線數(shù)字簽名算法(ECDSA)
ECDSA是用于基于橢圓曲線私鑰/公鑰對的數(shù)字簽名的算法。
數(shù)字簽名在以太坊中的用途
簽名證明私鑰的所有者
授權的證明是不可否認的
簽名證明交易數(shù)據(jù)在交易簽名后不被修改
(認證+不可否認+完整)
數(shù)字簽名如何工作
數(shù)字簽名由兩部分組成。第一部分是使用私鑰從交易中創(chuàng)建簽名,第二部分是允許任何人使用公鑰來驗證簽名。
創(chuàng)建數(shù)字簽名
ECDSA中:
F_sig產(chǎn)生了一個由兩個值組成的簽名Sig,通常稱為R,S
被簽名:交易(來自交易的哈希)
簽名密鑰:EOA私鑰
簽名結(jié)果:簽名
驗證簽名
驗證簽名必須有簽名(R和S)和序列化交易和公鑰(與創(chuàng)建簽名的私鑰對應)。
驗證簽名代表著:只有生成此公鑰的私鑰的所有者才能在此交易上簽名。
如果簽名對此消息有效,返回TRUE
※※※ECDSA數(shù)學--后續(xù)補上※※※
?實踐中的簽名過程
要在以太坊簽署交易,發(fā)件人必須:
- 創(chuàng)建一個包含九個字短的交易數(shù)據(jù)結(jié)構(gòu)
nonce、gasPrice、startGas、to、value、data、v、r、s(rs是ECDSA加密算法的輸出值,即簽名。v是用于恢復結(jié)果的ID) - 生成交易的RLP編碼的序列化消息
- 計算此序列化消息的Keccak256哈希
- 計算ECDSA簽名,得到r和s
簽名前綴v和公鑰恢復
從ECDSA簽名中計算簽名者公鑰稱為公鑰恢復
交易簽名包含一個前綴值v,它告訴我么兩個可能的R中哪一個是臨時的公鑰。(R和R'是公鑰的兩種可能性)
如果v是偶數(shù),R是正確值。如果v是奇數(shù),R'是正確值
離線簽名(為了安全)
交易廣播
以太坊網(wǎng)絡使用“泛洪”路由協(xié)議。每個以太坊客戶端,在_Peer-to-Peer(P2P)中作為_node,(理想情況下)構(gòu)成_mesh_網(wǎng)絡。沒有網(wǎng)絡節(jié)點是“特殊的”,它們都作為平等的對等體。 我們將使用術語“節(jié)點”來指代連接并參與P2P網(wǎng)絡的以太坊客戶端。
交易傳播開始于創(chuàng)建(或從離線接收)簽名交易的以太坊節(jié)點。交易被驗證,然后傳送到_直接_連接到始發(fā)節(jié)點的所有其他以太坊節(jié)點。平均而言,每個以太坊節(jié)點保持與至少13個其他節(jié)點的連接,稱為_鄰居_。每個鄰居節(jié)點在收到交易后立即驗證交易。如果他們同意這是有效的,他們會保存一份副本并將其傳播給所有的鄰居(除了它的鄰居)。結(jié)果,交易從始發(fā)節(jié)點向外漣漪式地遍歷網(wǎng)絡,直到網(wǎng)絡中的所有節(jié)點都擁有交易的副本。
幾秒鐘內(nèi),以太坊交易就會傳播到全球所有以太坊節(jié)點。從每個節(jié)點的角度來看,不可能辨別交易的起源。發(fā)送給我們節(jié)點的鄰居可能是交易的發(fā)起者,或者可能從其鄰居那里收到它。為了能夠跟蹤交易的起源或干擾傳播,攻擊者必須控制所有節(jié)點的相當大的百分比。這是P2P網(wǎng)絡安全和隱私設計的一部分,尤其適用于區(qū)塊鏈。
寫入?yún)^(qū)塊鏈
雖然以太坊中的所有節(jié)點都是相同的對等節(jié)點,但其中一些節(jié)點由_礦工_操作,并將交易和數(shù)據(jù)塊提供給_挖礦農(nóng)場_,這些節(jié)點是具有高性能圖形處理單元(GPU)的計算機。挖掘計算機將交易添加到候選塊,并嘗試查找使得候選塊有效的_Proof-of-Work_。
不深入太多細節(jié),有效的交易最終將被包含在一個交易塊中,并記錄在以太坊區(qū)塊鏈中。一旦開采成塊,交易還通過修改賬戶余額(在簡單付款的情況下)或通過調(diào)用改變其內(nèi)部狀態(tài)的合約來修改以太坊單例的狀態(tài)。這些更改將與交易一起以交易_收據(jù)_ receipt 的形式記錄,該交易也可能包含_事件_ events。
我們的交易已經(jīng)完成了從創(chuàng)建到被EOA簽署,傳播以及最終采礦的旅程。它改變了單例的狀態(tài),并在區(qū)塊鏈上留下了不可磨滅的印記。
-
賬戶有兩種:EOA和合約賬戶
-
EOA由以太坊以外的軟件控制
-
合約賬戶則由EVM內(nèi)運行的軟件控制
-
以下將討論合約賬戶和控制他們的軟件:智能合約(smart contract)
-
智能合約:Ethereum虛擬機環(huán)境中確定性的運行的不可變的計算機程序,該虛擬機作為一個去中心化的世界計算機而運轉(zhuǎn)。
-
每個合約都有以太坊地址表示,該地址源于合約創(chuàng)建交易,合約的以太坊地址可以在交易中作為接收者,可將資金發(fā)送到發(fā)送到合約或調(diào)用合約的某個功能。
-
合約只有在被交易調(diào)用時才會運行,以太坊的所有智能合約均由交易執(zhí)行。合約不會自動運行。
-
合約的內(nèi)容可以是調(diào)用另一個合約,連續(xù)的進行會產(chǎn)生一個合約鏈。
-
合約不可被更改,但可以被刪除。刪除合約需要執(zhí)行名為 SELFDESTRUCT(以前稱為 SUICIDE )的EVM操作碼,該操作花費負的gas。但這種操作不會刪除合約的交易歷史。
-
合約里的交易是“原子性”的:交易的執(zhí)行盡在交易成功終止的時候記錄狀態(tài)的更改(合約、賬戶等),而成功終止意味著程序執(zhí)行的時候沒有錯誤,如果交易由于出現(xiàn)錯誤而失敗,所有效果(狀態(tài)的變化)都會回滾,好像交易從未發(fā)生。但是失敗的交易仍儲存在區(qū)塊鏈中,并從原始賬戶扣除gas成本,但對合約或賬戶狀態(tài)沒有其他影響。
-
智能合約的生命周期:高級語言編寫(solidity)-->編譯為EVM中能運行的低級字節(jié)碼-->跟隨合約創(chuàng)建的交易被部署到以太坊區(qū)塊鏈
- EVM
EVM是一臺虛擬計算機,運行一種特殊形式的 機器代碼 ,稱為EVM 字節(jié)碼,就像計算機CPU運行機器代碼x86_64一樣。
我們使用高級語言編寫程序并使用編譯器將他們轉(zhuǎn)化為字節(jié)碼。 - 高級語言
一般來說編程語言分為函數(shù)式(聲明式)和過程式(命令式)
在函數(shù)式中,我們編寫的程序表達的是邏輯(logic)而非流程(flow),如SQL、HTML等。
在過程式中,編寫的是一套邏輯和流程混合在一起的程序,如C、C++、Java等。
有一些編程語言是混合的,他們鼓勵函數(shù)式編程,但也會用過程式表達一些必要的邏輯,如Python、JS等。
Solidity是過程式語言,是最豐富的智能合約語言。
目前的智能合約語言包括LLL、Serpent、Solidity、Vyper、Bamboo(從老到新) - Solidty來寫一個智能合約之前
Solidity編譯器:Solidity compiler(solc)
集成開發(fā)環(huán)境:Remix IDE
本地可以使用solc編譯.sol文件
solc的結(jié)果是產(chǎn)生一個可以交給以太坊區(qū)塊鏈的十六進制序列化二進制文件
以太坊合約應用程序二進制接口(ABI)
ABI是兩個程序模塊之間的接口,通常,一個在機器代碼級別,另一個在用戶運行的程序級別。ABI是將數(shù)據(jù)編碼到機器碼,和從機器碼解碼數(shù)據(jù)的主要方式.
在以太坊中,ABI用于編碼EVM的合約調(diào)用,并從交易中讀取數(shù)據(jù)。ABI的目的是定義合約中的哪些函數(shù)可以被調(diào)用,并描述函數(shù)如何接受參數(shù)并返回數(shù)據(jù)。
合約ABI的JSON格式由一系列函數(shù)描述和事件的數(shù)組給出。
solc為.sol文件生成ABI
如你所見,編譯器會生成一個描述由 Faucet.sol 定義的兩個函數(shù)的JSON對象。這個JSON對象可以被任何希望在部署時訪問 Faucet 合約的應用程序使用。使用ABI,應用程序(如錢包或DApp瀏覽器)可以使用正確的參數(shù)和參數(shù)類型構(gòu)造調(diào)用 Faucet 中的函數(shù)的交易。例如,錢包會知道要調(diào)用函數(shù)+withdraw+,它必須提供名為 withdraw_amount 的 uint256 參數(shù)。錢包可以提示用戶提供該值,然后創(chuàng)建一個編碼它并執(zhí)行+withdraw+功能的交易。
應用程序與合約進行交互所需的全部內(nèi)容是ABI以及合約的部署地址。
這個代碼可以檢測本地編譯器版本和目標版本是否兼容
Solidity語法
Solidity完整文檔: https://solidity.readthedocs.io/en/latest/
Solidity數(shù)據(jù)類型:
boolean (bool):布爾值, true 或 false, 以及邏輯操作符 ! (not), && (and), || (or), == (equal), != (not equal).
整數(shù) (int/uint):有符號 (int) 和 無符號 (uint) 整數(shù),從 u/int8 到 u/int256以 8 bits 遞增,沒有大小后綴的話,表示256 bits。
定點數(shù) (fixed/ufixed):定點數(shù), 定義為 u/fixedMxN,其中 M 是位大小(以8遞增),N 是小數(shù)點后的十進制數(shù)的個數(shù)。
地址:20字節(jié)的以太坊地址。address 對象有 balance (返回賬戶的余額) 和 transfer (轉(zhuǎn)移ether到該賬戶) 成員方法。
字節(jié)數(shù)組(定長):固定大小的字節(jié)數(shù)組,定義為+bytes1+到+bytes32+。
字節(jié)數(shù)組 (動態(tài)):動態(tài)大小的字節(jié)數(shù)組,定義為+bytes+或+string+。
enum:枚舉離散值的用戶定義類型。
struct:包含一組變量的用戶定義的數(shù)據(jù)容器。
mapping:+key ? value+對的哈希查找表。
除上述數(shù)據(jù)類型外,Solidity還提供了多種可用于計算不同單位的字面值:
時間單位:seconds, minutes, hours, 和 days 可用作后綴,轉(zhuǎn)換為基本單位 seconds 的倍數(shù)。
以太的單位:wei, finney, szabo, 和 ether 可用作后綴, 轉(zhuǎn)換為基本單位 wei 的倍數(shù)。
在withdraw函數(shù)中,我們限制最大提現(xiàn)額,將數(shù)量限制表示為wei,以太的基本單位:
這不是很容易閱讀,所以我們可以通過使用單位倍數(shù) ether 來改進我們的代碼,以ether而不是wei表示值:
預定義的全局變量和函數(shù)
在EVM中執(zhí)行合約時,它可以訪問一組較小范圍內(nèi)的全局對象。這些包括 block,msg 和 tx 對象。另外,Solidity公開了許多EVM操作碼作為預定義的Solidity功能。
調(diào)用交易/消息
msg:msg對象是啟動合約執(zhí)行的交易(源自EOA)或消息(源自合約)。它包含許多有用的屬性:
msg.sender:我們已經(jīng)使用過這個。它代表發(fā)起消息的地址。如果我們的合約是由EOA交易調(diào)用的,那么這是簽署交易的地址。
msg.value:與消息一起發(fā)送的以太值。
msg.gasgasleft():這次交易攜帶的gas減去交易執(zhí)行到現(xiàn)在的gas。它已經(jīng)被棄用,并將被替換為Solidity v0.4.21中的 gasleft() 函數(shù)。
msg.data:調(diào)用合約的消息中的數(shù)據(jù)。
msg.sig:數(shù)據(jù)的前四個字節(jié),它是函數(shù)選擇器,可以讓EVM明確的知道我們想調(diào)用的功能。
每當合約調(diào)用另一個合約時,msg的所有屬性的值都會發(fā)生變化,以反映新的調(diào)用者的信息。唯一的例外是在原始msg上下文中運行另一個合約/庫的代碼的 delegatecall 函數(shù)。
交易
tx.gasprice:發(fā)起調(diào)用的交易中的gas價格。
tx.origin:源自(EOA)的交易的完整調(diào)用堆棧。
區(qū)塊
block:包含有關當前塊的信息的塊對象。
block.blockhash(blockNumber)blockhash():指定塊編號的塊的哈希,直到之前的256個塊。已棄用,并使用Solidity v.0.4.22中的blockhash()函數(shù)替換。
block.coinbase:當前塊的礦工地址。
block.difficulty:當前塊的難度(Proof-of-Work)。
block.gaslimit:當前塊的區(qū)塊gas限制。
每個區(qū)塊都有gas limit,即單個區(qū)塊允許的最多gas總量,可以決定單個區(qū)塊中能打包多少筆交易。
我們每一次交易或合約調(diào)用都要設置一個gas limit,如果該次操作所使用的gas數(shù)量小于或等于您所設置的gas limit,則會被執(zhí)行,但如果gas總消耗量超過gas limit,所有的操作都會被重置,但費用依舊會被收取。在執(zhí)行中實際消耗的gas值總和叫gas used,沒有使用完的gas會退還到原賬號。
block.number:當前塊號(高度)。
block.timestamp:礦工在當前塊中放置的時間戳,自Unix紀元(秒)開始。
地址對象
任何地址(作為輸入傳遞或從合約對象轉(zhuǎn)換而來)都有一些屬性和方法:
address.balance:地址的余額,以wei為單位。例如,當前合約余額是 address(this).balance。
address.transfer(amount):將金額(wei)轉(zhuǎn)移到該地址,并在發(fā)生任何錯誤時拋出異常。我們在Faucet示例中的msg.sender地址上使用了此函數(shù),msg.sender.transfer()。
address.send(amount):類似于前面的transfer, 但是失敗時不拋出異常,而是返回false。
address.call():低級調(diào)用函數(shù),可以用value,data構(gòu)造任意消息。錯誤時返回false。
address.delegatecall():低級調(diào)用函數(shù),保持發(fā)起調(diào)用的合約的msg上下文,錯誤時返回false。
內(nèi)置函數(shù)
addmod, mulmod:模加法和乘法。例如,addmod(x,y,k) 計算 (x + y) % k。
keccak256, sha256, sha3, ripemd160:用各種標準哈希算法計算哈希值的函數(shù)。
ecrecover:從簽名中恢復用于簽署消息的地址。
合約定義
- Solidity的主要數(shù)據(jù)類型是contract對象
- 合約是一個包含數(shù)據(jù)和方法的容器
- Solidity提供了另外兩個與合約類似的對象:
interface:常被稱為 樁 stub,因為它告訴你有關函數(shù)的參數(shù)和返回值,沒有任何實現(xiàn),用來指定合約接口。
library:庫合約,是一個只能部署一次并被其他合約使用的合約。
函數(shù)
合約中定義了可有EOA交易或其他合約調(diào)用的函數(shù)(Faucet示例中有withdraw和fallback函數(shù))
定義函數(shù)的語法:
其中:
FunctionName
定義函數(shù)的名稱,用于通過交易(EOA),其他合約或同一合約調(diào)用函數(shù)。每個合約中的一個功能可以定義為不帶名稱的,在這種情況下,它是fallback函數(shù),在沒有指定其他函數(shù)時調(diào)用該函數(shù)。fallback函數(shù)不能有任何參數(shù)或返回任何內(nèi)容。
parameters
在名稱后面,我們指定必須傳遞給函數(shù)的參數(shù),包括名稱和類型。在我們的Faucet示例中,我們將uint withdraw_amount定義為withdraw函數(shù)的唯一參數(shù)。
關鍵字 public, private, internal, external)指定了函數(shù)的可見性:
public
Public是默認的,這些函數(shù)可以被其他合約,EOA交易或合約內(nèi)部調(diào)用。在我們的Faucet示例中,這兩個函數(shù)都被定義為public。
external
外部函數(shù)就像public一樣,但除非使用關鍵字this作為前綴,否則它們不能從合約中調(diào)用。
internal
內(nèi)部函數(shù)只能在合約內(nèi)部"可見",不能被其他合約或EOA交易調(diào)用。他們可以被派生合約調(diào)用(繼承的)。
private
private函數(shù)與內(nèi)部函數(shù)類似,但不能由派生的合約調(diào)用(繼承的)。
請記住,術語 internal 和 private 有些誤導性。公共區(qū)塊鏈中的任何函數(shù)或數(shù)據(jù)總是可見的,意味著任何人都可以看到代碼或數(shù)據(jù)。以上關鍵字僅影響函數(shù)的調(diào)用方式和時機。
關鍵字pure, constant, view, payable會影響函數(shù)的行為:
constant/view(constant被棄用)
標記為view的函數(shù),承諾不修改任何狀態(tài)。術語constant是view的別名,將被棄用。目前,編譯器不強制執(zhí)行view修飾器,只產(chǎn)生一個警告,但這應該成為Solidity v0.5中的強制關鍵字。
pure
純(pure)函數(shù)不讀寫任何變量。它只能對參數(shù)進行操作并返回數(shù)據(jù),而不涉及任何存儲的數(shù)據(jù)。純函數(shù)旨在鼓勵沒有副作用或狀態(tài)的聲明式編程。
payable
payable函數(shù)是可以接受付款的功能。沒有payable的函數(shù)將拒絕收款,除非它們來源于coinbase(挖礦收入)或 作為 SELFDESTRUCT(合約終止)的目的地。在這些情況下,由于EVM中的設計決策,合約無法阻止收款。
正如你在Faucet示例中看到的那樣,我們有一個payable函數(shù)(fallback函數(shù)),它是唯一可以接收付款的函數(shù)。
合約的構(gòu)造和自毀
(構(gòu)造函數(shù)和析構(gòu)函數(shù))
Solidity v.0.4.21,構(gòu)造函數(shù)是一個名稱與合約名稱相匹配的函數(shù)
合約的生命周期始于EOA或其他合約的創(chuàng)建交易。如果有一個構(gòu)造函數(shù),它將在相同的創(chuàng)建交易中調(diào)用,并可以在創(chuàng)建合約時初始化合約狀態(tài)。
constructor關鍵字,一般來說構(gòu)造函數(shù)的函數(shù)名要與合約名字相同,但是可以使用
來代替
避免了重命名合約帶來的錯誤,而且更容易確定哪個函數(shù)是構(gòu)造函數(shù)。
`// Version of Solidity compiler this program was written for
pragma solidity ^0.4.22;
// Our first contract is a faucet!
contract Faucet {
address owner;
// Initialize Faucet contract: set owner
constructor() {
owner = msg.sender;
}`
合約生命周期的另一端是 合約銷毀 contract destruction 合約被稱為SELFDESTRUCT的特殊EVM操作碼銷毀。它曾經(jīng)是SUICIDE,但由于該詞的負面性,該名稱已被棄用。在Solidity中,此操作碼作為高級內(nèi)置函數(shù)selfdestruct公開,該函數(shù)采用一個參數(shù):地址以接收合約帳戶中剩余的余額。看起來像這樣:
函數(shù)修飾器 modifier
這個函數(shù)修飾器onlyOwner為修飾的任何函數(shù)設置了條件,要求交易的msg.sender與合約的部署者owner相同。
使用函數(shù)修飾器,需要將其名稱添加到函數(shù)聲明,可以將多個修飾器作用于一個函數(shù),以逗號分隔。
這和上面的自毀函數(shù)相同
在修飾函數(shù)內(nèi)部可以訪問被修飾的函數(shù)的所有變量和參數(shù),但是被修飾的函數(shù)無法訪問修飾函數(shù)中的任何變量。
合約繼承
繼承合約為了附加功能,擴展基礎合約,使我們實現(xiàn)模塊化
繼承合約使用 is 關鍵字指定父合約
支持多繼承
錯誤處理 assert、require、revert
如果合約出現(xiàn)錯誤,所有的狀態(tài)變化都會恢復,除了已花費的gas(這表明交易是原子性的,要么完成,要么對狀態(tài)沒有影響)。
- assert和require函數(shù)以相同的方式運行:如果條件為假,則停止執(zhí)行返回錯誤。一般來說,預期為真時使用assert,這意味著我們一般用assert來測試內(nèi)部條件。在測試輸入(如函數(shù)參數(shù)或交易字段)時使用require。
- require函數(shù)是守護條件,阻止執(zhí)行函數(shù)的其他部分,在不滿足的時候產(chǎn)生錯誤。此外Solidity v.0.4.22開始,require還可以包含文本消息,用來顯示錯誤的原因,所以我們可以在的require函數(shù)中添加一條錯誤消息:
- revert函數(shù)停止合約并還原任何狀態(tài)更改
- 增加錯誤檢查代碼會略微增加gas小號,但他比不檢查提供了更好的錯誤報告(在錯誤處理語句后面加上的錯誤信息)。需要開發(fā)者掌控兩者間的平衡。
事件Events
- event函數(shù)便于產(chǎn)生交易日志。
- 當一個交易完成,會產(chǎn)生一個交易收據(jù)transaction receipt,包含log條目,用于提供有關在執(zhí)行交易期間發(fā)生的操作的信息。事件是用于構(gòu)造這些日志的Solidity高級對象。
- 事件在輕量級客戶端和DApps中特別有用,可以監(jiān)視特定事件并將其返回給用戶界面或者對應用程序的狀態(tài)進行更改以反映底層合約中的事件
嘗試添加兩個事件
contract Faucet is mortal {
event Withdrawal(address indexed to, uint amount);//記錄了任何提款
event Deposit(address indexed from, uint amount);//記錄了任何存款
...
}
使用emit觸發(fā)事件
function withdraw(uint withdraw_amount) public {
...
msg.sender.transfer(withdraw_amount);
emit Withdrawal(msg.sender, withdraw_amount);//此處該在日志中記錄,觸發(fā)了提款事件
}
function () public payable {
emit Deposit(msg.sender, msg.value);//此處該在日志中記錄,觸發(fā)了存款事件。
//此處用于交易中沒有合約中以聲明的功能或不包含數(shù)據(jù)時觸發(fā)。
}
捕捉事件:
web3.js庫提供了一個數(shù)據(jù)結(jié)構(gòu),包含交易日志的交易結(jié)果。在這里我們可以看到交易產(chǎn)生的事件。
使用Turffle在最新版的合約上運行測試交易:
- 使用developed()函數(shù)獲取部署后的合約。
- 執(zhí)行兩個交易:第一個是存款(send),會觸發(fā)Deposit事件
- 第二個是提款,會觸發(fā)Withdraw事件
- res返回了logs數(shù)組(這個來自turffle),其中l(wèi)ogs[0]包含logs[0].event的事件名字和logs[0].args的事件參數(shù)
- 事件 這種機制不僅用于合約內(nèi)通信,還用于開發(fā)過程中的調(diào)試。
合約調(diào)用(call、send、delegatecall、callcode)
調(diào)用其他合約:
- 調(diào)用合約是有用但有危險的操作。越靈活的調(diào)用一般越危險。
- 通過new實例化,這可以在區(qū)塊鏈上創(chuàng)建合約并返回一個可以用于引用他的對象。
- 如果不在一個文件內(nèi),用import
- new可以用來傳參 _faucet = (new Faucet).value(0.5 ether)();
- 原始調(diào)用,delegatecall
???solidity->bytecode(字節(jié)碼)->opcode(操作碼) ???
調(diào)用合約時可以使用更低級的功能,他們直接對應于相同名稱的EVM操作碼,這是一種盲調(diào)用(blind)。
風險提示:安全風險如可重入性(reentrancy),如出現(xiàn)問題返回false
delegatecall是call的變體,代替了更為危險的callcode。
delegatecall不同于call,不會導致msg的上下文改變,應謹慎使用。
Gas控制
gas是智能合約編程中一定要考慮的因素
gas是限制以太坊允許交易損耗大最大量的資源,如果計算過程中超過了gas限制則:
- 觸發(fā)out of gas異常
- 函數(shù)執(zhí)行前的合約狀態(tài)被恢復
- 全部gas作為交易費用給礦工,不予返還
gas由創(chuàng)建交易的用戶支付,因此程序員應最大限度的較少合約函數(shù)的gas費用: - 避免動態(tài)大小的數(shù)組(有風險引入過多gas)
- 避免調(diào)用其他合約(其他合約gas成本未知)
- 要估算gas成本(JavaScript),這應該是開發(fā)流程的一部分
這段可以根據(jù)調(diào)用參數(shù)估計執(zhí)行合約所需gas單位--gasEstimate
獲取網(wǎng)格的價格
估算gas成本
安全
- 所有智能合約都是公開的,任何用戶都可以創(chuàng)建交易來與之交互,因此任何漏洞都可以被利用。
- 智能合約領域的漏洞問題代價高昂。
- 防御性編程的編程風格特別適用于智能合約編程:
- 極簡/節(jié)約:用更少的辦法、更少的代碼、更少的復雜性和功能實現(xiàn)項目
- 代碼重用:這是最基本的軟件安全原則--最大限度的重用可信代碼:即如果庫和合約已經(jīng)存在,請重復使用。如果一些代碼片段多次使用,請作為函數(shù)或庫來編寫使用。
- 代碼質(zhì)量:采用嚴謹?shù)墓こ毯蛙浖_發(fā)方法論來對待智能合約代碼,而不是通用編程。
- 可讀性:應該編寫文檔良好、易于閱讀的代碼,遵遁以太坊社區(qū)的一部分樣式約定和命名約定。
- 測試覆蓋:測試所有可能的輸入。
- 常見的安全風險:重入( Re-entrancy)
當合約將一些以太幣發(fā)送給一個未知地址時,外部惡意合約調(diào)用受攻擊合約的上一個函數(shù)(如通過fallback回退函數(shù)),以類似遞歸的方法榨干合約里所有的以太。
THE DAO事件中黑客就是使用了這種方法導致了以太坊的硬分叉
設計模式
- 訪問控制 access control
- 狀態(tài)流 state flow
- 資金支出 fund disbursement
部署智能合約
- 測試智能合約
測試框架:
Truffle Test
Embark Framework Testing
DApp
populus
在區(qū)塊鏈上測試
在geth終端輸入
可用于獲得在處的合約地址。
獲取部署在的合約代碼。這可以用來驗證正確的部署。
獲取位于地址的合約的完整日志,在選項中指定。這有助于查看合約調(diào)用的歷史記錄。
獲取位于 address 的存儲,并使用 position 的偏移量顯示該合約中存儲的數(shù)據(jù)。
框架
- Truffle
Github; https://github.com/Trufflesuite/Truffle
網(wǎng)站; https://Truffleframework.com
文檔; https://Truffleframework.com/docs
Truffle Boxes; http://Truffleframework.com/boxes/
npm package repository; https://www.npmjs.com/package/Truffle
nodejs
npm
nvm(node版本管理器)
創(chuàng)建了一個包含DApp支持的Node.js版本的隱藏文件.nvmrc,這樣開發(fā)人員只需要在項目運行nvm install 就會自動安裝并切換該版本的nvm
繼續(xù)使用npm -g安裝truffle
使用一個預先構(gòu)建的模版上的DApp
創(chuàng)建Truffle項目
命名項目文件Faucet
初始化Truffle,目錄結(jié)構(gòu):
.
├── contracts
│ └── Migrations.sol
├── migrations
│ └── 1_initial_migration.js
├── test
└── truffle-config.js
安裝js支持包
安裝truffle依賴
配置Truffle
truffle-config.js設置了一個默認的以太坊節(jié)點,該網(wǎng)絡假定了我們正在運行以太坊客戶端,既可以作為完整節(jié)點,也可以作為輕量型節(jié)點。
truffle-config.js指示了truffle與端口8545上的本地節(jié)點通過RPC進行通信。
truffle將使用本地節(jié)點連接任何以太網(wǎng)和測試網(wǎng)絡,本地節(jié)點也會有錢包功能。
使用truffle部署合約
在contracts子目錄中粘貼合約。
還可以執(zhí)行truffle compile編譯合約。
truffle migrations 理解部署腳本
truffle通過migrations.sol來跟蹤哪些合約已經(jīng)部署。如果一個項目擁有數(shù)十個合約和復雜的依賴關系,就不必為了尚未更改的合約支付gas。
migrations目錄里有一個腳本1_initial_migration.js,它會部署Migrations.sol
我們需要第二個migrations腳本來部署Faucets.sol,可命名為2_deploy_contracts.js,只需要對1_initial_migration.js稍作修改。
準備好了后使用truffle migrate來部署合約
Embark
Github; https://github.com/embark-framework/embark/
文檔; https://embark.status.im/docs/
npm package repository; https://www.npmjs.com/package/embark
$ npm -g install embark
OpenZeppelin
OpenZeppelin是Solidity的一個可重復使用的安全的智能合約的開放框架。
它由社區(qū)驅(qū)動,由 https://zeppelin.solutions/[Zeppelin] 團隊領導。
最大的特點是安全。
使用時先將openzeppelin-solidity庫安裝到本地
openzeppelin可以和truffle很好的結(jié)合在一起。
zeppelin_os
zeppelin_os是一款開源的分布式工具和服務平臺,位于EVM之上,可以安全的開發(fā)和管理智能合約應用程序。
與OpenZeppelin的代碼每次都需要重新部署每個應用程序不同,zeppelin_os的代碼處于鏈上。
實用程序
ethereumJS helpeth: 命令行實用程序
helpeth是一個命令行工具,使開發(fā)人員更容易的操作密鑰和交易。
它是基于JavaScript的庫和工具集合ethereumjs的一部分。
https://github.com/ethereumjs/helpeth
Usage: helpeth [command]
dapp.tools
https://dapp.tools/
Dapp
https://dapp.tools/dapp/
Seth
https://dapp.tools/seth/
Hevm
https://dapp.tools/hevm/
SputnikVM
SputnikVM是一個獨立的可插拔的用于不同的基于以太坊的區(qū)塊鏈的虛擬機。它是用Rust編寫的,可以用作二進制,貨物箱,共享庫,或者通過FFI,Protobuf和JSON接口集成。它有一個單獨的用于測試目的的二進制sputnikvm-dev,它模擬大部分JSON RPC API和區(qū)塊挖掘。
Github link; https://github.com/etcdevteam/sputnikvm
Libraries【庫】
web3.js
web3.js是以太坊兼容的JS API,用于通過以太坊基金會開發(fā)的JSON RPC與客戶進行通信。
Github link; https://github.com/ethereum/web3.js
npm package repository link; https://www.npmjs.com/package/web3
Documentation link for web3.js API 0.2x.x; https://github.com/ethereum/wiki/wiki/JavaScript-API
Documentation link for web3.js API 1.0.0-beta.xx; https://web3js.readthedocs.io/en/1.0/web3.html
web3.py
web3.py 是一個用于與以太坊區(qū)塊鏈進行交互的Python庫。它現(xiàn)在也在以太坊基金會的GitHub中。
Github link; https://github.com/ethereum/web3.py
PyPi link; https://pypi.python.org/pypi/web3/4.0.0b9
Documentation link; https://web3py.readthedocs.io/
EthereumJS
以太坊的庫和實用程序集合。
Github link; https://github.com/ethereumjs
Website link; https://ethereumjs.github.io/
web3j
web3j 是Java和Android庫,用于與Ethereum客戶端集成并使用智能合約。
Github link; https://github.com/web3j/web3j
Website link; https://web3j.io
Documentation link; https://docs.web3j.io
Etherjar
Etherjar 是與Ethereum集成并與智能合約一起工作的另一個Java庫。它專為基于Java 8+的服務器端項目而設計,提供RPC的低層和高層封裝,以太坊數(shù)據(jù)結(jié)構(gòu)和智能合約訪問。
Github link; https://github.com/infinitape/etherjar
Nethereum
Nethereum 是以太坊的.Net集成庫。
Github link; https://github.com/Nethereum/Nethereum
Website link; http://nethereum.com/
Documentation link; https://nethereum.readthedocs.io/en/latest/
ethers.js
ethers.js 庫是一個緊湊,完整,功能齊全,經(jīng)過廣泛測試的以太坊庫,完全根據(jù)MIT許可證獲得,并且已收到來自以太坊基金會的DevEx資助以擴展和維護。
GitHub link: https://github.com/ethers-io/ethers.js
Documentation: https://docs.ethers.io
Emerald Platform
Emerald Platform提供了庫和UI組件,可以在以太坊上構(gòu)建Dapps。Emerald JS和Emerald JS UI提供了一組模塊和React組件來構(gòu)建Javascript應用程序和網(wǎng)站,Emerald SVG Icons是一組區(qū)塊鏈相關的圖標。除了Javascript庫之外,它還有Rust庫來操作私鑰和交易簽名。所有Emerald庫和組件都使用 Apache 2許可。
Github link; https://github.com/etcdevteam/emerald-platform
Documentation link; https://docs.etcdevteam.com
what's token
區(qū)塊鏈中,Token是一種抽象概念,可以被擁有,代表資產(chǎn)、貨幣或者訪問權。
怎么使用Token
Token最明顯的用途是數(shù)字私人貨幣,但也可以參與編程,提供許多功能。比如傳達投票權、訪問權和資源所有權。
-
貨幣
Token可以說一種貨幣形式,eth,bitcoin -
資源
Token可以是共享經(jīng)濟或者資源共享環(huán)境中獲得生成的資源,如網(wǎng)絡共享的存儲,cpu -
資產(chǎn)
有形的或無形的,黃金,石油 -
訪問
訪問權限,對于數(shù)字或?qū)嶓w資產(chǎn) -
權益
數(shù)字組織或者法律虛擬主體的股東權益 -
投票
數(shù)字或法律中的投票權 -
收藏品
-
身份
頭像或者身份證 -
實際用途
訪問或支付 -
tonken分可替換的和不可替換的
-
交易對手風險是交易中的其他方不能履行其義務的風險。
-
基于區(qū)塊鏈的token可以消除交易對手風險(共識規(guī)則的約束)
很多項目的發(fā)行都使用Token,用于為項目的服務支付。
一個新項目的Token分為實用Token(用于支付服務、應用程序、資源)和權益Token(代表初創(chuàng)團隊股票的Token)
如果Token的應用環(huán)境非常下載,那么Token只有“微不足道的價值”。
采用Token是因為不使用Token就無法工作,而不是為了快速籌集資金。
ERC20
ERC:以太坊征求意見
20:Github自動分配的發(fā)行號
目前絕大多數(shù)Token都基于ERC20,ERC20最終成為EIP20(以太坊改進建議)
https://github.com/ethereum/EIPs/blob/master/EIPS/eip-20.md
ERC20是可替換Token的標準,ERC標記的不同單元可互換,沒有唯一屬性
ERC必須的函數(shù)和事件:
- totalSupply:返回當前存在的Token總數(shù)
- balanceOf:返回給定地址的余額
- transfer:給定一個地址和數(shù)量,將該數(shù)量的Tokens從執(zhí)行該方法的地址的余額轉(zhuǎn)移到該地址
- tranferFrom:給定發(fā)送人、接受人、數(shù)量,執(zhí)行Token從一個賬戶轉(zhuǎn)移到另一個賬戶,與approve一起使用
- approve:給定接受人地址和數(shù)量時,授權該地址從發(fā)布標準的賬戶執(zhí)行多次轉(zhuǎn)賬,直到達到指定的數(shù)量
- allowance:給定所有者地址和消費者地址,返回該消費者被批準從所有者取款的剩余金額
- Transfer event:成功觸發(fā)轉(zhuǎn)移時的事件
- Approval event:成功調(diào)用approve的事件
ERC20可選函數(shù)
- name:返回Token可讀的名稱
- symbol:返回Token的人類可讀符號
- decimals:返回用于分割Token的小數(shù)位數(shù)(如果為2,則將Token除以100以獲取表示)
在Solidity中定義ERC20接口
在Solidity中ERC20接口規(guī)范的樣子
ERC20的數(shù)據(jù)結(jié)構(gòu)
任何ERC20實現(xiàn),它將包含兩個數(shù)據(jù)結(jié)構(gòu),一個用于追蹤余額,另一個用于追蹤配額(allowances)。在Solidity中,它們使用data mapping(數(shù)據(jù)映射)實現(xiàn)。
第一個data mapping是允許Token合約跟蹤誰擁有Token,
第二個data mapping是配額,允許花錢者從所有者的賬戶中花費特定金額,并通過二維映射追蹤配額(從所有者地址映射到一個花費者地址和配額金額)
ERC20的工作流程:transfer和approve&transerFrom
- 一個是使用transfer進行的單次交易工作流程:
如果愛麗絲希望向鮑勃發(fā)送10個Token,她的錢包會向Token合約的地址發(fā)送一個交易,并用Bob的地址和“10”作為參數(shù)調(diào)用transfer函數(shù)。Token合約調(diào)整Alice的余額(-10)和Bob的余額(10)并發(fā)出+Transfer事件。 - 一個是使用approve和transferFrom的雙交易工作流程:
該工作流程允許Token所有者將其控制權委托給另一個地址,常用于委托控制權給合約來分配Token,但也可以被交易所使用。
假設Alice希望允許AliceICO合同將所有AliceCoin Token的50%賣給像Bob和Charlie這樣的買方。首先,Alice發(fā)布AliceCoin ERC20合同,將所有AliceCoin發(fā)放到她自己的地址。然后,Alice發(fā)布可以以ether出售Token的AliceICO合同。接下來,Alice啟動approve & transferFrom工作流程。她向AliceCoin發(fā)送一個交易,調(diào)用approve,參數(shù)是AliceICO的地址和totalSupply的50%。這將觸發(fā)Approval事件。現(xiàn)在,AliceICO合同可以出售AliceCoin了。
當AliceICO從Bob那收到ether,它需要發(fā)送一些AliceCoin給Bob作為回報。在AliceICO合約內(nèi)是AliceCoin和ether之間的匯率。Alice在創(chuàng)建AliceICO時設置的匯率決定了Bob將根據(jù)他發(fā)送給AliceICO的ether數(shù)量能得到多少Token。當AliceICO調(diào)用AliceCoin transferFrom函數(shù)時,它將Alice的地址設置為發(fā)送者,將Bob的地址設置為接收者,并使用匯率來確定將在“value”字段中將多少AliceCoin Token傳送給Bob。AliceCoin合同將余額從Alice的地址轉(zhuǎn)移到Bob的地址并觸發(fā) Transfer 事件。只要不超過Alice設定的審批限制,AliceICO合同可以調(diào)用 transferFrom 無限次數(shù)。AliceICO合同可以通過調(diào)用allowance函數(shù)來跟蹤它能賣出多少AliceCoinToken。
實現(xiàn)ERC20
EIP20中提到了兩種實現(xiàn):
- consensys EIP20
簡單易讀的ERC20兼容Token的實現(xiàn)。
Consensys實現(xiàn)的Solidity代碼:
https://github.com/ConsenSys/Tokens/blob/master/contracts/eip20/EIP20.sol - OpenZeppelin StandardToken
此實現(xiàn)與ERC20兼容,并具有額外的安全防范措施。
它是OpenZeppelin庫的基礎,實現(xiàn)了更復雜的與ERC20兼容的Token,如籌款、拍賣等。
OpenZeppelin StandardToken的Solidity代碼: https://github.com/OpenZeppelin/zeppelin-solidity/blob/master/contracts/Token/ERC20/StandardToken.sol
發(fā)布自己的ERC20
- 使用truffle框架:
初始化
編輯truffle-config.js,以設置Truffle環(huán)境,或直接復制現(xiàn)有環(huán)境:
https://github.com/ethereumbook/ethereumbook/blob/develop/code/truffle/METoken/truffle-config.js - 導入OpenZeppelin StandardContract
它實現(xiàn)了關鍵的安全檢查并且易于擴展。 - 創(chuàng)建編寫合同
示例代碼:
https://github.com/ethereumbook/ethereumbook/blob/develop/code/truffle/METoken/contracts/METoken.sol - Truffle編譯
- 新建migration腳本來部署METoken合約
- 先用本地區(qū)塊鏈來測試
ganache-cli:私有以太坊區(qū)塊鏈,可以在上面測試、執(zhí)行命令 : https://truffleframework.com/ganache/
從 ganache-cli 的命令行或從圖形用戶界面啟動 ganache 區(qū)塊鏈。
此時在ganache的控制臺上,能看到我們部署創(chuàng)建了4個交易 - 在turffle控制臺與METoken交互
是一個交互式的JavaScript環(huán)境,可以訪問Truffle環(huán)境,并通過Web3訪問區(qū)塊鏈。
先將truffle連接到ganache
如果我們想要與已部署的合同進行交互,我們必須以JavaScript“promise”的形式使用異步調(diào)用。
ERC20Token的問題
ERC20Token暴露了Token和ether之間的細微差別。
ERC223
一種Token合同接口標準
通過檢測目的地地址是否是合同來解決無意中將Token轉(zhuǎn)移到合同的問題。
ERC777
另一種改進的Token合同標準
https://github.com/ethereum/EIPs/issues/777
ERC721
ERC721提案是不可互換的 Tokens標準,也稱為契據(jù)/deeds。
前面的所有Token標準是可互換的Token。
Token標準??
-
什么是Token標準:
是實現(xiàn)的最小范圍,為了符合ERC20,你至少要實施ERC20規(guī)定的功能和行為。主要是鼓勵合同之間的互用性,因此所有錢包、交易所、用戶界面和其他的基礎設施可以以可預見的方式與遵循任何規(guī)范的合同進行交流。 -
復雜是安全性的敵人
-
項目擴展常見功能
所有者控制
特定地址或一組地址(多重簽名)具有特殊功能,例如黑名單,白名單,鑄造,恢復等。
燃燒
Token燃燒是指Token被轉(zhuǎn)移到不可靠的地址或刪除余額并減少供應時故意銷毀。
Minting
以可預測的速率添加Token的總供應量的能力,或通過Token創(chuàng)建者的“命令”添加的能力。
Crowdfunding
提供Token銷售的能力,例如通過拍賣,市場銷售,反向拍賣等。
上限
總供給的預定義和不可改變的限制,與“minting”功能相反。
恢復“后門”
恢復資金,反向傳輸或拆除由指定地址或一組地址激活的Token(多重簽名)的功能。
白名單
限制Token傳輸僅限于列出的地址的功能。在經(jīng)過不同司法管轄區(qū)的規(guī)則審核后,最常用于向“經(jīng)認可的投資者”提供Token。通常有一種更新白名單的機制。
黑名單
通過禁止特定地址來限制Token傳輸?shù)哪芰?。通常有更新黑名單的功能?/p> -
Tokens和ICOs
ICO:首次公開發(fā)售數(shù)字貨幣融資
Token是重要組件
目前在ethereum上的很多Token都是偽裝的騙局、傳銷、集資。
應該將這種技術的長期愿景和影響與Token ICO的短期泡沫區(qū)分開。
泡沫中充斥著欺詐,但是Token標準和平臺將存活下去,并有可能改變世界。
DApp
p2p使互聯(lián)網(wǎng)用戶連接到了一起
USENET是第一個p2p架構(gòu)的分布式消息傳遞系統(tǒng)
Napster是一款音樂和文件共享應用,是完全點對點網(wǎng)絡運動轉(zhuǎn)化為BitTorrent的開始,參與用戶完全獨立于物理網(wǎng)絡,無需遵守任何管理機構(gòu)
DApp不屬于單個服務器,而是將整個棧建在p2p網(wǎng)絡上以分布式進行存儲和操作。
一個DApp包括前端、后端和數(shù)據(jù)存儲
DApp相比傳統(tǒng)集中式架構(gòu)有如下優(yōu)點:
- 彈性:
DApp不會有停機時間,只要區(qū)塊鏈在,它就還在 - 透明
允許任何人分叉代碼,并在區(qū)塊鏈上運行相同的應用程序
任何與區(qū)塊的互動都將永久儲存
任何有區(qū)塊鏈副本的人都可以獲得對他的訪問權限
可能無法將字節(jié)碼反編譯為源碼并完全理解合約的代碼 - 抗審查
用戶可以和DApp交互而不受集中機構(gòu)的干擾,一旦在網(wǎng)絡上部署代碼,任何人都無法修改代碼
DApp的組件
- 智能合約
智能合約用于存儲去中心化應用程序的業(yè)務邏輯、狀態(tài)和計算
將智能合約作為核心業(yè)務邏輯功能來運行的問題:
部署后無法修改
一個龐大的合約需要大量的gas來部署和運行。因此某些應用程序可能采取離線計算和外部數(shù)據(jù)源。
DApp的核心業(yè)務邏輯以來與外部數(shù)據(jù)或服務器意味著你的用戶必須信任這些外部事物 - 前端
DApp的前端與傳統(tǒng)的web前端沒有什么不同
前端與DApp的交互通常通過瀏覽器本身使用Mist瀏覽器或Metamask瀏覽器擴展等工具進行
目前由于缺少可用作具有密鑰管理功能等請客戶端等移動客戶端,沒有創(chuàng)建移動DApp等項目 - 數(shù)據(jù)存儲
gas成本高,導致智能合約目前不適合存儲大量數(shù)據(jù)。因此,多數(shù)DApp將利用去中心化存儲(IPDS或Swarm)來存儲和分發(fā)大量的靜態(tài)資源。
內(nèi)容的哈希通常使用data mapping存儲為智能合約中的字節(jié),然后前端應用程序調(diào)用智能合約檢索資產(chǎn)以獲取每個資產(chǎn)等URL。 - 上鏈&脫鏈
- IPFS&Swarm
- 中心化數(shù)據(jù)
DApp框架
- Truffle
- Embark
Embark專注于使用以太坊、IPFS和其他無服務器去中心化應用。
區(qū)塊鏈(以太坊):
自動部署合約并使其在JS代碼中可用。啟動監(jiān)視更改,如果你更新合約,Embark將自動重新部署合約(如果需要)和DApp。
JS通過Promises使用合約。
使用Javascript與合約進行測試驅(qū)動開發(fā)。
跟蹤已部署的合約; 只在真正需要時部署。
管理不同的鏈(例如,測試網(wǎng),私人網(wǎng),livenet)
輕松管理相互依賴合約的復雜系統(tǒng)。
去中心化存儲(IPFS):
通過EmbarkJS輕松存儲和檢索DApp上的數(shù)據(jù),包括上傳和檢索文件。
將完整的應用程序部署到IPFS或Swarm。
去中心化通信(Whisper,Orbit):
使用Whisper或Orbit通過P2P發(fā)送/接收消息
網(wǎng)絡技術:
與任何網(wǎng)絡技術集成,React,F(xiàn)oundation等
使用任何構(gòu)建管道或工具,grunt,gulp,webpack - Emerld Platform
這是一個框架和工具集,簡化了DApp的開發(fā)
提供了:
JS庫
常見圖標
管理私鑰的Rust庫
集成組件或服務
SputnikVM一個獨立的EVM
活躍的DApp
以下列出了以太坊網(wǎng)絡上的活躍DApp:
EthPM
一個旨在將包管理帶入以太坊生態(tài)系統(tǒng)的項目。
Website:https://www.ethpm.com/
Radar Relay
DEX(去中心化交易所)專注于直接從錢包到錢包交易基于以太坊的tokens。
Website:https://radarrelay.com/
CryptoKitties
在以太坊上部署的游戲,允許玩家購買,收集,繁殖和銷售各種類型的虛擬貓 它代表了為休閑和悠閑目的部署區(qū)塊鏈技術的最早嘗試之一。
Website:https://www.cryptokitties.co
Ethlance
Ethlance是一個連接自由職業(yè)者和開發(fā)者的平臺,用ether支付和收款。
Website:https://ethlance.com/
Decentraland
Decentraland是以太坊區(qū)塊鏈支持的虛擬現(xiàn)實平臺。用戶可以創(chuàng)建,體驗內(nèi)容和應用程序并從中獲利。
Website:https://decentraland.org/
EVM能保證相同的操作返回相同的輸出,無論運行在什么地方。這保證了以太坊的安全,但阻止了智能合約檢索和處理脫鏈數(shù)據(jù),Oracles能解決這個問題。
Oracles可以定義為離線數(shù)據(jù)的權威來源,允許智能合約使用外部信息接收和條件執(zhí)行---->也可以說Oracles是鏈接了鏈上鏈下之間的鴻溝的機制,允許了智能合約根據(jù)實際時間和數(shù)據(jù)強制,強制執(zhí)行合約關系。
oracles提供的數(shù)據(jù)示例:
物理量的隨機數(shù)、自然災害參數(shù)觸發(fā)器、匯率數(shù)據(jù)、資本市場數(shù)據(jù)、基準參考數(shù)據(jù)、靜態(tài)數(shù)據(jù)、事件和時間間隔、天氣、政治、體育、地理位置、損害賠償、其他區(qū)塊鏈上的事件(互操作函數(shù))、gas價格、航班
oracles的主要功能:
回應去中心化應用的查詢、解析查詢、檢查是否符合付款和數(shù)據(jù)權限、從脫鏈源中檢索數(shù)據(jù)、在交易中簽署數(shù)據(jù)、向網(wǎng)絡廣播交易、進一步安排交易
訂閱模式
請求響應模式:常見于客戶端-服務器體系
發(fā)布訂閱模式:例如:發(fā)布者是oracles,不直接向用戶發(fā)布信息,而是將發(fā)布的信息分到不同的類中。訂閱者能夠訂閱多個類。此時orcales可以將利率寫道自己的內(nèi)部存儲,當且僅當它發(fā)生變化時,多個DApp才從oracles中讀取它,以減少對網(wǎng)絡帶寬的影響
數(shù)據(jù)認證
數(shù)據(jù)認證的任務是保證脫鏈方法返回的數(shù)據(jù)完整安全不被篡改。
數(shù)據(jù)認證常用的兩種方法:
- 真實性證明
基于各種證明技術(如數(shù)字簽名),有效的將信任從數(shù)據(jù)載體轉(zhuǎn)移到了證明者。
Oraclize是一個利用真實性證明oracle服務的例子,目前可用于以太坊主網(wǎng)查詢的是TLSNotary Proof。
TLSNotary Proof依賴TLSNotary通過PageSigner簽名,利用傳輸層安全性(TLS)協(xié)議,使對數(shù)據(jù)進行TLS簽名的主密鑰在三方之間分配:服務器(oracles)、受審核方(Oraclize)和審核員。
Oraclize使用Amazon Web Service(AWS)作為審核員,來證明它實例化以來沒有修改。
這種方法要求亞馬遜本身不會篡改實例 - 可信執(zhí)行環(huán)境(TEE)
TownCrier是基于TEE的經(jīng)過身份驗證的數(shù)據(jù)發(fā)送系統(tǒng),利用基于硬件的安全區(qū)域來驗證數(shù)據(jù)的完整性。
計算oracle
oracles可用于執(zhí)行任意計算,這在以太坊的環(huán)境中特別有用。
去中心化的oracle
集中式oracle系統(tǒng)雖然滿足了許多應用,但也確實是以太網(wǎng)網(wǎng)絡的中心故障點。
ChainLink提出了一個去中心化的oracle網(wǎng)絡,包括三個關鍵的智能合約和數(shù)據(jù)提供商的脫鏈注冊:
- 信譽合約
用于跟蹤數(shù)據(jù)提供商的績效 - 訂單匹配合約
使用信譽合約選擇出價 - 匯總合約
從多個oracles收集使用提交,ChainLink建議計算加權響應,并提供了一組標準的匯總合約。
一個想法:SchellingCoin協(xié)議:將中位數(shù)作為答案
Solidity中的Oracle客戶端接口
此處復雜,先學Solidity
以太坊用于衡量執(zhí)行一組動作所需計算量的單位,交易或者合約執(zhí)行都需要一定量的gas。
gas具有雙重作用:
- 連接了以太坊價格和對礦工工作的獎勵(和以太坊價格的波動性對抗)
- 抵御拒絕服務攻擊
發(fā)送交易成本為21000gas、添加兩個數(shù)字成本3個gas
停機問題
交易過載
支付gas
gas有價格,但不能被擁有和花費,僅存在于EVM內(nèi)部,作為工作量的計數(shù)。
收取ether--->轉(zhuǎn)化為gas--->轉(zhuǎn)回ether
gas價格、成本限制、耗盡
交易前發(fā)起方需要指定gas limit和gas price
gas limit多了,發(fā)起方收到退款
gas limit少了,交易失敗,gas不退回
估算gas
通過假裝交易已經(jīng)被包含在區(qū)塊鏈中來估算gas
代碼
gas價格和交易優(yōu)先順序
gas price設置的高一般回處理的更快
區(qū)塊gas限制
Block gas limit是區(qū)塊中允許的最大gas量,鎖定了區(qū)塊中可以容納的交易數(shù)量
18年,區(qū)塊gas限制在500w左右,可以容納約238個交易,每個交易消耗21000gas
礦工決定了區(qū)塊gas限制是什么,礦工可以對gas限制進行投票來擴容
gas退款
以太坊通過退還高達一半的gas費用來鼓勵刪除存儲的變量
EVM中有2個負的gas操作:
清理合約是-24,000(SELFDESTRUCT)
清理存儲為-15,000(SSTORE [x] = 0)
gasToken
gasToken是一種ERC20的Token,允許任何人在gas價格低時存儲gas,在gas價格高時使用gas
租金
以太坊社區(qū)建議向智能合約收取“租金”來保持活力
不支付租金時,合約將被睡眠,簡單的讀取操作也無法進行,需要通過繳納租金和提交merkle證據(jù)來喚醒睡眠的合約。
EVM是實際處理內(nèi)部狀態(tài)和計算的協(xié)議部分,實際來看,EVM是包含數(shù)百萬個對象的大型去中心化虛擬機
- 虛擬機(Virtualbox、云計算)和EVM的不同:
虛擬機技術的目的是提供管理程序功能,或處理客戶操作系統(tǒng)與底層操作系統(tǒng)和硬件之間的系統(tǒng)調(diào)用,任務調(diào)度和資源管理的軟件抽象。 - JVM和EVM
JVM和EVM比較相像。
JVM旨在提供與底層主機操作系統(tǒng)或硬件無關的運行環(huán)境,從而實現(xiàn)各種操作系統(tǒng)的兼容性。
在JVM上運行的高級程序語言(JAVA、Scala)被編譯到相應的指令集字節(jié)碼中。這與編譯要在EVM上運行的Solidity源文件相當。
EVM機器語言
EVM的機器語言分為特定指令集合:
算術、邏輯和比較、控制流、系統(tǒng)調(diào)用、堆棧操作、存儲器操作、管理賬戶、gas和區(qū)塊信息
-
堆棧操作:
堆棧和內(nèi)存管理的操作碼指令:
POP // 項目出棧
PUSH // 項目入棧
MLOAD // 將項目加載到內(nèi)存中
MSTORE // 在內(nèi)存中存儲項目
JUMP // 改變程序計數(shù)器的位置
PC // 程序計數(shù)器
MSIZE // 活動的內(nèi)存大小
GAS // 交易可用的gas數(shù)量
DUP // 復制棧項目
SWAP // 交換棧項目 -
算術運算
通用算術運算代碼指令:
ADD //添加
MUL //乘法
SUB //減法
DIV //整數(shù)除法
SDIV //有符號整數(shù)除法
MOD // Modulo(剩余)操作
SMOD //簽名模運算
ADDMOD //模數(shù)加法
MULMOD //模數(shù)乘法
EXP //指數(shù)運算
STOP //停止操作 -
環(huán)境操作碼
處理執(zhí)行環(huán)境信息的通用操作碼:
ADDRESS //當前執(zhí)行賬戶的地址
BALANCE //賬戶余額
CALLVALUE //執(zhí)行環(huán)境的交易值
ORIGIN //執(zhí)行環(huán)境的原始地址
CALLER //執(zhí)行調(diào)用者的地址
CODESIZE //執(zhí)行環(huán)境代碼大小
GASPRICE //gas價格狀態(tài)
EXTCODESIZE //賬戶的代碼大小
RETURNDATACOPY //從先前的內(nèi)存調(diào)用輸出的數(shù)據(jù)的副本
狀態(tài)
就像CPU跟蹤執(zhí)行過程一樣,EVM必須跟蹤各種組件的狀態(tài)以支持以太坊交易。
以太坊也被描述為基于狀態(tài)的虛擬機,包含以下組件
-
World State
160位地址狀態(tài)標識符 和 賬戶狀態(tài) 之間的關系,在不可變的默克爾樹數(shù)據(jù)結(jié)構(gòu)中維護。 -
Account State
包含以下四個組件
nonce:從相應賬戶發(fā)送的交易數(shù)量的值
balance:賬戶擁有的wei數(shù)量
storageRoot:默克爾樹根節(jié)點的256位哈希值
codeHash:各個賬戶的EVM代碼的不可變哈希值 -
Storage State
EVM上運行時維護的賬戶特定狀態(tài)信息 -
Block State
交易所需的狀態(tài)值包括
blockhash:最近完成的塊的哈希值。
coinbase:收件人的地址。
timestamp:當前塊的時間戳。
number:當前塊的編號。
difficulty:當前區(qū)塊的難度。
gaslimit:當前區(qū)塊的gas限制。 -
Runtime Environment Information
用于使用交易的信息。
gasprice:當前gas價格,由交易發(fā)起人指定。
codesize:交易代碼庫的大小。
caller:執(zhí)行當前交易的賬戶的地址。
origin:當前交易原始發(fā)件人的地址。
狀態(tài)轉(zhuǎn)換計算函數(shù)
-
以太坊狀態(tài)轉(zhuǎn)換函數(shù)
用于計算_valid state transition_。 -
區(qū)塊終結(jié)狀態(tài)轉(zhuǎn)換函數(shù)
用于確定最終塊的狀態(tài),作為挖礦過程的一部分,包含區(qū)塊獎勵。 -
區(qū)塊級狀態(tài)轉(zhuǎn)換函數(shù)
應用于交易狀態(tài)時的區(qū)塊終結(jié)狀態(tài)轉(zhuǎn)換函數(shù)的結(jié)果狀態(tài)。
.sol編譯為EVM字節(jié)碼
solc命令
solc --help
使用 --opcode 命令編譯后生成 .opcode 機器語言操作碼文件
使用 --asm 命令編譯后生成一個 .evm 的機器語言說明文件
使用 --bin 命令編譯后生成一個字節(jié)碼文件
使用 --bin-runtime編譯后生成一個運行時字節(jié)碼文件
執(zhí)行EVM字節(jié)碼
-
gas
-
圖靈完備性和gas
圖靈完備:系統(tǒng)可以解決你輸入的任何問題。
EVM理論上是圖靈完備的,但是gas會組織它這么做。但對于大多數(shù)問題,EVM還是圖靈完備的。 -
字節(jié)碼與運行時字節(jié)碼
編譯時,會得到 合約字節(jié)碼 和 運行時字節(jié)碼。
-
合約字節(jié)碼
合約字節(jié)碼是最終位于區(qū)塊鏈上的字節(jié)碼,加上將字節(jié)碼放在區(qū)塊鏈上并運行構(gòu)造函數(shù)所需的字節(jié)碼。 -
運行時字節(jié)碼
運行時字節(jié)碼只是最終位于區(qū)塊鏈上的字節(jié)碼,不包括初始化合約并將其放在區(qū)塊鏈上所需的字節(jié)碼。
反匯編字節(jié)碼
以太坊網(wǎng)絡中,共識是指多個節(jié)點或代理在給定的時間點就區(qū)塊鏈狀態(tài)達成一致的能力。社區(qū)必須解決在技術上(網(wǎng)格內(nèi))和社交上(確保協(xié)議不會分叉和破裂)都達成共識。
以太坊可以更有效的抵御攻擊,但在應對變化時卻不那么果斷。
獲得共識和信任信息的能力將對區(qū)塊鏈技術作為資產(chǎn)類別和技術的未來使用有著重要意義。為應對這一挑戰(zhàn)并保持權力下放,社區(qū)不斷嘗試不同的共識模式。
共識度量
共識度量是可測量的數(shù)據(jù),區(qū)塊鏈網(wǎng)絡的節(jié)點必須在該數(shù)據(jù)上達成一致。
每當新塊添加到鏈中時,每個網(wǎng)絡節(jié)點會測量并批準一致性度量。
作為共識度量的結(jié)果,區(qū)塊鏈充當了從一個確定可驗證的事實延伸到下一個事實的真理鏈?;诠沧R度量,區(qū)塊鏈協(xié)議的節(jié)點變成迷你公證人(mini-notaries),它可以立刻從真實的節(jié)點中分辨出區(qū)塊鏈的錯誤副本,并報告全網(wǎng)絡,以便組織提交虛假信息的區(qū)塊來欺騙網(wǎng)絡。
基于一致性度量,區(qū)塊鏈不僅建立了完整性,并保持長期不變。
共識度量有多種,最重要的兩種是基于工作量的度量和基于風險的度量。
POW
基于POW,度量間建立了共識:因為他們使用協(xié)議將計算機設置為查找難題的答案,即找到適合網(wǎng)絡參數(shù)的散列的難題要求節(jié)點提交處理能力并使用電力與其他節(jié)點競爭以提出有效的哈希。
計算哈希很難,但驗證是否是正確結(jié)果很簡單。
比特幣網(wǎng)絡,平均每個區(qū)塊生成的時間是十分鐘。
以太坊網(wǎng)絡,平均每個區(qū)塊生成的時間是十秒。
基于風險的度量
基于風險的度量基于共識:選擇創(chuàng)建無效區(qū)塊的每個人都會失去比通過創(chuàng)建有效區(qū)塊獲得的更多的東西。
這種度量是通過鏈內(nèi)數(shù)據(jù)的共識創(chuàng)造的,而不是pow那種鏈外共識。
pow的共識度量主要關注SHA-256哈希的質(zhì)量和精確性
基于風險的度量的共識度量主要關注任何特定節(jié)點在添加新塊時所承擔的風險
-
Pos(權益證明)是一類共識算法
在基于pos的區(qū)塊鏈中,一組驗證者輪流對下一刻進行建議和投票,每個驗證者的投票權重取決于其存款大小
pos的優(yōu)勢包括安全、降低集中風險、能源效率
在Pos中任何人都可以都可以通過一種特殊交易成為驗證者,參與創(chuàng)建和同意新塊。
從算法角度來看,pos又分為基于鏈的權益證明和BFT風格的權益證明:
基于鏈的證明中,算法在每個時隙(10s)中偽隨機的選擇一個驗證者,為該驗證者分配創(chuàng)建單個塊的權限。
BFT 風格的權益證明中,驗證者被隨機分配提出區(qū)塊的權利,但通過多輪過程來確定那個區(qū)塊是規(guī)范的,其中每個炎癥者在每輪中對特定區(qū)塊投票,在流程結(jié)束后,所有(誠實并在線)的驗證者永久同意任何給定的塊是否屬于鏈條一部分。 -
PoA
PoA是pos的子集,常用于測試網(wǎng)、私有或聯(lián)盟網(wǎng)絡。
poa的區(qū)塊鏈中,交易有效性最終由一批經(jīng)過批準的鏈上賬戶(授權節(jié)點)確定。
poa是達成共識的最快途徑 -
DPos(代理權益證明)
dpos是一種修改的pos,網(wǎng)絡參與者投票選舉一系列代表來驗證和和保護區(qū)塊鏈。這可能類似poa,但是他們的權限可以被選民取消。
以太坊共識 Ethash
Ethash是pow,依賴于數(shù)據(jù)集的初始紀元產(chǎn)生,該數(shù)據(jù)集約1GB,是有向無環(huán)圖(DAG)。
DAG使用 Dagger-Hashimoto算法的版本,它是Vitalik Buterin的Dagger算法和Thaddeus Dryja的Hashimoto算法的組合。Dagger-Hashimoto算法是以太坊1.0使用的挖掘算法。隨著時間的推移,DAG線性增長,每紀元(30,000塊,125小時)更新一次。
Polkadot簡介
Polkadot是一種鏈間區(qū)塊鏈協(xié)議,包括與權益證明(PoS)鏈的整合,允許Parachain在沒有內(nèi)部共識的情況下獲得共識。
智能合約漏洞的基本類別:
- 自殺合約
可以被任意地址殺死的合約 - 貪婪合約
某個執(zhí)行狀態(tài)后無法釋放ether - 浪費合約
不經(jīng)意間將ether釋放到任意地址
Vtper是一種面向智能合約的實驗性編程語言,面向EVM。
Vyper致力于通過簡化代碼并使其對人類可讀而提供卓越的審計能力。Vyper的一個原則是讓開發(fā)人員幾乎不可能編寫誤導性代碼。
與Solidity比較
-
修飾器
Solidity中可以使用修飾器編寫函數(shù)
但將來修改修飾器會導致錯誤
Vyper中刪除了修飾器,使用內(nèi)聯(lián)檢查和斷言作為函數(shù)的一部分,提高了審計能力和可讀性。 -
類繼承
類繼承允許程序員從軟件庫中獲取預先存在的功能、屬性和行為。這可以促進代碼的重用。
Solidity支持多重繼承和多態(tài),這是面向?qū)ο缶幊痰闹匾匦?br> Vyper不支持,繼承會導致編碼人員和審計人員在多個文件之間跳轉(zhuǎn),這不利于理解程序。 -
內(nèi)聯(lián)匯編
內(nèi)聯(lián)匯編允許開發(fā)人員以低級別訪問EVM的機會,即在高級源代碼中使用EVM操作碼。
Vyper不支持 -
函數(shù)重載
具有相同名稱和不同參數(shù)選項的多個函數(shù)定義
foo(“hello”)和 foo(“hello”,“world”) -
變量類型轉(zhuǎn)換
允許程序員將變量從一種數(shù)據(jù)類型轉(zhuǎn)換為另一種數(shù)據(jù)類型的機制 -
前置條件和后置條件
Vyper明確處理前置條件和狀態(tài)更改。雖然會產(chǎn)生冗余,但確實更有可讀性和安全性。
Vyper編寫合約時要考慮:
條件:以太坊狀態(tài)變量的當前狀態(tài)/條件是什么
效果:這個合約對執(zhí)行狀態(tài)變量的條件有什么影響,什么會被影響什么不會,這些影響是否和我們的意圖一致
交互:充分考慮代碼執(zhí)行的所有永久結(jié)果、后果和方案,包括與其他合約的交互
Vyper
Vyper打開了新的大門,偏離了傳統(tǒng)的面向?qū)ο缶幊蹋∣OP)
裝飾符
類似@private、@public、@constant、@payable 這樣的裝飾符在每個函數(shù)開頭聲明。
@private:合約外部無法訪問
@public:公開可見可執(zhí)行
@constant:不允許變量改變
@payable:只有以@payable開頭聲明的函數(shù)才能接受價值
在線代碼編輯器
Vyper擁有自己的在線代碼編輯器
命令行運行
擴展名為 .v.py ,安裝Vyper后,可以運行命令來編譯和提供字節(jié)碼
讀寫數(shù)據(jù)
智能合約可以將數(shù)據(jù)寫入兩個地方:以太坊的全球狀態(tài)查找樹或以太坊的鏈數(shù)據(jù)。
-
全局狀態(tài)
給定智能合約中的狀態(tài)變量,存儲在以太坊的全局狀態(tài)查找樹中,給定的智能合約只能存儲,讀取和修改與該合約地址相關的數(shù)據(jù)(即智能合約無法讀取或?qū)懭肫渌悄芎霞s)。 -
Log
智能合約也可以通過日志事件寫入以太坊的鏈數(shù)據(jù)。最初Vyper用_log_語法來生命,但現(xiàn)在已經(jīng)更新。目前是
雖然智能合約可以寫入以太坊的鏈數(shù)據(jù)(通過日志事件),但智能合約無法讀取他們創(chuàng)建的鏈上日志事件。但可以在公共鏈上由輕客戶端發(fā)現(xiàn)和讀取日志。
ERC20接口
Vyper中ERC20上預編譯合同,默認可用。Vyper中的合約必須聲明為全局變量。如:
操作碼(OPCODES)
合約一般用Solidity或Vyper等高級語言編寫,編譯器負責獲取高級代碼并創(chuàng)建他的低級解釋,然后在EVM上運行。編譯器可以提取代碼的最低表示(在EVM執(zhí)行前)是操作碼。這種情況下需要高級語言的每個實現(xiàn)來提供適當?shù)木幾g機制以允許將高級代碼編譯到通用預定義的EVM操作碼中。
Vyper實現(xiàn)了以太坊的分片操作碼
https://github.com/oldnicke/MasteringEthereum/blob/master/第十七章.asciidoc
EIPs
EIP代表以太坊改進提案。
EIP是一個設計文檔,為以太坊社區(qū)提供信息,或描述以太坊或其過程或環(huán)境的新功能。
EIP應提供該功能的簡明技術規(guī)范和該功能的基本原理。
EIP作者負責在社區(qū)內(nèi)建立共識并記錄不同意見。
ERCs
ERC代表以太坊征求建議。
如果EIP被接受,它將成為ERC的一部分
多數(shù)的硬分叉作為路線圖的一部分,包含社區(qū)普遍認同的更新,這被稱為共識。
但也有一些硬分叉不是共識,這會導致多個不同的區(qū)塊鏈,例如以太坊/以太坊經(jīng)典。
以太坊經(jīng)典(ETC)
2016年7月20日,在192w的區(qū)塊高度上,以太坊通過硬分叉引入了改變,退還了360w的ether,這些ether來自名為The DAO的合約。
The DAO
DAO由Slock.lt創(chuàng)建,被視為基于社區(qū),為項目提供資金的一種方式。
核心思想是提交提案,管理者管理提案,資金從以太坊的投資者籌集,如果項目成功,投資者將會獲得收益。
DAO是以太坊token的一個實驗。
重入bug(Re-Entrancy)
DAO攻擊者從DAO中吸取了360w個ether。
RHG志愿者開始用相同的漏洞提取剩余的資金,并計劃退還社區(qū),保護了社區(qū)的大部分ether。
-
Re-Entrancy
DAO攻擊者發(fā)起多次請求,這允許了攻擊者在合約記錄攻擊者成功提取之前,再次提取。 -
攻擊流程
攻擊者要求合約提取tokens
在成功提取被記錄之前,再次要求提取tokens
盡可能重復上一步
合約最終記錄了一次DAO的提取,并失去了在此期間發(fā)生的取款
ETH和ETC
技術差異&意識形態(tài)差異
-
以太坊基礎相關文檔
以太坊黃皮書: https://ethereum.github.io/yellowpaper/paper.pdf
褐皮書”:為更廣泛的讀者以不太正式的語言重寫了“黃皮書”: https://github.com/chronaeon/beigepaper
DΞVp2p 網(wǎng)絡協(xié)議: https://github.com/ethereum/wiki/wiki/DΞVp2p-Wire-Protocol
以太坊狀態(tài)機 —— 一個“Awesome”資源列表 https://github.com/ethereum/wiki/wiki/Ethereum-Virtual-Machine-(EVM)-Awesome-List
LevelDB 數(shù)據(jù)庫 (最經(jīng)常用于存儲區(qū)塊鏈本地副本): http://leveldb.org
Merkle Patricia Trees: https://github.com/ethereum/wiki/wiki/Patricia-Tree
Ethash 工作量證明共識算法: https://github.com/ethereum/wiki/wiki/Ethash
Casper 權益證明 v1 實現(xiàn)指南: https://github.com/ethereum/research/wiki/Casper-Version-1-Implementation-Guide
Go-Ethereum (Geth) 客戶端: https://geth.ethereum.org/
Parity 以太坊客戶端: https://parity.io/ -
生成助記詞,種子和擴展私鑰的獨立網(wǎng)頁
https://iancoleman.io/bip39/ -
Solidity完整文檔
https://solidity.readthedocs.io/en/latest/ -
以太坊最佳安全開發(fā)指南
https://github.com/ConsenSys/smart-contract-best-practices/blob/master/README-zh.md
一個受到社區(qū)安全審查的庫OpenZeppelin:https://openzeppelin.org/[OpenZeppelin] ,具有廣泛的經(jīng)過審查的簡單行為
一個用于安全的開發(fā)和管理智能合約的服務和工具的開源平臺zeppelin_os:https://zeppelinos.org/[zeppelin_os] ,zeppelin_os在EVM上提供了一層,讓開發(fā)人員可以輕松發(fā)布和升級DApp
關于Ethereum ABI的更嚴格和更深入的解釋可以在這找到: https://solidity.readthedocs.io/en/develop/abi-spec.html
另
Github: https://github.com/ConsenSys/smart-contract-best-practices/
Docs: https://consensys.github.io/smart-contract-best-practices/
https://blog.zeppelin.solutions/onward-with-ethereum-smart-contract-security-97a827e47702
https://medium.com/zeppelin-blog/the-hitchhikers-guide-to-smart-contracts-in-ethereum-848f08001f05#.cox40d2ut -
合約庫
Github link: https://github.com/ethpm
Repository link: https://www.ethpm.com/registry
Website: https://www.ethpm.com/
Documentation: https://www.ethpm.com/docs/integration-guide -
Swarm&IPFS
Swarm主頁: http://swarm-gateways.net/bzz:/theswarm.eth/
閱讀文檔: https://swarm-guide.readthedocs.io/en/latest/index.html
Swarm開發(fā)人員的入門指南: https://github.com/ethersphere/swarm/wiki/swarm
Swarm討論組: https://gitter.im/ethersphere/orange-lounge
Ethereum’s Swarm 和 IPFS 的相似之處與不同之處; https://github.com/ethersphere/go-ethereum/wiki/IPFS-&-SWARM -
Truffle
入門和文檔:http://truffleframework.com/docs
Github:https://github.com/trufflesuite/truffle
Website:https://truffleframework.com -
Embark
入門和文檔:https://embark.readthedocs.io
Github:https://github.com/embark-framework/embark
Website:https://github.com/embark-framework/embark -
Emerald Platform
入門和文檔:https://docs.etcdevteam.com
Github:https://github.com/etcdevteam/emerald-platform
Website:https://emeraldplatform.io
dapp_develotment_tool_sec:一個用于智能合約的命令行工具
包管理、源代碼構(gòu)建、單元測試、簡單的合約部署
入門和文檔:https://dapp.readthedocs.io/en/latest/ -
第十二章 Oracle預言機用到的
點擊查看所有網(wǎng)站
-
gasToken:了解計算盈利能力以及如何使用釋放gas所涉及的數(shù)學
https://gastoken.io/ -
合約租金
ethereum/EIPs#35
https://ethresear.ch/t/a-simple-and-principled-way-to-compute-rent-fees/1455 https://ethresear.ch/t/improving-the-ux-of-rent-with-a-sleeping-waking-mechanism/1480 -
反編譯程序
Porosity 是一個流行的開源反編譯器:https://github.com/comaeio/porosity
Ethersplay 是Binary Ninja的EVM插件,一個反匯編程序:https://github.com/trailofbits/ethersplay
IDA-Evm 是IDA的EVM插件,另一個反匯編程序:https://github.com/trailofbits/ida-evm
EVM工具:ByteCode To Opcode Disassembler (用于檢查/調(diào)試編譯是否完整運行,如果源代碼未發(fā)布則可用于逆向工程) -
Vyper在線代碼編輯器
https://vyper.online -
DΞVp2p
https://github.com/oldnicke/MasteringEthereum/blob/master/第十七章.asciidoc -
以太坊改進提案(EIPs)
https://eips.ethereum.org/