在线观看不卡亚洲电影_亚洲妓女99综合网_91青青青亚洲娱乐在线观看_日韩无码高清综合久久

鍍金池/ 教程/ Linux/ 合約
簡(jiǎn)介
編程規(guī)范
雜項(xiàng)
源文件的布局
單位和全局可用變量
合約的結(jié)構(gòu)
通用模式
常見(jiàn)問(wèn)題
深入理解 Solidity
安裝Solidity
智能合約介紹
合約
Solidity 編程實(shí)例
http://solidity.readthedocs.io/en/latest/security-considerations
類型
表達(dá)式和控制結(jié)構(gòu)

合約

Solidity里的合約是面向?qū)ο笳Z(yǔ)言里的類。它們持久存放在狀態(tài)變量和函數(shù)中,(在里面)可以修改這些變量。在不同的合約(實(shí)例)中調(diào)用一個(gè)函數(shù)(的過(guò)程),(實(shí)際上)是在EVM(Ether虛擬機(jī))中完成一次調(diào)用,并且完成(一次)上下文切換,(此時(shí))狀態(tài)變量是不可訪問(wèn)的。

創(chuàng)建合約

????? 合約可以從“外部”創(chuàng)建,也可以由Solidity合約創(chuàng)立。在創(chuàng)建合約時(shí),它的構(gòu)造函數(shù)(函具有與合約名稱同名的函數(shù))將被執(zhí)行。

?? web3.js,即? JavaScript API, 是這樣做的:

// The json abi array generated by the compiler

var?abiArray?=?[

? {

??? "inputs":[

????? {"name":"x","type":"uint256"},

????? {"name":"y","type":"uint256"}

??? ],

??? "type":"constructor"

? },

? {

??? "constant":true,

??? "inputs":[],

??? "name":"x",

??? "outputs":[{"name":"","type":"bytes32"}],

??? "type":"function"

? }

];

var?MyContract?=?web3.eth.contract(abiArray);// deploy new contractvar?contractInstance?=?MyContract.new(

? 10,

? {from:?myAccount, gas:?1000000}

);

// The json abi array generated by the compiler??由編譯器生成的json abi 數(shù)組

var?abiArray?=?[

? {

??? "inputs":[

????? {"name":"x","type":"uint256"},

????? {"name":"y","type":"uint256"}

??? ],

??? "type":"constructor"

? },

? {

??? "constant":true,

??? "inputs":[],

??? "name":"x",

??? "outputs":[{"name":"","type":"bytes32"}],

??? "type":"function"

? }

];

var?MyContract?=?web3.eth.contract(abiArray);

// deploy new contract??部署一個(gè)新合約

var?contractInstance?=?MyContract.new(

? 10,

? {from:?myAccount, gas:?1000000}

);

在內(nèi)部,在合約的代碼后要接著有構(gòu)造函數(shù)的參數(shù),但如果你使用web3.js,就不必關(guān)心這個(gè)。

如果是一個(gè)合約要?jiǎng)?chuàng)立另外一個(gè)合約,被創(chuàng)立的合約的源碼(二進(jìn)制代碼)要能被創(chuàng)立者知曉。這意味著:循環(huán)創(chuàng)建依賴就成為不可能的事情。

contract?OwnedToken {

????// TokenCreator is a contract type that is defined below.

????// It is fine to reference it as long as it is not used

????// to create a new contract.

??? TokenCreator creator;

????address?owner;

????bytes32?name;

????// This is the constructor which registers the

????// creator and the assigned name.

????function?OwnedToken(bytes32?_name) {

??????? owner?=?msg.sender;

????????// We do an explicit type conversion from `address`

????????// to `TokenCreator` and assume that the type of

????????// the calling contract is TokenCreator, there is

????????// no real way to check that.

??????? creator?=?TokenCreator(msg.sender);

??????? name?=?_name;

??? }

????function?changeName(bytes32?newName) {

????????// Only the creator can alter the name --

????????// the comparison is possible since contracts

????????// are implicitly convertible to addresses.

????????if?(msg.sender?==?creator) name?=?newName;

??? }

????function?transfer(address?newOwner) {

????????// Only the current owner can transfer the token.

????????if?(msg.sender?!=?owner)?return;

????????// We also want to ask the creator if the transfer

????????// is fine. Note that this calls a function of the

????????// contract defined below. If the call fails (e.g.

????????// due to out-of-gas), the execution here stops

????????// immediately.

????????if?(creator.isTokenTransferOK(owner, newOwner))

??????????? owner?=?newOwner;

??? }}

contract?TokenCreator {

????function?createToken(bytes32?name)

???????returns?(OwnedToken tokenAddress)

??? {

????????// Create a new Token contract and return its address.

????????// From the JavaScript side, the return type is simply

????????// "address", as this is the closest type available in

????????// the ABI.

????????return?new?OwnedToken(name);

??? }

????function?changeName(OwnedToken tokenAddress,?bytes32?name) {

????????// Again, the external type of "tokenAddress" is

????????// simply "address".

??????? tokenAddress.changeName(name);

??? }

????function?isTokenTransferOK(

????????address?currentOwner,

????????address?newOwner

??? )?returns?(bool?ok) {

????????// Check some arbitrary condition.

????????address?tokenAddress?=?msg.sender;

????????return?(sha3(newOwner)?&?0xff)?==?(bytes20(tokenAddress)?&?0xff);

??? }}

