观点

分析 Mempool,复盘 “黑色星期四”

曾汨   |     |   315 次阅读

Blocknative 已经找出了 MakerDAO 在 3 月 12 日和 13 日时的清算活动乃是有人刻意为之的证据。这些证据是从 “Mempool”(交易内存池),即矿工打包区块时候的备选交易池,中来的。交易内存池是一个以太坊生态中经常被人忽略 —— 也不受大家重视 —— 的部分。

作为交易内存池分析专家,Blocknative 运营着一个由遍布全球且配置各不相同的 Geth 节点和 Parity(OpenEthereum)节点组成的网络。这一基础设施使我们能部署实时的交易监控服务,我们可以捕捉、规范化和归档内存交易池的状态变化(如无专门操作,这些变化都是川流不息、转瞬即逝的)。Blocknative 在 “黑色星期四” 时捕捉到了 3000 万行数据,使我们能做一项开放式的研究;迄今为止,我们已经发现了数个似乎已被利用过的 “漏洞”。

虽然很多人都已写过 “黑色星期四” 的结果,但在本文中我们会首次披露 Blocknative 内存池存档中的数据,用新的数据和理论说话。我们也把分析要用的底层内存池交易数据集合开放出来了,供大家审阅(见下文)。

如果你是一位安全研究员,想了解更多我们的发现,或想发现其它潜在的交易池异常,请联系我们

总结

Blocknative 的内存池法医报告显示,内存池中有三大因素影响到了 3 月 12 日和 13 日的事态:

  1. 被阻塞的交易 —— 内存池的拥堵极大地提高了交易卡壳率,让同一地址连续发出的多笔交易都被卡住、不能得到处理;
  2. 交易池 “压缩” —— 交易池中可上链部分(即被矿工认为 Gas 费用足够高的交易)比重的大幅减少,可能影响到了 Gas 价格的估计;
  3. “Hammerbot” —— 自动化的交易系统加剧了交易池堵塞,因此与交易池压缩效应相叠加;

我们出版这份分析以及相关的数据,是为了引起大家对动态交易风险的关注,这种风险在区块链网络拥堵期间可能会迅速出现。

背景: “黑色星期四” 概况

在 2020 年 3 月 12 日,密码学货币市场上出现了有史以来最大的抛售潮,几个小时里面,ETH 价格暴跌 43%,BTC 的价格跌掉了 39%。随着价格下跌,一个负反馈循环出现,多种 DeFi 合约内都开始出现流动性降低和强制清算活动。在此期间,这种下行的压力使得每一个尝试在网络上发送交易的个体都遇到了严酷的挑战。

1.png

- UTC 时间 2020 年 3 月 12 日到 13 日期间按小时加权的 ETH 平均价格。数据来源:cointelegraph.com -

迅速的价格变化导致了以太坊交易池的持续拥堵,因为自动化的交易系统(比如交易机器人)总是按照程序对波动性机械地作出反应。

如此堵塞情形的一个负面后果是 MakerDAO 债仓清算活动中出现了 “0 价格拍卖” 现象。在 “黑色星期四” 期间的 3994 个清算拍卖中,有 1462 个(36.6%)债仓的担保品是被 0 价格拍走的。在大概 12 小时的时间里,锁在债仓中、总计 832 万美元的担保品被 0 价格拍走,没有让系统回收到一分钱。更多细节请看 MakerDAO 自己发布的《2020 年 3 月 12 -13 日的价格暴跌及其对 MakerDAO 的影响》一文。

1. 由交易池堵塞导致的交易卡壳

3 月 12 日时 ETH 价格的迅速变化导致用户大量发送交易,产生了交易池内部的拥堵。对价格波动自动作出反应的机器人的活动加剧了这一情形。

2.png

- 进入交易池的待打包交易计数 vs. 打包上链的交易计数(按小时计) -

结果就是大量的交易通过网络传播,迫使使用默认交易池设置的节点通过下列手段来保护自己的系统资源:

  1. 删除,或者说丢弃掉许多有效的交易;
  2. 拒绝,或者说无视掉许多有效的交易。

