以太坊的事件与日志

2024-05-11 06:58
文章标签 日志 事件 以太

本文主要是介绍以太坊的事件与日志,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

1.什么是事件
事件是以太坊提供的一种链内链外沟通的一种机制。通过触发事件,智能合约可以通知链外组件某个交易完成了什么事儿。
下面是一个 ERC20 合约里常见的 Transfer 事件定义,通过 event 关键字表明这是一个事件定义声明。

event Transfer(address indexed from, address indexed to, uint256 value);
在 transfer 方法的实现中,我们会像下面代码里展示的这样通过 emit 关键字触发事件的发生。function transfer(address _to, uint256 _value) public returns (bool) {...emit Transfer(msg.sender, _to, _value);return true;}
  1. 什么是日志
    在以太坊的语境里,日志代表对事件的存储。下面是我从 ropsten 测试网上读取的交易回执,在里面我们可以看到 logs 数据项,这个就是我们这里所说的日志,合约执行时没触发一次事件,在交易回执里的 logs 数据项数组里就会多一个日志条目出来。

eth.getTransactionReceipt(“0xe03fac05ff4dde83fc9267184fd8c08bd78599f950e817dbf7fa4a4d4d319ce2”);
{
blockHash: “0x7eaf6abe64592d10828e136635aa6be6f4d09da3bb5b9fddf87773ee152d657c”,
blockNumber: 4654718,
contractAddress: null,
cumulativeGasUsed: 52464,
from: “0x076979a0b3c87334e5d72e3afcafaa80f7888cac”,
gasUsed: 52464,
logs: [{
address: “0x73c2a5b1a32fa8e33101a6ab119203f4417feae4”,
blockHash: “0x7eaf6abe64592d10828e136635aa6be6f4d09da3bb5b9fddf87773ee152d657c”,
blockNumber: 4654718,
data: “0x0000000000000000000000000000000000000000000000056bc75e2d63100000”,
logIndex: 0,
removed: false,
topics: [“0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef”, “0x000000000000000000000000076979a0b3c87334e5d72e3afcafaa80f7888cac”, “0x000000000000000000000000cd9f286ba6a3d2df7885f4a2be267fc524d32bd3”],
transactionHash: “0xe03fac05ff4dde83fc9267184fd8c08bd78599f950e817dbf7fa4a4d4d319ce2”,
transactionIndex: 0
}],
logsBloom: “0x20000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080000008000000000400000000000000000000000000000000000000040000000000000000100000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000200000000000000000000000200000002000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000040000000000000000400”,
status: “0x1”,
to: “0x73c2a5b1a32fa8e33101a6ab119203f4417feae4”,
transactionHash: “0xe03fac05ff4dde83fc9267184fd8c08bd78599f950e817dbf7fa4a4d4d319ce2”,
transactionIndex: 0
}
在这个日志里,我们可以看到很多和事件触发上下文相关的信息,比如 合约地址 address、所在区块哈希 blockHash、所在区块号 blockNumber、所属交易哈希 transactionHash 等等。

这里面最核心的就两个数据:topics 和 data。我们这里看到 topics 是个数组,这个数组的第一个元素就代表所触发的事件,是个 256 位的数字,用 16 进制表示。只是看这么个字符串,我们并不能确定这是哪个事件,这时候就需要借助于合约的 ABI 文件。在 ABI 文件中找出 type 为 event 的那些元素,如下面所示:

{"anonymous": false,"inputs": [{"indexed": true,"name": "owner","type": "address"},{"indexed": true,"name": "spender","type": "address"},{"indexed": false,"name": "value","type": "uint256"}],"name": "Approval","type": "event"},{"anonymous": false,"inputs": [{"indexed": true,"name": "from","type": "address"},{"indexed": true,"name": "to","type": "address"},{"indexed": false,"name": "value","type": "uint256"}],"name": "Transfer","type": "event"}