contract?OwnedToken {

????// TokenCreator is a contract type that is defined below.??TokenCreator是在下面定義的合約類型

????// It is fine to reference it as long as it is not used??若它本身不用于創(chuàng)建新的合約的話,它就是一個(gè)引用

????// to create a new contract.

??? TokenCreator creator;

????address?owner;

????bytes32?name;

????// This is the constructor which registers the?這個(gè)是一個(gè)登記創(chuàng)立者和分配名稱的結(jié)構(gòu)函數(shù)

????// creator and the assigned name.

????function?OwnedToken(bytes32?_name) {

??????? owner?=?msg.sender;

????????// We do an explicit type conversion from `address`?我們做一次由`address`到`TokenCreator` 的顯示類型轉(zhuǎn)換,,確保調(diào)用合約的類型是 TokenCreator, (因?yàn)闆](méi)有真正的方法來(lái)檢測(cè)這一點(diǎn))

????????// to `TokenCreator` and assume that the type of

????????// the calling contract is TokenCreator, there is

????????// no real way to check that.

??????? creator?=?TokenCreator(msg.sender);

??????? name?=?_name;

??? }

????function?changeName(bytes32?newName) {

????????// Only the creator can alter the name --??僅僅是創(chuàng)立者可以改變名稱--

????????// the comparison is possible since contracts??因?yàn)楹霞s是隱式轉(zhuǎn)換到地址上,這種比較是可能的

????????// are implicitly convertible to addresses.

????????if?(msg.sender?==?creator) name?=?newName;

??? }

????function?transfer(address?newOwner) {

????????// Only the current owner can transfer the token.??僅僅是 僅僅是當(dāng)前(合約)所有者可以轉(zhuǎn)移 token

????????if?(msg.sender?!=?owner)?return;

????????// We also want to ask the creator if the transfer??我們可以詢問(wèn)(合約)創(chuàng)立者"轉(zhuǎn)移是否成功"

????????// is fine. Note that this calls a function of the??注意下面定義的合約的函數(shù)調(diào)用

????????// contract defined below. If the call fails (e.g.?????如果函數(shù)調(diào)用失敗,(如gas用完了等原因)

????????// due to out-of-gas), the execution here stops??程序的執(zhí)行將立刻停止

????????// immediately.

????????if?(creator.isTokenTransferOK(owner, newOwner))

??????????? owner?=?newOwner;

??? }}

contract?TokenCreator {

????function?createToken(bytes32?name)

???????returns?(OwnedToken tokenAddress)

??? {

????????// Create a new Token contract and return its address.??創(chuàng)立一個(gè)新的Token合約,并且返回它的地址

????????// From the JavaScript side, the return type is simply??從 JavaScript觀點(diǎn)看,返回的地址類型是"address"

????????// "address", as this is the closest type available in???這個(gè)是和ABI最接近的類型

????????// the ABI.

????????return?new?OwnedToken(name);

??? }

????function?changeName(OwnedToken tokenAddress,?bytes32?name) {

????????// Again, the external type of "tokenAddress" is?????"tokenAddress" 的外部類型也是 簡(jiǎn)單的"address".

????????// simply "address".

??????? tokenAddress.changeName(name);

??? }

????function?isTokenTransferOK(

????????address?currentOwner,

????????address?newOwner

??? )?returns?(bool?ok) {

????????// Check some arbitrary condition.?檢查各種條件

????????address?tokenAddress?=?msg.sender;

????????return?(sha3(newOwner)?&?0xff)?==?(bytes20(tokenAddress)?&?0xff);

??? }

}

可見(jiàn)性和訪問(wèn)限制符

因?yàn)镾olidity可以理解兩種函數(shù)調(diào)用(“內(nèi)部調(diào)用”,不創(chuàng)建一個(gè)真實(shí)的EVM調(diào)用(也稱為“消息調(diào)用”);“外部的調(diào)用”-要?jiǎng)?chuàng)建一個(gè)真實(shí)的EMV調(diào)用),? 有四種的函數(shù)和狀態(tài)變量的可見(jiàn)性。

函數(shù)可以被定義為external, public, internal?or?private,缺省是 public。對(duì)狀態(tài)變量而言,?external是不可能的,默認(rèn)是?internal。

external:?外部函數(shù)是合約接口的一部分,這意味著它們可以從其他合約調(diào)用, 也可以通過(guò)事務(wù)調(diào)用。外部函數(shù)f不能被內(nèi)部調(diào)用(即 f()不執(zhí)行,但this.f()執(zhí)行)。外部函數(shù),當(dāng)他們接收大數(shù)組時(shí),更有效。

public:公共函數(shù)是合約接口的一部分,可以通過(guò)內(nèi)部調(diào)用或通過(guò)消息調(diào)用。對(duì)公共狀態(tài)變量而言,會(huì)有的自動(dòng)訪問(wèn)限制符的函數(shù)生成(見(jiàn)下文)。

internal:這些函數(shù)和狀態(tài)變量只能內(nèi)部訪問(wèn)(即在當(dāng)前合約或由它派生的合約),而不使用(關(guān)鍵字)this 。

private:私有函數(shù)和狀態(tài)變量?jī)H僅在定義該合約中可見(jiàn), 在派生的合約中不可見(jiàn)。

請(qǐng)注意

在外部觀察者中,合約的內(nèi)部的各項(xiàng)均可見(jiàn)。用 private?僅僅防止其他合約來(lái)訪問(wèn)和修改(該合約中)信息, 但它對(duì)blockchain之外的整個(gè)世界仍然可見(jiàn)。

可見(jiàn)性說(shuō)明符是放在在狀態(tài)變量的類型之后,(也可以放在)參數(shù)列表和函數(shù)返回的參數(shù)列表之間。

contract?c {

????function?f(uint?a)?private?returns?(uint?b) {?return?a?+?1; }

????function?setData(uint?a)?internal?{ data?=?a; }

????uint?public?data;

}

