千家信息网

Solidity Event是怎么实现的

发表于:2024-10-25 作者:千家信息网编辑
千家信息网最后更新 2024年10月25日,本篇内容主要讲解"Solidity Event是怎么实现的",感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习"Solidity Event是怎么实现的"吧!一个
千家信息网最后更新 2024年10月25日Solidity Event是怎么实现的

本篇内容主要讲解"Solidity Event是怎么实现的",感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习"Solidity Event是怎么实现的"吧!

一个Solidity Event的定义如下:

event Deposit(    address indexed _from,    bytes32 indexed _id,    uint _value);
  • 最多 3 indexed参数.

  • 如果一个 indexed 参数的类型是大于32 bytes (比如 string 和 bytes), 就不存实际数据, 而是存数据的KECCAK256摘要(Digest).

EVM Log Primitives

先来看log0, log1, ..., log4 EVM 指令.

EVM 日志功能使用不同的术语:

  • "topics": 最多 4 个topics. 每个topic 32 bytes.

  • "data": 数据是event的Payload. 可以是任意长度的bytes.

一个Solidity event 如何映射到一个log 原语?

  • 所有"non-indexed 参数" 被存为data.

  • 每个"indexed 参数" 被存为一个32 bytes的topic.

The log0 Primitive

Log0生成一个只有data的日志项目, 没有topic. data可以任意长度的 bytes.

下面看一个例子

pragma solidity ^0.4.18;
contract Logger {  function Logger() public {    log0(0xc0fefe);  }}

编译后

0x40指针是内存的空闲指针。第一部分将数据导入内存,第二部分将数据的大小在栈上准备好

memory: { 0x40 => 0x60 }tag_1:  // copy data into memory  0xc0fefe    [0xc0fefe]  mload(0x40)    [0x60 0xc0fefe]  swap1    [0xc0fefe 0x60]  dup2    [0x60 0xc0fefe 0x60]  mstore    [0x60]    memory: {      0x40 => 0x60      0x60 => 0xc0fefe    }// calculate data start position and size  0x20    [0x20 0x60]  add    [0x80]  mload(0x40)    [0x60 0x80]  dup1    [0x60 0x60 0x80]  swap2    [0x60 0x80 0x60]  sub    [0x20 0x60]  swap1    [0x60 0x20]log0

在执行log0前, 在栈上有2个参数: [0x60 0x20].

  • start: 0x60 是用来存放数据的内存指针.

  • size: 0x20 (或者32) 指定了载入数据的大小.

Logging With Topics

下面的例子使用 log2 原语. 第一个参数是数据(可以任意长字节),其后跟着 2个 topics (每个32 bytes ):

// log-2.solpragma solidity ^0.4.18;contract Logger {  function Logger() public {    log2(0xc0fefe, 0xaaaa1111, 0xbbbb2222);  }}

汇编代码是非常相似的。唯一的区别是2个topics (0xbbbb2222, 0xaaaa1111)被推倒了栈上:

tag_1:  // push topics  0xbbbb2222  0xaaaa1111// copy data into memory  0xc0fefe  mload(0x40)  swap1  dup2  mstore  0x20  add  mload(0x40)  dup1  swap2  sub  swap1// create log  log2

数据还是0xc0fefe, 被拷贝到内存. 执行 log2前,状态如下:

stack: [0x60 0x20 0xaaaa1111 0xbbbb2222]memory: {  0x60: 0xc0fefe}log2

头两个参数指定日志数据的内存领域,2个新增的栈上元素是2个32 bytes 的topics.

All EVM Logging Primitives

EVM支持5个日志的原语:

0xa0 LOG00xa1 LOG10xa2 LOG20xa3 LOG30xa4 LOG4

Logging Testnet Demo

pragma solidity ^0.4.18;contract Logger {  function Logger() public {    log0(0x0);    log1(0x1, 0xa);    log2(0x2, 0xa, 0xb);    log3(0x3, 0xa, 0xb, 0xc);    log4(0x4, 0xa, 0xb, 0xc, 0xd);  }}

这个合约被部署到Rinkeby测试网络. 在: https://rinkeby.etherscan.io/tx/0x0e88c5281bb38290ae2e9cd8588cd979bc92755605021e78550fbc4d130053d1

Solidity Events

下面是一个Log事件,带着3个uint256的参数(non-indexed):

pragma solidity ^0.4.18;contract Logger {  event Log(uint256 a, uint256 b, uint256 c);  function log(uint256 a, uint256 b, uint256 c) public {    Log(a, b, c);  }}

生成的原始日志在:

https://rinkeby.etherscan.io/tx/0x9d3d394867330ae75d7153def724d062b474b0feb1f824fe1ff79e772393d395

数据是事件参数, ABI编码:

000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000003

有一个topic, 1个 32 bytes hash:

0x00032a912636b05d31af43f00b91359ddcfddebcffa7c15470a13ba1992e10f0

这是事件类型的签名的SHA3哈希:

# Install pyethereum # https://github.com/ethereum/pyethereum/#installation> from ethereum.utils import sha3> sha3("Log(uint256,uint256,uint256)").hex()'00032a912636b05d31af43f00b91359ddcfddebcffa7c15470a13ba1992e10f0'

因为Solidity事件为事件签名用掉了一个topic, 留给indexed 参数的只有3个topic.

Solidity Event With Indexed Arguments

下面是有一个 indexed uint256 参数的事件:

pragma solidity ^0.4.18;contract Logger {  event Log(uint256 a, uint256 indexed b, uint256 c);  function log(uint256 a, uint256 b, uint256 c) public {    Log(a, b, c);  }}

有2个topic:

0x00032a912636b05d31af43f00b91359ddcfddebcffa7c15470a13ba1992e10f00x0000000000000000000000000000000000000000000000000000000000000002
  • 第一个topic是方法的签名.

  • 第二个topic是indexed参数的值.

除了indexed 参数,数据是ABI编码:

00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000003

String/Bytes Event Parameter

将事件的参数设为字符串:

pragma solidity ^0.4.18;contract Logger {  event Log(string a, string indexed b, string c);  function log(string a, string b, string c) public {    Log(a, b, c);  }}

交易在: https://rinkeby.etherscan.io/tx/0x21221c2924bbf1860db9e098ab98b3fd7a5de24dd68bab1ea9ce19ae9c303b56

有2个topics:

0xb857d3ea78d03217f929ae616bf22aea6a354b78e5027773679b7b4a6f66e86b0xb5553de315e0edf504d9150af82dafa5c4667fa618ed0a6f19c69b41166c5510
  • 第一个topic是方法的签名.

  • 第二个topic是字符串参数的SHA256摘要.

验证"b"的哈希值和第二个topic是一样的:

>>> sha3("b").hex()'b5553de315e0edf504d9150af82dafa5c4667fa618ed0a6f19c69b41166c5510'

日志数据是2个non-indexed 字符串 "a" 和 "c", ABI编码:

000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000001610000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000016300000000000000000000000000000000000000000000000000000000000000

indexed 字符串参数没有被存储,所以DApp客户无法恢复它.

如果你确实需要最初的字符串, 那就记录2次, indexed 和 non-indexed:

event Log(string a, string indexed indexedB, string b);Log("a", "b", "b");

到此,相信大家对"Solidity Event是怎么实现的"有了更深的了解,不妨来实际操作一番吧!这里是网站,更多相关内容可以进入相关频道进行查询,关注我们,继续学习!

0