交易池通常会用最低手续费门槛来选择放弃哪些交易。虽说这些被丢掉的交易并没在整个网络中 “完全丢失”,但被丢掉的交易可能会遭遇显著的时延,直至网络条件回归正常。

3.png

- 2020 年 3 月 11 日至 3 月 14 日期间,到达交易池的被丢弃掉的交易和卡壳交易(不能处理的交易)计数(按小时计)-

更重要的是,丢弃交易还会带来一个副作用:增加交易卡壳的可能性。

删掉一笔交易之后,该节点不会留下关于该笔交易的任何信息,比如发送地址和交易 nonce。因此,当同一个地址的一笔新的交易(使用新的一个 nonce)到达交易池,该节点会发现该地址已处理交易和这笔新交易之间有 nonce 空缺(nonce gap)(因为该新交易的上一笔交易还没得到处理),因此也不能处理这笔新交易。受此效果影响的交易就只能放到该节点的交易池中无法处理的队列中。这些交易,无论所支付的交易手续费(即 Gas 费)有多高,都一概会被卡住、无法处理。

想了解更多细节,请看我们此前讲解交易排序的文章:《节点是你通往内存池的网关》

与此同时,网络的堵塞导致进入内存池的 Gas Price 门槛随之迅速提高,因此最初的一些交易现在会因为 Gas Price 太低而被拒绝。而且,因为之前被删除的交易不能回到交易池中来,交易的 nonce 空缺问题又变得更严重了。事实上,一些节点实现会在一段时间内主动无视掉这些被拒绝的交易,以保护自己不受点对点网络中的泛滥攻击(spamming)影响。所以, nonce 空缺实际上会锁住这些受影响的地址,使得他们无法完成新的交易。

卡壳交易更有可能影响会发出许多新交易的地址,包括自动化交易系统、支付网络,甚至交易所。这些系统想回到正轨,但通常会加剧拥堵,因为越来越多交易被推迟处理。

有没有解决办法?你得主动发现自己何时开始遭遇卡壳。这可能有点年,因为你的交易可能仅在某些节点处是卡壳的,但并不是在所有节点处都面临卡壳。因此,你必须确定第一笔被丢弃、导致 nonce 空缺的交易,然后立即用可以得到打包的 Gas Price 加速让这笔交易上链。最后,你还得继续监控一开始发现卡壳的交易,确保它从不能处理的队列中移回到了交易池的待打包部分中,并成功上链。如果你的交易还是卡壳,重复上述加速步骤,直到你可以确认所有导致 nonce 空缺的交易都已成功上链。当然,这也是我们开发并运营我们的 Notify API 的理由之一。

2. 压缩内存池中可上链的部分

网络阻塞的出现 —— 及其导致的交易卡壳 —— 使得交易池中可上链交易的比重迅速缩减。我们管这叫 “交易池压缩”

矿工的激励分两部分:区块奖励和交易的 Gas Price;所以收益最大化需要打包 Gas Price 最高的那部分交易,挖矿时要根据交易池 —— 即候选交易(也可以说候选区块)—— 的情况(也就是各交易愿意给多高的 Gas Price)来决定打包哪些交易。

在交易池拥堵时,多种行为会导致交易池压缩,对矿工来说值得考虑的交易比重越来越小:

  1. 资源耗尽:在某些节点实现中,卡壳交易数量的迅速上升会消耗掉可观的交易内存池资源。这又反过来导致节点处理有效 pending 交易的可用资源进一步减少。
  2. 不断升高的 Gas Price:ETH 价格的下跌导致许多交易变得 “高度紧急”,因为这些交易在构造时是希望能够赶在 ETH 进一步下跌时上链确认的。这使得个体用户也好、自动化机器人也好,都赶紧提高 Gas Price。因为不是所有参与者都这样密切关注着堵塞情形,待打包交易总体包含了反常比例的低价 —— 因此不可能被打包的 —— 交易。
  3. 反应慢半拍的定价算法:交易池中待打包交易的 Gas Price 分布,以及近期被打包交易的 Gas Price,使得一些预测合适 Gas Price 的服务的测算出现扭曲。但这(些服务报出已低于实际情形的 Gas Price)就导致更多 Gas Price 过低的新交易出现,又进一步加剧了测算值与实际值的不一致。

