Ethereum学习笔记 ---- 通过 Event 学习《合约ABI规范》

news/2024/9/21 5:50:58/文章来源:https://www.cnblogs.com/dongling/p/18353660

以太坊合约ABI规范见 官方文档-合约ABI规范

这里通过实验来印证 ABI 编码在 Event log 中的实现。

本地启动 ganache

首先在本地启动 ganache 作为 evm 链单节点,稍后与以太坊的交互都是通过与本地的 ganache 节点交互来实现的。
Ganache官网
将 ganache 节点的端口设置为以太坊的默认RPC端口8545,方便后续的调试。

创建 hardhat 项目

执行如下命令

$ mkdir abi$ cd abi $ npm init$ npm install -D hardhat

然后在abi项目根目录中初始化 hardhat 项目

$ npx hardhat init
888    888                      888 888               888
888    888                      888 888               888
888    888                      888 888               888
8888888888  8888b.  888d888 .d88888 88888b.   8888b.  888888
888    888     "88b 888P"  d88" 888 888 "88b     "88b 888
888    888 .d888888 888    888  888 888  888 .d888888 888
888    888 888  888 888    Y88b 888 888  888 888  888 Y88b.
888    888 "Y888888 888     "Y88888 888  888 "Y888888  "Y888👷 Welcome to Hardhat v2.22.8 👷‍✔ What do you want to do? · Create a JavaScript project
✔ Hardhat project root: · /Users/dongling/mycode/blockchain/abi
✔ Do you want to add a .gitignore? (Y/n) · y
✔ Do you want to install this sample project's dependencies with npm (@nomicfoundation/hardhat-toolbox)? (Y/n) · ynpm install --save-dev "@nomicfoundation/hardhat-toolbox@^5.0.0"
⠋

创建合约文件

在 abi 项目中创建合约文件 contracts/EventFactory.sol,内容如下:

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;contract EventFactory {event Transfer(address indexed from,address indexed to,uint256 indexed tokenId);event Transfer2(address indexed from, address indexed to, uint256 tokenId);event Transfer3(address indexed from, address to, uint256 tokenId);event Msg(address indexed from,address indexed to,string indexed msg1,string msg2);event Msg2(address indexed from,address indexed to,string msg1,string msg2);address public owner;constructor() {owner = msg.sender;}function emitTransfer(address to, uint tokenId) public {emit Transfer(msg.sender, to, tokenId);}function emitTransfer2(address to, uint tokenId) public {emit Transfer2(msg.sender, to, tokenId);}function emitTransfer3(address to, uint tokenId) public {emit Transfer3(msg.sender, to, tokenId);}function emitMsg(address to,string memory msg1,string memory msg2) public {emit Msg(msg.sender, to, msg1, msg2);}function emitMsg2(address to,string memory msg1,string memory msg2) public {emit Msg2(msg.sender, to, msg1, msg2);}
}

编译合约:

$ npx hardhat compile
Compiled 2 Solidity files successfully (evm target: paris).

创建安装合约的脚本

创建 ignition/modules/EventFactory.js,内容如下

const { buildModule } = require('@nomicfoundation/hardhat-ignition/modules')module.exports = buildModule('EventFactoryModule', (m) => {const eventFactory = m.contract('EventFactory')return { eventFactory }
})

部署合约

ganache 中链启动的时候,默认会创建10个 account,并且给每一个 account 设置初始的 balance 为 100ETH。
部署合约的时候,默认使用的是 ganache 中的第一个 account

在 abi 项目中执行 npx hardhat ignition deploy ./ignition/modules/EventFactory.js --network localhost 命令部署合约

$ npx hardhat ignition deploy ./ignition/modules/EventFactory.js --network localhost
✔ Confirm deploy to network localhost (1337)? … yes
Hardhat Ignition 🚀Deploying [ EventFactoryModule ]Batch #1Executed EventFactoryModule#EventFactory[ EventFactoryModule ] successfully deployed 🚀Deployed AddressesEventFactoryModule#EventFactory - 0x87400459ABbd2D180c4DDae75D0060b16775cc84

