(图片出自网络,版权归原作者所有)
上一篇刺猬文中,提到了合约进行编码后的abi码问题。之后有朋友问我,abi码是干啥的?这个问题问的我无从回答——大家只要在百度上搜索一下,就有一大把一大把的现成答案啊。
这个朋友又问了我一句?这个abi编过的码,要如何解析呢?
这个问题真是把我问住了。我想了半天,最后只能给他说,你还是去百度吧。
后来我也在百度上搜了一下,发现百度搜到的都是如何编码,很少有如何对交易的payload字段进行解码的方法,而且绝大多数都是讲完了编码原理以后,带一句:“解码就是编码的逆过程,只要逆向一下就可以了”。
我晕,难道是我的智商有问题,没办法理解如此简单的实现到底如何实现的么?
还是说,他们语焉不详的原因,是他们也没搞清楚?
亦或者文章都是你抄我,我抄你,第一个人这么说了,后面的人也就全这么说了呢?
这几天添加扩展字段,对于这个问题刚好有点心得,于是写出来和大家分享一下。
要对这个abi码进行解码,方法大致有2种:
第一种处理方法:直接使用JS函数来进行解码:
在以太坊中,对于使用控制台调用指定合约函数的,会使用JS来进行编码。例如我们在控制台中调用一个合约的test方法:
以太坊调用js编码后的结果将是类似于:
msg="{\"jsonrpc\":\"2.0\",\"id\":20,\"method\":\"eth_sendTransaction\",\"params\":[{\"data\":\"0xf9fbd554000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000046861686100000000000000000000000000000000000000000000000000000000\",\"from\":\"0xdc107ca137adfc975b1edb5ec96f51627c4bbda7\",\"to\":\"0x274863146a4471528f895a57ee1a128cfc705c0b\"}]}"
这样的格式。其中data后面的编码,就是以太坊调用js中的函数来实现的。
这个js文件在go-ethereum\internal\jsre\deps\web3.js。而编码函数是:
相应的解码函数:
从这些代码中可以看出:JS既具有编码功能,又具有解码功能,完全可以实现编解码功能。例如我们在Jsre.go文件中的Evaluate函数中添加如下代码,就可以解码数据了:
这段代码,我们就可以将一串编过码的数据“0x657468657265756d000000000000000000000000000000000000000000000000”,解码成“ethereum”了,并且调用Get函数可以得到这个解过码的数据。
但是在go语言下调用JS代码个人觉得比较麻烦,需要修改jrse中的代码。
第二种处理方法:使用以太坊的go代码直接进行解码:
在以太坊代码中有个包叫做abi的,它的目录是go-ethereum\accounts\abi。这个包
专门用来进行abi的相关处理。其中在abi.go文件中有一个函数 func (abi ABI) Pack(name string, args ...interface{}) ([]byte, error) 和 func (abi ABI) Unpack(v interface{}, name string, output []byte) (err error)是专门用来进行编码和解码的。只要你先将相应的abi结构传入进去,就可以对于编码后的格式进行解码了。
听到这里肯定很多人觉得这多简单啊,我调用unpack函数来解码不就好了么?
真的是这么简单么?
我们来看一下unpack的代码:
1:首先它判断你传入的数据长度是否合理,也就是数据长度是否能被32整除。
2:其次它判断你所需要解码的方法名是否存在,这个判断需要先调用JSON函数,将abi的json格式导入进来,从这个json格式中去查找是否存在你所需要解码的函数。
3:判断这个方法的输出参数数量是否大于1。
等等,好像不大对啊,我们是要对用户的调用函数和传入的参数所编码来进行解码的,干嘛判断这个方法的输出参数呢?
大家看看代码就知道了:对于方法来说,是判断输出参数的;对于事件来说,是判断输入参数的。
那么对于方法来说怎么才能进行解码呢?
我的做法是:重新对这个函数的实现方法进行了修改。
上面的代码就是我添加的代码,使用这个代码可以对于函数的输入参数进行解码。
这个代码目前已经在播客链中使用了的。
如果这种尝试对你有所启发,欢迎你留言告诉我。