solidity函数类型及truffle使用

1、solidity类的多继承、重写

solidity类具有多继承的特性:

1
2
3
4
5
6
7
8
9
10
11
12
13
pragma solidity ^0.4.19

contract Animal1 {
uint age;
}

contract Animal2 {
string weight;
}

contract Dog is Animal1, Animal2 {
/** Dog 会继承 Animal1 及 Animal2 两个类 */
}

重写与其他语言相通,即子类的同名函数会覆盖从父类继承的方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
pragma solidity ^ 0.4 .19;

contract Animal {
function testFunc() public pure returns(string) {
return "Animal testFunc";
}
}

/** 子类重写了从父类继承过来的方法,会以子类的方法为基准 */
contract Dog is Animal {
function testFunc() public pure returns(string) {
return "Dog testFunc";
}
}

2、solidity函数的访问权限

solidity函数分为四种访问权限:

  • private: 私有函数。内部正常访问,外部无法访问,子类无法继承。
  • internal: 内部函数。内部正常访问,外部无法访问,子类可继承。
  • public: 公共函数。内部正常访问,外部正常访问,子类可继承。
  • external: 外部函数。内部不能访问,外部正常访问,子类可继承。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
pragma solidity ^0.4.19;

contract Animal {
/** public 公有:外部、内部、子类都可使用 */
function testPublic() public pure returns (string) {
return "public";
}
/** private 私有:合约内部可以正常访问 */
function testPrivate() private pure returns (string) {
return "private";
}
/** internal 内部:合约内部可以正常访问 */
function testInternal() internal pure returns (string) {
return "internal";
}
/** external 外部:只能供外部访问 */
function testExternal() external pure returns (string) {
return "external";
}
/** 未做任何修改时,使用pure */
function f() public pure {
testPublic();
testInternal();
testPrivate();
//testExternal(); // 报错,只能供外部访问
}
}

contract Dog is Animal {
/** 继承 public、internal、external 类型的函数 */
}

// Dog.testPublic() 继承,可用
// Dog.testInternal() 继承,不可用(internal函数外部无法访问)
// Dog.testExternal() 继承,可用
// Dog.f() 继承,可用

3、solidity函数中pureviewconstant的区别

solidity函数的完整声明格式为:

function 函数名(参数)  public|private|internal|external  pure|view|constant  无返回值|returns (返回值类型)

首先来个测试案例,根据编辑器提示补齐函数类型,然后总结不同之处:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
pragma solidity ^0.4.19;

contract Animal {
string homeAddress = "北京市";
uint weight;

/** pure */
function getAge() public pure returns (uint) {
return 30;
}

/** view */
function getCurrentAddress() public view returns (address) {
return msg.sender;
}

/** view */
function getHomeAddress() public view returns (string) {
return homeAddress;
}

function setWeight(uint w) public {
weight = w;
}

/** constant/view都行 */
function getWeight() public constant returns (uint) {
weight = 200;
return weight;
}
}

结论如下:

  • 只有当函数有返回值的情况下,才需要使用pureviewconstant
  • pure: 当函数返回值为自变量而非变量时,使用pure
  • view: 当函数返回值为全局变量或属性时,使用view
  • constant: 可以理解为view的旧版本,与view是等价的

如果一个函数有返回值,函数中正常来讲需要有 pureviewconstant关键字,如果没有,在调用函数的过程中,需要主动去调用底层的call方法。

注:如果一个函数中带了关键字viewconstant,就不能修改状态变量的值。但凡是是带了这两个关键字,区块链就默认只是向区块链读取数据,读取数据不需要花gas,但是不花gas就不可能修改状态变量的值。写入数据或者是修改状态变量的值都需要花费gas。

4、truffle初识

truffle是以太坊solidity编程语言的开发框架。

1.安装truffle

cnpm i -g truffle

2.创建项目工程

1
2
mkdir truffle && cd truffle
truffle init

3.项目结构
contracts目录下存的是solidity合约代码
migrations中存的是js脚本
test中存的是测试用例

4.编写代码、部署、调试
contracts中新建Hello.sol文件,代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
pragma solidity ^0.4.17;

contract Hello {

string weight = "18cm";
string height = "180cm";

function getAge() public pure returns (uint){
return 30;
}

function getWeight() public view returns (string) {
return weight;
}

function getHeight() public constant returns (string) {
return height;
}

function test() public returns (uint) {
return 250;
}
}

注意:

  • 类名Hello需要跟文件名Hello.sol保存一致
  • Migrations.sol文件不能删除

然后在migrations目录下添加对应的js脚本2_depoly_hello.js:

1
2
3
4
5
var myHello = artifacts.require("./Hello.sol");

module.exports = function(deployer) {
deployer.deploy(myHello);
};

代码添加完后,打开终端,切换到项目所在路径,执行:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
//启动测试网络
truffle develop

//编译
compile

//将合约部署到本地测试网络,成功后会返回合约地址(如:0x75c35c980c0d37ef46df04d31a140b65503c0eed)
migrate

//通过合约地址,得到合约对象,赋值给变量c
var c;
Hello.at('0x75c35c980c0d37ef46df04d31a140b65503c0eed').then((obj) => {
c = obj;
})

//调用合约暴露的方法
c.getAge()

//如果合约中暴露出的有返回值的函数,没有用 pure/view/constant 声明,则需要调用底层的 call 方法才能调用方法
//如上面代码中的test方法
c.test.call()

//修改完代码,编译后,重新部署时需重置之前的合约
migrate --reset

补充:调用migrate编译之后会build文件夹,存储的是编译之后生成的json文件,而这个json文件就是合约部署在虚拟机中的形式。

编译生成的的json文件中,有两个重要的额键值:

abi:通俗讲,abiapi类似,都是接口abi是合约的二进制接口。

bytecode:是合约代码的16进制码

通过abibytecode就能完成合约的部署。

5、基本值类型、引用类型

uint为值类型,只能深拷贝
string为引用类型,既可以深拷贝,也可以浅拷贝

uintstring深拷贝(默认的声明方式即为深拷贝):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
string a = "100";
uint b = 100;

// 浅拷贝
// string aa 等同于 string memory aa
function m(string aa) private {
aa = "1000";
}
function n(uint bb) private {
bb = 1000;
}

function f() public {
m(a)
n(b)
}

string类型浅拷贝(加storage关键字):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
string a = "100";
function f() public {
m(a)
}

// 深拷贝
// memory(深拷贝)、 storage(浅拷贝)
// 当函数参数为 storage 类型时,函数类型只能为 private 或 internal,否则报错
function m(string storage aa) private {
//aa = "1000"; // string不能直接修改,需要转换为可变的字节数组
bytes(aa)[0] = '6';
}

function getA() public view returns (string) {
return a;
}
Author

Ludis

Posted on

2018-02-07

Updated on

2018-02-20

Licensed under

Comments