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.jsReplacing Migrations...... 0xe4f911d95904c808a81f28de1e70a377968608348b627a66efa60077a900fb4cMigrations: 0x3ed10fd31b3fbb2c262e6ab074dd3c684b8aa06b
Saving successful migration to network...... 0x429a40ee574664a48753a33ea0c103fc78c5ca7750961d567d518ff7a31eefda
Saving artifacts...
Running migration: 2_deploy_contracts.jsReplacing SimpleStorage...... 0x6783341ba67d5c0415daa647513771f14cb8a3103cc5c15dab61e86a7ab0cfd2SimpleStorage: 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 - SimpleStorageCommands:(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) quitStore.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函数中调用的。
上面的三个例子我们都假设自己在犯错误,写错误的代码,不要弄混了。