其他合約可以調(diào)用c.data()來(lái)檢索狀態(tài)存儲(chǔ)中data的值,但不能訪問(wèn)(函數(shù))f。由c派生的合約可以訪問(wèn)(合約中)setData(函數(shù)),以便改變data的值(僅僅在它們自己的范圍里)。

訪問(wèn)限制符函數(shù)

編譯器會(huì)自動(dòng)創(chuàng)建所有公共狀態(tài)變量的訪問(wèn)限制符功能。下文中的合約中有一個(gè)稱作data的函數(shù),它不帶任何參數(shù)的,它返回一個(gè)uint類型,? 狀態(tài)變量的值是data??梢栽诼暶骼镞M(jìn)行狀態(tài)變量的初始化。

訪問(wèn)限制符函數(shù)有外部可見(jiàn)性。如果標(biāo)識(shí)符是內(nèi)部可訪問(wèn)(即沒(méi)有this),則它是一個(gè)狀態(tài)變量,如果外部可訪問(wèn)的(即 有this),則它是一個(gè)函數(shù)。

contract?test {

????uint?public?data?=?42;}

下面的例子復(fù)雜些:

contract?complex {

????struct?Data {?uint?a;?bytes3?b;?mapping(uint?=>?uint) map; }

????mapping(uint?=>?mapping(bool?=>?Data[]))?public?data;}

它生成了如下形式的函數(shù):

function?data(uint?arg1,?bool?arg2,?uint?arg3)?returns?(uint?a,?bytes3?b){

??? a?=?data[arg1][arg2][arg3].a;

??? b?=?data[arg1][arg2][arg3].b;}

注意 結(jié)構(gòu)體的映射省略了,因?yàn)闆](méi)有好的方法來(lái)提供映射的鍵值。

函數(shù)修飾符

修飾符可以用來(lái)輕松改變函數(shù)的行為, 例如,在執(zhí)行的函數(shù)之前自動(dòng)檢查條件。他們是可繼承合約的屬性,也可被派生的合約重寫。

contract?owned {

????function?owned() { owner?=?msg.sender; }

????address?owner;

????// This contract only defines a modifier but does not use

????// it - it will be used in derived contracts.

????// The function body is inserted where the special symbol

????// "_" in the definition of a modifier appears.

????// This means that if the owner calls this function, the

????// function is executed and otherwise, an exception is

????// thrown.

????modifier?onlyowner {?if?(msg.sender?!=?owner)?throw; _ }}contract?mortal is owned {

????// This contract inherits the "onlyowner"-modifier from

????// "owned" and applies it to the "close"-function, which

????// causes that calls to "close" only have an effect if

????// they are made by the stored owner.

????function?close() onlyowner {

??????? selfdestruct(owner);

??? }}contract?priced {

????// Modifiers can receive arguments:

????modifier?costs(uint?price) {?if?(msg.value?>=?price) _ }}contract?Register is priced, owned {

????mapping?(address?=>?bool) registeredAddresses;

????uint?price;

????function?Register(uint?initialPrice) { price?=?initialPrice; }

????function?register() costs(price) {

??????? registeredAddresses[msg.sender]?=?true;

??? }

????function?changePrice(uint?_price) onlyowner {

??????? price?=?_price;

??? }}

contract?owned {

????function?owned() { owner?=?msg.sender; }

????address?owner;

????// This contract only defines a modifier but does not use?這個(gè)合約僅僅定義了修飾符,但沒(méi)有使用它

????// it - it will be used in derived contracts.?在派生的合約里使用

????// The function body is inserted where the special symbol? ,函數(shù)體插入到特殊的標(biāo)識(shí) "_"定義的地方?

????// "_" in the definition of a modifier appears.

????// This means that if the owner calls this function, the?這意味著若它自己調(diào)用此函數(shù),則函數(shù)將被執(zhí)行

????// function is executed and otherwise, an exception is?否則,一個(gè)異常將拋出

????// thrown.

????modifier?onlyowner {?if?(msg.sender?!=?owner)?throw; _ }

}

contract?mortal is owned {

????// This contract inherits the "onlyowner"-modifier from???該合約是從"owned" 繼承的"onlyowner"修飾符,

????// "owned" and applies it to the "close"-function, which????并且應(yīng)用到"close"函數(shù), 如果他們存儲(chǔ)owner

????// causes that calls to "close" only have an effect if??

????// they are made by the stored owner.

????function?close() onlyowner {

??????? selfdestruct(owner);

??? }

}

contract?priced {

????// Modifiers can receive arguments:??修飾符可以接收參數(shù)

????modifier?costs(uint?price) {?if?(msg.value?>=?price) _ }

}

contract?Register is priced, owned {

????mapping?(address?=>?bool) registeredAddresses;

????uint?price;

????function?Register(uint?initialPrice) { price?=?initialPrice; }

????function?register() costs(price) {

??????? registeredAddresses[msg.sender]?=?true;

??? }

????function?changePrice(uint?_price) onlyowner {

??????? price?=?_price;

??? }

}

多個(gè)修飾符可以被應(yīng)用到一個(gè)函數(shù)中(用空格隔開(kāi)),并順序地進(jìn)行計(jì)算。當(dāng)離開(kāi)整個(gè)函數(shù)時(shí),顯式返回一個(gè)修飾詞或函數(shù)體,? 同時(shí)在“_”之后緊接著的修飾符,直到函數(shù)尾部的控制流,或者是修飾體將繼續(xù)執(zhí)行。任意表達(dá)式允許修改參數(shù),在修飾符中,所有函數(shù)的標(biāo)識(shí)符是可見(jiàn)的。在此函數(shù)由修飾符引入的標(biāo)識(shí)符是不可見(jiàn)的(雖然他們可以通過(guò)重寫,改變他們的值)。

