使用solidity语言撰写智能合约
Ethereum上的智能合约需要使用solidity语言来撰写。虽然还有其他能用来撰写智能合约的语言如Serpent(类Python)、lll(类Fortran)
但目前看到所有公开的智能合约都是使用solidity撰写。
solidity还是比较像Java或C#。因为和Javascript不同,solidity与Java或C#同属于强类型(Strong Type,在定义变数时需要指定类型)语言、在定义函式(function)时同样需指定回传的类型(type)、同样也需要先编译才能执行。这些特性都是Javascript所不具备的。
关于solidity编写智能合约的具体语法参见笔者专题
开发前的准备
本文将使用当前最活跃的智能合约开发框架truffle为基础来开发。ENS(Ethereum Name Service)也是采用truffle框架。其他选择还有embark等。
测试网:test rpc。 毕竟真实网络需要以太币,且操作需要等待十几秒,性价比不高。testrpc中也包含了Javascript版本的Ethereum虚拟机(Ethereum Virtual Machine),因此可以完整地执行智能合约。
-编译器: atom,好处之前说过
环境准备
- node.js (自行安卓)
- 安装truffle
$ npm install -g ethereumjs-testrpc truffle
- 启动testrpc
luoxuedeMBP:~ luoxue$ testrpc
EthereumJS TestRPC v6.0.3 (ganache-core: 2.0.2)
Available Accounts
==================
(0) 0x48e9abd440ff12f0f5bd457d0205d64a2c047973
(1) 0x14692d8b82328f29016db341c15f59585531ab58
(2) 0x0ff04903afd3dc4791ea5b747804de6cd6c4c513
(3) 0x4d77b3d7cadee90f4c90af72be75bd2ac88a72b5
(4) 0xc66ad1ee63b457047e7102425e657b70105f5eb9
(5) 0xfab642ee1cbacd8fc678c83e821f7d8beda61fec
(6) 0xf76e1c57250b88c1628430d0c04e3b4ecb111c0e
(7) 0xf4994bfce769c16f1631f0609867858a96885917
(8) 0xf31105aee2b095265ea46c51e2644eacbab96cb7
(9) 0x51213b5aa72783e7216b55fc2ecb3104dfd5f465
Private Keys
==================
(0) 77b92fa8bdf4bec94f3ebf67f44fb11ff660911ca64d3aa2c729b689aa8c807b
(1) e1b555743af9cc58fb094349776f944b76ff85663b51d5d6bc4802da2e92c54c
(2) c5180d4154bb2d89eb89335766070dcc8210b25374ef8d71c26f50d002f3c450
(3) 61acc2eaf3fdcecad846cf0507ae8cc07fa03a9bf00a581974c268c6bcba0afd
(4) 7f384dad1148c73257c596bc875ca492e8c6e99bb5f7de6958ea4cfe82a5b9a3
(5) 9796225e8d81a3f72e4fccfe3a562838bf589d4c40fcadbdc85c9b9ad5d5ad4c
(6) b858608913552368f4bdbc33a01f7fc7ec98684b013eab2d8dcb696246cbea56
(7) 3b581c0f56aa9937706cd0e27e7a1ccd45513c71d845ab5b18ff5039c20e9d8a
(8) f5d548c0b0c9974711814937b2d0d8373e244aa265ac2e0174b2b9ec35faa134
(9) 22cb6ad0b97dda82426c04485fca580f25746602bc94950efcb13d10528bf622
HD Wallet
==================
Mnemonic: marriage artwork corn dentist other feel decrease pride brass flash wire since
Base HD Path: m/44'/60'/0'/0/{account_index}
Listening on localhost:8545
- testrpc启动后自动建立了10个帐号(Accounts),与每个帐号对应的私钥(Private Key)。
- 每个帐号中都有100个测试用的以太币(Ether)。
- 要注意testrpc仅运行在內存中,因此每次重开时都会回到全新的状态。
建立项目
输入以下命令
luoxuedeMBP:~ luoxue$ cd desktop
luoxuedeMBP:desktop luoxue$ mkdir SmartContractDemo
luoxuedeMBP:desktop luoxue$ cd SmartContractDemo
luoxuedeMBP:SmartContractDemo luoxue$ mkdir hello
luoxuedeMBP:SmartContractDemo luoxue$ cd hello
luoxuedeMBP:hello luoxue$ truffle init
Downloading...
Unpacking...
Setting up...
Unbox successful. Sweet!
Commands:
Compile: truffle compile
Migrate: truffle migrate
Test contracts: truffle test
luoxuedeMBP:hello luoxue$ ls
目录结构:
- /contracts:存放智能合约原始代码的地方
- /migrations:这是 Truffle用来部署智能合约的功能,待会儿我们会修改2_deploy_contracts.js来部署 HelloWorld.sol。
- /test: 测试智能合约的代码放在这里,支持js 与 sol 测试。
- truffle.js: Truffle 的设置文档
新建HelloWorld.sol
放在contracts里,代码如下
pragma solidity ^0.4.4;
contract HelloWorld {
function sayHello() returns (string) {
return ("Hello World");
}
}
编译
现在执行truffle compile命令,我们可以将HelloWorld.sol原始码编译成Ethereum bytecode。
luoxuedeMBP:hello luoxue$ cd build
luoxuedeMBP:build luoxue$ ls
contracts
luoxuedeMBP:build luoxue$ cd contracts
luoxuedeMBP:contracts luoxue$ ls
HelloWorld.json Migrations.json
-
编译成功,新增了build文件夹,会在HelloWorld文件夹下面的build/contracts文件夹下面看见HelloWorld.json文件。
部署
- 复制migration文件夹中的init文件,创建名为2_deploy_contracts.js的文件,代码如下:
var HelloWorld = artifacts.require("HelloWorld");
module.exports = function(deployer) {
deployer.deploy(HelloWorld);
};
使用artifacts.require语句来取得准备部署的合约。使用deployer.deploy语句将合约部署到区块链上。这边HelloWorld是contract的名称而不是文件名。因此可以用此语法读入任一.sol文件中的任一合约。
现在执行truffle migrate命令
注意!!!需要切换到truffle develop 网络下编译和部署,并且部署文件需要在编译前写对,因为编译即产生一个合约地址,错误的部署文件会对不上
姿势如下:
luoxuedeMBP:HelloWorld luoxue$ truffle develop
省略
truffle(develop)> compile
Compiling ./contracts/HelloWorld.sol...
Compiling ./contracts/Migrations.sol...
Writing artifacts to ./build/contracts
truffle(develop)> migrate
Using network 'develop'.
Running migration: 1_initial_migration.js
Deploying Migrations...
... 0xad8b447607866b32b1c115156a97f9d247a125d35c14585ac3ed3f9eaa06a528
Migrations: 0x8cdaf0cd259887258bc13a92c0a6da92698644c0
Saving successful migration to network...
... 0xd7bc86d31bee32fa3988f1c1eabce403a1b5d570340a3a9cdba53a472ee8c956
Saving artifacts...
Running migration: 2_deploy_contracts.js
Deploying HelloWorld...
... 0xae7215511d62acedac6c4880d8578240e73913373eaaf6d0e86253197b35261b
HelloWorld: 0x345ca3e014aaf5dca488057592ee47305d9b3e10
Saving successful migration to network...
... 0xf36163615f41ef7ed8f4a8f192149a0bf633fe1a2398ce001bf44c43dc7bdda0
Saving artifacts...
truffle(develop)>
获取合约对象
- 通过web3 来获取合约对象
- 输入
// 声明一个对象
truffle(develop)> let contract;
undefined
// 实例化
// truffle console
truffle(develop)> HelloWorld.deployed().then(instance => contract = instance)
解释:truffle console中预载了truffle-contract函数库,以方便操作部署到区块链上的合约。
这边使用HelloWorld.deployed().then语句来取得HelloWorld合约的Instance(实例),并存到contract变量中,以方便后续的调用。
使用的是js es6+语句
调用合约函数
truffle(develop)> contract.sayHello()
'Hello World'
- 注意如果该函数没有constant 或者pure ,需要.call()
如将代码增加一个函数无pure/constant如下:
pragma solidity ^0.4.4;
contract HelloWorld {
// 能直接调用
function sayHello() pure public returns (string) {
return ("Hello World");
}
// 需要依靠.call调用
function test() public returns (string) {
return ("Hello World");
}
}
- 重新编译与部署, 注意部署时候需要加上-- reset
// 编译
truffle(develop)> compile
Compiling ./contracts/HelloWorld.sol...
Compilation warnings encountered:
/Users/luoxue/Desktop/SmartContractDemo/HelloWorld/contracts/HelloWorld.sol:9:3: Warning: Function state mutability can be restricted to pure
function test() public returns (string) {
^ (Relevant source part starts here and spans across multiple lines).
Writing artifacts to ./build/contracts
// 部署
truffle(develop)> migrate
Using network 'develop'.
Network up to date.
truffle(develop)> migrate --reset
正确的调用姿势
truffle(develop)> contract.sayHello()
'Hello World'
truffle(develop)> contract.test.call()
'Hello World'
truffle(develop)>
- 输入与输出,在代码里面加入一个echo函数,输入字符串,返回输出
function echo(string s) public returns (string) {
return s;
}
truffle(develop)> contract.echo.call("melody")
'melody'