Conflux研究院 | 交易转发中的带宽优化(下)

203 阅读6分钟

我们先来回顾一下上一期讲到的交易转发中带宽优化的问题。

一个节点将一笔交易转发给另一个节点时,为了节约带宽,可以发送这笔交易的 FID (Forwarding ID) 而不是直接发送完整的交易,由接受者根据 FID 判断是否需要向发送者请求完整的交易。我们的目标是将 FID 的长度降低到 4 个字节。

上一期的最后部分还提到如果每笔交易的 FID 固定不变,则攻击者可以用不高的成本阻塞特定交易的广播。基本方法是攻击者先构造一个覆盖所有 232 个可能的 FID 的交易库,当受害者发出一笔交易时,攻击者从交易库里选择具有相同 FID 的交易并抢先发送给其他节点,从而使其他节点都误以为已经收到了受害者交易,而选择不接收完整交易。

为了解决静态 FID 易被攻击阻塞的问题,我们将 FID 的取值由静态转变成动态的。因为 FID 只是在节点与节点之间转发交易时的临时取值,与共识逻辑无关也不需要被记录在区块链上,所以 FID 值没有必要是一个不变的数值。

考虑如下方案:

每个节点在计算 FID 时,并不仅仅由交易的 ID (交易的 32 字节哈希值)计算,而是选择一个随机数 r,通过 ID 和 r 共同计算出一个 4 字节的 FID,并将r 与 FID一同发送。这样,当随机数 r 改变时,交易的 FID 也会随之发生改变,收到 FID 和 r 的节点需要根据 r 重新计算已有交易的 FID 来完成对比,并确定需要请求哪些 FID 对应的完整交易。因为一次转发的多笔交易可以共享同一个随机数 r, 所以这个随机数 r 几乎不会在带宽上带来额外的负担。

由于 r 的随机性,攻击者并不能预计一笔交易在每次转发时选择的 r 和由此算出的 FID 分别是多少,自然也就无法预先构造冲突的交易了。

这个设计的另一个好处是,即便节点 B 给节点 A 发送交易时发生了 FID 值冲突,导致某笔交易没有成功发送给节点 A,另一个节点 C 发送这笔交易给 A 时,大概率将采用不同的随机数 r 计算 FID,相当于增加了一次把这笔交易发给 A 的机会。

只要计算 FID 的过程足够随机(如使用伪随机函数),则 B 发送成功和 C 发送成功可看作是两个独立的事件。根据我们之前的计算,一笔交易因为 FID 值冲突而发送失败的概率是 0.04%,而节点 B 和 C 两次独立发送都失败的概率是 0.04% × 0.04%,失败率大大降低。

然而,从随机数计算 FID 的设计也有一个缺点:每当节点收到一个新的随机数 r 时,就要为所有已经收到的交易(根据上一期的假设,全节点会将过去 5 分钟内收到的 180 万条交易和刚刚收到的 FID 对比)重新算一遍 FID,这会带来了极大的计算负担。

现在我们有两种不甚满意的方案:

静态的 FID 方案在一些攻击策略下有安全性问题,而完全随机的动态的 FID 方案又有计算负担过大的问题。如何同时解决两边的问题呢?

我们选择了一种静态和动态相结合的方案: FID 由 3 个静态字节和 1 个动态字节构成。其中静态字节部分直接取交易 ID (即交易 32 字节的完整哈希值)的前 3 字节,动态字节由交易 ID 和 r 共同计算得出。

这样,根据交易 ID 的前 3 字节,我们把所有的交易放在了 224 个“桶”里。每次一个节点收到其他节点发来的 FID 和 r 时,先根据 FID 的前 3 字节判断交易所在的桶,再将自己已经收到的,落在这个桶里交易根据 r 重新计算 FID 值并比对。

简单地计算可以发现,对于 180 万条随机生成的交易,平均每个桶里只有 0.1 笔交易,即使是含有交易最多的桶里也不会超过10 条。重新计算一个桶里交易的 FID 所需花费的成本远远低于重新计算所有交易的 FID。

在上面这个 3+1 动静结合的方案下,攻击者其实仍然可以重复类似的攻击策略。之前的攻击策略中,攻击者为每一个静态的 FID 准备一笔交易。在这一策略中,攻击者为每个桶预先准备一些交易。当受害者的交易出现后,攻击者在短时间内大量广播与受害者交易在同一个桶里的交易,就能够降低受害者交易被转发到其他节点的概率。即使同一笔交易每次转发时采用一个完全随机的动态字节,但是这个动态字节只有 256 种可能的取值,所以一个桶中的交易越多,冲突概率也会越高。

为了应对上述攻击,我们引入了一个额外的规则:如果一个节点收到一个 FID 时,发现这个 FID 对应的桶里已经有不少于 10 条交易,就不再使用 FID 来判断,而是直接请求完整的 32 字节交易哈希值来对比是否接受过这笔交易。这样,攻击者就不能再通过制造冲突的 FID 来阻塞交易广播了,因为冲突过多的时候反而无法起到阻塞的作用。

这条额外的规则触发时会让交易转发退化到没有使用 FID 优化的情况。但是因为正常情况下每个桶里期望只有 0.1 笔交易,几乎不可能发生超过 10 笔交易落在同一个桶里的情况,所以这条额外的规则在系统没有遭到攻击时并不会被触发。而当系统真的遭到攻击的情况下,保护系统的安全性和稳定性才是最重要的,即便为此而降低系统的吞吐量也是可以接受的妥协。