控制结构
- if-else
- while
- for
- break\continue
- return
- ?:
- 等等
//结构上和其他语法没有什么差异 contract sample { int a = 12; int[] b; function sample() { if(a == 12) {} else if(a==34){} else {} var temp = 10; while(temp<20) { if(temp==17){break;} else {continue;} } temp++; } for(var m=0;m<b.length;m++){ } }
用new 操作符创建合约
一个合约可以使用new关键字来创建一个新合约。
例如:
contract sample1 { int a; function assign(int b){ a = b; } } contract sample2 { function sample2(){ sample1 s = new sample1(); //注意写法 s.assign(12); } }
异常
异常的抛出分为自动和手动。
若你想手动抛出异常,可以使用throw手动抛出。
注意,异常抛出后,会撤销对状态和余额的所有改变。
contract sample { function myFunction () { throw; } }
函数调用
- 内部函数调用:一个函数在同一个合约中调用另一个函数
- 外部函数调用:一个函数调用另一个合约的函数。
外部函数调用–this关键字
合约sample1
contract sample1 { int a; function sample1(int b) payable { a = b; } function assign(int c){ a = c; } function makePayment(int d) payable { a = d; } }
合约sample2
contract sample2 { function hello() {} function sample2(address addressOfContract){ sample1 s = (new sample1).value(12)(23); s.makePayment(22); s.makePayment.value(45)(12); s.makePayment.value(4).gas(900)(12); this.hello(); //利用this调用外部合约函数 sample1 s2 = sample1(addressOfContract); s2.makePayment(112); } }
注意:使用this关键字进行的调用称为外部调用。在函数中,this关键字代表当前合约实例
合约功能——深入理解合约
可见性
可见性定义了谁可以看到它,函数和状态变量有四种可见性:external、public、internal和private
- 函数可见性,默认为 public
- 状态变量可见性,默认为 internal
- external:外部函数只能由其他合约调用,或者通过交易调用——this.f()
- public:公共函数和状态变量可以用所有可行办法访问
- internal:内部函数和状态变量只可以内部访问,即从当前合约内和继承它的合约访问。不可以使用this访问它
- private:私有函数和状态变量类似于内部函数,但是继承合约不可以访问它们
示例
contract sample1 { int public b = 78; int internal c = 90; function sample1() { this.a();//外部访问 b = 21;//内部访问 } function a() external {} } contract sample2 { int internal d = 9; int private e = 90; } //sample3 继承 sample2 contract sample3 is sample2 { sample1 s; function sample3() { s = new sample1(); s.a();//外部访问 } }
函数修改器(较难理解)
先看一个修改器的例子:
contract sample { int a = 90; modifier myModifier1(int b) { int c = b; _; c = a; a = 1; } modifier myModifier2 { int c = a; _; } modifier myModifier3 { a = 96; return; _; a = 99; } modifier myModifier4 { int c = a; _; } function myFunction() myModifier1(a) myModifier2 myModifier3 returns (int d) { a = 2; return a; } }
注:
- 在修改器中,无论下一个修改器体或者函数体二者哪个先到达,都会被插入到“_;”出现的地方。
回退函数
即一个合约中唯一一个未命名函数。
- 不能有实参
- 不能有返回值
- 如果其他函数都不能匹配给定的函数标识符,那么就执行回退函数
- 如果你想让你的合约接收以太币,就必须实现回退函数
contract sample { function() payable { } }
继承
即使一个合约继承自其他多个合约,在区块链上也只会创建一个合约。
父合约的代码总是会被复制到最终合约里。
- 关键字 is
示例
contract sample1 { function a(){} function b() {} } //合约2继承自合约1 contract sample2 is sample1{ function b() {} } contract sample3{ function sample3(int b){ } } //合约4继承自合约1与合约2 contract sample4 is sample1,sample2 { function a(){} function c() { a(); //执行合约1中的a方法 sample1.a(); //执行合约2中的b方法 b(); } }
关键字super
- 用于引用最终继承链中的下一个合约
示例
contract sample1{ } contract sample2{ } contract sample3 is sample2{ } contract sample4 is sample2{ } contract sample5 is sample4{ function myFunc(){} } contract sample6 is sample1,sample2,sample3,sample4,sample5 { function myFunc() { //执行sample5中的myFunc方法 super.myFunc(); } }
抽象合约
- 仅包含函数原型而不包含函数实现的合约
- 抽象合约不能被编译
- 如果一个合约继承自抽象合约且不重写,那么它自己也是抽象合约
示例
contract sample1{ function a() returns (int b); } contract sample2{ function myFunc(){ sample1 s = sample(); s.a(); } }
库
库的目的是在一个特定地址中只部署一次,其其代码可供复用。
示例-使用solidity的math库:
library math { function addInt(int a,int b) return (int c){ return a+b; } } contract sample { function data() returns (int d){ return math.addInt(1,2);//调用math库中的addInt方法 } }
使用场景
- 如果有许多合约,这些合约有一些共同的代码,那么可以把它们共同的代码部署成一个库。这么做的好处是这样能节省gas。因为gas的大小依赖于合约的规模。
返回多个值
示例:
contract sample{ function a() returns (int a,string c){ return (1,"m"); } function b(){ int A; string memory B; (A,B) = a();// A =1,B = "m" (A,) = a();// A =1 (,B) = a(); //B = "m" } }
全局变量
- 特殊变量
- 特殊函数
1、区块和交易属性
- block.blockhash(uint blockNumber) returns (bytes32) //区块哈希值
- block.coinbase(address) //当前区块矿工的地址
- block.difficulty(uint) //当前区块的难度值
- block.gaslimit(uint) //当前区块的gas上限,定义了整个区块中的所有交易最多能消耗多少gas
- block.number(uint) //当前区块的序号
- block.timestamp(uint) //当前区块的时间戳
- msg.gas(uint) //当前剩余的gas
- msg.sender(address) //当前调用发起人的地址
- msg.sig(bytes4) //调用数据的前四个字节
- msg.value(uint) //这个消息所附带的货币量,单位为wei
- now(uint) //当前区块的时间戳,等同于block.timestamp
- tx.gasprice(uint) //交易的gas价格
- tx.origin(address) //交易的发起人
2、地址类型相关
- .balance(uint256) //地址余额,单位为wei
- .send(uint256 amount) returns(bool) //发送指定数量的wei到地址
3、合约相关
- this //当前合约
- selfdestruct(address recipient) //销毁当前合约,把其中的资金发送到指定地址
以太币单位
一个数字可以用wei、finney、szabo、Ether等单位转换为不同面值的以太币。默认使用wei为单位。
存在、真实性和所有权合约的证明
下面我们要实现一个“证明文件所有权”的合约。
分以下几步来进行:
- 1、成对存储文件的哈希和文件所有者的名字,用以实现所有权证明(PoO)
- 2、成对存储文件的哈希和区块的时间戳,用以实现文件在某个特定时间存在的证明(PoE)
- 3、存储哈希自身,用以证明文件的真实性。如果文件被修改,其哈希也会被改变。更改过的文件的哈希会使得合约无法发现文件,从而证明文件被修改过。
代码如下:
contract Proof{ struct FileDetails { uint timestamp; string owner; } mapping (string => FileDetails) files; event logFileAddedStatus(bool status,uint timestamp,string owner,string fileHash); //存储文件所有者 function set(string owner,string fileHash){ if(files[fileHash].timestamp==0){ files[fileHash] = FileDetails(block.timestamp,owner); //触发一个事件以至于前端应用知道文件的存在 logFileAddedStatus(true,block.timestamp,owner,fileHash); }else { //告诉前端文件存在,但是不能存储 logFileAddedStatus(false,block.timestamp,owner,fileHash); } } //获取文件信息 function get(string fileHash) returns (uint timestamp,string owner){ return (files[fileHash].timestamp,files[fileHash].owner); } }
编译和部署合约
- sol编译器 之 安装指南
查看sol编辑器查看链接 - sol编译器 之 使用指南
查看sol编辑器使用指南链接
这里,我们使用solcjs和Browser Solidity ,其中solcjs允许在node.js中以编程方式编译Solidity,而Browser Solidity是一个适用于小型合约的IDE。
至此,我们将Solidity语言进行了基本的讲解,下一节中我们将介绍如何使用web3.js开发DApp前端。