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)建,也可以由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);
??? }
}
因?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的值(僅僅在它們自己的范圍里)。
編譯器會(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)提供映射的鍵值。
修飾符可以用來(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)算。
一個(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ù)。這可以在兩處進(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ù)和合約類似,但是它們的目的主要是在給定地址上部署,以及用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ì)被解除)
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ò)引用。
原則上使用LibraryName.functionName.value(x)()來(lái)轉(zhuǎn)移Ether。但若使用CALLCODE,Ether會(huì)在當(dāng)前合約里用完。
指令 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.