常量

狀態(tài)變量可以聲明為常量(在數(shù)組和結(jié)構(gòu)體類型上仍然不可以這樣做,映射類型也不可以)。

contract?C {

????uint?constant?x?=?32*\*22?+?8;

????string?constant?text?=?"abc";

}

編譯器不保留這些變量存儲(chǔ)塊,? 每到執(zhí)行到這個(gè)語(yǔ)句時(shí),常量值又被替換一次。

表達(dá)式的值只能包含整數(shù)算術(shù)運(yùn)算。

回退函數(shù)

一個(gè)合約可以有一個(gè)匿名函數(shù)。若沒(méi)有其他函數(shù)和給定的函數(shù)標(biāo)識(shí)符一致的話,該函數(shù)將沒(méi)有參數(shù),將執(zhí)行一個(gè)合約的調(diào)用(如果沒(méi)有提供數(shù)據(jù))。

此外,當(dāng)合約接收一個(gè)普通的Ether時(shí),函數(shù)將被執(zhí)行(沒(méi)有數(shù)據(jù))。在這樣一個(gè)情況下,幾乎沒(méi)有g(shù)as用于函數(shù)調(diào)用,所以調(diào)用回退函數(shù)是非常廉價(jià)的,這點(diǎn)非常重要。

contract?Test {

????function() { x?=?1; }

????uint?x;}

//? This contract rejects any Ether sent to it. It is good

// practise to? include such a function for every contract

// in order not to loose? Ether.

contract?Rejector {

????function() {?throw; }

}

contract?Caller {

??function?callTest(address?testAddress) {

????? Test(testAddress).call(0xabcdef01);?// hash does not exist

??????// results in Test(testAddress).x becoming == 1.

????? Rejector r?=?Rejector(0x123);

????? r.send(2 ether);

??????// results in r.balance == 0

? }

}

contract?Test {

????function() { x?=?1; }

????uint?x;}

//? This contract rejects any Ether sent to it. It is good???這個(gè)合約拒絕任何發(fā)給它的Ether.

// practise to? include such a function for every contract??為了嚴(yán)管Ether,在每個(gè)合約里包含一個(gè)這樣的函數(shù),是非常好的做法

// in order not to loose? Ether.

contract?Rejector {

????function() {?throw; }

}

contract?Caller {

??function?callTest(address?testAddress) {

????? Test(testAddress).call(0xabcdef01);?// hash does not exist?hash值不存在

??????// results in Test(testAddress).x becoming == 1.??Test(testAddress).x的結(jié)果? becoming == 1

????? Rejector r?=?Rejector(0x123);

????? r.send(2 ether);

??????// results in r.balance == 0?????結(jié)果里r.balance == 0??

? }

}

事件

事件允許EMV寫日志功能的方便使用, 進(jìn)而在dapp的用戶接口中用JavaScript順序調(diào)用,從而監(jiān)聽(tīng)這些事件。

事件是合約中可繼承的成員。當(dāng)他們調(diào)用時(shí),會(huì)在導(dǎo)致一些參數(shù)在事務(wù)日志上的存儲(chǔ)--在blockchain上的一種特殊的數(shù)據(jù)結(jié)構(gòu)。這些日志和合約的地址相關(guān)聯(lián),? 將被納入blockchain中,存儲(chǔ)在block里以便訪問(wèn)(?在Frontier?和?Homestead里是永久存儲(chǔ),但在Serenity里有些變化)。在合約內(nèi)部,日志和事件數(shù)據(jù)是不可訪問(wèn)的(從創(chuàng)建該日志的合約里)。

SPV日志證明是可行的, 如果一個(gè)外部實(shí)體提供一個(gè)這樣的證明給合約,? 它可以檢查blockchain內(nèi)實(shí)際存在的日志(但要注意這樣一個(gè)事實(shí),最終要提供block的headers, 因?yàn)楹霞s只能看到最近的256塊hash值)。

最多有三個(gè)參數(shù)可以接收屬性索引,它將對(duì)各自的參數(shù)進(jìn)行檢索:? 可以對(duì)用戶界面中的索引參數(shù)的特定值進(jìn)行過(guò)濾。

如果數(shù)組(包括string和?bytes)被用作索引參數(shù),? 就會(huì)以sha3-hash形式存儲(chǔ),而不是topic。

除了用anonymous聲明事件之外,事件的指紋的hash值都將是topic之一。這意味著,不可能通過(guò)名字來(lái)過(guò)濾特定的匿名事件。

所有非索引參數(shù)將被作為數(shù)據(jù)日志記錄的一部分進(jìn)行存儲(chǔ)。

contract?ClientReceipt {

????event?Deposit(

????????address?indexed _from,

????????bytes32?indexed _id,

????????uint?_value

??? );

????function?deposit(bytes32?_id) {

????????// Any call to this function (even deeply nested) can

????????// be detected from the JavaScript API by filtering

????????// for `Deposit` to be called.

??????? Deposit(msg.sender, _id, msg.value);

??? }

}

contract?ClientReceipt {

????event?Deposit(

????????address?indexed _from,

????????bytes32?indexed _id,

????????uint?_value

??? );

????function?deposit(bytes32?_id) {

????????// Any call to this function (even deeply nested) can?任何對(duì)這個(gè)函數(shù)的調(diào)用都能通過(guò)JavaScipt API , 用`Deposit` 過(guò)濾來(lái)檢索到(即使深入嵌套)

????????// be detected from the JavaScript API by filtering

????????// for `Deposit` to be called.

??????? Deposit(msg.sender, _id, msg.value);

??? }

}

