• 武汉链(基于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官方合约的开源代码可以在wuhanchain/ddc-contract at master · BSN-DDC/wuhanchain · GitHub中找到。

    一、生成DDC

    生成DDC的流程大致如下:

    1. 请求者通过RPC网关发送已签名的交易请求到官方DDC-721合约

    2. DDC-721合约会调用mint方法进行DDC的生成

    3. 执行mint方法后会按顺序判断是否满足生成条件,然后会依次按顺序执行_mintAndPay和_pay方法。

    4. 在执行_pay方法时,会调用计费合约中的pay方法,此时会进行业务费的扣除,即生成1个DDC需要扣除1元业务费,并且触发Pay事件

    5. 成功扣除业务费后,会继续执行mint方法生成DDC,并且触发Transfer事件,最终完成本次交易。

    本次示例中,填入的DDC URI的内容为:

    {"url":"https://ddc.bsnbase.com/main/index","Name":"BSN-DDC","Type":"DDC-721"}
    

    成功生成DDC后,返回了交易hash:

    0x5cd1257d4e89bf3c7247c397cfb167b2b4d66c13dbffdb030e6f7d5c3ceddb2d
    

    我们可以通过解析这个交易hash来获取需要的交易信息。获取信息分需要使用以下两种方法:

    • eth_getTransactionByHash : 返回指定交易对应的交易信息

    • eth_getTransactionReceipt : 返回指定交易对应的回执信息

    二、获取交易信息

    (一)调用eth_getTransactionByHash

    请求信息:

    1. {
    2.   "jsonrpc":"2.0",
    3.   "method":"eth_getTransactionByHash",
    4.   "params": ["0x5cd1257d4e89bf3c7247c397cfb167b2b4d66c13dbffdb030e6f7d5c3ceddb2d"],
    5.   "id":1
    6. }

    响应信息:

    1. {
    2.   "jsonrpc": "2.0",
    3.   "id": 1,
    4.   "result": {
    5.     "blockHash": "0x9a5bbfc07c318794a502f55905815f9b60fd42129bb0308745cd592fd4e633a9",
    6.     "blockNumber": "0x37dcf3",
    7.     "from": "0xa30f403fc5f6be6d1eab5465511d4866c97fddf8",
    8.     "gas": "0x45a9d",
    9.     "gasPrice": "0xb2d05e00",
    10.     "hash": "0x5cd1257d4e89bf3c7247c397cfb167b2b4d66c13dbffdb030e6f7d5c3ceddb2d",
    11.     "input":"0xd0def521000000000000000000000000a30f403fc5f6be6d1eab5465511d4866c97fddf80000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000004e7b2275726c223a2268747470733a2f2f6464632e62736e626173652e636f6d2f6d61696e2f696e646578222c224e616d65223a2242534e2d444443222c2254797065223a224444432d373231227d000000000000000000000000000000000000",
    12.     "nonce": "0x1",
    13.     "to": "0xad3b52b4f4bd9198dc69dd9ce4ac9846667461a2",
    14.     "transactionIndex": "0x3",
    15.     "value": "0x0",
    16.     "type": "0x0",
    17.     "v": "0x2b8a",
    18.     "r": "0xe6b6a3eb68875ac4efdcab11239095f92ab86018cedf6d5522074ebde0657a7e",
    19.     "s": "0x7e66438dcfa90da0d8f0b20513f99ea8c4f2d6ba7ba1da87e9735f6be32d59e5"
    20.     }
    21. }

    从响应信息中的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值为:

    0xd0def521000000000000000000000000a30f403fc5f6be6d1eab5465511d4866c97fddf80000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000004e7b2275726c223a2268747470733a2f2f6464632e62736e626173652e636f6d2f6d61696e2f696e646578222c224e616d65223a2242534e2d444443222c2254797065223a224444432d373231227d000000000000000000000000000000000000
    

    此数据分为2个部分: 方法名称的哈希值+方法中的参数

    1、方法名称的哈希值

    d0def521
    

    占4个字节,是mint(address,string)方法的签名编码。

    具体计算方法为先对mint(address,string)做keccak256计算,生成32位哈希值

    0xd0def521cdae71c63ecace61ee5a1cca744cf981e4d6ff45cdd07ec87394aad5
    

    然后再将此哈希值做bytes4计算,最终生成0xd0def521(如下图所示)

    2、方法中的参数

    此部分数据的结构根据方法本身的参数设置来决定。本例中的方法有两个参数: 生成DDC的目标地址(address)以及填入的ddcuri的内容(string)

    • address : 占32个字节,而武汉链的地址为20字节,所以高位自动补零,显示为:

    000000000000000000000000a30f403fc5f6be6d1eab5465511d4866c97fddf8
    
    • string : 剩余字节,本例中的输入是ddcuri

    0000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000004e7b2275726c223a2268747470733a2f2f6464632e62736e626173652e636f6d2f6d61696e2f696e646578222c224e616d65223a2242534e2d444443222c2254797065223a224444432d373231227d000000000000000000000000000000000000
    

    此数据乍看之下非常混乱。实际上是把字符串转换成16进制表示,所以为了方便辨认和阅读,需要将其转换为字符串形式。转换后为:

    @N{"url":"https://ddc.bsnbase.com/main/index","Name":"BSN-DDC","Type":"DDC-721"}
    

    可以看出,花括号中的内容与生成DDC时输入的ddcuri内容完全一致。

    三、获取交易回执

    (一)调用eth_getTransactionReceipt

    请求信息:

    1. {
    2.   "jsonrpc":"2.0", 
    3.   "method":" eth_getTransactionReceipt",
    4.   "params": ["0x5cd1257d4e89bf3c7247c397cfb167b2b4d66c13dbffdb030e6f7d5c3ceddb2d"],
    5.   "id":1
    6. }

    响应信息:

    1. {
    2.   "jsonrpc": "2.0",
    3.   "id": 1,
    4.   "result": {
    5.     "blockHash":"0x9a5bbfc07c318794a502f55905815f9b60fd42129bb0308745cd592fd4e633a9",
    6.     "blockNumber": "0x37dcf3",
    7.     "contractAddress": null,
    8.     "cumulativeGasUsed": "0x5de907",
    9.     "effectiveGasPrice": "0xb2d05e00",
    10.     "from": "0xa30f403fc5f6be6d1eab5465511d4866c97fddf8",
    11.     "gasUsed": "0x41043",
    12.     "logs": [
    13.       {
    14.         "address": "0xca97bf3a19403805d391102908665b16b4d0217c",
    15.         "topics": [
    16.           "0x750e56f33a72767cd99db8943f4d04ca416c55fb783003107a869f5d5062dbab",
    17.           "0x000000000000000000000000a30f403fc5f6be6d1eab5465511d4866c97fddf8",
    18.           "0x000000000000000000000000ad3b52b4f4bd9198dc69dd9ce4ac9846667461a2"   
    19.         ],
    20.         "data":"0xd0def521000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000064000000000000000000000000000000000000000000000000000000000003087f",
    21.         "blockNumber": "0x37dcf3",
    22.         "transactionHash": "0x5cd1257d4e89bf3c7247c397cfb167b2b4d66c13dbffdb030e6f7d5c3ceddb2d",
    23.         "transactionIndex": "0x3",
    24.         "blockHash": "0x9a5bbfc07c318794a502f55905815f9b60fd42129bb0308745cd592fd4e633a9",
    25.         "logIndex": "0x35",
    26.         "removed": false
    27.       },
    28.       {
    29.         "address": "0xad3b52b4f4bd9198dc69dd9ce4ac9846667461a2",
    30.         "topics": [
    31.           "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef",
    32.           "0x0000000000000000000000000000000000000000000000000000000000000000",
    33.           "0x000000000000000000000000a30f403fc5f6be6d1eab5465511d4866c97fddf8",
    34.           "0x000000000000000000000000000000000000000000000000000000000003087f"
    35.         ],
    36.         "data": "0x",
    37.         "blockNumber": "0x37dcf3",
    38.         "transactionHash": "0x5cd1257d4e89bf3c7247c397cfb167b2b4d66c13dbffdb030e6f7d5c3ceddb2d",
    39.         "transactionIndex": "0x3",
    40.         "blockHash": "0x9a5bbfc07c318794a502f55905815f9b60fd42129bb0308745cd592fd4e633a9",
    41.         "logIndex": "0x36",
    42.         "removed": false
    43.       }
    44.     ],
    45.     "logsBloom": "0x00000000000000000000000000000000000000000000000200000000000000000800000000000000000000000000000000040000000000000000000000000000000000020000000000000008000800000000000000000000000000000000000000000000020000000000000800000800000000000000000080000010000000000000000000000100000000000000000000000000000000000000000000000000000000000040000000000020000000000000000000000000000000000000000000010002000000000000000000000000001000000000000000000000800020000000010000000000000000020000000100800000000000000000000000000000",
    46.     "status": "0x1",
    47.     "to": "0xad3b52b4f4bd9198dc69dd9ce4ac9846667461a2",
    48.     "transactionHash": "0x5cd1257d4e89bf3c7247c397cfb167b2b4d66c13dbffdb030e6f7d5c3ceddb2d",
    49.     "transactionIndex": "0x3",
    50.     "type": "0x0"
    51.   }
    52. }

    这里的响应信息里包含的参数很多都与交易信息中一致,比如块哈希、块号、交易哈希、交易的发送方地址和接收方地址、交易索引等。

    其他的参数包含的信息如下:

    • contractAddress : 如果本次交易是部署合约,那么就会返回生成的合约地址,如果是其他类型的交易,那么会返回null

    • cumulativeGasUsed : 交易所在块消耗的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进制拆分,得到:

    1. d0def52100000000000000000000000000000000000000000000000000000000
    2. 0000000000000000000000000000000000000000000000000000000000000064
    3. 000000000000000000000000000000000000000000000000000000000003087f

    去掉补位的0,可以得到以下结果:

    1. d0def521
    2. 64
    3. 3087f

    这三个参数分别对应的是sigamountddcId

    • 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的内容。

    引用资料: https://copyfuture.com/blogs-details/20211208235620545g https://blog.csdn.net/qq_42200107/article/details/124560913 http://cw.hubwiz.com/card/c/ethereum-json-rpc-api/ https://www.jinse.com/news/blockchain/1178895.html https://blog.csdn.net/tianlongtc/article/details/84001623 https://blog.csdn.net/weixin_43343144/article/details/91502148

  • 相关阅读:
    【漏洞通告】CVE-2022-39198 Apache Dubbo Hession反序列化漏洞
    淘宝店铺上新图片上传获取请求方法
    【代码随想录算法训练Day65】卡码网47.参加科学大会、卡码网94. 城市间货物运输 I
    存储芯片大厂集体“越冬” 头部厂商扎堆砍单、业绩下滑
    OpenHarmony创新赛|赋能直播第五期
    godot引擎学习1
    LeetCode 刷题系列 -- 90. 子集 II
    两个线程交替执行的几种方式
    【附源码】Python计算机毕业设计球迷信息交流论坛
    【Mysql】B+树索引的使用(七)
  • 原文地址:https://blog.csdn.net/BSN_yanxishe/article/details/126264979