武汉链(基于 ETH)BSN 官方 DDC 链上数据解析
id:BSN_2021 公众号:BSN 研习社 作者:红枣科技史博涵
随着区块链概念的普及,这项新型技术逐渐走进大众视野。目前行业整体方兴未艾,数字商品、隐私保护、供应链金融等业务也在如火如荼的开展。为满足大众需求,使广大开发者可以低成本低门槛的对接区块链技术,BSN 在 2022 年 1 月 25 日推出了 BSN-DDC 基础网络(DDC 网络)。
DDC 网络由十多条技术体系完善并各具特点的 BSN 开放联盟链组成,通过部署 BSN 官方 DDC 智能合约,向有基于 DDC/NFT 技术的相关业务需求的平台方提供快速接入服务,使其可灵活地对 DDC/NFT 的生成、转移和销毁等操作进行管理。
为方便大家更好的了解和使用官方 DDC,我们将对 DDC 的交易信息进行详细解析。由于目前 DDC 网络中最为广泛使用的武汉链(基于 ETH)、文昌链(基于 IRITA)和泰安链(基于 FISCO BCOS)都集成了 EVM(以太坊虚拟机),所以这里的解析也使用以太坊中的标准方法。本文以武汉链为例,通过调用 DDC-721 合约的 mint 方法来生成一个 DDC,并解析其交易信息。武汉链中 DDC 官方合约的开源代码可以在https://github.com/BSN-DDC/wuhanchain/tree/master/ddc-contract中找到。
一、生成 DDC
生成 DDC 的流程大致如下:
请求者通过 RPC 网关发送已签名的交易请求到官方 DDC-721 合约
DDC-721 合约会调用 mint 方法进行 DDC 的生成
执行 mint 方法后会按顺序判断是否满足生成条件,然后会依次按顺序执行_mintAndPay 和_pay 方法。
在执行_pay 方法时,会调用计费合约中的 pay 方法,此时会进行业务费的扣除,即生成 1 个 DDC 需要扣除 1 元业务费,并且触发 Pay 事件
成功扣除业务费后,会继续执行 mint 方法生成 DDC,并且触发 Transfer 事件,最终完成本次交易。
本次示例中,填入的 DDC URI 的内容为:
成功生成 DDC 后,返回了交易 hash:
我们可以通过解析这个交易 hash 来获取需要的交易信息。获取信息分需要使用以下两种方法:
eth_getTransactionByHash
: 返回指定交易对应的交易信息eth_getTransactionReceipt
: 返回指定交易对应的回执信息
二、获取交易信息
(一)调用 eth_getTransactionByHash
请求信息:
响应信息:
从响应信息中的 result 里我们可以直观看到一些重要的数据:
blockHash
: 包含本次交易的区块哈希;blockNumber
: 包含本次交易的区块号;hash
: 本次交易的哈希值;from
: 发送方地址,指交易的发起者;to
: 这里的接收方地址不是生成 DDC 时提供的目标地址,而是交易发送方调用的 DDC-721 合约地址;gas
: 发送方提供的 gas 可用量。可以是系统自动计算得出,或者自行设置;gasLimit
: 表示本次交易可以发送的最大 gas 数量。如果链账户中的能量值余额小于这个数量,或者此笔交易消耗的 gas 超过这个数量,则此交易无法进行;gasPrice
: 本次交易的 gas 的价格;nonce
: 本次交易之前发送方已经生成的交易数量。为了防止交易重复进行,每个地址里的每笔交易必须有一个唯一的 nonce 数值。这个 nonce 值从 0 开始递增,每发送一笔交易,nonce 便加 1。本例中的 nonce 值 0x1 表示在此交易前发送方已完成了 1 笔交易;transactionIndex
: 交易在块中索引的位置。也就是说此交易在区块中排在第几个,这个值从 0x0 开始依次递增;value
: 发送的以太币数量。在开放联盟链中,由于禁止链账户之间对 gas 进行横向转移,此值为 0。
其他的信息还包括交易类型(type)以及交易签名(r、s、v)等,在这里就不深入讨论了。接下来我们重点解析一下交易信息中的 input 数据,看看这里面都包含了哪些重要信息。
(二) 解析 Input 内容
input 是随交易发送的数据。如果 input 的值为 0x,说明是非合约调用,否则是合约方法调用。
本例中调用了合法方法mint(address,string)
,得到的 input 值为:
此数据分为 2 个部分: 方法名称的哈希值+方法中的参数
1、方法名称的哈希值
占 4 个字节,是 mint(address,string)方法的签名编码。
具体计算方法为先对 mint(address,string)做 keccak256 计算,生成 32 位哈希值
然后再将此哈希值做 bytes4 计算,最终生成 0xd0def521(如下图所示)
2、方法中的参数
此部分数据的结构根据方法本身的参数设置来决定。本例中的方法有两个参数: 生成 DDC 的目标地址(address)以及填入的 ddcuri 的内容(string)
address
: 占 32 个字节,而武汉链的地址为 20 字节,所以高位自动补零,显示为:
string
: 剩余字节,本例中的输入是 ddcuri
此数据乍看之下非常混乱。实际上是把字符串转换成 16 进制表示,所以为了方便辨认和阅读,需要将其转换为字符串形式。转换后为:
可以看出,花括号中的内容与生成 DDC 时输入的 ddcuri 内容完全一致。
三、获取交易回执
(一)调用 eth_getTransactionReceipt
请求信息:
响应信息:
这里的响应信息里包含的参数很多都与交易信息中一致,比如块哈希、块号、交易哈希、交易的发送方地址和接收方地址、交易索引等。
其他的参数包含的信息如下:
contractAddress
: 如果本次交易是部署合约,那么就会返回生成的合约地址,如果是其他类型的交易,那么会返回 nullcumulativeGasUsed
: 交易所在块消耗的 gas 总量。这里的 gas 总量指的是根据 transactionIndex 来排列的到此交易为止的全部交易消耗的所有 gas,也就是这个区块中此交易与排在它前边的所有交易消耗的 gas 之和。effectiveGasPrice
: 本次交易的实际 gas 价格。gasUsed
: 本次交易消耗的 gas 用量。本次交易消耗的能量值为: gasUsed * effectiveGasPrice。 再根据武汉链中能量值的比例为 1 元 = 4,200,000,000,000,000 可以计算出本次交易消耗的实际费用。logsBloom
: bloom 过滤器,轻客户端用来快速提取相关日志。如果这个值是全零,说明交易日志不存在,即”logs”: []status
: 0x1 表示交易成功,0x0 表示交易失败
在交易回执信息中,最重要的数据是 logs,接下来我们看看 logs 中都包含了哪些重要信息。
(二)logs 解析
智能合约通过事件(event)来产生日志(log)。日志中存储的 gas 费用要比合约的存储便宜很多(日志每个字节花费 8 个 gas,而合约存储是每 32 个字节需要 20000 个 gas)。想要通过合约向用户返回数据,则需将数据以事件的形式传给用户,用户拿到交易回执后通过解析日志拿到数据。
在调用 eth_getTransactionByHash 方法得到的 Input 中,只能拿到调用合约以及 function 的信息,即合约在调用时的方法和输入的参数信息,而不能拿到 function 运行后内部产生的事件(事件不一定和 function 拥有相同名称和参数)。
比如在本例中,function 是mint(address,string)
,而需要触发的事件有两个,首先是调用 Charge 合约支付业务费的Pay(address indexed payer,address indexed payee,bytes4 sig,uint32 amount,uint256 ddcId)
事件,完成后,再触发生成 DDC 的 DDC-721 合约中的Transfer(address indexed from,address indexed to,uint256 indexed ddcId)
事件。
Logs 是由多个相同结构的 log 组合而成。log 中的第一个参数为 address,即触发事件的合约地址。在第一个 log 中,address 是0xca97bf3a19403805d391102908665b16b4d0217c
,即用来支付业务费的 Charge 合约的地址;第二个 log 中的 address 是0xad3b52b4f4bd9198dc69dd9ce4ac9846667461a2
,即生成 DDC 的 DDC-721 合约地址。
除此以外,logs 里还有还有块号、块哈希、交易哈希、交易索引、日志索引等在交易信息中也可以查到的信息,用来作为此笔交易在区块链中的标识。
在 logs 中,还有一个最为重要的数据 — Topics,下面我们进行详细解析。
(三) Topics 解析
Topics[]是一个数组,包含了此次交易中所有被触发的事件信息。在具体分析其内容之前,我们首先来看一个概念: indexed。
在本例中被触发的两个事件里都包含了 indexed 参数,这是 solidity 语言编写智能合约的时候一个标记参数的属性值。它的作用是在修饰事件时,将参数作为 topics 存储。也就是说,所有被标记为 indexed 的参数都会被放到 topics 里。在 Pay 事件中的 payer 和 payee,以及 Transfer 事件中的 from、to 和 ddcId 参数都会被记录到 topics 中。
接下来我们这两个 log 中的 topics 的具体构成:
1) Topics[0]
指向特定的事件,是事件的签名。
在第一个 log 中为 Pay 事件的签名:0x750e56f33a72767cd99db8943f4d04ca416c55fb783003107a869f5d5062dbab
签名的方法是对事件的字符做 keccak 散列运算,即keccak("Pay(address,address,bytes4,uint32,uint256)")
第二个 log 中的 topics[0]的值可以通过同样方法对 Transfer 事件进行计算得出:0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef
需要注意的是事件中的参数类型需要写完整,如 Pay(address,address,bytes,uint,uint)就不行,需要将 bytes 和 uint 后边的具体类型补充完整。
2) Topics[1]
第一个被 indexed 标记的参数,这里是 address 类型 from 参数补齐 64 位结果。
在第一个 log 中此值表示是发起交易的0xa30f403fc5f6be6d1eab5465511d4866c97fddf8
地址;
这里值得关注的是第二个 log 中的 topics[1]为:0x0000000000000000000000000000000000000000000000000000000000000000
也就是 0x0,称作空地址(null address),表示是生成 DDC 的 from 地址。
3) Topics[2]
第二个被 indexed 标记的参数,这里是 address 类型 to 参数补齐 64 位结果。
第一个 log 中的地址为0xad3b52b4f4bd9198dc69dd9ce4ac9846667461a2
。这个地址是 DDC-721 合约地址,也就是说,第一个 log 表示交易发起方在调用 DDC-721 合约时,需要先向其支付业务费。
第二个 log 中的地址为0xa30f403fc5f6be6d1eab5465511d4866c97fddf8
,是生成 DDC 的目标地址。
4) Topics[3]
第三个被 indexed 标记的参数。
这个参数只在第二个 log 的 Topics 中存在,是 Transfer 事件中被 indexed 标记的第三个参数,表示本次交易所生成的 DDC ID,用 16 进制来表示。
以上就是 Topics 中的全部参数。需要说明的是,indexed 只能为事件中最多三个参数做标记。但是我们可以看到,在 Pay 事件中,总共有五个参数,其中的三个参数 sig、amount 和 ddcId 并未被 indexed 标记。那么这三个参数又传到哪里去了呢?
此时我们注意到在 topics 的后边还有一个 data 参数。是的,所有未被 indexed 标记的参数,最终都会传入 data 中。
(四) Data 解析
data 中的内容是没有 indexed 标记的 value 的值转化为 16 进制,并补齐 64 位。
根据这个描述,我们把第一个事件中的 data 按照 64 位 16 进制拆分,得到:
去掉补位的 0,可以得到以下结果:
这三个参数分别对应的是 sig、amount 和 ddcId。
d0def521
: 这个参数我们在交易信息中已经分析过了,是 mint(address,string)方法的 bytes4 签名值;64
: 从 16 进制转换成 10 进制为 100,也就是指支付的业务费金额,单位是人民币(分),即 1 元钱;3087f
: 从 16 进制转换成 10 进制为 198783,是 DDC ID。
此时 DDC 官方门户搜索 198783 就可以看到这个 DDC 的信息了:
四、总结
此文章仅对 DDC-721 合约生成 DDC 的交易进行了解析。对于其他的交易,比如 DDC 的流转、销毁、授权,以及 DDC-1155 合约的批量安全生成等交易,读者都可以尝试自行解析。这里我们仅对 DDC 的交易分析提供一个大致的思路:
在对官方 DDC 进行交易后,我们可以首先调用eth_getTransactionReceipt
查看交易回执中 status 参数来确定此交易是否成功。
交易成功后,我们可以通过eth_getTransactionByHash
返回结果中的 input,以及eth_getTransactionReceipt
返回结果中的 logs 来对交易内容进行完整的解读,比如生成 DDC 后可以直接在 logs 中的 topics 里获取到 DDC ID 的信息,也可以在 input 中获取到生成 DDC 时输入的 ddcuri 的内容。
评论