Coin Metrics:数据分析 Bybit 黑客攻击事件及
409 2025-02-26
作者:NIC Lin,Medium
Pectra 硬分叉预计于 2025 年 3 月启动主网部署。Pectra 升级包含 11 个技术协议(EIP),它们分别是:
EIP-2537:BLS12-381 曲线操作预编译
EIP-2935:在 State 中保存历史区块哈希值
EIP-6110:提供链上validator deposits
EIP-7002:执行层触发退出
EIP-7251:增加 the MAX_EFFECTIVE_BALANCE
EIP-7549:将committee索引移至验证之外
EIP-7623: 增加 calldata 成本
EIP-7685: 通用执行层请求
EIP-7691: 增加 Blob 吞吐量
EIP-7702: 设置 EOA 帐户代码
EIP-7840:在 EL配置文件中添加 Blob 计划
EIP-6110: BLS12-381 曲线操作预编译
简化用户参与质押的处理流程,让等待时间大幅缩短。
用户参与质押的方式是在执行层上存入 32 个 ETH 并由事件日志(Event Log)记录,接着共识层执行解析事件日志来判断是否有人参与质押,然后参与质押的用户就成为验证者。
不过,共识层的验证者首先需要针对哪一个时间点存入达成共识,否则,会发现有些验证者看到 5 个新的存入,而有些验证者只看到 3 个,因此共识层验证者们会对要参考哪一个执行层区块(eth1data)进行投票,确保大家看到的是一样的执行层区块。
不过,一开始设计时为了避免执行层出现重大错误导致链分叉,所以参考的执行层区块(eth1data)会是一个约 10 多个小时以前的执行层区块,确保当重大错误发生时,共识层的开发者们有足够的时间反应处理,不过这也导致参与质押最快也要等上 10 多个小时才会生效。
△ CL 区块里的 10900000 eth1data,它里面记载的 Block Hash 是执行层区块 21683339,出现在它的 10 个小时以前。
执行 EIP-6110 技术协议之后 ,用户在合约上的质押资料会直接变成执行层的一部分,而因为共识层区块本身就会包含执行层区块(但不是 eth1data),所以,共识层验证者们就不用再考虑「要确认参考的执行层内存块是⼀样的」的问题,只要共识层内存块获得超过三分之二的验证者们投票确认,则大家对看到的是同一个执行层区块达成共识。因此,用户在参与质押后,最快约 13 分钟等执行层内存块完成处理后就可以生效,而共识层客户端也可以移除原本用来处理质押数据相关的复杂逻辑。
EIP-7002: 在State 中保存历史区块哈希值
能够用于改善验证者退出质押或提领押⾦和收益的流程,降低验证者的风险。
参与质押需要有两把钥匙,分别是 Validator Key 和 Withdrawal Credential。
Validator Key 用于验证者⼯作内容,Withdrawal Credential 用于验证者退出质押时,押⾦和收益会提取到的地址,另外,目前退出质押必须要用 Validator Key 操作。
如果遗失 Validator Key,则无法执⾏验证者⼯作,且无法退出质押;如果遗失 Withdrawal Credential,则会丢失所有押⾦和收益。此外,部分用户会使用诸如 Lido 的第三⽅质押服务,当使用这些平台时,用户需要自行保管 Withdrawal Credential,而此时 Validator Key 则是由服务提供商保管并代为执⾏验证者的⼯作。
执行 EIP-7002 技术协议,用户就可以自⼰用 Withdrawal Credential 呼叫「Withdraw 合约」(即部署在0x0c15F14308530b7CDB8460094BbB9cC28b9AaaAA)来退出质押(Exit)或提领押⾦和收益(Partial Withdrawal),可以降低使用第三方质押服务可能存在的相关风险。如果用户自行参与质押但遗失了 Validator Key,也能借此退出质押。
发起请求的参数包含 validator_pubkey 及 amount : validator_pubkey 是验证者的 Validator(Public)Key,amount 是要提领的数量。
发起请求的 Withdrawal Credential 必须是 validator_pubkey 验证者的 Withdrawal Credential。
呼叫 Withdraw 合约去发起请求时要附上 Gas 费(ETH),Gas 费会按照当前的提领请求数量计算,如果请求数量很多,Gas 费就会上升。
如果用户的 Withdrawal Credential 是⼀个合约的话,那可以先去 Withdraw 合约取得当前⼿续费⾦額,然后再发起请求并附上⼿续费;但如果 Withdrawal Credential 是⼀个 EOA 账户的话,就没办法取得精准的⼿续费,只能事先链下模拟并付超额⼿续费(不会退还),确保请求发起会执⾏成功。
注:如果你的 Withdrawal Credential 还是 BLS 公钥格式,记得要先进⾏切换,将它换成 EL 地址的格式。
EIP-7251: 增加 the MAX_EFFECTIVE_BALANCE
能够⼤幅提高质押⾦額上限来减少验证者数量,且未达上限的验证者可以自动享受质押收益。
用户质押成为验证者要提供 MAX_EFFECTIVE_BALANCE 数量的 ETH,不能更少也不能更多(目前 MAX_EFFECTIVE_BALANCE 是 32 ETH)。如果用户持有 1024 ETH 要质押,可以分 32 次参与质押、启用 32 个验证者,运⾏ 32 个验证者节点。⽽⼤家积极参与质押也导致目前已有约 100 万个验证者并持续增加,这除了让共识层的状态数据变得更⼤更多,对共识层 p2p 网络层的负荷更为显著,因为每⼀个 Slot(每 12 秒)都有数万个验证者的签名要在 p2p 网络层里不断传递并聚合。
执行 EIP-7251 技术协议后,质押下限(MIN_ACTIVATION_BALANCE)仍然是 32 ETH,但上限(MAX_EFFECTIVE_BALANCE)将大幅调高为 2048 ETH,你可以质押 32~2048 之间任何数量的 ETH,可以获得质押收益,不再需要定期取出收益,累积 32 ETH 后再继续新质押。
目前,已存在的验证者也不需要特别先退出质押再合并⼀起重新加⼊质押,⽽是可以直接利用执行层上新增的「合并押金用的合约」(部署在0x00431F263cE400f4455c2dCf564e53007Ca4bbBb),由验证者的 Withdrawal Crendential 去呼叫合约发起合并押金的请求。
合并押金请求的参数包含 source_pubkey 及 target_pubkey:这两个 key 都是验证者的 Validator Key,source 验证者会合并到 target 验证者。
发起请求的 Withdrawal Credential 必须是 source 验证者的 Withdrawal Credential。
呼叫合并押金合约去发起请求时要附上手续费(ETH),手续费用会按照当前的请求数量计算,如果请求数量很多,手续费就会上升。
如果用户的 Withdrawal Credential 是⼀个合约的话,那可以先呼叫合并押金合约取得当前⼿续费⾦額,然后再发起请求并附上⼿续费;但如果 Withdrawal Credential 是⼀个 EOA 账户的话,就没办法取得精准的⼿续费,只能事先链下模拟并付超额⼿续费(不会退还),确保请求发起会执⾏成功。
注:如果你的 Withdrawal Credential 是 BLS 公钥格式,需要先进⾏切换,将它换成 EL 地址的格式。
EIP-7685: 通用执行层请求
建立⼀个正式的 EL -> CL 信息管道,⽅便用户及质押服务能直接送出请求给共识层。
用户能直接从执行层发送请求给共识层,质押服务(例如 Lido)就可以以更去中心化的⽅式运⾏。例如前面提到的 EIP-7002 的退出质押的请求,以及 EIP-7251 的合并押金的请求。如果没有这个技术协议,那 Lido 的用户就必须要相信 Lido 节点服务提供商会如实在共识层去执⾏退出质押或是合并押金;有了这个技术协议,Lido 的用户就可以在执行层上直接透过治理合约去送出请求。
这些请求会有 Request Type 来区分不同类型的请求,以及透过不同合约发起请求,最后这些请求都会被写⼊到执行层内存块里,因此共识层可以直接透过执行层内存块获得这些信息,不必再写个别的解析逻辑。
EIP-6110、EIP-7002 和 EIP-7251 都是以 EIP-7685 定义的标准来制定请求:
EIP-6110 加⼊质押请求:Request Type=0,透过 Deposit 合约
(0x00000000219ab540356cbb839cbe05303d7705fa)发起请求。
EIP-7002 退出质押请求:Request Type=1,透过Withdraw合约
(0x0c15F14308530b7CDB8460094BbB9cC28b9AaaAA)发起请求。
EIP-7251 合并押金请求:Request Type=2,透过Consolidation合约
(0x00431F263cE400f4455c2dCf564e53007Ca4bbBb)发起请求。
EIP-7702: 设置 EOA 帐户代码
让 EOA 账户能任意变身成合约账户,⼤幅提升使用体验。
EOA 账户在使用上有一些不足包括:
需要记录和保管私钥或助记词,对于新用户注册使用的门槛较高。
EOA 账户⼀笔交易只能执⾏⼀个操作,比如,要去 Uniswap 将 USDT 换为 ETH,要先发起⼀笔交易批准 USDT,然后才能送另⼀笔交易执⾏兑换。
无法细化的权限控管,像是将帐⼾的某些操作交给第三⽅代为操作,用户必须要亲自处理每⼀件杂事且每⼀个操作都要签名发交易⼀次。
没有 Recovery 机制,只能自⼰保管好私钥或助记词,如果遗失了就再也拿不回帐⼾的资产。
如果是⼀个智能合约帐⼾(例如 Safe),那以上的问题都可以被解决:
用户可以用⼿机(或电脑)的安全芯片里的私钥来签名授权,不用记任何私钥或助记词,或是用 Email 来签名授权也可以,或其他各式各样的授权⽅式。
可以将多个操作 Batch 起来在同⼀笔交易内⼀起执⾏,原先复杂的 DApp 操作都可以只用⼀次签名授权、⼀次交易就可完成。
可以有非常细化的权限控管,用户可以授权第三⽅来控制自⼰的帐⼾,但同时指定「可以和什么合约互动」「不可以执⾏什么操作」、「牵涉到资产转移最多只能动用多少资产」或「每个礼拜最多不能超过多少次操作」等等限制。
可以新增 Recovery 机制,在自⼰遗失助记词或⼿机或 Email 时还能透过 Recovery 机制将帐⼾的资产转移⾄新的帐⼾。
EIP-7702 提案便是赋予 EOA 账户变身成为合约账户的能⼒。用户用 EOA 私钥对变身的讯息签名,签名内容包含「Chain ID」「变成的合约地址」及「EOA的 Nonce值」:
Chain ID:用来防止 A 链的签名被拿到 B 链重放。不过,如果 Chain ID 填 0,则表示愿意在每条链都变身。
想变成的合约地址:如果你填⼀个 Safe 合约地址,那你的 EOA 账户就会变成为⼀个 Safe 合约;如果填空地址(address(0)),那就表示要取消变化,变回单纯的 EOA 账户。
EOA 的 Nonce 值:用来防⽌签名被重放。如果 Nonce 值增加了,那原本的签名就会失效。
不过有几点需要注意:
1. EOA 私钥⼀样可以继续使用
即便用户的 EOA 账户变成⼀个合约,他还是可以继续以原本 EOA 账户的⽅式使用。他的帐⼾,例如假设你的 EOA 账户变成⼀个 Safe 合约,则你可以使用 Safe 介面、走 Safe 交易流程,也可以继续用原本的 EOA 钱包签名送交易。不过这也表示帐⼾的安全性还是局限在那把私钥。
2.仍然是 EOA 私钥的安全性
即便用户的 EOA 变成⼀个多签,只要他没有把 EOA 私钥丢掉,他的账户安全性永远都是 EOA 私钥的安全性:他仍然要好好保管他的私钥或助记词,他的账户不会因此变得和多签⼀样安全。
3. EOA 账户的 Storage 不会格式化
当⼀个 EOA 账户变身成合约并写⼊数据到其 Storage,除非明确执⾏删除数据的动作,否则这些写⼊到 Storage 的数据并不会因为 EOA 账户变身成其他合约或取消变身⽽格式化,所以开发者要注意 Storage 不要读取到以前变身合约留下的数据,可以参考 ERC-7201。
4. EIP-7702 的流程不包含初始化
⼀般合约帐⼾都会需要⼀个初始化的步骤,在帐⼾部署时同步写⼊帐⼾拥有者的信息(例如公钥或地址),避免部署步骤被抢跑(Frontrun)导致失去帐⼾拥有权。这通常是由部署合约帐⼾的 Factory 合约来执⾏「部署+初始化」,但因为 EIP-7702 是直接变化,⽽不是由⼀个 Factory 来部署合约到 EOA 身上,所以攻击者可以抄⾛用户的变身签名并抢先发送交易上链去替用户变身但将帐⼾初始化为攻击者可控制的,因此开发者需要留意 EIP-7702。可能的防范⽅法例如在初始化函式内检查 EOA 账户的签名,如此即便被抢跑,攻击者也没办法产⽣该 EOA 账户的签章来完成初始化。
5. 钱包要把关变化的请求
钱包需要替用户做好把关,在恶意的 DApp 网站请求用户签⼀个变身的交易时把请求拦下来并警告用户,否则如果用户签了恶意的变身交易,将导致资产瞬间被转⾛。以下是⼀些变身合约的实作示例:
Modified Safe Ithaca Account
Ithaca Account
EIP-2537: BLS12-381 曲线操作预编译
让基于 BLS 曲线的零知识证明应用的成本降低,变得更可⾏。
EIP-2537 新增数个预编译合约(Precompile)来提供便宜的 BLS 曲线运算,如此基于 BLS 曲线来开发零知识证明的应用将变得更可⾏。
EIP-2935: 在 State 中保存历史区块哈希值
让开发者或节点可以直接从系统合约的 Storage 中读取过去内存块的杂凑值(Block Hash)。
如果开发者需要证明某个以前内存块的内容,例如假设 Optimismtic Rollup 的欺诈挑战中要证明 1000 个以前的内存块存在某笔交易,挑战者没办法直接说。
「请相信我 1000 个内存块以前真的存在这笔交易」,他必须要提出证据,但没有⼀个直接的证据可以直接证明「1000 个以前的内存块里包含这些内容」,因此他必须以内存块「链」的⽅式,⼀个内存块⼀个内存块往前证明,直到达到 1000 个以前的内存块,然后再证明该内存块里存在该笔交易。
△ 每一个区块都会指向一个母区块,所以可以一路往前证明历史中的任何一个区块。
假设目前是编号为 10000 的内存块,⽽诈欺挑战要提供编号 9000 的内存块存在某⼀笔交易 X 的证明,则挑战者需要从内存块 10000 的哈希值开始,先证明内存块10000 所连接的母内存块 9999 的哈希值,然后再证明内存块 9998…直到内存块 9000,最后再提出内存块 9000 的内容里包含该笔交易 X。
EIP-2935之后,会有⼀个系统合约(部署在0x0F792be4B0c0cb4DAE440Ef133E90C0eCD48CCCC),它的 Storage 会储存最多 8192 个以前的内存块的哈希值。每当⼀个新的内存块产⽣时,这个系统合约就会自动更新,将前⼀个内存块的哈希值写进系统合约中(会复写掉 8192 个以前的内存块的哈希值)。
如此在 Optimismtic Rollup 欺诈挑战的例⼦中,挑战者就不必再往前⼀个内存块⼀个内存块慢慢证明,⽽是可以直接证明内存块 10000 当下的链的状态中,该系统合约的某⼀个 Storage(对应到内存块 9000)的值是内存块 9000 的哈希值。如果范围超过 8192,例如内存块 1000,那顶多就是多⼀步,先证明内存块 1808(= 10000 - 8192)的哈希值,然后再证明内存块 1808 当下的链的状态中,系统合约里的内存块 1000 的哈希值。
这也为未来的无状态客⼾端(Stateless Client)铺路:未来的轻节点就不需要再储存着历史中所有的内存块的头文件(Block Header),⽽是当有需要用到历史中某个内存块的哈希值或是内存块内容时,再请其他⼈用前面欺诈挑战例⼦中的证明⽅式提供证明即可。
EIP-7623: : 增加 calldata 成本
调高利用 calldata 来发布数据的成本,以挪出⾜够的安全空间来调高 Block Gas Limit 和 Blob 数量。
随着 Rollup 的数据发布需求越来越高,在 EIP-4844 中引⼊ Blob 来让 Rollup 以非常便宜的⽅式放数据之后,调⾼ Blob 数量便⼀直是社群所期待的⼀个升级,或像是最近社群在推动的调高 Block Gas Limit,都反应⽣态对提高资源的需求。
△ 越来越多的验证者表示支持调高 Block Gas Limit。
但不管是调⾼ Block Gas Limit 或是 Blob 数量,都会因为交易的数据量变得更大而对 Ethereum 的 p2p 网络造成更多压⼒,这会使得攻击者攻击的效率提⾼,除非将发布数据的成本也提⾼。
EIP-7623 协议发布之后,calldata 的成本将会从原本的「Zero Byte: 4 Gas、Non-Zero Byte: 16 Gas」调⾼ 2.5倍为「Zero Byte: 10 Gas、Non-Zero Byte: 40 Gas」。
原本如果攻击者将全部的 Block Gas Limit(30M)都拿来放垃圾数据的话,内存块的数据⼤小约会是 1.79 MB(30M / 16),相比于平均内存块⼤小只有约 100 KB;而如果 Block Gas Limit 调⾼到 40M 的话,攻击者可以产⽣约 2.38 MB大小的内存块。当 calldata 成本调高为 2.5 倍,攻击者的效率会因此下降,变为 30M 最⼤ 0.72MB、40M 最⼤ 0.95MB,如此就可以更放⼼地调高 Block Gas Limit 和 Blob 数量。不过这个技术协议也不想因此影响到「不是将 calldata 拿来发布数据」的⼀般用户,所以它会以两种⽅式计算交易的总 Gas 用量,再取较高的:
原本的交易 Gas 用量计算⽅式,搭配旧的calldata成本来计算:也就是将 calldata 以「Zero Byte: 4 Gas、Non-Zero Byte: 16 Gas」的⽅式计算,并加上交易执⾏所消耗的 Gas 及部署合约所消耗的 Gas。
单纯计算 calldata Gas 用量,但是是用新的成本来计算:也就是将 calldata 以「Zero Byte: 10 Gas、Non-Zero Byte: 40 Gas」的⽅式计算,但不计入执⾏所消耗的Gas或部署合约所消耗的 Gas 所以对⼀般「不是将 calldata 拿来发布数据」的用户来说(例如去 Uniswap 兑换),本来主要的Gas 消耗就是在执⾏的部分,即便 calldata 以新的成本计算也不会超过执⾏所消耗的Gas,因此⼀般用户将不会受影响。
真正受影响的会是规模还小的 Rollup,因为 Blob 是固定⼤小、固定费用,所以小 Rollup 使用 Blob 效率低,使用 calldata 还比较划算,但在 EIP-7623 之后,等于这些小 Rollup 的成本都会提升 2.5 倍,它们可能得因此转为使用 Blob 或想办法联合起来共同分担⼀个 Blob。
EIP-7691: 增加 Blob 吞吐量
提⾼ Blob 数量,增加更多资料发布的空间给Rollup。
EIP-7691 将 Blob 的数量由「目标:3 Blob,上限:6 Blob」调⾼为「目标:6 Blob、上限:9 Blob」,增加更多资料发布的空间给 Rollup。
注:另外 Blob ⼿续费市场还有⼀些设计需要微调,例如⼿续费调整的速度不够即时及⼿续费底限太低,但这不在这个技术协议要解决的问题里。
EIP-7549: 将 committee 索引移至验证之外
调整验证者投票的内容,让选票更⽅便被聚合起来,降低 p2p 网络的压⼒。
验证者们每个 Epoch 都会被随机分到⼀组⼀组的委员会(Committee)并对
内存块投票,每个委员会的验证者们的选票可以被聚合在⼀起,如此可以降低选票在 p2p 网络中传递的数量,但验证者的选票里会包含「该验证者属于第几个委员会」的信息,这导致不同委员会的选票不能被聚合在⼀起,即便他们都对相同的内存块投票。
EIP-7549 将「该验证者属于第几个委员会」的信息移出投票内容,使得不同委员会的验证者在投票内容⼀样的情况下可以被聚合在⼀起,进⼀步降低选票在 p2p 网络中传递的数量,降低 p2p 网络的压⼒。
EIP-7840: 在 EL 配置文件中添加 Blob 计划
在执行层为 Blob 参数建立⼀份设定档,省去执行层节点要去询问共识层节点 Blob 相关参数的麻烦。
Blob 相关参数目前都是储存在共识层节点,但执行层节点在某些情况还是需要这些参数(例如 RPC eth_feeHistory),所以都必须去向共识层节点询问。
EIP-7840 在执行层为 Blob 相关参数建立⼀份设定档,执行层节点都可以直接透过这份设定档读取 Blob 相关参数,不需要再向共识层节点询问。