可以看到 EventFactory 合约被部署后的地址是 0x87400459ABbd2D180c4DDae75D0060b16775cc84
Evm 中合约地址是通过如下方式计算得到的:

contract_address = keccak256(rpl_encode(creator_account_address, creator_nonce))

不同人的操作得到的地址结果可能不同。

可以在 ganache 中看到创建合约新生成的 block:

创建测试脚本

在 abi 项目根目录中,创建 config.js,将刚才部署的合约的地址填写进去,内容如下:

const allchain = {localchain: 'http://localhost:8545',
}const Contracts = {eventFactory: {address: '0x87400459ABbd2D180c4DDae75D0060b16775cc84',},
}module.exports = {allchain,Contracts,
}

然后创建文件 test/EventFactory.js,内容如下:

const contractName = 'EventFactory'const eventFactoryABI =require(`../artifacts/contracts/${contractName}.sol/${contractName}.json`)['abi']
const { allchain, Contracts } = require('../config.js')describe('EventFactory', () => {let provider, allAccounts, eventFactorylet alice, bobbeforeEach(async () => {provider = new ethers.JsonRpcProvider(allchain.localchain)allAccounts = await provider.listAccounts();[alice, bob] = allAccountseventFactory = new ethers.Contract(Contracts.eventFactory.address,eventFactoryABI,provider)})describe('EventFactory contract', () => {it('emit events', async () => {console.log('alice:', alice.address)console.log('bob:', bob.address)let tx = await eventFactory.connect(alice).emitTransfer(bob.address, 5)await tx.wait()tx = await eventFactory.connect(alice).emitTransfer2(bob.address, 5)await tx.wait()tx = await eventFactory.connect(alice).emitTransfer3(bob.address, 5)await tx.wait()tx = await eventFactory.connect(alice).emitMsg(bob.address,'how are you','good better best never let it rest, till good is better, and better is best')await tx.wait()tx = await eventFactory.connect(alice).emitMsg(bob.address,'The quick brown fox jumps over the lazy dog','good better best never let it rest, till good is better, and better is best')await tx.wait()tx = await eventFactory.connect(alice).emitMsg2(bob.address,'how are you','good better best never let it rest, till good is better, and better is best')await tx.wait()})it.skip('filter event logs', async () => {let events = ['Transfer', 'Transfer2', 'Transfer3', 'Msg', 'Msg2']for (let event of events) {let eventResult = await eventFactory.queryFilter(eventFactory.filters[event],43 // fromBlock, default 0; inclusive//100 // toBlock, default 'latest'; inclusive)console.log('Event[%s]:\n', event, eventResult)console.log('==========================================')}})})
})

执行测试用例

$ npx hardhat test test/EventFactory.js EventFactoryEventFactory contract
alice: 0x01a387460D96Ca136631232765e6Ff7e855Ef283
bob: 0x784E8581a693308781223b60D05bce7608f0cadf✔ emit events (25173ms)- filter event logs1 passing (25s)1 pending

可以看到调用合约方法的测试用例 emit events 执行成功。

接下来将 emit events 测试用例的 it 修改成 it.skip,将filter event logs 测试用例的 it.skip 修改成 it,再次执行测试命令npx hardhat test test/EventFactory.js,这次将会略过 emit events,只执行 filter event logs 来过滤 emit events 步骤中发出的 event。执行结果如下:

$ npx hardhat test test/EventFactory.js EventFactoryEventFactory contract- emit events
Event[Transfer]:[EventLog {provider: JsonRpcProvider {},transactionHash: '0xe0de4c0f343c4afeda73b80e14f5652d7b028b96f02c7c44c3f88212930f604f',blockHash: '0x169bd2547de85358584d74bc50bd81c4e922bfb87c880f42eb213b6af34701fb',blockNumber: 43,removed: false,address: '0x87400459ABbd2D180c4DDae75D0060b16775cc84',data: '0x',topics: ['0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef','0x00000000000000000000000001a387460d96ca136631232765e6ff7e855ef283','0x000000000000000000000000784e8581a693308781223b60d05bce7608f0cadf','0x0000000000000000000000000000000000000000000000000000000000000005'],index: 0,transactionIndex: 0,interface: Interface {fragments: [Array],deploy: [ConstructorFragment],fallback: null,receive: false},fragment: EventFragment {type: 'event',inputs: [Array],name: 'Transfer',anonymous: false},args: Result(3) ['0x01a387460D96Ca136631232765e6Ff7e855Ef283','0x784E8581a693308781223b60D05bce7608f0cadf',5n]}
]
==========================================
Event[Transfer2]:[EventLog {provider: JsonRpcProvider {},transactionHash: '0x075ca454a8cc870c157431e663fc368d9cc9a1905a6538fed183f7b50b272af2',blockHash: '0xfce37abbea895037cae15724d64f30716f33551140e7c334e434bd8ad688c6b2',blockNumber: 44,removed: false,address: '0x87400459ABbd2D180c4DDae75D0060b16775cc84',data: '0x0000000000000000000000000000000000000000000000000000000000000005',topics: ['0x6a0adf17eb20399b431dc509145e9448c952cb299ead47af739fed7289c553f5','0x00000000000000000000000001a387460d96ca136631232765e6ff7e855ef283','0x000000000000000000000000784e8581a693308781223b60d05bce7608f0cadf'],index: 0,transactionIndex: 0,interface: Interface {fragments: [Array],deploy: [ConstructorFragment],fallback: null,receive: false},fragment: EventFragment {type: 'event',inputs: [Array],name: 'Transfer2',anonymous: false},args: Result(3) ['0x01a387460D96Ca136631232765e6Ff7e855Ef283','0x784E8581a693308781223b60D05bce7608f0cadf',5n]}
]
==========================================
Event[Transfer3]:[EventLog {provider: JsonRpcProvider {},transactionHash: '0xad771fe51e7a4cb8cff316ba15c5745f30c200025c834cc0931166bed8f8b34f',blockHash: '0x7c0d73ff6300af7c57698170717e48d3e6d65323583d6d9ce2b1dd7ecd55dd13',blockNumber: 45,removed: false,address: '0x87400459ABbd2D180c4DDae75D0060b16775cc84',data: '0x000000000000000000000000784e8581a693308781223b60d05bce7608f0cadf0000000000000000000000000000000000000000000000000000000000000005',topics: ['0x2411965a414db56a3afdcb174d6049ed9467924617e5455ab694e45e8f69fd80','0x00000000000000000000000001a387460d96ca136631232765e6ff7e855ef283'],index: 0,transactionIndex: 0,interface: Interface {fragments: [Array],deploy: [ConstructorFragment],fallback: null,receive: false},fragment: EventFragment {type: 'event',inputs: [Array],name: 'Transfer3',anonymous: false},args: Result(3) ['0x01a387460D96Ca136631232765e6Ff7e855Ef283','0x784E8581a693308781223b60D05bce7608f0cadf',5n]}
]
==========================================
Event[Msg]:[EventLog {provider: JsonRpcProvider {},transactionHash: '0x4f8b80d243832d9ad25f2df3dbdb7f7e63f968e0a552f612d3f6141bb17e153b',blockHash: '0x6965462fd249a980aeb43e7713f2b2bd3370ed794b677d4be0e221db7174ccec',blockNumber: 46,removed: false,address: '0x87400459ABbd2D180c4DDae75D0060b16775cc84',data: '0x0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000004b676f6f64206265747465722062657374206e65766572206c657420697420726573742c2074696c6c20676f6f64206973206265747465722c20616e64206265747465722069732062657374000000000000000000000000000000000000000000',topics: ['0x8fc6e328d73f399066d9993d9bddb182ebabfc570eb5c4ba235fb99073172bcc','0x00000000000000000000000001a387460d96ca136631232765e6ff7e855ef283','0x000000000000000000000000784e8581a693308781223b60d05bce7608f0cadf','0x6b69b0c17a167ec6f6698d7336ec915e3d1b019470128672b70c20c7106c8bb3'],index: 0,transactionIndex: 0,interface: Interface {fragments: [Array],deploy: [ConstructorFragment],fallback: null,receive: false},fragment: EventFragment {type: 'event',inputs: [Array],name: 'Msg',anonymous: false},args: Result(4) ['0x01a387460D96Ca136631232765e6Ff7e855Ef283','0x784E8581a693308781223b60D05bce7608f0cadf',[Indexed],'good better best never let it rest, till good is better, and better is best']},EventLog {provider: JsonRpcProvider {},transactionHash: '0xef71afe9fa8d516a4f250c4e64710fb5004b6168ee3441ba1900e9bd30fbc315',blockHash: '0x9e397437a3c03b142c46f94cf7743345b91be15e095c4d10badc1b6d7b15b0af',blockNumber: 47,removed: false,address: '0x87400459ABbd2D180c4DDae75D0060b16775cc84',data: '0x0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000004b676f6f64206265747465722062657374206e65766572206c657420697420726573742c2074696c6c20676f6f64206973206265747465722c20616e64206265747465722069732062657374000000000000000000000000000000000000000000',topics: ['0x8fc6e328d73f399066d9993d9bddb182ebabfc570eb5c4ba235fb99073172bcc','0x00000000000000000000000001a387460d96ca136631232765e6ff7e855ef283','0x000000000000000000000000784e8581a693308781223b60d05bce7608f0cadf','0x4d741b6f1eb29cb2a9b9911c82f56fa8d73b04959d3d9d222895df6c0b28aa15'],index: 0,transactionIndex: 0,interface: Interface {fragments: [Array],deploy: [ConstructorFragment],fallback: null,receive: false},fragment: EventFragment {type: 'event',inputs: [Array],name: 'Msg',anonymous: false},args: Result(4) ['0x01a387460D96Ca136631232765e6Ff7e855Ef283','0x784E8581a693308781223b60D05bce7608f0cadf',[Indexed],'good better best never let it rest, till good is better, and better is best']}
]
==========================================
Event[Msg2]:[EventLog {provider: JsonRpcProvider {},transactionHash: '0x4a76b83604fa7587f2ac8f05842a7f73b0a68fe5d1eeebb8f8ea6d1ae740d6b4',blockHash: '0x8e164c2dfbe0e7e71f577ecb780ee015b6142b1ed761af81819afcac1eb09e22',blockNumber: 48,removed: false,address: '0x87400459ABbd2D180c4DDae75D0060b16775cc84',data: '0x00000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000b686f772061726520796f75000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004b676f6f64206265747465722062657374206e65766572206c657420697420726573742c2074696c6c20676f6f64206973206265747465722c20616e64206265747465722069732062657374000000000000000000000000000000000000000000',topics: ['0x24b5fc4f941a6ebaa645b15f9bdeb17b7f2eb51f22fcada5f988e1ffd6b518ee','0x00000000000000000000000001a387460d96ca136631232765e6ff7e855ef283','0x000000000000000000000000784e8581a693308781223b60d05bce7608f0cadf'],index: 0,transactionIndex: 0,interface: Interface {fragments: [Array],deploy: [ConstructorFragment],fallback: null,receive: false},fragment: EventFragment {type: 'event',inputs: [Array],name: 'Msg2',anonymous: false},args: Result(4) ['0x01a387460D96Ca136631232765e6Ff7e855Ef283','0x784E8581a693308781223b60D05bce7608f0cadf','how are you','good better best never let it rest, till good is better, and better is best']}
]
==========================================✔ filter event logs (89ms)1 passing (126ms)1 pending

分析 Event Log

ABI 规范 Event:

Events are an abstraction of the Ethereum logging/event-watching protocol. Log entries provide the contract’s address, a series of up to four topics and some arbitrary length binary data. Events leverage the existing function ABI in order to interpret this (together with an interface spec) as a properly typed structure.

Given an event name and series of event parameters, we split them into two sub-series: those which are indexed and those which are not. Those which are indexed, which may number up to 3 (for non-anonymous events) or 4 (for anonymous ones), are used alongside the Keccak hash of the event signature to form the topics of the log entry. Those which are not indexed form the byte array of the event.

In effect, a log entry using this ABI is described as:

  • address: the address of the contract (intrinsically provided by Ethereum);

  • topics[0]: keccak(EVENT_NAME+"("+EVENT_ARGS.map(canonical_type_of).join(",")+")") (canonical_type_of is a function that simply returns the canonical type of a given argument, e.g. for uint indexed foo, it would return uint256). This value is only present in topics[0] if the event is not declared as anonymous;

  • topics[n]: abi_encode(EVENT_INDEXED_ARGS[n - 1]) if the event is not declared as anonymous or abi_encode(EVENT_INDEXED_ARGS[n]) if it is (EVENT_INDEXED_ARGS is the series of EVENT_ARGS that are indexed);

  • data: ABI encoding of EVENT_NON_INDEXED_ARGS (EVENT_NON_INDEXED_ARGS is the series of EVENT_ARGS that are not indexed, abi_encode is the ABI encoding function used for returning a series of typed values from a function, as described above).


ABI 规范中文版 事件:

事件是Ethereum日志/事件观察协议的一个抽象。日志条目提供了合约的地址, 一系列最多四个主题和一些任意长度的二进制数据。 事件利用现有的函数ABI,以便将其(连同接口规范)解释为一个正确的类型化结构。

给定一个事件名称和一系列的事件参数,我们把它们分成两个子系列:那些有索引的和那些没有索引的。 那些被索引的参数,可能多达3个(对于非匿名事件)或4个(对于匿名事件), 与事件签名的Keccak散列一起使用,形成日志条目的主题。 那些没有索引的则构成事件的字节数组。

实际上,使用该ABI的日志条目被描述为:

  • address: 合约的地址(由以太坊真正提供);

  • topics[0]keccak(EVENT_NAME+"("+EVENT_ARGS.map(canonical_type_of).join(",")+")") (canonical_type_of 是一个可以返回给定参数的权威类型的函数,例如,对 uint indexed foo 它会返回 uint256)。 如果事件被声明为 anonymous,那么 topics[0] 不会被生成;

  • topics[n]: 如果事件没有被声明为 anonymous, 则为 abi_encode(EVENT_INDEXED_ARGS[n - 1]) 或者如果它被声明为该类型,则为 abi_encode(EVENT_INDEXED_ARGS[n])EVENT_INDEXED_ARGS 是被索引的 EVENT_ARGS 的系列);

  • dataEVENT_NON_INDEXED_ARGS 的ABI编码 ( EVENT_NON_INDEXED_ARGS 是一系列没有索引的 EVENT_ARGSabi_encode 是ABI编码函数, 用于从一个函数返回一系列类型的值,如上所述)。

根据以上规范说明,我们来分析一下

Event Transfer

topics[0] = 0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef
正是事件签名 "Transfer(address,address,uint256)" 的 keccak256 哈希值。
字符串 "Transfer(address,address,uint256)" 的 16 进制编码为 5472616e7366657228616464726573732c616464726573732c75696e7432353629,使用 evm 节点的 JSON-RPC web3_sha3 计算 keccak256 哈希值,如下所示:

$ curl --request POST \--url http://localhost:8545/ \--header 'user-agent: vscode-restclient' \--data '{"jsonrpc": "2.0","id": 64,"method": "web3_sha3","params": ["0x5472616e7366657228616464726573732c616464726573732c75696e7432353629"]}'{"id":64,"jsonrpc":"2.0","result":"0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef"}

结果与 topics[0] 相等。

topics[1]topics[2] 是 20字节的 alice.address 0x01a387460D96Ca136631232765e6Ff7e855Ef283 和 bob.address 0x784E8581a693308781223b60D05bce7608f0cadf 左填充到32字节后的结果。

topics[3] 是数字 5 的32字节编码结果。

Event Transfer2

Transfer2Transfer 相似,只是第三个参数 tokenId 没有使用 indexed 进行修饰,所以 tokenId 没有放在在 topics 中进行编码,而是放在 data 中进行编码;tokenIduint256 类型,所以编码结果是32字节的内容,用16进制表示,结果是64个字符(未计算 0x前缀): 0x0000000000000000000000000000000000000000000000000000000000000005

Event Transfer3

由于只有第一个参数有 indexed 修饰,所以后面的两个参数都放入了 data 字段

0x000000000000000000000000784e8581a693308781223b60d05bce7608f0cadf0000000000000000000000000000000000000000000000000000000000000005

前64个字符 000000000000000000000000784e8581a693308781223b60d05bce7608f0cadf 正是 address to = 784e8581a693308781223b60d05bce7608f0cadf 这个20字节、40字符的地址补足为32字节后的16进制表示,

而后64个字符 0000000000000000000000000000000000000000000000000000000000000005 正是 uint256 tokenId = 5 的 16进制表示。

Event Msg

  • topics[0] = 0x8fc6e328d73f399066d9993d9bddb182ebabfc570eb5c4ba235fb99073172bcc 是事件签名 "Msg(address,address,string,string)" 的 keccak256 哈希值。
    字符串"Msg(address,address,string,string)" 的16进制编码结果是4d736728616464726573732c616464726573732c737472696e672c737472696e6729,使用 JSON-RPC web3_sha3 验证其 keccak256 哈希值:

    $ curl --request POST \--url http://localhost:8545/ \--header 'user-agent: vscode-restclient' \--data '{"jsonrpc": "2.0","id": 64,"method": "web3_sha3","params": ["0x4d736728616464726573732c616464726573732c737472696e672c737472696e6729"]}'{"id":64,"jsonrpc":"2.0","result":"0x8fc6e328d73f399066d9993d9bddb182ebabfc570eb5c4ba235fb99073172bcc"}
    

    结果符合预期。

  • topics[1]topics[2]address 左填充到32字节的16进制编码结果。

  • 打印的日志用有两个Event Msg:
    第一个topics[3] = 0x6b69b0c17a167ec6f6698d7336ec915e3d1b019470128672b70c20c7106c8bb3
    test/EventFactory.js 中的代码可以看到,该 topic 对应的原始值为 "how are you",其16进制表示结果为 686f772061726520796f75,使用 JSON-RPC web3_sha3 计算 keccak256("how are you")

    $ curl --request POST \--url http://localhost:8545/ \--header 'user-agent: vscode-restclient' \--data '{"jsonrpc": "2.0","id": 64,"method": "web3_sha3","params": ["0x686f772061726520796f75"]}'{"id":64,"jsonrpc":"2.0","result":"0x6b69b0c17a167ec6f6698d7336ec915e3d1b019470128672b70c20c7106c8bb3"}
    

    符合预期。

    第二个 topics[3] = 0x4d741b6f1eb29cb2a9b9911c82f56fa8d73b04959d3d9d222895df6c0b28aa15,对应的原始值为 "The quick brown fox jumps over the lazy dog",其16进制编码结果为:54686520717569636b2062726f776e20666f78206a756d7073206f76657220746865206c617a7920646f67,使用 JSON-RPC web3_sha3 计算 keccak256("The quick brown fox jumps over the lazy dog"):

    $ curl --request POST \--url http://localhost:8545/ \--header 'user-agent: vscode-restclient' \--data '{"jsonrpc": "2.0","id": 64,"method": "web3_sha3","params": ["0x54686520717569636b2062726f776e20666f78206a756d7073206f76657220746865206c617a7920646f67"]}'{"id":64,"jsonrpc":"2.0","result":"0x4d741b6f1eb29cb2a9b9911c82f56fa8d73b04959d3d9d222895df6c0b28aa15"}
    

    符合预期。

  • data 部分:
    两个 Msg 的 msg2 相同,所以 data 部分的数据也相同,同为 0x0000000000000000000000000000000000000000000000000000000000000020_000000000000000000000000000000000000000000000000000000000000004b_676f6f64206265747465722062657374206e65766572206c657420697420726573742c2074696c6c20676f6f64206973206265747465722c20616e64206265747465722069732062657374000000000000000000000000000000000000000000

    这部分数据除去开头的前缀 0x,共计320个字符,也就是160个字节的数据。这段数据是包含75个字符的字符串"good better best never let it rest, till good is better, and better is best" ABI编码后的结果,分成了3部分,如上所示,用 _ 隔开了每一个部分的最后一个字节。各部分解释如下:

    • 第一部分
      0000000000000000000000000000000000000000000000000000000000000020 :
      32个字节,0x20 的数值为32,表示从 data[32] 开始表示数据的长度,表示长度的数据也是32字节;这里将 data 看作是字节数组,而不是16进制的字符串

    • 第二部分
      000000000000000000000000000000000000000000000000000000000000004b :
      32个字节,0x4b 的数值为75,正是原始字符串的长度,表示从 data[32+32] 开始接下来的75个字节是msg2数据

    • 第三部分
      676f6f64206265747465722062657374206e65766572206c657420697420726573742c2074696c6c20676f6f64206973206265747465722c20616e64206265747465722069732062657374000000000000000000000000000000000000000000 :
      96个字节,前75个字节为字符串"good better best never let it rest, till good is better, and better is best" 的16进制编码,后面的21个字节为0,用于 padding,与前面的75个字节构成32个字节的整数倍。

Event Msg2

由于 msg1 和 msg2 都没有被 indexed 修饰,所以 msg1="how are you" 和 msg2="good better best never let it rest, till good is better, and better is best" 都被 ABI 编码进了 data 字段,为:0x0000000000000000000000000000000000000000000000000000000000000040_0000000000000000000000000000000000000000000000000000000000000080_000000000000000000000000000000000000000000000000000000000000000b_686f772061726520796f75000000000000000000000000000000000000000000_000000000000000000000000000000000000000000000000000000000000004b_676f6f64206265747465722062657374206e65766572206c657420697420726573742c2074696c6c20676f6f64206973206265747465722c20616e64206265747465722069732062657374000000000000000000000000000000000000000000

这部分去掉开头的 0x,共计512个16进制字符,也就是256个字节的数据。这段数据是["how are you", "good better best never let it rest, till good is better, and better is best"] ABI编码后的结果,分成了6部分,如上所示,用 _ 隔开了每一部分。各部分解释如下:

  • 第一部分
    0000000000000000000000000000000000000000000000000000000000000040
    32个字节,0x40 的数值为64,表示 data[64:64+32] 是 msg1 的数据长度。
  • 第二部分
    0000000000000000000000000000000000000000000000000000000000000080
    32个字节,0x80的数值为128,表示 data[128:128+32] 是 msg2 的数据长度。
  • 第三部分
    000000000000000000000000000000000000000000000000000000000000000b
    offset=64,是由第一部分指向的数据长度,32个字节,0xb 数值为11,表示后续11个字节是 msg1 的数据
  • 第四部分
    686f772061726520796f75000000000000000000000000000000000000000000
    32个字节,前11个字节是 "how are you" 的16进制编码,后面的21个字节为0,用于 padding,与前面的11字节构成32个字节的整数倍。
  • 第五部分
    000000000000000000000000000000000000000000000000000000000000004b
    offset=128,是第二部分指向的数据长度,32个字节,0x4b数值为75,表示后续的75个字节是 msg2 的数据
  • 第六部分
    676f6f64206265747465722062657374206e65766572206c657420697420726573742c2074696c6c20676f6f64206973206265747465722c20616e64206265747465722069732062657374000000000000000000000000000000000000000000
    96个字节,前75个字节是"good better best never let it rest, till good is better, and better is best"的16进制编码,后面的21个字节为0,用于 padding,与前面的75个字节构成32个字节的整数倍。

至此,我们通过实验数据印证了 Ethereum的 ABI 编码结果。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/781506.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

pycharm专业版的安装和破解教程

1. 下载资源后解压,以管理员身份运行pycharm-professional-2024.1.exe 解压码:520000 下载链接:https://pan.xunlei.com/s/VO41LIAJ5geCjsQuhaUTNRM8A1?pwd=tjwt# 2. 安装程序内:下一步 -> 设置安装目录(软件安装不推荐放c盘,可以改成别的盘),下一步 -> 根据需要…

【转载】Behinder4.1(冰蝎)重写传输协议

0x01 编写流程首先新建你自己的传输协议名称然后使用java编写本地的加密函数再编写对应的解密函数,就可以保存然后再用对应语言编写远程加解密函数然后通过Wireshark抓包可以发现流量数据已经成功就行了加密 0x02 测试源码 1、本地加解密 // 加密函数 private byte[] Encrypt(…

pwntools缓冲区溢出与栈没对齐

我是ubuntu22.04,遇到了极其诡异的事情:可真是气死我了,got EOF是在逗我?怎么就EOF了?怎么就到end of file了? 下面请欣赏大佬的讲解:以下贴上gpt更详细的解答,我这次深刻的记住了栈要16字节对齐(我以前一直以为这只是为了提高效率的,可选可不选,无非慢一点) 当栈没…

Elasticsearch 磁盘空间异常:一次成功的故障排除案例分享

故障现象 近日有客户找到我们,说有个 ES 集群节点,磁盘利用率达到了 82% ,而其节点才 63% ,想处理下这个节点,降低节点的磁盘利用率。 起初以为是没有打开自动平衡导致的,经查询,数据还是比较平衡的。利用率较高的是 76 节点,如果 76 节点的分片比其他节点多,好像还比…

039.Vue3入门,异步加载组件,初始时不全部加载,使用时才加载

1.App.vue代码如下:<template><button @click="change">切换组件</button><p></p><keep-alive><component :is="tabComponent"></component></keep-alive> </template><script> impor…

P1270 “访问”美术馆

题意注意:要预留一秒的时间!!!不然你就 80 pts 分了。 小偷要回到大门。思路 定义 \(f_{i, j}\) 表示到在 \(i\) 的子树内拿 \(j\) 幅画。 那么我们可以枚举 \(f_{to, k}\) 表示在儿子结点拿 \(k\),那么总共为 \(f_{u, j + k} = min(f_{u, j + k}, f_{u, j} + f_{to, k} +…

matlab求解非线性规划

目录前言一、非线性规划的标准型二、fmincon函数1.目标函数--function f = fun(x)2.非线性约束函数--[c,ceq] = nonlfun(x)3.设置求解方法--option三、matlab求解非线性规划的实例与可能遇到的问题1.初值问题2.算法问题(1)内点法求解(2)SQP算法求解(3)active set算法求解…

genieacs安装

linux环境:ubuntu18.041. 安装node.js16.14wget https://nodejs.org/dist/v16.14.2/node-v16.14.2-linux-x64.tar.xz tar -Jxvf node-v16.14.2-linux-x64.tar.xz sudo mv node-v16.14.2-linux-x64/ /opt/ sudo ln -s /opt/node-v16.14.2-linux-x64/bin/node /usr/local/bin/ …

Scanner的进阶使用——基础计算

通过Scanner,可以将我们输入的数字进行计算从而反映出和以及平均数 1.定义两个变量,分别是输入的整数以及总数的和2.建立一个扫描器3.使用while关键字进行循环,在符合条件下(输入的是数字)可以一直进行计算过程4.设置电脑接收数据5.设置我们输入的次数以及数字的总和6.输出…

jUC中的锁

在JUC中 可以使用synchronized关键字进行加锁 如下所示 Object object = new Object(); synchronized (object){ // TODO }synchronized关键字所加的锁是逐步升级的,顺序是 无锁-> 偏向锁 -> 轻量级锁 -> 重量级锁、随着锁等级的提高,所带来的消耗也会越…

Scanner的进阶使用——数字的输入

1.用Scanner输入数字(整数和小数) 1.定义一个整数变量2.建立扫描器3.使用if4.建立电脑接收数据5.设置else(那么)语法6.关闭Scanner