官方文档讲得不全也不是很清楚,特别是动态类型string。这里补充。
以太坊的智能合约ABI编码,官方文档和说明:https://solidity.readthedocs.io/en/v0.6.8/abi-spec.html
一个实用的工具,可直接生成ABI编码。
比如下图使用
这里侧重于solidity智能合约的构造方法的ABI编码构造。
普通方法的编码官网说得很清楚了。
构造函数的ABI编码由两部分构成,一是编译后的智能合约代码,而是构造函数的参数。
constructor (uint256 _deadline, uint32 _reward, string memory _code) public{
现在的问题是,如果我们使用后台提交一个智能合约,需要提交该智能合约的构造函数的参数的值,但是,上面第一步的所复制的编码中不包含参数的值,因此,需要我们自己构造构造函数的值的ABI编码。
换一种说法描述上面的问题,如果使用remix部署我们上面的智能合约的话,因为只有一个构造函数,且是带参数的,因此deploy前需要填写这些参数的值,如下图。现在remix自动帮我们对参数进行ABI编码了,并将它追加在第一步所复制的数据的后面。如果我们自己在后台构造和部署智能合约,如何进行ABI编码构造方法的参数?
0x0000000000000000000000000000000000000000000000000000000000000003
,使用0填充,uint32同理,追加到前面的数中。第三个是string,动态类型,因此先填下面这个固定的数,以60为结尾,长度为64。0000000000000000000000000000000000000000000000000000000000000060
,变成:
为什么要追加这个"0000000…0060"呢?看下文。
4. 接下来追加string的长度(使用16进制表示)用0填充前面部分;再追加字符串的16进制表示,用0填充后面部分。
5. 因为0填充是以64长度为单位的,因此,当字符串的16进制编码长度操作了64长度,后面统一使用0填充。因此有
官网没有说清楚的是,为什么要追加上面的值0000000000000000000000000000000000000000000000000000000000000060
。原因是,string类型是动态类型,长度不是固定的,因此,需要将string数据从哪里开始的这个信息编码进去,这个信息我们称为offset。现在的offset=60(16进制)=96(10进制),我们的字符串的长度表示是使用十进制的,又因为我们需要编码的参数为(uint256 _deadline, uint32 _reward, string memory _code)
,前面两个参数+offset,相当于3个数,每一个数的长度是一样的,因此 ** 每一个数的长度为96/3 = 32。** 这就是我们的结论。
根据上面这个结论,如果所编码的参数变为四个(uint256 _deadline, uint32 _reward, uint32 other, string memory _code)
,那么string前面的offset变为32*4 = 128(10进制) = 80 (16进制)。使用线上工具验证结果一致:
在solidity中的bytes类型也是动态类型,编码方式跟string类似。
谢谢
c++代码
// deadline, uint256; reward, uint32,
string appendParams_ABIEncode(string contractCode, unsigned long long deadline, unsigned int reward, string evalCode) {
stringstream temp_time;
temp_time << setfill('0') << setw(64) << hex << deadline;
stringstream temp_reward;
temp_reward << setfill('0') << setw(64) << hex << reward;
stringstream temp_len;
temp_len << setfill('0') << setw(64) << hex << evalCode.length();
string code_hex = toHex(evalCode);
int padding_num = code_hex.length() % 64;
if (padding_num != 0) {
padding_num = 64 - padding_num;
}
string padding = string(padding_num, '0');
code_hex += padding;
contractCode = contractCode
+ temp_time.str()
+ temp_reward.str()
//后面是字符串,动态空间,所以要加这个,没在文档中找到这个定义。https://abi.hashex.org/
+ string("0000000000000000000000000000000000000000000000000000000000000060")
+ temp_len.str() + code_hex;
return contractCode;
}
调用:
const string compiledCode = "";
string requestData = appendParams_ABIEncode(compiledCode, 20200529124532, 20, "dsfasdjfkldsjkaflksdjfljsdflksdj");
cout << requestData << endl;