JavaScript API 的使用如下:

var?abi?=?/\ abi as generated by the compiler /;

var?ClientReceipt?=?web3.eth.contract(abi);

var?clientReceipt?=?ClientReceipt.at(0x123?/\ address /);

var?event?=?clientReceipt.Deposit();

// watch for changes

event.watch(function(error, result){

????// result will contain various information

????// including the argumets given to the Deposit

????// call.

????if?(!error)

??????? console.log(result);});

// Or pass a callback to start watching immediately

var?event?=?clientReceipt.Deposit(function(error, result) {

????if?(!error)

??????? console.log(result);

});

var?abi?=?/\ abi as generated by the compiler /;?????/\ 由編譯器生成的abi /;??

var?ClientReceipt?=?web3.eth.contract(abi);

var?clientReceipt?=?ClientReceipt.at(0x123?/\ address /);???/\ 地址 /);?

var?event?=?clientReceipt.Deposit();

// watch for changes???觀察變化

event.watch(function(error, result){

????// result will contain various information???結(jié)果包含不同的信息: 包括給Deposit調(diào)用的參數(shù)

????// including the argumets given to the Deposit

????// call.

????if?(!error)

??????? console.log(result);});

// Or pass a callback to start watching immediately?或者通過(guò)callback立刻開(kāi)始觀察

var?event?=?clientReceipt.Deposit(function(error, result) {

????if?(!error)

??????? console.log(result);

});

底層日志的接口

還可以通過(guò)函數(shù)log0 log1,log2,log3 log4到 logi,共i+1個(gè)bytes32類型的參數(shù)來(lái)訪問(wèn)底層日志機(jī)制的接口。第一個(gè)參數(shù)將用于數(shù)據(jù)日志的一部分,其它的參數(shù)將用于topic。上面的事件調(diào)用可以以相同的方式執(zhí)行。.

log3(

??? msg.value,

??? 0x50cb9fe53daa9737b786ab3646f04d0150dc50ef4e75f59509d83667ad5adb20,

??? msg.sender,

??? _id

);

很長(zhǎng)的十六進(jìn)制數(shù)等于

sha3(“Deposit(address,hash256,uint256)”), 這個(gè)就是事件的指紋。

理解事件的額外的資源

繼承

通過(guò)包括多態(tài)性的復(fù)制代碼,Solidity支持多重繼承。

除非合約是顯式給出的,所有的函數(shù)調(diào)用都是虛擬的,絕大多數(shù)派生函數(shù)可被調(diào)用。

即使合約是繼承了多個(gè)其他合約, 在blockchain上只有一個(gè)合約被創(chuàng)建,? 基本合約代碼總是被復(fù)制到最終的合約上。

通用的繼承機(jī)制非常類似于Python里的繼承,特別是關(guān)于多重繼承方面。

下面給出了詳細(xì)的例子。

contract owned {

??? function owned() { owner = msg.sender; }

??? address owner;}

// Use "is" to derive from another contract. Derived// contracts can access all non-private members including// internal functions and state variables. These cannot be// accessed externally via `this`, though.contract mortal is owned {

??? function kill() {

??????? if (msg.sender == owner) selfdestruct(owner);

??? }}

// These abstract contracts are only provided to make the// interface known to the compiler. Note the function// without body. If a contract does not implement all// functions it can only be used as an interface.contract Config {

??? function lookup(uint id) returns (address adr);}contract NameReg {

??? function register(bytes32 name);

??? function unregister();

?}

// Multiple inheritance is possible. Note that "owned" is// also a base class of "mortal", yet there is only a single// instance of "owned" (as for virtual inheritance in C++).contract named is owned, mortal {

??? function named(bytes32 name) {

??????? Config config = Config(0xd5f9d8d94886e70b06e474c3fb14fd43e2f23970);

??????? NameReg(config.lookup(1)).register(name);

??? }

??? // Functions can be overridden, both local and

??? // message-based function calls take these overrides

??? // into account.

??? function kill() {

??????? if (msg.sender == owner) {

??????????? Config config = Config(0xd5f9d8d94886e70b06e474c3fb14fd43e2f23970);

??????????? NameReg(config.lookup(1)).unregister();

??????????? // It is still possible to call a specific

??????????? // overridden function.

??????????? mortal.kill();

??????? }

??? }}

// If a constructor takes an argument, it needs to be// provided in the header (or modifier-invocation-style at// the constructor of the derived contract (see below)).contract PriceFeed is owned, mortal, named("GoldFeed") {

?? function updateInfo(uint newInfo) {

????? if (msg.sender == owner) info = newInfo;

?? }

?? function get() constant returns(uint r) { return info; }

?? uint info;

}

contract owned {

??? function owned() { owner = msg.sender; }

??? address owner;}

// Use "is" to derive from another contract. Derived???用"is"是從其他的合約里派生出

// contracts can access all non-private members including???派生出的合約能夠訪問(wèn)所有非私有的成員,包括內(nèi)部函數(shù)和狀態(tài)變量。? 它們不能從外部用'this'來(lái)訪問(wèn)。

// internal functions and state variables. These cannot be

// accessed externally via `this`, though.

contract mortal is owned {

??? function kill() {

??????? if (msg.sender == owner) selfdestruct(owner);

??? }}

// These abstract contracts are only provided to make the??這些抽象的合約僅僅是讓編譯器知道已經(jīng)生成了接口,

// interface known to the compiler. Note the function???注意:函數(shù)沒(méi)有函數(shù)體。如果合約不做實(shí)現(xiàn)的話,它就只能當(dāng)作接口。

// without body. If a contract does not implement all

// functions it can only be used as an interface.

