EIP-758: 已完成交易的订阅和过滤器
Authors | Jack Peterson <jack@tinybike.net> |
---|---|
Created | 2017-11-09 |
Requires | EIP-1474 |
简单总结
提供一种方式让外部调用者能够收到已完成交易的通知,并访问交易被挖掘时执行的函数的返回数据。
摘要
当一个新的交易成功提交给以太坊节点时,节点会返回交易的哈希值。如果交易涉及到执行一个返回数据的合约函数,那么这些数据会被丢弃。如果返回数据是依赖于状态的(这种情况很常见),那么调用者就没有直接的方法来访问或计算返回数据。本 EIP 提议,调用者应该能够订阅(或轮询)已完成的交易。当交易被确认时,以太坊节点会将返回数据发送给调用者。
动机
如果函数是通过 eth_sendTransaction
或 eth_sendRawTransaction
RPC 请求执行的,那么外部调用者目前无法访问来自以太坊的返回数据。在许多情况下,访问函数返回数据是一个理想的功能。使外部调用者能够访问返回数据,也解决了内部调用者(可以在交易上下文中访问返回数据)和外部调用者(不能访问返回数据)之间的不一致性。目前,一种常见的解决方法是记录返回数据,但这有几个缺点:它会导致链的膨胀,给调用者带来额外的 gas 成本,并且如果外部调用的函数涉及到其他(内部)函数调用,这些函数也会记录它们的返回数据,那么可能会导致写入未使用的日志。在实现此 EIP 的原始版本时,决定稍微扩展此功能,以便在没有返回数据的情况下,外部调用者也能收到其已完成交易的通知。这可能是因为调用的方法没有返回值,或者因为交易只是简单的价值转移。
规范
订阅
想要在交易完成时收到通知的调用者,会发送一个 eth_subscribe
RPC 请求,其中第一个参数是 "completedTransaction"
:
{"jsonrpc": "2.0", "id": 1, "method": "eth_subscribe", "params": ["completedTransaction", filter]}
filter
参数是一个字典,包含 3 个可选的命名参数:from
、to
和 hasReturnData
。from
和 to
可以是单个地址,也可以是地址列表。它们用于过滤掉任何不是从 from
列表中的地址发送,并且不是发送到 to
列表中的地址的交易。hasReturnData
是一个布尔值——如果指定为 true
,则仅接收包含 returnData 的已完成交易的通知。
例如,要将结果限制为源自以下两个地址之一(0x3f7d39bDBf1f5cE649c194571aEd3D2BbB2F85ce 或 0x7097f41F1C1847D52407C629d0E0ae0fDD24fd58)的合约创建:
filter = { "from" : ["0x3f7d39bDBf1f5cE649c194571aEd3D2BbB2F85ce",
"0x7097f41F1C1847D52407C629d0E0ae0fDD24fd58"],
"to" : "0x0"
}
要将结果限制为合约地址 0xD9Cb531aB97A652c8fC60dcF6D263fcA2F5764e9 上的方法调用:
filter = { "to" : "0xD9Cb531aB97A652c8fC60dcF6D263fcA2F5764e9", "hasReturnData" : true }
或者,要在 rpc 客户端提交的任何交易完成时收到通知,而不做进一步的限制:
filter = {}
收到请求后,以太坊节点会返回一个订阅 ID:
{"jsonrpc": "2.0", "id": 1, "result": "0x00000000000000000000000000000b0b"}
假设调用者随后通过 eth_sendTransaction
或 eth_sendRawTransaction
RPC 请求提交了一个交易,该交易的哈希值为 "0x00000000000000000000000000000000000000000000000000000000deadbeef"
。当交易被确认(挖掘)时,以太坊节点会向调用者推送一个通知。如果交易是对合约的方法调用,这将包括被调用函数的返回值(例如 "0x000000000000000000000000000000000000000000000000000000000000002a"
):
{
"jsonrpc": "2.0",
"method": "eth_subscription",
"params": {
"result": {
"transactionHash": "0x00000000000000000000000000000000000000000000000000000000deadbeef",
"returnData": "0x000000000000000000000000000000000000000000000000000000000000002a"
},
"subscription": "0x00000000000000000000000000000b0b"
}
}
调用者在两种情况下会收到关于其交易的通知:首先是当交易被确认时,以及如果交易受到链重组的影响时(带有额外的 "removed": true
字段)。对于从客户端提交的,并且在订阅之后被确认的所有交易,都会向客户端发送通知。如果指定了 from
、to
或 hasReturnData
,那么只有符合筛选标准的交易才会生成通知。与其他订阅一样,调用者可以发送 eth_unsubscribe
RPC 请求来停止接收推送通知:
{"jsonrpc": "2.0", "id": 2, "method": "eth_unsubscribe", "params": ["0x00000000000000000000000000000b0b"]}
轮询
推送通知需要全双工连接(即,websocket 或 IPC)。使用 HTTP 的调用者可以发送一个 eth_newCompletedTransactionFilter
请求,而不是订阅:
{"jsonrpc": "2.0", "id": 1, "method": "eth_newCompletedTransactionFilter", "params": [filter] }
以太坊节点会返回一个过滤器 ID:
{"jsonrpc": "2.0", "id": 1, "result": "0x1"}
当一个交易被提交时,以太坊节点会将交易通知(包括返回值)推送到一个队列中,当调用者使用 eth_getFilterChanges
进行轮询时,该队列会被清空:
{"jsonrpc": "2.0", "id": 2, "method": "eth_getFilterChanges", "params": ["0x1"]}
节点会返回一个交易哈希及其对应返回数据组成的数组,按照计算的顺序排列:
{
"jsonrpc": "2.0",
"id": 2,
"result": [{
"transactionHash": "0x00000000000000000000000000000000000000000000000000000000deadbeef",
"returnData": "0x000000000000000000000000000000000000000000000000000000000000002a"
}]
}
所有在初始 eth_newCompletedTransactionFilter
请求之后 被确认的交易都包含在这个数组中。同样,如果 filter
参数是一个非空字典(包含 from
、to
或 hasReturnData
中的任何一个),那么只有符合筛选标准的交易才会生成通知。请注意,在轮询的情况下,以太坊节点无法确定提交交易的 RPC 客户端与创建过滤器的客户端是否相同,因此没有基于交易提交位置的限制。
原理
EIP-658 最初提议将返回数据添加到交易回执中。然而,返回数据是不收费的(因为它没有存储在区块链上),所以将其添加到交易回执中可能会导致 DoS 和垃圾邮件的机会。相反,一个简单的布尔值 status
字段被添加到交易回执中。这个修改后的 EIP 658 版本包含在拜占庭硬分叉中。虽然 status
字段很有用,但应用程序通常也需要返回数据。
使用此处概述的策略的主要优势是效率:不需要在区块链上存储额外的数据,并且对节点施加的额外计算负载最小。虽然不支持事后查找返回值,但这与返回数据的传统使用方式一致,即只有在函数返回时,调用者才能访问返回数据,并且不会存储以供以后使用。
版权
版权及相关权利通过 CC0 放弃。
Citation
Please cite this document as:
Jack Peterson <jack@tinybike.net>, "EIP-758: 已完成交易的订阅和过滤器 [DRAFT]," Ethereum Improvement Proposals, no. 758, November 2017. [Online serial]. Available: https://eips.ethereum.org/EIPS/eip-758.