2021 年 12 月 1 日
UMA 是一个允许用户在以太坊区块链上输入信任最小化的金融合约的平台。我们之前审核过 去中心化的预言机, 特定的金融合同模板, 一些临时拉取请求, 永久多方模板 和 长期参与期间的各种增量拉取请求。在本次审计中,我们审查了一种将代币从第 2 层链快速发送到以太坊主网的新机制,以及对 Optimistic Oracle 的相关更改。审核由 2 名审核员在 3 周内完成。
范围
审计的提交是 f24ad501c8e813cf685f72217e7f13c8f3c366df
范围包括以下合同:
- Contracts/insured-bridge/*(不包括测试合同)
- 合同-ovm/insured-bridge/实施/*
- 合同/通用/实施/AncillaryData.sol
- 合同/oracle/实施/SkinnyOptimisticOracle.sol
我们还审查了对 Solidity 文件的更改 请求请求 3445.
假定所有外部代码和合同依赖项均按文档记录工作。
系统总览
支持的第 2 层 (L2) 链 Optimism 和 Arbitrum 提供了一种将资金转移到以太坊主网 (L1) 的机制。然而,出于安全原因,这些转移在最终完成之前存在明显的延迟。为了解决这个问题,L2 代币持有者可以将资金存入 UMA 合约(即存款箱),因为知道代币最终将(批量)转移到 L1 UMA 合约(即桥池)。每个要转移的代币都有一个单独的桥池。
L2 存款后,任何人都可以将详细信息转发给 L1 桥池,该池会等待一小段时间,以防有人对转发的信息提出异议。所有争议均由 Skinny Optimistic Oracle 处理(如下所述)。在接受中继之前,流动性提供者必须预先为 Bridge Pool 合约提供资金以换取 LP 代币。假定无争议的中继有效,桥池使用自己的储备金完成转账,其中一部分转移给中继者,另一部分保留作为流动性费用。当 L2 存款完成时,资金最终将得到补充,流动性费用将分配给 LP 代币持有者。
桥池还允许任何人在争议期到期之前单独资助转账(没有桥池储备),以换取转账金额的一小部分。由于中继仍然存在争议,如果中继被认为不正确,这些资金将会丢失。预计在大多数情况下,该机制将允许用户体验即时的 L2 到 L1 代币转移。
Skinny Optimistic Oracle 在概念上与现有的 Optimistic Oracle 非常相似。它为用户提供了一种激励机制,让他们简单地断言预言机请求的结果,如果没有争议,则认为该结果是准确的。争议归咎于我们之前的审计报告中描述的较慢的 DVM 机制。主要区别在于,新版本要求用户在执行函数调用时提供所有相关信息,因此不需要保存或从存储中检索值。它还消除了请求者更改活动请求中的配置参数的能力。
我们之前回顾过多空对合约,它提供了创建各种金融工具的通用机制。当这些合约到期并且结算价格已知时,就可以结算。 Pull Request 3445 引入的更改引入了如果在到期时间之前已知结算价格则可以提前解决合约的可能性。
特权角色
L2 存款箱有多个配置参数,包括支持的代币以及通过网桥向 L1 发送批量代币的最大速率。还必须对它们进行配置,以确保 L1 代币合约、L2 代币合约和相应的桥池池之间的一致性。这些参数由 L1 上的管理员合约设置,该合约还参数化了桥池的争议解决过程。该合约预计将受到UMA治理机制的控制,因此用户必须相信这个过程能够合理、公平地管理系统。
生态系统依赖性
所有经过审查的组件都使用基于时间的逻辑,这意味着它们依赖于以太坊的可用性。特别是,如果争议交易明显延迟,则无效的中继或价格建议可能会被错误地确认。
此外,代币桥隐含地假设发送到 L2 存款箱的所有资金最终将转移到相应的 L1 桥池。这依赖于乐观桥和仲裁桥及其争议解决机制的正确和持续运作。
最后,发送到 L2 存款箱的代币将被分配给 L1 中的桥池,而不是指定的接收者。要从池中取回资金,L1 代币持有者必须首先将其与其他代币相匹配。因此,该机制依赖于L1代币足够深度的市场来确保始终有流动性。
客户报告的问题
在审计过程中,UMA团队独立发现了一些值得强调的问题和行为:
如果 Optimistic Oracle 或 Bridge Admin 参数在中继挑战期间发生变化,则对中继提出争议会删除该中继,而提议者或争议者都没有额外的追索权。例如,假设发送中继是为了抵押代币
TOKEN_A
,但是在继电器的中间TOKEN_A
已从抵押白名单中删除。由于您无法向 OO 或 DVM 提交任何未列入白名单的抵押品的价格请求,因此争议现在将恢复。由于我们不想阻止有效的争议请求,BridgePool
将删除待处理的中继TOKEN_A
如有争议。该设计决策的后果是:
1. 最终费用的增加将导致: 该代币上的任何未完成的中继都可以通过争议“取消”,无论是对还是错。取消对任何一方都没有好处,因此它假设存在诚实的争议者,他们愿意消除在最终费用变更执行期间碰巧存在的(罕见的)不良请求。这也意味着恶意破坏者有可能花费天然气来取消中继并迫使它们重新中继。
2. 标识符或令牌去白名单,除非出现严重错误,否则不应发生这种情况,否则会导致:
3. 无可争议请求的延长期限,任何请求都可以取消,并且不存在争议的经济激励。这似乎比完全阻止争议的替代方案更好,但不可否认,这非常糟糕,因为任何恶意破坏者都可以通过支付汽油费来无限期地阻止中继或发送不良中继而不受惩罚(除了汽油费)。注意:这是让 OO 在一段时间内“冻结”最终费用或抵押品白名单等参数的替代方案,但这需要对 OO 进行额外的调用,这对于快乐路径来说成本高昂。
中继可以通过以下方式加速
speedUpRelay()
当他们过了活性之后。虽然我们认为这没有任何风险,但它确实开启了闪电贷款+加速+活跃后结算的可能性,为闪电借款人提供“免费”的即时中继费用。我们在此提议中防止这种情况发生 PR.
On
settle
,如果BridgePool
是一个WETH
池和接收者是一个合同,不是payable
(不能接受 ETH),那么settle
将失败。我们计划解决这个问题并回退发送WETH
,但这方面尚未完成任何出色的工作。
In
relayDeposit
,我们检查BridgePool
的余额大于中继金额加上提议者保证金。这是一个过时的检查,并且过于保守,因为在检查后提议者债券被从用户处撤回。我们在这个提议中解决了这个问题 PR.
我刚刚发现了一个错误
chainId
inBridgePool
,作为一部分包含在内Deposit
struct 并作为所有与继电器相关的函数的函数输入(即relayDeposit
,speedUpRelay
,settle
) 是类型uint8
。这是一个太小的类型,无法处理 Arbitrum(例如 ID 为 421611)。我们实际上捕获了这个错误并在 L2 端修复了它:BridgeDeposit
已设定其chainId
输入uint256
。此 PR 将使chainId
onBridgePool
匹配类型BridgeDepositBox
: UMA协议/协议#3463
此前,争议功能并未从存款金额中扣除
pendingReserves
(这是一个变量,用于跟踪由于尚未结算的中继而锁定了多少储备池)。结果是每次争议都会无限期锁定池中的中继金额。它不能被 LP 撤回或被未来的中继使用。修复在这里: UMA协议/协议#3473.
我们发现了一个错误
BridgeDepositBox
哪里hasEnoughTimeElapsedToBridge
不检查是否uint256
值等于0
默认: PR 3484 中已修复
在桥池合约的 addLiquidity 方法中,在转入的代币和铸造的 LP 代币之间调用汇率方法(即状态修改)。该计算需要移至方法的顶部。这会导致非常奇怪的状态值。参见公关 此处 修理。
视图方法
liquidityUtilizationPostRelay
(仅在链外使用),报告错误的利用率数字。分母为 这条线 不应该只是liquidReserves
,它应该代表未使用和已使用的储备。固定的 此处.
更新
除了问题修复之外,我们还审查了以下增量更改:
- PR72 从中删除冗余令牌参数
BridgePool
事件。 - PR72 将 DVM 最终费用添加到本地缓存变量列表中。
- PR72 考虑了可能出现的负流动性利用率边缘情况(除了解决 N04 之外)
- PR72 删除冗余文件并根据 OVM 2.0 更改更新 OVM 常量。
- PR72 更新
BridgeDepositBox
接口的一致性并使用 OpenZeppelinSafeERC20
图书馆。
在审查修复程序时,我们发现了另一个问题。当确定其值时 BridgePool
LP代币,有一个 中间计算可能会意外出现负溢出,这将暂时禁用添加和删除流动性。应重新排序计算,以在减去未分配费用之前添加已使用的准备金。
严重程度
[C01] 被困提议者奖励
LongShortPair
合同 检索提议者奖励 来自触发到期的地址,用于激励乐观预言机中的价格建议。但是,那 LongShortPairCreator
合同也 检索并转发资金 来自部署者地址。这些额外的资金不会传递给乐观预言机,而是保留在 LongShortPair
合同。
考虑删除重复传输。
更新: 截至提交时已修复 9bab1ff353a417952ba8c96a098773f340d9da17
in PR72.
高严重性
[H01]并发中继耗尽储备
relayDeposit
的功能 BridgePool
合同 确保合约有足够的资金 执行转移。然而,它并没有考虑到 待定准备金,它跟踪指定用于活动中继的资金。因此,多个同时中继可能依赖相同的资金,并且它们可能无法立即全部结算。特别是,在稳定的传输流中,即时中继器返回可能会无限期延迟。
考虑防止可能导致待决储备超过流动储备的中继。
更新: 截至提交时已修复 6290f3facbca8d878605a1d390ed59d4b6b6db02
in PR72.
[H02]桥接参数范围不匹配
deposit
功能 的 BridgeDepositBox
合约部署在第 2 层链上,用于桥接 L2 和 L1 之间的资金。特别是,中继者被激励 中继 相关 L1 上的交易详细信息 BridgePool
。然而,存款箱使用 包容性界限 限制中继费用,而桥池使用 独占边界。这意味着某些存款(包含 25% 的中继费用)无法中继,并且资金将无法在两层上访问。
考虑同步两层上的验证,以确保所有有效存款都可以转发。
更新: 已在提交中修复 2345966b3a2ace0159379b3a13256cc1a4c5d52f
of PR72。这最初被归类为严重严重性,但当 UMA 团队指出资金不会被严格限制并且如果 DVM 选民同意接受受影响存款的修改后的中继描述时可以释放资金时,该问题被降级。
中等严重程度
[M01] 回调到错误的地址
SkinnyOptimisticOracle
调用价格请求者的回调函数(如果存在),以便请求者可以响应重大的状态变化。然而,回调被错误地调用在价格提议者身上,而不是价格请求者身上。 此 proposePriceFor
功能。这意味着价格请求者无法响应价格建议。
幸运的是,当前的代码库中并未使用此功能。尽管如此,请考虑调用 priceProposed
对请求者的回调。
更新: 已在提交时修复 7bd3faeb6f3706132f77b9ba2dce192d1a151e74
in PR72.
[M02]附加功能错误
appendKeyValueBytes32
功能 应该将其输入组合成格式化的 bytes
大批。 但是,那 currentAncillaryData
is 错误地丢弃.
由于辅助数据 影响oracle解析过程,错误的值可能会破坏预言机的结果。幸运的是,只有 一通电话 appendKeyValueBytes32
在代码库中,它使用一个空的 currentAncillaryData
缓冲区,因此该错误不会影响这种情况。
考虑更新 appendKeyValueBytes32
功能,使 currentAncillaryData
包含在返回的字节数组中。
更新: 已在提交中修复 5609433c154f47e8ee9c52f9b6d7c787fbe3e455
of PR72.
[M03]辅助数据验证不完整
LongShortPair
构造函数 确认 customAncillaryData
是足够小。然而,它并没有考虑到 提前过期字段。这意味着乐观预言家 可能会意外拒绝 提前到期价格请求,这将禁用此功能。
考虑更新验证以考虑附加字段。
更新: 截至提交时已修复 4a56e66492f40e20254cebb145c2d91304f7cb43
in PR72.
[M04]零时间戳处理不当
在 LongShortPair
合约,零提前到期时间戳是 用作旗帜 表明没有人触发提前过期机制。然而,有可能 触发该机制 时间戳为零。在这种情况下,将调用乐观预言机,但 防止后续价格请求 不会有效。幸运的是,有一次 选择结算价,它不会被覆盖,因此这不会导致结算不一致。尽管如此,随后的价格请求可能会 更改记录的提前过期时间戳,即使使用零时间戳来确定结算价格。还可以 发出误导性事件.
考虑使用零时间戳来防止提前过期。
更新: 截至提交时已修复 11d287c07c93c04f534b2ef3c869966d9f18ac60
in PR72.
[M05]可能的零键
requestPrice
的功能 SkinnyOptimisticOracle
合同 使用最终费用作为保证金 如果未指定债券。但是,那 requestAndProposePriceFor
功能 可以使用零键,这与其相矛盾 @notice
和 @param
评论。零债券削弱了针对无效提案或争议的激励。
幸运的是 只调用这个函数 在代码库中设置提议者债券。不过,如果未指定保证金,请考虑使用最终费用。
更新: 截至提交时已修复 daaabfc342ba1395a577159b6eb26adb20fcd232
in PR72.
[M06]不必要的管理员权限
BridgePool
合同 继承自 ExpandedERC20
以便它可以向流动性提供者发行 LP 代币。这继承了 OpenZeppelin 的功能 ERC20
合同还有 提供管理员权限 给合约部署者,这允许他们铸造和销毁 LP 代币。然而,这种权力不是必需的,如果行使,可能会不公平地惩罚流动性提供者。
考虑修改 BridgePool
直接继承自 ERC20
而不是 ExpandedERC20
.
更新: 已在提交中修复 370e8b21b660543eadbd764fed984a5bdeddce24
in PR72.
严重程度低
[L01]到期无法结算
settle
的功能 LongShortPair
合同 考虑结算条件 当当前时间严格位于过期时间戳之前或之后时。但是,当当前时间与过期时间戳匹配时,它会错误地恢复。
考虑使用包含边界来匹配 postExpiration
变化.
更新: 已在提交中修复 f03cdaa50b16d29e8f42f000bf7cd50a042cf616
in PR72.
[L02] require 语句中缺少错误消息
里面有一个require语句 BridgePool
合同 没有错误消息。
考虑在所有 require 语句中包含具体且信息丰富的错误消息。
更新: 截至提交时已修复 67e60faa3a44c842c37211d2e903a983ff192e57
in PR72.
[L03] 缺少文档字符串
整个代码库中有一些实例 以太坊自然规范 缺失或不完整。示例包括:
考虑彻底记录作为合约公共 API 一部分的所有函数(及其参数)。
更新: 突出显示的评论已在提交中修复 e943e85a7dae60acd17a6d6aa027fbb1017c95ee
of PR72。我们没有验证代码库其余部分中的 NatSpec 完整性。
注释和其他信息
[N01] 未检查调用返回值
在 deposit
功能 L2的 BridgeDepositBox
合约中有一个低级别的调用 l2Token
当 l1Token
is l1Weth
。这个低级调用是 deposit()
函数,属于 Weth 界面。如果这 l2Token
其行为与 WETH 完全相同,它永远不会失败。但在这种情况下 l2Token
行为不同并且确实失败,因此不会进行恢复,因为从不检查此低级调用的成功标志。
考虑检查所有低级调用的返回值并做出适当的反应。
[N02] 事件中缺少索引参数
此代码库中定义的许多事件都具有应建立索引的参数:
考虑 索引事件参数 避免妨碍链下服务搜索和过滤特定事件的任务。
更新: 部分修复在提交中 d156b40b2ddb109806336c4d169dbdea91ed1c3e
of PR72。 该 chainId
的参数 WhitelistToken
没有更新。
[N03]隐式转换不一致
LongShortPair
一般合同 将时间戳视为 uint64
价值观,它们被隐式转换为 uint256
值时 传递给乐观预言机。 但是,那 requestTimestamp
的参数 此 _requestOraclePrice
功能 过早地转换为 uint256
。这没有任何功能后果。
尽管如此,为了保持一致性,请考虑使用 uint64
对于这个参数并允许它隐式转换为 uint256
当传递给乐观预言机时。
更新: 已在提交中修复 1c3c5c000ef450f5e2da056e41caff468c3fcdcb
of PR72。时间戳现在已显式转换。
[N04]类型错误
sendMessage
的功能 iOptimism_CrossDomainMessenger
接口 使用 uint256
气体限制 而乐观主义的 OVM_CrossDomainEnabled
使用 uint32
气体限制.
为了一致性和可预测性,请考虑更新 iOptimisim_CrossDomainMessenger
sendMessage
函数来使用 uint32
气体限制。
更新: 截至提交时已修复 381951aad988bbba6b2ef1b136ed5c48df50aa88
in PR72.
[N05]缺乏验证
所有功能都在 BridgeAdmin
那个电话 _relayMessage
假设交易价值与 l1CallValue
参数,但这不是强制的。
考虑确保正确 msg.value
置。
更新: 截至提交时已修复 f19b8d04c2343051ff2a8145abd41c39bd025063
in PR72.
[N06] 可读性
_getDepositHash
功能 的 BridgePool
合同展开 depositData
结构间隙 l1Token
作为组成中的参数 keccak256
与 abi
编码。这使得操作变得不必要地冗长,并且在其他层上重新实现时可能会导致错误。
考虑将参数简化为有序对 depositData
和 l1Token
.
更新: 截至提交时已修复 31754be4a818109fa12131f854c3f70d6c72dba7
in PR72.
[N07]可重入函数
requestAndProposePriceFor
功能 的 SkinnyOptimisticOracle
合约调用不受信任的 msg.sender
但不受保护 nonReentrant
修饰符。虽然在这种情况下,这似乎不是一个安全问题,但这可能会导致意外的行为。
考虑添加 nonReentrant
修改所有调用可能不受信任的合约的函数。
更新: 已在提交中修复 b744d24e7579b7afa2c778f4dd680f26117b3990
of PR72.
[N08] seqNum
未登录
relayMessage
功能 的 Arbitrum_Messenger
执行敏感操作后,合约不会发出相关事件。这 relayMessage
作为子例程的函数调用 sentTxToL2NoAliasing
它本身返回 uint256
折扣值 seqNum
,但是这个返回值没有记录在 relayMessage
功能。
考虑在发生敏感更改后发出事件,以便于跟踪合约活动并通知链下客户。
更新: 截至提交时已修复 30343f33532a6c255dc4cc18c3b497d9b2767a7c
in PR72.
[N09]印刷错误
代码库包含以下拼写错误:
考虑更正这些拼写错误以提高代码的可读性。
更新: 截至提交时已修复 2dccbe1c2c82fe2a21c179ac06c2d4f0d911a2ca
in PR72.
[N10] 未记录的 ERC20 批准要求
requestEarlyExpiration
和 expire
功能 的 LongShortPair
每个合同都假设调用者已授予合同津贴 拉取提议者奖励.
为了可预测性,请考虑在函数注释中记录此要求。
更新: 已在提交中修复 da3754f50284480df57b90b80002da06a1ce0d02
in PR72.
[N11]未使用的修饰符
在 BridgePool
合同 onlyFromOptimisticOracle
变化 已定义但从未在代码库中使用,因此应删除。
更新: 已在提交中修复 7abece6377637e8c4cd3bd07ab9adcfa051d4e94
in PR72.
结论
发现 2 个严重问题和 1 个高严重性问题。提出了一些更改,以遵循最佳实践并减少潜在的攻击面。
- &
- 7
- 账号管理
- 操作
- 要积极。
- Ad
- 额外
- 地址
- 管理员
- 优点
- 所有类型
- 允许
- API
- 参数
- 审计
- 可用性
- 作为
- 最佳
- 最佳实践
- blockchain
- 盒子
- 桥
- 问题
- 虫子
- 呼叫
- 例
- 抓
- 原因
- 挑战
- 更改
- 检查
- 码
- 注释
- 配置
- 包含
- 合同
- 合同的
- 可以
- 电流
- data
- 分散
- 延迟
- 设计
- 确定
- DID
- 争议
- 不会
- 早
- 经济
- 边缘
- 有效
- ERC20
- ETH
- 复仇
- 燕窝块
- 活动
- 事件
- 例子
- 交换
- 预期
- 体验
- 专栏
- 费用
- 金融
- 姓氏:
- 固定
- Flash
- 遵循
- 发现
- 功能
- 基金
- 资金
- 未来
- 天然气
- 煤气费
- 给予
- 治理
- 快乐
- 有
- 此处
- 高
- 突出
- 持有人
- 创新中心
- HTTPS
- 激励
- 包括
- 包含
- 增加
- 信息
- 兴趣
- 接口
- 问题
- IT
- 已知
- 铅
- 自学资料库
- 液体肥产线
- 流动性
- 流动性提供者
- 清单
- 贷款
- 当地
- 锁定
- LP
- 有限合伙人
- 市场
- 匹配
- 最先进的
- 打开
- 神谕
- 其他名称
- 平台
- 池
- 矿池
- 功率
- 预防
- 车资
- 过程
- 提案
- 提供
- 提供
- 国家
- 原因
- 减少
- 业务报告
- REST的
- 成果
- 回报
- 检讨
- 风险
- 保安
- 特色服务
- 集
- 沉降
- 短
- 显著
- 类似
- 小
- So
- 坚固
- 有人
- 东西
- 速度
- 花
- 州/领地
- 个人陈述
- 存储
- 成功
- 支持
- 磁化面
- 系统
- test
- 通过
- 始终
- 次
- 象征
- 令牌
- 最佳
- 跟踪
- 交易
- 交易
- 信任
- 更新
- 最新动态
- UPS
- 用户
- 折扣值
- 查看
- 白名单
- WHO
- 中
- 也完全不需要
- 工作
- 价值
- 零