为了找出 topic 所对应的事件,我们需要计算每个事件的签名并找到匹配的签名。 签名是事件名和输入参数类型的 sha3 散列,参数名被忽略。 对于事件 Transfer(address indexed from, address indexed to, uint256 value),签名将是 sha3 (‘Transfer(address,address,uint256)’),这些都是可以从 ABI 中获得。

web3.sha3(“Transfer(address,address,uint256)”)
“0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef”
web3.sha3(“Approval(address,address,uint256)”)
“0x8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925”
很明显可以看出,“Transfer(address,address,uint256)” 这个事件的签名和上面提到过的 topics 的第一个数据元素是一致的,说明这个日志就是对应的 Transfer 事件。

[“0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef”, “0x000000000000000000000000076979a0b3c87334e5d72e3afcafaa80f7888cac”, “0x000000000000000000000000cd9f286ba6a3d2df7885f4a2be267fc524d32bd3”],

现在我们知道了,日志里面 topics 数组里的第一个数据元素就是事件的签名。那么 topics 里其它的元素是什么呢?会看上面截取的 ABI,我们可以看到在事件元素的 inputs 数据项中,有的 indexed 的值为 true,有的为 false,我们在声明一个事件时,也可以指定事件的参数是否 indexed,比如下面这个 Transfer 事件的声明,from 就是表明为 indexed,表明是被索引收录的。

event Transfer(address indexed from, address indexed to, uint256 value);
topics 里的其它数据元素就是被索引收录的事件参数值,所有在 topics 里的内容,都是被索引收录,可以通过 bloom filter 进行过滤的。
日志里还有一个关键内容就是 data,这个比较容易理解,就是触发事件的时候传给事件的实际参数值,值得注意的是这里面只包含未索引的数据项。

  1. 有什么作用

最大的作用就是进行检索了,以太坊提供了基于 http 的 JSON-RPC 和基于 websockets 事件订阅监听。我们可以提供相应的 topics 对某个事件或事件数据进行检索。这在对 dApp 开发是很有帮助的。
值得注意的时,当我们对事件进行监听的时候,如果是基于 http,这种监听本质上仍然是种定期轮询机制。

  1. web3j 对 websockets 的支持
    在最新发布的 web3j 4.0.x 中,web3j 刚刚加入了对 websockets 的支持。
    而被广泛使用的 Infura 节点服务仅支持通过 websockets 对事件进行监听,正是由于这个原因,很多使用 web3j 的团队不得不自己维护以太坊全节点,通过轮询的方式去监听合约事件的发生。
    使用最新版本的 web3j,通过下面的示例代码,就可以对合约事件进行监听了。
// 注意 infura websockets 服务的路径和 http rpc 服务的路径是有所不同的
final WebSocketClient webSocketClient = new WebSocketClient(new URI("wss://ropsten.infura.io/ws/v3/<你的id>"));
final boolean includeRawResponses = false;
final WebSocketService webSocketService = new WebSocketService(webSocketClient, includeRawResponses);// 这个没有出现在官方文档中,但必须要加上,否则会有 WebsocketNotConnectedException。
webSocketService.connect();
final Web3j web3j = Web3j.build(webSocketService);
System.out.println(event.getParams()));// 0x73C2a5B1A32fa8E33101A6ab119203f4417feAE4 为合约地址
final Flowable<LogNotification> logNotifications = web3j.logsNotifications(Arrays.asList("0x73C2a5B1A32fa8E33101A6ab119203f4417feAE4"),// EventEncoder 这里主要为了生成事件签名
Arrays.asList(EventEncoder.encode(DfttToken.TRANSFER_EVENT)));// 对数据进行监听
logNotifications.subscribe(event -> {Log log = event.getParams().getResult();List<Type> eventsData = FunctionReturnDecoder.decode(log.getData(), DfttToken.TRANSFER_EVENT.getNonIndexedParameters());for (Type eventDataItem : eventsData) {System.out.println(eventDataItem.getTypeAsString() + ": " + eventDataItem.getValue());}});

