solidity语法
第一部分 基本语法
智能合约中的变量结构
1.状态变量State Variables
永久储存在合约的存储中。
contract SimpleStorage {
uint storedData; // State variable
// ...
}
2.函数Functions
contract SimpleAuction {
function bid() payable { // Function
// ...
}
}
函数调用时,分为内部调用和外部调用(internal和external)
内部调用:不建立一个EVM调用(也就是message
call)
外部调用:建立一个EVM调用
注:状态变量和函数有四种可见性,external、public、internal和private。对于函数来说,默认是public;对于状态变量,external是不可能的,默认是internal。
external:可以通过其他合约或者交易外部调用。但是不可以内部调用,比如f()不可调用,但可以通过this.f()调用。接收大数组数据时,external效率会更高。
public:内部调用或者外部调用都可以。public的状态变量会自动生成访问函数(get/set)
internal:当前合约和源于它的合约(就是继承它的合约)可调用。
private:只在定义变量或函数的合约中可见。只是让区块链内部不可见,但是对于外界,仍然是可见的……
3.Modifier
modifier是以声明的方式,修改函数语句。
contract Purchase {
address public seller;
modifier onlySeller() { // Modifier
if (msg.sender != seller) throw;
_;
}
function abort() onlySeller { // Modifierusage
// ...
}
}
4.event
EVM日志记录工具的快捷接口
contract SimpleAuction {
event HighestBidIncreased(address bidder,uint amount); // Event
function bid() payable {
// ...
HighestBidIncreased(msg.sender, msg.value);// Triggering event
}
}
5.Structs结构体
contract Ballot {
struct Voter { // Struct
uint weight;
bool voted;
address delegate;
uint vote;
}
}
6.Enum枚举类型
contract Purchase {
enum State { Created, Locked, Inactive} // Enum
}
function getDefaultChoice() returns (uint){
return uint(defaultChoice);
}
}
变量
1.boolean布尔类型
就是true和false
操作有
!逻辑非
&&逻辑与
||逻辑或
==等
!=不等
2.Integer整形
uint和int型
有uint8和int8到uint256和int256
默认为uint256和int256
3.Address地址类型
比较重要,就是一个账户地址。20字节长,address有成员balance和send。
address x = 0x123;
address myAddress = this;
if (x.balance < 10 && myAddress.balance >= 10) x.send(10);
如果x是一个合约地址,代码会和send调用在一起运行(这是EVM的局限性)。如果代码用光gas也没执行完,那么会返回以太币,这时候,send调用会返回false。
send使用的不安全性:如果调用栈深度达到了1024(),交易会失败。或者接收方没了gas也会失败(?!)。所以转账时,要校验send函数的返回值,或者采用更好的方案:让接收方取钱。
call/delegatecall/callcode
调用call会返回一个布尔类型,表示函数调用完成(true)或者EVM抛出异常(false)。
delegatecall的作用差不多:但是区别在于,使用指定合约的代码,但是其他部分(比如存储、余额……)使用本合约。作用是类似于使用其他合约的代码作为库。
所有的合约都会继承address的成员,可以使用this.balance查看本合约地址。
4.固定大小的字符数组
bytes1, bytes2, bytes3, ..., bytes32.
byte默认是bytes1。
5.变长字符数组
bytes,就相当于特殊的byte[]
string,同上,唯一区别在于使用了UTF-8编码。
bytes有length和索引访问操作,bytes(str).length和bytes(str)[6]。
string没有各种操作,比如length和按索引访问string[0],非常难。
注:不能给函数传入二维变长数组,比如int[][] x,string[] str。
6.枚举
contract test {
enum ActionChoices { GoLeft, GoRight,GoStraight, SitStill }
ActionChoices choice;
ActionChoices constant defaultChoice = ActionChoices.GoStraight;
function setGoStraight() {
choice = ActionChoices.GoStraight;
}
// Since enum types are not part of theABI, the signature of "getChoice"
// will automatically be changed to"getChoice() returns (uint8)"
// for all matters external to Solidity.The integer type used is just
// large enough to hold all enum values,i.e. if you have more values,
// `uint16` will be used and so on.
function getChoice() returns (ActionChoices){
return choice;
}
7.函数类型变量
这个东西好像大多情况下用不到。最好先看下面的函数部分,再看这里。
内部函数变量只能在当前合约(包括内部库函数和继承函数)中使用,因为它们不能脱离当前合约的上下文执行。调用内部函数通过跳转到入口标签实现。
外部函数变量由一个地址和一个函数签名组成,并且它们可以由外部函数调用传递或者返回。
格式如下:
function () {internal|external}[constant] [payable] [returns ()]
如果有返回值,return type不能为空;如果没有返回值,那么就删掉[returns ()]
默认的function type是internal。
如果一个函数类型变量没有初始化或者删除了,调用它就会出现异常。
内部函数变量
闭上眼,用心去感受……
library ArrayUtils {
// internal functions can be used in internallibrary functions because
// they will be part of the same code context
function map(uint[] memory self, function(uint) returns (uint) f)
returns (uint[] memory r)
{
r = new uint[](self.length);
for (uint i = 0; i < self.length; i++){
r[i] = f(self[i]);
}
}
function reduce(
uint[] memory self,
function (uint) returns (uint) f
)
returns (uint r)
{
r = self[0];
for (uint i = 1; i < self.length; i++){
r = f(r, self[i]);
}
}
function range(uint length) returns (uint[]memory r) {
r = new uint[](length);
for (uint i = 0; i < r.length; i++){
r[i] = i;
}
}
}
contract Pyramid {
using ArrayUtils for *;
function pyramid(uint l) return (uint){
return ArrayUtils.range(l).map(square).reduce(sum);
}
function square(uint x) internal returns(uint) {
return x * x;
}
function sum(uint x, uint y) internal returns(uint) {
return x + y;
}
}
外部函数变量
contract Oracle {
struct Request {
bytes data;
function(bytes) external callback;
}
Request[] requests;
event NewRequest(uint);
function query(bytes data, function(bytes)external callback) {
requests.push(Request(data, callback));
NewRequest(requests.length - 1);
}
function reply(uint requestID, bytes response){
// Here goes the check that the replycomes from a trusted source
requests[requestID].callback(response);
}
}
contract OracleUser {
Oracle constant oracle = 0x1234567; // knowncontract
function buySomething() {
oracle.query("USD", oracleResponse);
}
function oracleResponse(bytes response){
if (msg.sender != oracle) throw;
// Use the data
}
}
8.引用类型。
对于一些大于256bit的的复杂的类型,比如数组和结构体等,会使用引用类型。引用类型有三种:memory(非永久)、storage(状态变量)还有calldata。
函数变量默认为memory类型;本地变量默认为storage类型;状态变量强制都是storage类型。calldata比较特殊,是不可修改的、非永久的区域,用于保存函数的参数。external函数的参数强制为calldata类型,跟memory类型差不多。
数据位置挺重要的,,它可以影响赋值语句的执行:storage和memory之间的赋值或者赋值给状态变量,都会产生一份独立的copy。赋值给本地的storage变量,则只赋一个引用,而且这个引用会永远指向一个状态变量,即使这个状态变量变化。而把一个memory的引用类型赋值给另一个memory引用类型也不会产生一个copy。
contract C {
uint[]x; //默认为storage
//memoryArray是memory
functionf(uint[] memoryArray) {
x = memoryArray; //memory到storage,x保存了一份copy
var y = x; // y是storage
y[7]; //没啥问题
y.length= 2; //通过改变y改变了x
delete x; //清空数组x,同时改变y
//下面这个不能用,不能把一个memory变量赋
// y = memoryArray;
//下面这个不能用,因为y没有一个pointer
// delete y;
g(x); //调用g函数,将x的引用传过去
h(x); //调用h函数,在memory中产生一个临时的copy
}
functiong(uint[] storage storageArray) internal {}
functionh(uint[] memoryArray) {}
}
对memory类型的变量长度,可以通过new关键字,来设定长度
storage类型 变量长度,不可以更改。
contract C {
functionf(uint len) { uint[] memory a = new uint[](7);
a[6] = 8;
}
}
Mappings映射
这是一个在solidity中比较重要的部分,可以被视为一个虚拟初始化的hash表,每一个可能的key默认为全0.
其中key的类型可以是除了mapping之外的任意类型,value可以是任意类型,包括mapping
contract MappingExample {
mapping(address=> uint) public balances;
functionupdate(uint newBalance) {
balances[msg.sender] = newBalance;
}
}
contract MappingUser {
functionf() returns (uint) {
return MappingExample().balances(this);
}
}
delete关键字hash
delete a相当于给a赋一个新的对象。
一些预置的变量和函数
block.blockhash(uint blockNumber) returns (bytes32):指定区块的hash,只有最近256个,其他全0
block.coinbase (address):当前的挖矿地址
block.difficulty (uint):当前区块难度
block.gaslimit (uint):当前区块的gaslimit
block.number (uint):当前区块数
block.timestamp (uint):当前区块时间戳
msg.data (bytes):完成calldata
msg.gas (uint):剩余gas
msg.sender (address):消息发送人
msg.sig (bytes4): calldata的前四个字节,即函数标识符
msg.value (uint):消息发送的以太币数,以wei为单位
now (uint):当前区块时间戳,跟上面的哪个一样。
tx.gasprice (uint):交易的gas单价
tx.origin (address):交易的发起人(全链上)
.balance (uint256):地址的余额
.send(uint256 amount) returns
(bool):发送给该地址制定以太币。j
this表示本合约的地址
selfdestruct(address recipient):销毁本合约,剩余资金转移到给定地址
预置函数
addmod(uint x, uint y, uint k) returns (uint):
mulmod(uint x, uint y, uint k) returns (uint):
keccak256(...) returns (bytes32):
sha3(...) returns (bytes32):
sha256(...) returns (bytes32):
ripemd160(...) returns (bytes20):RIPEMD-160
ecrecover(bytes32 hash, uint8 v, bytes32 r,
bytes32 s) returns (address):恢复地址的公钥
函数
内部函数调用
contract C {
functiong(uint a) returns (uint ret) { return f(); }
functionf() returns (uint ret) { return g(7) + f(); }
}
外部函数调用
contract InfoFeed {
functioninfo() payable returns (uint ret) { return 42; }
}
contract Consumer {
InfoFeedfeed;
functionsetFeed(address addr) { feed = InfoFeed(addr); }
functioncallFeed() { feed.info.value(10).gas(800)(); }
}
�]�%~��