当前位置: 代码迷 >> 综合 >> solidity-以太坊区块链Truffle-webpack开发入门 (三) 合约的认识以及程序错误调试
  详细解决方案

solidity-以太坊区块链Truffle-webpack开发入门 (三) 合约的认识以及程序错误调试

热度:19   发布时间:2024-01-24 09:34:58.0

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函数中调用的。

 

上面的三个例子我们都假设自己在犯错误,写错误的代码,不要弄混了。