contract Config {

??? function lookup(uint id) returns (address adr);

}

contract NameReg {

??? function register(bytes32 name);

??? function unregister();

?}

// Multiple inheritance is possible. Note that "owned" is?多重繼承也是可以的,注意"owned" 也是mortal的基類, 雖然 僅僅有"owned"的單個(gè)實(shí)例,(和C++里的virtual繼承一樣)

// also a base class of "mortal", yet there is only a single

// instance of "owned" (as for virtual inheritance in C++).

contract named is owned, mortal {

??? function named(bytes32 name) {

??????? Config config = Config(0xd5f9d8d94886e70b06e474c3fb14fd43e2f23970);

??????? NameReg(config.lookup(1)).register(name);

??? }

??? // Functions can be overridden, both local and??函數(shù)被重寫,本地和基于消息的函數(shù)調(diào)用把這些override帶入賬戶里。

??? // message-based function calls take these overrides

??? // into account.

??? function kill() {

??????? if (msg.sender == owner) {

??????????? Config config = Config(0xd5f9d8d94886e70b06e474c3fb14fd43e2f23970);

??????????? NameReg(config.lookup(1)).unregister();

??????????? // It is still possible to call a specific??還可以調(diào)用特定的override函數(shù)

??????????? // overridden function.

??????????? mortal.kill();

??????? }

??? }}

// If a constructor takes an argument, it needs to be??如果構(gòu)造器里帶有一個(gè)參數(shù),有必要在頭部給出,(或者在派生合約的構(gòu)造器里使用修飾符調(diào)用方式modifier-invocation-style(見(jiàn)下文))

// provided in the header (or modifier-invocation-style at

// the constructor of the derived contract (see below)).

contract PriceFeed is owned, mortal, named("GoldFeed") {

?? function updateInfo(uint newInfo) {

????? if (msg.sender == owner) info = newInfo;

?? }

?? function get() constant returns(uint r) { return info; }

?? uint info;

}

注意:在上文中,我們使用mortal.kill() 來(lái)“forward” 析構(gòu)請(qǐng)求。這種做法是有問(wèn)題的,請(qǐng)看下面的例子:

contract?mortal is owned {

????function?kill() {

????????if?(msg.sender?==?owner) selfdestruct(owner);

??? }

}

contract?Base1 is mortal {

????function?kill() {?/\ do cleanup 1??清除1?/?mortal.kill();?

}

}

contract?Base2 is mortal {

????function?kill() {?/\ do cleanup 2? 清除2 /?mortal.kill();?

}

}

contract?Final is Base1, Base2 {

}

?Final.kill() 將調(diào)用Base2.kill作為最后的派生重寫,但這個(gè)函數(shù)繞開(kāi)了Base1.kill。因?yàn)樗恢烙蠦ase1。這種情況下要使用 super

contract?mortal is owned {

????function?kill() {

????????if?(msg.sender?==?owner) selfdestruct(owner);

??? }

}

contract?Base1 is mortal {

????function?kill() {?/\ do cleanup 1???清除1??\/????super.kill(); }

}

contract?Base2 is mortal {

????function?kill() {?/\ do cleanup 2??清除2? \/????super.kill(); }

}

contract?Final is Base2, Base1 {

}

若Base1 調(diào)用了super函數(shù),它不是簡(jiǎn)單地調(diào)用基本合約之一的函數(shù), 它是調(diào)用最后繼承關(guān)系的下一個(gè)基本合約的函數(shù)。所以它會(huì)調(diào)用 base2.kill()(注意,最后的繼承順序是–從最后的派生合約開(kāi)始:Final, Base1, Base2, mortal, owned)。當(dāng)使用類的上下文中super不知道的情況下,真正的函數(shù)將被調(diào)用,雖然它的類型已經(jīng)知道。這個(gè)和普通的virtual方法的查找相似。

基本構(gòu)造函數(shù)的參數(shù)

派生的合約需要為基本構(gòu)造函數(shù)提供所有的參數(shù)。這可以在兩處進(jìn)行:

contract?Base {

????uint?x;

????function?Base(uint?_x) { x?=?_x;}

}

contract?Derived is Base(7) {

????function?Derived(uint?_y) Base(_y?*?_y) {

??? }

}

第一種方式是直接在繼承列表里實(shí)現(xiàn)(是?Base(7)),第二種方式是在派生的構(gòu)造器的頭部,修飾符被調(diào)用時(shí)實(shí)現(xiàn)(Base(_y * _y))。如果構(gòu)造函數(shù)參數(shù)是一個(gè)常量,并且定義了合約的行為或描述了它的行為,第一種方式比較方便。 如果基本構(gòu)造函數(shù)參數(shù)依賴于派生合約的構(gòu)造函數(shù),則必須使用第二種方法。如果在這個(gè)荒謬的例子中,這兩個(gè)地方都被使用,修飾符樣式的參數(shù)優(yōu)先。

多繼承和線性化

允許多重繼承的編程語(yǔ)言,要處理這樣幾個(gè)問(wèn)題,其中一個(gè)是Diamond問(wèn)題。Solidity是沿用Python的方式, 使用“C3線性化”,在基類的DAG強(qiáng)制使用特定的順序。這導(dǎo)致單調(diào)但不允許有一些的繼承關(guān)系。特別是,在其中的基礎(chǔ)類的順序是直接的,這點(diǎn)非常重要。在下面的代碼中,Solidity會(huì)報(bào)錯(cuò):“繼承關(guān)系的線性化是不可能的”。

contract?X {}

contract?A is X {}

contract?C is A, X {}

這個(gè)原因是,C要求X來(lái)重寫A(定義A,X這個(gè)順序),但A本身的要求重寫X,這是一個(gè)矛盾,不能解決。

