前言:

在使用solidity写智能合约的时候,会使用到revert和require来进行断言,比如:

require(tokenOwner[tokenId] == 0x0,'this is not the first create');

在上面的断言中,只有当你满足了tokenOwner[tokenId] == 0x0这个 条件,你才能继续往下执行,否则就会报错“this is not the first create”。

然后当我们使用remix这个编译器的时候,是能够在出错的时候得到reason string这个错误信息的,如下:

Transact to  token.create errored:VM error:revert.
revert The transaction has been reverted to the initial state.
Reason provided by the contract: “this is not the first create”. Debug the transaction to get more information.

在remix-ide中当transaction出错,即require断言没通过时,在terminal处会返回这样的信息,但是如果你在geth私有链中运行的时候,你只能知道你的transaction失败了,但是这个时候我们更多的是希望知道是哪里出错了,而不是仅仅得到交易失败的结果。

目的:

就是查找如何能够在使用geth搭建的私有链及web3js来实现revert/require的reason string的获得。

过程:

首先去查看了remix-ide的源码,然后发现这个返回的错误信息是通过读取获得的
网址:https://github.com/ethereum/remix/blob/73849adc6bf0eb5f1b8198842cfb9a8f417264b9/remix-lib/src/execution/txExecution.js

然后在GitHub中的EIP758(https://github.com/ethereum/EIPs/blob/master/EIPS/eip-758.md)可以看见这的确是一个没有实现的功能,就是在remix中能够返回require和revert中的信息,但是geth是会把这个信息直接扔掉的。除此之外,从EIP758也知道,如果你智能合约中的函数有返回值(return data),那么也是会被抛弃的。

⚠️但是EIP758一直讲的都是return data的问题,但是很多地方谈reason string的时候又把他们合在一起,可能两者的实现是一起的吧!!!!!
这个好像就是大家也都发现的问题,就是geth是会把内部得到的返回值(或者require和revert中的信息reason string)信息直接扔掉的,但是后面EIP758提出了解决了这个问题的方案:
This EIP proposes that callers should be able to subscribe to (or poll for) completed transactions. The Ethereum node sends the return data to the caller when the transactions are sealed.
目前如果当你的函数是通过eth_sendTransaction or eth_sendRawTransaction RPC request来执行的,那么外部访问者是没有方法获得return data的。

解决方法是在web3js中的函数eth_subscribe中添加查看交易的return data的功能,详细情况自己看。

后面在GitHub中的确有看见别人问和我一样的问题:https://github.com/ethereum/go-ethereum/issues/15721,是2017/11/21号的时候问的,那时候是说还没有办法:
Directly - not yet, pending ethereum/solidity#1686.
You should be able to step it through a debugger, like the one in Remix.
您应该能够通过调试器进行调试,比如Remix中的调试器。其实就是使用remix,但我就是想知道能不能用geth。

参考:https://github.com/ethereum/go-ethereum/pull/16424

https://github.com/ethereum/EIPs/issues/963

这里有详细解释了要如何实现EIP758这个提案:
This functions in two different ways,:
one
via the JSON-RPC command eth_subscribe. If the client has a full-duplex
connection (ipc or ws), it can issue an eth_subscribe command with
params= [“returnData”].For any transactions submitted by the same client
after that, the tx hash is saved internally while the transaction is
pending, and the appropriate client is notified when it completes.
就是如果你的连接方式是ipc or ws,那你就可以使用命令eth_subscribe,参数指定是“returnData”,即返回信息。然后当交易pending的时候,tx hash并不会给你,而是会保存在内部,直到成功写上区块的时候才会通知你信息
the
client has only a half-duplex connection (http), then eth_subscribe is
not allowed since there are no callbacks. Instead,
eth_newReturnDataFilter is sent, the return data of subsequent
transactions are stored up in a queue internally, and returned to the
client when eth_getFilterChanges is called with the same subscription
id.
如果你使用的是http的话,就不能使用命令eth_subscribe,要变成使用eth_newReturnDataFilter,并且返回数据会以队列的形式保存在内部,知道你调用命令eth_getFilterChanges才会返回给你,不像上面那个直接给你

My
proposal is to add a from field in the params of
eth_newReturnDataFilter, so that they only listen to transactions sent
from a specific address, or a list of addresses--not everyone's
transactions. (I've also mentioned an alternative of adding a subID
param to eth_sendTransaction and eth_sendRawTransaction, which would
also solve the problem.)
就是eth_newReturnDataFilter会监听所有的返回数据,可以通过设置{from:…}来限制只监听哪个from的返回数据,或者在发送数据时(即使用eth_sendTransaction和eth_sendRawTransaction)添加subID参数??

Another
improvement I plan to add soon is an optional bool param noEmpty, which
can suppress notifications for transactions where there is no return
data. For noEmpty=false, the client will still be notified when the
requested transactions complete, but the return data field will be []
noEmpty=true,就是在当没有返回数据时就可以不用返回东西,否则仍会返回一个[]空数组

因为EIP758的status是Draft,说明该建议在ethereum还没有真正实现,还在实现中,我们只好等待了!!!!!!

EIP Status Terms

Draft - an EIP that is undergoing rapid iteration and changes
Last Call - an EIP that is done with its initial iteration and ready for review by a wide audience
Accepted - a core EIP that has been in Last Call for at least weeks and any technical changes that were requested have been addressed by the author
Final (non-Core) - an EIP that has been in Last Call for at least weeks and any technical changes that were requested have been addressed by the author.
Final (Core) - an EIP that the Core Devs have decide to implement and release in a future hard fork or has already been released in a hard fork
Deferred - an EIP that is not being considered for immediate adoption. May be reconsidered in the future for a subsequent hard fork.

这个建议的依据是EIP 658

https://github.com/ethereum/EIPs/blob/master/EIPS/eip-658.md

EIP 658是建议将return data写到 transaction receipts,但是这样会导致 return data is not charged for (as it is not stored on the blockchain), so adding it to transaction receipts could result in DoS and spam opportunities.
所以最后的解决方案是在transaction receipts加上一个状态status,通过eth.getTransactionReceipt()查看,status为0x0,则交易失败;status为0x1,则交易成功。该建议的status是final,说明已经实现。

但是后面有其他的惊喜:
https://github.com/trufflesuite/ganache-core/issues/116
https://github.com/trufflesuite/truffle/issues/976

在这里我们能够发现现在ganache-core和truffle的新版本v5.0(beta)已经实现了能够查看reason string的功能,但是我还没试过,大家可以自己去看看(但是我就是想使用geth的)

  • Remix gets the reason string because it runs ethereumjs-vm in the browser and can just grab the return data directly out of the vm. The clients' default behavior is to throw that data away rather than include it in the response, so we need changes at that layer to be able to do the same thing.

  • Ganache just merged a PR last week that attaches return data to the error message on eth_call. That change is queued for the next release. As soon as it becomes available we'll begin work on integrating this into the next version of truffle-contract.

通过查看ganache-core的代码来看它是如何实现这个操作的:

https://github.com/trufflesuite/ganache-core

感觉自己好像低估了ganache的功能,后面好好学学。

最终:

终于查明这个功能还没有实现,只能等待了
https://github.com/OpenZeppelin/openzeppelin-solidity/issues/917

web3.js is not the culprit. There is actually no way to retrieve the revert reason from a node. 

EIP 758 is a proposal to solve that, and there's Geth (ethereum/go-ethereum#16424) and Ganache (trufflesuite/ganache-core#116) issues to implement it.

即上面提到的:
https://github.com/ethereum/go-ethereum/pull/16424(ethereum/go-ethereum#16424)

https://github.com/trufflesuite/ganache-core/issues/116(trufflesuite/ganache-core#116)

I guess we'll have to wait and see how that plays out.

looks like truffle 5.0 will allow for this to be possible: see the relevant beta release notes.

https://github.com/trufflesuite/truffle/releases/tag/v5.0.0-beta.0#reason-strings(truffle的新版本)

truffle v5.0.0-beta.0 是如何解决这个问题的呢:

Revert with reason strings!!

Find out the reason. At the moment this feature is only supported by the ganache-cli client (>= 6.1.3). Parity and Geth are still working out their implementations.

Solidity中运行:

require(msg.sender == owner, 'not authorized');

再在Javascript中运行

try {
await example.transferToSelf({from: nonOwner})
} catch (err) {
assert(err.reason === 'not authorized');
assert(err.message.includes('not authorized');
}

软件版本阶段说明

Alpha版: 此版本表示该软件在此阶段主要是以实现软件功能为主,通常只在软件开发者内部交流,一般而言,该版本软件的Bug较多,需要继续修改。
Beta版: 该版本相对于α版已有了很大的改进,消除了严重的错误,但还是存在着一些缺陷,需要经过多次测试来进一步消除,此版本主要的修改对像是软件的UI。
RC版: 该版本已经相当成熟了,基本上不存在导致错误的BUG,与即将发行的正式版相差无几。
Release版: 该版本意味“最终版本”,在前面版本的一系列测试版之后,终归会有一个正式版本,是最终交付用户使用的一个版本。该版本有时也称为标准版。一般情况下,Release不会以单词形式出现在软件封面上,取而代之的是符号(R)。

所以该truffle的beta版还是测试版

其他:

(1)

一开始以为eth_subscribe已经实现了上面的功能,所以去查看并希望使用web3的1.0版本,在这里遇见了个问题:为了实现上面的函数eth_subscribe,来获得solidity中require里的断言失败信息:

新生成一个文件remix-revert,将之前运行智能合约的文件夹中的package.json复制粘贴过去,然后将里面的web3版本改为1.0.0,而不是还使用0.20.1,然后使用npm init来把package.json中的模块都安装下来。但是后面发现这样不能成功,老是报错,所以打算还是直接一个个安装吧。

npm init :生成package.json
安装时出现了这样的错误:
npm ERR! Unexpected end of JSON input while parsing near ‘...xpress/-/express-2.1.
解决方法是:
npm cache clean —-force

为什么要改web3
因为要试着使用web3.eth.subscribe(“pendingTransactions”)来获得solidity中require中的信息,但是这个是1.0版本才有的功能,0.20中报错

(2)
再后面我看见一个问题就是有人问他发现就是如果使用了reason string,它所使用的gas会变得很高,就是使用reason string是一件十分奢侈的事情:
https://github.com/ethereum/solidity/issues/4588

It will not be part of storage, but more likely be stored as a combination of push and mstore
原因可能就是在编译的过程中,需要将这些reason string压到内存当中,这个过程当然是会花费gas的。所以我的交易总是频繁地出现out of gas的原因很有可能就是使用了require的reason string的原因了,所以我的tx的gas才会这么高

https://github.com/ethereum/solidity/issues/4774

说明gas高的原因
Currently all strings are broken up into 32 byte items, PUSHd and MSTOREd.
The helper function CompilerUtils::storeStringData is doing this, however it has a fixed rule for only doing it for >128 characters.
It should be exposed to the optimiser (or user) to decide based on cost.

solidity return data和revert/require的reason string的获得的更多相关文章

  1. org.hibernate.id.IdentifierGenerationException: Unknown integral data type for ids : java.lang.String

    org.hibernate.id.IdentifierGenerationException: Unknown integral data type for ids : java.lang.Strin ...

  2. hibernate报错Unknown integral data type for ids : java.lang.String

    package com.model; // Generated 2016-10-27 14:02:17 by Hibernate Tools 4.3.1.Final /** * CmDept gene ...

  3. Python入门篇-递归函数Recursion

    Python入门篇-递归函数(recursion) 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 一.递归概述 (1)函数直接或者间接调用自身就是递归: (2)递归需要有边界,递归 ...

  4. Python 练习题总结(待续)

    1.编写一个函数,接受一个参数n,n为正整数,左右两种打印方 式.要求数字必须对齐 正三角 倒三角 实现思路: 思路1.一行一行打印,前面追加空格,每一个空格的宽度等于数字字符串的宽度 #正三角打印d ...

  5. 【原创分享·微信支付】 C# MVC 微信支付教程系列之扫码支付

    微信支付教程系列之扫码支付                  今天,我们来一起探讨一下这个微信扫码支付.何为扫码支付呢?这里面,扫的码就是二维码了,就是我们经常扫一扫的那种二维码图片,例如,我们自己添 ...

  6. FileUtil(from logparser)

    import java.io.BufferedReader; import java.io.BufferedWriter; import java.io.ByteArrayOutputStream; ...

  7. PHP如何自动识别第三方Restful API的内容,自动渲染成 json、xml、html、serialize、csv、php等数据

    如题,PHP如何自动识别第三方Restful API的内容,自动渲染成 json.xml.html.serialize.csv.php等数据? 其实这也不难,因为Rest API也是基于http协议的 ...

  8. ASP.NET使用Memcached

    一.安装Memcached及Memcached配置和状态查询 要想使用Memcached做缓存首先需要安装Memcached服务,安装方法如下: memcached.exe下载 保存至相应路径 打开c ...

  9. ASP.NET MVC EF 中使用异步控制器

    最近悟出来一个道理,在这儿分享给大家:学历代表你的过去,能力代表你的现在,学习代表你的将来. 十年河东十年河西,莫欺少年穷 学无止境,精益求精   为什么使用异步操作/线程池 ASP.NET MVC ...

随机推荐

  1. 类Exception_A继承Exception,类Exception_B继承Exception_A,请问执行此段代码的输出是什么?

    @Test public void Test_Exception() { try { throw new ExceptionB("A"); } catch (ExceptionA ...

  2. Oracle入门《Oracle介绍》第一章1-4 Oracle 用户管理

    1.Oracle 默认用户 只有用合法的用户帐号才能访问Oracle数据库 Oracle 有几个默认的数据库用户 数据库中所有数据字典表和视图都存储在 SYS 模式中.SYS用户主要用来维护系统信息和 ...

  3. 使用Nexus2.x为Maven3.x搭建私服构件仓库

    前言 在笔者的前一篇博文<Use Maven3.x>中,笔者详细的讲解了如何通过使用Maven3.x来构建及管理你的项目.其中笔者提到过些关于私服的概念,但却没有明确的对私服做出详细的阐述 ...

  4. 内存分析工具-MAT(Memory Analyzer Tool)

    内存分析工具-MAT(Memory Analyzer Tool) 首先查看如下代码,main函数中有一个成员变量map,map里被循环放入对象Hanson,hanson持有姓名和age还有friend ...

  5. Nhibernate学习的第二天

    Fluent-Nhibernate   网站:http://www.fluentnhibernate.org/ 使用NuGet下载Fluent-Nhibernate. 1.创建实体类 2.创建实体类映 ...

  6. sublime text3快速生成html头部信息

    1.在网站开发过程中尤其写前台页面时要写头部很麻烦,怎么办呢?直接生成不更好吗? 这是快速生成的信息 2.方法: ctrl+shift+P打开命令面板 点击安装控制器 3.输入emmet 安装(以下图 ...

  7. 前端面试(原生js篇) - 精确运算

    一.面试题 问:开发的时候有用到过 Math 吗? 答:很多啊.比如生成 GUID 的时候,就会用到 Math.random() 来生成随机数. 问:别的呢?比如向下取整.向上取整? 答:向下取整是  ...

  8. pycharm技巧

    常用快捷键 1.Ctrl + Enter:在下方新建行但不移动光标: 2.Shift + Enter:在下方新建行并移到新行行首: 3.Ctrl + /:注释(取消注释)选择的行: 4.Ctrl + ...

  9. 【代码笔记】Web-ionic-安装及第一个app

    一,下载ionic v1.0.1版本,下载地址为:ionic-v1.0.1.zip. ionic 最新版本下载地址:http://ionicframework.com/docs/overview/#d ...

  10. js+bootstrap实现分页页码

    制作page.jsp,在其他页码引入,只需把最外层的form标签的id设置为myForm: 其中 totalPages:共有多少页:totalElements:共有有多少条记录:currentPage ...