Solidity 官方文档中文版 2_Ethereum 智能合约介绍
一個(gè)簡(jiǎn)單的智能合約
先從一個(gè)非常基礎(chǔ)的例子開始,不用擔(dān)心你現(xiàn)在還一點(diǎn)都不了解,我們將逐步了解到更多的細(xì)節(jié)。
Storage
contract SimpleStorage {uint storedData;function set(uint x) {storedData = x;}function get() constant returns (uint retVal) {return storedData;} }在Solidity中,一個(gè)合約由一組代碼(合約的函數(shù))和數(shù)據(jù)(合約的狀態(tài))組成。合約位于以太坊區(qū)塊鏈上的一個(gè)特殊地址。*uint storedData*;?這行代碼聲明了一個(gè)狀態(tài)變量,變量名為storedData,類型為?uint?(256bits無符號(hào)整數(shù))。你可以認(rèn)為它就像數(shù)據(jù)庫(kù)里面的一個(gè)存儲(chǔ)單元,跟管理數(shù)據(jù)庫(kù)一樣,可以通過調(diào)用函數(shù)查詢和修改它。在以太坊中,通常只有合約的擁有者才能這樣做。在這個(gè)例子中,函數(shù)?set?和?get?分別用于修改和查詢變量的值。
跟很多其他語言一樣,訪問狀態(tài)變量時(shí),不需要在前面增加 this. 這樣的前綴。
這個(gè)合約還無法做很多事情(受限于以太坊的基礎(chǔ)設(shè)施),僅僅是允許任何人儲(chǔ)存一個(gè)數(shù)字。而且世界上任何一個(gè)人都可以來存取這個(gè)數(shù)字,缺少一個(gè)(可靠的)方式來保護(hù)你發(fā)布的數(shù)字。任何人都可以調(diào)用set方法設(shè)置一個(gè)不同的數(shù)字覆蓋你發(fā)布的數(shù)字。但是你的數(shù)字將會(huì)留存在區(qū)塊鏈的歷史上。稍后我們會(huì)學(xué)習(xí)如何增加一個(gè)存取限制,使得只有你才能修改這個(gè)數(shù)字。
代幣的例子
接下來的合約將實(shí)現(xiàn)一個(gè)形式最簡(jiǎn)單的加密貨幣。空中取幣不再是一個(gè)魔術(shù),當(dāng)然只有創(chuàng)建合約的人才能做這件事情(想用其他貨幣發(fā)行模式也很簡(jiǎn)單,只是實(shí)現(xiàn)細(xì)節(jié)上的差異)。而且任何人都可以發(fā)送貨幣給其他人,不需要注冊(cè)用戶名和密碼,只要有一對(duì)以太坊的公私鑰即可。
Note
對(duì)于在線solidity環(huán)境來說,這不是一個(gè)好的例子。如果你使用在線solidity環(huán)境?來嘗試這個(gè)例子。調(diào)用函數(shù)時(shí),將無法改變from的地址。所以你只能扮演鑄幣者的角色,可以鑄造貨幣并發(fā)送給其他人,而無法扮演其他人的角色。這點(diǎn)在線solidity環(huán)境將來會(huì)做改進(jìn)。
contract Coin { //關(guān)鍵字“public”使變量能從合約外部訪問。address public minter;mapping (address => uint) public balances;//事件讓輕客戶端能高效的對(duì)變化做出反應(yīng)。event Sent(address from, address to, uint amount);//這個(gè)構(gòu)造函數(shù)的代碼僅僅只在合約創(chuàng)建的時(shí)候被運(yùn)行。function Coin() {minter = msg.sender;}function mint(address receiver, uint amount) {if (msg.sender != minter) return;balances[receiver] += amount;}function send(address receiver, uint amount) {if (balances[msg.sender] < amount) return;balances[msg.sender] -= amount;balances[receiver] += amount;Sent(msg.sender, receiver, amount);} }這個(gè)合約引入了一些新的概念,讓我們一個(gè)一個(gè)來看一下。
address public minter;?這行代碼聲明了一個(gè)可公開訪問的狀態(tài)變量,類型為address。address類型的值大小為160?bits,不支持任何算術(shù)操作。適用于存儲(chǔ)合約的地址或其他人的公私鑰。public關(guān)鍵字會(huì)自動(dòng)為其修飾的狀態(tài)變量生成訪問函數(shù)。沒有public關(guān)鍵字的變量將無法被其他合約訪問。另外只有本合約內(nèi)的代碼才能寫入。自動(dòng)生成的函數(shù)如下:
function minter() returns (address) { return minter; }當(dāng)然我們自己增加一個(gè)這樣的訪問函數(shù)是行不通的。編譯器會(huì)報(bào)錯(cuò),指出這個(gè)函數(shù)與一個(gè)狀態(tài)變量重名。
下一行代碼?mapping (address => uint) public balances;?創(chuàng)建了一個(gè)public的狀態(tài)變量,但是其類型更加的復(fù)雜。該類型將一些address映射到無符號(hào)整數(shù)。mapping可以被認(rèn)為是一個(gè)哈希表,每一個(gè)可能的key對(duì)應(yīng)的value被虛擬的初始化為全0.這個(gè)類比不是很嚴(yán)謹(jǐn),對(duì)于一個(gè)mapping,無法獲取一個(gè)包含其所有key或者value的鏈表。所以我們得自己記著添加了哪些東西到mapping中。更好的方式是維護(hù)一個(gè)這樣的鏈表,或者使用其他更高級(jí)的數(shù)據(jù)類型。或者只在不受這個(gè)缺陷影響的場(chǎng)景中使用mapping,就像這個(gè)例子。在這個(gè)例子中由public關(guān)鍵字生成的訪問函數(shù)將會(huì)更加復(fù)雜,其代碼大致如下:
function balances(address _account) returns (uint balance) {return balances[_account]; }我們可以很方便的通過這個(gè)函數(shù)查詢某個(gè)特定賬號(hào)的余額。
event Sent(address from, address to, uint value);?這行代碼聲明了一個(gè)“事件”。由send函數(shù)的最后一行代碼觸發(fā)。客戶端(服務(wù)端應(yīng)用也適用)可以以很低的開銷來監(jiān)聽這些由區(qū)塊鏈觸發(fā)的事件。事件觸發(fā)時(shí),監(jiān)聽者會(huì)同時(shí)接收到from,to,value這些參數(shù)值,可以方便的用于跟蹤交易。為了監(jiān)聽這個(gè)事件,你可以使用如下代碼:
Coin.Sent().watch({}, '', function(error, result) {if (!error) {console.log("Coin transfer: " + result.args.amount +" coins were sent from " + result.args.from +" to " + result.args.to + ".");console.log("Balances now:\n" +"Sender: " + Coin.balances.call(result.args.from) +"Receiver: " + Coin.balances.call(result.args.to));} }注意在客戶端中是如何調(diào)用自動(dòng)生成的 balances 函數(shù)的。
這里有個(gè)比較特殊的函數(shù) Coin。它是一個(gè)構(gòu)造函數(shù),會(huì)在合約創(chuàng)建的時(shí)候運(yùn)行,之后就無法被調(diào)用。它會(huì)永久得存儲(chǔ)合約創(chuàng)建者的地址。msg(以及tx和block)是一個(gè)神奇的全局變量,它包含了一些可以被合約代碼訪問的屬于區(qū)塊鏈的屬性。msg.sender 總是存放著當(dāng)前函數(shù)的外部調(diào)用者的地址。
最后,真正被用戶或者其他合約調(diào)用,用來完成本合約功能的函數(shù)是mint和send。如果合約創(chuàng)建者之外的其他人調(diào)用mint,什么都不會(huì)發(fā)生。而send可以被任何人(擁有一定數(shù)量的代幣)調(diào)用,發(fā)送一些幣給其他人。注意,當(dāng)你通過該合約發(fā)送一些代幣到某個(gè)地址,在區(qū)塊鏈瀏覽器中查詢?cè)摰刂穼⑹裁匆部床坏健R驗(yàn)榘l(fā)送代幣導(dǎo)致的余額變化只存儲(chǔ)在該代幣合約的數(shù)據(jù)存儲(chǔ)中。通過事件我們可以很容易創(chuàng)建一個(gè)可以追蹤你的新幣交易和余額的“區(qū)塊鏈瀏覽器”。
區(qū)塊鏈基礎(chǔ)
對(duì)于程序員來說,區(qū)塊鏈這個(gè)概念其實(shí)不難理解。因?yàn)樽铍y懂的一些東西(挖礦,哈希,橢圓曲線加密,點(diǎn)對(duì)點(diǎn)網(wǎng)絡(luò)等等)只是為了提供一系列的特性和保障。你只需要接受這些既有的特性,不需要關(guān)心其底層的技術(shù)。就像你如果僅僅是為了使用亞馬遜的AWS,并不需要了解其內(nèi)部工作原理。
交易/事務(wù)
區(qū)塊鏈?zhǔn)且粋€(gè)全局共享的,事務(wù)性的數(shù)據(jù)庫(kù)。這意味著參與這個(gè)網(wǎng)絡(luò)的每一個(gè)人都可以讀取其中的記錄。如果你想修改這個(gè)數(shù)據(jù)庫(kù)中的東西,就必須創(chuàng)建一個(gè)事務(wù),并得到其他所有人的確認(rèn)。事務(wù)這個(gè)詞意味著你要做的修改(假如你想同時(shí)修改兩個(gè)值)只能被完完全全的實(shí)施或者一點(diǎn)都沒有進(jìn)行。
此外,當(dāng)你的事務(wù)被應(yīng)用到這個(gè)數(shù)據(jù)庫(kù)的時(shí)候,其他事務(wù)不能修改該數(shù)據(jù)庫(kù)。
舉個(gè)例子,想象一張表,里面列出了某個(gè)電子貨幣所有賬號(hào)的余額。當(dāng)從一個(gè)賬戶到另外一個(gè)賬戶的轉(zhuǎn)賬請(qǐng)求發(fā)生時(shí),這個(gè)數(shù)據(jù)庫(kù)的事務(wù)特性確保從一個(gè)賬戶中減掉的金額會(huì)被加到另一個(gè)賬戶上。如果因?yàn)槟撤N原因,往目標(biāo)賬戶上增加金額無法進(jìn)行,那么源賬戶的金額也不會(huì)發(fā)生任何變化。
此外,一個(gè)事務(wù)會(huì)被發(fā)送者(創(chuàng)建者)進(jìn)行密碼學(xué)簽名。這項(xiàng)措施非常直觀的為數(shù)據(jù)庫(kù)的特定修改增加了訪問保護(hù)。在電子貨幣的例子中,一個(gè)簡(jiǎn)單的檢查就可以確保只有持有賬戶密鑰的人,才能從該賬戶向外轉(zhuǎn)賬。
區(qū)塊
區(qū)塊鏈要解決的一個(gè)主要難題,在比特幣中被稱為“雙花攻擊”。當(dāng)網(wǎng)絡(luò)上出現(xiàn)了兩筆交易,都要花光一個(gè)賬戶中的錢時(shí),會(huì)發(fā)生什么?一個(gè)沖突?
簡(jiǎn)單的回答是你不需要關(guān)心這個(gè)問題。這些交易會(huì)被排序并打包成“區(qū)塊”,然后被所有參與的節(jié)點(diǎn)執(zhí)行和分發(fā)。如果兩筆交易相互沖突,排序靠后的交易會(huì)被拒絕并剔除出區(qū)塊。
這些區(qū)塊按時(shí)間排成一個(gè)線性序列。這也正是“區(qū)塊鏈”這個(gè)詞的由來。區(qū)塊以一個(gè)相當(dāng)規(guī)律的時(shí)間間隔加入到鏈上。對(duì)于以太坊,這個(gè)間隔大致是17秒。
作為“順序選擇機(jī)制”(通常稱為“挖礦”)的一部分,一段區(qū)塊鏈可能會(huì)時(shí)不時(shí)被回滾。但這種情況只會(huì)發(fā)生在整條鏈的末端。回滾涉及的區(qū)塊越多,其發(fā)生的概率越小。所以你的交易可能會(huì)被回滾,甚至?xí)粡膮^(qū)塊鏈中刪除。但是你等待的越久,這種情況發(fā)生的概率就越小。
以太坊虛擬機(jī)
總覽
以太坊虛擬機(jī)(EVM)是以太坊中智能合約的運(yùn)行環(huán)境。它不僅被沙箱封裝起來,事實(shí)上它被完全隔離,也就是說運(yùn)行在EVM內(nèi)部的代碼不能接觸到網(wǎng)絡(luò)、文件系統(tǒng)或者其它進(jìn)程。甚至智能合約與其它智能合約只有有限的接觸。
賬戶
以太坊中有兩類賬戶,它們共用同一個(gè)地址空間。外部賬戶,該類賬戶被公鑰-私鑰對(duì)控制(人類)。合約賬戶,該類賬戶被存儲(chǔ)在賬戶中的代碼控制。
外部賬戶的地址是由公鑰決定的,合約賬戶的地址是在創(chuàng)建該合約時(shí)確定的(這個(gè)地址由合約創(chuàng)建者的地址和該地址發(fā)出過的交易數(shù)量計(jì)算得到,地址發(fā)出過的交易數(shù)量也被稱作"nonce")
合約賬戶存儲(chǔ)了代碼,外部賬戶則沒有,除了這點(diǎn)以外,這兩類賬戶對(duì)于EVM來說是一樣的。
每個(gè)賬戶有一個(gè)key-value形式的持久化存儲(chǔ)。其中key和value的長(zhǎng)度都是256比特,名字叫做storage.
另外,每個(gè)賬戶都有一個(gè)以太幣余額(單位是“Wei"),該賬戶余額可以通過向它發(fā)送帶有以太幣的交易來改變。
交易
一筆交易是一條消息,從一個(gè)賬戶發(fā)送到另一個(gè)賬戶(可能是相同的賬戶或者零賬戶,見下文)。交易可以包含二進(jìn)制數(shù)據(jù)(payload)和以太幣。
如果目標(biāo)賬戶包含代碼,該代碼會(huì)執(zhí)行,payload就是輸入數(shù)據(jù)。
如果目標(biāo)賬戶是零賬戶(賬戶地址是0),交易將創(chuàng)建一個(gè)新合約。正如上文所講,這個(gè)合約地址不是零地址,而是由合約創(chuàng)建者的地址和該地址發(fā)出過的交易數(shù)量(被稱為nonce)計(jì)算得到。創(chuàng)建合約交易的payload被當(dāng)作EVM字節(jié)碼執(zhí)行。執(zhí)行的輸出做為合約代碼被永久存儲(chǔ)。這意味著,為了創(chuàng)建一個(gè)合約,你不需要向合約發(fā)送真正的合約代碼,而是發(fā)送能夠返回真正代碼的代碼。
Gas
以太坊上的每筆交易都會(huì)被收取一定數(shù)量的gas,gas的目的是限制執(zhí)行交易所需的工作量,同時(shí)為執(zhí)行支付費(fèi)用。當(dāng)EVM執(zhí)行交易時(shí),gas將按照特定規(guī)則被逐漸消耗。
gas price(以太幣計(jì))是由交易創(chuàng)建者設(shè)置的,發(fā)送賬戶需要預(yù)付的交易費(fèi)用 = gas price * gas amount。 如果執(zhí)行結(jié)束還有g(shù)as剩余,這些gas將被返還給發(fā)送賬戶。
無論執(zhí)行到什么位置,一旦gas被耗盡(比如降為負(fù)值),將會(huì)觸發(fā)一個(gè)out-of-gas異常。當(dāng)前調(diào)用幀所做的所有狀態(tài)修改都將被回滾。
存儲(chǔ),主存和棧
每個(gè)賬戶有一塊持久化內(nèi)存區(qū)域被稱為存儲(chǔ)。其形式為key-value,key和value的長(zhǎng)度均為256比特。在合約里,不能遍歷賬戶的存儲(chǔ)。相對(duì)于另外兩種,存儲(chǔ)的讀操作相對(duì)來說開銷較大,修改存儲(chǔ)更甚。一個(gè)合約只能對(duì)它自己的存儲(chǔ)進(jìn)行讀寫。
第二個(gè)內(nèi)存區(qū)被稱為主存。合約執(zhí)行每次消息調(diào)用時(shí),都有一塊新的,被清除過的主存。主存可以以字節(jié)粒度尋址,但是讀寫粒度為32字節(jié)(256比特)。操作主存的開銷隨著其增長(zhǎng)而變大(平方級(jí)別)。
EVM不是基于寄存器,而是基于棧的虛擬機(jī)。因此所有的計(jì)算都在一個(gè)被稱為棧的區(qū)域執(zhí)行。棧最大有1024個(gè)元素,每個(gè)元素256比特。對(duì)棧的訪問只限于其頂端,方式為:允許拷貝最頂端的16個(gè)元素中的一個(gè)到棧頂,或者是交換棧頂元素和下面16個(gè)元素中的一個(gè)。所有其他操作都只能取最頂?shù)膬蓚€(gè)(或一個(gè),或更多,取決于具體的操作)元素,并把結(jié)果壓在棧頂。當(dāng)然可以把棧上的元素放到存儲(chǔ)或者主存中。但是無法只訪問棧上指定深度的那個(gè)元素,在那之前必須要把指定深度之上的所有元素都從棧中移除才行。
指令集
EVM的指令集被刻意保持在最小規(guī)模,以盡可能避免可能導(dǎo)致共識(shí)問題的錯(cuò)誤實(shí)現(xiàn)。所有的指令都是針對(duì)256比特這個(gè)基本的數(shù)據(jù)類型的操作。具備常用的算術(shù),位,邏輯和比較操作。也可以做到條件和無條件跳轉(zhuǎn)。此外,合約可以訪問當(dāng)前區(qū)塊的相關(guān)屬性,比如它的編號(hào)和時(shí)間戳。
消息調(diào)用
合約可以通過消息調(diào)用的方式來調(diào)用其它合約或者發(fā)送以太幣到非合約賬戶。消息調(diào)用和交易非常類似,它們都有一個(gè)源,一個(gè)目標(biāo),數(shù)據(jù)負(fù)載,以太幣,gas和返回?cái)?shù)據(jù)。事實(shí)上每個(gè)交易都可以被認(rèn)為是一個(gè)頂層消息調(diào)用,這個(gè)消息調(diào)用會(huì)依次產(chǎn)生更多的消息調(diào)用。
一個(gè)合約可以決定剩余gas的分配。比如內(nèi)部消息調(diào)用時(shí)使用多少gas,或者期望保留多少gas。如果在內(nèi)部消息調(diào)用時(shí)發(fā)生了out-of-gas異常(或者其他異常),合約將會(huì)得到通知,一個(gè)錯(cuò)誤碼被壓在棧上。這種情況只是內(nèi)部消息調(diào)用的gas耗盡。在solidity中,這種情況下發(fā)起調(diào)用的合約默認(rèn)會(huì)觸發(fā)一個(gè)人工異常。這個(gè)異常會(huì)打印出調(diào)用棧。就像之前說過的,被調(diào)用的合約(發(fā)起調(diào)用的合約也一樣)會(huì)擁有嶄新的主存并能夠訪問調(diào)用的負(fù)載。調(diào)用負(fù)載被存儲(chǔ)在一個(gè)單獨(dú)的被稱為calldata的區(qū)域。調(diào)用執(zhí)行結(jié)束后,返回?cái)?shù)據(jù)將被存放在調(diào)用方預(yù)先分配好的一塊內(nèi)存中。
調(diào)用層數(shù)被限制為1024,因此對(duì)于更加復(fù)雜的操作,我們應(yīng)該使用循環(huán)而不是遞歸。
代碼調(diào)用和庫(kù)
存在一種特殊類型的消息調(diào)用,被稱為callcode。它跟消息調(diào)用幾乎完全一樣,只是加載自目標(biāo)地址的代碼將在發(fā)起調(diào)用的合約上下文中運(yùn)行。
這意味著一個(gè)合約可以在運(yùn)行時(shí)從另外一個(gè)地址動(dòng)態(tài)加載代碼。存儲(chǔ),當(dāng)前地址和余額都指向發(fā)起調(diào)用的合約,只有代碼是從被調(diào)用地址獲取的。
這使得Solidity可以實(shí)現(xiàn)”庫(kù)“。可復(fù)用的庫(kù)代碼可以應(yīng)用在一個(gè)合約的存儲(chǔ)上,可以用來實(shí)現(xiàn)復(fù)雜的數(shù)據(jù)結(jié)構(gòu)。
日志
在區(qū)塊層面,可以用一種特殊的可索引的數(shù)據(jù)結(jié)構(gòu)來存儲(chǔ)數(shù)據(jù)。這個(gè)特性被稱為日志,Solidity用它來實(shí)現(xiàn)事件。合約創(chuàng)建之后就無法訪問日志數(shù)據(jù),但是這些數(shù)據(jù)可以從區(qū)塊鏈外高效的訪問。因?yàn)椴糠秩罩緮?shù)據(jù)被存儲(chǔ)在布隆過濾器(Bloom filter)?中,我們可以高效并且安全的搜索日志,所以那些沒有下載整個(gè)區(qū)塊鏈的網(wǎng)絡(luò)節(jié)點(diǎn)(輕客戶端)也可以找到這些日志。
創(chuàng)建
合約甚至可以通過一個(gè)特殊的指令來創(chuàng)建其他合約(不是簡(jiǎn)單的向零地址發(fā)起調(diào)用)。創(chuàng)建合約的調(diào)用跟普通的消息調(diào)用的區(qū)別在于,負(fù)載數(shù)據(jù)執(zhí)行的結(jié)果被當(dāng)作代碼,調(diào)用者/創(chuàng)建者在棧上得到新合約的地址。
自毀
只有在某個(gè)地址上的合約執(zhí)行自毀操作時(shí),合約代碼才會(huì)從區(qū)塊鏈上移除。合約地址上剩余的以太幣會(huì)發(fā)送給指定的目標(biāo),然后其存儲(chǔ)和代碼被移除。
注意,即使一個(gè)合約的代碼不包含自毀指令,依然可以通過代碼調(diào)用(callcode)來執(zhí)行這個(gè)操作。
?
參考文獻(xiàn):?
http://solidity.readthedocs.io/en/latest/installing-solidity.html?
http://wiki.jikexueyuan.com/project/solidity-zh/installing-solidity.html
?
轉(zhuǎn)載于:https://www.cnblogs.com/felixzuo/p/8821851.html
總結(jié)
以上是生活随笔為你收集整理的Solidity 官方文档中文版 2_Ethereum 智能合约介绍的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 做梦梦到卖鱼是什么意思
- 下一篇: 梦到佛像倒了意味什么