一個(gè)簡(jiǎn)單的規(guī)則是要指定基類中的順序,從“最基本”到“最近派生”。

抽象契約

合約函數(shù)可以缺少實(shí)現(xiàn)(請(qǐng)注意,函數(shù)聲明頭將被終止),見(jiàn)下面的例子:

contract?feline {

????function?utterance()?returns?(bytes32);

}

這樣的合約不能被編譯(即使它們包含實(shí)現(xiàn)的函數(shù)和非實(shí)現(xiàn)的函數(shù)),但它們可以用作基本合約:

contract?Cat is feline {

????function?utterance()?returns?(bytes32) {?return?"miaow"; }

}

如果一個(gè)合約是從抽象合約中繼承的,而不實(shí)現(xiàn)所有非執(zhí)行功能,則它本身就是抽象的。

庫(kù)

庫(kù)和合約類似,但是它們的目的主要是在給定地址上部署,以及用EVM的CALLCODE特性來(lái)重用代碼。這些代碼是在調(diào)用合約的上下文里執(zhí)行的,例如調(diào)用合約的指針和調(diào)用合約的存儲(chǔ)能夠被訪問(wèn)。由于庫(kù)是一片獨(dú)立的代碼,如果它們顯示地提供的話,就僅僅能訪問(wèn)到調(diào)用合約的狀態(tài)變量(有方法命名它們)

下面的例子解釋了怎樣使用庫(kù)(確保用using for 來(lái)實(shí)現(xiàn))

library?Set {

??// We define a new struct datatype that will be used to???我們定義了一個(gè)新的結(jié)構(gòu)體數(shù)據(jù)類型,用于存放調(diào)用合約中的數(shù)據(jù)

??// hold its data in the calling contract.

??struct?Data {?mapping(uint?=>?bool) flags; }

??// Note that the first parameter is of type "storage?注意第一個(gè)參數(shù)是 “存儲(chǔ)引用”類型,這樣僅僅是它的地址,而不是它的內(nèi)容在調(diào)用中被傳入 這是庫(kù)函數(shù)的特點(diǎn),?

??// reference" and thus only its storage address and not

??// its contents is passed as part of the call.? This is a

??// special feature of library functions.? It is idiomatic??若第一個(gè)參數(shù)用"self"調(diào)用時(shí)很笨的的,如果這個(gè)函數(shù)可以被對(duì)象的方法可見(jiàn)。

??// to call the first parameter 'self', if the function can

??// be seen as a method of that object.

??function?insert(Data?storage?self,?uint?value)

??????returns?(bool)

? {

??????if?(self.flags[value])

??????????return?false;?// already there?已經(jīng)在那里

????? self.flags[value]?=?true;

??????return?true;

? }

??function?remove(Data?storage?self,?uint?value)

??????returns?(bool)

? {

??????if?(!self.flags[value])

??????????return?false;?// not there?不在那里

????? self.flags[value]?=?false;

??????return?true;

? }

??function?contains(Data?storage?self,?uint?value)

??????returns?(bool)

? {

??????return?self.flags[value];

? }

}

contract?C {

??? Set.Data knownValues;

????function?register(uint?value) {

????????// The library functions can be called without a??這個(gè)庫(kù)函數(shù)沒(méi)有特定的函數(shù)實(shí)例被調(diào)用,因?yàn)椤癷nstance”是當(dāng)前的合約

????????// specific instance of the library, since the

????????// "instance" will be the current contract.

????????if?(!Set.insert(knownValues, value))

????????????throw;

??? }

????// In this contract, we can also directly access knownValues.flags, if we want?在這個(gè)合約里,如果我們要的話,也可以直接訪問(wèn)?knownValues.flags

.*}

當(dāng)然,你不必這樣使用庫(kù)--他們也可以事前不定義結(jié)構(gòu)體數(shù)據(jù)類型,就可以使用。 沒(méi)有任何存儲(chǔ)引入?yún)?shù),函數(shù)也可以執(zhí)行。也可以在任何位置,有多個(gè)存儲(chǔ)引用參數(shù)。