交易池堵塞出现时,让交易打包上链所需付出的 Gas Price 会升高是意料中的事情。但是,因为并不是所有人都跟踪到了 Gas Price 这种迅速的提高 —— 包括,尤其是那些有 Gas Price 报告功能的服务 —— 上链所需的平均 Gas 价格开始偏离进入交易池的交易平均 Gas 价格。而一般的出价上涨跟不上当时的价格上涨速率。

在 3 月 12 日,我们的数据平台发现了交易手续费定价的明显偏离。在 3 月 13 日,交易池中部分交易的 Gas Price 与上链交易的 Gas Price 差额在可预料范围内,但在 3 月 12 日,已挖出交易和未挖出交易的 Gas 出价简直是天壤之别

不能上链的交易要成为可以上链的交易,一般来说标准的操作就是提高 Gas Price。但是,在 3 月 12 日,这样做根本就没用。因为,大量交易池资源被以几乎同样的 Gas Price 重发的交易消耗掉了

4.png

- 2020 年 3 月 12 日至 13 日每小时的 Gas Price 箱型图。每个小时的箱型都划分了 1 分位值和 3 分位值,两者中间的线表示中值,而箱顶和箱底的线表示最大值和最小值。已忽略掉了异常值(1.5 IQR) -

这张图反映了真实情形吗?还是说这些出价过低的交易本身就是为了堵塞掉交易池呢?如果真是有意为之,为什么呢?

3. Hammerbot 交易导致内存池失真

我们的内存池存档数据暗示,机器人成功地提高了堵塞情形,并扭曲了交易池中交易的 Gas 价格分布,而且还没有导致交易手续费的相应提高。

这样做的净效果就是交易卡壳率的提高和 Gas Price 报告服务扭曲,结果是交易池一场,使天平偏向了某些特定的交易 —— 即,提高了清算 CDP 仓位的交易以 0 价格成功竞拍的几率。

机器人用本来就无意提交上链的交易捶打(hammer)交易池。这些 Hammerbot 通过发送置换率极高的交易(不相应提高 Gas 价格但又不断重发)消耗掉了交易池的资源但是,交易池还有一种设计,是要求重发交易至少要提高 10% 的 Gas 价格,本身就是用来防止此类行为的。那这些交易是如何实现置换的呢?

答案很简单:异常高的交易丢弃率导致节点 “失忆”(见上文)。

Hammerbot 等待着 —— 或者仅仅是预估 —— 自己发出的交易从交易内存池中丢弃,然后立即用相同(甚至更低)的 Gas 价格重发交易。因为节点 “忘记掉了” 之前被丢弃的交易,自然就尽职地把这些置换后的交易当成有效交易接了过来。当然,结果就是进一步的拥堵。

Hammerbot 用显然是 “自动化” 的方式让自己的交易变得畸形,每一次置换都包括了稍微更改过的合约输出。因此,每一笔 Hammerbot 交易都有一个独特的哈希值,可以绕过所有节点的点对点网络协议中的泛滥攻击过滤保护。

如下图中重点标出的粉色线所示,从 UTC 时间 3 月 12 日 9 点开始,我们的交易池数据平台发现根本不可能被打包的待打包交易数量急剧上升。35 分钟后,此类不可能上链的交易产生的速度翻了一倍,在 10 点之后才降为线性增长的模式。

5.png

- 从 UTC 时间 8 点到 10 点期间达到交易池的交易计数,以分钟计。蓝线表示最终上链了的交易计数;而橙线表示根本没有上链的交易计数。-

即使这些交易似乎没有上链的意图,这些从未被打包的 Hammerbot 交易的 Gas Price 还是显得太低了:在市场要求给出 30 Gwei 乃至更高价格时,这些交易几乎总是只给 5 Gwei。Hammerbot 交易既没有像套利机器人经常做的那样加速交易(从而炒高 Gas Price),又高效地消耗掉了节点的交易池资源。

