本文详细介绍了跨链桥的工作原理,以Wormhole为例,解释了其内部机制、操作流程和面临的挑战。分析了跨链信息的处理,包括消息的创建、签名验证、代币转账等重要步骤,展现了跨链技术的复杂性和解决方案。
我们读者可能会对加密黑客造成超过30亿美元的资金损失在2022年感到有些意外。然而,损失大部分发生在跨链桥上(例如,Ronin Network、Wormhole、Nomad、BNB Token Hub、Horizon、Qubit)。
跨链桥为何如此脆弱?难道它们不是经过细致设计或实施的吗?相反,由于其高价值,它们是由一些最好的工程师以尽可能高的标准开发的(例如,leoluk)。真正的原因是跨链桥实在太复杂,涉及太多技术细节。
在本系列文章中,我们将详细阐述跨链桥的内部结构、它们是如何实现的,以及从用户的角度来看它们的注意事项。我们将以一座现代化的桥梁Wormhole为例。
跨链桥只是两个区块链之间的(虚拟)链接,用于消息通信。消息本质上可以是任何位序列(例如,将代币或NFT从链A转移到链B)。
假设你要将比特币从以太坊转移到Solana,你需要做至少两个操作:
然而,由于这两个链并不直接通信(用技术术语说:没有办法在单一交易中触碰到两个链),你需要一个中介,拥有这两个链上的比特币,并且可以帮助你完成两个步骤:
跨链桥就是这样一个中介。这里有几个挑战:
Web3研究人员提出了多种不同的解决方案来应对这些挑战(有不同的权衡)。例如,多重签名验证者、多方计算(MPC)、rollup和乐观桥等。见互操作性三难题的精彩总结。
我们将重点解释一种基于守护者的代表性解决方案(即,一种多重签名验证者),它被Wormhole使用。
截至2023年1月,Wormhole能够桥接包括以太坊、Solana、Binance Smart Chain、Polygon、Aptos等20条不同链。
它通过运营一个称为守护者的19节点网络(查看所有19个守护者地址),以及在每条链上部署的多个智能合约(包括核心桥合约、代币桥合约和NFT桥合约)。核心桥合约提供了发出消息、验证守护者签名等功能。代币和NFT桥合约分别负责转移代币和NFT。
这19个守护者不断观察Wormhole核心桥合约发出的消息,并对这些消息进行签名(例如,“Alice刚刚在以太坊上给Wormhole发送了一个比特币,她想让Wormhole把一个比特币发送给Solana的Bob”这样的消息)。
这些守护者的权重相等。当其中一个超大多数(2/3)的他们对某条消息签名时,守护者网络产生一个验证者行动批准(VAA),这作为Wormhole在目标链上交付相同消息的证明(例如,将比特币发送到Solana的Bob)。
VAAs是所有Wormhole技术细节的核心。有一些疑问,热心的读者可能会想了解:
接下来,我们将逐一回答这些问题(必要时附上代码示例)。
每个VAA被编码为一个字节数组,包含两部分 - 头部和主体。
类型 | 字段 | 描述 |
---|---|---|
byte | version | VAA版本 |
u32 | guardian_set_index | 指示哪个守护者组在签名 |
u8 | len_signatures | 存储的签名数量 |
[][66]byte | signatures | ecdsa签名集合 |
类型 | 字段 | 描述 |
---|---|---|
u32 | timestamp | 发生源交易的区块时间戳 |
u32 | nonce | 一个分组编号 |
u16 | emitter_chain | 触发合约的Wormhole ChainId |
[32]byte | emitter_address | 触发合约地址,以Wormhole格式 |
u64 | sequence | 与触发地址和链关联的严格递增序列 |
u8 | consistency_level | 在发出此消息之前达到的最终性水平 |
[]byte | payload | VAA消息内容 |
特别是,sequence是确保每个VAA对于消息都是唯一的重要信息(即:两个不同的消息即使消息内容相同也会有不同的VAA)。
sequence数字在每次publishMessage调用中由useSequence函数增加1:
这是保证相同消息永远不会被双重交付的关键。我们将在下一部分中进一步详细说明这一点。
payload字节数组包含消息内容。例如,对于一个代币Transfer,它包括转移金额、代币地址、代币链、收件人地址、目标链ID、转移费等:
struct Transfer {
// PayloadID uint8 = 1
uint8 payloadID;
// Amount being transferred (big-endian uint256)
uint256 amount;
// Address of the token. Left-zero-padded if shorter than 32 bytes
bytes32 tokenAddress;
// Chain ID of the token
uint16 tokenChain;
// Address of the recipient. Left-zero-padded if shorter than 32 bytes
bytes32 to;
// Chain ID of the recipient
uint16 toChain;
// Amount of tokens (big-endian uint256) that the user is willing to pay as relayer fee. Must be <= Amount.
uint256 fee;
}
一旦产生了VAA(即,一条消息经过2/3守护者的签名),它将存储在守护者网络中(可能会存放很长时间或甚至永久)。
每个VAA通过其emitterChain、emittedAddress和sequence唯一索引,并可以通过此信息查询守护者节点(通过RPC API)来获取:
// 从Wormhole网络获取signedVAA(这可能需要重试以等待确认)
const { signedVAA } = await getSignedVAA(
WORMHOLE_RPC_HOST,
CHAIN_ID_ETH,
emitterAddress,
sequence
);
b, err := s.db.GetSignedVAABytes(db.VAAID{
EmitterChain: vaa.ChainID(req.MessageId.EmitterChain.Number()),
EmitterAddress: addr,
Sequence: req.MessageId.Sequence,
})
{
"EmitterChain": "ethereum",
"EmitterAddress": "0000000000000000000000003ee18b2214aff97000d974cf647e7c347e8fa585",
"Sequence": "99139",
}
将返回以下VAA字节:
{"vaaBytes":"AQAAAAINAAOuz9Ep03HIS1T2ypAoahT/BO2UBHpRWxiA1CfnlUUKVvMnPyR+E4Eusb1JkHivpoi6mBcEUvDkGW/bOThL7hoAArNvvcthq87MTGsQHjReeIIL0o3G0Epg/kT438O3GzDtZki5wlZ5YpRbuISJlT3nB7x8fYsxNUpJFAhO1DOyHj8BA3XfOUc2I0oIbQF8waRjl27BQQJsexBmNvXyT9XWJEwoXqq5hSQ+tf/0AUuGwHQ9ae84ENBpxyQhgfLxjqm3YAoBBAlFk8Rj/3Am5ULZUZGpGuoghigwNpCH8/OQLq9KEpxYXgTVecr9yznjvlw8PeerhjSUvgjjEuoT6bMV9WWHhPYBBaJOC3wrK3Lp6fbTcLlkRxJqjLPWeu6I33a3BKSAbJtcRAdm40CWwX5t0Kgt5EadmrvIRLIiKu0w037ok9nZUdIABocfns7u1MOoYaUdozFdyd2yJauIXNtAHrhHWE7QXq2XCITt6LK3qSGex9kwZhVeQVxfbplphKDS4ecDEo3l7/UBDLx+py+3KJWOByFPE5ZSBvimJ3GL3KOARgXM13+FvZxDL4tbeil+im9nVEb2J0J7dFBSPAnIP6+6QXkGtMK2Tx8BDTItV66QzmT4wQ9Io6Is+X7xjjhWIThkoPkUVHxqcLQjId1fOs69sd1KuXRt7Fi7TTVb7+OcPh15Uwkx1U5gAH8BDkJ4qZNL3y0gd7hFBLs57NxUPMljmdgbADS2uv5M7i0icdW9AUYxkY3mJXhUlKeS40VFMBAONKnwiIxfAgO/MXsAD/U/vytJGBHNrXO/OSC/Kkc8rH9h57pzUD2gBRULRjTUW4hL9iOULd6HbjYTW9F/U+jqh1SLktILvXS70c+gQnUAEB6M8654Oi/bCoChGFc1/vfDed0n5e9geecJvXz9fIHgMQG561i9aYmhoYtaAPrwW0NV0WweUJBZr3sgD9Yxm0YBEekZvilyWm03vf1/1WiOU6HO15FBQGb8xKyUIEp9BtuWTHAEy3rRe7KfC2grX8XvxBiUK8RDw2kc3M3udKt6nv4AEjivx2qzCoyUcvDJ2GaotcyiWRbZSYfd67PdeNDtlQhyOtD3eGxqKDC8UfxdS/LiTpLVY6sTG7P3TmXm6RBzko0BY7o2y1grAQAAAgAAAAAAAAAAAAAAAD7hiyIUr/lwANl0z2R+fDR+j6WFAAAAAAABg0MBAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAmwGdAAAAAAAAAAAAAAAAwCqqObIj/o0KDlxPJ+rZCDx1bMIAAv3Kt+tcG5vAryvnKh9emBXjIeGPLemctNwQfsx3bVQvAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAJrHQ=="}
对应从以太坊到Solana的0.1 WETH的转移($128.15):
图1:从以太坊到Solana的0.1 WETH的代币转账,见Wormhole交易
任何人都可以查询守护者节点,返回的VAA字节可以由任何人提交到目标链以完成消息(例如,向VAA中编码的接收地址铸造比特币)。
考虑图1中的消息“从以太坊到Solana的0.1 WETH的代币转账”。从用户的角度来看,实际上涉及五个链上交易(在以太坊上一个,在Solana上四个),顺序如下:
用户0x4f98通过调用wrapAndTransferETH,将0.1 WETH发送到Wormhole代币桥0x3ee1,并带有指定接收链(Solana 0x01)、接收者(0xfdca,在Solana上以base58编码为7vfC)、仲介费(即,如果使用则为中继费用)和nonce的参数:
Tx1 0x6539 在以太坊上调用了wrapAndTransferETH
Tx1内部会调用Wormhole核心桥0x98f3,发出包含sequence、nonce、payload和consistencyLevel等信息的消息,如下所示:
守护者观察到上面所示的消息,并产生一个VAA(AQAA…),如图1所示。
接下来,从守护者网络获取VAA并用于调用Solana的Wormhole核心桥合约。
SolanaWormhole核心桥上通过签名者HZBb调用VerifySignatures函数,使用VAA创建SignatureSet58Ui:
该交易还调用预编译的Secp256k1 SigVerify程序验证VAA中的守护者签名。
VAA总共包含13个签名。由于在Solana上的计算限制,Wormhole将这些签名的验证分成两次交易。
00 ff 01 02 03 04 05 ff ff ff ff ff 06 ff ff ff ff ff ff
ff ff ff ff ff ff ff ff ff ff ff ff ff 00 01 02 03 04 05
在验证了VAA中的所有签名之后,便可以调用PostVAA函数来创建消息账户31Np,该账户唯一标识转移的消息:
pub message: Mut<PostedVAA<’b, { AccountState::MaybeInitialized }>>,
另外两笔交易3goi和ZpYN也成功调用了PostVAA,但是,因为消息账户是PDA,所以它只会在Tx4(5AoD)中首次初始化。
PostVAA函数中会检查2/3的法定人数:
signature_count > 2/3 guardian_set.keys.size
最后,在Wormhole代币桥(wormDTUJ)上调用了CompleteWrapped函数以完成转移。
接收者0xfdca实际上是Solana上由4Kt8拥有的一个关联代币账户(PDA)以接收相应的WETH代币(ETH — Ether (Portal) 7vfC)。如果接收者尚不存在,则必须在Tx5之前创建该接收者账户。账户0xfdca是在Solana上的Tx sSjG中创建的。
验证守护者签名对任何跨链桥的安全性至关重要,这并不奇怪,这是一个复杂的任务。我们将在第二部分中详细阐述。
任何人都可以检索VAA并执行后续交易。这让消息发送者(例如,在源链上发起代币转账的用户)这样做变得很简单。可是,如果用户在目标链上没有账户或者用户的余额不足以支付交易费用,怎么办?
为了解决这个问题,Wormhole允许bridgerelayers传送消息并赚取费用。费用可以在源交易中指定,并编码到VAA负载中:
在目标链上转移的代币必须与在源链上转移的代币相同或等值。例如,两个都是USDC,或一个是在以太坊上的Wrapped Ether (WETH),另一个是在Solana上的ETH — Ether (Portal)。但不可能是以太坊上的WETH和Solana上的USDC。我们将在第三部分讨论这个部分。
这是一个微妙的点,并涉及到对目标链的仔细设计。本质上,需要一个全局状态来标记每条已交付的消息,并拒绝尝试重复交付相同消息的交易。我们将在第四部分进一步解释这部分。
sec3是一家安全研究公司,旨在为数百万用户的Solana项目提供准备。sec3的启动审核是一项严格的,由研究人员主导的代码检查,调查并验证主网级智能合约;sec3的持续审核软件平台X-ray与Github集成,逐步扫描拉取请求,帮助项目在部署之前加固代码;而sec3的后部署安全解决方案WatchTower则确保资金安全。sec3正在为Web3项目构建基于技术的可扩展解决方案,以确保协议在扩展时保持安全。
要了解更多关于sec3的信息,请访问https://www.sec3.dev
- 原文链接: sec3.dev/blog/bridge1...
- 登链社区 AI 助手,为大家转译优秀英文文章,如有翻译不通的地方,还请包涵~
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!