4,智能合約
4.1 EVM基礎(chǔ)
賬戶
以太坊中有兩種不同類型但是共享同一地址空間的賬戶:外部賬戶由一對公私鑰控制,合約賬戶由賬戶內(nèi)部的合約代碼控制。
外部賬戶的地址是由公鑰(經(jīng)過hash運算)決定的,而合約賬戶的地址在此合約被創(chuàng)建的時候決定的(由合約創(chuàng)建者的地址和發(fā)送到此合約地址的交易數(shù)決定,這就是所謂的“nonce”)不管是哪種類型的賬戶,EVM的處理方式是一樣的
每個賬戶都有一個持久的key-value類型的存儲,把256字節(jié)的key映射到256字節(jié)的value
此外,每個賬戶都有以“Wei”為單位,在交易過程中會被修改的資產(chǎn)(balance)信息
交易
交易是一個從賬戶發(fā)往另一個賬戶(可以是同一個賬戶或者是special zero-account)的消息。它包含二進(jìn)制數(shù)據(jù)(交易相關(guān)的數(shù)據(jù))and Ether。
如果目標(biāo)賬戶包含代碼,代碼會被執(zhí)行,交易相關(guān)的數(shù)據(jù)將作為參數(shù)
如果目標(biāo)賬戶是地址為0的賬戶zero-account, 交易會創(chuàng)建一個新的合約。如上文提到的,合約地址不是一個地址為0的地址,而是一個由交易發(fā)送者和交易數(shù)來決定的地址。這樣的一筆(到zero-account)交易的相關(guān)參數(shù)會被轉(zhuǎn)化為EVM字節(jié)碼然后被執(zhí)行,輸出結(jié)果就是被永久存儲的合約代碼。這意味著為了創(chuàng)建一個合約,并不需要發(fā)送真實的合約代碼,代碼可以被自動創(chuàng)建
費用
創(chuàng)建之后,每筆交易都需要一定數(shù)量的費用,用于限制交易所消耗的工作量,即交易是需要付出代價的(避免DDoS攻擊)。EVM執(zhí)行交易的過程中,費用會按一個特殊規(guī)則逐漸減少
費用的多少是由交易發(fā)起者設(shè)置,至少需要從發(fā)起賬戶支付gas_price * gas用費。如果交易執(zhí)行完畢費用還有剩余的,將退回到發(fā)起賬戶。
如果交易完成之前費用耗盡,將會拋出一個out-of-gas的異常,所有的修改都會被回滾
Storage,Memory,Stack
每個賬戶都有一個持久的內(nèi)存空間,稱之為storage,storage以key-value形式存儲,256字節(jié)的key映射到256字節(jié)value,合約內(nèi)部不可能枚舉storage(內(nèi)部元素),讀取或者修改storage操作消耗都很大(原文是 It is not possible to enumerate storage from within a contract and it is comparatively costly to read and even more so, to modify storage. )。 合約只能讀取和修改自己的storage里的數(shù)據(jù)。
第二種內(nèi)存空間稱之為memory,里面存儲著每個消息調(diào)用時合約創(chuàng)建的實例。memory是線型的,可以以字節(jié)級別來處理,但是限制為256字節(jié)寬度,寫入可以是8或256字節(jié)寬度。當(dāng)讀取或?qū)懭胍粋€預(yù)先未觸發(fā)的指令的時候會消耗memory的空間,消耗空間的同時,必須支付費用(gas)。memory消耗的越多,手續(xù)費越多(按平方級增長)
EVM不是一個注冊的機(jī)器而是一個堆棧機(jī)器,所以所有的計算指令都在stack空間里面執(zhí)行。stack最多只能容納1024個長度不超過256字節(jié)的指令元素。只能用下述方法,從頂部訪問stack:可以拷貝最頂部的16個元素中的一個到stack的最頂部,或者將最頂部的那個元素與其下面的16個元素之一互換。所有其它操作從stack最頂部取出兩個(或一個,或更多,取決于操作)元素,然后把結(jié)果push到stack頂端。當(dāng)然將stack中的元素移到memory或者storage也是可以的,但是不能直接訪問stack中間的元素(必須從頭部開始訪問)
4.2 Solidity代碼實例
pragma solidity ^0.4.18;
contract Ballot{
address founder; // who found this contract
struct Proposal{
string name;
uint count; // count ballot
}
Proposal[] public proposals; // All proposals
mapping(address => uint8) voters;
function Ballot() public {
founder = msg.sender;
}
// Only founder could active voter
function activeVoter(address voterAddr) public{
if (founder != msg.sender){
return;
}
if(voters[voterAddr] == 0){
voters[voterAddr] = 1;
}
return;
}
// Only founder could active proposal
function activeProposal(string proposalName) public{
if (founder != msg.sender){
return;
}
for (uint index = 0; index < proposals.length; index++){
if(keccak256(proposals[index].name) == keccak256(proposalName)){
return;
}
}
proposals.push(Proposal({
name: proposalName,
count: 0 }));
return;
}
// Any activated voter could vode
function vote(string proposalName) public {
address voter = msg.sender;
if(voters[voter] != 1){
return;
}
for(uint index = 0; index < proposals.length; index ++){
if(keccak256(proposals[index].name) == keccak256(proposalName)){
proposals[index].count++;
voters[voter] = 2;
return;
}
}
return;
}
// Anyone could check who is getting the most votes
function getWinner() public constant returns(string winnerName, uint winnerCount) {
winnerCount = 0;
winnerName = "";
for(uint index = 0; index < proposals.length; index ++){
if(proposals[index].count > winnerCount){
winnerName = proposals[index].name;
winnerCount = proposals[index].count;
}
}
}
}