合約
Solidity合約與面向?qū)ο笳Z(yǔ)言中的類(lèi)相似。它們包含可以修改這些變量的狀態(tài)變量和函數(shù)中的持久數(shù)據(jù)。在不同的合約(實(shí)例)上調(diào)用函數(shù)將執(zhí)行EVM函數(shù)調(diào)用,從而切換上下文以使?fàn)顟B(tài)變量不可訪(fǎng)問(wèn)。
創(chuàng)建合約
合約可以從“外部”或從Solidity合約創(chuàng)建。創(chuàng)建合約時(shí),其構(gòu)造函數(shù)(與合約名稱(chēng)相同的函數(shù))將執(zhí)行一次。
構(gòu)造函數(shù)是可選的。只允許一個(gè)構(gòu)造函數(shù),這意味著不支持重載。
從web3.js,即JavaScript API,這是按如下方式完成的:
// Need to specify some source including contract name for the data param below
var source = "contract CONTRACT_NAME { function CONTRACT_NAME(uint a, uint b) {} }";
// 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(source);
MyContract = web3.eth.contract(MyContract_.CONTRACT_NAME.info.abiDefinition);
// deploy new contract
var contractInstance = MyContract.new(
10,
11,
{from: myAccount, gas: 1000000}
);
在內(nèi)部,構(gòu)造函數(shù)參數(shù)是在合約本身的代碼之后傳遞的,但如果使用web3.js,則不必關(guān)心這一點(diǎn)。
如果合約要?jiǎng)?chuàng)建另一個(gè)合約,則創(chuàng)建者必須知道所創(chuàng)建合約的源代碼(和二進(jìn)制文件)。這意味著循環(huán)創(chuàng)建依賴(lài)是不可能的。
pragma solidity ^0.4.0;
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) {
// State variables are accessed via their name
// and not via e.g. this.owner. This also applies
// to functions and especially in the constructors,
// you can only call them like that ("internally"),
// because the contract itself does not exist yet.
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 == address(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 (keccak256(newOwner) & 0xff) == (bytes20(tokenAddress) & 0xff);
}
}
可見(jiàn)標(biāo)識(shí)的及getter方法
由于Solidity知道兩種函數(shù)調(diào)用(不產(chǎn)生實(shí)際EVM調(diào)用的內(nèi)部函數(shù)(也稱(chēng)為“消息調(diào)用”)和外部函數(shù)調(diào)用),函數(shù)和狀態(tài)變量有四種類(lèi)型的可見(jiàn)性。
函數(shù)可以被指定為external, public, internal 和 private,默認(rèn)為public。對(duì)于狀態(tài)變量,external是不被允許的,默認(rèn)是internal。
external:
external函數(shù)是合約接口的一部分,這意味著可以從其他合約和交易中調(diào)用它們。external函數(shù)f不能在內(nèi)部調(diào)用(即f()不起作用,但this.f()起作用)。external函數(shù)在接收大量數(shù)據(jù)時(shí)有時(shí)更高效。
public:
public函數(shù)是合約接口的一部分,可以在內(nèi)部或通過(guò)消息調(diào)用。對(duì)于公共狀態(tài)變量,會(huì)生成一個(gè)自動(dòng)填充getter方法(見(jiàn)下文)。
internal:
這些函數(shù)和狀態(tài)變量只能在內(nèi)部進(jìn)行訪(fǎng)問(wèn)(即從當(dāng)前合約或從中得出的合同),而不使用this。
private:
private函數(shù)和狀態(tài)變量?jī)H對(duì)它們定義的合約而不是衍生合約中可見(jiàn)。
注意:所有外部觀察者都可以看到合約內(nèi)的所有內(nèi)容。使用private修飾只會(huì)阻止其他合約訪(fǎng)問(wèn)和修改信息,但在區(qū)塊鏈之外,整個(gè)世界仍然可以看到它。
可見(jiàn)性說(shuō)明符在狀態(tài)變量的類(lèi)型之后以及函數(shù)的參數(shù)列表和返回參數(shù)列表之間給出。
pragma solidity ^0.4.0;
contract C {
function f(uint a) private returns (uint b) { return a + 1; }
function setData(uint a) internal { data = a; }
uint public data;
}
在下面的例子中,D可以調(diào)用c.getData()來(lái)檢索狀態(tài)存儲(chǔ)中的數(shù)據(jù)值,但不能調(diào)用f。合約E來(lái)自C,因此可以調(diào)用compute。
// This will not compile
pragma solidity ^0.4.0;
contract C {
uint private data;
function f(uint a) private returns(uint b) { return a + 1; }
function setData(uint a) { data = a; }
function getData() public returns(uint) { return data; }
function compute(uint a, uint b) internal returns (uint) { return a+b; }
}
contract D {
function readData() {
C c = new C();
uint local = c.f(7); // error: member "f" is not visible
c.setData(3);
local = c.getData();
local = c.compute(3, 5); // error: member "compute" is not visible
}
}
contract E is C {
function g() {
C c = new C();
uint val = compute(3, 5); // acces to internal member (from derivated to parent contract)
}
}
Getter函數(shù)
編譯器自動(dòng)為所有公共狀態(tài)變量創(chuàng)建getter函數(shù)。對(duì)于下面給出的合約,編譯器將生成一個(gè)名為data的函數(shù),該函數(shù)不接受任何參數(shù)并返回uint,狀態(tài)變量數(shù)據(jù)的data。狀態(tài)變量的初始化可以在聲明中完成。
pragma solidity ^0.4.0;
contract C {
uint public data = 42;
}
contract Caller {
C c = new C();
function f() {
uint local = c.data();
}
}
getter函數(shù)具有外部可見(jiàn)性。如果符號(hào)是在內(nèi)部訪(fǎng)問(wèn)的(即沒(méi)有this),則它被評(píng)估為一個(gè)狀態(tài)變量。如果它被外部訪(fǎng)問(wèn)(即用this),它被作為一個(gè)函數(shù)來(lái)評(píng)估。
pragma solidity ^0.4.0;
contract C {
uint public data;
function x() {
data = 3; // internal access
uint val = this.data(); // external access
}
}
下一個(gè)例子有點(diǎn)復(fù)雜:
pragma solidity ^0.4.0;
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;
}
請(qǐng)注意,結(jié)構(gòu)中的映射被省略,因?yàn)闆](méi)有提供映射關(guān)鍵的好方法。