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

Prunella ·
更新时间:2024-09-20
· 773 次阅读

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

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


作者:大都废



Truffle-webpack solidity 调试 程序 以太坊 webpack

需要 登录 后方可回复, 如果你还没有账号请 注册新账号
相关文章