当前位置:   article > 正文

Qtum量子链研究院:x86 SimpleABI 协议与 abigen 工具

abigen.exe




Qtum abigen


这是 Qtum x86 合约的一个轻量级的 ABI。这个 ABI 规范称为 Simple ABI。


SimpleABI 只编码字段值(flat values)和简单数组(simple arrays)。它不是智能合约 ABI 的终极状态,只是实现起来非常简单,最重要的是使用起来非常顺手。


abigen 可以以下 3 种方式运行:

1

Dispatcher -- 生成代码,用于解码 SCCS 上的 ABI 数据并调用适当的函数

2

Caller -- 为指定的合约生成代码,可使用 SimpleABI 轻松调用外部合约

3

Encoder -- 用一系列参数生成合约调用的数据。人们可以简单地用 sendtocontract 等即可调用 SimpleABI 合约


与 Solidity 的区别是什么?


Solidity 调用外部合约的 ABI 直接内建在语言中。Solidity 专门用于构建智能合约,因此这么设计是有一定道理的。

但是,x86 VM 支持多种不同的语言,这些语言不是专门为智能合约而设计的。这意味着我们必须在这些现有语言之上构建 ABI。使用复杂的语言分析库等或许可以自动构建适当的 ABI,但很难用,限制大,且不能在语言之间移植。


我们正在设计的这个新VM,应该是可以从你的 C 语言合约调用另一个 C 语言合约,也可以从你的 C++ 合约调用一个 Rust 合约等等。


这个 ABI 比 Solidity 的 ABI 简单得多,但与 Solidity 不同,它是显式调用的,即它只能调用在 ABI 文件中直接指定的函数。因此,这个 ABI 往往需要更多的模板代码来处理简单的事情,比如调用函数,甚至只是解码发送的 ABI 数据......所以,一个理想的工具是代码生成。我们可以使用模板代码来生成函数,这样开发人员实际中使用函数只要几行代码。


与 EVM 暴露“调用数据”的方式不一样的是,x86 VM具有“智能合约通信堆栈”(SCCS,smart contract communication stack)。我们只需要一个在合约之间传递和返回数据的栈,没必要解析一个大的扁平字节数组。


这大大简化了智能合约的实现,这个 ABI 就是为了利用这一点而设计的。SCCS 可以将每个参数视为栈上的一个子项,而不需要解码一大块数据。这也使得调用合约变得更容易,因为不再需要构造一大块数据。大块数据往往需要分配足够内存,将所有元素放入一个连续的内存区域;而 SCCS 可以使用许多较小的内存实现。


ABI 规范


ABI 规范很简单,每个函数 1 行。它还充当合约函数前后堆栈的表。即使不使用abigen,这也是一种有用的规范,可以手动实现对合约数据的编码解码的堆栈操作。

类似 ERC20 的接口示例:

  1. ERC20Interface
  2. # The first non-comment line is the name of the interface and used for all codegen prefixes
  3. # this is a comment
  4. selfBalance -> balance:uint64
  5. address:UniversalAddress balance:fn -> balance:uint64
  6. addressTo:UniversalAddress value:uint64 send:fn -> newFromBalance:uint64 newToBalance:uint64
  7. address:UniversalAddress buyTokens:fn -> newBalance:uint64 -- payable
  8. 复制代码

也可以使用数组:

  1. ArrayExample
  2. #declares someFunction takes an array of 20 bytes exactly
  3. someData:uint8[20]:fixed someFunction:fn -> void
  4. #declares someFunctionDynamic that takes an array of no more than 20 bytes
  5. someData:uint8[20]:max someFunctionDynamic:fn -> void复制代码

支持的基本类型:

  • uint8

  • uint16

  • uint32

  • uint64

  • int8

  • int16

  • int32

  • int64

  • char

  • void -- 仅对返回数据有效。对应无返回数据

  • fn -- 特殊

更高级的类型:

  • UniversalAddress

基本类型尽可能传值使用。高级类型传引用。数组传引用,并指向值,值包括高级类型。