从整体上来看,虽然进入交易池的交易数量急剧增加,交易池中还是有很大一个比例的交易 Gas Price 被人为压低了。

6.png

- 从 UTC 时间 8 点到 10 点间的交易 Gas Price 箱型图(按分钟划分)。蓝线表示最终上链的交易的情形。橙线表示从未上链过的交易的情形。-

拿出一笔 Hammerbot 交易作为例子(我们用的是一笔 nonce 值为 3070 的交易),可能有说明价值。这里是最终的交易哈希值:0x5b00c13020b82c9e8a098393564feca976dbbd2e8da6c54263f6e492be56fbfb

仅仅用你惯用的区块浏览器检视这笔交易并不能给出除了其区块确认信息以外的洞见。表面上来看,这笔交易平平无奇。但 Blocknative 的交易池数据平台检测到了这笔交易上链之前使用这个 nonce 值的 418 笔独特交易。这些交易都是在一个小时内出现的,也就是这些置换交易平均每 6.86 秒重发一次,而某些置换交易之间的时间差不超过 0.1 秒。

7.png

- 地址 0x5cf2fa4e0c84e71fd2e4fa86d2fa64b7a50a6fc0 从 UTC 时间 9 点开始在相连的 4 个 nonce 上发起的置换交易次数(按分钟计)-

大多数置换交易都使用同样高的 Gas Price,都是因为节点的交易丢弃机制才作为 “新” 交易成功进入到节点的交易池内。只有最后一笔想要上链的置换交易才需要给出更高的 Gas Price,然后被打包到区块内。

8.png

- 地址 0x5cf2fa4e0c84e71fd2e4fa86d2fa64b7a50a6fc0 从 UTC 时间 9 点开始在相连的 4 个 nonce 上的平均 Gas Price(按分钟作平均化)-

这种模式使我们很难说这些 Hammerbot 交易到底是有心还是无意的。实际上,我们的系统在一分钟之内记录下了超过 20 个从 UTC 时间 9:05 开始活跃的可能的 Hammerbot 地址:

地址 Nonce 一分钟之内的置换交易笔数
0xdd3b6ae71ff420375fefaa2448046a37beeed800 9211 22
0x704bf43e578b2b912584495467c3a2210deaec11 7307 20
0x56f0bdbdf48556ed41248021fbc8027f69c41f27 5004 19
0x4a1351523071ed88d20afd1d10cda75d1b34f4e7 6644 19
0x2a7139e98f47c2fc65aec0de9a3adb8ffd46206a 10712 19
0x602bf7ba827a61d708614eb35284e79654c7e58f 8458 18
0xe6919901cef07c15373feac6871046848efd4212 2745 17
0x58500f55ac86a3022703806a7415cac321cce2a1 2587 16
0x6861d397f7ff510a1ab4bb60434d8a9c4dd01240 5837 16
0x5cf2fa4e0c84e71fd2e4fa86d2fa64b7a50a6fc0 3067 15
0x3a711e39640b802d201391d14d5f2d3159f07957 2279 14
0x4769ad67bce2779d1374675069a3b78b8dafbea6 2671 14
0x97e7ba1f2db224dac7e0a812a071474e7c60819a 9715 14
0x124f58b41fd43a498fe7041bc2d9d5813c4f80d7 2379 13
0x124f58b41fd43a498fe7041bc2d9d5813c4f80d7 2379 13
0x82b1e33c6465a9bedefd4af8a2c1cfc1c874bfbe 2516 13
0x3a711e39640b802d201391d14d5f2d3159f07957 2354 12
0x95227df275141c9bbf679f695668a838a31459fc 2435 12
0x5e4f7ce2607c39f4ec08355e2ea48e50f6f77bff 9208 12
0x3046a9743a1b8d967f2ddb014e341b0eca41c191 6193 10
0xb123a59c4e3ff44e57b3113234fa6ebe804e996d 2223 8
0xc1bfbc44536200b02f92aec4dee08ea390fa0535 2187 6

