Inter-Blockchain Communication (IBC) 协议是一个开创性的框架,旨在促进独立区块链之间的安全无缝通信,使它们能够相互传输代币和数据。IBC 是一项复杂而具有挑战性的技术。
在过去的几个月里,Sei 实验室工程团队观察到在几个第三方服务商上,Sei 区块链上 OSMO 代币的总流入和流出存在差异。Sei 实验室团队经过调查和分析确定了问题的可能原因:这些不匹配是由第三方服务商索引和解释数据的方式导致的。值得注意的是,这并不是由于任何链级问题。
本博文将详细介绍如何发现、调查此问题,并提出两种潜在解决方案。
注:在调查后,Flipside Crypto团队被告知了此问题,并迅速修复,我们对他们的合作和支持表示感谢。
问题最初是通过Flipside 报告暴露的,该报告似乎显示 Sei 区块链上 OSMO 的总流入自启动第二天以来似乎总比总流出要小。这是没有道理的 - 要进行交易流出,必须先放入代币,因此总流入必须等于或大于总流出。唯一可能发生这种情况的合理解释是 Sei 和 Osmosis 两侧 IBC 逻辑存在错误。
Sei 实验室工程团队在 Mintscan 上查找了类似但不同的结果。差异如下:
为了确定这些不一致性的起源,Sei 实验室工程师重新索引了 Sei 主网启动后的前几天数据。他们的研究证实了流入和流出总量是一致的:
(Net_Inflow_18520000_to_19300000 - Net_Outflow_18520000_to_19300000) + Bank Supply of IBC Uosmo on height 18520000 = total supply of uosmo at height 19300000.In this equation: Net_Inflow_18520000_to_19300000 = 289607886926903Net_Outflow_18520000_to_19300000 = 288034713858306 Bank Supply of IBC Uosmo on height 18520000 = 20685000 Bank supply of uosmo at height 19300000 = 1573193753597
Sei 实验室工程师现在已经发现了两个不同的第三方数据提供商看似不一致的流入和流出总量问题,并进行了根本原因分析。
在确认链上总量确实如预期般正确匹配后,关注点转向了第三方服务商如何对 IBC 数据进行索引。Flipside在GitHub上开源了他们的数据模型,因此这是一个好的起点。
Flipside 的数据模型通过观察区块链上的 ‘ibc_transfer’ 活动计算 IBC 流出,通过观察 ‘write_acknowledgement’ 消息和 ‘packet_data’ 属性计算 IBC 流入。
让我们看一下成功的 IBC 转账是如何进行的:
a. 链 A 上的用户启动向链 B 的代币转账。
b. 这个操作导致在链 A 上创建一个表示代币转账的 IBC 数据包。链 A 上的代币被保存在IBC 托管账户中,确保它们不会被双重花费。需要注意的是,这里的每个通道对应一个唯一的托管账户,例如 osmo<>sei 通道的托管账户与 axelar<>sei 通道的托管账户不同。
c. 交易#1:在链 A 上进行一次交易以启动转账。
a. 一个中继者注意到链 A 上的新数据包,并提交了一笔交易到链 B 作为中继数据包。
b. 交易#2:在链 B 上进行一次交易以接收数据包。
a. 链 B 处理数据包,为预期的接收方在链 B 上铸造相应的代币,然后生成确认。
将确认中继到链 A:
a. 一个中继者注意到链 B 上的确认,并提交了一笔交易到链 A 来中继这个确认。
b. 交易#3:在链 A 上进行一次交易以处理确认。
因此,如果所有的IBC 转账消息都被记录,而所有的交易都按照上述方式进行,总数就会匹配,对吧?理论上是的。
实际上,有一个问题:超时。一些 IBC 转账创建时带有超时参数,可以指定为块高度、时间戳,有时两者都有。在此时间或块高度之后,IBC 数据包将被视为超时……那么接下来会发生什么?
如果一个中继者试图将一个已超时的 IBC 数据包中继到链 B 的 IBC 模块,该模块将返回一个超时错误。中继者通知链 A 上的发送模块数据包已超时(使用 TimeoutPacket)。原本要转账的代币被发送回转账发起者,IBC 转账失败。
现在您已经看到了 IBC 转账交易的生命周期,并了解了由于超时而导致这些交易有时会失败的原因,您就能理解在数据中观察到的 IBC 流入 / 流出差异的可能原因。Sei Labs 工程团队回顾了 Sei 区块链启动后几天的交易数据。的确有许多流出 IBC 转账超时的情况。
第三方数据索引器将每个 ibc_transfer 事件都计入 IBC 流出,即使该转账后来超时 - 即使转账交易在源链上最初成功,仍然有可能在中继期间超时。因此,这些数据提供商计算的总 IBC 流出既代表了成功的 IBC 转账,也代表了那些已经超时的转账。这就是为什么 IBC 流出大于流入的原因。
与其处理 IBC 转账事件以获取流入 / 流出金额,Sei Labs 工程团队建议统计从 IBC 模块账户中转出或转入的所有非本地代币的总量,因为这考虑了上述所有情况(即转入、转出以及超时后的退款)。
他们提供了以下代码片段,以实现这种方法来解析金额:
// IBC Transactions: Check tx event for event type 'transfer' between ibc-transfer module account and sei account// Example: {"type":"transfer","attributes":// [{"key":"recipient","value":"sei1z3g0ccd0gpe6m4afjjmtxka269yyrymhwwvf3x"},// {"key":"sender","value":"sei1yl6hdjhmkf37639730gffanpzndzdpmhrn8l3z"}, // {"key":"amount","value":"7898600ibc/2CC0B1B7A981ACC748547..."}]}func ParseBridgeTx(txResponse *sdktypes.TxResponse, chainId string, msgTypes []string) ([]types.RawBridgeTx, error) { // txResponse here is the result the same as `seid q txs --events` for _, log := range txResponse.Logs { for _, event := range log.Events {... if event.Type != transferEventType { continue }... amountCoins, err := sdktypes.ParseCoinsNormalized(amount) for _, amountCoin := range amountCoins { // Case: IBC Transfer Bridge In or IBC Timeout Refund if sender == IBCTransferModuleAccount { bridgeUserAddress = recipient bridgeAddress = sender bridgeTxType = IBCTransferBridgeInType } // Case: IBC Transfer Bridge Out if recipient == IBCTransferModuleAccount { bridgeUserAddress = sender bridgeAddress = recipient bridgeTxType = IBCTransferBridgeOutType }... } } }
在源链上对原生代币的流入和流出金额进行索引的方法与对非原生代币进行索引的方法相同。唯一的微妙区别是,与其从 IBC 模块账户中计算转账信息,你应该从特定的 IBC 托管账户(用于你的通道)中计算。
在本报告中,我们阐述了 Inter-Blockchain Communication(IBC)协议及其复杂的流入和流出索引过程的细微差异。
正如在 Flipside 仪表板中观察到的不一致性所证明的那样,索引中的异常可能导致社区内的误解。在使用 IBC 时,特别是在网络拥塞或遇到意外情况(如数据包超时)的情况下,所有利益相关方都必须意识到 IBC 的细微数据差异。
Sei Labs 工程团队要对所有社区成员和利益相关方表达感谢,感谢他们的耐心和信任。团队已经充分配备并致力于确保我们链上所有 IBC 交易的透明度、准确性和效率。我们坚定不移地致力于建立信任,以维护 IBC 生态系统的完整性。
不想错过 Sei 的最新动态?点击下方按钮免费订阅!
“Sei 中文”官方推特、Sei 中文电报群、Sei 中文公告电报群已开通,现在 Follow 来第一时间了解 Sei 的最新进展:
【免责声明】市场有风险,投资需谨慎。本文不构成投资建议,用户应考虑本文中的任何意见、观点或结论是否符合其特定状况。据此投资,责任自负。