注:本博客转载于 https://blog.csdn.net/weixin_34352449/article/details/87061495

这篇关于以太坊的事件与日志的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/978821

相关文章

vue+elementui分页输入框回车与页面中@keyup.enter事件冲突解决

解决这个问题的思路只要判断事件源是哪个就好。el分页的回车触发事件是在按下时,抬起并不会再触发。而keyup.enter事件是在抬起时触发。 so,找不到分页的回车事件那就拿keyup.enter事件搞事情。只要判断这个抬起事件的$event中的锚点样式判断不等于分页特有的样式就可以了 @keyup.enter="allKeyup($event)" //页面上的//js中allKeyup(e

通知中心设置一个键盘的捕捉事件

//通知中心监听键盘的frame发生改变

Sapphire开发日志 (十) 关于页面

关于页面 任务介绍 关于页面用户对我组工作量的展示。 实现效果 代码解释 首先封装一个子组件用于展示用户头像和名称。 const UserGrid = ({src,name,size,link,}: {src: any;name: any;size?: any;link?: any;}) => (<Box sx={{ display: "flex", flexDirecti

XMG 触摸事件的处理过程

1.自己本身并不处理,顺着响应者链条向上传递,将事件交给响应者进行处理 2.touches默认做法:把事件传递到上一个响应者 3. super是父类不是父控件

linux匹配Nginx日志,某个字符开头和结尾的字符串

匹配 os=1 开头, &ip结尾的字符串 cat 2018-06-07.log | egrep -o ‘os=1.*.&ip’ 存入日志。然后使用submit 前面和后面的值去掉,剩下就是需要的字符串。 cat 2018-06-07.log | egrep -o ‘os=1.*.&ip’ >log.log

springboot学习02-[热部署和日志]

热部署和日志 热部署 热部署

C# 日志框架Serilog使用

1、框架和说明        C#日志框架Serilog支持多种场景输出,简单验证了一下,比较方便        包的安装,推荐直接使用“推荐NuGet包管理器”安装Serilog.AspNetCore,常见的组件都已经集成在一个包中,使用比较方便 2、配置文件        Serilog可以由配置文件来定义行为,而且配置文件的修改即时生效。参考配置文件如下: {"Serilog":

Android Log日志 - 打印不全问题

AndroidStudio在打印Log的时候目前支持4*1024长度,超出部分不能打印。当你在各种百度之后有对应的解决办法,但是每次都是部分代码,看着都忧伤。索性此次项目调试的数据也是比较多滴,目前就准备对Log开刀来写一个Log类,还是如以往的性格直接写完整的类,方便需要的人用。反正又不是什么高深的东西,为了给被方便同时也是给自己方便。 /*** Relin* 2019-07-10 10:40

【Android面试八股文】你能说一说在平常开发过程中你是如何解决事件冲突问题的吗?

文章目录 一、内部拦截法(Inner Intercept)1.1 工作原理:1.2 实现步骤:1.3 适用场景:1.4 内部拦截法示例1.4.1. 自定义 `RecyclerView` 以处理内部拦截1.4.2. 在布局中使用 `InterceptableRecyclerView` 1.5 为什么`requestDisallowInterceptTouchEvent(boolean disa

注解+Aspect 省时省力的管理好接口日志

背景 无论是对外提供的RPC接口,还是项目内的普通方法,我们都会有需要打印方法入参、出参的需求,方便在遇到问题时通过查看日志快速定位,我们也会需要对方法的执行时间进行打印 方便分析和调优。 比较笨的做法就是在每个需要打印日志的地方使用log.info对参数进行打印,在每个方法内部方法体前后获取系统时间 在最后打印时间差 但这种对方法自身业务逻辑没有什么意义的的代码 侵入性太强 编写时也浪费时间