- 如果你是上述任一地址的运营者,我们很希望跟您聊聊。请联系我们 -

注意:这些特征明显的、使用同样的 Gas Price 重发被丢弃交易的 Hammerbot 行为,也可能跟最近大家讨论的 “backrunning” 行为有关。见此处此处

交易池漏洞对 MakerDAO 的影响

MakerDAO 的担保债仓(CDP)是用户生成稳定币 DAI 时托管被锁定的担保品 ETH 的智能合约。因为 ETH 的价格有波动,而 DAI 希望能保持 1 美元的价格,所以维持一个开放的 CDP 所需的担保品数量是不断变动的。

当 3 月 12 日 ETH 价格暴跌时,大量的 CDP 立即变成了担保不足的状态,需要被强制清算。系统为保证担保品清算时可以获得竞争性的市场价格,安排了折价机制:参与清算者可以以折价买到担保品 ETH;这就使得许多人都愿意运营多种多样的 Keeper 机器人。清算活动的表现形式为拍卖

任何 Keeper(看护者)都可以用 链上 出价参与拍卖,而一个看护者出价之后,另一个看护者想要与之竞争必须在 10 分钟之内发送竞拍价更高的交易。每当有新的竞拍交易到达,都会刷新 10 分钟的竞拍窗口期。如果 10 分钟之内系统没有收到更高的出价,则拍卖结束,最高出价者胜出。[注意:在 “黑色星期四” 下午,MakerDAO 把竞拍窗口期从 10 分钟提高到了 6 小时。]

但是,当矿工节点因为网络哟难度和交易池压缩而以异常高的比例丢弃交易时,许多看护者的交易都被卡住了,因此根本没能及时让清算 CDP 的竞拍交易成功上链。结果就是看护者们无法在由 0 价格竞拍交易开启的窗口中可靠地开展竞争,即使看护机器人正确地发现了 0 价格拍卖并发送出了更高出价的交易也没用。

最终,因为拥堵和实际手续费的混淆,看护者无法恰当地解读出 Gas Price 的实际上涨幅度,看护者的交易也因此被节点丢弃。接下来是产生 nonce 空缺、交易在使用默认交易池设置的节点处卡壳。遭遇此种情形的看护者机器人为其他拍卖发出的所有后续竞拍交易会全部被卡住,让该看护者根本无法在 10 分钟的竞拍窗口内参与交易,最终让 0 价格竞拍交易得逞

下面我们用 1866 号拍卖作为例子来说明上述过程:

  1. 一个 “0 价格竞拍机器人” 在 UTC 时间 15:59:50 时以 200 Gwei 的价格发送一笔 0 价格竞拍交易。该交易在 26 秒后成功上链,10 分钟倒计时就此开始。
  2. 一个 “诚信看护者机器人” 在 16:08:01 时以 450 Gwei 的 Gas Price 发送一笔竞价交易。这笔交易是在 10 分钟内发出的 —— 之隔了 8 分 11 秒 —— 而且 Gas 价格还是最初那笔 0 价格竞拍交易的 2.5 倍。如果能在接下来的 1 分 49 秒内被打包到区块中,1866 号拍卖就会像大家预期的那样进行。但是,这笔竞拍交易因为这个好机器人之前发送的一笔交易被丢弃(也就是产生了 nonce 空缺)而被卡住了。
  3. 这个好机器人在 16:15:31 又发出一笔 4500 Gwei 的竞价交易。但是哪怕 Gas Price 提高了 10 倍也无济于事,因为这个地址被 nonce 空缺锁死了。再然后,等到竞价交易不再被卡的时候,已经过了 10 分钟的窗口期,交易自然就失败了。

上述模式可能重复了一整天,最终导致总计 832 万美元的担保品被 0 价格拍走。值得指出的是,大多数 Hammerbot 的活动似乎都在当天较早的时候发生,这样就造成了后来影响到 Keeper 机器人交易的拥堵情况。

