Tutorials 1
起步-调试程序
本教程版本需truffle的版本为 v4.0+
1.新建一个项目
打开终端,命名为1in
$ mkdir tutorials1 $ cd tutorials1 $ truffle init
2.在contracts目录中新建文件 Store.sol
内容如下:
pragma solidity ^0.4.17; contract SimpleStorage { uint myVariable; function set(uint x) public { myVariable = x; } function get() constant public returns (uint) { return myVariable; } }
这个合约的名字为:SimpleStorage,它拥有两个函数和一个参数
set get 和 myVariable
3.在migrations目录中新建2_deploy_contracts.js文件,内容如下:
var SimpleStorage = artifacts.require("SimpleStorage"); module.exports = function(deployer) { deployer.deploy(SimpleStorage); };
这个文件的内容能够把合约SimpleStorage部署到区块链上
4.在终端1in中,编译文件
$ truffle compile
5.打开一个新的终端窗口 标记为2in
进入truffle开发模式
$ truffle develop
6.部署合约
在2in中
$ migrate
打印信息如下:
Running migration: 1_initial_migration.js Replacing Migrations... ... 0xe4f911d95904c808a81f28de1e70a377968608348b627a66efa60077a900fb4c Migrations: 0x3ed10fd31b3fbb2c262e6ab074dd3c684b8aa06b Saving successful migration to network... ... 0x429a40ee574664a48753a33ea0c103fc78c5ca7750961d567d518ff7a31eefda Saving artifacts... Running migration: 2_deploy_contracts.js Replacing SimpleStorage... ... 0x6783341ba67d5c0415daa647513771f14cb8a3103cc5c15dab61e86a7ab0cfd2 SimpleStorage: 0x377bbcae5327695b32a1784e0e13bedc8e078c9c Saving successful migration to network... ... 0x6e25158c01a403d33079db641cb4d46b6245fd2e9196093d9e5984e45d64a866 Saving artifacts...
7.到目前为止,合约已经部署到开发网络上了,现在尝试跟合约交互(调用合约的函数)
在终端2in:
$ SimpleStorage.deployed().then(function(instance){return instance.get.call();}).then(function(value){return value.toNumber()});
这个调用合约的get函数,结果如下:
$ 0
这个值就是合约中定义的myVariable,虽然在合约中并没有给它赋初始值,但是solidity语言会给uint类型的变量默认赋0;不像js或其他语言会报NULL或undefined
8.给变量赋值
在终端2in:
$ SimpleStorage.deployed().then(function(instance){return instance.set(4);});
在上面我们给变量myVariable赋值4;
执行完之后会有如下信息打印:
{ tx: '0x7f799ad56584199db36bd617b77cc1d825ff18714e80da9d2d5a0a9fff5b4d42', receipt: { transactionHash: '0x7f799ad56584199db36bd617b77cc1d825ff18714e80da9d2d5a0a9fff5b4d42', transactionIndex: 0, blockHash: '0x60adbf0523622dc1be52c627f37644ce0a343c8e7c8955b34c5a592da7d7c651', blockNumber: 5, gasUsed: 41577, cumulativeGasUsed: 41577, contractAddress: null, logs: [] }, logs: [] }
这个就是一个transaction操作执行后的返回值
其中tx和receipt中的第一个值时transaction的hash值,后面debug的时候会非常有用
9.去值,验证是否赋值成功
$ SimpleStorage.deployed().then(function(instance){return instance.get.call();}).then(function(value){return value.toNumber()});
如果顺利,会有一下打印信息:
$ 4
到目前为止,已经成功的测试了call和transaction操作,下面讲解如何排查错误。
Debugging errors
以三个例子来介绍truffle的debug的使用1.无限循环
2.无效的错误检查
3.没有错误,但是函数没有按照我们预想的执行
1.无限循环改造set()函数
function set(uint x) public { while(true) { myVariable = x; } }
*改写之后记得保存
然后打开终端2in
$ migrate —reset
***reset前面是两个短划线
这个命令会自动重新编译,紧接着部署合约,所以不用使用compile再migrate了
在上面执行set函数的时候打印信息中会出现hash值,但是如果函数报错的话就没那么幸运了,但是我们后面要debug错误的时候又需要这个hash值怎么办?别担心,truffle还有log。打开新终端窗口 标记为3in 输入执行命令:
$ truffle develop — log
***log前面是两个短划线
执行完就不用管了,在后面我们每次跟合约交互的时候操作log都会在此打印出来,不管这个函数是否执行成功
回到终端2in,执行我们的set函数,看看无限循环会报什么错
$ SimpleStorage.deployed().then(function(instance){return instance.set(4);});
执行完打印信息如下:(如果没有报错检查刚才的set函数的改动是否保存了)
Error: VM Exception while processing transaction: out of gas
在终端3in中,会有以下log信息:
develop:testrpc eth_sendTransaction +0ms develop:testrpc +1s develop:testrpc Transaction: 0xe493340792ab92b95ac40e43dca6bc88fba7fd67191989d59ca30f79320e883f +2ms develop:testrpc Gas usage: 4712388 +11ms develop:testrpc Block Number: 6 +15ms develop:testrpc Runtime Error: out of gas +0ms develop:testrpc +16ms
拿到transaction后面的值 回到终端2in
$ debug 0xe493340792ab92b95ac40e43dca6bc88fba7fd67191989d59ca30f79320e883f
执行后有以下打印信息:
Gathering transaction data... Addresses affected: 0x377bbcae5327695b32a1784e0e13bedc8e078c9c - SimpleStorage Commands: (enter) last command entered (step next) (o) step over, (i) step into, (u) step out, (n) step next (;) step instruction, (p) print instruction, (h) print this help, (q) quit Store.sol | 0x377bbcae5327695b32a1784e0e13bedc8e078c9c: 1: pragma solidity ^0.4.17; 2: 3: contract SimpleStorage { ^^^^^^^^^^^^^^^^^^^^^^^ debug(develop:0xe4933407...)>
在此打印信息里面有关于“Commands”的介绍
就是告诉你怎么使用调试工具,就类似于普通app开发使用IDE工具的断点调试,跳到下一步/跳入函数内部/跳出函数,只不过这个需要输入对应的符号来操作
输入 n然后按enter键,或者直接enter
Store.sol | 0x377bbcae5327695b32a1784e0e13bedc8e078c9c: 4: uint myVariable; 5: 6: function set(uint x) public { ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
这就可以看到应用程序执行到下一个函数了,继续enter
Store.sol | 0x377bbcae5327695b32a1784e0e13bedc8e078c9c: 5: 6: function set(uint x) public { 7: while(true) { ^^^^ debug(develop:0xe4933407...)> Store.sol | 0x377bbcae5327695b32a1784e0e13bedc8e078c9c: 5: 6: function set(uint x) public { 7: while(true) { ^^^^^^^^^^^^ debug(develop:0xe4933407...)> Store.sol | 0x377bbcae5327695b32a1784e0e13bedc8e078c9c: 6: function set(uint x) public { 7: while(true) { 8: myVariable = x; ^ debug(develop:0xe4933407...)> Store.sol | 0x377bbcae5327695b32a1784e0e13bedc8e078c9c: 6: function set(uint x) public { 7: while(true) { 8: myVariable = x; ^^^^^^^^^^ debug(develop:0xe4933407...)> Store.sol | 0x377bbcae5327695b32a1784e0e13bedc8e078c9c: 6: function set(uint x) public { 7: while(true) { 8: myVariable = x; ^^^^^^^^^^^^^^ debug(develop:0xe4933407...)> Store.sol | 0x377bbcae5327695b32a1784e0e13bedc8e078c9c: 5: 6: function set(uint x) public { 7: while(true) { ^^^^^^^^^^^^
你会发现一直在循环,这就发现问题所在了。
输入 q 按enter键退出debug模式
$ q
2 “An invalid error check” 一个无效的错误检查在开发阶段,我们可以使用断言来进行调试程序,断言会导致一个“An invalid error check” 错误
修改set函数如下:
function set(uint x) public { assert(x == 0); myVariable = x; }
*修改后记得保存
判断如果x == 0,就通过,如果不等0就中断
在终端2in中,执行之前的命令:
$ migrate — reset
***reset前有两个短划线
调用set函数:
SimpleStorage.deployed().then(function(instance){return instance.set(4);});
执行之后会有以下打印信息:
Error: VM Exception while processing transaction: invalid opcode
在终端窗口3in中,拿到此次操作的hash值,进行debug
debug 0xe493340792ab92b95ac40e43dca6bc88fba7fd67191989d59ca30f79320e883f
按enter直到出现以下信息:
Store.sol | 0x377bbcae5327695b32a1784e0e13bedc8e078c9c: 5: 6: function set(uint x) public { 7: assert(x == 0); ^^^^^^^^^^^^^^ debug(develop:0x7e060037...)> Transaction halted with a RUNTIME ERROR. This is likely due to an intentional halting expression, like assert(), require() or revert(). It can also be due to out-of-gas exceptions. Please inspect your transaction parameters and contract code to determine the meaning of this error.
在debug中提到了可能的错误原因,其中确实有我们设置的错误
3.function没有按照设计运行有些时候错误不是在执行的时候能报error的,所有的编译和deploy都OK,function也能够执行,但是执行的方式并不是我们想要的。
修改SimpleStorage合约内容:
event Odd(); event Even(); function set(uint x) public { myVariable = x; if (x % 2 == 0) { Odd(); } else { Even(); } }代码中引入了两个模型事件:Odd和Even,通过判断传入的参数x是是否满足 x%2 == 0来触发
在终端2in:
$ migrate —reset*reset前有两个短划线
$ SimpleStorage.deployed().then(function(instance){return instance.set(4);});查看执行后的打印信息:
{ tx: '0x7f799ad56584199db36bd617b77cc1d825ff18714e80da9d2d5a0a9fff5b4d42', receipt: { transactionHash: '0x7f799ad56584199db36bd617b77cc1d825ff18714e80da9d2d5a0a9fff5b4d42', transactionIndex: 0, blockHash: '0x08d7c35904e4a93298ed5be862227fcf18383fec374759202cf9e513b390956f', blockNumber: 5, gasUsed: 42404, cumulativeGasUsed: 42404, contractAddress: null, logs: [ [Object] ] }, logs: [ { logIndex: 0, transactionIndex: 0, transactionHash: '0x7f799ad56584199db36bd617b77cc1d825ff18714e80da9d2d5a0a9fff5b4d42', blockHash: '0x08d7c35904e4a93298ed5be862227fcf18383fec374759202cf9e513b390956f', blockNumber: 5, address: '0x377bbcae5327695b32a1784e0e13bedc8e078c9c', type: 'mined',event: 'Odd',
args: {} } ] }打印信息中显示这个是’Odd’事件,而我们正常执行的话应该不会出发这个事件,是因为在set函数中错误写入了odd事件导致此事件被调用,所以打开终端3in,拿到hash值(当前这种情况hash可以知道在打印信息中拿到,tx的值),回到终端2in中:
$ debug 0x7f799ad56584199db36bd617b77cc1d825ff18714e80da9d2d5a0a9fff5b4d42一步步enter,定位在哪一步调用了odd事件,最终会发现是在set函数中调用的。
上面的三个例子我们都假设自己在犯错误,写错误的代码,不要弄混了。
作者:大都废