数组类型:

  • fixed(默认) - 数据必须是指定的确切大小

  • max(指定最大) -数据不能大于指定的大小。如果它较大,则会触发错误

  • dynamic(动态) - 任何长度都有效(使用前 uint8[])有效

  • clip(截断) - 如果数据大于指定的大小,那么它会被截断,不会触发任何错误


函数编号

函数编号的构造方式与 Solidity 类似。 sha256 哈希由函数行和接口名称组成,哈希值截断到后面 4 个字节作为函数号。


内存分配

大于 256 字节的数组都使用堆分配而不是栈。


接口

一个合约可以实现多个接口。每个接口使用该接口名称前缀来生成代码。对于具有相同名称的多个函数,只要它们由不同名称的接口定义,就可以同时存在于一个合约内。

包括其他接口:

MyContract
:interfaces ERC20, ERC721, MyParentContract

abigen 自动查找当前目录中的 ABI 文件名,并实现指定全局接口目录的方法。


语言

现在只支持 C. 之后会支持 Rust。

示例(手动生成) C代码:

  1. struct simpletoken_Send_Params{
  2. UniversalAddressABI* address;
  3. uint64_t value;
  4. };
  5. struct simpletoken_Send_Returns{
  6. uint64_t recvvalue;
  7. uint64_t sendervalue;
  8. };
  9. void decodeABI(){
  10. //format: address:address value:uint64 SEND -> sendervalue:uint64 recvvalue:uint64
  11. //format: address:address BALANCE -> balance:uint6
  12. //format: SELFBALANCE -> balance:uint64
  13. uint32_t function = 0;
  14. if(qtumStackItemCount() == 0){
  15. //fallback function...
  16. }
  17. QTUM_POP_VAL(function);
  18. switch(function){
  19. case CONTRACT_SELFBALANCE:
  20. {
  21. uint64_t resBalance;
  22. selfBalance(&resBalance);
  23. QTUM_PUSH_VAL(resBalance);
  24. return;
  25. }
  26. case CONTRACT_BALANCE:
  27. {
  28. UniversalAddressABI address;
  29. QTUM_POP_VAL(address);
  30. uint64_t resBalance;
  31. balance(&address, &resBalance);
  32. QTUM_PUSH_VAL(resBalance);
  33. return;
  34. }
  35. case CONTRACT_SEND:
  36. {
  37. struct simpletoken_Send_Params params;
  38. UniversalAddressABI __tmp1;
  39. params.address = &__tmp1;
  40. QTUM_POP_VAL(params.value);
  41. QTUM_POP_VAL(__tmp1);
  42. struct simpletoken_Send_Returns returns;
  43. send(&params, &returns);
  44. QTUM_PUSH_VAL(returns.sendervalue);
  45. QTUM_PUSH_VAL(returns.recvvalue);
  46. return;
  47. }
  48. default:
  49. qtumError("Invalid function");
  50. return;
  51. }
  52. }
  53. //format for this:
  54. //address:address value:uint64 SEND -> sendervalue:uint64 recvvalue:uint64
  55. struct QtumCallResultABI simpletoken_Send(const UniversalAddressABI* __contract, uint64_t __gasLimit,
  56. const struct simpletoken_Send_Params* params,
  57. struct simpletoken_Send_Returns* returns
  58. )
  59. {
  60. if(__gasLimit == 0){
  61. __gasLimit = QTUM_CALL_GASLIMIT;
  62. }
  63. qtumStackClear();
  64. QTUM_PUSH_VAL(*params->address);
  65. QTUM_PUSH_VAL(params->value);
  66. uint32_t f = CONTRACT_SEND;
  67. QTUM_PUSH_VAL(f);
  68. struct QtumCallResultABI result;
  69. qtumCallContract(__contract, __gasLimit, 0, &result);
  70. if(result.errorCode != 0){
  71. return result;
  72. }
  73. QTUM_POP_VAL(returns->recvvalue);
  74. QTUM_POP_VAL(returns->sendervalue);
  75. return result;
  76. }复制代码


其他

对于不支持内置数组大小的语言,不定长数组数组也会有个 "length" 参数暴露给 Caller 和 Dispatcher。



声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/不正经/article/detail/468370
推荐阅读
相关标签
  

闽ICP备14008679号