Gas Price 的迅速上升、导致 Gas 价格估计失灵的交易池压缩,同样对其它价格信息传输机制造成了负面影响。这导致了定价方面的混乱,也有可能使得面临清算风险的 CDP 持有者推迟了添加担保品的决定。

总结

总结一下,我们对 “黑色星期四” 交易池状况的事后检验暗示了下述情况:

  1. 多种 Hammerbot 的活动导致挖矿节点的交易池饱和。这推高了以太坊网络的拥堵程度并使之不断上升。
  2. 发送许多以同样的 Gas Price 发出的置换交易在点对点 gossip 层和节点本身产生了巨大的开销。
  3. 正常交易的传播受到阻碍,大量的交易被挖矿节点丢弃或者拒绝。
  4. 这又反过来提高了出现 nonce 空缺和卡壳交易的几率,还扭曲了对 Gas Price 的估计。
  5. 虽然自动化的交易系统通常被设计成会自动提高交易的 Gas Price,许多交易系统并不能很好地处理 nonce 空缺问题 —— 甚至是完全束手无策。

以上就是我们在研究过程中的发现。我们的数据集可能还能揭示出更多的证据 以及/或者 新的发现来支持或证否我们的结论。

为加速这个发现过程,我们现在把那两天的交易池存档数据开放给社区,我们希望这能促进对交易池在 “黑色星期四” 及以太坊网络其它类似事件中所扮演的角色的研究。

建议:保护好你自己,以及你的用户

交易池是区块链生态系统非常关键的一环,虽然它瞬息万变,常常被忽略。因此,交易池中藏着很多开发者和用户 “完全无知的未知之物”。

现在,我们并不知道有多少人在开发这样的利用交易池弱点的技术 —— 只是显然有人在利用交易池的特点。我们也不知道有多少这样的弱点存在 —— 只是那些复杂的利用方法似乎已经在现实中证明了其有效性。

因此,我们建议所有交易、所有协议、所有钱包供应商和交易员:

  1. 持续监控交易池的情况,注意(1)发现交易池开始变得拥堵的时机;(2)交易丢弃率和拒绝率的变化。
  2. 理解交易 nonce 排序的细微差别。即使合理构造的交易也可能被卡在网络的某个角落。
  3. 主动观察被卡壳的交易,并且要知道加速先前的哪一笔交易能使发送交易的地址脱困。
  4. 基于交易池中对矿工有吸引力的部分来计算 Gas Price。还需要知道你所依赖的 Gas Price 报告服务所用的算法。
  5. 在高度拥堵期间,不要假设交易待打包的情形是可以预测的。先防范,你要监控每一笔交易,了解每一个细节,包括 Gas 是否充分,交易是不是被丢弃、被卡壳,会不会被人抢跑(front-running),等等。

这些问题,如果你自己动手解决,是非常有挑战性的、很耗时而且很昂贵的。Blocknative 的全球数据平台一直在往捕捉和规范化去中心化交易池的方向优化 —— 也就是一直在实现上述的五个建议。我们的基础设施和 API 提供了实时的交易池监控服务,保证你的交易能可靠、灵活、可预测地流动

……

非常感谢评议本文初稿的各位,包括:Sarah Baker-MillsDmitriy BerenzonSpencer BogartNic CarterHsin-Ju ChuangTomasz Drwięga, Andy GrayHudson JamesonJon KolCalvin LiuJustin MartGavin McDermottTaylor MonahanAndra NicolauCharlie NoyesSimona PopAlex PrudenAustin RobertsCuy SheffieldLarry SukernikChris Whinfrey,等等。我们非常感谢你们的反馈、洞见和指导。

(完)


原文链接: https://blog.blocknative.com/blog/mempool-forensics
作者: Blocknative
翻译: 阿剑


你可能还会喜欢:

MakerDAO 的黑色星期四:被 0 价格拍走的质押品

以太坊交易的生命周期

MakerDAO 多担保 Dai (MCD) 系统白皮书

 
0 人喜欢