// 堆代码 www.duidaima.com function addSequencerL2Batch( uint256 sequenceNumber, bytes calldata data, uint256 afterDelayedMessagesRead, IGasRefunder gasRefunder, uint256 prevMessageCount, uint256 newMessageCount ) external override refundsGas(gasRefunder) { if ( !isBatchPoster[msg.sender] && msg.sender != address(rollup) ) revert NotBatchPoster(); [...] addSequencerL2BatchImpl( dataHash_, afterDelayedMessagesRead_, 0, prevMessageCount_, newMessageCount_ ); [...] }然后在addSequencerL2BatchImpl[9]里面调用桥接(bridge),将信息排队到收件箱。
bridge.enqueueSequencerMessage( dataHash, afterDelayedMessagesRead, prevMessageCount, newMessageCount );然后在桥接中调用 enqueueSequencerMessage[10],它只是向收件箱数组添加一个新的哈希值。
bytes32[] public sequencerInboxAccs; function enqueueSequencerMessage( bytes32 dataHash, uint256 afterDelayedMessagesRead, uint256 prevMessageCount, uint256 newMessageCount ) external onlySequencerInbox returns ( uint256 seqMessageIndex, bytes32 beforeAcc, bytes32 delayedAcc, bytes32 acc ) { [...] acc = keccak256(abi.encodePacked(beforeAcc, dataHash, delayedAcc)); sequencerInboxAccs.push(acc); }
bytes32[] public delayedInboxAccs; function enqueueDelayedMessage( uint8 kind, address sender, bytes32 messageDataHash ) external payable returns (uint256) { [...] delayedInboxAccs.push( Messages.accumulateInboxMessage( prevAcc, messageHash ) ); [...] } function deliverToBridge( uint8 kind, address sender, bytes32 messageDataHash ) internal returns (uint256) { return bridge.enqueueDelayedMessage{value: msg.value}( kind, AddressAliasHelper.applyL1ToL2Alias(sender), messageDataHash ); }
// 堆代码 www.duidaima.com function forceInclusion( uint256 _totalDelayedMessagesRead, uint8 kind, uint64[2] calldata l1BlockAndTime, uint256 baseFeeL1, address sender, bytes32 messageDataHash ) external { [...] if (l1BlockAndTime[0] + maxTimeVariation.delayBlocks >= block.number) revert ForceIncludeBlockTooSoon(); if (l1BlockAndTime[1] + maxTimeVariation.delaySeconds >= block.timestamp) revert ForceIncludeTimeTooSoon(); [...] addSequencerL2BatchImpl( dataHash, __totalDelayedMessagesRead, 0, prevSeqMsgCount, newSeqMsgCount ); [...] }
struct Machine { MachineStatus status; ValueStack valueStack; ValueStack internalStack; StackFrameWindow frameStack; bytes32 globalStateHash; uint32 moduleIdx; uint32 functionIdx; uint32 functionPc; bytes32 modulesRoot; }挑战者将用所有的数据初始化这个机器 Machine。在合约中,我们只需要再次检查这些数据是否代表了存储的 Merkle 根哈希值。
require(mach.hash() == beforeHash, "MACHINE_BEFORE_HASH")现在我们可以信任 Module(模块)根,用它来验证模块的数据。
struct Module { bytes32 globalsMerkleRoot; ModuleMemory moduleMemory; bytes32 tablesMerkleRoot; bytes32 functionsMerkleRoot; uint32 internalsOffset; }这里面持有的数据是 WAVM 机器数据的进一步 Merkle 根哈希值的形式。而挑战者也初始化了这些数据。
(mod, offset) = Deserialize.module(proof, offset); (modProof, offset) = Deserialize.merkleProof(proof, offset); require( modProof.computeRootFromModule(mach.moduleIdx, mod) == mach.modulesRoot, "MODULES_ROOT" );最后我们再对Instruction(指令)[19]数据做同样的处理。
struct Instruction { uint16 opcode; uint256 argumentData; }并且将通过函数 MerkleRoot 进行验证:
MerkleProof memory instProof; MerkleProof memory funcProof; (inst, offset) = Deserialize.instruction(proof, offset); (instProof, offset) = Deserialize.merkleProof(proof, offset); (funcProof, offset) = Deserialize.merkleProof(proof, offset); bytes32 codeHash = instProof.computeRootFromInstruction(mach.functionPc, inst); bytes32 recomputedRoot = funcProof.computeRootFromFunction( mach.functionIdx, codeHash ); require(recomputedRoot == mod.functionsMerkleRoot, "BAD_FUNCTIONS_ROOT");所以现在我们有一个初始化的 WAVM 机器,剩下的就是执行某个有分歧的的操作。现在这取决于我们需要运行的确切指令。
uint32 b = mach.valueStack.pop().assumeI32(); uint32 a = mach.valueStack.pop().assumeI32(); [...] return (a + b, false);
// 堆代码 www.duidaima.com function executeLocalGet( Machine memory mach, Module memory, Instruction calldata inst, bytes calldata proof ) internal pure { StackFrame memory frame = mach.frameStack.peek(); Value memory val = merkleProveGetValue(frame.localsMerkleRoot, inst.argumentData, proof); mach.valueStack.push(val); }这个StackFrame[22]来自 WAVM 的初始化,在这里我们可以找到 localsMerkleRoot。
struct StackFrame { Value returnPc; bytes32 localsMerkleRoot; uint32 callerModule; uint32 callerModuleInternals; }并通过 Merkle 证明,我们可以检索到该值并将其推送到堆栈。最后,我们检查[23]这个计算步骤产生的最终哈希值是否等于存储的哈希值。
require( afterHash != selection.oldSegments[selection.challengePosition + 1], "SAME_OSP_END" );只有当它不匹配时,证明才有效,我们继续。现在挑战者赢了,一个新的后状态将被接受。
5.msg.sender的工作方式与以太坊上正常的 L2 到 L2 交易相同;对于 L1 到 L2 的 “retryable ticket(可重试票据) ”交易,它将返回触发消息的 L1 合约的 L2 地址别名。更多内容见可重试票据地址别名[25]。
const params = [{ "chainId": "42161", // testnet: "421611" "chainName": "Arbitrum", "rpcUrls": [ "https://arb1.arbitrum.io/rpc" // rinkeby: "https://rinkeby.arbitrum.io/rpc" // goerli: "https://goerli-rollup.arbitrum.io/rpc" ], "nativeCurrency": { "name": "Ether", "symbol": "ETH", "decimals": 18 }, "blockExplorerUrls": [ "https://explorer.arbitrum.io" // rinkeby: "https://rinkeby-explorer.arbitrum.io" // goerli: "https://goerli-rollup-explorer.arbitrum.io" ] }] try { await ethereum.request({ method: 'wallet_addEthereumChain', params, }) } catch (error) { // something failed, e.g., user denied request }要在 Arbitrum 获得资金,可使用 https://bridge.arbitrum.io/ 提供的桥。
{ arbitrum_mainnet: { provider: function () { return new HDWalletProvider( mnemonic, "https://arbitrum-mainnet.infura.io/v3/" + infuraKey, 0, 1 ); }, }, arbitrum_rinkeby: { provider: function () { return new HDWalletProvider( mnemonic, "https://rinkeby.arbitrum.io/rpc", 0, 1 ); }, }, arbitrum_goerli: { provider: function () { return new HDWalletProvider( mnemonic, "https://goerli-rollup.arbitrum.io/rpc", 0, 1 ); } } }推荐的一个好的做法是用 Hardhat 编写测试,用常规的本地配置,这样你可以快速运行测试,并有 console.log/stacktraces 功能可用。如果需要,可以在Infura[30]设置中激活 Arbitrum。
参考资料
[1]登链翻译计划: https://github.com/lbc-team/Pioneer
[2]翻译小组: https://learnblockchain.cn/people/412
[3]Tiny 熊: https://learnblockchain.cn/people/15
[4]最成功的: https://twitter.com/JackNiewold/status/1564374766441611266
[5]Merkle 树指南: https://learnblockchain.cn/article/5297
[6]UTXO: https://www.investopedia.com/terms/u/utxo.asp
[7]这里: https://www.learnplasma.org/en/learn/mvp.html#plasma-mvp
[8]addSequencerL2Batch: https://github.com/OffchainLabs/nitro/blob/2678e0b57abfbcda8f21e844a94368eea5389348/contracts/src/bridge/SequencerInbox.sol#L235
[9]addSequencerL2BatchImpl: https://github.com/OffchainLabs/nitro/blob/2678e0b57abfbcda8f21e844a94368eea5389348/contracts/src/bridge/SequencerInbox.sol#L338-L383
[10]enqueueSequencerMessage: https://github.com/OffchainLabs/nitro/blob/2678e0b57abfbcda8f21e844a94368eea5389348/contracts/src/bridge/Bridge.sol#L100-L132
[11]deliverToBridge: https://github.com/OffchainLabs/nitro/blob/2678e0b57abfbcda8f21e844a94368eea5389348/contracts/src/bridge/Inbox.sol#L419-L430
[12]enqueueDelayedMessage: https://github.com/OffchainLabs/nitro/blob/2678e0b57abfbcda8f21e844a94368eea5389348/contracts/src/bridge/Bridge.sol#L152-L167
[13]forceInclusion(强制包含): https://github.com/OffchainLabs/nitro/blob/2678e0b57abfbcda8f21e844a94368eea5389348/contracts/src/bridge/SequencerInbox.sol#L89-L152
[14]这里: https://developer.offchainlabs.com/inside-arbitrum-nitro#dissection-protocol-simplified-version
[15]源代码: https://github.com/OffchainLabs/nitro/blob/c191708c7847bf9e92c3c0a5263d31e4876d9e18/contracts/src/challenge/ChallengeManager.sol#L171
[16]WAVM 机器(Machine): https://github.com/OffchainLabs/nitro/blob/c191708c7847bf9e92c3c0a5263d31e4876d9e18/contracts/src/state/Machine.sol#L18-L28
[17]模块: https://github.com/OffchainLabs/nitro/blob/c191708c7847bf9e92c3c0a5263d31e4876d9e18/contracts/src/state/Module.sol#L9
[18]验证它: https://github.com/OffchainLabs/nitro/blob/c191708c7847bf9e92c3c0a5263d31e4876d9e18/contracts/src/osp/OneStepProofEntry.sol#L60
[19]Instruction(指令): https://github.com/OffchainLabs/nitro/blob/c191708c7847bf9e92c3c0a5263d31e4876d9e18/contracts/src/state/Instructions.sol#L7
[20]简单加法: https://github.com/OffchainLabs/nitro/blob/c191708c7847bf9e92c3c0a5263d31e4876d9e18/contracts/src/osp/OneStepProverMath.sol#L217
[21]本地获取: https://github.com/OffchainLabs/nitro/blob/c191708c7847bf9e92c3c0a5263d31e4876d9e18/contracts/src/osp/OneStepProver0.sol#L334
[22]StackFrame: https://github.com/OffchainLabs/nitro/blob/c191708c7847bf9e92c3c0a5263d31e4876d9e18/contracts/src/state/StackFrame.sol#L9
[23]检查: https://github.com/OffchainLabs/nitro/blob/c191708c7847bf9e92c3c0a5263d31e4876d9e18/contracts/src/challenge/ChallengeManager.sol#L262
[24]支持Solidity: https://developer.offchainlabs.com/solidity-support
[25]可重试票据地址别名: https://developer.offchainlabs.com/arbos/l1-to-l2-messaging#address-aliasing
[26]wallet_addEthereumChain: https://docs.metamask.io/guide/rpc-api.html#wallet-addethereumchain
[27]点击这里连接到 Arbitrum One: https://chainlist.org/chain/42161
[28]点击这里连接到 Arbitrrum Nitro Rinkeby: https://chainlist.org/chain/421611
[29]点击这里连接到 Aribitrum Nitro Goerli: https://chainlist.org/chain/421613
[30]Infura: https://infura.io