Set.contains, Set.insert and Set.remove都可編譯到(CALLCODE)外部合約/庫(kù)。如果你使用庫(kù),注意真正進(jìn)行的外部函數(shù)調(diào)用,所以`msg.sender不再指向來(lái)源的sender了,而是指向了正在調(diào)用的合約。msg.value包含了調(diào)用庫(kù)函數(shù)中發(fā)送的資金。

因?yàn)榫幾g器不知道庫(kù)將部署在哪里。這些地址不得不由linker填進(jìn)最后的字節(jié)碼(見(jiàn)使用命令行編譯器如何使用命令行編譯器鏈接)。如果不給編譯器一個(gè)地址做參數(shù),編譯的十六進(jìn)制碼就會(huì)包含Set 這樣的占位符(Set是庫(kù)的名字)。通過(guò)替換所有的40個(gè)字符的十六進(jìn)制編碼的庫(kù)合約的地址,地址可以手動(dòng)進(jìn)行填充。

比較合約和庫(kù)的限制:

  • 無(wú)狀態(tài)變量

  • 不能繼承或被繼承

(這些可能在以后會(huì)被解除)

庫(kù)的常見(jiàn)“坑”

msg.sender的值

msg.sender的值將是調(diào)用庫(kù)函數(shù)的合約的值。

例如,如果A調(diào)用合約B,B內(nèi)部調(diào)用庫(kù)C。在庫(kù)C庫(kù)的函數(shù)調(diào)用里,msg.sender將是合約B的地址。

表達(dá)式LibraryName.functionName() 用CALLCODE完成外部函數(shù)調(diào)用, 它映射到一個(gè)真正的EVM調(diào)用,就像otherContract.functionName() 或者 this.functionName()。這種調(diào)用可以一級(jí)一級(jí)擴(kuò)展調(diào)用深度(最多1024級(jí)),把msg.sender存儲(chǔ)為當(dāng)前的調(diào)用者,然后執(zhí)行庫(kù)合約的代碼,而不是執(zhí)行當(dāng)前的合約存儲(chǔ)。這種執(zhí)行方式是發(fā)生在一個(gè)完全嶄新的內(nèi)存環(huán)境中,它的內(nèi)存類型將被復(fù)制,并且不能繞過(guò)引用。

轉(zhuǎn)移Ether

原則上使用LibraryName.functionName.value(x)()來(lái)轉(zhuǎn)移Ether。但若使用CALLCODE,Ether會(huì)在當(dāng)前合約里用完。

Using For

指令 using A for B;??可用于附加庫(kù)函數(shù)(從庫(kù)A)到任何類型(B)。這些函數(shù)將收到一個(gè)作為第一個(gè)參數(shù)的對(duì)象(像Python中self變量)。

using A for *;,是指函數(shù)從庫(kù)A附加到任何類型。

在這兩種情況下,所有的函數(shù)將被附加,(即使那些第一個(gè)參數(shù)的類型與對(duì)象的類型不匹配)。該被調(diào)用函數(shù)的入口類型將被檢查,并進(jìn)行函數(shù)重載解析。

using A for B;?指令在當(dāng)前的范圍里是有效的,作用范圍限定在現(xiàn)在的合約里。但(出了當(dāng)前范圍)在全局范圍里就被移除。因此,通過(guò)?including一個(gè)模塊,其數(shù)據(jù)類型(包括庫(kù)函數(shù))都將是可用的,而不必添加額外的代碼。

讓我們用這種方式重寫庫(kù)中的set示例:

// This is the same code as before, just without comments

library Set {

??struct?Data {?mapping(uint?=>?bool) flags; }

??function?insert(Data?storage?self,?uint?value)

??????returns?(bool)

? {

??????if?(self.flags[value])

????????return?false;?// already there

????? self.flags[value]?=?true;

??????return?true;

? }

??function?remove(Data?storage?self,?uint?value)

??????returns?(bool)

? {

??????if?(!self.flags[value])

??????????return?false;?// not there

????? self.flags[value]?=?false;

??????return?true;

? }

??function?contains(Data?storage?self,?uint?value)

??????returns?(bool)

? {

??????return?self.flags[value];

? }

}

contract?C {

??? using Set?for?Set.Data;?// this is the crucial change

??? Set.Data knownValues;

????function?register(uint?value) {

????????// Here, all variables of type Set.Data have

????????// corresponding member functions.

????????// The following function call is identical to

????????// Set.insert(knownValues, value)

????????if?(!knownValues.insert(value))

????????????throw;

??? }

}

// This is the same code as before, just without comments???這個(gè)代碼和之前的一樣,僅僅是沒(méi)有注釋

library Set {

??struct?Data {?mapping(uint?=>?bool) flags; }

??function?insert(Data?storage?self,?uint?value)

??????returns?(bool)

? {

??????if?(self.flags[value])

????????return?false;?// already there?已經(jīng)在那里

????? self.flags[value]?=?true;

??????return?true;

? }

??function?remove(Data?storage?self,?uint?value)

??????returns?(bool)

? {

??????if?(!self.flags[value])

??????????return?false;?// not there?沒(méi)有

????? self.flags[value]?=?false;

??????return?true;

? }

??function?contains(Data?storage?self,?uint?value)

??????returns?(bool)

? {

??????return?self.flags[value];

? }

}

contract?C {

??? using Set?for?Set.Data;?// this is the crucial change???這個(gè)是關(guān)鍵的變化

??? Set.Data knownValues;

????function?register(uint?value) {

????????// Here, all variables of type Set.Data have???這里,所有Set.Data 的變量都有相應(yīng)的成員函數(shù)

????????// corresponding member functions.

????????// The following function call is identical to??下面的函數(shù)調(diào)用和Set.insert(knownValues, value) 作用一樣

????????// Set.insert(knownValues, value)

????????if?(!knownValues.insert(value))

????????????throw;

??? }

}

It is also possible to extend elementary types in that way:

這個(gè)也是一種擴(kuò)展基本類型的(方式)

library?Search {

????function?indexOf(uint[]?storage?self,?uint?value) {

????????for?(uint?i?=?0; i?<?self.length; i++)

????????????if?(self[i]?==?value)?return?i;

????????return?uint(-1);

??? }}

contract?C {

??? using Search?for?uint[];

????uint[] data;

????function?append(uint?value) {

??????? data.push(value);

??? }

????function?replace(uint?_old,?uint?_new) {

????????// This performs the library function call???這樣完成了庫(kù)函數(shù)的調(diào)用

????????uint?index?=?data.find(_old);

????????if?(index?==?-1)

??????????? data.push(_new);

????????else

??????????? data[index]?=?_new;

??? }}

注意:所有的庫(kù)函數(shù)調(diào)用都是調(diào)用實(shí)際的EVM。這意味著,如果你要使用內(nèi)存或值類型,就必須執(zhí)行一次拷貝操作,即使是self變量??截悰](méi)有完成的情況可能是存儲(chǔ)引用變量已被使用。

Next??Previous

? Copyright 2015, Ethereum. Revision 37381072.

Built with?Sphinx?using a?theme?provided by?Read the Docs.

上一篇:簡(jiǎn)介下一篇:安裝Solidity