ENS编程指南

本篇文章会从最基础的概念开始讲如何进行ens合约调用编程

以太坊系统

刚接触以太坊时,我们会频繁看到geth,web3.js, contract等概念。

  1. geth
    geth是以太坊的golang节点。这是以太坊中最核心的部分,大量的节点组成了以太坊网络,geth负责与其他节点进行通信,获取和更新全网账单。我们对链上合约的调用,最后都需要通过geth节点提交到区块链上。

  2. web3.js
    geth启动后会对外暴露出一个rpc接口,通过http的方式接收消息。
    web3.js就是一个处理与geth通信解析的库,当我们启动一个geth节点,并将一个web3实例连接上该geth节点后。我们可以调用web3的函数,web3会将请求编码然后发送给geth,再有geth将交易提交到链上。

geth里默认集成了web3.js,如果已经有一个运行中的geth实例,我们可以通过

    geth attach

去连接,然后我们会看到一个js命令行,这里面就已经有一个连接上geth的web3实例了。

我们可以简单的call一些web3的api看看效果。


image.png

合约的调用

调用合约时,我们经常需要知道合约的地址和合约的ABI,那么这两个东西有什么用?要了解这个,必须先了解以太坊合约调用编码。

  1. 以太坊合约调用标准
    每一个已经部署的合约都是链上的一个block里的二进制代码。我们调用合约的代码时,也要给出一个符合标准的二进制代码。
    调用某一个contract的函数需要提供(合约地址,函数签名,参数)。每个部分都要按照标准格式编码,这个过程普通用户掌握起来很困难,而且容易出错。
    为了方便用户,web3.js封装了这个编码过程,只要我们给出合约地址和合约ABI文件,在我们通过web3调用合约时,web3会根据ABI文件将我们的调用编码成正确格式发送给geth。

  2. ABI

合约的ABI其实就是这个合约的接口的json表示,最终目的是方便web3这样的库函数对我们的调用进行编码。
以ENS合约的abi为例:

    {
      "constant": false,
      "inputs": [
        {
          "name": "_hashes",
          "type": "bytes32[]"
        }
      ],
      "name": "startAuctions",
      "outputs": [],
      "payable": false,
      "type": "function"
    },

这段代码是官网ens里的ensutils.js里的ensRegistra合约ABI的一部分。我们可以看到,ensRegistra合约里有这个函数

function startAuction(bytes32 _hash) registryOpen() {
    var mode = state(_hash);
    if (mode == Mode.Auction) return;
    if (mode != Mode.Open) throw;

    entry newAuction = _entries[_hash];
    newAuction.registrationDate = now + totalAuctionLength;
    newAuction.value = 0;
    newAuction.highestBid = 0;
    AuctionStarted(_hash, newAuction.registrationDate);
}
  1. 如果调用合约

这里我们继续以ensutils.js为例,看下如何通过js来调用合约。

  • 首先它直接给出了ens相关合约的ABI json数据

  • 然后它调用web3的contract,直接指向已经部署的ens合约的地址

    var ens = ensContract.at('0x314159265dd8dbb310642f98f50c066173c1259b');
    //注意,这里需要使用者先提供一个已经连接上节点的web3对象
    //然后以合约abi为参数创建一个contract对象,最后将该对象指向mainnet上的合约地址
    //ens对象现在就成为指向mainnet上已经部署的ens合约的一个引用。
    
  • 接下来我们就可以直接call这个contract引用的函数

  • web3会将调用编码后发送给geth

  • geth将交易提交到网络,矿工会在EVM里执行调用,将结果写入链上

附录

其实,如果我们知道函数签名,不需要ABI文件,我们自己也能够生成交易数据。
以太坊有一个调用函数的标准格式,当我们发出一个合约调用时,交易里其实包括:

  1. Keccak编码函数签名
  2. 对于定长参数,直接编码值
  3. 变长参数一律放在最后,原位置只放一个偏移量

举一个列子,当我们call一个函数f,其签名为

  f(uint256,uint32[],bytes10,bytes)

我们发出的调用是:

  f(a, b, c, d)

最终提交的交易数据是:

0x8be65246
0000000000000000000000000000000000000000000000000000000000000123
0000000000000000000000000000000000000000000000000000000000000080
3132333435363738393000000000000000000000000000000000000000000000
00000000000000000000000000000000000000000000000000000000000000e0
0000000000000000000000000000000000000000000000000000000000000002
0000000000000000000000000000000000000000000000000000000000000456
0000000000000000000000000000000000000000000000000000000000000789
000000000000000000000000000000000000000000000000000000000000000d
48656c6c6f2c20776f726c642100000000000000000000000000000000000000
  • 首先是函数名称的哈希值取前4个byte


    image.png
  • 第一位是个定长参数(uint245),直接写的参数值0x123

  • 第二位是个变长参数(uint32[]),放在后面,只填写了偏移量80,就是128bytes,注意上图每一行是32bytes,所以该参数实际放在第五行(从0开始算)

  • 第三位是定长参数bytes10,直接写了它的ASCII编码

  • 第四位又是变长参数(bytes),放在e0,也就是224bytes,第八行

  • 第五行之后是之前第一个变长参数的表示,首先第五行记录了2,表示该参数长度为2,两个uint32

  • 第六,七行就是这两个uint32的值

  • 第八行是第二个变长参数的长度,13,表示13个字节,也就是第九行从左往右13个字节

这里有该规则的示例解释。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容