本篇文章会从最基础的概念开始讲如何进行ens合约调用编程
以太坊系统
刚接触以太坊时,我们会频繁看到geth,web3.js, contract等概念。
-
geth
geth是以太坊的golang节点。这是以太坊中最核心的部分,大量的节点组成了以太坊网络,geth负责与其他节点进行通信,获取和更新全网账单。我们对链上合约的调用,最后都需要通过geth节点提交到区块链上。 -
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,那么这两个东西有什么用?要了解这个,必须先了解以太坊合约调用编码。
-
以太坊合约调用标准
每一个已经部署的合约都是链上的一个block里的二进制代码。我们调用合约的代码时,也要给出一个符合标准的二进制代码。
调用某一个contract的函数需要提供(合约地址,函数签名,参数)。每个部分都要按照标准格式编码,这个过程普通用户掌握起来很困难,而且容易出错。
为了方便用户,web3.js封装了这个编码过程,只要我们给出合约地址和合约ABI文件,在我们通过web3调用合约时,web3会根据ABI文件将我们的调用编码成正确格式发送给geth。 -
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);
}
- 如果调用合约
这里我们继续以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文件,我们自己也能够生成交易数据。
以太坊有一个调用函数的标准格式,当我们发出一个合约调用时,交易里其实包括:
- Keccak编码函数签名
- 对于定长参数,直接编码值
- 变长参数一律放在最后,原位置只放一个偏移量
举一个列子,当我们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个字节
这里有该规则的示例解释。
网友评论