From 2ef37a302f2b1f3e9e8cef2c8c08795dbbce896c Mon Sep 17 00:00:00 2001 From: Segue Date: Tue, 11 Aug 2026 19:29:59 +0800 Subject: [PATCH 01/16] chore(abi): freeze pre-submitters cutover baselines --- contracts/abi/archive/README.md | 12 + .../archive/legacy-l1-staking/L1Staking.json | 1033 ++++++++++++ contracts/abi/archive/manifest.json | 13 + .../abi/archive/pre-submitter/Rollup.json | 1416 +++++++++++++++++ .../archive/Rollup.pre-submitter.json | 424 +++++ .../app/src/abi/pre_submitter/Rollup.json | 1416 +++++++++++++++++ node/types/abi/pre_submitter_rollup.json | 1416 +++++++++++++++++ .../src/abi/pre_submitter/Rollup.json | 1416 +++++++++++++++++ .../abi/pre_submitter/Rollup.json | 1416 +++++++++++++++++ 9 files changed, 8562 insertions(+) create mode 100644 contracts/abi/archive/README.md create mode 100644 contracts/abi/archive/legacy-l1-staking/L1Staking.json create mode 100644 contracts/abi/archive/manifest.json create mode 100644 contracts/abi/archive/pre-submitter/Rollup.json create mode 100644 contracts/storage-layout/archive/Rollup.pre-submitter.json create mode 100644 gas-oracle/app/src/abi/pre_submitter/Rollup.json create mode 100644 node/types/abi/pre_submitter_rollup.json create mode 100644 prover/bin/challenge/src/abi/pre_submitter/Rollup.json create mode 100644 prover/bin/shadow-prove/abi/pre_submitter/Rollup.json diff --git a/contracts/abi/archive/README.md b/contracts/abi/archive/README.md new file mode 100644 index 000000000..20b3253a3 --- /dev/null +++ b/contracts/abi/archive/README.md @@ -0,0 +1,12 @@ +# Archived production ABIs + +These ABI-only artifacts are immutable compatibility inputs for the centralized +submitter cutover. They were frozen from the pre-Submitter source tree before +the current Rollup ABI was regenerated. + +- `pre-submitter/Rollup.json` decodes the three production submission methods + that still include `BatchSignatureInput`. +- `legacy-l1-staking/L1Staking.json` is the only runtime interface used by the + L1 staking retirement tooling after the legacy Solidity source is removed. + +Do not regenerate or overwrite these files from the current contracts. diff --git a/contracts/abi/archive/legacy-l1-staking/L1Staking.json b/contracts/abi/archive/legacy-l1-staking/L1Staking.json new file mode 100644 index 000000000..e8d86b6d8 --- /dev/null +++ b/contracts/abi/archive/legacy-l1-staking/L1Staking.json @@ -0,0 +1,1033 @@ +[ + { + "inputs": [ + { + "internalType": "address payable", + "name": "_messenger", + "type": "address" + } + ], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint256", + "name": "oldChallengeDeposit", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "newChallengeDeposit", + "type": "uint256" + } + ], + "name": "ChallengeDepositUpdated", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "staker", + "type": "address" + }, + { + "indexed": false, + "internalType": "address", + "name": "receiver", + "type": "address" + } + ], + "name": "Claimed", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint256", + "name": "oldGasLimit", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "newGasLimit", + "type": "uint256" + } + ], + "name": "GasLimitAddStakerUpdated", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint256", + "name": "oldGasLimit", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "newGasLimit", + "type": "uint256" + } + ], + "name": "GasLimitRemoveStakersUpdated", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint8", + "name": "version", + "type": "uint8" + } + ], + "name": "Initialized", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "previousOwner", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "newOwner", + "type": "address" + } + ], + "name": "OwnershipTransferred", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "addr", + "type": "address" + }, + { + "indexed": false, + "internalType": "bytes32", + "name": "tmKey", + "type": "bytes32" + }, + { + "indexed": false, + "internalType": "bytes", + "name": "blsKey", + "type": "bytes" + } + ], + "name": "Registered", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint256", + "name": "oldPercentage", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "newPercentage", + "type": "uint256" + } + ], + "name": "RewardPercentageUpdated", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "receiver", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "SlashRemainingClaimed", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address[]", + "name": "stakers", + "type": "address[]" + } + ], + "name": "Slashed", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address[]", + "name": "stakers", + "type": "address[]" + } + ], + "name": "StakersRemoved", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint256", + "name": "oldStakingValue", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "newStakingValue", + "type": "uint256" + } + ], + "name": "StakingValueUpdated", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address[]", + "name": "add", + "type": "address[]" + }, + { + "indexed": false, + "internalType": "address[]", + "name": "remove", + "type": "address[]" + } + ], + "name": "WhitelistUpdated", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "addr", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "unlockHeight", + "type": "uint256" + } + ], + "name": "Withdrawn", + "type": "event" + }, + { + "inputs": [], + "name": "MESSENGER", + "outputs": [ + { + "internalType": "contract ICrossDomainMessenger", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "OTHER_STAKING", + "outputs": [ + { + "internalType": "contract Staking", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes", + "name": "blsPubkey", + "type": "bytes" + } + ], + "name": "blsKeys", + "outputs": [ + { + "internalType": "bool", + "name": "exist", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "challengeDeposit", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "receiver", + "type": "address" + } + ], + "name": "claimSlashRemaining", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "receiver", + "type": "address" + } + ], + "name": "claimWithdrawal", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "cleanStakerStore", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "name": "deleteList", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "stakerAddr", + "type": "address" + } + ], + "name": "deleteableHeight", + "outputs": [ + { + "internalType": "uint256", + "name": "height", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "gasLimitAddStaker", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "gasLimitRemoveStakers", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getActiveStakers", + "outputs": [ + { + "internalType": "address[]", + "name": "", + "type": "address[]" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_staker", + "type": "address" + } + ], + "name": "getStakerBitmap", + "outputs": [ + { + "internalType": "uint256", + "name": "bitmap", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getStakers", + "outputs": [ + { + "internalType": "address[255]", + "name": "", + "type": "address[255]" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address[]", + "name": "_stakers", + "type": "address[]" + } + ], + "name": "getStakersBitmap", + "outputs": [ + { + "internalType": "uint256", + "name": "bitmap", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "bitmap", + "type": "uint256" + } + ], + "name": "getStakersFromBitmap", + "outputs": [ + { + "internalType": "address[]", + "name": "stakerAddrs", + "type": "address[]" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_rollupContract", + "type": "address" + }, + { + "internalType": "uint256", + "name": "_stakingValue", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "_challengeDeposit", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "_lockBlocks", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "_rewardPercentage", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "_gasLimitAdd", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "_gasLimitRemove", + "type": "uint256" + } + ], + "name": "initialize", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "addr", + "type": "address" + } + ], + "name": "isActiveStaker", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "addr", + "type": "address" + } + ], + "name": "isStaker", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "addr", + "type": "address" + } + ], + "name": "isStakerInDeleteList", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "messenger", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "owner", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "tmKey", + "type": "bytes32" + }, + { + "internalType": "bytes", + "name": "blsKey", + "type": "bytes" + } + ], + "name": "register", + "outputs": [], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address[]", + "name": "_stakers", + "type": "address[]" + } + ], + "name": "removeStaker", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "stakerAddr", + "type": "address" + } + ], + "name": "removedList", + "outputs": [ + { + "internalType": "bool", + "name": "removed", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "renounceOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "rewardPercentage", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "rollupContract", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "sequencersBitmap", + "type": "uint256" + } + ], + "name": "slash", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "slashRemaining", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "stakerAddr", + "type": "address" + } + ], + "name": "stakerIndexes", + "outputs": [ + { + "internalType": "uint8", + "name": "index", + "type": "uint8" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "name": "stakerSet", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "stakerAddr", + "type": "address" + } + ], + "name": "stakers", + "outputs": [ + { + "internalType": "address", + "name": "addr", + "type": "address" + }, + { + "internalType": "bytes32", + "name": "tmKey", + "type": "bytes32" + }, + { + "internalType": "bytes", + "name": "blsKey", + "type": "bytes" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "stakingValue", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "tmPubkey", + "type": "bytes32" + } + ], + "name": "tmKeys", + "outputs": [ + { + "internalType": "bool", + "name": "exist", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "newOwner", + "type": "address" + } + ], + "name": "transferOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "_challengeDeposit", + "type": "uint256" + } + ], + "name": "updateChallengeDeposit", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "_gasLimitAdd", + "type": "uint256" + } + ], + "name": "updateGasLimitAddStaker", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "_gasLimitRemove", + "type": "uint256" + } + ], + "name": "updateGasLimitRemoveStakers", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "_rewardPercentage", + "type": "uint256" + } + ], + "name": "updateRewardPercentage", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "_stakingValue", + "type": "uint256" + } + ], + "name": "updateStakingValue", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address[]", + "name": "add", + "type": "address[]" + }, + { + "internalType": "address[]", + "name": "remove", + "type": "address[]" + } + ], + "name": "updateWhitelist", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + }, + { + "internalType": "address[]", + "name": "", + "type": "address[]" + }, + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + }, + { + "internalType": "bytes", + "name": "", + "type": "bytes" + } + ], + "name": "verifySignature", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "pure", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "stakerAddr", + "type": "address" + } + ], + "name": "whitelist", + "outputs": [ + { + "internalType": "bool", + "name": "inWhitelist", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "withdraw", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "withdrawalLockBlocks", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "staker", + "type": "address" + } + ], + "name": "withdrawals", + "outputs": [ + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + } +] diff --git a/contracts/abi/archive/manifest.json b/contracts/abi/archive/manifest.json new file mode 100644 index 000000000..fd4d36083 --- /dev/null +++ b/contracts/abi/archive/manifest.json @@ -0,0 +1,13 @@ +{ + "sourceCommit": "3d21e1f45f743b7fb4131fd6655ceb4d99f15356", + "artifacts": { + "pre-submitter/Rollup.json": "aea8c9f75f2c8e132a4ffca3f0bb3f6abd83ae2ae9457827a099e75463c5c03a", + "legacy-l1-staking/L1Staking.json": "df14c1e4264ba93661b1476ed9166ab06e03d077713fa13b16fded7f1dd6eb46", + "../../storage-layout/archive/Rollup.pre-submitter.json": "f8a745e49d6ea6e141ae26440ce12d6b2feeb155ef1dfdbe9dff40d124c59c37" + }, + "selectors": { + "commitBatch": "0x428868b5", + "commitState": "0x1e8825be", + "commitBatchWithProof": "0x4e8f1d67" + } +} diff --git a/contracts/abi/archive/pre-submitter/Rollup.json b/contracts/abi/archive/pre-submitter/Rollup.json new file mode 100644 index 000000000..c8c7586c1 --- /dev/null +++ b/contracts/abi/archive/pre-submitter/Rollup.json @@ -0,0 +1,1416 @@ +[ + { + "inputs": [ + { + "internalType": "uint64", + "name": "_chainID", + "type": "uint64" + } + ], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "inputs": [], + "name": "ErrZeroAddress", + "type": "error" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint256", + "name": "batchIndex", + "type": "uint256" + }, + { + "indexed": true, + "internalType": "address", + "name": "winner", + "type": "address" + }, + { + "indexed": true, + "internalType": "string", + "name": "res", + "type": "string" + } + ], + "name": "ChallengeRes", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "receiver", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "ChallengeRewardClaim", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint64", + "name": "batchIndex", + "type": "uint64" + }, + { + "indexed": true, + "internalType": "address", + "name": "challenger", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "challengeDeposit", + "type": "uint256" + } + ], + "name": "ChallengeState", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint256", + "name": "batchIndex", + "type": "uint256" + }, + { + "indexed": true, + "internalType": "bytes32", + "name": "batchHash", + "type": "bytes32" + } + ], + "name": "CommitBatch", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint256", + "name": "batchIndex", + "type": "uint256" + }, + { + "indexed": true, + "internalType": "bytes32", + "name": "batchHash", + "type": "bytes32" + }, + { + "indexed": false, + "internalType": "bytes32", + "name": "stateRoot", + "type": "bytes32" + }, + { + "indexed": false, + "internalType": "bytes32", + "name": "withdrawRoot", + "type": "bytes32" + } + ], + "name": "FinalizeBatch", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint8", + "name": "version", + "type": "uint8" + } + ], + "name": "Initialized", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "previousOwner", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "newOwner", + "type": "address" + } + ], + "name": "OwnershipTransferred", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "account", + "type": "address" + } + ], + "name": "Paused", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "receiver", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "ProveRemainingClaimed", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint256", + "name": "batchIndex", + "type": "uint256" + }, + { + "indexed": true, + "internalType": "bytes32", + "name": "batchHash", + "type": "bytes32" + } + ], + "name": "RevertBatch", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "account", + "type": "address" + } + ], + "name": "Unpaused", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "account", + "type": "address" + }, + { + "indexed": false, + "internalType": "bool", + "name": "status", + "type": "bool" + } + ], + "name": "UpdateChallenger", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint256", + "name": "oldPeriod", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "newPeriod", + "type": "uint256" + } + ], + "name": "UpdateFinalizationPeriodSeconds", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint256", + "name": "oldPercent", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "newPercent", + "type": "uint256" + } + ], + "name": "UpdateProofRewardPercent", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint256", + "name": "oldWindow", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "newWindow", + "type": "uint256" + } + ], + "name": "UpdateProofWindow", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint256", + "name": "oldPeriod", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "newPeriod", + "type": "uint256" + } + ], + "name": "UpdateRollupDelayPeriod", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "oldVerifier", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "newVerifier", + "type": "address" + } + ], + "name": "UpdateVerifier", + "type": "event" + }, + { + "inputs": [], + "name": "LAYER_2_CHAIN_ID", + "outputs": [ + { + "internalType": "uint64", + "name": "", + "type": "uint64" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "__maxNumTxInChunk", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_account", + "type": "address" + } + ], + "name": "addChallenger", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "batchIndex", + "type": "uint256" + } + ], + "name": "batchBlobVersionedHashes", + "outputs": [ + { + "internalType": "bytes32", + "name": "blobVersionedHash", + "type": "bytes32" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "owner", + "type": "address" + } + ], + "name": "batchChallengeReward", + "outputs": [ + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "batchChallenged", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "batchIndex", + "type": "uint256" + } + ], + "name": "batchChallengedSuccess", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "batchIndex", + "type": "uint256" + } + ], + "name": "batchDataStore", + "outputs": [ + { + "internalType": "uint256", + "name": "originTimestamp", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "finalizeTimestamp", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "blockNumber", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "signedSequencersBitmap", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "batchIndex", + "type": "uint256" + } + ], + "name": "batchExist", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "batchIndex", + "type": "uint256" + } + ], + "name": "batchInChallenge", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "batchIndex", + "type": "uint256" + } + ], + "name": "batchInsideChallengeWindow", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint64", + "name": "batchIndex", + "type": "uint64" + }, + { + "internalType": "bytes32", + "name": "_batchHash", + "type": "bytes32" + } + ], + "name": "challengeState", + "outputs": [], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "batchIndex", + "type": "uint256" + } + ], + "name": "challenges", + "outputs": [ + { + "internalType": "uint64", + "name": "batchIndex", + "type": "uint64" + }, + { + "internalType": "address", + "name": "challenger", + "type": "address" + }, + { + "internalType": "uint256", + "name": "challengeDeposit", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "startTime", + "type": "uint256" + }, + { + "internalType": "bool", + "name": "challengeSuccess", + "type": "bool" + }, + { + "internalType": "bool", + "name": "finished", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "receiver", + "type": "address" + } + ], + "name": "claimProveRemaining", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "receiver", + "type": "address" + } + ], + "name": "claimReward", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "components": [ + { + "internalType": "uint8", + "name": "version", + "type": "uint8" + }, + { + "internalType": "bytes", + "name": "parentBatchHeader", + "type": "bytes" + }, + { + "internalType": "uint64", + "name": "lastBlockNumber", + "type": "uint64" + }, + { + "internalType": "uint16", + "name": "numL1Messages", + "type": "uint16" + }, + { + "internalType": "bytes32", + "name": "prevStateRoot", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "postStateRoot", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "withdrawalRoot", + "type": "bytes32" + } + ], + "internalType": "struct IRollup.BatchDataInput", + "name": "batchDataInput", + "type": "tuple" + }, + { + "components": [ + { + "internalType": "uint256", + "name": "signedSequencersBitmap", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "sequencerSets", + "type": "bytes" + }, + { + "internalType": "bytes", + "name": "signature", + "type": "bytes" + } + ], + "internalType": "struct IRollup.BatchSignatureInput", + "name": "batchSignatureInput", + "type": "tuple" + } + ], + "name": "commitBatch", + "outputs": [], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [ + { + "components": [ + { + "internalType": "uint8", + "name": "version", + "type": "uint8" + }, + { + "internalType": "bytes", + "name": "parentBatchHeader", + "type": "bytes" + }, + { + "internalType": "uint64", + "name": "lastBlockNumber", + "type": "uint64" + }, + { + "internalType": "uint16", + "name": "numL1Messages", + "type": "uint16" + }, + { + "internalType": "bytes32", + "name": "prevStateRoot", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "postStateRoot", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "withdrawalRoot", + "type": "bytes32" + } + ], + "internalType": "struct IRollup.BatchDataInput", + "name": "batchDataInput", + "type": "tuple" + }, + { + "components": [ + { + "internalType": "uint256", + "name": "signedSequencersBitmap", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "sequencerSets", + "type": "bytes" + }, + { + "internalType": "bytes", + "name": "signature", + "type": "bytes" + } + ], + "internalType": "struct IRollup.BatchSignatureInput", + "name": "batchSignatureInput", + "type": "tuple" + }, + { + "internalType": "bytes", + "name": "_batchHeader", + "type": "bytes" + }, + { + "internalType": "bytes", + "name": "_batchProof", + "type": "bytes" + } + ], + "name": "commitBatchWithProof", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "components": [ + { + "internalType": "uint8", + "name": "version", + "type": "uint8" + }, + { + "internalType": "bytes", + "name": "parentBatchHeader", + "type": "bytes" + }, + { + "internalType": "uint64", + "name": "lastBlockNumber", + "type": "uint64" + }, + { + "internalType": "uint16", + "name": "numL1Messages", + "type": "uint16" + }, + { + "internalType": "bytes32", + "name": "prevStateRoot", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "postStateRoot", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "withdrawalRoot", + "type": "bytes32" + } + ], + "internalType": "struct IRollup.BatchDataInput", + "name": "batchDataInput", + "type": "tuple" + }, + { + "components": [ + { + "internalType": "uint256", + "name": "signedSequencersBitmap", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "sequencerSets", + "type": "bytes" + }, + { + "internalType": "bytes", + "name": "signature", + "type": "bytes" + } + ], + "internalType": "struct IRollup.BatchSignatureInput", + "name": "batchSignatureInput", + "type": "tuple" + } + ], + "name": "commitState", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "batchIndex", + "type": "uint256" + } + ], + "name": "committedBatches", + "outputs": [ + { + "internalType": "bytes32", + "name": "batchHash", + "type": "bytes32" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "batchIndex", + "type": "uint256" + } + ], + "name": "committedStateRoots", + "outputs": [ + { + "internalType": "bytes32", + "name": "stateRoot", + "type": "bytes32" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "finalizationPeriodSeconds", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes", + "name": "_batchHeader", + "type": "bytes" + } + ], + "name": "finalizeBatch", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "batchIndex", + "type": "uint256" + } + ], + "name": "finalizedStateRoots", + "outputs": [ + { + "internalType": "bytes32", + "name": "stateRoot", + "type": "bytes32" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes", + "name": "_batchHeader", + "type": "bytes" + } + ], + "name": "importGenesisBatch", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "inChallenge", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_l1StakingContract", + "type": "address" + }, + { + "internalType": "address", + "name": "_messageQueue", + "type": "address" + }, + { + "internalType": "address", + "name": "_verifier", + "type": "address" + }, + { + "internalType": "uint256", + "name": "_finalizationPeriodSeconds", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "_proofWindow", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "_proofRewardPercent", + "type": "uint256" + } + ], + "name": "initialize", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "_prevStateRoot", + "type": "bytes32" + } + ], + "name": "initialize2", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "_rollupDelayPeriod", + "type": "uint256" + } + ], + "name": "initialize3", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "_batchIndex", + "type": "uint256" + } + ], + "name": "isBatchFinalized", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "challengerAddress", + "type": "address" + } + ], + "name": "isChallenger", + "outputs": [ + { + "internalType": "bool", + "name": "isChallenger", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "l1StakingContract", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "lastCommittedBatchIndex", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "lastFinalizedBatchIndex", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "messageQueue", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "owner", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "paused", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "proofRewardPercent", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "proofWindow", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes", + "name": "_batchHeader", + "type": "bytes" + }, + { + "internalType": "bytes", + "name": "_batchProof", + "type": "bytes" + } + ], + "name": "proveCommittedBatchState", + "outputs": [], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "proveRemaining", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes", + "name": "_batchHeader", + "type": "bytes" + }, + { + "internalType": "bytes", + "name": "_batchProof", + "type": "bytes" + } + ], + "name": "proveState", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_account", + "type": "address" + } + ], + "name": "removeChallenger", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "renounceOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes", + "name": "_batchHeader", + "type": "bytes" + }, + { + "internalType": "uint256", + "name": "_count", + "type": "uint256" + } + ], + "name": "revertBatch", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "revertReqIndex", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "rollupDelayPeriod", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bool", + "name": "_status", + "type": "bool" + } + ], + "name": "setPause", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "newOwner", + "type": "address" + } + ], + "name": "transferOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "_newPeriod", + "type": "uint256" + } + ], + "name": "updateFinalizePeriodSeconds", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "_newWindow", + "type": "uint256" + } + ], + "name": "updateProofWindow", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "_newProofRewardPercent", + "type": "uint256" + } + ], + "name": "updateRewardPercentage", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "_newPeriod", + "type": "uint256" + } + ], + "name": "updateRollupDelayPeriod", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_newVerifier", + "type": "address" + } + ], + "name": "updateVerifier", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "verifier", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "withdrawalRoot", + "type": "bytes32" + } + ], + "name": "withdrawalRoots", + "outputs": [ + { + "internalType": "bool", + "name": "exist", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "stateMutability": "payable", + "type": "receive" + } +] diff --git a/contracts/storage-layout/archive/Rollup.pre-submitter.json b/contracts/storage-layout/archive/Rollup.pre-submitter.json new file mode 100644 index 000000000..847c1cf90 --- /dev/null +++ b/contracts/storage-layout/archive/Rollup.pre-submitter.json @@ -0,0 +1,424 @@ +{ + "storage": [ + { + "astId": 123005, + "contract": "contracts/l1/rollup/Rollup.sol:Rollup", + "label": "_initialized", + "offset": 0, + "slot": "0", + "type": "t_uint8" + }, + { + "astId": 123008, + "contract": "contracts/l1/rollup/Rollup.sol:Rollup", + "label": "_initializing", + "offset": 1, + "slot": "0", + "type": "t_bool" + }, + { + "astId": 125558, + "contract": "contracts/l1/rollup/Rollup.sol:Rollup", + "label": "__gap", + "offset": 0, + "slot": "1", + "type": "t_array(t_uint256)50_storage" + }, + { + "astId": 122758, + "contract": "contracts/l1/rollup/Rollup.sol:Rollup", + "label": "_owner", + "offset": 0, + "slot": "51", + "type": "t_address" + }, + { + "astId": 122878, + "contract": "contracts/l1/rollup/Rollup.sol:Rollup", + "label": "__gap", + "offset": 0, + "slot": "52", + "type": "t_array(t_uint256)49_storage" + }, + { + "astId": 123189, + "contract": "contracts/l1/rollup/Rollup.sol:Rollup", + "label": "_paused", + "offset": 0, + "slot": "101", + "type": "t_bool" + }, + { + "astId": 123294, + "contract": "contracts/l1/rollup/Rollup.sol:Rollup", + "label": "__gap", + "offset": 0, + "slot": "102", + "type": "t_array(t_uint256)49_storage" + }, + { + "astId": 8103, + "contract": "contracts/l1/rollup/Rollup.sol:Rollup", + "label": "l1StakingContract", + "offset": 0, + "slot": "151", + "type": "t_address" + }, + { + "astId": 8106, + "contract": "contracts/l1/rollup/Rollup.sol:Rollup", + "label": "finalizationPeriodSeconds", + "offset": 0, + "slot": "152", + "type": "t_uint256" + }, + { + "astId": 8109, + "contract": "contracts/l1/rollup/Rollup.sol:Rollup", + "label": "proofWindow", + "offset": 0, + "slot": "153", + "type": "t_uint256" + }, + { + "astId": 8112, + "contract": "contracts/l1/rollup/Rollup.sol:Rollup", + "label": "__maxNumTxInChunk", + "offset": 0, + "slot": "154", + "type": "t_uint256" + }, + { + "astId": 8115, + "contract": "contracts/l1/rollup/Rollup.sol:Rollup", + "label": "messageQueue", + "offset": 0, + "slot": "155", + "type": "t_address" + }, + { + "astId": 8118, + "contract": "contracts/l1/rollup/Rollup.sol:Rollup", + "label": "verifier", + "offset": 0, + "slot": "156", + "type": "t_address" + }, + { + "astId": 8122, + "contract": "contracts/l1/rollup/Rollup.sol:Rollup", + "label": "lastFinalizedBatchIndex", + "offset": 0, + "slot": "157", + "type": "t_uint256" + }, + { + "astId": 8126, + "contract": "contracts/l1/rollup/Rollup.sol:Rollup", + "label": "lastCommittedBatchIndex", + "offset": 0, + "slot": "158", + "type": "t_uint256" + }, + { + "astId": 8131, + "contract": "contracts/l1/rollup/Rollup.sol:Rollup", + "label": "isChallenger", + "offset": 0, + "slot": "159", + "type": "t_mapping(t_address,t_bool)" + }, + { + "astId": 8137, + "contract": "contracts/l1/rollup/Rollup.sol:Rollup", + "label": "finalizedStateRoots", + "offset": 0, + "slot": "160", + "type": "t_mapping(t_uint256,t_bytes32)" + }, + { + "astId": 8143, + "contract": "contracts/l1/rollup/Rollup.sol:Rollup", + "label": "committedBatches", + "offset": 0, + "slot": "161", + "type": "t_mapping(t_uint256,t_bytes32)" + }, + { + "astId": 8149, + "contract": "contracts/l1/rollup/Rollup.sol:Rollup", + "label": "batchDataStore", + "offset": 0, + "slot": "162", + "type": "t_mapping(t_uint256,t_struct(BatchData)6917_storage)" + }, + { + "astId": 8154, + "contract": "contracts/l1/rollup/Rollup.sol:Rollup", + "label": "withdrawalRoots", + "offset": 0, + "slot": "163", + "type": "t_mapping(t_bytes32,t_bool)" + }, + { + "astId": 8160, + "contract": "contracts/l1/rollup/Rollup.sol:Rollup", + "label": "challenges", + "offset": 0, + "slot": "164", + "type": "t_mapping(t_uint256,t_struct(BatchChallenge)6931_storage)" + }, + { + "astId": 8165, + "contract": "contracts/l1/rollup/Rollup.sol:Rollup", + "label": "batchChallengeReward", + "offset": 0, + "slot": "165", + "type": "t_mapping(t_address,t_uint256)" + }, + { + "astId": 8168, + "contract": "contracts/l1/rollup/Rollup.sol:Rollup", + "label": "inChallenge", + "offset": 0, + "slot": "166", + "type": "t_bool" + }, + { + "astId": 8171, + "contract": "contracts/l1/rollup/Rollup.sol:Rollup", + "label": "batchChallenged", + "offset": 0, + "slot": "167", + "type": "t_uint256" + }, + { + "astId": 8174, + "contract": "contracts/l1/rollup/Rollup.sol:Rollup", + "label": "revertReqIndex", + "offset": 0, + "slot": "168", + "type": "t_uint256" + }, + { + "astId": 8177, + "contract": "contracts/l1/rollup/Rollup.sol:Rollup", + "label": "proofRewardPercent", + "offset": 0, + "slot": "169", + "type": "t_uint256" + }, + { + "astId": 8180, + "contract": "contracts/l1/rollup/Rollup.sol:Rollup", + "label": "proveRemaining", + "offset": 0, + "slot": "170", + "type": "t_uint256" + }, + { + "astId": 8185, + "contract": "contracts/l1/rollup/Rollup.sol:Rollup", + "label": "committedStateRoots", + "offset": 0, + "slot": "171", + "type": "t_mapping(t_uint256,t_bytes32)" + }, + { + "astId": 8188, + "contract": "contracts/l1/rollup/Rollup.sol:Rollup", + "label": "rollupDelayPeriod", + "offset": 0, + "slot": "172", + "type": "t_uint256" + }, + { + "astId": 8193, + "contract": "contracts/l1/rollup/Rollup.sol:Rollup", + "label": "batchBlobVersionedHashes", + "offset": 0, + "slot": "173", + "type": "t_mapping(t_uint256,t_bytes32)" + } + ], + "types": { + "t_address": { + "encoding": "inplace", + "label": "address", + "numberOfBytes": "20" + }, + "t_array(t_uint256)49_storage": { + "encoding": "inplace", + "label": "uint256[49]", + "numberOfBytes": "1568", + "base": "t_uint256" + }, + "t_array(t_uint256)50_storage": { + "encoding": "inplace", + "label": "uint256[50]", + "numberOfBytes": "1600", + "base": "t_uint256" + }, + "t_bool": { + "encoding": "inplace", + "label": "bool", + "numberOfBytes": "1" + }, + "t_bytes32": { + "encoding": "inplace", + "label": "bytes32", + "numberOfBytes": "32" + }, + "t_mapping(t_address,t_bool)": { + "encoding": "mapping", + "key": "t_address", + "label": "mapping(address => bool)", + "numberOfBytes": "32", + "value": "t_bool" + }, + "t_mapping(t_address,t_uint256)": { + "encoding": "mapping", + "key": "t_address", + "label": "mapping(address => uint256)", + "numberOfBytes": "32", + "value": "t_uint256" + }, + "t_mapping(t_bytes32,t_bool)": { + "encoding": "mapping", + "key": "t_bytes32", + "label": "mapping(bytes32 => bool)", + "numberOfBytes": "32", + "value": "t_bool" + }, + "t_mapping(t_uint256,t_bytes32)": { + "encoding": "mapping", + "key": "t_uint256", + "label": "mapping(uint256 => bytes32)", + "numberOfBytes": "32", + "value": "t_bytes32" + }, + "t_mapping(t_uint256,t_struct(BatchChallenge)6931_storage)": { + "encoding": "mapping", + "key": "t_uint256", + "label": "mapping(uint256 => struct IRollup.BatchChallenge)", + "numberOfBytes": "32", + "value": "t_struct(BatchChallenge)6931_storage" + }, + "t_mapping(t_uint256,t_struct(BatchData)6917_storage)": { + "encoding": "mapping", + "key": "t_uint256", + "label": "mapping(uint256 => struct IRollup.BatchData)", + "numberOfBytes": "32", + "value": "t_struct(BatchData)6917_storage" + }, + "t_struct(BatchChallenge)6931_storage": { + "encoding": "inplace", + "label": "struct IRollup.BatchChallenge", + "numberOfBytes": "128", + "members": [ + { + "astId": 6920, + "contract": "contracts/l1/rollup/Rollup.sol:Rollup", + "label": "batchIndex", + "offset": 0, + "slot": "0", + "type": "t_uint64" + }, + { + "astId": 6922, + "contract": "contracts/l1/rollup/Rollup.sol:Rollup", + "label": "challenger", + "offset": 8, + "slot": "0", + "type": "t_address" + }, + { + "astId": 6924, + "contract": "contracts/l1/rollup/Rollup.sol:Rollup", + "label": "challengeDeposit", + "offset": 0, + "slot": "1", + "type": "t_uint256" + }, + { + "astId": 6926, + "contract": "contracts/l1/rollup/Rollup.sol:Rollup", + "label": "startTime", + "offset": 0, + "slot": "2", + "type": "t_uint256" + }, + { + "astId": 6928, + "contract": "contracts/l1/rollup/Rollup.sol:Rollup", + "label": "challengeSuccess", + "offset": 0, + "slot": "3", + "type": "t_bool" + }, + { + "astId": 6930, + "contract": "contracts/l1/rollup/Rollup.sol:Rollup", + "label": "finished", + "offset": 1, + "slot": "3", + "type": "t_bool" + } + ] + }, + "t_struct(BatchData)6917_storage": { + "encoding": "inplace", + "label": "struct IRollup.BatchData", + "numberOfBytes": "128", + "members": [ + { + "astId": 6910, + "contract": "contracts/l1/rollup/Rollup.sol:Rollup", + "label": "originTimestamp", + "offset": 0, + "slot": "0", + "type": "t_uint256" + }, + { + "astId": 6912, + "contract": "contracts/l1/rollup/Rollup.sol:Rollup", + "label": "finalizeTimestamp", + "offset": 0, + "slot": "1", + "type": "t_uint256" + }, + { + "astId": 6914, + "contract": "contracts/l1/rollup/Rollup.sol:Rollup", + "label": "blockNumber", + "offset": 0, + "slot": "2", + "type": "t_uint256" + }, + { + "astId": 6916, + "contract": "contracts/l1/rollup/Rollup.sol:Rollup", + "label": "signedSequencersBitmap", + "offset": 0, + "slot": "3", + "type": "t_uint256" + } + ] + }, + "t_uint256": { + "encoding": "inplace", + "label": "uint256", + "numberOfBytes": "32" + }, + "t_uint64": { + "encoding": "inplace", + "label": "uint64", + "numberOfBytes": "8" + }, + "t_uint8": { + "encoding": "inplace", + "label": "uint8", + "numberOfBytes": "1" + } + } +} diff --git a/gas-oracle/app/src/abi/pre_submitter/Rollup.json b/gas-oracle/app/src/abi/pre_submitter/Rollup.json new file mode 100644 index 000000000..c8c7586c1 --- /dev/null +++ b/gas-oracle/app/src/abi/pre_submitter/Rollup.json @@ -0,0 +1,1416 @@ +[ + { + "inputs": [ + { + "internalType": "uint64", + "name": "_chainID", + "type": "uint64" + } + ], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "inputs": [], + "name": "ErrZeroAddress", + "type": "error" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint256", + "name": "batchIndex", + "type": "uint256" + }, + { + "indexed": true, + "internalType": "address", + "name": "winner", + "type": "address" + }, + { + "indexed": true, + "internalType": "string", + "name": "res", + "type": "string" + } + ], + "name": "ChallengeRes", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "receiver", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "ChallengeRewardClaim", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint64", + "name": "batchIndex", + "type": "uint64" + }, + { + "indexed": true, + "internalType": "address", + "name": "challenger", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "challengeDeposit", + "type": "uint256" + } + ], + "name": "ChallengeState", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint256", + "name": "batchIndex", + "type": "uint256" + }, + { + "indexed": true, + "internalType": "bytes32", + "name": "batchHash", + "type": "bytes32" + } + ], + "name": "CommitBatch", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint256", + "name": "batchIndex", + "type": "uint256" + }, + { + "indexed": true, + "internalType": "bytes32", + "name": "batchHash", + "type": "bytes32" + }, + { + "indexed": false, + "internalType": "bytes32", + "name": "stateRoot", + "type": "bytes32" + }, + { + "indexed": false, + "internalType": "bytes32", + "name": "withdrawRoot", + "type": "bytes32" + } + ], + "name": "FinalizeBatch", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint8", + "name": "version", + "type": "uint8" + } + ], + "name": "Initialized", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "previousOwner", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "newOwner", + "type": "address" + } + ], + "name": "OwnershipTransferred", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "account", + "type": "address" + } + ], + "name": "Paused", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "receiver", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "ProveRemainingClaimed", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint256", + "name": "batchIndex", + "type": "uint256" + }, + { + "indexed": true, + "internalType": "bytes32", + "name": "batchHash", + "type": "bytes32" + } + ], + "name": "RevertBatch", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "account", + "type": "address" + } + ], + "name": "Unpaused", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "account", + "type": "address" + }, + { + "indexed": false, + "internalType": "bool", + "name": "status", + "type": "bool" + } + ], + "name": "UpdateChallenger", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint256", + "name": "oldPeriod", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "newPeriod", + "type": "uint256" + } + ], + "name": "UpdateFinalizationPeriodSeconds", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint256", + "name": "oldPercent", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "newPercent", + "type": "uint256" + } + ], + "name": "UpdateProofRewardPercent", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint256", + "name": "oldWindow", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "newWindow", + "type": "uint256" + } + ], + "name": "UpdateProofWindow", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint256", + "name": "oldPeriod", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "newPeriod", + "type": "uint256" + } + ], + "name": "UpdateRollupDelayPeriod", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "oldVerifier", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "newVerifier", + "type": "address" + } + ], + "name": "UpdateVerifier", + "type": "event" + }, + { + "inputs": [], + "name": "LAYER_2_CHAIN_ID", + "outputs": [ + { + "internalType": "uint64", + "name": "", + "type": "uint64" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "__maxNumTxInChunk", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_account", + "type": "address" + } + ], + "name": "addChallenger", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "batchIndex", + "type": "uint256" + } + ], + "name": "batchBlobVersionedHashes", + "outputs": [ + { + "internalType": "bytes32", + "name": "blobVersionedHash", + "type": "bytes32" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "owner", + "type": "address" + } + ], + "name": "batchChallengeReward", + "outputs": [ + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "batchChallenged", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "batchIndex", + "type": "uint256" + } + ], + "name": "batchChallengedSuccess", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "batchIndex", + "type": "uint256" + } + ], + "name": "batchDataStore", + "outputs": [ + { + "internalType": "uint256", + "name": "originTimestamp", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "finalizeTimestamp", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "blockNumber", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "signedSequencersBitmap", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "batchIndex", + "type": "uint256" + } + ], + "name": "batchExist", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "batchIndex", + "type": "uint256" + } + ], + "name": "batchInChallenge", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "batchIndex", + "type": "uint256" + } + ], + "name": "batchInsideChallengeWindow", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint64", + "name": "batchIndex", + "type": "uint64" + }, + { + "internalType": "bytes32", + "name": "_batchHash", + "type": "bytes32" + } + ], + "name": "challengeState", + "outputs": [], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "batchIndex", + "type": "uint256" + } + ], + "name": "challenges", + "outputs": [ + { + "internalType": "uint64", + "name": "batchIndex", + "type": "uint64" + }, + { + "internalType": "address", + "name": "challenger", + "type": "address" + }, + { + "internalType": "uint256", + "name": "challengeDeposit", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "startTime", + "type": "uint256" + }, + { + "internalType": "bool", + "name": "challengeSuccess", + "type": "bool" + }, + { + "internalType": "bool", + "name": "finished", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "receiver", + "type": "address" + } + ], + "name": "claimProveRemaining", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "receiver", + "type": "address" + } + ], + "name": "claimReward", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "components": [ + { + "internalType": "uint8", + "name": "version", + "type": "uint8" + }, + { + "internalType": "bytes", + "name": "parentBatchHeader", + "type": "bytes" + }, + { + "internalType": "uint64", + "name": "lastBlockNumber", + "type": "uint64" + }, + { + "internalType": "uint16", + "name": "numL1Messages", + "type": "uint16" + }, + { + "internalType": "bytes32", + "name": "prevStateRoot", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "postStateRoot", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "withdrawalRoot", + "type": "bytes32" + } + ], + "internalType": "struct IRollup.BatchDataInput", + "name": "batchDataInput", + "type": "tuple" + }, + { + "components": [ + { + "internalType": "uint256", + "name": "signedSequencersBitmap", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "sequencerSets", + "type": "bytes" + }, + { + "internalType": "bytes", + "name": "signature", + "type": "bytes" + } + ], + "internalType": "struct IRollup.BatchSignatureInput", + "name": "batchSignatureInput", + "type": "tuple" + } + ], + "name": "commitBatch", + "outputs": [], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [ + { + "components": [ + { + "internalType": "uint8", + "name": "version", + "type": "uint8" + }, + { + "internalType": "bytes", + "name": "parentBatchHeader", + "type": "bytes" + }, + { + "internalType": "uint64", + "name": "lastBlockNumber", + "type": "uint64" + }, + { + "internalType": "uint16", + "name": "numL1Messages", + "type": "uint16" + }, + { + "internalType": "bytes32", + "name": "prevStateRoot", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "postStateRoot", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "withdrawalRoot", + "type": "bytes32" + } + ], + "internalType": "struct IRollup.BatchDataInput", + "name": "batchDataInput", + "type": "tuple" + }, + { + "components": [ + { + "internalType": "uint256", + "name": "signedSequencersBitmap", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "sequencerSets", + "type": "bytes" + }, + { + "internalType": "bytes", + "name": "signature", + "type": "bytes" + } + ], + "internalType": "struct IRollup.BatchSignatureInput", + "name": "batchSignatureInput", + "type": "tuple" + }, + { + "internalType": "bytes", + "name": "_batchHeader", + "type": "bytes" + }, + { + "internalType": "bytes", + "name": "_batchProof", + "type": "bytes" + } + ], + "name": "commitBatchWithProof", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "components": [ + { + "internalType": "uint8", + "name": "version", + "type": "uint8" + }, + { + "internalType": "bytes", + "name": "parentBatchHeader", + "type": "bytes" + }, + { + "internalType": "uint64", + "name": "lastBlockNumber", + "type": "uint64" + }, + { + "internalType": "uint16", + "name": "numL1Messages", + "type": "uint16" + }, + { + "internalType": "bytes32", + "name": "prevStateRoot", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "postStateRoot", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "withdrawalRoot", + "type": "bytes32" + } + ], + "internalType": "struct IRollup.BatchDataInput", + "name": "batchDataInput", + "type": "tuple" + }, + { + "components": [ + { + "internalType": "uint256", + "name": "signedSequencersBitmap", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "sequencerSets", + "type": "bytes" + }, + { + "internalType": "bytes", + "name": "signature", + "type": "bytes" + } + ], + "internalType": "struct IRollup.BatchSignatureInput", + "name": "batchSignatureInput", + "type": "tuple" + } + ], + "name": "commitState", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "batchIndex", + "type": "uint256" + } + ], + "name": "committedBatches", + "outputs": [ + { + "internalType": "bytes32", + "name": "batchHash", + "type": "bytes32" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "batchIndex", + "type": "uint256" + } + ], + "name": "committedStateRoots", + "outputs": [ + { + "internalType": "bytes32", + "name": "stateRoot", + "type": "bytes32" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "finalizationPeriodSeconds", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes", + "name": "_batchHeader", + "type": "bytes" + } + ], + "name": "finalizeBatch", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "batchIndex", + "type": "uint256" + } + ], + "name": "finalizedStateRoots", + "outputs": [ + { + "internalType": "bytes32", + "name": "stateRoot", + "type": "bytes32" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes", + "name": "_batchHeader", + "type": "bytes" + } + ], + "name": "importGenesisBatch", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "inChallenge", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_l1StakingContract", + "type": "address" + }, + { + "internalType": "address", + "name": "_messageQueue", + "type": "address" + }, + { + "internalType": "address", + "name": "_verifier", + "type": "address" + }, + { + "internalType": "uint256", + "name": "_finalizationPeriodSeconds", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "_proofWindow", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "_proofRewardPercent", + "type": "uint256" + } + ], + "name": "initialize", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "_prevStateRoot", + "type": "bytes32" + } + ], + "name": "initialize2", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "_rollupDelayPeriod", + "type": "uint256" + } + ], + "name": "initialize3", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "_batchIndex", + "type": "uint256" + } + ], + "name": "isBatchFinalized", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "challengerAddress", + "type": "address" + } + ], + "name": "isChallenger", + "outputs": [ + { + "internalType": "bool", + "name": "isChallenger", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "l1StakingContract", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "lastCommittedBatchIndex", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "lastFinalizedBatchIndex", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "messageQueue", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "owner", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "paused", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "proofRewardPercent", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "proofWindow", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes", + "name": "_batchHeader", + "type": "bytes" + }, + { + "internalType": "bytes", + "name": "_batchProof", + "type": "bytes" + } + ], + "name": "proveCommittedBatchState", + "outputs": [], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "proveRemaining", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes", + "name": "_batchHeader", + "type": "bytes" + }, + { + "internalType": "bytes", + "name": "_batchProof", + "type": "bytes" + } + ], + "name": "proveState", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_account", + "type": "address" + } + ], + "name": "removeChallenger", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "renounceOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes", + "name": "_batchHeader", + "type": "bytes" + }, + { + "internalType": "uint256", + "name": "_count", + "type": "uint256" + } + ], + "name": "revertBatch", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "revertReqIndex", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "rollupDelayPeriod", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bool", + "name": "_status", + "type": "bool" + } + ], + "name": "setPause", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "newOwner", + "type": "address" + } + ], + "name": "transferOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "_newPeriod", + "type": "uint256" + } + ], + "name": "updateFinalizePeriodSeconds", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "_newWindow", + "type": "uint256" + } + ], + "name": "updateProofWindow", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "_newProofRewardPercent", + "type": "uint256" + } + ], + "name": "updateRewardPercentage", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "_newPeriod", + "type": "uint256" + } + ], + "name": "updateRollupDelayPeriod", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_newVerifier", + "type": "address" + } + ], + "name": "updateVerifier", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "verifier", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "withdrawalRoot", + "type": "bytes32" + } + ], + "name": "withdrawalRoots", + "outputs": [ + { + "internalType": "bool", + "name": "exist", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "stateMutability": "payable", + "type": "receive" + } +] diff --git a/node/types/abi/pre_submitter_rollup.json b/node/types/abi/pre_submitter_rollup.json new file mode 100644 index 000000000..c8c7586c1 --- /dev/null +++ b/node/types/abi/pre_submitter_rollup.json @@ -0,0 +1,1416 @@ +[ + { + "inputs": [ + { + "internalType": "uint64", + "name": "_chainID", + "type": "uint64" + } + ], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "inputs": [], + "name": "ErrZeroAddress", + "type": "error" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint256", + "name": "batchIndex", + "type": "uint256" + }, + { + "indexed": true, + "internalType": "address", + "name": "winner", + "type": "address" + }, + { + "indexed": true, + "internalType": "string", + "name": "res", + "type": "string" + } + ], + "name": "ChallengeRes", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "receiver", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "ChallengeRewardClaim", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint64", + "name": "batchIndex", + "type": "uint64" + }, + { + "indexed": true, + "internalType": "address", + "name": "challenger", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "challengeDeposit", + "type": "uint256" + } + ], + "name": "ChallengeState", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint256", + "name": "batchIndex", + "type": "uint256" + }, + { + "indexed": true, + "internalType": "bytes32", + "name": "batchHash", + "type": "bytes32" + } + ], + "name": "CommitBatch", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint256", + "name": "batchIndex", + "type": "uint256" + }, + { + "indexed": true, + "internalType": "bytes32", + "name": "batchHash", + "type": "bytes32" + }, + { + "indexed": false, + "internalType": "bytes32", + "name": "stateRoot", + "type": "bytes32" + }, + { + "indexed": false, + "internalType": "bytes32", + "name": "withdrawRoot", + "type": "bytes32" + } + ], + "name": "FinalizeBatch", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint8", + "name": "version", + "type": "uint8" + } + ], + "name": "Initialized", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "previousOwner", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "newOwner", + "type": "address" + } + ], + "name": "OwnershipTransferred", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "account", + "type": "address" + } + ], + "name": "Paused", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "receiver", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "ProveRemainingClaimed", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint256", + "name": "batchIndex", + "type": "uint256" + }, + { + "indexed": true, + "internalType": "bytes32", + "name": "batchHash", + "type": "bytes32" + } + ], + "name": "RevertBatch", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "account", + "type": "address" + } + ], + "name": "Unpaused", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "account", + "type": "address" + }, + { + "indexed": false, + "internalType": "bool", + "name": "status", + "type": "bool" + } + ], + "name": "UpdateChallenger", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint256", + "name": "oldPeriod", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "newPeriod", + "type": "uint256" + } + ], + "name": "UpdateFinalizationPeriodSeconds", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint256", + "name": "oldPercent", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "newPercent", + "type": "uint256" + } + ], + "name": "UpdateProofRewardPercent", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint256", + "name": "oldWindow", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "newWindow", + "type": "uint256" + } + ], + "name": "UpdateProofWindow", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint256", + "name": "oldPeriod", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "newPeriod", + "type": "uint256" + } + ], + "name": "UpdateRollupDelayPeriod", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "oldVerifier", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "newVerifier", + "type": "address" + } + ], + "name": "UpdateVerifier", + "type": "event" + }, + { + "inputs": [], + "name": "LAYER_2_CHAIN_ID", + "outputs": [ + { + "internalType": "uint64", + "name": "", + "type": "uint64" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "__maxNumTxInChunk", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_account", + "type": "address" + } + ], + "name": "addChallenger", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "batchIndex", + "type": "uint256" + } + ], + "name": "batchBlobVersionedHashes", + "outputs": [ + { + "internalType": "bytes32", + "name": "blobVersionedHash", + "type": "bytes32" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "owner", + "type": "address" + } + ], + "name": "batchChallengeReward", + "outputs": [ + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "batchChallenged", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "batchIndex", + "type": "uint256" + } + ], + "name": "batchChallengedSuccess", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "batchIndex", + "type": "uint256" + } + ], + "name": "batchDataStore", + "outputs": [ + { + "internalType": "uint256", + "name": "originTimestamp", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "finalizeTimestamp", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "blockNumber", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "signedSequencersBitmap", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "batchIndex", + "type": "uint256" + } + ], + "name": "batchExist", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "batchIndex", + "type": "uint256" + } + ], + "name": "batchInChallenge", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "batchIndex", + "type": "uint256" + } + ], + "name": "batchInsideChallengeWindow", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint64", + "name": "batchIndex", + "type": "uint64" + }, + { + "internalType": "bytes32", + "name": "_batchHash", + "type": "bytes32" + } + ], + "name": "challengeState", + "outputs": [], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "batchIndex", + "type": "uint256" + } + ], + "name": "challenges", + "outputs": [ + { + "internalType": "uint64", + "name": "batchIndex", + "type": "uint64" + }, + { + "internalType": "address", + "name": "challenger", + "type": "address" + }, + { + "internalType": "uint256", + "name": "challengeDeposit", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "startTime", + "type": "uint256" + }, + { + "internalType": "bool", + "name": "challengeSuccess", + "type": "bool" + }, + { + "internalType": "bool", + "name": "finished", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "receiver", + "type": "address" + } + ], + "name": "claimProveRemaining", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "receiver", + "type": "address" + } + ], + "name": "claimReward", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "components": [ + { + "internalType": "uint8", + "name": "version", + "type": "uint8" + }, + { + "internalType": "bytes", + "name": "parentBatchHeader", + "type": "bytes" + }, + { + "internalType": "uint64", + "name": "lastBlockNumber", + "type": "uint64" + }, + { + "internalType": "uint16", + "name": "numL1Messages", + "type": "uint16" + }, + { + "internalType": "bytes32", + "name": "prevStateRoot", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "postStateRoot", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "withdrawalRoot", + "type": "bytes32" + } + ], + "internalType": "struct IRollup.BatchDataInput", + "name": "batchDataInput", + "type": "tuple" + }, + { + "components": [ + { + "internalType": "uint256", + "name": "signedSequencersBitmap", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "sequencerSets", + "type": "bytes" + }, + { + "internalType": "bytes", + "name": "signature", + "type": "bytes" + } + ], + "internalType": "struct IRollup.BatchSignatureInput", + "name": "batchSignatureInput", + "type": "tuple" + } + ], + "name": "commitBatch", + "outputs": [], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [ + { + "components": [ + { + "internalType": "uint8", + "name": "version", + "type": "uint8" + }, + { + "internalType": "bytes", + "name": "parentBatchHeader", + "type": "bytes" + }, + { + "internalType": "uint64", + "name": "lastBlockNumber", + "type": "uint64" + }, + { + "internalType": "uint16", + "name": "numL1Messages", + "type": "uint16" + }, + { + "internalType": "bytes32", + "name": "prevStateRoot", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "postStateRoot", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "withdrawalRoot", + "type": "bytes32" + } + ], + "internalType": "struct IRollup.BatchDataInput", + "name": "batchDataInput", + "type": "tuple" + }, + { + "components": [ + { + "internalType": "uint256", + "name": "signedSequencersBitmap", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "sequencerSets", + "type": "bytes" + }, + { + "internalType": "bytes", + "name": "signature", + "type": "bytes" + } + ], + "internalType": "struct IRollup.BatchSignatureInput", + "name": "batchSignatureInput", + "type": "tuple" + }, + { + "internalType": "bytes", + "name": "_batchHeader", + "type": "bytes" + }, + { + "internalType": "bytes", + "name": "_batchProof", + "type": "bytes" + } + ], + "name": "commitBatchWithProof", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "components": [ + { + "internalType": "uint8", + "name": "version", + "type": "uint8" + }, + { + "internalType": "bytes", + "name": "parentBatchHeader", + "type": "bytes" + }, + { + "internalType": "uint64", + "name": "lastBlockNumber", + "type": "uint64" + }, + { + "internalType": "uint16", + "name": "numL1Messages", + "type": "uint16" + }, + { + "internalType": "bytes32", + "name": "prevStateRoot", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "postStateRoot", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "withdrawalRoot", + "type": "bytes32" + } + ], + "internalType": "struct IRollup.BatchDataInput", + "name": "batchDataInput", + "type": "tuple" + }, + { + "components": [ + { + "internalType": "uint256", + "name": "signedSequencersBitmap", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "sequencerSets", + "type": "bytes" + }, + { + "internalType": "bytes", + "name": "signature", + "type": "bytes" + } + ], + "internalType": "struct IRollup.BatchSignatureInput", + "name": "batchSignatureInput", + "type": "tuple" + } + ], + "name": "commitState", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "batchIndex", + "type": "uint256" + } + ], + "name": "committedBatches", + "outputs": [ + { + "internalType": "bytes32", + "name": "batchHash", + "type": "bytes32" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "batchIndex", + "type": "uint256" + } + ], + "name": "committedStateRoots", + "outputs": [ + { + "internalType": "bytes32", + "name": "stateRoot", + "type": "bytes32" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "finalizationPeriodSeconds", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes", + "name": "_batchHeader", + "type": "bytes" + } + ], + "name": "finalizeBatch", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "batchIndex", + "type": "uint256" + } + ], + "name": "finalizedStateRoots", + "outputs": [ + { + "internalType": "bytes32", + "name": "stateRoot", + "type": "bytes32" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes", + "name": "_batchHeader", + "type": "bytes" + } + ], + "name": "importGenesisBatch", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "inChallenge", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_l1StakingContract", + "type": "address" + }, + { + "internalType": "address", + "name": "_messageQueue", + "type": "address" + }, + { + "internalType": "address", + "name": "_verifier", + "type": "address" + }, + { + "internalType": "uint256", + "name": "_finalizationPeriodSeconds", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "_proofWindow", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "_proofRewardPercent", + "type": "uint256" + } + ], + "name": "initialize", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "_prevStateRoot", + "type": "bytes32" + } + ], + "name": "initialize2", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "_rollupDelayPeriod", + "type": "uint256" + } + ], + "name": "initialize3", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "_batchIndex", + "type": "uint256" + } + ], + "name": "isBatchFinalized", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "challengerAddress", + "type": "address" + } + ], + "name": "isChallenger", + "outputs": [ + { + "internalType": "bool", + "name": "isChallenger", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "l1StakingContract", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "lastCommittedBatchIndex", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "lastFinalizedBatchIndex", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "messageQueue", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "owner", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "paused", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "proofRewardPercent", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "proofWindow", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes", + "name": "_batchHeader", + "type": "bytes" + }, + { + "internalType": "bytes", + "name": "_batchProof", + "type": "bytes" + } + ], + "name": "proveCommittedBatchState", + "outputs": [], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "proveRemaining", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes", + "name": "_batchHeader", + "type": "bytes" + }, + { + "internalType": "bytes", + "name": "_batchProof", + "type": "bytes" + } + ], + "name": "proveState", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_account", + "type": "address" + } + ], + "name": "removeChallenger", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "renounceOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes", + "name": "_batchHeader", + "type": "bytes" + }, + { + "internalType": "uint256", + "name": "_count", + "type": "uint256" + } + ], + "name": "revertBatch", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "revertReqIndex", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "rollupDelayPeriod", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bool", + "name": "_status", + "type": "bool" + } + ], + "name": "setPause", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "newOwner", + "type": "address" + } + ], + "name": "transferOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "_newPeriod", + "type": "uint256" + } + ], + "name": "updateFinalizePeriodSeconds", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "_newWindow", + "type": "uint256" + } + ], + "name": "updateProofWindow", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "_newProofRewardPercent", + "type": "uint256" + } + ], + "name": "updateRewardPercentage", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "_newPeriod", + "type": "uint256" + } + ], + "name": "updateRollupDelayPeriod", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_newVerifier", + "type": "address" + } + ], + "name": "updateVerifier", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "verifier", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "withdrawalRoot", + "type": "bytes32" + } + ], + "name": "withdrawalRoots", + "outputs": [ + { + "internalType": "bool", + "name": "exist", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "stateMutability": "payable", + "type": "receive" + } +] diff --git a/prover/bin/challenge/src/abi/pre_submitter/Rollup.json b/prover/bin/challenge/src/abi/pre_submitter/Rollup.json new file mode 100644 index 000000000..c8c7586c1 --- /dev/null +++ b/prover/bin/challenge/src/abi/pre_submitter/Rollup.json @@ -0,0 +1,1416 @@ +[ + { + "inputs": [ + { + "internalType": "uint64", + "name": "_chainID", + "type": "uint64" + } + ], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "inputs": [], + "name": "ErrZeroAddress", + "type": "error" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint256", + "name": "batchIndex", + "type": "uint256" + }, + { + "indexed": true, + "internalType": "address", + "name": "winner", + "type": "address" + }, + { + "indexed": true, + "internalType": "string", + "name": "res", + "type": "string" + } + ], + "name": "ChallengeRes", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "receiver", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "ChallengeRewardClaim", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint64", + "name": "batchIndex", + "type": "uint64" + }, + { + "indexed": true, + "internalType": "address", + "name": "challenger", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "challengeDeposit", + "type": "uint256" + } + ], + "name": "ChallengeState", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint256", + "name": "batchIndex", + "type": "uint256" + }, + { + "indexed": true, + "internalType": "bytes32", + "name": "batchHash", + "type": "bytes32" + } + ], + "name": "CommitBatch", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint256", + "name": "batchIndex", + "type": "uint256" + }, + { + "indexed": true, + "internalType": "bytes32", + "name": "batchHash", + "type": "bytes32" + }, + { + "indexed": false, + "internalType": "bytes32", + "name": "stateRoot", + "type": "bytes32" + }, + { + "indexed": false, + "internalType": "bytes32", + "name": "withdrawRoot", + "type": "bytes32" + } + ], + "name": "FinalizeBatch", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint8", + "name": "version", + "type": "uint8" + } + ], + "name": "Initialized", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "previousOwner", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "newOwner", + "type": "address" + } + ], + "name": "OwnershipTransferred", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "account", + "type": "address" + } + ], + "name": "Paused", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "receiver", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "ProveRemainingClaimed", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint256", + "name": "batchIndex", + "type": "uint256" + }, + { + "indexed": true, + "internalType": "bytes32", + "name": "batchHash", + "type": "bytes32" + } + ], + "name": "RevertBatch", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "account", + "type": "address" + } + ], + "name": "Unpaused", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "account", + "type": "address" + }, + { + "indexed": false, + "internalType": "bool", + "name": "status", + "type": "bool" + } + ], + "name": "UpdateChallenger", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint256", + "name": "oldPeriod", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "newPeriod", + "type": "uint256" + } + ], + "name": "UpdateFinalizationPeriodSeconds", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint256", + "name": "oldPercent", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "newPercent", + "type": "uint256" + } + ], + "name": "UpdateProofRewardPercent", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint256", + "name": "oldWindow", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "newWindow", + "type": "uint256" + } + ], + "name": "UpdateProofWindow", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint256", + "name": "oldPeriod", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "newPeriod", + "type": "uint256" + } + ], + "name": "UpdateRollupDelayPeriod", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "oldVerifier", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "newVerifier", + "type": "address" + } + ], + "name": "UpdateVerifier", + "type": "event" + }, + { + "inputs": [], + "name": "LAYER_2_CHAIN_ID", + "outputs": [ + { + "internalType": "uint64", + "name": "", + "type": "uint64" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "__maxNumTxInChunk", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_account", + "type": "address" + } + ], + "name": "addChallenger", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "batchIndex", + "type": "uint256" + } + ], + "name": "batchBlobVersionedHashes", + "outputs": [ + { + "internalType": "bytes32", + "name": "blobVersionedHash", + "type": "bytes32" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "owner", + "type": "address" + } + ], + "name": "batchChallengeReward", + "outputs": [ + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "batchChallenged", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "batchIndex", + "type": "uint256" + } + ], + "name": "batchChallengedSuccess", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "batchIndex", + "type": "uint256" + } + ], + "name": "batchDataStore", + "outputs": [ + { + "internalType": "uint256", + "name": "originTimestamp", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "finalizeTimestamp", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "blockNumber", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "signedSequencersBitmap", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "batchIndex", + "type": "uint256" + } + ], + "name": "batchExist", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "batchIndex", + "type": "uint256" + } + ], + "name": "batchInChallenge", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "batchIndex", + "type": "uint256" + } + ], + "name": "batchInsideChallengeWindow", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint64", + "name": "batchIndex", + "type": "uint64" + }, + { + "internalType": "bytes32", + "name": "_batchHash", + "type": "bytes32" + } + ], + "name": "challengeState", + "outputs": [], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "batchIndex", + "type": "uint256" + } + ], + "name": "challenges", + "outputs": [ + { + "internalType": "uint64", + "name": "batchIndex", + "type": "uint64" + }, + { + "internalType": "address", + "name": "challenger", + "type": "address" + }, + { + "internalType": "uint256", + "name": "challengeDeposit", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "startTime", + "type": "uint256" + }, + { + "internalType": "bool", + "name": "challengeSuccess", + "type": "bool" + }, + { + "internalType": "bool", + "name": "finished", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "receiver", + "type": "address" + } + ], + "name": "claimProveRemaining", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "receiver", + "type": "address" + } + ], + "name": "claimReward", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "components": [ + { + "internalType": "uint8", + "name": "version", + "type": "uint8" + }, + { + "internalType": "bytes", + "name": "parentBatchHeader", + "type": "bytes" + }, + { + "internalType": "uint64", + "name": "lastBlockNumber", + "type": "uint64" + }, + { + "internalType": "uint16", + "name": "numL1Messages", + "type": "uint16" + }, + { + "internalType": "bytes32", + "name": "prevStateRoot", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "postStateRoot", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "withdrawalRoot", + "type": "bytes32" + } + ], + "internalType": "struct IRollup.BatchDataInput", + "name": "batchDataInput", + "type": "tuple" + }, + { + "components": [ + { + "internalType": "uint256", + "name": "signedSequencersBitmap", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "sequencerSets", + "type": "bytes" + }, + { + "internalType": "bytes", + "name": "signature", + "type": "bytes" + } + ], + "internalType": "struct IRollup.BatchSignatureInput", + "name": "batchSignatureInput", + "type": "tuple" + } + ], + "name": "commitBatch", + "outputs": [], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [ + { + "components": [ + { + "internalType": "uint8", + "name": "version", + "type": "uint8" + }, + { + "internalType": "bytes", + "name": "parentBatchHeader", + "type": "bytes" + }, + { + "internalType": "uint64", + "name": "lastBlockNumber", + "type": "uint64" + }, + { + "internalType": "uint16", + "name": "numL1Messages", + "type": "uint16" + }, + { + "internalType": "bytes32", + "name": "prevStateRoot", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "postStateRoot", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "withdrawalRoot", + "type": "bytes32" + } + ], + "internalType": "struct IRollup.BatchDataInput", + "name": "batchDataInput", + "type": "tuple" + }, + { + "components": [ + { + "internalType": "uint256", + "name": "signedSequencersBitmap", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "sequencerSets", + "type": "bytes" + }, + { + "internalType": "bytes", + "name": "signature", + "type": "bytes" + } + ], + "internalType": "struct IRollup.BatchSignatureInput", + "name": "batchSignatureInput", + "type": "tuple" + }, + { + "internalType": "bytes", + "name": "_batchHeader", + "type": "bytes" + }, + { + "internalType": "bytes", + "name": "_batchProof", + "type": "bytes" + } + ], + "name": "commitBatchWithProof", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "components": [ + { + "internalType": "uint8", + "name": "version", + "type": "uint8" + }, + { + "internalType": "bytes", + "name": "parentBatchHeader", + "type": "bytes" + }, + { + "internalType": "uint64", + "name": "lastBlockNumber", + "type": "uint64" + }, + { + "internalType": "uint16", + "name": "numL1Messages", + "type": "uint16" + }, + { + "internalType": "bytes32", + "name": "prevStateRoot", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "postStateRoot", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "withdrawalRoot", + "type": "bytes32" + } + ], + "internalType": "struct IRollup.BatchDataInput", + "name": "batchDataInput", + "type": "tuple" + }, + { + "components": [ + { + "internalType": "uint256", + "name": "signedSequencersBitmap", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "sequencerSets", + "type": "bytes" + }, + { + "internalType": "bytes", + "name": "signature", + "type": "bytes" + } + ], + "internalType": "struct IRollup.BatchSignatureInput", + "name": "batchSignatureInput", + "type": "tuple" + } + ], + "name": "commitState", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "batchIndex", + "type": "uint256" + } + ], + "name": "committedBatches", + "outputs": [ + { + "internalType": "bytes32", + "name": "batchHash", + "type": "bytes32" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "batchIndex", + "type": "uint256" + } + ], + "name": "committedStateRoots", + "outputs": [ + { + "internalType": "bytes32", + "name": "stateRoot", + "type": "bytes32" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "finalizationPeriodSeconds", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes", + "name": "_batchHeader", + "type": "bytes" + } + ], + "name": "finalizeBatch", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "batchIndex", + "type": "uint256" + } + ], + "name": "finalizedStateRoots", + "outputs": [ + { + "internalType": "bytes32", + "name": "stateRoot", + "type": "bytes32" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes", + "name": "_batchHeader", + "type": "bytes" + } + ], + "name": "importGenesisBatch", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "inChallenge", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_l1StakingContract", + "type": "address" + }, + { + "internalType": "address", + "name": "_messageQueue", + "type": "address" + }, + { + "internalType": "address", + "name": "_verifier", + "type": "address" + }, + { + "internalType": "uint256", + "name": "_finalizationPeriodSeconds", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "_proofWindow", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "_proofRewardPercent", + "type": "uint256" + } + ], + "name": "initialize", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "_prevStateRoot", + "type": "bytes32" + } + ], + "name": "initialize2", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "_rollupDelayPeriod", + "type": "uint256" + } + ], + "name": "initialize3", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "_batchIndex", + "type": "uint256" + } + ], + "name": "isBatchFinalized", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "challengerAddress", + "type": "address" + } + ], + "name": "isChallenger", + "outputs": [ + { + "internalType": "bool", + "name": "isChallenger", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "l1StakingContract", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "lastCommittedBatchIndex", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "lastFinalizedBatchIndex", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "messageQueue", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "owner", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "paused", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "proofRewardPercent", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "proofWindow", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes", + "name": "_batchHeader", + "type": "bytes" + }, + { + "internalType": "bytes", + "name": "_batchProof", + "type": "bytes" + } + ], + "name": "proveCommittedBatchState", + "outputs": [], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "proveRemaining", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes", + "name": "_batchHeader", + "type": "bytes" + }, + { + "internalType": "bytes", + "name": "_batchProof", + "type": "bytes" + } + ], + "name": "proveState", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_account", + "type": "address" + } + ], + "name": "removeChallenger", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "renounceOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes", + "name": "_batchHeader", + "type": "bytes" + }, + { + "internalType": "uint256", + "name": "_count", + "type": "uint256" + } + ], + "name": "revertBatch", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "revertReqIndex", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "rollupDelayPeriod", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bool", + "name": "_status", + "type": "bool" + } + ], + "name": "setPause", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "newOwner", + "type": "address" + } + ], + "name": "transferOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "_newPeriod", + "type": "uint256" + } + ], + "name": "updateFinalizePeriodSeconds", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "_newWindow", + "type": "uint256" + } + ], + "name": "updateProofWindow", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "_newProofRewardPercent", + "type": "uint256" + } + ], + "name": "updateRewardPercentage", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "_newPeriod", + "type": "uint256" + } + ], + "name": "updateRollupDelayPeriod", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_newVerifier", + "type": "address" + } + ], + "name": "updateVerifier", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "verifier", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "withdrawalRoot", + "type": "bytes32" + } + ], + "name": "withdrawalRoots", + "outputs": [ + { + "internalType": "bool", + "name": "exist", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "stateMutability": "payable", + "type": "receive" + } +] diff --git a/prover/bin/shadow-prove/abi/pre_submitter/Rollup.json b/prover/bin/shadow-prove/abi/pre_submitter/Rollup.json new file mode 100644 index 000000000..c8c7586c1 --- /dev/null +++ b/prover/bin/shadow-prove/abi/pre_submitter/Rollup.json @@ -0,0 +1,1416 @@ +[ + { + "inputs": [ + { + "internalType": "uint64", + "name": "_chainID", + "type": "uint64" + } + ], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "inputs": [], + "name": "ErrZeroAddress", + "type": "error" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint256", + "name": "batchIndex", + "type": "uint256" + }, + { + "indexed": true, + "internalType": "address", + "name": "winner", + "type": "address" + }, + { + "indexed": true, + "internalType": "string", + "name": "res", + "type": "string" + } + ], + "name": "ChallengeRes", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "receiver", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "ChallengeRewardClaim", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint64", + "name": "batchIndex", + "type": "uint64" + }, + { + "indexed": true, + "internalType": "address", + "name": "challenger", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "challengeDeposit", + "type": "uint256" + } + ], + "name": "ChallengeState", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint256", + "name": "batchIndex", + "type": "uint256" + }, + { + "indexed": true, + "internalType": "bytes32", + "name": "batchHash", + "type": "bytes32" + } + ], + "name": "CommitBatch", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint256", + "name": "batchIndex", + "type": "uint256" + }, + { + "indexed": true, + "internalType": "bytes32", + "name": "batchHash", + "type": "bytes32" + }, + { + "indexed": false, + "internalType": "bytes32", + "name": "stateRoot", + "type": "bytes32" + }, + { + "indexed": false, + "internalType": "bytes32", + "name": "withdrawRoot", + "type": "bytes32" + } + ], + "name": "FinalizeBatch", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint8", + "name": "version", + "type": "uint8" + } + ], + "name": "Initialized", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "previousOwner", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "newOwner", + "type": "address" + } + ], + "name": "OwnershipTransferred", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "account", + "type": "address" + } + ], + "name": "Paused", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "receiver", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "ProveRemainingClaimed", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint256", + "name": "batchIndex", + "type": "uint256" + }, + { + "indexed": true, + "internalType": "bytes32", + "name": "batchHash", + "type": "bytes32" + } + ], + "name": "RevertBatch", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "account", + "type": "address" + } + ], + "name": "Unpaused", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "account", + "type": "address" + }, + { + "indexed": false, + "internalType": "bool", + "name": "status", + "type": "bool" + } + ], + "name": "UpdateChallenger", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint256", + "name": "oldPeriod", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "newPeriod", + "type": "uint256" + } + ], + "name": "UpdateFinalizationPeriodSeconds", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint256", + "name": "oldPercent", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "newPercent", + "type": "uint256" + } + ], + "name": "UpdateProofRewardPercent", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint256", + "name": "oldWindow", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "newWindow", + "type": "uint256" + } + ], + "name": "UpdateProofWindow", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint256", + "name": "oldPeriod", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "newPeriod", + "type": "uint256" + } + ], + "name": "UpdateRollupDelayPeriod", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "oldVerifier", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "newVerifier", + "type": "address" + } + ], + "name": "UpdateVerifier", + "type": "event" + }, + { + "inputs": [], + "name": "LAYER_2_CHAIN_ID", + "outputs": [ + { + "internalType": "uint64", + "name": "", + "type": "uint64" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "__maxNumTxInChunk", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_account", + "type": "address" + } + ], + "name": "addChallenger", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "batchIndex", + "type": "uint256" + } + ], + "name": "batchBlobVersionedHashes", + "outputs": [ + { + "internalType": "bytes32", + "name": "blobVersionedHash", + "type": "bytes32" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "owner", + "type": "address" + } + ], + "name": "batchChallengeReward", + "outputs": [ + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "batchChallenged", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "batchIndex", + "type": "uint256" + } + ], + "name": "batchChallengedSuccess", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "batchIndex", + "type": "uint256" + } + ], + "name": "batchDataStore", + "outputs": [ + { + "internalType": "uint256", + "name": "originTimestamp", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "finalizeTimestamp", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "blockNumber", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "signedSequencersBitmap", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "batchIndex", + "type": "uint256" + } + ], + "name": "batchExist", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "batchIndex", + "type": "uint256" + } + ], + "name": "batchInChallenge", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "batchIndex", + "type": "uint256" + } + ], + "name": "batchInsideChallengeWindow", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint64", + "name": "batchIndex", + "type": "uint64" + }, + { + "internalType": "bytes32", + "name": "_batchHash", + "type": "bytes32" + } + ], + "name": "challengeState", + "outputs": [], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "batchIndex", + "type": "uint256" + } + ], + "name": "challenges", + "outputs": [ + { + "internalType": "uint64", + "name": "batchIndex", + "type": "uint64" + }, + { + "internalType": "address", + "name": "challenger", + "type": "address" + }, + { + "internalType": "uint256", + "name": "challengeDeposit", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "startTime", + "type": "uint256" + }, + { + "internalType": "bool", + "name": "challengeSuccess", + "type": "bool" + }, + { + "internalType": "bool", + "name": "finished", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "receiver", + "type": "address" + } + ], + "name": "claimProveRemaining", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "receiver", + "type": "address" + } + ], + "name": "claimReward", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "components": [ + { + "internalType": "uint8", + "name": "version", + "type": "uint8" + }, + { + "internalType": "bytes", + "name": "parentBatchHeader", + "type": "bytes" + }, + { + "internalType": "uint64", + "name": "lastBlockNumber", + "type": "uint64" + }, + { + "internalType": "uint16", + "name": "numL1Messages", + "type": "uint16" + }, + { + "internalType": "bytes32", + "name": "prevStateRoot", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "postStateRoot", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "withdrawalRoot", + "type": "bytes32" + } + ], + "internalType": "struct IRollup.BatchDataInput", + "name": "batchDataInput", + "type": "tuple" + }, + { + "components": [ + { + "internalType": "uint256", + "name": "signedSequencersBitmap", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "sequencerSets", + "type": "bytes" + }, + { + "internalType": "bytes", + "name": "signature", + "type": "bytes" + } + ], + "internalType": "struct IRollup.BatchSignatureInput", + "name": "batchSignatureInput", + "type": "tuple" + } + ], + "name": "commitBatch", + "outputs": [], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [ + { + "components": [ + { + "internalType": "uint8", + "name": "version", + "type": "uint8" + }, + { + "internalType": "bytes", + "name": "parentBatchHeader", + "type": "bytes" + }, + { + "internalType": "uint64", + "name": "lastBlockNumber", + "type": "uint64" + }, + { + "internalType": "uint16", + "name": "numL1Messages", + "type": "uint16" + }, + { + "internalType": "bytes32", + "name": "prevStateRoot", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "postStateRoot", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "withdrawalRoot", + "type": "bytes32" + } + ], + "internalType": "struct IRollup.BatchDataInput", + "name": "batchDataInput", + "type": "tuple" + }, + { + "components": [ + { + "internalType": "uint256", + "name": "signedSequencersBitmap", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "sequencerSets", + "type": "bytes" + }, + { + "internalType": "bytes", + "name": "signature", + "type": "bytes" + } + ], + "internalType": "struct IRollup.BatchSignatureInput", + "name": "batchSignatureInput", + "type": "tuple" + }, + { + "internalType": "bytes", + "name": "_batchHeader", + "type": "bytes" + }, + { + "internalType": "bytes", + "name": "_batchProof", + "type": "bytes" + } + ], + "name": "commitBatchWithProof", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "components": [ + { + "internalType": "uint8", + "name": "version", + "type": "uint8" + }, + { + "internalType": "bytes", + "name": "parentBatchHeader", + "type": "bytes" + }, + { + "internalType": "uint64", + "name": "lastBlockNumber", + "type": "uint64" + }, + { + "internalType": "uint16", + "name": "numL1Messages", + "type": "uint16" + }, + { + "internalType": "bytes32", + "name": "prevStateRoot", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "postStateRoot", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "withdrawalRoot", + "type": "bytes32" + } + ], + "internalType": "struct IRollup.BatchDataInput", + "name": "batchDataInput", + "type": "tuple" + }, + { + "components": [ + { + "internalType": "uint256", + "name": "signedSequencersBitmap", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "sequencerSets", + "type": "bytes" + }, + { + "internalType": "bytes", + "name": "signature", + "type": "bytes" + } + ], + "internalType": "struct IRollup.BatchSignatureInput", + "name": "batchSignatureInput", + "type": "tuple" + } + ], + "name": "commitState", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "batchIndex", + "type": "uint256" + } + ], + "name": "committedBatches", + "outputs": [ + { + "internalType": "bytes32", + "name": "batchHash", + "type": "bytes32" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "batchIndex", + "type": "uint256" + } + ], + "name": "committedStateRoots", + "outputs": [ + { + "internalType": "bytes32", + "name": "stateRoot", + "type": "bytes32" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "finalizationPeriodSeconds", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes", + "name": "_batchHeader", + "type": "bytes" + } + ], + "name": "finalizeBatch", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "batchIndex", + "type": "uint256" + } + ], + "name": "finalizedStateRoots", + "outputs": [ + { + "internalType": "bytes32", + "name": "stateRoot", + "type": "bytes32" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes", + "name": "_batchHeader", + "type": "bytes" + } + ], + "name": "importGenesisBatch", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "inChallenge", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_l1StakingContract", + "type": "address" + }, + { + "internalType": "address", + "name": "_messageQueue", + "type": "address" + }, + { + "internalType": "address", + "name": "_verifier", + "type": "address" + }, + { + "internalType": "uint256", + "name": "_finalizationPeriodSeconds", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "_proofWindow", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "_proofRewardPercent", + "type": "uint256" + } + ], + "name": "initialize", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "_prevStateRoot", + "type": "bytes32" + } + ], + "name": "initialize2", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "_rollupDelayPeriod", + "type": "uint256" + } + ], + "name": "initialize3", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "_batchIndex", + "type": "uint256" + } + ], + "name": "isBatchFinalized", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "challengerAddress", + "type": "address" + } + ], + "name": "isChallenger", + "outputs": [ + { + "internalType": "bool", + "name": "isChallenger", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "l1StakingContract", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "lastCommittedBatchIndex", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "lastFinalizedBatchIndex", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "messageQueue", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "owner", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "paused", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "proofRewardPercent", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "proofWindow", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes", + "name": "_batchHeader", + "type": "bytes" + }, + { + "internalType": "bytes", + "name": "_batchProof", + "type": "bytes" + } + ], + "name": "proveCommittedBatchState", + "outputs": [], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "proveRemaining", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes", + "name": "_batchHeader", + "type": "bytes" + }, + { + "internalType": "bytes", + "name": "_batchProof", + "type": "bytes" + } + ], + "name": "proveState", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_account", + "type": "address" + } + ], + "name": "removeChallenger", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "renounceOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes", + "name": "_batchHeader", + "type": "bytes" + }, + { + "internalType": "uint256", + "name": "_count", + "type": "uint256" + } + ], + "name": "revertBatch", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "revertReqIndex", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "rollupDelayPeriod", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bool", + "name": "_status", + "type": "bool" + } + ], + "name": "setPause", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "newOwner", + "type": "address" + } + ], + "name": "transferOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "_newPeriod", + "type": "uint256" + } + ], + "name": "updateFinalizePeriodSeconds", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "_newWindow", + "type": "uint256" + } + ], + "name": "updateProofWindow", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "_newProofRewardPercent", + "type": "uint256" + } + ], + "name": "updateRewardPercentage", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "_newPeriod", + "type": "uint256" + } + ], + "name": "updateRollupDelayPeriod", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_newVerifier", + "type": "address" + } + ], + "name": "updateVerifier", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "verifier", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "withdrawalRoot", + "type": "bytes32" + } + ], + "name": "withdrawalRoots", + "outputs": [ + { + "internalType": "bool", + "name": "exist", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "stateMutability": "payable", + "type": "receive" + } +] From d708a45829e268b2b7743d0fbe9a1974efebbb05 Mon Sep 17 00:00:00 2001 From: Segue Date: Tue, 11 Aug 2026 20:31:30 +0800 Subject: [PATCH 02/16] feat(contracts): replace staking authority with submitters --- contracts/abi/archive/README.md | 5 +- .../archive/legacy-l1-staking/IL1Staking.json | 1 + contracts/abi/archive/manifest.json | 1 + contracts/contracts/Staking.md | 134 -- contracts/contracts/l1/rollup/BLS.sol | 4 - contracts/contracts/l1/rollup/IRollup.sol | 39 +- contracts/contracts/l1/rollup/ISubmitter.sol | 18 + contracts/contracts/l1/rollup/Rollup.sol | 193 ++- contracts/contracts/l1/rollup/Submitter.sol | 221 ++++ contracts/contracts/l1/staking/IL1Staking.sol | 119 -- contracts/contracts/l1/staking/L1Staking.sol | 515 -------- contracts/contracts/test/L1Staking.t.sol | 1154 ----------------- contracts/contracts/test/Rollup.t.sol | 591 ++++++--- contracts/contracts/test/Submitter.t.sol | 461 +++++++ .../contracts/test/base/L1MessageBase.t.sol | 41 +- 15 files changed, 1216 insertions(+), 2281 deletions(-) create mode 100644 contracts/abi/archive/legacy-l1-staking/IL1Staking.json delete mode 100644 contracts/contracts/Staking.md delete mode 100644 contracts/contracts/l1/rollup/BLS.sol create mode 100644 contracts/contracts/l1/rollup/ISubmitter.sol create mode 100644 contracts/contracts/l1/rollup/Submitter.sol delete mode 100644 contracts/contracts/l1/staking/IL1Staking.sol delete mode 100644 contracts/contracts/l1/staking/L1Staking.sol delete mode 100644 contracts/contracts/test/L1Staking.t.sol create mode 100644 contracts/contracts/test/Submitter.t.sol diff --git a/contracts/abi/archive/README.md b/contracts/abi/archive/README.md index 20b3253a3..15697249e 100644 --- a/contracts/abi/archive/README.md +++ b/contracts/abi/archive/README.md @@ -6,7 +6,8 @@ the current Rollup ABI was regenerated. - `pre-submitter/Rollup.json` decodes the three production submission methods that still include `BatchSignatureInput`. -- `legacy-l1-staking/L1Staking.json` is the only runtime interface used by the - L1 staking retirement tooling after the legacy Solidity source is removed. +- `legacy-l1-staking/IL1Staking.json` preserves the retired public protocol + interface, while `legacy-l1-staking/L1Staking.json` is the complete runtime + interface used by the retirement tooling after the Solidity source is removed. Do not regenerate or overwrite these files from the current contracts. diff --git a/contracts/abi/archive/legacy-l1-staking/IL1Staking.json b/contracts/abi/archive/legacy-l1-staking/IL1Staking.json new file mode 100644 index 000000000..21a217018 --- /dev/null +++ b/contracts/abi/archive/legacy-l1-staking/IL1Staking.json @@ -0,0 +1 @@ +[{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"oldChallengeDeposit","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"newChallengeDeposit","type":"uint256"}],"name":"ChallengeDepositUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"staker","type":"address"},{"indexed":false,"internalType":"address","name":"receiver","type":"address"}],"name":"Claimed","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"oldGasLimit","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"newGasLimit","type":"uint256"}],"name":"GasLimitAddStakerUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"oldGasLimit","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"newGasLimit","type":"uint256"}],"name":"GasLimitRemoveStakersUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"addr","type":"address"},{"indexed":false,"internalType":"bytes32","name":"tmKey","type":"bytes32"},{"indexed":false,"internalType":"bytes","name":"blsKey","type":"bytes"}],"name":"Registered","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"oldPercentage","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"newPercentage","type":"uint256"}],"name":"RewardPercentageUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"receiver","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"SlashRemainingClaimed","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address[]","name":"stakers","type":"address[]"}],"name":"Slashed","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address[]","name":"stakers","type":"address[]"}],"name":"StakersRemoved","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"oldStakingValue","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"newStakingValue","type":"uint256"}],"name":"StakingValueUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address[]","name":"add","type":"address[]"},{"indexed":false,"internalType":"address[]","name":"remove","type":"address[]"}],"name":"WhitelistUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"addr","type":"address"},{"indexed":false,"internalType":"uint256","name":"unlockHeight","type":"uint256"}],"name":"Withdrawn","type":"event"},{"inputs":[],"name":"challengeDeposit","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getActiveStakers","outputs":[{"internalType":"address[]","name":"","type":"address[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"staker","type":"address"}],"name":"getStakerBitmap","outputs":[{"internalType":"uint256","name":"bitmap","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getStakers","outputs":[{"internalType":"address[255]","name":"","type":"address[255]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address[]","name":"stakers","type":"address[]"}],"name":"getStakersBitmap","outputs":[{"internalType":"uint256","name":"bitmap","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"addr","type":"address"}],"name":"isActiveStaker","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"addr","type":"address"}],"name":"isStaker","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"sequencersBitmap","type":"uint256"}],"name":"slash","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"stakingValue","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"signedSequencersBitmap","type":"uint256"},{"internalType":"address[]","name":"sequencerSet","type":"address[]"},{"internalType":"bytes32","name":"msgHash","type":"bytes32"},{"internalType":"bytes","name":"signature","type":"bytes"}],"name":"verifySignature","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"}] diff --git a/contracts/abi/archive/manifest.json b/contracts/abi/archive/manifest.json index fd4d36083..1afdd8748 100644 --- a/contracts/abi/archive/manifest.json +++ b/contracts/abi/archive/manifest.json @@ -2,6 +2,7 @@ "sourceCommit": "3d21e1f45f743b7fb4131fd6655ceb4d99f15356", "artifacts": { "pre-submitter/Rollup.json": "aea8c9f75f2c8e132a4ffca3f0bb3f6abd83ae2ae9457827a099e75463c5c03a", + "legacy-l1-staking/IL1Staking.json": "cadf899ad272d9018a3009cd8d5ad4a70760d9a6cddf52de44498f5024ef0ad3", "legacy-l1-staking/L1Staking.json": "df14c1e4264ba93661b1476ed9166ab06e03d077713fa13b16fded7f1dd6eb46", "../../storage-layout/archive/Rollup.pre-submitter.json": "f8a745e49d6ea6e141ae26440ce12d6b2feeb155ef1dfdbe9dff40d124c59c37" }, diff --git a/contracts/contracts/Staking.md b/contracts/contracts/Staking.md deleted file mode 100644 index b657c8114..000000000 --- a/contracts/contracts/Staking.md +++ /dev/null @@ -1,134 +0,0 @@ -# Staking - ---- - -## Core Process - -**Staking & Sequencer Selection** - -Morph token staking will be divided into three stages: - -- MorphToken has not yet issued yet -- MorphToken has been issued, but inflation and staking rewards have not started yet. -- Inflation starts and distribute staking rewards - -1. `L1` Update whitelist -2. `L1` Register and stake eth to become staker -3. `L1->L2` Send {add staker} message -4. `L2` Update stakers -5. `L2` Delegate stake MorphToken to staker -6. `L2` Update sequencer set by amount of MorphToken delegation - -**Rollp Verify** - -- The submitted batch requires the BLS signature of more than 2/3 of the sequencers -- Before BLS implementation, only staker (not withdrawing, not slashing) can rollup - -1. `L1` Submit batch and sequencer sets -2. `L1` Verify sequencer set -3. `L1` Verify batch signature - -**Slash Sequencers After Challenger Win** - -- Sequencer will be confiscated all stake amount and removed from sequencer set if defense challenge fails -- Even if challenged repeatedly, each sequencer will only be slashed once -- The reward for a successful challenge is a fixed proportion of the staking amount, and is greater than the challenge deposit -- If the slash causes the number of sequences to be 0, then the layer2 will stop running. We can restart by upgrading, reset stakers and sequencer set. This does not affect the security of the evm state - -1. `L1` Slash staking value of signed sequencers and remove from stakers -2. `L1` Distribute challenger rewards -3. `L1->L2` Send {remove stakers} message -4. `L2` Update sequencer set by amount of MorphToken delegation - -**Delegation Rewards** - -- MorphToken is inflated at a fixed ratio every day, and the additional tokens are used as rewards for sequencers and delegators -- The sequencer and his delegators get MorphToken inflation reward according to the block production ratio -- Sequencer charges commission as its own income -- Users get remaining reward according to their delegation amount - -1. `L2` Staker set delegation commission rate -2. `L2` Upload sequencers work records in epoch (an epoch is a day) -3. `L2` Mint MorphToken inflation as delegation reward -4. `L2` Claim delegation reward or claim commission - -**Quit Staker** - -The exit lock period should be long enough to ensure that stakers and sequencers in L2 have been updated - -1. `L1` Apply to withdraw, remove from staker list, enter lock period -2. `L1->L2` Send {remove staker} message -3. `L2` Remove from stakers and sequencers if needed -4. `L1` Claim allowed until reach unlock height,remove staker info after claiming - ---- - -## Contracts - -### L1 Contract - -#### L1Staking Contract - -**Main functions** - -- Update whitelist -- Register as staker -- Staker quit -- Slash signed sequencers if challenger win, call by rollup contract - -#### Rollup Contract - -**Main functions** - -- Commit batch & verify -- Challenge batch - -### L2 Contract - -#### MorphToken Contract - -**Main functions** - -- Mint inflation - -#### Record Contract - -**Main functions** - -- Record finalized batch submissions, call by oracle -- Record rollup epochs, call by oracle -- Record reward epochs, call by oracle - -#### L2Staking Contract - -**Main functions** - -- Add staker, sync from L1 staking contract -- Remove stakers, sync from L1 staking contract -- Staker set commission rate -- User delegate stake morph token -- User undelegate stake morph token -- User claim delegation reward -- Sequencer claim commission - -#### Sequencer Contract - -**Main functions** - -- Update sequencer set, call form l2 staking contract - -#### Distribute Contract - -**Main functions** - -- Record delegation info by l2 staking contract -- Record undelegation info by l2 staking contract -- Compute delegator's delegation reward, call form l2 staking contract -- Compute sequencer's commission, call form l2 staking contract - -#### Gov Contract - -**Main functions** - -- Submit proposal -- Vote proposal diff --git a/contracts/contracts/l1/rollup/BLS.sol b/contracts/contracts/l1/rollup/BLS.sol deleted file mode 100644 index 5f78c7036..000000000 --- a/contracts/contracts/l1/rollup/BLS.sol +++ /dev/null @@ -1,4 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity =0.8.24; - -contract BLS {} diff --git a/contracts/contracts/l1/rollup/IRollup.sol b/contracts/contracts/l1/rollup/IRollup.sol index d6691cb14..1394916d9 100644 --- a/contracts/contracts/l1/rollup/IRollup.sol +++ b/contracts/contracts/l1/rollup/IRollup.sol @@ -23,23 +23,15 @@ interface IRollup { bytes32 withdrawalRoot; } - /// @param signedSequencers The bitmap of signed sequencers - /// @param sequencerSets The latest 3 sequencer sets - /// @param signature The BLS signature - struct BatchSignatureInput { - uint256 signedSequencersBitmap; - bytes sequencerSets; - bytes signature; - } - /// @param originTimestamp /// @param finalizeTimestamp /// @param blockNumber + /// @param submitter The account responsible for the batch, or zero for permissionless submissions. struct BatchData { uint256 originTimestamp; uint256 finalizeTimestamp; uint256 blockNumber; - uint256 signedSequencersBitmap; + address submitter; } /// @dev Structure to store information about a batch challenge. @@ -128,6 +120,9 @@ interface IRollup { /// @param amount claimed amount. event ProveRemainingClaimed(address receiver, uint256 amount); + /// @notice Emitted when the legacy staking dependency is replaced with Submitter. + event SubmitterContractUpdated(address indexed oldAddr, address indexed newAddr); + /// @notice Emitted when the state of Challenge is updated. /// @param batchIndex The index of the batch. /// @param challenger The address of challenger. @@ -174,36 +169,30 @@ interface IRollup { /// @notice Return the rollup config of finalizationPeriodSeconds. function finalizationPeriodSeconds() external view returns (uint256); + /// @notice Number of committed, non-terminated batches attributable to a submitter. + function pendingBatchCount(address submitter) external view returns (uint256); + /***************************** * Public Mutating Functions * *****************************/ /// @notice Commit a batch of transactions on layer 1. /// - /// @param batchDataInput The BatchDataInput struct - /// @param batchSignatureInput The BatchSignatureInput struct - function commitBatch( - BatchDataInput calldata batchDataInput, - BatchSignatureInput calldata batchSignatureInput - ) external payable; + /// @param batchDataInput The BatchDataInput struct + function commitBatch(BatchDataInput calldata batchDataInput) external payable; /// @notice Commit batch state when blob hash is already stored (recommit after revert without blob). /// @dev Requires batchBlobVersionedHashes[nextBatchIndex] != 0. - function commitState( - BatchDataInput calldata batchDataInput, - BatchSignatureInput calldata batchSignatureInput - ) external; + function commitState(BatchDataInput calldata batchDataInput) external; /// @notice Commit a batch with ZKP proof for permissionless submission. /// @dev This function allows anyone to submit batches when the sequencer is offline or censoring. /// - /// @param batchDataInput The BatchDataInput struct - /// @param batchSignatureInput The BatchSignatureInput struct - /// @param batchHeader The batch header for ZKP verification - /// @param batchProof The ZKP proof data + /// @param batchDataInput The BatchDataInput struct + /// @param batchHeader The batch header for ZKP verification + /// @param batchProof The ZKP proof data function commitBatchWithProof( BatchDataInput calldata batchDataInput, - BatchSignatureInput calldata batchSignatureInput, bytes calldata batchHeader, bytes calldata batchProof ) external; diff --git a/contracts/contracts/l1/rollup/ISubmitter.sol b/contracts/contracts/l1/rollup/ISubmitter.sol new file mode 100644 index 000000000..57747a235 --- /dev/null +++ b/contracts/contracts/l1/rollup/ISubmitter.sol @@ -0,0 +1,18 @@ +// SPDX-License-Identifier: MIT +pragma solidity =0.8.24; + +/// @notice Runtime interface used by Rollup after the centralized submitter cutover. +interface ISubmitter { + function rollupContract() external view returns (address); + + function isActive(address submitter) external view returns (bool); + + function challengeDeposit() external view returns (uint256); + + function slash(address submitter) external returns (uint256 reward); +} + +/// @notice Bounded liability view used by Submitter before releasing stake. +interface IRollupSubmitterView { + function pendingBatchCount(address submitter) external view returns (uint256); +} diff --git a/contracts/contracts/l1/rollup/Rollup.sol b/contracts/contracts/l1/rollup/Rollup.sol index 14aae181b..77d80454a 100644 --- a/contracts/contracts/l1/rollup/Rollup.sol +++ b/contracts/contracts/l1/rollup/Rollup.sol @@ -9,7 +9,7 @@ import {BatchHeaderCodecV1} from "../../libraries/codec/BatchHeaderCodecV1.sol"; import {IRollupVerifier} from "../../libraries/verifier/IRollupVerifier.sol"; import {IL1MessageQueue} from "./IL1MessageQueue.sol"; import {IRollup} from "./IRollup.sol"; -import {IL1Staking} from "../staking/IL1Staking.sol"; +import {ISubmitter} from "./ISubmitter.sol"; // solhint-disable no-inline-assembly // solhint-disable reason-string @@ -24,13 +24,6 @@ contract Rollup is IRollup, OwnableUpgradeable, PausableUpgradeable { /// @notice The zero versioned hash. bytes32 internal constant ZERO_VERSIONED_HASH = 0x010657f37554c781402a22917dee2f75def7ab966d7b770905398eba3c444014; - /// @notice The BLS MODULUS. Deprecated. - uint256 internal constant __BLS_MODULUS = - 52435875175126190479447740508185965837690552500527637822603658699938581184513; - - /// @dev Address of the point evaluation precompile used for EIP-4844 blob verification. - address internal constant POINT_EVALUATION_PRECOMPILE_ADDR = address(0x0A); - /// @notice The chain id of the corresponding layer 2 chain. uint64 public immutable LAYER_2_CHAIN_ID; @@ -38,8 +31,9 @@ contract Rollup is IRollup, OwnableUpgradeable, PausableUpgradeable { * Variables * *************/ - /// @notice L1 staking contract - address public l1StakingContract; + /// @notice Submitter registration and staking contract. + /// @dev Occupies the legacy staking dependency's storage slot. + address public submitterContract; /// @notice Batch challenge time. uint256 public finalizationPeriodSeconds; @@ -109,13 +103,16 @@ contract Rollup is IRollup, OwnableUpgradeable, PausableUpgradeable { /// @dev Placed after rollupDelayPeriod for upgrade-safe storage layout (forward compatibility). mapping(uint256 batchIndex => bytes32 blobVersionedHash) public batchBlobVersionedHashes; + /// @inheritdoc IRollup + mapping(address submitter => uint256 count) public override pendingBatchCount; + /********************** * Function Modifiers * **********************/ - /// @notice Only active staker allowed. - modifier onlyActiveStaker() { - require(IL1Staking(l1StakingContract).isActiveStaker(_msgSender()), "only active staker allowed"); + /// @notice Only an active submitter is allowed. + modifier onlyActiveSubmitter() { + require(ISubmitter(submitterContract).isActive(_msgSender()), "only active submitter allowed"); _; } @@ -150,13 +147,13 @@ contract Rollup is IRollup, OwnableUpgradeable, PausableUpgradeable { ***************/ /// @notice initializer - /// @param _l1StakingContract l1 staking contract + /// @param _submitterContract submitter contract /// @param _messageQueue message queue /// @param _verifier verifier /// @param _finalizationPeriodSeconds finalization period seconds /// @param _proofWindow proof window function initialize( - address _l1StakingContract, + address _submitterContract, address _messageQueue, address _verifier, uint256 _finalizationPeriodSeconds, @@ -166,12 +163,12 @@ contract Rollup is IRollup, OwnableUpgradeable, PausableUpgradeable { if (_messageQueue == address(0) || _verifier == address(0)) { revert ErrZeroAddress(); } - require(_l1StakingContract != address(0), "invalid l1 staking contract"); + require(_submitterContract != address(0), "invalid submitter contract"); __Pausable_init(); __Ownable_init(); - l1StakingContract = _l1StakingContract; + submitterContract = _submitterContract; messageQueue = _messageQueue; verifier = _verifier; finalizationPeriodSeconds = _finalizationPeriodSeconds; @@ -183,7 +180,7 @@ contract Rollup is IRollup, OwnableUpgradeable, PausableUpgradeable { emit UpdateProofRewardPercent(0, _proofRewardPercent); } - function initialize2(bytes32 _prevStateRoot) external reinitializer(2) { + function initialize2(bytes32 _prevStateRoot) external reinitializer(2) onlyOwner { require(_getInitializedVersion() == 2, "must have initialized!"); require(_prevStateRoot != bytes32(0), "can not set state root with bytes32(0)!"); @@ -194,12 +191,42 @@ contract Rollup is IRollup, OwnableUpgradeable, PausableUpgradeable { /// @notice Initializer for upgrade to version 3. /// @param _rollupDelayPeriod The delay period for permissionless batch submission. - function initialize3(uint256 _rollupDelayPeriod) external reinitializer(3) { + function initialize3(uint256 _rollupDelayPeriod) external reinitializer(3) onlyOwner { require(_rollupDelayPeriod != 0, "invalid rollup delay period"); rollupDelayPeriod = _rollupDelayPeriod; emit UpdateRollupDelayPeriod(0, _rollupDelayPeriod); } + /// @notice One-time cutover from the legacy staking address to the Submitter proxy. + /// @dev The cutover is deliberately gated by an empty pending-batch tail, not by pause state. + function initialize4(address _newSubmitter) external reinitializer(4) onlyOwner { + require(_newSubmitter != address(0) && _newSubmitter.code.length > 0, "invalid submitter"); + + bytes32 _declaredRollupRaw = _staticcallExact32( + _newSubmitter, + abi.encodeCall(ISubmitter.rollupContract, ()) + ); + require(uint256(_declaredRollupRaw) >> 160 == 0, "invalid rollup encoding"); + address _declaredRollup = address(uint160(uint256(_declaredRollupRaw))); + uint256 _zeroAddressActive = uint256( + _staticcallExact32(_newSubmitter, abi.encodeCall(ISubmitter.isActive, (address(0)))) + ); + uint256 _challengeDeposit = uint256( + _staticcallExact32(_newSubmitter, abi.encodeCall(ISubmitter.challengeDeposit, ())) + ); + + require(_declaredRollup == address(this), "submitter rollup mismatch"); + require(_zeroAddressActive == 0 && _challengeDeposit > 0, "invalid submitter state"); + require(lastCommittedBatchIndex == lastFinalizedBatchIndex, "pending batches"); + require(revertReqIndex == 0, "pending revert request"); + + // The last finalized entry survives normal GC. Clear only the legacy bitmap slot so it + // cannot be interpreted as a submitter address after the storage-compatible type change. + batchDataStore[lastFinalizedBatchIndex].submitter = address(0); + emit SubmitterContractUpdated(submitterContract, _newSubmitter); + submitterContract = _newSubmitter; + } + /************************ * Restricted Functions * ************************/ @@ -221,7 +248,7 @@ contract Rollup is IRollup, OwnableUpgradeable, PausableUpgradeable { require(BatchHeaderCodecV0.getBlobVersionedHash(memPtr) == ZERO_VERSIONED_HASH, "invalid versioned hash"); committedBatches[_batchIndex] = _batchHash; - batchDataStore[_batchIndex] = BatchData(block.timestamp, block.timestamp, 0, 0); + batchDataStore[_batchIndex] = BatchData(block.timestamp, block.timestamp, 0, address(0)); batchBlobVersionedHashes[_batchIndex] = BatchHeaderCodecV0.getBlobVersionedHash(memPtr); committedStateRoots[_batchIndex] = _postStateRoot; @@ -235,29 +262,23 @@ contract Rollup is IRollup, OwnableUpgradeable, PausableUpgradeable { /// @inheritdoc IRollup function commitBatch( - BatchDataInput calldata batchDataInput, - BatchSignatureInput calldata batchSignatureInput - ) external payable override onlyActiveStaker nonReqRevert whenNotPaused { - // check if the next batch has a stored blob hash - (uint256 _batchPtr, ) = _loadBatchHeader(batchDataInput.parentBatchHeader); - uint256 _nextBatchIndex = BatchHeaderCodecV0.getBatchIndex(_batchPtr) + 1; - require(batchBlobVersionedHashes[_nextBatchIndex] == bytes32(0), "commitBatch requires no stored blob hash"); + BatchDataInput calldata batchDataInput + ) external payable override onlyActiveSubmitter nonReqRevert whenNotPaused { + require(blobhash(0) != bytes32(0), "must carry blob"); if ( IL1MessageQueue(messageQueue).getFirstUnfinalizedMessageEnqueueTime() + rollupDelayPeriod < block.timestamp ) { require(batchDataInput.numL1Messages > 0, "l1msg delay"); } - uint256 submitterBitmap = IL1Staking(l1StakingContract).getStakerBitmap(_msgSender()); bytes32 _blobVersionedHash = _computeBlobVersionedHash(batchDataInput.version); - _commitBatchWithBatchData(batchDataInput, batchSignatureInput, submitterBitmap, _blobVersionedHash); + _commitBatchWithBatchData(batchDataInput, _msgSender(), _blobVersionedHash); } /// @inheritdoc IRollup /// @notice Commit batch state when blob hash is already stored (recommit after revert without blob). function commitState( - BatchDataInput calldata batchDataInput, - BatchSignatureInput calldata batchSignatureInput - ) external override onlyActiveStaker nonReqRevert whenNotPaused { + BatchDataInput calldata batchDataInput + ) external override onlyActiveSubmitter nonReqRevert whenNotPaused { require(blobhash(0) == bytes32(0), "commitState must not carry blob"); (uint256 _batchPtr, ) = _loadBatchHeader(batchDataInput.parentBatchHeader); uint256 _nextBatchIndex = BatchHeaderCodecV0.getBatchIndex(_batchPtr) + 1; @@ -267,19 +288,12 @@ contract Rollup is IRollup, OwnableUpgradeable, PausableUpgradeable { ) { require(batchDataInput.numL1Messages > 0, "l1msg delay"); } - uint256 submitterBitmap = IL1Staking(l1StakingContract).getStakerBitmap(_msgSender()); - _commitBatchWithBatchData( - batchDataInput, - batchSignatureInput, - submitterBitmap, - batchBlobVersionedHashes[_nextBatchIndex] - ); + _commitBatchWithBatchData(batchDataInput, _msgSender(), batchBlobVersionedHashes[_nextBatchIndex]); } function _commitBatchWithBatchData( BatchDataInput calldata batchDataInput, - BatchSignatureInput calldata batchSignatureInput, - uint256 submitterBitmap, + address submitter, bytes32 blobVersionedHash ) internal { require(batchDataInput.version <= 2, "invalid version"); @@ -340,10 +354,7 @@ contract Rollup is IRollup, OwnableUpgradeable, PausableUpgradeable { BatchHeaderCodecV0.storePrevStateHash(_batchPtr, batchDataInput.prevStateRoot); BatchHeaderCodecV0.storePostStateHash(_batchPtr, batchDataInput.postStateRoot); BatchHeaderCodecV0.storeWithdrawRootHash(_batchPtr, batchDataInput.withdrawalRoot); - BatchHeaderCodecV0.storeSequencerSetVerifyHash( - _batchPtr, - keccak256(batchSignatureInput.sequencerSets) - ); + BatchHeaderCodecV0.storeSequencerSetVerifyHash(_batchPtr, bytes32(0)); BatchHeaderCodecV0.storeParentBatchHash(_batchPtr, _parentBatchHash); if (batchDataInput.version >= 1) { BatchHeaderCodecV1.storeLastBlockNumber(_batchPtr, batchDataInput.lastBlockNumber); @@ -362,24 +373,12 @@ contract Rollup is IRollup, OwnableUpgradeable, PausableUpgradeable { block.timestamp, block.timestamp + finalizationPeriodSeconds + proveRemainingTime, batchDataInput.lastBlockNumber, - // Before BLS is implemented, the accuracy of the sequencer set uploaded by rollup cannot be guaranteed. - // Therefore, if the batch is successfully challenged, only the submitter will be punished. - submitterBitmap // => batchSignature.signedSequencersBitmap + submitter ); + _increaseLiability(submitter); lastCommittedBatchIndex = _batchIndex; } - - // verify bls signature - require( - IL1Staking(l1StakingContract).verifySignature( - batchSignatureInput.signedSequencersBitmap, - _getValidSequencerSet(batchSignatureInput.sequencerSets, 0), - _getBLSMsgHash(batchDataInput), - batchSignatureInput.signature - ), - "the signature verification failed" - ); emit CommitBatch(_batchIndex, committedBatches[_batchIndex]); } @@ -388,7 +387,6 @@ contract Rollup is IRollup, OwnableUpgradeable, PausableUpgradeable { /// Entry conditions: rollup delay OR L1 message queue delay must be met. function commitBatchWithProof( BatchDataInput calldata batchDataInput, - BatchSignatureInput calldata batchSignatureInput, bytes calldata _batchHeader, bytes calldata _batchProof ) external override nonReqRevert whenNotPaused { @@ -416,7 +414,7 @@ contract Rollup is IRollup, OwnableUpgradeable, PausableUpgradeable { } else { _blobVersionedHash = _computeBlobVersionedHash(batchDataInput.version); } - _commitBatchWithBatchData(batchDataInput, batchSignatureInput, 0, _blobVersionedHash); + _commitBatchWithBatchData(batchDataInput, address(0), _blobVersionedHash); // get batch data from batch header (uint256 memPtr, bytes32 _batchHash) = _loadBatchHeader(_batchHeader); @@ -452,6 +450,7 @@ contract Rollup is IRollup, OwnableUpgradeable, PausableUpgradeable { while (_count > 0) { emit RevertBatch(_batchIndex, _batchHash); + _releaseLiability(_batchIndex); committedBatches[_batchIndex] = bytes32(0); // if challenge exist and not finished yet, return challenge deposit to challenger if (batchInChallenge(_batchIndex)) { @@ -488,7 +487,7 @@ contract Rollup is IRollup, OwnableUpgradeable, PausableUpgradeable { // check challenge window require(batchInsideChallengeWindow(batchIndex), "cannot challenge batch outside the challenge window"); // check challenge amount - require(msg.value >= IL1Staking(l1StakingContract).challengeDeposit(), "insufficient value"); + require(msg.value >= ISubmitter(submitterContract).challengeDeposit(), "insufficient value"); batchChallenged = batchIndex; challenges[batchIndex] = BatchChallenge(batchIndex, _msgSender(), msg.value, block.timestamp, false, false); @@ -603,7 +602,7 @@ contract Rollup is IRollup, OwnableUpgradeable, PausableUpgradeable { function proveState( bytes calldata _batchHeader, bytes calldata _batchProof - ) external nonReqRevert whenNotPaused onlyActiveStaker { + ) external nonReqRevert whenNotPaused onlyActiveSubmitter { // get batch data from batch header (uint256 memPtr, bytes32 _batchHash) = _loadBatchHeader(_batchHeader); // check batch hash @@ -621,7 +620,7 @@ contract Rollup is IRollup, OwnableUpgradeable, PausableUpgradeable { if (challenges[_batchIndex].startTime + proofWindow <= block.timestamp) { // set status challenges[_batchIndex].challengeSuccess = true; - _challengerWin(_batchIndex, batchDataStore[_batchIndex].signedSequencersBitmap, "Timeout"); + _challengerWin(_batchIndex, batchDataStore[_batchIndex].submitter, "Timeout"); } else { _verifyProof(memPtr, _batchProof); // Record defender win @@ -662,6 +661,7 @@ contract Rollup is IRollup, OwnableUpgradeable, PausableUpgradeable { BatchHeaderCodecV0.getL1MessagePopped(memPtr) ); + _releaseLiability(_batchIndex); delete batchDataStore[_batchIndex - 1]; delete committedStateRoots[_batchIndex - 1]; delete batchBlobVersionedHashes[_batchIndex - 1]; @@ -784,37 +784,6 @@ contract Rollup is IRollup, OwnableUpgradeable, PausableUpgradeable { ); } - /// @dev Internal function to compute BLS msg hash - function _getBLSMsgHash( - BatchDataInput calldata // batchDataInput - ) internal pure returns (bytes32) { - // TODO compute bls message hash - return bytes32(0); - } - - /// @dev todo - function _getValidSequencerSet( - bytes calldata sequencerSets, - uint256 blockHeight - ) internal pure returns (address[] memory) { - // TODO require submitter was in valid sequencer set after BLS was implemented - ( - , - address[] memory sequencerSet0, - uint256 blockHeight1, - address[] memory sequencerSet1, - uint256 blockHeight2, - address[] memory sequencerSet2 - ) = abi.decode(sequencerSets, (uint256, address[], uint256, address[], uint256, address[])); - if (blockHeight >= blockHeight2) { - return sequencerSet2; - } - if (blockHeight >= blockHeight1) { - return sequencerSet1; - } - return sequencerSet0; - } - /// @dev Internal function executed when the defender wins. /// @param batchIndex The index of the batch indicating where the challenge occurred. /// @param prover The zkProof prover address. @@ -829,16 +798,44 @@ contract Rollup is IRollup, OwnableUpgradeable, PausableUpgradeable { /// @dev Internal function executed when the challenger wins. /// @param batchIndex The index of the batch indicating where the challenge occurred. - /// @param sequencersBitmap An array containing the sequencers to be slashed. + /// @param submitter The account responsible for the challenged batch. /// @param _type Description of the challenge type. - function _challengerWin(uint256 batchIndex, uint256 sequencersBitmap, string memory _type) internal { + function _challengerWin(uint256 batchIndex, address submitter, string memory _type) internal { revertReqIndex = batchIndex; address challenger = challenges[batchIndex].challenger; - uint256 reward = IL1Staking(l1StakingContract).slash(sequencersBitmap); + uint256 reward = ISubmitter(submitterContract).slash(submitter); batchChallengeReward[challenges[batchIndex].challenger] += (challenges[batchIndex].challengeDeposit + reward); emit ChallengeRes(batchIndex, challenger, _type); } + function _increaseLiability(address submitter) internal { + if (submitter != address(0)) { + pendingBatchCount[submitter] += 1; + } + } + + function _releaseLiability(uint256 batchIndex) internal { + address liable = batchDataStore[batchIndex].submitter; + if (liable == address(0)) return; + uint256 count = pendingBatchCount[liable]; + require(count > 0, "pending batch count underflow"); + pendingBatchCount[liable] = count - 1; + } + + function _staticcallExact32(address target, bytes memory input) internal view returns (bytes32 result) { + bool ok; + uint256 size; + assembly ("memory-safe") { + ok := staticcall(gas(), target, add(input, 0x20), mload(input), 0, 0) + size := returndatasize() + if and(ok, eq(size, 0x20)) { + returndatacopy(0, 0, 0x20) + result := mload(0) + } + } + require(ok && size == 32, "invalid submitter interface"); + } + /// @dev Internal function to transfer ETH to a specified address. /// @param _to The address to transfer ETH to. /// @param _amount The amount of ETH to transfer. diff --git a/contracts/contracts/l1/rollup/Submitter.sol b/contracts/contracts/l1/rollup/Submitter.sol new file mode 100644 index 000000000..0ed52cf4a --- /dev/null +++ b/contracts/contracts/l1/rollup/Submitter.sol @@ -0,0 +1,221 @@ +// SPDX-License-Identifier: MIT +pragma solidity =0.8.24; + +import {OwnableUpgradeable} from "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol"; +import {ReentrancyGuardUpgradeable} from "@openzeppelin/contracts-upgradeable/security/ReentrancyGuardUpgradeable.sol"; + +import {IRollupSubmitterView, ISubmitter} from "./ISubmitter.sol"; + +/// @title Submitter +/// @notice Registration, stake, exit, and slashing for centralized batch submitters. +contract Submitter is ISubmitter, OwnableUpgradeable, ReentrancyGuardUpgradeable { + uint256 internal constant LIABILITY_QUERY_GAS_LIMIT = 50_000; + uint256 internal constant REFUND_GAS_LIMIT = 50_000; + + address public override rollupContract; + uint256 public minimumStake; + uint256 public override challengeDeposit; + uint256 public rewardPercentage; + uint256 public slashRemaining; + + mapping(address submitter => bool isRegistered) public registered; + mapping(address submitter => uint256 amount) public stakeOf; + mapping(address submitter => bool isExiting) public exiting; + mapping(address submitter => uint256 amount) public claimable; + + event SubmitterAdded(address indexed submitter); + event SubmitterRemoved(address indexed submitter); + event MinimumStakeUpdated(uint256 oldValue, uint256 newValue); + event ChallengeDepositUpdated(uint256 oldValue, uint256 newValue); + event RewardPercentageUpdated(uint256 oldValue, uint256 newValue); + event Staked(address indexed submitter, uint256 amount, uint256 total); + event WithdrawalRequested(address indexed submitter, address indexed receiver, uint256 amount); + event WithdrawalPaid(address indexed submitter, address indexed receiver, uint256 amount); + event WithdrawalCredited(address indexed submitter, uint256 amount, uint256 totalCredit); + event CreditClaimed(address indexed submitter, address indexed receiver, uint256 amount); + event Slashed(address indexed submitter, uint256 amount, uint256 reward); + event SlashRemainingClaimed(address indexed receiver, uint256 amount); + + modifier onlyRollup() { + require(_msgSender() == rollupContract, "only rollup contract"); + _; + } + + constructor() { + _disableInitializers(); + } + + function initialize( + address owner_, + address rollupContract_, + uint256 minimumStake_, + uint256 challengeDeposit_, + uint256 rewardPercentage_ + ) external initializer { + require(owner_ != address(0), "invalid owner"); + require(rollupContract_ != address(0) && rollupContract_.code.length > 0, "invalid rollup contract"); + require(minimumStake_ > 0, "invalid minimum stake"); + require(challengeDeposit_ > 0, "invalid challenge deposit"); + require(rewardPercentage_ > 0 && rewardPercentage_ <= 100, "invalid reward percentage"); + + __Ownable_init(); + __ReentrancyGuard_init(); + _transferOwnership(owner_); + + rollupContract = rollupContract_; + minimumStake = minimumStake_; + challengeDeposit = challengeDeposit_; + rewardPercentage = rewardPercentage_; + + emit MinimumStakeUpdated(0, minimumStake_); + emit ChallengeDepositUpdated(0, challengeDeposit_); + emit RewardPercentageUpdated(0, rewardPercentage_); + } + + function isActive(address submitter) public view override returns (bool) { + return registered[submitter] && !exiting[submitter] && stakeOf[submitter] >= minimumStake; + } + + function addSubmitter(address submitter) external onlyOwner { + require(submitter != address(0) && !registered[submitter] && !exiting[submitter], "invalid submitter"); + registered[submitter] = true; + emit SubmitterAdded(submitter); + } + + function removeSubmitter(address submitter) external onlyOwner nonReentrant { + require(registered[submitter] && !exiting[submitter], "invalid submitter"); + registered[submitter] = false; + exiting[submitter] = true; + emit SubmitterRemoved(submitter); + emit WithdrawalRequested(submitter, submitter, stakeOf[submitter]); + _settleIfReleased(submitter, submitter); + } + + function setMinimumStake(uint256 value) external onlyOwner { + require(value > 0 && value != minimumStake, "invalid minimum stake"); + emit MinimumStakeUpdated(minimumStake, value); + minimumStake = value; + } + + function updateChallengeDeposit(uint256 value) external onlyOwner { + require(value > 0 && value != challengeDeposit, "invalid challenge deposit"); + emit ChallengeDepositUpdated(challengeDeposit, value); + challengeDeposit = value; + } + + function updateRewardPercentage(uint256 value) external onlyOwner { + require(value > 0 && value <= 100 && value != rewardPercentage, "invalid reward percentage"); + emit RewardPercentageUpdated(rewardPercentage, value); + rewardPercentage = value; + } + + function stake() external payable nonReentrant { + address submitter = _msgSender(); + require(registered[submitter] && !exiting[submitter], "not stakeable"); + uint256 total = stakeOf[submitter] + msg.value; + require(msg.value > 0 && total >= minimumStake, "below minimum stake"); + stakeOf[submitter] = total; + emit Staked(submitter, msg.value, total); + } + + function withdraw(address receiver) external nonReentrant { + address submitter = _msgSender(); + require(receiver != address(0), "invalid receiver"); + require(registered[submitter] && !exiting[submitter] && stakeOf[submitter] > 0, "not withdrawable"); + + // A completed exit always requires a fresh owner registration before staking again. + registered[submitter] = false; + exiting[submitter] = true; + emit WithdrawalRequested(submitter, receiver, stakeOf[submitter]); + _settleIfReleased(submitter, receiver); + } + + function claimWithdrawal(address receiver) external nonReentrant { + address submitter = _msgSender(); + require(receiver != address(0), "invalid receiver"); + require(exiting[submitter], "not exiting"); + require(_liabilityReleased(submitter), "pending batch liability"); + _settle(submitter, receiver); + } + + function claimCredit(address receiver) external nonReentrant { + address submitter = _msgSender(); + require(receiver != address(0), "invalid receiver"); + uint256 amount = claimable[submitter]; + require(amount > 0, "no credit"); + claimable[submitter] = 0; + _transfer(receiver, amount); + emit CreditClaimed(submitter, receiver, amount); + } + + function slash(address submitter) external override onlyRollup nonReentrant returns (uint256 reward) { + if (submitter == address(0)) return 0; + + uint256 amount = stakeOf[submitter]; + registered[submitter] = false; + stakeOf[submitter] = 0; + exiting[submitter] = true; + + reward = (amount * rewardPercentage) / 100; + slashRemaining += amount - reward; + _transfer(rollupContract, reward); + emit Slashed(submitter, amount, reward); + } + + function claimSlashRemaining(address receiver) external onlyOwner nonReentrant { + require(receiver != address(0), "invalid receiver"); + uint256 amount = slashRemaining; + require(amount > 0, "no slash remaining"); + slashRemaining = 0; + _transfer(receiver, amount); + emit SlashRemainingClaimed(receiver, amount); + } + + function _settleIfReleased(address submitter, address receiver) internal { + if (_liabilityReleased(submitter)) _settle(submitter, receiver); + } + + function _settle(address submitter, address receiver) internal { + uint256 amount = stakeOf[submitter]; + stakeOf[submitter] = 0; + exiting[submitter] = false; + + if (amount == 0) { + emit WithdrawalPaid(submitter, receiver, 0); + return; + } + + (bool success,) = receiver.call{value: amount, gas: REFUND_GAS_LIMIT}(""); + if (success) { + emit WithdrawalPaid(submitter, receiver, amount); + } else { + claimable[submitter] += amount; + emit WithdrawalCredited(submitter, amount, claimable[submitter]); + } + } + + function _liabilityReleased(address submitter) internal view returns (bool) { + bytes memory input = abi.encodeCall(IRollupSubmitterView.pendingBatchCount, (submitter)); + address target = rollupContract; + bool ok; + uint256 size; + uint256 count; + assembly ("memory-safe") { + ok := staticcall(LIABILITY_QUERY_GAS_LIMIT, target, add(input, 0x20), mload(input), 0, 0) + size := returndatasize() + if and(ok, eq(size, 0x20)) { + returndatacopy(0, 0, 0x20) + count := mload(0) + } + } + return ok && size == 32 && count == 0; + } + + function _transfer(address receiver, uint256 amount) internal { + if (amount == 0) return; + (bool success,) = receiver.call{value: amount}(""); + require(success, "ETH transfer failed"); + } + + uint256[50] private __gap; +} diff --git a/contracts/contracts/l1/staking/IL1Staking.sol b/contracts/contracts/l1/staking/IL1Staking.sol deleted file mode 100644 index ba4f6801b..000000000 --- a/contracts/contracts/l1/staking/IL1Staking.sol +++ /dev/null @@ -1,119 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity =0.8.24; - -interface IL1Staking { - /********** - * Events * - **********/ - - /// @notice staker registered - /// @param addr staker address - /// @param tmKey tendermint pubkey - /// @param blsKey BLS pubkey - event Registered(address addr, bytes32 tmKey, bytes blsKey); - - /// @notice stakers removed - /// @param stakers stakers removed - event StakersRemoved(address[] stakers); - - /// @notice Withdrawn - /// @param addr staker address - /// @param unlockHeight unlock block height - event Withdrawn(address indexed addr, uint256 unlockHeight); - - /// @notice staker claimed - /// @param staker staker claimed - /// @param receiver receiver address - event Claimed(address indexed staker, address receiver); - - /// @notice stakers were slashed - /// @param stakers slashed stakers - event Slashed(address[] stakers); - - /// @notice slash remaining claimed - /// @param receiver receiver address - /// @param amount claimed amount - event SlashRemainingClaimed(address receiver, uint256 amount); - - /// @notice whitelist updated - /// @param add addresses added - /// @param remove addresses removed - event WhitelistUpdated(address[] add, address[] remove); - - /// @notice staking value updated - /// @param oldStakingValue old staking value - /// @param newStakingValue new staking value - event StakingValueUpdated(uint256 oldStakingValue, uint256 newStakingValue); - - /// @notice gas limit add staker updated - /// @param oldGasLimit old gas limit - /// @param newGasLimit new gas limit - event GasLimitAddStakerUpdated(uint256 oldGasLimit, uint256 newGasLimit); - - /// @notice gas limit remove stakers updated - /// @param oldGasLimit old gas limit - /// @param newGasLimit new gas limit - event GasLimitRemoveStakersUpdated(uint256 oldGasLimit, uint256 newGasLimit); - - /// @notice reward percentage updated - /// @param oldPercentage old percentage - /// @param newPercentage new percentage - event RewardPercentageUpdated(uint256 oldPercentage, uint256 newPercentage); - - /// @notice challenge deposit value updated - /// @param oldChallengeDeposit old challengeDeposit - /// @param newChallengeDeposit new challengeDeposit - event ChallengeDepositUpdated(uint256 oldChallengeDeposit, uint256 newChallengeDeposit); - - /************************* - * Public View Functions * - *************************/ - - /// @notice return all stakers - function getStakers() external view returns (address[255] memory); - - /// @notice return active stakers - function getActiveStakers() external view returns (address[] memory); - - /// @notice return staking value - function stakingValue() external view returns (uint256); - - /// @notice return challenge deposit value - function challengeDeposit() external view returns (uint256); - - /// @notice whether address is staker - /// @param addr the address to check - function isStaker(address addr) external view returns (bool); - - /// @notice whether address is active staker - /// @param addr the address to check - function isActiveStaker(address addr) external view returns (bool); - - /// @notice get staker bitmap - /// @param staker the staker address - function getStakerBitmap(address staker) external view returns (uint256); - - /// @notice get stakers bitmap - /// @param stakers the staker address array - function getStakersBitmap(address[] calldata stakers) external view returns (uint256); - - /// @notice verify BLS signature - /// @param signedSequencersBitmap bitmap of signed sequencers - /// @param sequencerSet sequencer set - /// @param msgHash bls message hash - /// @param signature batch signature - function verifySignature( - uint256 signedSequencersBitmap, - address[] calldata sequencerSet, - bytes32 msgHash, - bytes calldata signature - ) external view returns (bool); - - /***************************** - * Public Mutating Functions * - *****************************/ - - /// @notice challenger win, slash sequencers - /// @param sequencersBitmap the sequencers to slash - function slash(uint256 sequencersBitmap) external returns (uint256); -} diff --git a/contracts/contracts/l1/staking/L1Staking.sol b/contracts/contracts/l1/staking/L1Staking.sol deleted file mode 100644 index 165fec18d..000000000 --- a/contracts/contracts/l1/staking/L1Staking.sol +++ /dev/null @@ -1,515 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity =0.8.24; - -import {OwnableUpgradeable} from "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol"; -import {ReentrancyGuardUpgradeable} from "@openzeppelin/contracts-upgradeable/security/ReentrancyGuardUpgradeable.sol"; - -import {Predeploys} from "../../libraries/constants/Predeploys.sol"; -import {Types} from "../../libraries/common/Types.sol"; -import {Staking} from "../../libraries/staking/Staking.sol"; -import {IL1Staking} from "./IL1Staking.sol"; -import {IL2Staking} from "../../l2/staking/IL2Staking.sol"; - -contract L1Staking is IL1Staking, Staking, OwnableUpgradeable, ReentrancyGuardUpgradeable { - /************* - * Variables * - *************/ - - /// @notice rollup Contract - address public rollupContract; - - /// @notice staking value - uint256 public stakingValue; - - /// @notice exit lock blocks - uint256 public withdrawalLockBlocks; - - /// @notice percentage awarded to challenger - uint256 public rewardPercentage; - - /// @notice cross-chain gas limit add staker - uint256 public gasLimitAddStaker; - - /// @notice cross-chain gas limit remove stakers - uint256 public gasLimitRemoveStakers; - - /// @notice slash remaining - uint256 public slashRemaining; - - /// @notice staker whitelist - mapping(address stakerAddr => bool inWhitelist) public whitelist; - - /// @notice staker removed list - mapping(address stakerAddr => bool removed) public removedList; - - /// @notice all stakers (0-254) - address[255] public stakerSet; - - /// @notice all stakers indexes (1-255). '0' means not exist. stakerIndexes[1] releated to stakerSet[0] - mapping(address stakerAddr => uint8 index) public stakerIndexes; - - /// @notice stakers to delete - address[] public deleteList; - - /// @notice staker deleteable height - mapping(address stakerAddr => uint256 height) public deleteableHeight; - - /// @notice all stakers info - mapping(address stakerAddr => Types.StakerInfo) public stakers; - - /// @notice bls key map - mapping(bytes blsPubkey => bool exist) public blsKeys; - - /// @notice tendermint key map - mapping(bytes32 tmPubkey => bool exist) public tmKeys; - - /// @notice withdraw unlock block height - mapping(address staker => uint256 amount) public withdrawals; - - /// @notice challenge deposit value - uint256 public challengeDeposit; - - /********************** - * Function Modifiers * - **********************/ - - /// @notice whether in whitelist - modifier inWhitelist(address addr) { - require(whitelist[addr], "not in whitelist"); - _; - } - - /// @notice only rollup contract - modifier onlyRollupContract() { - require(_msgSender() == rollupContract, "only rollup contract"); - _; - } - - /*************** - * Constructor * - ***************/ - - /// @param _messenger Address of CrossDomainMessenger on this network. - constructor(address payable _messenger) Staking(_messenger, payable(Predeploys.L2_STAKING)) { - _disableInitializers(); - } - - /*************** - * Initializer * - ***************/ - - /// @notice initializer - /// @param _rollupContract rollup contract address - /// @param _stakingValue staking value - /// @param _challengeDeposit challenge deposit value - /// @param _lockBlocks withdraw lock blocks - /// @param _rewardPercentage percentage awarded to challenger - /// @param _gasLimitAdd cross-chain gas limit add staker - /// @param _gasLimitRemove cross-chain gas limit remove stakers - function initialize( - address _rollupContract, - uint256 _stakingValue, - uint256 _challengeDeposit, - uint256 _lockBlocks, - uint256 _rewardPercentage, - uint256 _gasLimitAdd, - uint256 _gasLimitRemove - ) public initializer { - require(_rollupContract != address(0), "invalid rollup contract"); - require(_stakingValue > 0, "invalid staking value"); - require(_challengeDeposit > 0, "invalid challenge deposit value"); - require(_lockBlocks > 0, "invalid withdrawal lock blocks"); - require(_gasLimitAdd > 0, "invalid gas limit add staker"); - require(_gasLimitRemove > 0, "invalid gas limit remove stakers"); - require(_rewardPercentage > 0 && _rewardPercentage <= 100, "invalid challenger reward percentage"); - - __Ownable_init(); - __ReentrancyGuard_init(); - - rollupContract = _rollupContract; - rewardPercentage = _rewardPercentage; - stakingValue = _stakingValue; - challengeDeposit = _challengeDeposit; - withdrawalLockBlocks = _lockBlocks; - gasLimitAddStaker = _gasLimitAdd; - gasLimitRemoveStakers = _gasLimitRemove; - - emit GasLimitAddStakerUpdated(0, _gasLimitAdd); - emit GasLimitRemoveStakersUpdated(0, _gasLimitRemove); - emit RewardPercentageUpdated(0, _rewardPercentage); - } - - /************************ - * Restricted Functions * - ************************/ - - /// @notice update whitelist - function updateWhitelist(address[] calldata add, address[] calldata remove) external onlyOwner { - for (uint256 i = 0; i < add.length; i++) { - require(!removedList[add[i]], "in removed list"); - whitelist[add[i]] = true; - } - for (uint256 i = 0; i < remove.length; i++) { - delete whitelist[remove[i]]; - } - emit WhitelistUpdated(add, remove); - } - - /// @notice register staker - /// @param tmKey tendermint pubkey - /// @param blsKey bls pubkey - function register(bytes32 tmKey, bytes memory blsKey) external payable inWhitelist(_msgSender()) { - require(stakers[_msgSender()].addr == address(0), "already registered"); - require(tmKey != 0 && !tmKeys[tmKey], "invalid tendermint pubkey"); - require(blsKey.length == 256 && !blsKeys[blsKey], "invalid bls pubkey"); - require(msg.value == stakingValue, "invalid staking value"); - - stakers[_msgSender()] = Types.StakerInfo(_msgSender(), tmKey, blsKey); - _addStaker(_msgSender()); - blsKeys[blsKey] = true; - tmKeys[tmKey] = true; - emit Registered(_msgSender(), tmKey, blsKey); - - // send message to add staker on l2 - _msgAddStaker(stakers[_msgSender()]); - } - - /// @notice remove staker - function removeStaker(address[] memory _stakers) external onlyOwner { - for (uint256 i = 0; i < _stakers.length; i++) { - require(isActiveStaker(_stakers[i]), "only active staker can be removed"); - require(withdrawals[_stakers[i]] == 0, "withdrawing"); - - withdrawals[_stakers[i]] = block.number + withdrawalLockBlocks; - _removeStaker(_stakers[i]); - emit Withdrawn(_stakers[i], withdrawals[_stakers[i]]); - - delete whitelist[_stakers[i]]; - removedList[_stakers[i]] = true; - } - emit StakersRemoved(_stakers); - - // send message to remove stakers on l2 - _msgRemoveStakers(_stakers); - } - - /// @notice withdraw staking - function withdraw() external { - require(isActiveStaker(_msgSender()), "only active staker"); - require(withdrawals[_msgSender()] == 0, "withdrawing"); - - withdrawals[_msgSender()] = block.number + withdrawalLockBlocks; - _removeStaker(_msgSender()); - emit Withdrawn(_msgSender(), withdrawals[_msgSender()]); - - delete whitelist[_msgSender()]; - removedList[_msgSender()] = true; - - address[] memory remove = new address[](1); - remove[0] = _msgSender(); - emit StakersRemoved(remove); - - // send message to remove staker on l2 - _msgRemoveStakers(remove); - } - - /// @notice challenger win, slash sequencers - function slash(uint256 sequencersBitmap) external onlyRollupContract nonReentrant returns (uint256) { - address[] memory sequencers = getStakersFromBitmap(sequencersBitmap); - - uint256 valueSum; - for (uint256 i = 0; i < sequencers.length; i++) { - if (withdrawals[sequencers[i]] > 0) { - delete withdrawals[sequencers[i]]; - valueSum += stakingValue; - } else if (!isStakerInDeleteList(sequencers[i])) { - // If it is the first time to be slashed - valueSum += stakingValue; - _removeStaker(sequencers[i]); - // remove from whitelist - delete whitelist[sequencers[i]]; - removedList[sequencers[i]] = true; - } - } - - uint256 reward = (valueSum * rewardPercentage) / 100; - slashRemaining += valueSum - reward; - _transfer(rollupContract, reward); - - emit Slashed(sequencers); - emit StakersRemoved(sequencers); - - // send message to remove stakers on l2 - _msgRemoveStakers(sequencers); - - return reward; - } - - /// @notice claim slash remaining - /// @param receiver receiver address - function claimSlashRemaining(address receiver) external onlyOwner nonReentrant { - uint256 _slashRemaining = slashRemaining; - _transfer(receiver, slashRemaining); - slashRemaining = 0; - emit SlashRemainingClaimed(receiver, _slashRemaining); - } - - /// @notice update staking value - /// @param _stakingValue staking value - function updateStakingValue(uint256 _stakingValue) external onlyOwner { - require(_stakingValue > 0 && _stakingValue != stakingValue, "invalid staking value"); - uint256 _oldStakingValue = stakingValue; - stakingValue = _stakingValue; - emit StakingValueUpdated(_oldStakingValue, stakingValue); - } - - /// @notice update gas limit of add staker - /// @param _gasLimitAdd cross-chain gas limit add staker - function updateGasLimitAddStaker(uint256 _gasLimitAdd) external onlyOwner { - require(_gasLimitAdd > 0 && _gasLimitAdd != gasLimitAddStaker, "invalid new gas limit"); - uint256 _oldGasLimitAddStaker = gasLimitAddStaker; - gasLimitAddStaker = _gasLimitAdd; - emit GasLimitAddStakerUpdated(_oldGasLimitAddStaker, _gasLimitAdd); - } - - /// @notice update gas limit of remove stakers - /// @param _gasLimitRemove cross-chain gas limit remove stakers - function updateGasLimitRemoveStakers(uint256 _gasLimitRemove) external onlyOwner { - require(_gasLimitRemove > 0 && _gasLimitRemove != gasLimitRemoveStakers, "invalid new gas limit"); - uint256 _oldGasLimitRemove = gasLimitRemoveStakers; - gasLimitRemoveStakers = _gasLimitRemove; - emit GasLimitRemoveStakersUpdated(_oldGasLimitRemove, _gasLimitRemove); - } - - /// @notice update challenge deposit - /// @param _challengeDeposit challenge deposit value - function updateChallengeDeposit(uint256 _challengeDeposit) external onlyOwner { - require(_challengeDeposit > 0 && _challengeDeposit != challengeDeposit, "invalid challenge deposit value"); - uint256 _oldChallengeDeposit = challengeDeposit; - challengeDeposit = _challengeDeposit; - emit ChallengeDepositUpdated(_oldChallengeDeposit, _challengeDeposit); - } - - /// @notice update reward percentage - /// @param _rewardPercentage percentage awarded to challenger - function updateRewardPercentage(uint256 _rewardPercentage) external onlyOwner { - require( - _rewardPercentage > 0 && _rewardPercentage <= 100 && _rewardPercentage != rewardPercentage, - "invalid reward percentage" - ); - uint256 _oldRewardPercentage = rewardPercentage; - rewardPercentage = _rewardPercentage; - emit RewardPercentageUpdated(_oldRewardPercentage, _rewardPercentage); - } - - /// @notice clean staker store - function cleanStakerStore() external onlyOwner { - _cleanStakerStore(); - } - - /***************************** - * Public Mutating Functions * - *****************************/ - - /// @notice claim withdrawal - /// @param receiver receiver address - function claimWithdrawal(address receiver) external nonReentrant { - require(withdrawals[_msgSender()] > 0, "withdrawal not exist"); - require(withdrawals[_msgSender()] <= block.number, "withdrawal locked"); - - delete withdrawals[_msgSender()]; - _cleanStakerStore(); - - emit Claimed(_msgSender(), receiver); - - _transfer(receiver, stakingValue); - } - - /************************* - * Public View Functions * - *************************/ - - /// @notice verify BLS signature - function verifySignature( - uint256, // signedSequencersBitmap - address[] calldata, // sequencerSet - bytes32, // msgHash - bytes calldata // signature - ) external pure returns (bool) { - // TODO verify BLS signature - return true; - } - - /// @notice return all stakers - function getStakers() external view returns (address[255] memory) { - return stakerSet; - } - - /// @notice return active stakers - function getActiveStakers() external view returns (address[] memory) { - uint256 activeStakersNumber; - bool[] memory tags = new bool[](255); - for (uint256 i = 0; i < 255; i++) { - // valid address and not in delete list - if (stakerSet[i] != address(0) && deleteableHeight[stakerSet[i]] == 0) { - activeStakersNumber++; - tags[i] = true; - } - } - address[] memory activeStakers = new address[](activeStakersNumber); - uint256 index; - for (uint256 i = 0; i < 255; i++) { - if (tags[i]) { - activeStakers[index] = stakerSet[i]; - index++; - } - } - return activeStakers; - } - - /// @notice whether address is staker - /// @param addr address to check - function isStaker(address addr) public view returns (bool) { - if (stakerIndexes[addr] == 0) { - return false; - } - return stakerSet[stakerIndexes[addr] - 1] == addr; - } - - /// @notice whether address is active staker - /// @param addr address to check - function isActiveStaker(address addr) public view returns (bool) { - if (stakerIndexes[addr] == 0) { - return false; - } - return (stakerSet[stakerIndexes[addr] - 1] == addr) && (deleteableHeight[addr] == 0); - } - - /// @notice whether address in delete list - /// @param addr address to check - function isStakerInDeleteList(address addr) public view returns (bool) { - return deleteableHeight[addr] > 0; - } - - /// @notice get staker bitmap - /// @param _staker the staker address - function getStakerBitmap(address _staker) external view returns (uint256 bitmap) { - require(isStaker(_staker), "invalid staker"); - bitmap = 1 << stakerIndexes[_staker]; - return bitmap; - } - - /// @notice get stakers bitmap - /// @param _stakers the staker address array - function getStakersBitmap(address[] calldata _stakers) external view returns (uint256 bitmap) { - require(_stakers.length <= 255, "stakers length out of bounds"); - for (uint256 i = 0; i < _stakers.length; i++) { - require(isStaker(_stakers[i]), "invalid staker"); - bitmap = bitmap | (1 << stakerIndexes[_stakers[i]]); - } - return bitmap; - } - - /// @notice get stakers from bitmap - /// @param bitmap the stakers bitmap - function getStakersFromBitmap(uint256 bitmap) public view returns (address[] memory stakerAddrs) { - // skip first bit - uint256 _bitmap = bitmap >> 1; - uint256 stakersLength = 0; - while (_bitmap > 0) { - stakersLength = stakersLength + 1; - _bitmap = _bitmap & (_bitmap - 1); - } - - stakerAddrs = new address[](stakersLength); - uint256 index = 0; - for (uint8 i = 1; i <= 255; i++) { - if ((bitmap & (1 << i)) > 0) { - stakerAddrs[index] = stakerSet[i - 1]; - index = index + 1; - if (index >= stakersLength) { - break; - } - } - } - } - - /********************** - * Internal Functions * - **********************/ - - /// @notice add stater - /// @param addr staker address - function _addStaker(address addr) internal { - for (uint8 i = 0; i < 255; i++) { - if (stakerSet[i] == address(0)) { - stakerSet[i] = addr; - stakerIndexes[addr] = i + 1; - return; - } - } - require(false, "slot full"); - } - - /// @notice Add staker to deleteList, it will not be actually deleted until cleanStakerStore is executed - /// @param addr staker address - function _removeStaker(address addr) internal { - require(deleteableHeight[addr] == 0, "already in deleteList"); - deleteList.push(addr); - deleteableHeight[addr] = block.number + withdrawalLockBlocks; - } - - /// @notice transfer ETH - /// @param _to The address to transfer ETH to. - /// @param _amount The amount of ETH to transfer. - function _transfer(address _to, uint256 _amount) internal { - if (_amount > 0) { - (bool success, ) = _to.call{value: _amount}(""); - require(success, "Rollup: ETH transfer failed"); - } - } - - /// @notice add staker - /// @param add staker to add - function _msgAddStaker(Types.StakerInfo memory add) internal { - MESSENGER.sendMessage( - address(OTHER_STAKING), - 0, - abi.encodeCall(IL2Staking.addStaker, (add)), - gasLimitAddStaker - ); - } - - /// @notice remove stakers - /// @param remove stakers to remove - function _msgRemoveStakers(address[] memory remove) internal { - MESSENGER.sendMessage( - address(OTHER_STAKING), - 0, - abi.encodeCall(IL2Staking.removeStakers, (remove)), - gasLimitRemoveStakers - ); - } - - /// @notice clean staker store - function _cleanStakerStore() internal { - uint256 i = 0; - while (i < deleteList.length) { - if (deleteableHeight[deleteList[i]] <= block.number) { - // clean stakerSet - delete stakerSet[stakerIndexes[deleteList[i]] - 1]; - delete stakerIndexes[deleteList[i]]; - - // clean staker info - delete stakers[deleteList[i]]; - - // clean deleteList - delete deleteableHeight[deleteList[i]]; - deleteList[i] = deleteList[deleteList.length - 1]; - deleteList.pop(); - } else { - i++; - } - } - } -} diff --git a/contracts/contracts/test/L1Staking.t.sol b/contracts/contracts/test/L1Staking.t.sol deleted file mode 100644 index 825e5040f..000000000 --- a/contracts/contracts/test/L1Staking.t.sol +++ /dev/null @@ -1,1154 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity =0.8.24; - -import {ITransparentUpgradeableProxy, TransparentUpgradeableProxy} from "@openzeppelin/contracts/proxy/transparent/TransparentUpgradeableProxy.sol"; - -import {Predeploys} from "../libraries/constants/Predeploys.sol"; -import {Types} from "../libraries/common/Types.sol"; -import {IL1Staking} from "../l1/staking/IL1Staking.sol"; -import {L1Staking} from "../l1/staking/L1Staking.sol"; -import {L1MessageBaseTest} from "./base/L1MessageBase.t.sol"; - -// import "forge-std/console.sol"; - -contract StakingInitializeTest is L1MessageBaseTest { - function test_initialize_initializeAgain_revert() external { - // verify the initialize only can be called once. - hevm.expectRevert("Initializable: contract is already initialized"); - l1Staking.initialize( - address(1), - STAKING_VALUE, - CHALLENGE_DEPOSIT, - LOCK_BLOCKS, - rewardPercentage, - defaultGasLimitAdd, - defaultGasLimitRemove - ); - } - - function test_initialize_rollupZeroAddress_revert() external { - hevm.startPrank(multisig); - - // Deploy a proxy contract for l1StakingProxyTemp. - TransparentUpgradeableProxy l1StakingProxyTemp = new TransparentUpgradeableProxy( - address(emptyContract), - address(multisig), - new bytes(0) - ); - - // Expect revert due to invalid rollup contract. - hevm.expectRevert("invalid rollup contract"); - ITransparentUpgradeableProxy(address(l1StakingProxyTemp)).upgradeToAndCall( - address(l1StakingImpl), - abi.encodeCall( - L1Staking.initialize, - ( - address(0), - STAKING_VALUE, - CHALLENGE_DEPOSIT, - LOCK_BLOCKS, - rewardPercentage, - defaultGasLimitAdd, - defaultGasLimitRemove - ) - ) - ); - hevm.stopPrank(); - } - - function test_initialize_stakingValueEqZero_revert() external { - hevm.startPrank(multisig); - - // Deploy a proxy contract for l1StakingProxyTemp. - TransparentUpgradeableProxy l1StakingProxyTemp = new TransparentUpgradeableProxy( - address(emptyContract), - address(multisig), - new bytes(0) - ); - - // Expect revert due to invalid staking value. - hevm.expectRevert("invalid staking value"); - ITransparentUpgradeableProxy(address(l1StakingProxyTemp)).upgradeToAndCall( - address(l1StakingImpl), - abi.encodeCall( - L1Staking.initialize, - ( - address(1), - 0, - CHALLENGE_DEPOSIT, - LOCK_BLOCKS, - rewardPercentage, - defaultGasLimitAdd, - defaultGasLimitRemove - ) - ) - ); - hevm.stopPrank(); - } - - function test_initialize_challengeDepositEqZero_revert() external { - hevm.startPrank(multisig); - - // Deploy a proxy contract for l1StakingProxyTemp. - TransparentUpgradeableProxy l1StakingProxyTemp = new TransparentUpgradeableProxy( - address(emptyContract), - address(multisig), - new bytes(0) - ); - - // Expect revert due to invalid staking value. - hevm.expectRevert("invalid challenge deposit value"); - ITransparentUpgradeableProxy(address(l1StakingProxyTemp)).upgradeToAndCall( - address(l1StakingImpl), - abi.encodeCall( - L1Staking.initialize, - (address(1), STAKING_VALUE, 0, LOCK_BLOCKS, rewardPercentage, defaultGasLimitAdd, defaultGasLimitRemove) - ) - ); - hevm.stopPrank(); - } - - function test_initialize_lockBlocksEqZero_revert() external { - hevm.startPrank(multisig); - - // Deploy a proxy contract for l1StakingProxyTemp. - TransparentUpgradeableProxy l1StakingProxyTemp = new TransparentUpgradeableProxy( - address(emptyContract), - address(multisig), - new bytes(0) - ); - - // Expect revert due to invalid withdrawal lock blocks. - hevm.expectRevert("invalid withdrawal lock blocks"); - ITransparentUpgradeableProxy(address(l1StakingProxyTemp)).upgradeToAndCall( - address(l1StakingImpl), - abi.encodeCall( - L1Staking.initialize, - ( - address(1), - STAKING_VALUE, - CHALLENGE_DEPOSIT, - 0, - rewardPercentage, - defaultGasLimitAdd, - defaultGasLimitRemove - ) - ) - ); - hevm.stopPrank(); - } - - function test_initialize_gasLimitAddEqZero_revert() external { - hevm.startPrank(multisig); - - // Deploy a proxy contract for l1StakingProxyTemp. - TransparentUpgradeableProxy l1StakingProxyTemp = new TransparentUpgradeableProxy( - address(emptyContract), - address(multisig), - new bytes(0) - ); - - // Expect revert due to invalid gas limit add staker. - hevm.expectRevert("invalid gas limit add staker"); - ITransparentUpgradeableProxy(address(l1StakingProxyTemp)).upgradeToAndCall( - address(l1StakingImpl), - abi.encodeCall( - L1Staking.initialize, - (address(1), STAKING_VALUE, CHALLENGE_DEPOSIT, LOCK_BLOCKS, rewardPercentage, 0, defaultGasLimitRemove) - ) - ); - hevm.stopPrank(); - } - - function test_initialize_gasLimitRemoveEqZero_revert() external { - hevm.startPrank(multisig); - - // Deploy a proxy contract for l1StakingProxyTemp. - TransparentUpgradeableProxy l1StakingProxyTemp = new TransparentUpgradeableProxy( - address(emptyContract), - address(multisig), - new bytes(0) - ); - - // Expect revert due to invalid gas limit remove stakers. - hevm.expectRevert("invalid gas limit remove stakers"); - ITransparentUpgradeableProxy(address(l1StakingProxyTemp)).upgradeToAndCall( - address(l1StakingImpl), - abi.encodeCall( - L1Staking.initialize, - (address(1), STAKING_VALUE, CHALLENGE_DEPOSIT, LOCK_BLOCKS, rewardPercentage, defaultGasLimitAdd, 0) - ) - ); - hevm.stopPrank(); - } - - function test_initialize_rewardPercentageEqZero_revert() external { - hevm.startPrank(multisig); - - // Deploy a proxy contract for l1StakingProxyTemp. - TransparentUpgradeableProxy l1StakingProxyTemp = new TransparentUpgradeableProxy( - address(emptyContract), - address(multisig), - new bytes(0) - ); - - // Expect revert due to invalid challenger reward percentage. - hevm.expectRevert("invalid challenger reward percentage"); - ITransparentUpgradeableProxy(address(l1StakingProxyTemp)).upgradeToAndCall( - address(l1StakingImpl), - abi.encodeCall( - L1Staking.initialize, - ( - address(1), - STAKING_VALUE, - CHALLENGE_DEPOSIT, - LOCK_BLOCKS, - 0, - defaultGasLimitAdd, - defaultGasLimitRemove - ) - ) - ); - hevm.stopPrank(); - } - - function test_initialize_rewardPercentageEq101_revert() external { - hevm.startPrank(multisig); - - // Deploy a proxy contract for l1StakingProxyTemp. - TransparentUpgradeableProxy l1StakingProxyTemp = new TransparentUpgradeableProxy( - address(emptyContract), - address(multisig), - new bytes(0) - ); - - // Expect revert due to invalid challenger reward percentage. - hevm.expectRevert("invalid challenger reward percentage"); - ITransparentUpgradeableProxy(address(l1StakingProxyTemp)).upgradeToAndCall( - address(l1StakingImpl), - abi.encodeCall( - L1Staking.initialize, - ( - address(1), - STAKING_VALUE, - CHALLENGE_DEPOSIT, - LOCK_BLOCKS, - 101, - defaultGasLimitAdd, - defaultGasLimitRemove - ) - ) - ); - hevm.stopPrank(); - } - - function test_initialize_succeeds() external { - hevm.startPrank(multisig); - - // Deploy a proxy contract for l1StakingProxyTemp. - TransparentUpgradeableProxy l1StakingProxyTemp = new TransparentUpgradeableProxy( - address(emptyContract), - address(multisig), - new bytes(0) - ); - - // Verify the GasLimitAddStakerUpdated event is emitted successful. - hevm.expectEmit(false, false, false, true); - emit IL1Staking.GasLimitAddStakerUpdated(0, 22); - - // Verify the GasLimitRemoveStakersUpdated event is emitted successful. - hevm.expectEmit(false, false, false, true); - emit IL1Staking.GasLimitRemoveStakersUpdated(0, 33); - - // Verify the RewardPercentageUpdated event is emitted successful. - hevm.expectEmit(false, false, false, true); - emit IL1Staking.RewardPercentageUpdated(0, 11); - - ITransparentUpgradeableProxy(address(l1StakingProxyTemp)).upgradeToAndCall( - address(l1StakingImpl), - abi.encodeCall( - L1Staking.initialize, - (address(1), STAKING_VALUE, CHALLENGE_DEPOSIT, LOCK_BLOCKS, 11, 22, 33) - ) - ); - hevm.stopPrank(); - } -} - -contract StakingTest is L1MessageBaseTest { - function test_setUpCheck_succeeds() external { - assertEq(rollup.l1StakingContract(), address(l1Staking)); - assertEq(address(l1Staking.OTHER_STAKING()), address(Predeploys.L2_STAKING)); - assertEq(address(l1Staking.MESSENGER()), address(l1CrossDomainMessenger)); - assertEq(l1Staking.rollupContract(), address(rollup)); - assertEq(l1Staking.stakingValue(), STAKING_VALUE); - assertEq(l1Staking.withdrawalLockBlocks(), LOCK_BLOCKS); - assertEq(l1Staking.rewardPercentage(), rewardPercentage); - assertEq(l1Staking.gasLimitAddStaker(), 1000000); - assertEq(l1Staking.gasLimitRemoveStakers(), 10000000); - } - - function test_invalidStaker_reverts() external { - // make param - address[] memory sequencers = new address[](2); - sequencers[0] = alice; - sequencers[1] = bob; - hevm.startPrank(address(emptyContract)); - hevm.expectRevert("invalid staker"); - l1Staking.getStakersBitmap(sequencers); - hevm.stopPrank(); - } - - function test_registerAlice_succeeds() external { - hevm.deal(alice, 5 * STAKING_VALUE); - Types.StakerInfo memory stakerInfo = ffi.generateStakerInfo(alice); - address[] memory add = new address[](1); - add[0] = alice; - hevm.prank(multisig); - l1Staking.updateWhitelist(add, new address[](0)); - hevm.stopPrank(); - hevm.startPrank(alice); - hevm.expectEmit(true, true, true, true); - emit IL1Staking.Registered(alice, stakerInfo.tmKey, stakerInfo.blsKey); - l1Staking.register{value: STAKING_VALUE}(stakerInfo.tmKey, stakerInfo.blsKey); - (address addrCheck, bytes32 tmKeyCheck, bytes memory blsKeyCheck) = l1Staking.stakers(alice); - assertEq(addrCheck, alice); - assertEq(tmKeyCheck, stakerInfo.tmKey); - assertBytesEq(blsKeyCheck, stakerInfo.blsKey); - assertTrue(l1Staking.blsKeys(stakerInfo.blsKey)); - assertTrue(l1Staking.tmKeys(stakerInfo.tmKey)); - hevm.stopPrank(); - } - - function test_stakerWithdrawAndClaim_succeeds() external { - // register - - hevm.deal(bob, 5 * STAKING_VALUE); - Types.StakerInfo memory stakerInfo = ffi.generateStakerInfo(bob); - address[] memory add = new address[](1); - add[0] = bob; - hevm.prank(multisig); - l1Staking.updateWhitelist(add, new address[](0)); - hevm.stopPrank(); - hevm.startPrank(bob); - hevm.expectEmit(true, true, true, true); - emit IL1Staking.Registered(bob, stakerInfo.tmKey, stakerInfo.blsKey); - l1Staking.register{value: STAKING_VALUE}(stakerInfo.tmKey, stakerInfo.blsKey); - (address addrCheck, bytes32 tmKeyCheck, bytes memory blsKeyCheck) = l1Staking.stakers(bob); - assertEq(addrCheck, bob); - assertEq(tmKeyCheck, stakerInfo.tmKey); - assertBytesEq(blsKeyCheck, stakerInfo.blsKey); - assertTrue(l1Staking.blsKeys(stakerInfo.blsKey)); - assertTrue(l1Staking.tmKeys(stakerInfo.tmKey)); - hevm.stopPrank(); - - // withdraw - - hevm.prank(bob); - hevm.expectEmit(true, true, true, true); - emit IL1Staking.Withdrawn(bob, block.number + LOCK_BLOCKS); - l1Staking.withdraw(); - assertFalse(l1Staking.whitelist(bob)); - assertTrue(l1Staking.isStaker(bob)); - assertTrue(l1Staking.isStakerInDeleteList(bob)); - assertTrue(l1Staking.removedList(bob)); - assertEq(l1Staking.withdrawals(bob), block.number + LOCK_BLOCKS); - hevm.stopPrank(); - - // claim - - hevm.prank(bob); - assertEq(bob.balance, 4 * STAKING_VALUE); - hevm.roll(block.number + LOCK_BLOCKS + 1); - l1Staking.claimWithdrawal(bob); - assertEq(bob.balance, 5 * STAKING_VALUE); - assertEq(l1Staking.withdrawals(bob), 0); - hevm.stopPrank(); - } - - function test_stakersBitmap_succeeds() external { - address[] memory stakersAddr = new address[](255); - for (uint256 i = 0; i < 255; i++) { - stakersAddr[i] = address(bytes20(bytes32(keccak256(abi.encodePacked(1500 + i))))); - hevm.deal(stakersAddr[i], 5 * STAKING_VALUE); - } - hevm.prank(multisig); - l1Staking.updateWhitelist(stakersAddr, new address[](0)); - - for (uint256 i = 0; i < 255; i++) { - Types.StakerInfo memory stakerInfo = ffi.generateStakerInfo(stakersAddr[i]); - hevm.startPrank(stakersAddr[i]); - l1Staking.register{value: STAKING_VALUE}(stakerInfo.tmKey, stakerInfo.blsKey); - hevm.stopPrank(); - } - - uint256 bitmap = l1Staking.getStakersBitmap(stakersAddr); - address[] memory stakersConvert = l1Staking.getStakersFromBitmap(bitmap); - assertEq(stakersAddr.length, stakersConvert.length); - assertEq(stakersAddr[0], stakersConvert[0]); - assertEq(stakersAddr[stakersAddr.length - 1], stakersConvert[stakersConvert.length - 1]); - - // console.logString("......................"); - // console.logUint(bitmap); - // console.logUint(stakersAddr.length); - // console.logUint(stakersConvert.length); - // console.logString("......................"); - } - - function test_slash_succeeds() external { - // TODO - } - - function test_updateParams_succeeds() external { - // TODO - } -} - -contract StakingWhitelistTest is L1MessageBaseTest { - function test_whitelist_notOwner_reverts() external { - address[] memory add = new address[](2); - add[0] = alice; - add[1] = bob; - hevm.prank(alice); - hevm.expectRevert("Ownable: caller is not the owner"); - l1Staking.updateWhitelist(add, new address[](0)); - } - - function test_whitelist_add_succeeds() external { - // make add param - address[] memory add = new address[](2); - add[0] = alice; - add[1] = bob; - - // Verify the WhitelistUpdated event is emitted successfully. - hevm.expectEmit(true, true, true, true); - emit IL1Staking.WhitelistUpdated(add, new address[](0)); - - hevm.prank(multisig); - l1Staking.updateWhitelist(add, new address[](0)); - assertTrue(l1Staking.whitelist(alice)); - assertTrue(l1Staking.whitelist(bob)); - } - - function test_whitelist_remove_succeeds() external { - address[] memory add = new address[](1); - add[0] = alice; - address[] memory remove = new address[](1); - remove[0] = bob; - - // Verify the WhitelistUpdated event is emitted successfully. - hevm.expectEmit(true, true, true, true); - emit IL1Staking.WhitelistUpdated(add, remove); - - hevm.prank(multisig); - l1Staking.updateWhitelist(add, remove); - assertTrue(l1Staking.whitelist(alice)); - assertFalse(l1Staking.whitelist(bob)); - } - - function test_whitelist_withdraw_succeeds() external { - // add to whitelist - address[] memory add = new address[](2); - add[0] = alice; - add[1] = bob; - hevm.prank(multisig); - l1Staking.updateWhitelist(add, new address[](0)); - assertTrue(l1Staking.whitelist(alice)); - assertTrue(l1Staking.whitelist(bob)); - - // alice register - Types.StakerInfo memory aliceInfo = ffi.generateStakerInfo(alice); - hevm.deal(alice, 5 * STAKING_VALUE); - hevm.prank(alice); - l1Staking.register{value: STAKING_VALUE}(aliceInfo.tmKey, aliceInfo.blsKey); - - // bob register - Types.StakerInfo memory bobInfo = ffi.generateStakerInfo(bob); - hevm.deal(bob, 5 * STAKING_VALUE); - hevm.startPrank(bob); - l1Staking.register{value: STAKING_VALUE}(bobInfo.tmKey, bobInfo.blsKey); - - // bob withdraw - l1Staking.withdraw(); - hevm.stopPrank(); - - // check - assertTrue(l1Staking.whitelist(alice)); - assertFalse(l1Staking.whitelist(bob)); - assertFalse(l1Staking.removedList(alice)); - assertTrue(l1Staking.removedList(bob)); - } - - function test_whitelist_reAdd_inRemovedList_reverts() external { - // add to whitelist - address[] memory add = new address[](2); - add[0] = alice; - add[1] = bob; - hevm.prank(multisig); - l1Staking.updateWhitelist(add, new address[](0)); - assertTrue(l1Staking.whitelist(alice)); - assertTrue(l1Staking.whitelist(bob)); - - // alice register - Types.StakerInfo memory aliceInfo = ffi.generateStakerInfo(alice); - hevm.deal(alice, 5 * STAKING_VALUE); - hevm.prank(alice); - l1Staking.register{value: STAKING_VALUE}(aliceInfo.tmKey, aliceInfo.blsKey); - - // bob register - Types.StakerInfo memory bobInfo = ffi.generateStakerInfo(bob); - hevm.deal(bob, 5 * STAKING_VALUE); - hevm.startPrank(bob); - l1Staking.register{value: STAKING_VALUE}(bobInfo.tmKey, bobInfo.blsKey); - - // bob withdraw - l1Staking.withdraw(); - hevm.stopPrank(); - - // check - assertTrue(l1Staking.whitelist(alice)); - assertFalse(l1Staking.whitelist(bob)); - assertFalse(l1Staking.removedList(alice)); - assertTrue(l1Staking.removedList(bob)); - - hevm.startPrank(multisig); - address[] memory reAdd = new address[](1); - reAdd[0] = bob; - hevm.expectRevert("in removed list"); - l1Staking.updateWhitelist(reAdd, new address[](0)); - hevm.stopPrank(); - } - - function test_whitelist_slash_succeeds() external { - // add to whitelist - address[] memory add = new address[](2); - add[0] = alice; - add[1] = bob; - hevm.prank(multisig); - l1Staking.updateWhitelist(add, new address[](0)); - assertTrue(l1Staking.whitelist(alice)); - assertTrue(l1Staking.whitelist(bob)); - - // alice register - Types.StakerInfo memory aliceInfo = ffi.generateStakerInfo(alice); - hevm.deal(alice, 5 * STAKING_VALUE); - hevm.prank(alice); - l1Staking.register{value: STAKING_VALUE}(aliceInfo.tmKey, aliceInfo.blsKey); - - // bob register - Types.StakerInfo memory bobInfo = ffi.generateStakerInfo(bob); - hevm.deal(bob, 5 * STAKING_VALUE); - hevm.startPrank(bob); - l1Staking.register{value: STAKING_VALUE}(bobInfo.tmKey, bobInfo.blsKey); - hevm.stopPrank(); - - // bob sequencer to slash - uint256 bitmap = l1Staking.getStakerBitmap(bob); - assertEq(bitmap, 4); - address[] memory stakers = l1Staking.getStakersFromBitmap(bitmap); - assertEq(stakers.length, 1); - assertEq(stakers[0], bob); - hevm.prank(l1Staking.rollupContract()); - l1Staking.slash(bitmap); - - // check - assertTrue(l1Staking.whitelist(alice)); - assertFalse(l1Staking.whitelist(bob)); - assertFalse(l1Staking.removedList(alice)); - assertTrue(l1Staking.removedList(bob)); - } -} - -contract StakingRegisterTest is L1MessageBaseTest { - function test_register_notInWhitelist_reverts() external { - // alice register - Types.StakerInfo memory aliceInfo = ffi.generateStakerInfo(alice); - hevm.deal(alice, 5 * STAKING_VALUE); - hevm.startPrank(alice); - hevm.expectRevert("not in whitelist"); - l1Staking.register{value: STAKING_VALUE}(aliceInfo.tmKey, aliceInfo.blsKey); - hevm.stopPrank(); - } - - function test_register_alreadyRegistered_reverts() external { - // add to whitelist - address[] memory add = new address[](1); - add[0] = alice; - hevm.prank(multisig); - l1Staking.updateWhitelist(add, new address[](0)); - - // alice register - Types.StakerInfo memory aliceInfo = ffi.generateStakerInfo(alice); - hevm.deal(alice, 5 * STAKING_VALUE); - hevm.startPrank(alice); - l1Staking.register{value: STAKING_VALUE}(aliceInfo.tmKey, aliceInfo.blsKey); - - hevm.expectRevert("already registered"); - l1Staking.register{value: STAKING_VALUE}(aliceInfo.tmKey, aliceInfo.blsKey); - hevm.stopPrank(); - } - - function test_register_notStakingValue_reverts() external { - // add to whitelist - address[] memory add = new address[](1); - add[0] = alice; - hevm.prank(multisig); - l1Staking.updateWhitelist(add, new address[](0)); - - // alice register - Types.StakerInfo memory aliceInfo = ffi.generateStakerInfo(alice); - hevm.deal(alice, 5 * STAKING_VALUE); - hevm.startPrank(alice); - hevm.expectRevert("invalid staking value"); - l1Staking.register{value: STAKING_VALUE - 1}(aliceInfo.tmKey, aliceInfo.blsKey); - hevm.stopPrank(); - } - - function test_register_notTmKey_reverts() external { - // add to whitelist - address[] memory add = new address[](1); - add[0] = alice; - hevm.prank(multisig); - l1Staking.updateWhitelist(add, new address[](0)); - - // alice register - Types.StakerInfo memory aliceInfo = ffi.generateStakerInfo(alice); - hevm.deal(alice, 5 * STAKING_VALUE); - hevm.startPrank(alice); - hevm.expectRevert("invalid tendermint pubkey"); - l1Staking.register{value: STAKING_VALUE}(0, aliceInfo.blsKey); - hevm.stopPrank(); - } - - function test_register_notBlsKey_reverts() external { - // add to whitelist - address[] memory add = new address[](1); - add[0] = alice; - hevm.prank(multisig); - l1Staking.updateWhitelist(add, new address[](0)); - - // alice register - Types.StakerInfo memory aliceInfo = ffi.generateStakerInfo(alice); - hevm.deal(alice, 5 * STAKING_VALUE); - hevm.startPrank(alice); - hevm.expectRevert("invalid bls pubkey"); - l1Staking.register{value: STAKING_VALUE}(aliceInfo.tmKey, ""); - hevm.stopPrank(); - } - - function test_register_succeeds() external { - // add to whitelist - address[] memory add = new address[](1); - add[0] = alice; - hevm.prank(multisig); - l1Staking.updateWhitelist(add, new address[](0)); - - (address addressCheck, , ) = l1Staking.stakers(alice); - assertEq(addressCheck, address(0)); - // alice register - Types.StakerInfo memory aliceInfo = ffi.generateStakerInfo(alice); - assertFalse(l1Staking.blsKeys(aliceInfo.blsKey)); - assertFalse(l1Staking.tmKeys(aliceInfo.tmKey)); - hevm.deal(alice, 5 * STAKING_VALUE); - hevm.prank(alice); - l1Staking.register{value: STAKING_VALUE}(aliceInfo.tmKey, aliceInfo.blsKey); - - (address addrCheck, bytes32 tmKeyCheck, bytes memory blsKeyCheck) = l1Staking.stakers(alice); - assertEq(addrCheck, alice); - assertEq(tmKeyCheck, aliceInfo.tmKey); - assertBytesEq(blsKeyCheck, aliceInfo.blsKey); - assertTrue(l1Staking.blsKeys(aliceInfo.blsKey)); - assertTrue(l1Staking.tmKeys(aliceInfo.tmKey)); - } -} - -contract StakingWithdrawTest is L1MessageBaseTest { - function test_withdraw_notStaker_reverts() external { - // bob withdraw - hevm.expectRevert("only active staker"); - hevm.startPrank(bob); - l1Staking.withdraw(); - hevm.stopPrank(); - } - - function test_withdraw_succeeds() external { - // add to whitelist - address[] memory add = new address[](2); - add[0] = alice; - add[1] = bob; - hevm.prank(multisig); - l1Staking.updateWhitelist(add, new address[](0)); - assertTrue(l1Staking.whitelist(alice)); - assertTrue(l1Staking.whitelist(bob)); - - // alice register - Types.StakerInfo memory aliceInfo = ffi.generateStakerInfo(alice); - hevm.deal(alice, 5 * STAKING_VALUE); - hevm.prank(alice); - l1Staking.register{value: STAKING_VALUE}(aliceInfo.tmKey, aliceInfo.blsKey); - - // bob register - Types.StakerInfo memory bobInfo = ffi.generateStakerInfo(bob); - hevm.deal(bob, 5 * STAKING_VALUE); - hevm.startPrank(bob); - l1Staking.register{value: STAKING_VALUE}(bobInfo.tmKey, bobInfo.blsKey); - - // before withdraw - assertEq(l1Staking.withdrawals(bob), 0); - assertTrue(l1Staking.isStaker(bob)); - assertFalse(l1Staking.removedList(bob)); - - // Verify the StakersRemoved event is emitted successfully. - address[] memory remove = new address[](1); - remove[0] = bob; - hevm.expectEmit(true, true, true, true); - emit IL1Staking.StakersRemoved(remove); - - // bob withdraw - l1Staking.withdraw(); - hevm.stopPrank(); - - // after withdraw - // check - assertEq(l1Staking.withdrawals(bob), block.number + l1Staking.withdrawalLockBlocks()); - assertTrue(l1Staking.isStaker(bob)); - assertTrue(l1Staking.isStakerInDeleteList(bob)); - - assertTrue(l1Staking.whitelist(alice)); - assertFalse(l1Staking.whitelist(bob)); - - assertFalse(l1Staking.removedList(alice)); - assertTrue(l1Staking.removedList(bob)); - } -} - -contract StakingRemoveStakerTest is L1MessageBaseTest { - // TODO -} - -contract StakingSlashTest is L1MessageBaseTest { - function test_slash_notRollup_reverts() external { - hevm.startPrank(address(emptyContract)); - hevm.expectRevert("only rollup contract"); - l1Staking.slash(0); - hevm.stopPrank(); - } - - function test_slash_withdraw_succeeds() external { - // add to whitelist - address[] memory add = new address[](2); - add[0] = alice; - add[1] = bob; - hevm.prank(multisig); - l1Staking.updateWhitelist(add, new address[](0)); - assertTrue(l1Staking.whitelist(alice)); - assertTrue(l1Staking.whitelist(bob)); - - // alice register - Types.StakerInfo memory aliceInfo = ffi.generateStakerInfo(alice); - hevm.deal(alice, 5 * STAKING_VALUE); - hevm.prank(alice); - l1Staking.register{value: STAKING_VALUE}(aliceInfo.tmKey, aliceInfo.blsKey); - - // bob register - Types.StakerInfo memory bobInfo = ffi.generateStakerInfo(bob); - hevm.deal(bob, 5 * STAKING_VALUE); - hevm.startPrank(bob); - l1Staking.register{value: STAKING_VALUE}(bobInfo.tmKey, bobInfo.blsKey); - - // bob withdraw - l1Staking.withdraw(); - hevm.stopPrank(); - - // bob sequencer to slash - uint256 bitmap = l1Staking.getStakerBitmap(bob); - hevm.prank(l1Staking.rollupContract()); - uint256 reward = l1Staking.slash(bitmap); - assertEq(reward, (STAKING_VALUE * l1Staking.rewardPercentage()) / 100); - - // check - assertTrue(l1Staking.whitelist(alice)); - assertFalse(l1Staking.whitelist(bob)); - assertFalse(l1Staking.removedList(alice)); - assertTrue(l1Staking.removedList(bob)); - } - - function test_slash_notWithdraw_succeeds() external { - // add to whitelist - address[] memory add = new address[](2); - add[0] = alice; - add[1] = bob; - hevm.prank(multisig); - l1Staking.updateWhitelist(add, new address[](0)); - assertTrue(l1Staking.whitelist(alice)); - assertTrue(l1Staking.whitelist(bob)); - - // alice register - Types.StakerInfo memory aliceInfo = ffi.generateStakerInfo(alice); - hevm.deal(alice, 5 * STAKING_VALUE); - hevm.prank(alice); - l1Staking.register{value: STAKING_VALUE}(aliceInfo.tmKey, aliceInfo.blsKey); - - // bob register - Types.StakerInfo memory bobInfo = ffi.generateStakerInfo(bob); - hevm.deal(bob, 5 * STAKING_VALUE); - hevm.startPrank(bob); - l1Staking.register{value: STAKING_VALUE}(bobInfo.tmKey, bobInfo.blsKey); - hevm.stopPrank(); - - // bob sequencer to slash - uint256 bitmap = l1Staking.getStakerBitmap(bob); - hevm.prank(l1Staking.rollupContract()); - uint256 reward = l1Staking.slash(bitmap); - assertEq(reward, (STAKING_VALUE * l1Staking.rewardPercentage()) / 100); - - // check - assertTrue(l1Staking.whitelist(alice)); - assertFalse(l1Staking.whitelist(bob)); - assertFalse(l1Staking.removedList(alice)); - assertTrue(l1Staking.removedList(bob)); - } - - function test_slash_all_succeeds() external { - // add to whitelist - address[] memory add = new address[](2); - add[0] = alice; - add[1] = bob; - hevm.prank(multisig); - l1Staking.updateWhitelist(add, new address[](0)); - assertTrue(l1Staking.whitelist(alice)); - assertTrue(l1Staking.whitelist(bob)); - - // alice register - Types.StakerInfo memory aliceInfo = ffi.generateStakerInfo(alice); - hevm.deal(alice, 5 * STAKING_VALUE); - hevm.prank(alice); - l1Staking.register{value: STAKING_VALUE}(aliceInfo.tmKey, aliceInfo.blsKey); - - // bob register - Types.StakerInfo memory bobInfo = ffi.generateStakerInfo(bob); - hevm.deal(bob, 5 * STAKING_VALUE); - hevm.startPrank(bob); - l1Staking.register{value: STAKING_VALUE}(bobInfo.tmKey, bobInfo.blsKey); - hevm.stopPrank(); - - address[] memory sequencers = new address[](2); - sequencers[0] = alice; - sequencers[1] = bob; - uint256 beforeBalance = l1Staking.rollupContract().balance; - - // Verify the Slashed event is emitted successfully. - hevm.expectEmit(true, true, true, true); - emit IL1Staking.Slashed(sequencers); - - // Verify the StakersRemoved event is emitted successfully. - hevm.expectEmit(true, true, true, true); - emit IL1Staking.StakersRemoved(sequencers); - - // sequencer to slash - uint256 bitmap = l1Staking.getStakersBitmap(sequencers); - hevm.prank(l1Staking.rollupContract()); - uint256 reward = l1Staking.slash(bitmap); - assertEq(reward, (2 * STAKING_VALUE * l1Staking.rewardPercentage()) / 100); - uint256 afterBalance = l1Staking.rollupContract().balance; - assertEq(reward, afterBalance - beforeBalance); - } -} - -contract StakingClaimSlashRemainingTest is L1MessageBaseTest { - function test_claimSlashRemaining_notOwner_reverts() external { - hevm.prank(alice); - hevm.expectRevert("Ownable: caller is not the owner"); - l1Staking.claimSlashRemaining(bob); - } - - function test_claimSlashRemaining_succeeds() external { - // add to whitelist - address[] memory add = new address[](2); - add[0] = alice; - add[1] = bob; - hevm.prank(multisig); - l1Staking.updateWhitelist(add, new address[](0)); - assertTrue(l1Staking.whitelist(alice)); - assertTrue(l1Staking.whitelist(bob)); - - // alice register - Types.StakerInfo memory aliceInfo = ffi.generateStakerInfo(alice); - hevm.deal(alice, 5 * STAKING_VALUE); - hevm.prank(alice); - l1Staking.register{value: STAKING_VALUE}(aliceInfo.tmKey, aliceInfo.blsKey); - - // bob register - Types.StakerInfo memory bobInfo = ffi.generateStakerInfo(bob); - hevm.deal(bob, 5 * STAKING_VALUE); - hevm.startPrank(bob); - l1Staking.register{value: STAKING_VALUE}(bobInfo.tmKey, bobInfo.blsKey); - hevm.stopPrank(); - - address[] memory sequencers = new address[](2); - sequencers[0] = alice; - sequencers[1] = bob; - uint256 beforeBalance = l1Staking.rollupContract().balance; - - // sequencer to slash - uint256 bitmap = l1Staking.getStakersBitmap(sequencers); - hevm.prank(l1Staking.rollupContract()); - uint256 reward = l1Staking.slash(bitmap); - assertEq(reward, (2 * STAKING_VALUE * l1Staking.rewardPercentage()) / 100); - uint256 afterBalance = l1Staking.rollupContract().balance; - assertEq(reward, afterBalance - beforeBalance); - - // Verify the SlashRemainingClaimed event is emitted successfully. - hevm.expectEmit(true, true, true, true); - emit IL1Staking.SlashRemainingClaimed(multisig, 2 * STAKING_VALUE - reward); - - uint256 beforeBalanceOfM = multisig.balance; - hevm.prank(multisig); - l1Staking.claimSlashRemaining(multisig); - uint256 afterBalanceOfM = multisig.balance; - emit log_uint(STAKING_VALUE * 2); - emit log_uint(afterBalanceOfM - beforeBalanceOfM); - emit log_uint(reward); - uint256 remaining = 2 * STAKING_VALUE - reward; - assertEq(afterBalanceOfM - beforeBalanceOfM, remaining); - } -} - -contract StakingUpdateGasLimitAddStakerTest is L1MessageBaseTest { - function test_updateGasLimitAddStaker_notOwner_reverts() external { - hevm.prank(alice); - hevm.expectRevert("Ownable: caller is not the owner"); - l1Staking.updateGasLimitAddStaker(0); - } - - function test_updateGasLimitAddStaker_eqZero_reverts() external { - hevm.prank(multisig); - hevm.expectRevert("invalid new gas limit"); - l1Staking.updateGasLimitAddStaker(0); - // defaultGasLimitAdd - } - - function test_updateGasLimitAddStaker_eqDefaultValue_reverts() external { - hevm.prank(multisig); - hevm.expectRevert("invalid new gas limit"); - l1Staking.updateGasLimitAddStaker(defaultGasLimitAdd); - } - - function test_updateGasLimitAddStaker_succeeds() external { - uint256 newValue = defaultGasLimitAdd / 10; - uint256 oldValue = l1Staking.gasLimitAddStaker(); - - // Verify the GasLimitAddStakerUpdated event is emitted successfully. - hevm.expectEmit(true, true, true, true); - emit IL1Staking.GasLimitAddStakerUpdated(oldValue, newValue); - - hevm.prank(multisig); - l1Staking.updateGasLimitAddStaker(newValue); - assertEq(l1Staking.gasLimitAddStaker(), newValue); - } -} - -contract StakingUpdateGasLimitRemoveStakersTest is L1MessageBaseTest { - function test_updateGasLimitRemoveStakers_notOwner_reverts() external { - hevm.prank(alice); - hevm.expectRevert("Ownable: caller is not the owner"); - l1Staking.updateGasLimitRemoveStakers(0); - } - - function test_updateGasLimitRemoveStakers_eqZero_reverts() external { - hevm.prank(multisig); - hevm.expectRevert("invalid new gas limit"); - l1Staking.updateGasLimitRemoveStakers(0); - } - - function test_updateGasLimitRemoveStakers_eqDefaultValue_reverts() external { - hevm.prank(multisig); - hevm.expectRevert("invalid new gas limit"); - l1Staking.updateGasLimitRemoveStakers(defaultGasLimitRemove); - } - - function test_updateGasLimitRemoveStakers_succeeds() external { - uint256 newValue = defaultGasLimitRemove / 10; - uint256 oldValue = l1Staking.gasLimitRemoveStakers(); - - // Verify the GasLimitRemoveStakersUpdated event is emitted successfully. - hevm.expectEmit(true, true, true, true); - emit IL1Staking.GasLimitRemoveStakersUpdated(oldValue, newValue); - - hevm.prank(multisig); - l1Staking.updateGasLimitRemoveStakers(newValue); - assertEq(l1Staking.gasLimitRemoveStakers(), newValue); - } -} - -contract StakingUpdateRewardPercentageTest is L1MessageBaseTest { - function test_updateRewardPercentage_notOwner_reverts() external { - hevm.prank(alice); - hevm.expectRevert("Ownable: caller is not the owner"); - l1Staking.updateRewardPercentage(0); - } - - function test_updateRewardPercentage_eqZero_reverts() external { - hevm.prank(multisig); - hevm.expectRevert("invalid reward percentage"); - l1Staking.updateRewardPercentage(0); - } - - function test_updateRewardPercentage_ge100_reverts() external { - hevm.prank(multisig); - hevm.expectRevert("invalid reward percentage"); - l1Staking.updateRewardPercentage(101); - } - - function test_updateRewardPercentage_eqDefaultValue_reverts() external { - hevm.prank(multisig); - hevm.expectRevert("invalid reward percentage"); - l1Staking.updateRewardPercentage(rewardPercentage); - } - - function test_updateRewardPercentage_succeeds() external { - uint256 newValue = 66; - uint256 oldValue = l1Staking.rewardPercentage(); - - // Verify the GasLimitRemoveStakersUpdated event is emitted successfully. - hevm.expectEmit(true, true, true, true); - emit IL1Staking.RewardPercentageUpdated(oldValue, newValue); - - hevm.prank(multisig); - l1Staking.updateRewardPercentage(newValue); - assertEq(l1Staking.rewardPercentage(), newValue); - } -} - -contract StakingClaimWithdrawalTest is L1MessageBaseTest { - function test_claimWithdrawal_notWithdraw_reverts() external { - // add to whitelist - address[] memory add = new address[](2); - add[0] = alice; - add[1] = bob; - hevm.prank(multisig); - l1Staking.updateWhitelist(add, new address[](0)); - assertTrue(l1Staking.whitelist(alice)); - assertTrue(l1Staking.whitelist(bob)); - - // alice register - Types.StakerInfo memory aliceInfo = ffi.generateStakerInfo(alice); - hevm.deal(alice, 5 * STAKING_VALUE); - hevm.prank(alice); - l1Staking.register{value: STAKING_VALUE}(aliceInfo.tmKey, aliceInfo.blsKey); - - // bob register - Types.StakerInfo memory bobInfo = ffi.generateStakerInfo(bob); - hevm.deal(bob, 5 * STAKING_VALUE); - hevm.startPrank(bob); - l1Staking.register{value: STAKING_VALUE}(bobInfo.tmKey, bobInfo.blsKey); - hevm.expectRevert("withdrawal not exist"); - l1Staking.claimWithdrawal(bob); - hevm.stopPrank(); - } - - function test_claimWithdrawal_locked_reverts() external { - // add to whitelist - address[] memory add = new address[](2); - add[0] = alice; - add[1] = bob; - hevm.prank(multisig); - l1Staking.updateWhitelist(add, new address[](0)); - assertTrue(l1Staking.whitelist(alice)); - assertTrue(l1Staking.whitelist(bob)); - - // alice register - Types.StakerInfo memory aliceInfo = ffi.generateStakerInfo(alice); - hevm.deal(alice, 5 * STAKING_VALUE); - hevm.prank(alice); - l1Staking.register{value: STAKING_VALUE}(aliceInfo.tmKey, aliceInfo.blsKey); - - // bob register - Types.StakerInfo memory bobInfo = ffi.generateStakerInfo(bob); - hevm.deal(bob, 5 * STAKING_VALUE); - hevm.startPrank(bob); - l1Staking.register{value: STAKING_VALUE}(bobInfo.tmKey, bobInfo.blsKey); - - // bob withdraw - l1Staking.withdraw(); - hevm.expectRevert("withdrawal locked"); - l1Staking.claimWithdrawal(bob); - hevm.stopPrank(); - } - - function test_claimWithdrawal_eqLockedBlockNumber_reverts() external { - // add to whitelist - address[] memory add = new address[](2); - add[0] = alice; - add[1] = bob; - hevm.prank(multisig); - l1Staking.updateWhitelist(add, new address[](0)); - assertTrue(l1Staking.whitelist(alice)); - assertTrue(l1Staking.whitelist(bob)); - - // alice register - Types.StakerInfo memory aliceInfo = ffi.generateStakerInfo(alice); - hevm.deal(alice, 5 * STAKING_VALUE); - hevm.prank(alice); - l1Staking.register{value: STAKING_VALUE}(aliceInfo.tmKey, aliceInfo.blsKey); - - // bob register - Types.StakerInfo memory bobInfo = ffi.generateStakerInfo(bob); - hevm.deal(bob, 5 * STAKING_VALUE); - hevm.startPrank(bob); - l1Staking.register{value: STAKING_VALUE}(bobInfo.tmKey, bobInfo.blsKey); - - // bob withdraw - l1Staking.withdraw(); - - // set block number - hevm.roll(block.number + l1Staking.withdrawalLockBlocks() - 1); - hevm.expectRevert("withdrawal locked"); - l1Staking.claimWithdrawal(bob); - hevm.stopPrank(); - } - - function test_claimWithdrawal_succeeds() external { - // add to whitelist - address[] memory add = new address[](2); - add[0] = alice; - add[1] = bob; - hevm.prank(multisig); - l1Staking.updateWhitelist(add, new address[](0)); - assertTrue(l1Staking.whitelist(alice)); - assertTrue(l1Staking.whitelist(bob)); - - // alice register - Types.StakerInfo memory aliceInfo = ffi.generateStakerInfo(alice); - hevm.deal(alice, 5 * STAKING_VALUE); - hevm.prank(alice); - l1Staking.register{value: STAKING_VALUE}(aliceInfo.tmKey, aliceInfo.blsKey); - - // bob register - Types.StakerInfo memory bobInfo = ffi.generateStakerInfo(bob); - hevm.deal(bob, 5 * STAKING_VALUE); - hevm.startPrank(bob); - l1Staking.register{value: STAKING_VALUE}(bobInfo.tmKey, bobInfo.blsKey); - - // bob withdraw - l1Staking.withdraw(); - - // Verify the Claimed event is emitted successfully. - hevm.expectEmit(true, true, true, true); - emit IL1Staking.Claimed(bob, bob); - - // set block number - hevm.roll(block.number + l1Staking.withdrawalLockBlocks() + 1); - uint256 beforeBalance = bob.balance; - l1Staking.claimWithdrawal(bob); - uint256 afterBalance = bob.balance; - assertEq(afterBalance - beforeBalance, STAKING_VALUE); - hevm.stopPrank(); - } -} - -contract StakingVerifySignatureTest is L1MessageBaseTest { - // function testVerifySignature() external { - // l1Staking.verifySignature(); - // } -} diff --git a/contracts/contracts/test/Rollup.t.sol b/contracts/contracts/test/Rollup.t.sol index dbcc95c1c..acc05e63e 100644 --- a/contracts/contracts/test/Rollup.t.sol +++ b/contracts/contracts/test/Rollup.t.sol @@ -2,12 +2,71 @@ pragma solidity =0.8.24; import {L1MessageBaseTest} from "./base/L1MessageBase.t.sol"; -import {Types} from "../libraries/common/Types.sol"; import {IRollup} from "../l1/rollup/IRollup.sol"; -import {IL1Staking} from "../l1/staking/IL1Staking.sol"; +import {ISubmitter} from "../l1/rollup/ISubmitter.sol"; import {BatchHeaderCodecV0} from "../libraries/codec/BatchHeaderCodecV0.sol"; import {BatchHeaderCodecV1} from "../libraries/codec/BatchHeaderCodecV1.sol"; +contract SubmitterCandidateMock { + enum ReturnMode { + Normal, + RevertCall, + Short, + Long, + BadAddressEncoding + } + + address public declaredRollup; + uint256 public deposit; + uint256 public zeroAddressActive; + ReturnMode public returnMode; + + constructor(address rollup_, uint256 deposit_, uint256 zeroAddressActive_) { + declaredRollup = rollup_; + deposit = deposit_; + zeroAddressActive = zeroAddressActive_; + } + + function setReturnMode(ReturnMode mode) external { + returnMode = mode; + } + + fallback() external { + ReturnMode mode = returnMode; + if (mode == ReturnMode.RevertCall) revert("candidate reverted"); + + uint256 value; + if (msg.sig == ISubmitter.rollupContract.selector) { + value = uint160(declaredRollup); + if (mode == ReturnMode.BadAddressEncoding) value |= uint256(1) << 200; + } else if (msg.sig == ISubmitter.isActive.selector) { + value = zeroAddressActive; + } else if (msg.sig == ISubmitter.challengeDeposit.selector) { + value = deposit; + } else { + revert("unknown selector"); + } + + assembly { + mstore(0, value) + } + if (mode == ReturnMode.Short) { + assembly { + return(0, 31) + } + } + if (mode == ReturnMode.Long) { + assembly { + mstore(32, 1) + return(0, 64) + } + } + assembly { + return(0, 32) + } + } +} + contract RollupCommitBatchWithProofTest is L1MessageBaseTest { /// @dev Test contract for commitBatchWithProof function /// This function allows permissionless batch submission when sequencers are stalled. @@ -15,17 +74,11 @@ contract RollupCommitBatchWithProofTest is L1MessageBaseTest { bytes public batchHeader0; bytes32 public batchHash0; - IRollup.BatchSignatureInput public batchSignatureInput; function setUp() public virtual override { super.setUp(); // Setup batch signature input - batchSignatureInput = IRollup.BatchSignatureInput( - uint256(0), - abi.encode(uint256(0), new address[](0), uint256(0), new address[](0), uint256(0), new address[](0)), - bytes("0x") - ); // Import genesis batch bytes memory _batchHeader0 = new bytes(249); @@ -45,23 +98,15 @@ contract RollupCommitBatchWithProofTest is L1MessageBaseTest { rollup.importGenesisBatch(batchHeader0); batchHash0 = rollup.committedBatches(0); - // Setup staker - hevm.deal(alice, 5 * STAKING_VALUE); - Types.StakerInfo memory stakerInfo = ffi.generateStakerInfo(alice); - address[] memory addrs = new address[](1); - addrs[0] = alice; - hevm.prank(multisig); - l1Staking.updateWhitelist(addrs, new address[](0)); - hevm.prank(alice); - l1Staking.register{value: STAKING_VALUE}(stakerInfo.tmKey, stakerInfo.blsKey); + _registerAndStakeSubmitter(alice); // Set rollupDelayPeriod (e.g., 1 hour) - no prank needed for hevm.store hevm.store(address(rollup), bytes32(ROLLUP_DELAY_PERIOD_SLOT), bytes32(uint256(3600))); } /// @dev Helper to compute sequencerSetVerifyHash from sequencerSets - function _getSequencerSetVerifyHash() internal view returns (bytes32) { - return keccak256(batchSignatureInput.sequencerSets); + function _getSequencerSetVerifyHash() internal pure returns (bytes32) { + return bytes32(0); } /// @dev Helper to create batch header that matches what _commitBatchWithBatchData produces @@ -142,7 +187,6 @@ contract RollupCommitBatchWithProofTest is L1MessageBaseTest { hevm.expectRevert("invalid timing"); rollup.commitBatchWithProof( batchDataInput, - batchSignatureInput, batchHeader1, bytes("") ); @@ -171,7 +215,6 @@ contract RollupCommitBatchWithProofTest is L1MessageBaseTest { hevm.expectRevert("invalid version"); rollup.commitBatchWithProof( batchDataInput, - batchSignatureInput, batchHeader1, bytes("") ); @@ -198,7 +241,6 @@ contract RollupCommitBatchWithProofTest is L1MessageBaseTest { hevm.expectRevert("V2 requires at least 1 blob"); rollup.commitBatchWithProof( batchDataInput, - batchSignatureInput, batchHeader1, bytes("") ); @@ -226,7 +268,6 @@ contract RollupCommitBatchWithProofTest is L1MessageBaseTest { hevm.expectRevert("Pausable: paused"); rollup.commitBatchWithProof( batchDataInput, - batchSignatureInput, batchHeader1, bytes("") ); @@ -254,7 +295,6 @@ contract RollupCommitBatchWithProofTest is L1MessageBaseTest { hevm.expectRevert("previous state root is zero"); rollup.commitBatchWithProof( batchDataInput, - batchSignatureInput, batchHeader1, bytes("") ); @@ -282,7 +322,6 @@ contract RollupCommitBatchWithProofTest is L1MessageBaseTest { hevm.expectRevert("new state root is zero"); rollup.commitBatchWithProof( batchDataInput, - batchSignatureInput, batchHeader1, bytes("") ); @@ -315,7 +354,6 @@ contract RollupCommitBatchWithProofTest is L1MessageBaseTest { hevm.expectRevert("incorrect parent batch hash"); rollup.commitBatchWithProof( batchDataInput, - batchSignatureInput, batchHeader1, bytes("") ); @@ -343,7 +381,6 @@ contract RollupCommitBatchWithProofTest is L1MessageBaseTest { hevm.expectRevert("incorrect previous state root"); rollup.commitBatchWithProof( batchDataInput, - batchSignatureInput, batchHeader1, bytes("") ); @@ -376,7 +413,6 @@ contract RollupCommitBatchWithProofTest is L1MessageBaseTest { hevm.prank(alice); rollup.commitBatchWithProof( batchDataInput, - batchSignatureInput, batchHeader1, hex"deadbeef" // Non-empty proof required ); @@ -417,7 +453,6 @@ contract RollupCommitBatchWithProofTest is L1MessageBaseTest { hevm.prank(alice); rollup.commitBatchWithProof( batchDataInput, - batchSignatureInput, batchHeader1, hex"deadbeef" // Non-empty proof required ); @@ -451,7 +486,6 @@ contract RollupCommitBatchWithProofTest is L1MessageBaseTest { hevm.prank(alice); rollup.commitBatchWithProof( batchDataInput, - batchSignatureInput, batchHeader1, mockProof ); @@ -486,7 +520,6 @@ contract RollupCommitBatchWithProofTest is L1MessageBaseTest { hevm.prank(alice); rollup.commitBatchWithProof( batchDataInput, - batchSignatureInput, batchHeader1, hex"deadbeef" // Non-empty proof required ); @@ -542,7 +575,6 @@ contract RollupCommitBatchWithProofTest is L1MessageBaseTest { hevm.expectRevert("l1msg delay"); rollup.commitBatchWithProof( batchDataInput, - batchSignatureInput, batchHeader1, hex"deadbeef" ); @@ -575,7 +607,6 @@ contract RollupCommitBatchWithProofTest is L1MessageBaseTest { hevm.prank(bob); rollup.commitBatchWithProof( batchDataInput, - batchSignatureInput, batchHeader1, hex"deadbeef" ); @@ -626,7 +657,6 @@ contract RollupCommitBatchWithProofTest is L1MessageBaseTest { hevm.prank(alice); rollup.commitBatchWithProof( batchDataInput, - batchSignatureInput, batchHeader1, hex"deadbeef" ); @@ -661,7 +691,6 @@ contract RollupCommitBatchWithProofTest is L1MessageBaseTest { hevm.prank(alice); rollup.commitBatchWithProof( batchDataInput, - batchSignatureInput, batchHeader1, hex"deadbeef" // Non-empty proof required ); @@ -680,16 +709,10 @@ contract RollupCommitBatchWithProofTest is L1MessageBaseTest { contract RollupCommitBatchTest is L1MessageBaseTest { address public caller = address(0xb4c79daB8f259C7Aee6E5b2Aa729821864227e84); IRollup.BatchDataInput public batchDataInput; - IRollup.BatchSignatureInput public batchSignatureInput; function setUp() public virtual override { super.setUp(); - batchSignatureInput = IRollup.BatchSignatureInput( - uint256(0), - abi.encode(uint256(0), new address[](0), uint256(0), new address[](0), uint256(0), new address[](0)), - bytes("0x") - ); } function test_commitAndFinalizeWithL1Messages_succeeds() public { @@ -756,27 +779,17 @@ contract RollupCommitBatchTest is L1MessageBaseTest { mstore(add(batchHeader1, add(0x20, 153)), bytesData3) // withdrawRootHash mstore( add(batchHeader1, add(0x20, 185)), - 0xf1f58308e98844ec99e2990d88bfb36e1a30f0e6591e62af90ae6f8498a1b067 + 0 ) // sequencerSetVerifyHash mstore(add(batchHeader1, add(0x20, 217)), batchHash0) // parentBatchHash } uint64 lastBlockNumber = 1; uint16 numL1Messages = 1; - hevm.mockCall( - address(rollup.l1StakingContract()), - abi.encodeCall(IL1Staking.isActiveStaker, (address(0))), - abi.encode(true) - ); - hevm.mockCall( - address(rollup.l1StakingContract()), - abi.encodeCall(IL1Staking.getStakerBitmap, (address(0))), - abi.encode(2) - ); _setupDelayAndWarpForProof(); _mockVerifierForProof(); hevm.startPrank(address(0)); hevm.expectEmit(true, true, false, true); - emit IRollup.CommitBatch(1, bytes32(0xc1862b08d265f073817a8ce0d7cbb426c16d58a86b93464244ab1d027318642e)); + emit IRollup.CommitBatch(1, keccak256(batchHeader1)); batchDataInput = IRollup.BatchDataInput( 0, batchHeader0, @@ -786,12 +799,12 @@ contract RollupCommitBatchTest is L1MessageBaseTest { bytesData1, bytesData3 ); - rollup.commitBatchWithProof(batchDataInput, batchSignatureInput, batchHeader1, hex"deadbeef"); + rollup.commitBatchWithProof(batchDataInput, batchHeader1, hex"deadbeef"); hevm.stopPrank(); assertFalse(rollup.isBatchFinalized(1)); bytes32 batchHash1 = rollup.committedBatches(1); - assertEq(batchHash1, bytes32(0xc1862b08d265f073817a8ce0d7cbb426c16d58a86b93464244ab1d027318642e)); + assertEq(batchHash1, keccak256(batchHeader1)); bytes32 stateRoot1 = rollup.committedStateRoots(1); assertEq(stateRoot1, bytesData1); @@ -828,26 +841,15 @@ contract RollupCommitBatchTest is L1MessageBaseTest { mstore(add(batchHeader2, add(0x20, 153)), bytesData4) // withdrawRootHash mstore( add(batchHeader2, add(0x20, 185)), - 0xf1f58308e98844ec99e2990d88bfb36e1a30f0e6591e62af90ae6f8498a1b067 + 0 ) // sequencerSetVerifyHash mstore(add(batchHeader2, add(0x20, 217)), batchHash1) // parentBatchHash mstore(add(batchHeader2, add(0x20, 249)), shl(192, lastBlockNumber)) // lastBlockNumber } - hevm.prank(multisig); - hevm.mockCall( - address(rollup.l1StakingContract()), - abi.encodeCall(IL1Staking.isActiveStaker, (address(0))), - abi.encode(true) - ); - hevm.mockCall( - address(rollup.l1StakingContract()), - abi.encodeCall(IL1Staking.getStakerBitmap, (address(0))), - abi.encode(2) - ); hevm.startPrank(address(0)); hevm.expectEmit(true, true, false, true); - emit IRollup.CommitBatch(2, bytes32(0x772132c2e12f21bfc5f2792838e480830f2c1dd2be0f3207b159905a9f321038)); + emit IRollup.CommitBatch(2, keccak256(batchHeader2)); batchDataInput = IRollup.BatchDataInput( 1, @@ -858,12 +860,12 @@ contract RollupCommitBatchTest is L1MessageBaseTest { bytesData1, bytesData4 ); - rollup.commitBatchWithProof(batchDataInput, batchSignatureInput, batchHeader2, hex"deadbeef"); + rollup.commitBatchWithProof(batchDataInput, batchHeader2, hex"deadbeef"); hevm.stopPrank(); assertFalse(rollup.isBatchFinalized(2)); bytes32 batchHash2 = rollup.committedBatches(2); - assertEq(batchHash2, bytes32(0x772132c2e12f21bfc5f2792838e480830f2c1dd2be0f3207b159905a9f321038)); + assertEq(batchHash2, keccak256(batchHeader2)); bytes32 stateRoot2 = rollup.committedStateRoots(2); assertEq(stateRoot2, bytesData1); @@ -889,36 +891,11 @@ contract RollupTest is L1MessageBaseTest { address public caller = address(0xb4c79daB8f259C7Aee6E5b2Aa729821864227e84); bytes32 public stateRoot = bytes32(uint256(1)); IRollup.BatchDataInput public batchDataInput; - IRollup.BatchSignatureInput public batchSignatureInput; function setUp() public virtual override { super.setUp(); - batchSignatureInput = IRollup.BatchSignatureInput( - uint256(0), - abi.encode(uint256(0), new address[](0), uint256(0), new address[](0), uint256(0), new address[](0)), - bytes("0x") - ); - // add staker - - hevm.deal(alice, 5 * STAKING_VALUE); - Types.StakerInfo memory stakerInfo = ffi.generateStakerInfo(alice); - address[] memory add = new address[](1); - add[0] = alice; - hevm.prank(multisig); - l1Staking.updateWhitelist(add, new address[](0)); - hevm.stopPrank(); - hevm.startPrank(alice); - hevm.expectEmit(true, true, true, true); - emit IL1Staking.Registered(alice, stakerInfo.tmKey, stakerInfo.blsKey); - l1Staking.register{value: STAKING_VALUE}(stakerInfo.tmKey, stakerInfo.blsKey); - (address addrCheck, bytes32 tmKeyCheck, bytes memory blsKeyCheck) = l1Staking.stakers(alice); - assertEq(addrCheck, alice); - assertEq(tmKeyCheck, stakerInfo.tmKey); - assertBytesEq(blsKeyCheck, stakerInfo.blsKey); - assertTrue(l1Staking.blsKeys(stakerInfo.blsKey)); - assertTrue(l1Staking.tmKeys(stakerInfo.tmKey)); - hevm.stopPrank(); + _registerAndStakeSubmitter(alice); } function test_commitBatches_succeeds() external { @@ -940,66 +917,19 @@ contract RollupTest is L1MessageBaseTest { } hevm.prank(multisig); rollup.importGenesisBatch(batchHeader0); - // only active staker allowed, revert + // only active submitter allowed, revert hevm.startPrank(address(0)); - hevm.expectRevert("only active staker allowed"); + hevm.expectRevert("only active submitter allowed"); batchDataInput = IRollup.BatchDataInput(0, batchHeader0, 0, 0, stateRoot, stateRoot, getTreeRoot()); - rollup.commitBatch(batchDataInput, batchSignatureInput); + rollup.commitBatch(batchDataInput); hevm.stopPrank(); - // invalid version, revert (version 3+ is invalid; version 2 is now valid V2 multi-blob) + // The official path is structurally fresh-blob only. This Foundry version cannot + // attach a blob sidecar, so validation after the blob gate is covered by withProof. hevm.startPrank(alice); - hevm.expectRevert("invalid version"); - batchDataInput = IRollup.BatchDataInput(3, batchHeader0, 0, 0, stateRoot, stateRoot, getTreeRoot()); - rollup.commitBatch(batchDataInput, batchSignatureInput); - hevm.stopPrank(); - - // batch header length incorrect, revert - hevm.startPrank(alice); - hevm.expectRevert("batch header length too small"); - batchDataInput = IRollup.BatchDataInput(0, new bytes(120), 0, 0, stateRoot, stateRoot, getTreeRoot()); - rollup.commitBatch(batchDataInput, batchSignatureInput); - hevm.stopPrank(); - - // incorrect batch index, revert - assembly { - mstore(add(batchHeader0, add(0x20, 1)), shl(192, 1)) // change batch index to 1 - } - hevm.startPrank(alice); - hevm.expectRevert("incorrect batch index"); + hevm.expectRevert("must carry blob"); batchDataInput = IRollup.BatchDataInput(0, batchHeader0, 0, 0, stateRoot, stateRoot, getTreeRoot()); - rollup.commitBatch(batchDataInput, batchSignatureInput); - hevm.stopPrank(); - assembly { - mstore(add(batchHeader0, add(0x20, 1)), 0) // change back - } - - // incorrect parent batch hash, revert - assembly { - mstore(add(batchHeader0, add(0x20, 25)), 2) // change data hash for batch0 - } - hevm.startPrank(alice); - hevm.expectRevert("incorrect parent batch hash"); - batchDataInput = IRollup.BatchDataInput(0, batchHeader0, 0, 0, stateRoot, stateRoot, getTreeRoot()); - rollup.commitBatch(batchDataInput, batchSignatureInput); - hevm.stopPrank(); - assembly { - mstore(add(batchHeader0, add(0x20, 25)), 1) // change back - mstore(add(batchHeader0, add(0x20, 57)), 0x010657f37554c781402a22917dee2f75def7ab966d7b770905398eba3c444014) - } - - // incorrect previous state root, revert - hevm.startPrank(alice); - hevm.expectRevert("incorrect previous state root"); - batchDataInput = IRollup.BatchDataInput(0, batchHeader0, 0, 0, bytes32(uint256(2)), stateRoot, getTreeRoot()); - rollup.commitBatch(batchDataInput, batchSignatureInput); - hevm.stopPrank(); - - // incorrect batch header length, revert - hevm.startPrank(alice); - hevm.expectRevert("batch header length too small"); - batchDataInput = IRollup.BatchDataInput(0, new bytes(248), 0, 0, stateRoot, stateRoot, getTreeRoot()); - rollup.commitBatch(batchDataInput, batchSignatureInput); + rollup.commitBatch(batchDataInput); hevm.stopPrank(); // commit batch with one chunk, no tx, correctly (commitBatch requires blob when no stored hash; use commitBatchWithProof) @@ -1014,7 +944,7 @@ contract RollupTest is L1MessageBaseTest { stateRoot, stateRoot, getTreeRoot(), - keccak256(batchSignatureInput.sequencerSets), + bytes32(0), rollup.committedBatches(0) ); hevm.startPrank(alice); @@ -1022,18 +952,17 @@ contract RollupTest is L1MessageBaseTest { hevm.deal(address(0), 10 ether); rollup.commitBatchWithProof( batchDataInput, - batchSignatureInput, batchHeader1ForProof, hex"deadbeef" ); hevm.stopPrank(); assertGt(uint256(rollup.committedBatches(1)), 0); - // batch is already committed, revert (commitBatch also reverts with "commitBatch requires no stored blob hash" when slot is set) + // The fresh-blob gate remains first even if the target index is already occupied. hevm.startPrank(alice); - hevm.expectRevert("commitBatch requires no stored blob hash"); + hevm.expectRevert("must carry blob"); batchDataInput = IRollup.BatchDataInput(0, batchHeader0, 1, 0, stateRoot, stateRoot, getTreeRoot()); - rollup.commitBatch(batchDataInput, batchSignatureInput); + rollup.commitBatch(batchDataInput); hevm.stopPrank(); } @@ -1071,19 +1000,18 @@ contract RollupTest is L1MessageBaseTest { stateRoot, stateRoot, bytes32(uint256(4)), - keccak256(batchSignatureInput.sequencerSets), + bytes32(0), batchHash0 ); hevm.startPrank(alice); batchDataInput = IRollup.BatchDataInput(0, batchHeader0, 1, 0, stateRoot, stateRoot, bytes32(uint256(4))); rollup.commitBatchWithProof( batchDataInput, - batchSignatureInput, batchHeader1ForProof, hex"deadbeef" ); hevm.stopPrank(); - assertEq(rollup.committedBatches(1), 0x25c3e4fee90e53de960c1092746c431ab570eacf8513011902fa65f10c814541); + assertEq(rollup.committedBatches(1), keccak256(batchHeader1ForProof)); // warp again so second commitBatchWithProof passes rollupDelay (batchDataStore[1].originTimestamp + period < block.timestamp) hevm.warp(block.timestamp + 3601); bytes memory batchHeader1 = new bytes(249); @@ -1099,7 +1027,7 @@ contract RollupTest is L1MessageBaseTest { mstore(add(batchHeader1, add(0x20, 153)), bytesData4) // withdrawRootHash mstore( add(batchHeader1, add(0x20, 185)), - 0xf1f58308e98844ec99e2990d88bfb36e1a30f0e6591e62af90ae6f8498a1b067 + 0 ) // sequencerSetVerifyHash mstore(add(batchHeader1, add(0x20, 217)), batchHash0) // parentBatchHash mstore(add(batchHeader1, add(0x20, 249)), 0) // bitmap0 @@ -1115,14 +1043,13 @@ contract RollupTest is L1MessageBaseTest { stateRoot, stateRoot, bytes32(uint256(4)), - keccak256(batchSignatureInput.sequencerSets), + bytes32(0), rollup.committedBatches(1) ); hevm.startPrank(alice); batchDataInput = IRollup.BatchDataInput(0, batchHeader1, 1, 0, stateRoot, stateRoot, bytes32(uint256(4))); rollup.commitBatchWithProof( batchDataInput, - batchSignatureInput, batchHeader2ForProof, hex"deadbeef" ); @@ -1180,7 +1107,7 @@ contract RollupTest is L1MessageBaseTest { hevm.startPrank(alice); hevm.expectRevert("Pausable: paused"); batchDataInput = IRollup.BatchDataInput(0, new bytes(0), 0, 0, stateRoot, stateRoot, bytes32(uint256(4))); - rollup.commitBatch(batchDataInput, batchSignatureInput); + rollup.commitBatch(batchDataInput); hevm.expectRevert("Pausable: paused"); rollup.finalizeBatch(new bytes(0)); @@ -1294,23 +1221,10 @@ contract RollupTest is L1MessageBaseTest { contract RollupCommitStateTest is L1MessageBaseTest { bytes32 public stateRoot = bytes32(uint256(1)); IRollup.BatchDataInput public batchDataInput; - IRollup.BatchSignatureInput public batchSignatureInput; function setUp() public virtual override { super.setUp(); - batchSignatureInput = IRollup.BatchSignatureInput( - uint256(0), - abi.encode(uint256(0), new address[](0), uint256(0), new address[](0), uint256(0), new address[](0)), - bytes("0x") - ); - hevm.deal(alice, 5 * STAKING_VALUE); - Types.StakerInfo memory stakerInfo = ffi.generateStakerInfo(alice); - address[] memory add = new address[](1); - add[0] = alice; - hevm.prank(multisig); - l1Staking.updateWhitelist(add, new address[](0)); - hevm.prank(alice); - l1Staking.register{value: STAKING_VALUE}(stakerInfo.tmKey, stakerInfo.blsKey); + _registerAndStakeSubmitter(alice); } function _genesisBatchHeader() internal pure returns (bytes memory batchHeader0) { @@ -1334,7 +1248,7 @@ contract RollupCommitStateTest is L1MessageBaseTest { stateRoot, stateRoot, bytes32(uint256(4)), - keccak256(batchSignatureInput.sequencerSets), + bytes32(0), parentBatchHash ); } @@ -1353,7 +1267,7 @@ contract RollupCommitStateTest is L1MessageBaseTest { mstore(add(batchHeader1, add(0x20, 89)), bytesData1) mstore(add(batchHeader1, add(0x20, 121)), bytesData1) mstore(add(batchHeader1, add(0x20, 153)), bytesData4) - mstore(add(batchHeader1, add(0x20, 185)), 0xf1f58308e98844ec99e2990d88bfb36e1a30f0e6591e62af90ae6f8498a1b067) + mstore(add(batchHeader1, add(0x20, 185)), 0) mstore(add(batchHeader1, add(0x20, 217)), parentBatchHash) mstore(add(batchHeader1, add(0x20, 249)), 0) } @@ -1373,7 +1287,6 @@ contract RollupCommitStateTest is L1MessageBaseTest { hevm.prank(alice); rollup.commitBatchWithProof( batchDataInput, - batchSignatureInput, batchHeader1ForProof, hex"deadbeef" ); @@ -1397,11 +1310,11 @@ contract RollupCommitStateTest is L1MessageBaseTest { hevm.prank(alice); rollup.commitBatchWithProof( batchDataInput, - batchSignatureInput, batchHeader1ForProof, hex"deadbeef" ); - assertEq(rollup.committedBatches(1), 0x25c3e4fee90e53de960c1092746c431ab570eacf8513011902fa65f10c814541); + bytes32 expectedBatchHash = keccak256(batchHeader1ForProof); + assertEq(rollup.committedBatches(1), expectedBatchHash); assertGt(uint256(rollup.batchBlobVersionedHashes(1)), 0); // Revert batch 1 (blob hash is preserved) @@ -1415,8 +1328,8 @@ contract RollupCommitStateTest is L1MessageBaseTest { // Recommit with commitState (no blob in tx; uses stored blob hash) batchDataInput = IRollup.BatchDataInput(0, batchHeader0, 1, 0, stateRoot, stateRoot, bytes32(uint256(4))); hevm.prank(alice); - rollup.commitState(batchDataInput, batchSignatureInput); - assertEq(rollup.committedBatches(1), 0x25c3e4fee90e53de960c1092746c431ab570eacf8513011902fa65f10c814541); + rollup.commitState(batchDataInput); + assertEq(rollup.committedBatches(1), expectedBatchHash); assertEq(uint256(rollup.lastCommittedBatchIndex()), 1); } @@ -1428,7 +1341,7 @@ contract RollupCommitStateTest is L1MessageBaseTest { _setupCommitStatePrecondition(); batchDataInput = IRollup.BatchDataInput(0, _genesisBatchHeader(), 1, 0, stateRoot, stateRoot, bytes32(uint256(4))); hevm.prank(alice); - rollup.commitState(batchDataInput, batchSignatureInput); + rollup.commitState(batchDataInput); // Without blob in tx, commitState succeeds. Revert "commitState must not carry blob" is hit only when blobhash(0) != 0 (blob tx). assertEq(rollup.lastCommittedBatchIndex(), 1); } @@ -1442,59 +1355,315 @@ contract RollupCommitStateTest is L1MessageBaseTest { batchDataInput = IRollup.BatchDataInput(0, batchHeader0, 1, 0, stateRoot, stateRoot, bytes32(uint256(4))); hevm.prank(alice); hevm.expectRevert("no stored blob hash for this batch"); - rollup.commitState(batchDataInput, batchSignatureInput); + rollup.commitState(batchDataInput); } /// @dev commitState can only be called by an active staker. - function test_commitState_only_active_staker() public { + function test_commitState_only_active_submitter() public { _setupCommitStatePrecondition(); batchDataInput = IRollup.BatchDataInput(0, _genesisBatchHeader(), 1, 0, stateRoot, stateRoot, bytes32(uint256(4))); hevm.prank(address(1)); - hevm.expectRevert("only active staker allowed"); - rollup.commitState(batchDataInput, batchSignatureInput); + hevm.expectRevert("only active submitter allowed"); + rollup.commitState(batchDataInput); } - /// @dev Mutual exclusion: (1) commitBatch reverts when stored blob hash exists; (2) commitState reverts when no stored hash (see test_commitState_reverts_when_no_stored_blob_hash). - function test_commitState_commitBatch_mutual_exclusion() public { + /// @dev commitBatch is fresh-blob only even when a stale stored hash exists. + function test_commitBatch_requires_blob_when_stored_hash_exists() public { bytes memory batchHeader0 = _genesisBatchHeader(); hevm.prank(multisig); rollup.importGenesisBatch(batchHeader0); - // When there IS stored blob hash for batch 1, commitBatch must revert + // A real blob transaction may overwrite this stale value; a no-blob call cannot. _setStoredBlobHash(1); batchDataInput = IRollup.BatchDataInput(0, batchHeader0, 1, 0, stateRoot, stateRoot, bytes32(uint256(4))); hevm.prank(alice); - hevm.expectRevert("commitBatch requires no stored blob hash"); - rollup.commitBatch(batchDataInput, batchSignatureInput); + hevm.expectRevert("must carry blob"); + rollup.commitBatch(batchDataInput); } } +contract RollupSubmitterCutoverTest is L1MessageBaseTest { + bytes32 internal constant ROOT_1 = bytes32(uint256(1)); + bytes32 internal constant ROOT_2 = bytes32(uint256(2)); + bytes32 internal constant ROOT_3 = bytes32(uint256(3)); + uint256 internal constant BATCH_DATA_STORE_SLOT = 162; + uint256 internal constant PENDING_BATCH_COUNT_SLOT = 174; + + function setUp() public virtual override { + super.setUp(); + _registerAndStakeSubmitter(alice); + _registerAndStakeSubmitter(bob); + } + + function test_currentSelectorsAreDistinctFromPreSubmitterSelectors() public { + assertEq(IRollup.commitBatch.selector, bytes4(0x41f756da)); + assertEq(IRollup.commitState.selector, bytes4(0x67caa37a)); + assertEq(IRollup.commitBatchWithProof.selector, bytes4(0x1544ba3a)); + assertTrue(IRollup.commitBatch.selector != bytes4(0x428868b5)); + assertTrue(IRollup.commitState.selector != bytes4(0x1e8825be)); + assertTrue(IRollup.commitBatchWithProof.selector != bytes4(0x4e8f1d67)); + } + + function test_liabilityReleasedExactlyByRevertAndFinalize() public { + bytes memory header0 = _importGenesis(); + bytes memory header1 = _commitStateAs(alice, header0, 1, ROOT_1, ROOT_2); + bytes memory header2 = _commitStateAs(bob, header1, 2, ROOT_2, ROOT_3); + + assertEq(rollup.pendingBatchCount(alice), 1); + assertEq(rollup.pendingBatchCount(bob), 1); + (, , , address batch1Submitter) = rollup.batchDataStore(1); + (, , , address batch2Submitter) = rollup.batchDataStore(2); + assertEq(batch1Submitter, alice); + assertEq(batch2Submitter, bob); + + hevm.prank(multisig); + rollup.revertBatch(header2, 1); + assertEq(rollup.pendingBatchCount(alice), 1); + assertEq(rollup.pendingBatchCount(bob), 0); + + hevm.warp(block.timestamp + finalizationPeriodSeconds + 1); + rollup.finalizeBatch(header1); + assertEq(rollup.pendingBatchCount(alice), 0); + } + + function test_challengeAndSlashDoNotReleaseLiability() public { + bytes memory header0 = _importGenesis(); + bytes memory header1 = _commitStateAs(alice, header0, 1, ROOT_1, ROOT_2); + bytes32 batchHash = rollup.committedBatches(1); + + hevm.prank(bob); + rollup.challengeState{value: CHALLENGE_DEPOSIT}(1, batchHash); + hevm.warp(block.timestamp + proofWindow + 1); + hevm.prank(alice); + rollup.proveState(header1, hex"01"); + + assertEq(rollup.pendingBatchCount(alice), 1); + assertEq(rollup.revertReqIndex(), 1); + assertEq(submitter.stakeOf(alice), 0); + + hevm.prank(multisig); + rollup.revertBatch(header1, 1); + assertEq(rollup.pendingBatchCount(alice), 0); + assertEq(rollup.revertReqIndex(), 0); + } + + function test_permissionlessProofUsesZeroSubmitterAndZeroHeaderField() public { + bytes memory header0 = _importGenesis(); + _setupDelayAndWarpForProof(); + _mockMessageQueueNotDelayedForProof(); + _mockVerifierForProof(); + + bytes memory header1 = _createBatchHeaderV0ForProof( + 1, + 0, + 0, + _computeDataHash(1, 0), + ROOT_1, + ROOT_2, + bytes32(uint256(101)), + bytes32(0), + rollup.committedBatches(0) + ); + IRollup.BatchDataInput memory input = IRollup.BatchDataInput( + 0, + header0, + 1, + 0, + ROOT_1, + ROOT_2, + bytes32(uint256(101)) + ); + + hevm.prank(address(0xBEEF)); + rollup.commitBatchWithProof(input, header1, hex"01"); + + (, , , address liable) = rollup.batchDataStore(1); + assertEq(liable, address(0)); + assertEq(rollup.pendingBatchCount(address(0xBEEF)), 0); + bytes32 sequencerSetVerifyHash; + assembly { + sequencerSetVerifyHash := mload(add(add(header1, 0x20), 185)) + } + assertEq(sequencerSetVerifyHash, bytes32(0)); + assertEq(rollup.committedBatches(1), keccak256(header1)); + } + + function test_liabilityUnderflowFailsClosed() public { + bytes memory header0 = _importGenesis(); + bytes memory header1 = _commitStateAs(alice, header0, 1, ROOT_1, ROOT_2); + bytes32 countSlot = keccak256(abi.encode(alice, PENDING_BATCH_COUNT_SLOT)); + hevm.store(address(rollup), countSlot, bytes32(0)); + + hevm.prank(multisig); + hevm.expectRevert("pending batch count underflow"); + rollup.revertBatch(header1, 1); + assertEq(rollup.lastCommittedBatchIndex(), 1); + } + + function test_initialize2And3AreOwnerOnly() public { + hevm.prank(alice); + hevm.expectRevert("Ownable: caller is not the owner"); + rollup.initialize2(ROOT_1); + hevm.prank(multisig); + rollup.initialize2(ROOT_1); + + hevm.prank(alice); + hevm.expectRevert("Ownable: caller is not the owner"); + rollup.initialize3(3600); + hevm.prank(multisig); + rollup.initialize3(3600); + } + + function test_initialize4ValidatesInterfaceAndClearsLegacyTailOwnerField() public { + _importGenesis(); + bytes32 batchDataBase = keccak256(abi.encode(uint256(0), BATCH_DATA_STORE_SLOT)); + hevm.store(address(rollup), bytes32(uint256(batchDataBase) + 3), bytes32(uint256(uint160(alice)))); + (uint256 originBefore, uint256 finalizeBefore, uint256 blockBefore, address legacyTail) = rollup + .batchDataStore(0); + assertEq(legacyTail, alice); + + hevm.prank(alice); + hevm.expectRevert("Ownable: caller is not the owner"); + rollup.initialize4(address(submitter)); + + hevm.prank(multisig); + hevm.expectRevert("invalid submitter"); + rollup.initialize4(address(0x1234)); + + SubmitterCandidateMock malformed = new SubmitterCandidateMock(address(rollup), CHALLENGE_DEPOSIT, 0); + malformed.setReturnMode(SubmitterCandidateMock.ReturnMode.Short); + hevm.prank(multisig); + hevm.expectRevert("invalid submitter interface"); + rollup.initialize4(address(malformed)); + malformed.setReturnMode(SubmitterCandidateMock.ReturnMode.Long); + hevm.prank(multisig); + hevm.expectRevert("invalid submitter interface"); + rollup.initialize4(address(malformed)); + malformed.setReturnMode(SubmitterCandidateMock.ReturnMode.BadAddressEncoding); + hevm.prank(multisig); + hevm.expectRevert("invalid rollup encoding"); + rollup.initialize4(address(malformed)); + + SubmitterCandidateMock mismatch = new SubmitterCandidateMock(address(1), CHALLENGE_DEPOSIT, 0); + hevm.prank(multisig); + hevm.expectRevert("submitter rollup mismatch"); + rollup.initialize4(address(mismatch)); + SubmitterCandidateMock badActive = new SubmitterCandidateMock(address(rollup), CHALLENGE_DEPOSIT, 1); + hevm.prank(multisig); + hevm.expectRevert("invalid submitter state"); + rollup.initialize4(address(badActive)); + SubmitterCandidateMock zeroDeposit = new SubmitterCandidateMock(address(rollup), 0, 0); + hevm.prank(multisig); + hevm.expectRevert("invalid submitter state"); + rollup.initialize4(address(zeroDeposit)); + + hevm.prank(multisig); + rollup.initialize4(address(submitter)); + assertEq(rollup.submitterContract(), address(submitter)); + (uint256 originAfter, uint256 finalizeAfter, uint256 blockAfter, address tailSubmitter) = rollup.batchDataStore( + 0 + ); + assertEq(originAfter, originBefore); + assertEq(finalizeAfter, finalizeBefore); + assertEq(blockAfter, blockBefore); + assertEq(tailSubmitter, address(0)); + + hevm.prank(multisig); + hevm.expectRevert("Initializable: contract is already initialized"); + rollup.initialize4(address(submitter)); + } + + function test_initialize4PendingBatchRevertsWithoutConsumingVersion() public { + bytes memory header0 = _importGenesis(); + bytes memory header1 = _commitStateAs(alice, header0, 1, ROOT_1, ROOT_2); + + hevm.prank(multisig); + hevm.expectRevert("pending batches"); + rollup.initialize4(address(submitter)); + + hevm.prank(multisig); + rollup.revertBatch(header1, 1); + hevm.prank(multisig); + rollup.initialize4(address(submitter)); + } + + function _importGenesis() internal returns (bytes memory header0) { + header0 = new bytes(BatchHeaderCodecV0.BATCH_HEADER_LENGTH); + assembly { + mstore(add(header0, add(0x20, 25)), 1) + mstore(add(header0, add(0x20, 57)), 0x010657f37554c781402a22917dee2f75def7ab966d7b770905398eba3c444014) + mstore(add(header0, add(0x20, 121)), 1) + } + hevm.prank(multisig); + rollup.importGenesisBatch(header0); + } + + function _commitStateAs( + address actor, + bytes memory parentHeader, + uint256 batchIndex, + bytes32 previousRoot, + bytes32 postRoot + ) internal returns (bytes memory header) { + _setStoredBlobHash(batchIndex); + hevm.mockCall( + address(l1MessageQueueWithGasPriceOracle), + abi.encodeWithSignature("getFirstUnfinalizedMessageEnqueueTime()"), + abi.encode(block.timestamp) + ); + bytes32 withdrawalRoot = bytes32(batchIndex + 100); + IRollup.BatchDataInput memory input = IRollup.BatchDataInput( + 0, + parentHeader, + uint64(batchIndex), + 0, + previousRoot, + postRoot, + withdrawalRoot + ); + hevm.prank(actor); + rollup.commitState(input); + + header = _buildStateHeader(batchIndex, previousRoot, postRoot, withdrawalRoot); + assertEq(rollup.committedBatches(batchIndex), keccak256(header)); + } + + function _buildStateHeader( + uint256 batchIndex, + bytes32 previousRoot, + bytes32 postRoot, + bytes32 withdrawalRoot + ) internal view returns (bytes memory header) { + header = new bytes(BatchHeaderCodecV0.BATCH_HEADER_LENGTH); + bytes32 dataHash = _computeDataHash(uint64(batchIndex), 0); + bytes32 parentHash = rollup.committedBatches(batchIndex - 1); + assembly { + let p := add(header, 0x20) + mstore(p, 0) + mstore(add(p, 1), shl(192, batchIndex)) + mstore(add(p, 25), dataHash) + mstore(add(p, 57), 0x010657f37554c781402a22917dee2f75def7ab966d7b770905398eba3c444014) + mstore(add(p, 89), previousRoot) + mstore(add(p, 121), postRoot) + mstore(add(p, 153), withdrawalRoot) + mstore(add(p, 185), 0) + mstore(add(p, 217), parentHash) + } + } +} + /// @dev Tests for Rollup V2 multi-blob batch header support (simplified: 257-byte header with aggregated blob hash). contract RollupCommitBatchV2Test is L1MessageBaseTest { bytes32 public stateRoot = bytes32(uint256(1)); - IRollup.BatchSignatureInput public batchSignatureInput; bytes public batchHeader0; bytes32 public batchHash0; function setUp() public virtual override { super.setUp(); - batchSignatureInput = IRollup.BatchSignatureInput( - uint256(0), - abi.encode(uint256(0), new address[](0), uint256(0), new address[](0), uint256(0), new address[](0)), - bytes("0x") - ); - // Register staker - hevm.deal(alice, 5 * STAKING_VALUE); - Types.StakerInfo memory stakerInfo = ffi.generateStakerInfo(alice); - address[] memory addrs = new address[](1); - addrs[0] = alice; - hevm.prank(multisig); - l1Staking.updateWhitelist(addrs, new address[](0)); - hevm.prank(alice); - l1Staking.register{value: STAKING_VALUE}(stakerInfo.tmKey, stakerInfo.blsKey); + _registerAndStakeSubmitter(alice); // Import genesis batch (V0) bytes memory _genesis = new bytes(249); @@ -1557,7 +1726,7 @@ contract RollupCommitBatchV2Test is L1MessageBaseTest { Rollup Integration Tests //////////////////////////////////////////////////////////////*/ - /// @dev commitBatch with V2 requires at least 1 blob (reverts in test env where blobhash(0)=0). + /// @dev commitBatch with must carry blob (reverts in test env where blobhash(0)=0). function test_commitBatchV2_reverts_without_blob() public { IRollup.BatchDataInput memory batchDataInput = IRollup.BatchDataInput({ version: 2, @@ -1570,8 +1739,8 @@ contract RollupCommitBatchV2Test is L1MessageBaseTest { }); hevm.prank(alice); - hevm.expectRevert("V2 requires at least 1 blob"); - rollup.commitBatch(batchDataInput, batchSignatureInput); + hevm.expectRevert("must carry blob"); + rollup.commitBatch(batchDataInput); } /// @dev V2 _loadBatchHeader accepts a valid 257-byte V2 header (uses V1 loader). diff --git a/contracts/contracts/test/Submitter.t.sol b/contracts/contracts/test/Submitter.t.sol new file mode 100644 index 000000000..c8637f63b --- /dev/null +++ b/contracts/contracts/test/Submitter.t.sol @@ -0,0 +1,461 @@ +// SPDX-License-Identifier: MIT +pragma solidity =0.8.24; + +import "forge-std/Test.sol"; +import {TransparentUpgradeableProxy} from "@openzeppelin/contracts/proxy/transparent/TransparentUpgradeableProxy.sol"; + +import {ISubmitter} from "../l1/rollup/ISubmitter.sol"; +import {Submitter} from "../l1/rollup/Submitter.sol"; + +contract LiabilityRollupMock { + enum ReturnMode { + Normal, + RevertCall, + Short, + Long, + BurnGas + } + + mapping(address submitter => uint256 count) public counts; + ReturnMode public returnMode; + + function setPendingBatchCount(address submitter, uint256 count) external { + counts[submitter] = count; + } + + function setReturnMode(ReturnMode mode) external { + returnMode = mode; + } + + function slash(ISubmitter submitterContract, address submitter) external returns (uint256) { + return submitterContract.slash(submitter); + } + + receive() external payable {} + + fallback() external payable { + require(msg.sig == bytes4(keccak256("pendingBatchCount(address)")), "unknown selector"); + ReturnMode mode = returnMode; + address submitter; + assembly { + submitter := calldataload(4) + } + uint256 count = counts[submitter]; + if (mode == ReturnMode.RevertCall) revert("getter reverted"); + if (mode == ReturnMode.BurnGas) { + assembly { + for {} 1 {} {} + } + } + assembly { + mstore(0, count) + } + if (mode == ReturnMode.Short) { + assembly { + return(0, 31) + } + } + if (mode == ReturnMode.Long) { + assembly { + mstore(32, 1) + return(0, 64) + } + } + assembly { + return(0, 32) + } + } +} + +contract RejectEther { + function stakeInto(Submitter target) external { + target.stake{value: address(this).balance}(); + } + + function claimFrom(Submitter target, address receiver) external { + target.claimCredit(receiver); + } + + receive() external payable { + revert("reject ether"); + } +} + +contract BurnRefundGas { + receive() external payable { + assembly { + for {} 1 {} {} + } + } +} + +contract ReentrantSubmitterActor { + Submitter internal immutable target; + bool public reentrySucceeded; + bytes32 public reentryErrorHash; + + constructor(Submitter target_) { + target = target_; + } + + function stakeInto() external { + target.stake{value: address(this).balance}(); + } + + function withdrawToSelf() external { + target.withdraw(address(this)); + } + + receive() external payable { + try target.claimWithdrawal(address(this)) { + reentrySucceeded = true; + } catch (bytes memory reason) { + reentryErrorHash = keccak256(reason); + } + } +} + +contract SubmitterTest is Test { + uint256 internal constant MINIMUM_STAKE = 1 ether; + uint256 internal constant CHALLENGE_DEPOSIT = 2 ether; + uint256 internal constant REWARD_PERCENTAGE = 25; + + address internal owner = address(0xA11CE); + address internal proxyAdmin = address(0xA11); + address internal alice = address(0xB0B); + address internal bob = address(0xCAFE); + + LiabilityRollupMock internal rollupMock; + Submitter internal implementation; + Submitter internal submitter; + + function setUp() public { + vm.deal(alice, 100 ether); + vm.deal(bob, 100 ether); + rollupMock = new LiabilityRollupMock(); + implementation = new Submitter(); + TransparentUpgradeableProxy proxy = new TransparentUpgradeableProxy( + address(implementation), + proxyAdmin, + abi.encodeCall( + Submitter.initialize, (owner, address(rollupMock), MINIMUM_STAKE, CHALLENGE_DEPOSIT, REWARD_PERCENTAGE) + ) + ); + submitter = Submitter(payable(address(proxy))); + } + + function test_initialize_proxyAndImplementationProtection() public { + assertEq(submitter.owner(), owner); + assertEq(submitter.rollupContract(), address(rollupMock)); + assertEq(submitter.minimumStake(), MINIMUM_STAKE); + assertEq(submitter.challengeDeposit(), CHALLENGE_DEPOSIT); + assertEq(submitter.rewardPercentage(), REWARD_PERCENTAGE); + + vm.expectRevert("Initializable: contract is already initialized"); + implementation.initialize(owner, address(rollupMock), 1, 1, 1); + + vm.prank(owner); + vm.expectRevert("Initializable: contract is already initialized"); + submitter.initialize(owner, address(rollupMock), 1, 1, 1); + } + + function test_initialize_rejectsInvalidParameters() public { + _expectProxyInitializationRevert(address(0), address(rollupMock), 1, 1, 1, "invalid owner"); + _expectProxyInitializationRevert(owner, address(0), 1, 1, 1, "invalid rollup contract"); + _expectProxyInitializationRevert(owner, alice, 1, 1, 1, "invalid rollup contract"); + _expectProxyInitializationRevert(owner, address(rollupMock), 0, 1, 1, "invalid minimum stake"); + _expectProxyInitializationRevert(owner, address(rollupMock), 1, 0, 1, "invalid challenge deposit"); + _expectProxyInitializationRevert(owner, address(rollupMock), 1, 1, 0, "invalid reward percentage"); + _expectProxyInitializationRevert(owner, address(rollupMock), 1, 1, 101, "invalid reward percentage"); + } + + function test_addStakeAndDynamicMinimum() public { + vm.prank(alice); + vm.expectRevert("Ownable: caller is not the owner"); + submitter.addSubmitter(alice); + + vm.prank(owner); + submitter.addSubmitter(alice); + assertFalse(submitter.isActive(alice)); + + vm.prank(alice); + vm.expectRevert("below minimum stake"); + submitter.stake{value: MINIMUM_STAKE - 1}(); + + vm.prank(alice); + submitter.stake{value: MINIMUM_STAKE}(); + assertTrue(submitter.isActive(alice)); + + vm.prank(owner); + submitter.setMinimumStake(2 ether); + assertFalse(submitter.isActive(alice)); + + vm.prank(alice); + vm.expectRevert("below minimum stake"); + submitter.stake{value: 0.5 ether}(); + vm.prank(alice); + submitter.stake{value: 1 ether}(); + assertTrue(submitter.isActive(alice)); + + vm.prank(owner); + submitter.setMinimumStake(3 ether); + assertFalse(submitter.isActive(alice)); + vm.prank(owner); + submitter.setMinimumStake(2 ether); + assertTrue(submitter.isActive(alice)); + } + + function test_multipleSubmittersCanBeActive() public { + _registerAndStake(alice, MINIMUM_STAKE); + _registerAndStake(bob, MINIMUM_STAKE + 1); + assertTrue(submitter.isActive(alice)); + assertTrue(submitter.isActive(bob)); + } + + function test_withdraw_noLiabilityPaysAndRequiresFreshRegistration() public { + _registerAndStake(alice, MINIMUM_STAKE); + uint256 before = bob.balance; + + vm.prank(alice); + submitter.withdraw(bob); + + assertEq(bob.balance - before, MINIMUM_STAKE); + assertEq(submitter.stakeOf(alice), 0); + assertFalse(submitter.exiting(alice)); + assertFalse(submitter.registered(alice)); + vm.prank(alice); + vm.expectRevert("not stakeable"); + submitter.stake{value: MINIMUM_STAKE}(); + } + + function test_remove_pendingExitRejectsDuplicateAddUntilClaimed() public { + vm.prank(owner); + submitter.addSubmitter(alice); + vm.prank(owner); + vm.expectRevert("invalid submitter"); + submitter.addSubmitter(alice); + + vm.prank(alice); + submitter.stake{value: MINIMUM_STAKE}(); + rollupMock.setPendingBatchCount(alice, 1); + vm.prank(owner); + submitter.removeSubmitter(alice); + + assertFalse(submitter.registered(alice)); + assertTrue(submitter.exiting(alice)); + assertEq(submitter.stakeOf(alice), MINIMUM_STAKE); + vm.prank(owner); + vm.expectRevert("invalid submitter"); + submitter.addSubmitter(alice); + vm.prank(alice); + vm.expectRevert("pending batch liability"); + submitter.claimWithdrawal(alice); + + rollupMock.setPendingBatchCount(alice, 0); + vm.prank(alice); + submitter.claimWithdrawal(alice); + vm.prank(owner); + submitter.addSubmitter(alice); + assertTrue(submitter.registered(alice)); + } + + function test_exit_keepsStakeSlashable_untilAllBatchesTerminate() public { + _registerAndStake(alice, MINIMUM_STAKE); + rollupMock.setPendingBatchCount(alice, 2); + + vm.prank(alice); + submitter.withdraw(alice); + assertFalse(submitter.isActive(alice)); + assertTrue(submitter.exiting(alice)); + assertEq(submitter.stakeOf(alice), MINIMUM_STAKE); + + uint256 reward = rollupMock.slash(submitter, alice); + assertEq(reward, MINIMUM_STAKE / 4); + assertEq(submitter.stakeOf(alice), 0); + assertTrue(submitter.exiting(alice)); + assertFalse(submitter.registered(alice)); + + rollupMock.setPendingBatchCount(alice, 0); + vm.prank(alice); + submitter.claimWithdrawal(alice); + assertFalse(submitter.exiting(alice)); + vm.prank(alice); + vm.expectRevert("not stakeable"); + submitter.stake{value: MINIMUM_STAKE}(); + + vm.prank(owner); + submitter.addSubmitter(alice); + vm.prank(alice); + submitter.stake{value: MINIMUM_STAKE}(); + assertTrue(submitter.isActive(alice)); + } + + function test_withdraw_getterFailuresFailClosed() public { + LiabilityRollupMock.ReturnMode[4] memory modes = [ + LiabilityRollupMock.ReturnMode.RevertCall, + LiabilityRollupMock.ReturnMode.Short, + LiabilityRollupMock.ReturnMode.Long, + LiabilityRollupMock.ReturnMode.BurnGas + ]; + for (uint256 i; i < modes.length; ++i) { + address actor = address(uint160(0x1000 + i)); + vm.deal(actor, 10 ether); + _registerAndStake(actor, MINIMUM_STAKE); + rollupMock.setReturnMode(modes[i]); + vm.prank(actor); + submitter.withdraw(actor); + assertFalse(submitter.isActive(actor)); + assertTrue(submitter.exiting(actor)); + assertEq(submitter.stakeOf(actor), MINIMUM_STAKE); + rollupMock.setReturnMode(LiabilityRollupMock.ReturnMode.Normal); + vm.prank(actor); + submitter.claimWithdrawal(actor); + } + } + + function test_receiverFailureCreditsWithoutBlockingRemove() public { + RejectEther actor = new RejectEther(); + vm.deal(address(actor), MINIMUM_STAKE); + vm.prank(owner); + submitter.addSubmitter(address(actor)); + actor.stakeInto(submitter); + + vm.prank(owner); + submitter.removeSubmitter(address(actor)); + assertFalse(submitter.registered(address(actor))); + assertFalse(submitter.exiting(address(actor))); + assertEq(submitter.claimable(address(actor)), MINIMUM_STAKE); + + uint256 before = bob.balance; + actor.claimFrom(submitter, bob); + assertEq(bob.balance - before, MINIMUM_STAKE); + assertEq(submitter.claimable(address(actor)), 0); + } + + function test_withdraw_revertingAndGasBurningReceiversBecomeCredit() public { + RejectEther reject = new RejectEther(); + BurnRefundGas burner = new BurnRefundGas(); + + _registerAndStake(alice, MINIMUM_STAKE); + vm.prank(alice); + submitter.withdraw(address(reject)); + assertEq(submitter.claimable(alice), MINIMUM_STAKE); + + _registerAndStake(bob, MINIMUM_STAKE); + vm.prank(bob); + submitter.withdraw(address(burner)); + assertEq(submitter.claimable(bob), MINIMUM_STAKE); + } + + function test_withdraw_receiverReentryIsBlocked() public { + ReentrantSubmitterActor actor = new ReentrantSubmitterActor(submitter); + vm.deal(address(actor), MINIMUM_STAKE); + vm.prank(owner); + submitter.addSubmitter(address(actor)); + actor.stakeInto(); + + actor.withdrawToSelf(); + + assertFalse(actor.reentrySucceeded()); + assertEq( + actor.reentryErrorHash(), + keccak256(abi.encodeWithSignature("Error(string)", "ReentrancyGuard: reentrant call")) + ); + assertEq(address(actor).balance, MINIMUM_STAKE); + assertEq(submitter.stakeOf(address(actor)), 0); + } + + function test_claimCreditFailureRollsBackCredit() public { + RejectEther reject = new RejectEther(); + _registerAndStake(alice, MINIMUM_STAKE); + vm.prank(alice); + submitter.withdraw(address(reject)); + + vm.prank(alice); + vm.expectRevert("ETH transfer failed"); + submitter.claimCredit(address(reject)); + assertEq(submitter.claimable(alice), MINIMUM_STAKE); + } + + function test_slashPermissionsZeroAndOwnerAccounting() public { + _registerAndStake(alice, 4 ether); + + vm.prank(owner); + vm.expectRevert("only rollup contract"); + submitter.slash(alice); + assertEq(rollupMock.slash(submitter, address(0)), 0); + + uint256 rollupBefore = address(rollupMock).balance; + assertEq(rollupMock.slash(submitter, alice), 1 ether); + assertEq(address(rollupMock).balance - rollupBefore, 1 ether); + assertEq(submitter.slashRemaining(), 3 ether); + assertEq(address(submitter).balance, 3 ether); + + uint256 ownerBefore = owner.balance; + vm.prank(owner); + submitter.claimSlashRemaining(owner); + assertEq(owner.balance - ownerBefore, 3 ether); + assertEq(submitter.slashRemaining(), 0); + } + + function test_parameterUpdatesValidateBoundsAndPermissions() public { + vm.prank(alice); + vm.expectRevert("Ownable: caller is not the owner"); + submitter.updateChallengeDeposit(3 ether); + + vm.startPrank(owner); + vm.expectRevert("invalid minimum stake"); + submitter.setMinimumStake(0); + vm.expectRevert("invalid challenge deposit"); + submitter.updateChallengeDeposit(CHALLENGE_DEPOSIT); + vm.expectRevert("invalid reward percentage"); + submitter.updateRewardPercentage(101); + submitter.updateChallengeDeposit(3 ether); + submitter.updateRewardPercentage(50); + vm.stopPrank(); + + assertEq(submitter.challengeDeposit(), 3 ether); + assertEq(submitter.rewardPercentage(), 50); + } + + function testFuzz_accountingConservation(uint96 aliceStake, uint96 bobStake) public { + aliceStake = uint96(bound(aliceStake, MINIMUM_STAKE, 20 ether)); + bobStake = uint96(bound(bobStake, MINIMUM_STAKE, 20 ether)); + _registerAndStake(alice, aliceStake); + _registerAndStake(bob, bobStake); + + RejectEther reject = new RejectEther(); + vm.prank(alice); + submitter.withdraw(address(reject)); + rollupMock.slash(submitter, bob); + + uint256 tracked = submitter.stakeOf(alice) + submitter.stakeOf(bob); + tracked += submitter.claimable(alice) + submitter.claimable(bob); + tracked += submitter.slashRemaining(); + assertLe(tracked, address(submitter).balance); + } + + function _registerAndStake(address actor, uint256 amount) internal { + vm.prank(owner); + submitter.addSubmitter(actor); + vm.prank(actor); + submitter.stake{value: amount}(); + } + + function _expectProxyInitializationRevert( + address owner_, + address rollup_, + uint256 minimumStake_, + uint256 deposit_, + uint256 percentage_, + string memory reason + ) internal { + Submitter freshImplementation = new Submitter(); + vm.expectRevert(bytes(reason)); + new TransparentUpgradeableProxy( + address(freshImplementation), + proxyAdmin, + abi.encodeCall(Submitter.initialize, (owner_, rollup_, minimumStake_, deposit_, percentage_)) + ); + } +} diff --git a/contracts/contracts/test/base/L1MessageBase.t.sol b/contracts/contracts/test/base/L1MessageBase.t.sol index d2fa9db4e..0cd82ba51 100644 --- a/contracts/contracts/test/base/L1MessageBase.t.sol +++ b/contracts/contracts/test/base/L1MessageBase.t.sol @@ -7,23 +7,20 @@ import {CommonTest} from "./CommonTest.t.sol"; import {Whitelist} from "../../libraries/common/Whitelist.sol"; import {L1CrossDomainMessenger} from "../../l1/L1CrossDomainMessenger.sol"; import {L1MessageQueueWithGasPriceOracle} from "../../l1/rollup/L1MessageQueueWithGasPriceOracle.sol"; -import {L1Staking} from "../../l1/staking/L1Staking.sol"; +import {Submitter} from "../../l1/rollup/Submitter.sol"; import {Rollup} from "../../l1/rollup/Rollup.sol"; import {IRollup} from "../../l1/rollup/IRollup.sol"; import {BatchHeaderCodecV0} from "../../libraries/codec/BatchHeaderCodecV0.sol"; import {MockZkEvmVerifier} from "../../mock/MockZkEvmVerifier.sol"; contract L1MessageBaseTest is CommonTest { - // Staking config - L1Staking public l1Staking; - L1Staking public l1StakingImpl; + // Submitter config + Submitter public submitter; + Submitter public submitterImpl; uint256 public constant STAKING_VALUE = 1e18; // 1 eth uint256 public constant CHALLENGE_DEPOSIT = 1e18; // 1 eth - uint256 public constant LOCK_BLOCKS = 3; uint256 public rewardPercentage = 20; - uint32 public defaultGasLimitAdd = 1000000; - uint32 public defaultGasLimitRemove = 10000000; // Rollup config Rollup public rollup; @@ -92,6 +89,14 @@ contract L1MessageBaseTest is CommonTest { ); } + function _registerAndStakeSubmitter(address account) internal { + hevm.deal(account, 5 * STAKING_VALUE); + hevm.prank(multisig); + submitter.addSubmitter(account); + hevm.prank(account); + submitter.stake{value: STAKING_VALUE}(); + } + /// @dev Build V0 batch header for commitBatchWithProof (blob = ZERO_VERSIONED_HASH). function _createBatchHeaderV0ForProof( uint256 batchIndex, @@ -144,7 +149,7 @@ contract L1MessageBaseTest is CommonTest { address(multisig), new bytes(0) ); - TransparentUpgradeableProxy l1StakingProxy = new TransparentUpgradeableProxy( + TransparentUpgradeableProxy submitterProxy = new TransparentUpgradeableProxy( address(emptyContract), address(multisig), new bytes(0) @@ -158,7 +163,7 @@ contract L1MessageBaseTest is CommonTest { address(alice) ); l1CrossDomainMessengerImpl = new L1CrossDomainMessenger(); - l1StakingImpl = new L1Staking(payable(l1CrossDomainMessengerProxy)); + submitterImpl = new Submitter(); // upgrade and initialize ITransparentUpgradeableProxy(address(rollupProxy)).upgradeToAndCall( @@ -166,7 +171,7 @@ contract L1MessageBaseTest is CommonTest { abi.encodeCall( Rollup.initialize, ( - address(l1StakingProxy), + address(submitterProxy), address(l1MessageQueueWithGasPriceOracleProxy), // _messageQueue address(verifier), // _verifier finalizationPeriodSeconds, // _finalizationPeriodSeconds @@ -196,18 +201,16 @@ contract L1MessageBaseTest is CommonTest { ) ) ); - ITransparentUpgradeableProxy(address(l1StakingProxy)).upgradeToAndCall( - address(l1StakingImpl), + ITransparentUpgradeableProxy(address(submitterProxy)).upgradeToAndCall( + address(submitterImpl), abi.encodeCall( - L1Staking.initialize, + Submitter.initialize, ( + multisig, address(rollupProxy), STAKING_VALUE, CHALLENGE_DEPOSIT, - LOCK_BLOCKS, - rewardPercentage, - defaultGasLimitAdd, - defaultGasLimitRemove + rewardPercentage ) ) ); @@ -217,9 +220,9 @@ contract L1MessageBaseTest is CommonTest { l1MessageQueueWithGasPriceOracle = L1MessageQueueWithGasPriceOracle( address(l1MessageQueueWithGasPriceOracleProxy) ); - l1Staking = L1Staking(address(l1StakingProxy)); + submitter = Submitter(payable(address(submitterProxy))); - _changeAdmin(address(l1Staking)); + _changeAdmin(address(submitter)); _changeAdmin(address(rollup)); _changeAdmin(address(l1CrossDomainMessenger)); _changeAdmin(address(l1MessageQueueWithGasPriceOracle)); From e159e51630e3fb108d7f2685ce126d0ef65232a9 Mon Sep 17 00:00:00 2001 From: Segue Date: Tue, 11 Aug 2026 20:31:37 +0800 Subject: [PATCH 03/16] feat(deploy): install submitter and retire legacy staking --- bindings/Makefile | 11 +- bindings/bin/l1staking_deployed.hex | 1 - bindings/bin/rollup_deployed.hex | 2 +- bindings/bin/submitter_deployed.hex | 1 + bindings/bindings/l1staking.go | 3445 ----------------- bindings/bindings/l1staking_more.go | 25 - bindings/bindings/rollup.go | 408 +- bindings/bindings/rollup_more.go | 4 +- bindings/bindings/submitter.go | 2863 ++++++++++++++ bindings/bindings/submitter_more.go | 25 + contracts/deploy/013-DeployProxys.ts | 10 +- contracts/deploy/014-DeployImpls.ts | 43 +- contracts/deploy/015-SubmitterInstall.ts | 109 + contracts/deploy/016-RollupInit.ts | 10 +- contracts/deploy/018-StakingInit.ts | 128 - contracts/deploy/019-AdminTransfer.ts | 10 +- contracts/deploy/020-ContractInit.ts | 44 +- contracts/deploy/021-StakingRegister.ts | 57 - contracts/deploy/021-SubmitterRegister.ts | 38 + contracts/deploy/index.ts | 10 +- contracts/hardhat.config.ts | 8 +- .../retire-l1-staking.spec.ts | 122 + .../integration-test/submitter-deploy.spec.ts | 72 + .../legacy-l1-staking-retirement.example.json | 18 + contracts/scripts/retire-l1-staking.ts | 664 ++++ contracts/src/deploy-config/holesky.ts | 16 +- contracts/src/deploy-config/hoodi.ts | 16 +- contracts/src/deploy-config/l1.ts | 16 +- contracts/src/deploy-config/qanetl1.ts | 16 +- contracts/src/deploy-config/sepolia.ts | 8 +- contracts/src/deploy-config/testnetl1.ts | 8 +- contracts/src/deploy-utils.ts | 5 +- contracts/src/types.ts | 15 +- contracts/tasks/deploy.ts | 54 +- contracts/tasks/staking_upgrade.ts | 901 ----- 35 files changed, 4327 insertions(+), 4856 deletions(-) delete mode 100644 bindings/bin/l1staking_deployed.hex create mode 100644 bindings/bin/submitter_deployed.hex delete mode 100644 bindings/bindings/l1staking.go delete mode 100644 bindings/bindings/l1staking_more.go create mode 100644 bindings/bindings/submitter.go create mode 100644 bindings/bindings/submitter_more.go create mode 100644 contracts/deploy/015-SubmitterInstall.ts delete mode 100644 contracts/deploy/018-StakingInit.ts delete mode 100644 contracts/deploy/021-StakingRegister.ts create mode 100644 contracts/deploy/021-SubmitterRegister.ts create mode 100644 contracts/integration-test/retire-l1-staking.spec.ts create mode 100644 contracts/integration-test/submitter-deploy.spec.ts create mode 100644 contracts/ops/legacy-l1-staking-retirement.example.json create mode 100644 contracts/scripts/retire-l1-staking.ts delete mode 100644 contracts/tasks/staking_upgrade.ts diff --git a/bindings/Makefile b/bindings/Makefile index a98e10d37..616ebf372 100644 --- a/bindings/Makefile +++ b/bindings/Makefile @@ -5,7 +5,7 @@ pkg := bindings all: version mkdir bindings more bindings: \ - l1-staking-bindings \ + submitter-bindings \ l1-cross-domain-messenger-bindings \ l1-standard-erc20-gateway-bindings \ l1-eth-gateway-bindings \ @@ -65,9 +65,9 @@ compile-forge: ./compile.sh forge ## L1 -# Staking -l1-staking-bindings: compile - ./gen_bindings.sh contracts/l1/staking/L1Staking.sol:L1Staking $(pkg) +# Submitter +submitter-bindings: compile + ./gen_bindings.sh contracts/l1/rollup/Submitter.sol:Submitter $(pkg) # CrossDomainMessenger l1-cross-domain-messenger-bindings: compile @@ -194,7 +194,7 @@ more: compile-forge go run ./gen/main.go \ -artifacts ../contracts/artifacts \ -out ./bindings \ - -contracts ProxyAdmin,TransparentUpgradeableProxy,L2TokenRegistry,L1MessageQueueWithGasPriceOracle,L1USDCGateway,L1Staking,L1CrossDomainMessenger,L1StandardERC20Gateway,L1ETHGateway,L1ERC20Gateway,L1GatewayRouter,L1WETHGateway,L1LidoGateway,Rollup,MultipleVersionRollupVerifier,L2CrossDomainMessenger,GasPriceOracle,L2ToL1MessagePasser,L2TxFeeVault,Sequencer,Gov,Distribute,L2Staking,Record,L2ETHGateway,L2StandardERC20Gateway,L2GatewayRouter,L2WETHGateway,L2ERC20Gateway,L2ERC721Gateway,L2ERC1155Gateway,L2LidoGateway,MorphToken,MorphStandardERC20,MorphStandardERC20Factory,WrappedEther,L2USDCGateway,EnforcedTxGateway,L1ERC721Gateway,L1ERC1155Gateway,L1ReverseCustomGateway,L2WithdrawLockERC20Gateway,L2ReverseCustomGateway,Whitelist,ZkEvmVerifierV1,L2WstETHToken \ + -contracts ProxyAdmin,TransparentUpgradeableProxy,L2TokenRegistry,L1MessageQueueWithGasPriceOracle,L1USDCGateway,Submitter,L1CrossDomainMessenger,L1StandardERC20Gateway,L1ETHGateway,L1ERC20Gateway,L1GatewayRouter,L1WETHGateway,L1LidoGateway,Rollup,MultipleVersionRollupVerifier,L2CrossDomainMessenger,GasPriceOracle,L2ToL1MessagePasser,L2TxFeeVault,Sequencer,Gov,Distribute,L2Staking,Record,L2ETHGateway,L2StandardERC20Gateway,L2GatewayRouter,L2WETHGateway,L2ERC20Gateway,L2ERC721Gateway,L2ERC1155Gateway,L2LidoGateway,MorphToken,MorphStandardERC20,MorphStandardERC20Factory,WrappedEther,L2USDCGateway,EnforcedTxGateway,L1ERC721Gateway,L1ERC1155Gateway,L1ReverseCustomGateway,L2WithdrawLockERC20Gateway,L2ReverseCustomGateway,Whitelist,ZkEvmVerifierV1,L2WstETHToken \ -package bindings mkdir: @@ -211,4 +211,3 @@ test: lint: GOBIN=$(PWD)/build/bin go run ../build/lint.go .PHONY: lint - diff --git a/bindings/bin/l1staking_deployed.hex b/bindings/bin/l1staking_deployed.hex deleted file mode 100644 index 401476f33..000000000 --- a/bindings/bin/l1staking_deployed.hex +++ /dev/null @@ -1 +0,0 @@ -0x6080604052600436106102f8575f3560e01c80637a9262a211610191578063ab8c53dc116100dc578063d51c90a911610087578063e2a6ad5f11610062578063e2a6ad5f1461090c578063f2fde38b1461092b578063f4fe7fef1461094a575f80fd5b8063d51c90a91461087f578063dd4785f514610894578063df155033146108d5575f80fd5b8063c7cd469a116100b7578063c7cd469a14610822578063cde4cd1114610841578063d096c3c614610860575f80fd5b8063ab8c53dc146107cf578063ae81de53146107e4578063bfa02ba914610803575f80fd5b8063927ede2d1161013c5780639fb82db9116101175780639fb82db91461077d578063a3066aab14610791578063a4f209b0146107b0575f80fd5b8063927ede2d146106f05780639b19251a146107235780639d48f41714610751575f80fd5b80638a565ac31161016c5780638a565ac3146106865780638da5cb5b146106a55780639168ae72146106c2575f80fd5b80637a9262a2146106085780638142951a14610634578063831cfb5814610653575f80fd5b806341de239b11610251578063692c565b116101fc57806374fe27b7116101d757806374fe27b714610588578063797adbde146105ae5780637a4e87c3146105cd575f80fd5b8063692c565b146105265780636f1e853314610555578063715018a614610574575f80fd5b80634d64903a1161022c5780634d64903a146104dd57806352d472eb146104f25780636801579114610507575f80fd5b806341de239b1461048857806343352d611461049d57806345bc4d10146104be575f80fd5b8063320ff860116102b15780633cb747bf1161028c5780633cb747bf1461042e5780633ccfd60b146104605780633ee2a1f914610474575f80fd5b8063320ff860146103b257806335928991146103d15780633a9bbede146103f0575f80fd5b80632a28e5a3116102e15780632a28e5a31461033a5780632e407a6f1461034f578063303afb9e1461037b575f80fd5b80630d13fd7b146102fc5780632108db3514610325575b5f80fd5b348015610307575f80fd5b506103126101a65481565b6040519081526020015b60405180910390f35b348015610330575f80fd5b50610312609c5481565b61034d610348366004613407565b610969565b005b34801561035a575f80fd5b50610312610369366004613461565b6101a16020525f908152604090205481565b348015610386575f80fd5b5061039a610395366004613481565b610d64565b6040516001600160a01b03909116815260200161031c565b3480156103bd575f80fd5b5061034d6103cc366004613498565b610d83565b3480156103dc575f80fd5b5061034d6103eb366004613481565b61109e565b3480156103fb575f80fd5b5061041e61040a366004613461565b609f6020525f908152604090205460ff1681565b604051901515815260200161031c565b348015610439575f80fd5b507f000000000000000000000000000000000000000000000000000000000000000061039a565b34801561046b575f80fd5b5061034d61114b565b34801561047f575f80fd5b5061034d611317565b348015610493575f80fd5b5061031260995481565b3480156104a8575f80fd5b506104b1611329565b60405161031c9190613540565b3480156104c9575f80fd5b506103126104d8366004613481565b61136f565b3480156104e8575f80fd5b5061031260985481565b3480156104fd575f80fd5b50610312609a5481565b348015610512575f80fd5b5061041e610521366004613461565b611675565b348015610531575f80fd5b5061041e610540366004613481565b6101a46020525f908152604090205460ff1681565b348015610560575f80fd5b5061041e61056f366004613461565b611710565b34801561057f575f80fd5b5061034d611789565b348015610593575f80fd5b5061041e6105a23660046135c2565b60019695505050505050565b3480156105b9575f80fd5b5061039a6105c8366004613481565b61179a565b3480156105d8575f80fd5b5061041e6105e7366004613668565b80516020818301810180516101a38252928201919093012091525460ff1681565b348015610613575f80fd5b50610312610622366004613461565b6101a56020525f908152604090205481565b34801561063f575f80fd5b5061034d61064e3660046136a2565b6117c3565b34801561065e575f80fd5b5061039a7f000000000000000000000000000000000000000000000000000000000000000081565b348015610691575f80fd5b5061034d6106a0366004613481565b611c85565b3480156106b0575f80fd5b506033546001600160a01b031661039a565b3480156106cd575f80fd5b506106e16106dc366004613461565b611d28565b60405161031c9392919061373d565b3480156106fb575f80fd5b5061039a7f000000000000000000000000000000000000000000000000000000000000000081565b34801561072e575f80fd5b5061041e61073d366004613461565b609e6020525f908152604090205460ff1681565b34801561075c575f80fd5b5061077061076b366004613481565b611ddb565b60405161031c919061376d565b348015610788575f80fd5b50610770611ef8565b34801561079c575f80fd5b5061034d6107ab366004613461565b6120a6565b3480156107bb575f80fd5b5061034d6107ca366004613481565b6121d6565b3480156107da575f80fd5b50610312609d5481565b3480156107ef575f80fd5b5061034d6107fe366004613481565b612286565b34801561080e575f80fd5b5060975461039a906001600160a01b031681565b34801561082d575f80fd5b5061034d61083c3660046137b9565b612329565b34801561084c575f80fd5b5061034d61085b366004613461565b6124ca565b34801561086b575f80fd5b5061031261087a366004613461565b612538565b34801561088a575f80fd5b50610312609b5481565b34801561089f575f80fd5b506108c36108ae366004613461565b61019f6020525f908152604090205460ff1681565b60405160ff909116815260200161031c565b3480156108e0575f80fd5b5061041e6108ef366004613461565b6001600160a01b03165f9081526101a16020526040902054151590565b348015610917575f80fd5b50610312610926366004613820565b6125b2565b348015610936575f80fd5b5061034d610945366004613461565b6126e4565b348015610955575f80fd5b5061034d610964366004613481565b612771565b335f818152609e602052604090205460ff166109cc5760405162461bcd60e51b815260206004820152601060248201527f6e6f7420696e2077686974656c6973740000000000000000000000000000000060448201526064015b60405180910390fd5b335f9081526101a260205260409020546001600160a01b031615610a325760405162461bcd60e51b815260206004820152601260248201527f616c72656164792072656769737465726564000000000000000000000000000060448201526064016109c3565b8215801590610a5057505f8381526101a4602052604090205460ff16155b610a9c5760405162461bcd60e51b815260206004820152601960248201527f696e76616c69642074656e6465726d696e74207075626b65790000000000000060448201526064016109c3565b8151610100148015610acf57506101a382604051610aba919061385f565b9081526040519081900360200190205460ff16155b610b1b5760405162461bcd60e51b815260206004820152601260248201527f696e76616c696420626c73207075626b6579000000000000000000000000000060448201526064016109c3565b6098543414610b6c5760405162461bcd60e51b815260206004820152601560248201527f696e76616c6964207374616b696e672076616c7565000000000000000000000060448201526064016109c3565b6040518060600160405280610b7e3390565b6001600160a01b03908116825260208083018790526040928301869052335f9081526101a28252839020845181547fffffffffffffffffffffffff00000000000000000000000000000000000000001693169290921782558301516001820155908201516002820190610bf1908261390f565b50905050610c04610bff3390565b612814565b60016101a383604051610c17919061385f565b90815260408051918290036020908101909220805493151560ff199485161790555f8681526101a49092529020805490911660011790557fb9c7babb56df9f2da4a30811a6c778e4e68af88b72712d56cf62c5516e20e199610c763390565b8484604051610c879392919061373d565b60405180910390a1335f9081526101a26020908152604091829020825160608101845281546001600160a01b03168152600182015492810192909252600281018054610d5f9484019190610cda9061387a565b80601f0160208091040260200160405190810160405280929190818152602001828054610d069061387a565b8015610d515780601f10610d2857610100808354040283529160200191610d51565b820191905f5260205f20905b815481529060010190602001808311610d3457829003601f168201915b505050505081525050612922565b505050565b60a08160ff8110610d73575f80fd5b01546001600160a01b0316905081565b610d8b612a4a565b5f5b815181101561105a57610db8828281518110610dab57610dab613a0d565b6020026020010151611675565b610e2a5760405162461bcd60e51b815260206004820152602160248201527f6f6e6c7920616374697665207374616b65722063616e2062652072656d6f766560448201527f640000000000000000000000000000000000000000000000000000000000000060648201526084016109c3565b6101a55f838381518110610e4057610e40613a0d565b60200260200101516001600160a01b03166001600160a01b031681526020019081526020015f20545f14610eb65760405162461bcd60e51b815260206004820152600b60248201527f7769746864726177696e6700000000000000000000000000000000000000000060448201526064016109c3565b609954610ec39043613a67565b6101a55f848481518110610ed957610ed9613a0d565b60200260200101516001600160a01b03166001600160a01b031681526020019081526020015f2081905550610f26828281518110610f1957610f19613a0d565b6020026020010151612aa4565b818181518110610f3857610f38613a0d565b60200260200101516001600160a01b03167f7084f5476618d8e60b11ef0d7d3f06914655adb8793e28ff7f018d4c76d505d56101a55f858581518110610f8057610f80613a0d565b60200260200101516001600160a01b03166001600160a01b031681526020019081526020015f2054604051610fb791815260200190565b60405180910390a2609e5f838381518110610fd457610fd4613a0d565b60200260200101516001600160a01b03166001600160a01b031681526020019081526020015f205f6101000a81549060ff02191690556001609f5f84848151811061102157611021613a0d565b6020908102919091018101516001600160a01b031682528101919091526040015f20805460ff1916911515919091179055600101610d8d565b507f3f446646c03e618be8238a586960d6d625f35c653cdca1ef59609993e8ed2c848160405161108a919061376d565b60405180910390a161109b81612b96565b50565b6110a6612a4a565b5f811180156110b857506101a6548114155b6111045760405162461bcd60e51b815260206004820152601f60248201527f696e76616c6964206368616c6c656e6765206465706f7369742076616c75650060448201526064016109c3565b6101a680549082905560408051828152602081018490527f36f971a40478225aeb80cfbf5e80306e8cb76d3bf7d56fdc5e490945cddb7d5591015b60405180910390a15050565b61115433611675565b6111a05760405162461bcd60e51b815260206004820152601260248201527f6f6e6c7920616374697665207374616b6572000000000000000000000000000060448201526064016109c3565b335f9081526101a56020526040902054156111fd5760405162461bcd60e51b815260206004820152600b60248201527f7769746864726177696e6700000000000000000000000000000000000000000060448201526064016109c3565b60995461120a9043613a67565b335f8181526101a5602052604090209190915561122690612aa4565b335f8181526101a560209081526040918290205491519182527f7084f5476618d8e60b11ef0d7d3f06914655adb8793e28ff7f018d4c76d505d5910160405180910390a2335f908152609e60209081526040808320805460ff19908116909155609f83528184208054909116600190811790915581518181528083019092529091828101908036833701905050905033815f815181106112c8576112c8613a0d565b60200260200101906001600160a01b031690816001600160a01b0316815250507f3f446646c03e618be8238a586960d6d625f35c653cdca1ef59609993e8ed2c848160405161108a919061376d565b61131f612a4a565b611327612c8e565b565b6113316132d4565b60408051611fe08101918290529060a09060ff9082845b81546001600160a01b03168152600190910190602001808311611348575050505050905090565b6097545f906001600160a01b0316336001600160a01b0316146113d45760405162461bcd60e51b815260206004820152601460248201527f6f6e6c7920726f6c6c757020636f6e747261637400000000000000000000000060448201526064016109c3565b6113dc612f59565b5f6113e683611ddb565b90505f805b8251811015611598575f6101a55f85848151811061140b5761140b613a0d565b60200260200101516001600160a01b03166001600160a01b031681526020019081526020015f2054111561148e576101a55f84838151811061144f5761144f613a0d565b60200260200101516001600160a01b03166001600160a01b031681526020019081526020015f205f9055609854826114879190613a67565b9150611590565b6114c88382815181106114a3576114a3613a0d565b60200260200101516001600160a01b03165f9081526101a16020526040902054151590565b611590576098546114d99083613a67565b91506114f0838281518110610f1957610f19613a0d565b609e5f84838151811061150557611505613a0d565b60200260200101516001600160a01b03166001600160a01b031681526020019081526020015f205f6101000a81549060ff02191690556001609f5f85848151811061155257611552613a0d565b60200260200101516001600160a01b03166001600160a01b031681526020019081526020015f205f6101000a81548160ff0219169083151502179055505b6001016113eb565b505f6064609a54836115aa9190613a7a565b6115b49190613a91565b90506115c08183613ac9565b609d5f8282546115d09190613a67565b90915550506097546115eb906001600160a01b031682612fb2565b7f654f4a61849f1b3ad10abb283d27f02d5fece7b820acc5a3b874713b58748b5a8360405161161a919061376d565b60405180910390a17f3f446646c03e618be8238a586960d6d625f35c653cdca1ef59609993e8ed2c8483604051611651919061376d565b60405180910390a161166283612b96565b925050506116706001606555565b919050565b6001600160a01b0381165f90815261019f602052604081205460ff16810361169e57505f919050565b6001600160a01b0382165f81815261019f602052604090205460a0906116c99060019060ff16613adc565b60ff1660ff81106116dc576116dc613a0d565b01546001600160a01b031614801561170a57506001600160a01b0382165f9081526101a16020526040902054155b92915050565b6001600160a01b0381165f90815261019f602052604081205460ff16810361173957505f919050565b6001600160a01b0382165f81815261019f602052604090205460a0906117649060019060ff16613adc565b60ff1660ff811061177757611777613a0d565b01546001600160a01b03161492915050565b611791612a4a565b6113275f613062565b6101a081815481106117aa575f80fd5b5f918252602090912001546001600160a01b0316905081565b5f54610100900460ff16158080156117e157505f54600160ff909116105b806117fa5750303b1580156117fa57505f5460ff166001145b61186c5760405162461bcd60e51b815260206004820152602e60248201527f496e697469616c697a61626c653a20636f6e747261637420697320616c72656160448201527f647920696e697469616c697a656400000000000000000000000000000000000060648201526084016109c3565b5f805460ff1916600117905580156118aa575f80547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ff166101001790555b6001600160a01b0388166119005760405162461bcd60e51b815260206004820152601760248201527f696e76616c696420726f6c6c757020636f6e747261637400000000000000000060448201526064016109c3565b5f871161194f5760405162461bcd60e51b815260206004820152601560248201527f696e76616c6964207374616b696e672076616c7565000000000000000000000060448201526064016109c3565b5f861161199e5760405162461bcd60e51b815260206004820152601f60248201527f696e76616c6964206368616c6c656e6765206465706f7369742076616c75650060448201526064016109c3565b5f85116119ed5760405162461bcd60e51b815260206004820152601e60248201527f696e76616c6964207769746864726177616c206c6f636b20626c6f636b73000060448201526064016109c3565b5f8311611a3c5760405162461bcd60e51b815260206004820152601c60248201527f696e76616c696420676173206c696d697420616464207374616b65720000000060448201526064016109c3565b5f8211611a8b5760405162461bcd60e51b815260206004820181905260248201527f696e76616c696420676173206c696d69742072656d6f7665207374616b65727360448201526064016109c3565b5f84118015611a9b575060648411155b611b0c5760405162461bcd60e51b8152602060048201526024808201527f696e76616c6964206368616c6c656e676572207265776172642070657263656e60448201527f746167650000000000000000000000000000000000000000000000000000000060648201526084016109c3565b611b146130cb565b611b1c61314f565b609780547fffffffffffffffffffffffff0000000000000000000000000000000000000000166001600160a01b038a16179055609a84905560988790556101a68690556099859055609b839055609c829055604080515f8152602081018590527f0ef80fb82bf5439b8591744c0fde771f5d93dce7a7970b1e9b7828cdc3970e9c910160405180910390a1604080515f8152602081018490527fdd4b37d1f14888147fe4be2cfaedcbf148fd07ececc856d0433241b8b6e4a7f7910160405180910390a1604080515f8152602081018690527fa46de936426e045703b2d34a292a19fde92b329018db8e0da750033876b655ba910160405180910390a18015611c7b575f80547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ff169055604051600181527f7f26b83ff96e1f2b6a682f133852f6798a09c465da95921460cefb38474024989060200160405180910390a15b5050505050505050565b611c8d612a4a565b5f81118015611c9e5750609c548114155b611cea5760405162461bcd60e51b815260206004820152601560248201527f696e76616c6964206e657720676173206c696d6974000000000000000000000060448201526064016109c3565b609c80549082905560408051828152602081018490527fdd4b37d1f14888147fe4be2cfaedcbf148fd07ececc856d0433241b8b6e4a7f7910161113f565b6101a26020525f90815260409020805460018201546002830180546001600160a01b03909316939192611d5a9061387a565b80601f0160208091040260200160405190810160405280929190818152602001828054611d869061387a565b8015611dd15780601f10611da857610100808354040283529160200191611dd1565b820191905f5260205f20905b815481529060010190602001808311611db457829003601f168201915b5050505050905083565b6060600182901c5f5b8115611e0b57611df5816001613a67565b9050611e02600183613ac9565b82169150611de4565b8067ffffffffffffffff811115611e2457611e2461333d565b604051908082528060200260200182016040528015611e4d578160200160208202803683370190505b5092505f60015b60ff8160ff1611611eef57600160ff82161b861615611edd5760a0611e7a600183613adc565b60ff1660ff8110611e8d57611e8d613a0d565b015485516001600160a01b0390911690869084908110611eaf57611eaf613a0d565b6001600160a01b0390921660209283029190910190910152611ed2826001613a67565b915082821015611eef575b80611ee781613af5565b915050611e54565b50505050919050565b6040805160ff80825261200082019092526060915f91829160208201611fe0803683370190505090505f5b60ff811015611fc7575f60a08260ff8110611f4057611f40613a0d565b01546001600160a01b031614801590611f8857506101a15f60a08360ff8110611f6b57611f6b613a0d565b01546001600160a01b0316815260208101919091526040015f2054155b15611fbf5782611f9781613b13565b9350506001828281518110611fae57611fae613a0d565b911515602092830291909101909101525b600101611f23565b505f8267ffffffffffffffff811115611fe257611fe261333d565b60405190808252806020026020018201604052801561200b578160200160208202803683370190505b5090505f805b60ff81101561209c5783818151811061202c5761202c613a0d565b6020026020010151156120945760a08160ff811061204c5761204c613a0d565b015483516001600160a01b039091169084908490811061206e5761206e613a0d565b6001600160a01b03909216602092830291909101909101528161209081613b13565b9250505b600101612011565b5090949350505050565b6120ae612f59565b335f9081526101a5602052604090205461210a5760405162461bcd60e51b815260206004820152601460248201527f7769746864726177616c206e6f7420657869737400000000000000000000000060448201526064016109c3565b335f9081526101a560205260409020544310156121695760405162461bcd60e51b815260206004820152601160248201527f7769746864726177616c206c6f636b656400000000000000000000000000000060448201526064016109c3565b335f9081526101a56020526040812055612181612c8e565b604080516001600160a01b0383168152905133917f89309c9b2aeaffbdce717113df9427298b20448c05919bf889e05f8c3094254b919081900360200190a26121cc81609854612fb2565b61109b6001606555565b6121de612a4a565b5f811180156121ee575060648111155b80156121fc5750609a548114155b6122485760405162461bcd60e51b815260206004820152601960248201527f696e76616c6964207265776172642070657263656e746167650000000000000060448201526064016109c3565b609a80549082905560408051828152602081018490527fa46de936426e045703b2d34a292a19fde92b329018db8e0da750033876b655ba910161113f565b61228e612a4a565b5f8111801561229f5750609b548114155b6122eb5760405162461bcd60e51b815260206004820152601560248201527f696e76616c6964206e657720676173206c696d6974000000000000000000000060448201526064016109c3565b609b80549082905560408051828152602081018490527f0ef80fb82bf5439b8591744c0fde771f5d93dce7a7970b1e9b7828cdc3970e9c910161113f565b612331612a4a565b5f5b8381101561242957609f5f86868481811061235057612350613a0d565b90506020020160208101906123659190613461565b6001600160a01b0316815260208101919091526040015f205460ff16156123ce5760405162461bcd60e51b815260206004820152600f60248201527f696e2072656d6f766564206c697374000000000000000000000000000000000060448201526064016109c3565b6001609e5f8787858181106123e5576123e5613a0d565b90506020020160208101906123fa9190613461565b6001600160a01b0316815260208101919091526040015f20805460ff1916911515919091179055600101612333565b505f5b8181101561248657609e5f84848481811061244957612449613a0d565b905060200201602081019061245e9190613461565b6001600160a01b0316815260208101919091526040015f20805460ff1916905560010161242c565b507fe375867e538b40218c1b3db2ccceaf875eb073e38b510449e6088c1539ac8622848484846040516124bc9493929190613b8f565b60405180910390a150505050565b6124d2612a4a565b6124da612f59565b609d546124e78282612fb2565b5f609d55604080516001600160a01b0384168152602081018390527fa1fefb6c5328a92a416e321ed50997303fe7135fd88c28b0592b21ce42b5cdd9910160405180910390a15061109b6001606555565b5f61254282611710565b61258e5760405162461bcd60e51b815260206004820152600e60248201527f696e76616c6964207374616b657200000000000000000000000000000000000060448201526064016109c3565b506001600160a01b03165f90815261019f6020526040902054600160ff9091161b90565b5f60ff8211156126045760405162461bcd60e51b815260206004820152601c60248201527f7374616b657273206c656e677468206f7574206f6620626f756e64730000000060448201526064016109c3565b5f5b828110156126dd5761263884848381811061262357612623613a0d565b905060200201602081019061056f9190613461565b6126845760405162461bcd60e51b815260206004820152600e60248201527f696e76616c6964207374616b657200000000000000000000000000000000000060448201526064016109c3565b61019f5f85858481811061269a5761269a613a0d565b90506020020160208101906126af9190613461565b6001600160a01b0316815260208101919091526040015f2054600160ff90911681901b929092179101612606565b5092915050565b6126ec612a4a565b6001600160a01b0381166127685760405162461bcd60e51b815260206004820152602660248201527f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160448201527f646472657373000000000000000000000000000000000000000000000000000060648201526084016109c3565b61109b81613062565b612779612a4a565b5f8111801561278a57506098548114155b6127d65760405162461bcd60e51b815260206004820152601560248201527f696e76616c6964207374616b696e672076616c7565000000000000000000000060448201526064016109c3565b609880549082905560408051828152602081018490527f91e3ed304d49776a78bbe0df6bf7156f5433763621e5e728a3c2adfe7de74368910161113f565b5f5b60ff8160ff1610156128d9575f60a060ff80841690811061283957612839613a0d565b01546001600160a01b0316036128d1578160a08260ff1660ff811061286057612860613a0d565b0180547fffffffffffffffffffffffff0000000000000000000000000000000000000000166001600160a01b03929092169190911790556128a2816001613bc0565b6001600160a01b03929092165f90815261019f60205260409020805460ff191660ff9093169290921790915550565b600101612816565b5060405162461bcd60e51b815260206004820152600960248201527f736c6f742066756c6c000000000000000000000000000000000000000000000060448201526064016109c3565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663b2267a7b7f00000000000000000000000000000000000000000000000000000000000000005f846040516024016129849190613bd9565b60408051601f198184030181529181526020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167f6d454d5100000000000000000000000000000000000000000000000000000000179052609b5490517fffffffff0000000000000000000000000000000000000000000000000000000060e087901b168152612a1a9493929190600401613c10565b5f604051808303815f87803b158015612a31575f80fd5b505af1158015612a43573d5f803e3d5ffd5b5050505050565b6033546001600160a01b031633146113275760405162461bcd60e51b815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e657260448201526064016109c3565b6001600160a01b0381165f9081526101a1602052604090205415612b0a5760405162461bcd60e51b815260206004820152601560248201527f616c726561647920696e2064656c6574654c697374000000000000000000000060448201526064016109c3565b6101a080546001810182555f919091527f7980fe0f714a613298681d64b7b8ffa7b148338dd52429f307d72798d5c317c40180547fffffffffffffffffffffffff0000000000000000000000000000000000000000166001600160a01b038316179055609954612b7a9043613a67565b6001600160a01b039091165f9081526101a16020526040902055565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663b2267a7b7f00000000000000000000000000000000000000000000000000000000000000005f84604051602401612bf8919061376d565b60408051601f198184030181529181526020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167f0be67fcc00000000000000000000000000000000000000000000000000000000179052609c5490517fffffffff0000000000000000000000000000000000000000000000000000000060e087901b168152612a1a9493929190600401613c10565b5f5b6101a05481101561109b57436101a15f6101a08481548110612cb457612cb4613a0d565b5f9182526020808320909101546001600160a01b0316835282019290925260400190205411612f475760a0600161019f5f6101a08581548110612cf957612cf9613a0d565b5f9182526020808320909101546001600160a01b03168352820192909252604001902054612d2a919060ff16613adc565b60ff1660ff8110612d3d57612d3d613a0d565b0180547fffffffffffffffffffffffff00000000000000000000000000000000000000001690556101a0805461019f915f9184908110612d7f57612d7f613a0d565b5f9182526020808320909101546001600160a01b031683528201929092526040018120805460ff191690556101a080546101a292919084908110612dc557612dc5613a0d565b5f9182526020808320909101546001600160a01b03168352820192909252604001812080547fffffffffffffffffffffffff00000000000000000000000000000000000000001681556001810182905590612e2360028301826132f3565b50506101a15f6101a08381548110612e3d57612e3d613a0d565b5f9182526020808320909101546001600160a01b031683528201929092526040018120556101a08054612e7290600190613ac9565b81548110612e8257612e82613a0d565b5f918252602090912001546101a080546001600160a01b039092169183908110612eae57612eae613a0d565b905f5260205f20015f6101000a8154816001600160a01b0302191690836001600160a01b031602179055506101a0805480612eeb57612eeb613c48565b5f8281526020902081017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff90810180547fffffffffffffffffffffffff0000000000000000000000000000000000000000169055019055612c90565b80612f5181613b13565b915050612c90565b600260655403612fab5760405162461bcd60e51b815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c0060448201526064016109c3565b6002606555565b8015613057575f826001600160a01b0316826040515f6040518083038185875af1925050503d805f8114613001576040519150601f19603f3d011682016040523d82523d5f602084013e613006565b606091505b5050905080610d5f5760405162461bcd60e51b815260206004820152601b60248201527f526f6c6c75703a20455448207472616e73666572206661696c6564000000000060448201526064016109c3565b5050565b6001606555565b603380546001600160a01b038381167fffffffffffffffffffffffff0000000000000000000000000000000000000000831681179093556040519116919082907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0905f90a35050565b5f54610100900460ff166131475760405162461bcd60e51b815260206004820152602b60248201527f496e697469616c697a61626c653a20636f6e7472616374206973206e6f74206960448201527f6e697469616c697a696e6700000000000000000000000000000000000000000060648201526084016109c3565b6113276131d3565b5f54610100900460ff166131cb5760405162461bcd60e51b815260206004820152602b60248201527f496e697469616c697a61626c653a20636f6e7472616374206973206e6f74206960448201527f6e697469616c697a696e6700000000000000000000000000000000000000000060648201526084016109c3565b611327613258565b5f54610100900460ff1661324f5760405162461bcd60e51b815260206004820152602b60248201527f496e697469616c697a61626c653a20636f6e7472616374206973206e6f74206960448201527f6e697469616c697a696e6700000000000000000000000000000000000000000060648201526084016109c3565b61132733613062565b5f54610100900460ff1661305b5760405162461bcd60e51b815260206004820152602b60248201527f496e697469616c697a61626c653a20636f6e7472616374206973206e6f74206960448201527f6e697469616c697a696e6700000000000000000000000000000000000000000060648201526084016109c3565b60405180611fe0016040528060ff906020820280368337509192915050565b5080546132ff9061387a565b5f825580601f1061330e575050565b601f0160209004905f5260205f209081019061109b91905b80821115613339575f8155600101613326565b5090565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52604160045260245ffd5b604051601f8201601f1916810167ffffffffffffffff811182821017156133935761339361333d565b604052919050565b5f82601f8301126133aa575f80fd5b813567ffffffffffffffff8111156133c4576133c461333d565b6133d76020601f19601f8401160161336a565b8181528460208386010111156133eb575f80fd5b816020850160208301375f918101602001919091529392505050565b5f8060408385031215613418575f80fd5b82359150602083013567ffffffffffffffff811115613435575f80fd5b6134418582860161339b565b9150509250929050565b80356001600160a01b0381168114611670575f80fd5b5f60208284031215613471575f80fd5b61347a8261344b565b9392505050565b5f60208284031215613491575f80fd5b5035919050565b5f60208083850312156134a9575f80fd5b823567ffffffffffffffff808211156134c0575f80fd5b818501915085601f8301126134d3575f80fd5b8135818111156134e5576134e561333d565b8060051b91506134f684830161336a565b818152918301840191848101908884111561350f575f80fd5b938501935b83851015613534576135258561344b565b82529385019390850190613514565b98975050505050505050565b611fe0810181835f5b60ff8110156135715781516001600160a01b0316835260209283019290910190600101613549565b50505092915050565b5f8083601f84011261358a575f80fd5b50813567ffffffffffffffff8111156135a1575f80fd5b6020830191508360208260051b85010111156135bb575f80fd5b9250929050565b5f805f805f80608087890312156135d7575f80fd5b86359550602087013567ffffffffffffffff808211156135f5575f80fd5b6136018a838b0161357a565b9097509550604089013594506060890135915080821115613620575f80fd5b818901915089601f830112613633575f80fd5b813581811115613641575f80fd5b8a6020828501011115613652575f80fd5b6020830194508093505050509295509295509295565b5f60208284031215613678575f80fd5b813567ffffffffffffffff81111561368e575f80fd5b61369a8482850161339b565b949350505050565b5f805f805f805f60e0888a0312156136b8575f80fd5b6136c18861344b565b9960208901359950604089013598606081013598506080810135975060a0810135965060c00135945092505050565b5f5b8381101561370a5781810151838201526020016136f2565b50505f910152565b5f81518084526137298160208601602086016136f0565b601f01601f19169290920160200192915050565b6001600160a01b0384168152826020820152606060408201525f6137646060830184613712565b95945050505050565b602080825282518282018190525f9190848201906040850190845b818110156137ad5783516001600160a01b031683529284019291840191600101613788565b50909695505050505050565b5f805f80604085870312156137cc575f80fd5b843567ffffffffffffffff808211156137e3575f80fd5b6137ef8883890161357a565b90965094506020870135915080821115613807575f80fd5b506138148782880161357a565b95989497509550505050565b5f8060208385031215613831575f80fd5b823567ffffffffffffffff811115613847575f80fd5b6138538582860161357a565b90969095509350505050565b5f82516138708184602087016136f0565b9190910192915050565b600181811c9082168061388e57607f821691505b6020821081036138c5577f4e487b71000000000000000000000000000000000000000000000000000000005f52602260045260245ffd5b50919050565b601f821115610d5f57805f5260205f20601f840160051c810160208510156138f05750805b601f840160051c820191505b81811015612a43575f81556001016138fc565b815167ffffffffffffffff8111156139295761392961333d565b61393d81613937845461387a565b846138cb565b602080601f83116001811461398f575f84156139595750858301515b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff600386901b1c1916600185901b178555613a05565b5f85815260208120601f198616915b828110156139bd5788860151825594840194600190910190840161399e565b50858210156139f957878501517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff600388901b60f8161c191681555b505060018460011b0185555b505050505050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52603260045260245ffd5b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601160045260245ffd5b8082018082111561170a5761170a613a3a565b808202811582820484141761170a5761170a613a3a565b5f82613ac4577f4e487b71000000000000000000000000000000000000000000000000000000005f52601260045260245ffd5b500490565b8181038181111561170a5761170a613a3a565b60ff828116828216039081111561170a5761170a613a3a565b5f60ff821660ff8103613b0a57613b0a613a3a565b60010192915050565b5f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8203613b4357613b43613a3a565b5060010190565b8183525f60208085019450825f5b85811015613b84576001600160a01b03613b718361344b565b1687529582019590820190600101613b58565b509495945050505050565b604081525f613ba2604083018688613b4a565b8281036020840152613bb5818587613b4a565b979650505050505050565b60ff818116838216019081111561170a5761170a613a3a565b602081526001600160a01b038251166020820152602082015160408201525f604083015160608084015261369a6080840182613712565b6001600160a01b0385168152836020820152608060408201525f613c376080830185613712565b905082606083015295945050505050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52603160045260245ffdfea164736f6c6343000818000a diff --git a/bindings/bin/rollup_deployed.hex b/bindings/bin/rollup_deployed.hex index 68a52ca9c..502f165cb 100644 --- a/bindings/bin/rollup_deployed.hex +++ b/bindings/bin/rollup_deployed.hex @@ -1 +1 @@ -0x608060405260043610610353575f3560e01c8063728cdbca116101bd578063b8d0a1b0116100f2578063d8dc99d211610092578063dff7827e1161006d578063dff7827e14610ac4578063e3fff1dd14610ad9578063f2fde38b14610af8578063fb1e8b0414610b17575f80fd5b8063d8dc99d214610a71578063ddd8a3dc14610a86578063de8b303514610aa5575f80fd5b8063cd4edc69116100cd578063cd4edc6914610a0b578063ce5db8d614610a1e578063cf9a674514610a33578063d279c19114610a52575f80fd5b8063b8d0a1b0146109ae578063bedb86fb146109cd578063c5553892146109ec575f80fd5b8063a479265d1161015d578063b31a77d311610138578063b31a77d31461093c578063b348442514610951578063b35dac4e14610970578063b3e0a5091461098f575f80fd5b8063a479265d146108dd578063a4f209b0146108f2578063abc8d68d14610911575f80fd5b80638f1d3776116101985780638f1d3776146107af578063910129d41461085f57806397fc007c14610890578063a415d8dc146108af575f80fd5b8063728cdbca1461075a57806388b1ea09146107795780638da5cb5b14610792575f80fd5b80632a213ba1116102935780635c975abb11610233578063612672901161020e57806361267290146106dd57806368589dfa146106fc5780636c578c1d14610727578063715018a614610746575f80fd5b80635c975abb1461061a5780635ef7a94a146106315780635f77cf1d14610691575f80fd5b80633e001b661161026e5780633e001b66146105b4578063428868b5146105c95780634e8f1d67146105dc57806357e0af6c146105fb575f80fd5b80632a213ba1146105335780632b7ac3f31461055e5780633b70c18a14610595575f80fd5b806313361101116102fe5780631e8825be116102d95780631e8825be1461049f57806321e2f9e0146104be5780632362f03e146104dd5780632571098d14610508575f80fd5b8063133611011461043b57806318463fb01461045a57806318af3b2b1461046f575f80fd5b806310d445831161032e57806310d44583146103e5578063116a1f4214610404578063121dcd5014610426575f80fd5b806304d772151461035e578063059def61146103a15780630ceb6780146103c4575f80fd5b3661035a57005b5f80fd5b348015610369575f80fd5b5061038c610378366004614e13565b60a36020525f908152604090205460ff1681565b60405190151581526020015b60405180910390f35b3480156103ac575f80fd5b506103b6609d5481565b604051908152602001610398565b3480156103cf575f80fd5b506103e36103de366004614e40565b610b2c565b005b3480156103f0575f80fd5b506103e36103ff366004614e9e565b610bfe565b34801561040f575f80fd5b5061038c61041e366004614e13565b609d54101590565b348015610431575f80fd5b506103b6609e5481565b348015610446575f80fd5b506103e3610455366004614ee6565b610f3a565b348015610465575f80fd5b506103b660a75481565b34801561047a575f80fd5b5061038c610489366004614e13565b5f90815260a26020526040902060010154421090565b3480156104aa575f80fd5b506103e36104b9366004614f45565b611434565b3480156104c9575f80fd5b5061038c6104d8366004614e13565b61181f565b3480156104e8575f80fd5b506103b66104f7366004614e13565b60a16020525f908152604090205481565b348015610513575f80fd5b506103b6610522366004614e13565b60a06020525f908152604090205481565b34801561053e575f80fd5b506103b661054d366004614e13565b60ad6020525f908152604090205481565b348015610569575f80fd5b50609c5461057d906001600160a01b031681565b6040516001600160a01b039091168152602001610398565b3480156105a0575f80fd5b50609b5461057d906001600160a01b031681565b3480156105bf575f80fd5b506103b660aa5481565b6103e36105d7366004614f45565b61184d565b3480156105e7575f80fd5b506103e36105f6366004614fa5565b611be8565b348015610606575f80fd5b506103e3610615366004614e13565b611f9c565b348015610625575f80fd5b5060655460ff1661038c565b34801561063c575f80fd5b5061067161064b366004614e13565b60a26020525f908152604090208054600182015460028301546003909301549192909184565b604080519485526020850193909352918301526060820152608001610398565b34801561069c575f80fd5b506106c47f000000000000000000000000000000000000000000000000000000000000000081565b60405167ffffffffffffffff9091168152602001610398565b3480156106e8575f80fd5b506103e36106f7366004614e13565b612047565b348015610707575f80fd5b506103b6610716366004614e13565b60ab6020525f908152604090205481565b348015610732575f80fd5b506103e3610741366004614e40565b612222565b348015610751575f80fd5b506103e36122df565b348015610765575f80fd5b506103e3610774366004615056565b6122f2565b348015610784575f80fd5b5060a65461038c9060ff1681565b34801561079d575f80fd5b506033546001600160a01b031661057d565b3480156107ba575f80fd5b5061081b6107c9366004614e13565b60a46020525f9081526040902080546001820154600283015460039093015467ffffffffffffffff831693680100000000000000009093046001600160a01b0316929060ff8082169161010090041686565b6040805167ffffffffffffffff90971687526001600160a01b03909516602087015293850192909252606084015215156080830152151560a082015260c001610398565b34801561086a575f80fd5b5061038c610879366004614e13565b5f90815260a4602052604090206003015460ff1690565b34801561089b575f80fd5b506103e36108aa366004614e40565b6125fb565b3480156108ba575f80fd5b5061038c6108c9366004614e40565b609f6020525f908152604090205460ff1681565b3480156108e8575f80fd5b506103b660995481565b3480156108fd575f80fd5b506103e361090c366004614e13565b6126de565b34801561091c575f80fd5b506103b661092b366004614e40565b60a56020525f908152604090205481565b348015610947575f80fd5b506103b660a85481565b34801561095c575f80fd5b506103e361096b3660046150b1565b61278e565b34801561097b575f80fd5b506103e361098a366004614e40565b612a92565b34801561099a575f80fd5b506103e36109a9366004614ee6565b612aeb565b3480156109b9575f80fd5b506103e36109c83660046150b1565b612e48565b3480156109d8575f80fd5b506103e36109e7366004615125565b612ed1565b3480156109f7575f80fd5b506103e3610a06366004614e13565b61300f565b6103e3610a19366004615157565b6130b2565b348015610a29575f80fd5b506103b660985481565b348015610a3e575f80fd5b506103e3610a4d366004614e13565b613644565b348015610a5d575f80fd5b506103e3610a6c366004614e40565b6137b3565b348015610a7c575f80fd5b506103b660ac5481565b348015610a91575f80fd5b5060975461057d906001600160a01b031681565b348015610ab0575f80fd5b5061038c610abf366004614e13565b613872565b348015610acf575f80fd5b506103b6609a5481565b348015610ae4575f80fd5b506103e3610af3366004614e13565b6138bc565b348015610b03575f80fd5b506103e3610b12366004614e40565b61395f565b348015610b22575f80fd5b506103b660a95481565b610b346139ec565b6001600160a01b0381165f908152609f602052604090205460ff1615610ba15760405162461bcd60e51b815260206004820152601f60248201527f6163636f756e7420697320616c72656164792061206368616c6c656e6765720060448201526064015b60405180910390fd5b6001600160a01b0381165f818152609f6020908152604091829020805460ff1916600190811790915591519182527f7feb653c7b1f0d23daeed494225b3f28851cdc8973fcc653866d9b6e205fc00991015b60405180910390a250565b610c066139ec565b5f8111610c555760405162461bcd60e51b815260206004820152601560248201527f636f756e74206d757374206265206e6f6e7a65726f00000000000000000000006044820152606401610b98565b5f80610c618585613a46565b915091505f610c74836001015160c01c90565b5f81815260a160205260409020549091508214610cd35760405162461bcd60e51b815260206004820152601460248201527f696e636f727265637420626174636820686173680000000000000000000000006044820152606401610b98565b5f60a181610ce187856151ac565b81526020019081526020015f205414610d615760405162461bcd60e51b8152602060048201526024808201527f726576657274696e67206d7573742073746172742066726f6d2074686520656e60448201527f64696e67000000000000000000000000000000000000000000000000000000006064820152608401610b98565b609d548111610dd85760405162461bcd60e51b815260206004820152602160248201527f63616e206f6e6c792072657665727420756e46696e616c697a6564206261746360448201527f68000000000000000000000000000000000000000000000000000000000000006064820152608401610b98565b610de36001826151bf565b609e555b8315610f3257604051829082907ecae2739091badfd91c373f0a16cede691e0cd25bb80cff77dd5caeb4710146905f90a35f81815260a16020526040812055610e2f81613872565b15610e8a575f81815260a460209081526040808320600181015490546801000000000000000090046001600160a01b0316845260a59092528220805491929091610e7a9084906151ac565b909155505060a6805460ff191690555b5f81815260a46020526040812080547fffffffff00000000000000000000000000000000000000000000000000000000168155600181018290556002810191909155600301805461ffff1916905560a85415801590610eea575060a85481145b15610ef4575f60a8555b6001015f81815260a160205260409020547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff90940193915081610de7575b505050505050565b60a85415610f8a5760405162461bcd60e51b815260206004820152601660248201527f70656e64696e67207265766572742072657175657374000000000000000000006044820152606401610b98565b610f92613aee565b5f80610f9e8484613a46565b915091505f610fb1836001015160c01c90565b5f81815260a1602052604090205490915082146110105760405162461bcd60e51b815260206004820152601460248201527f696e636f727265637420626174636820686173680000000000000000000000006044820152606401610b98565b6110198161181f565b6110655760405162461bcd60e51b815260206004820152600f60248201527f6261746368206e6f7420657869737400000000000000000000000000000000006044820152606401610b98565b61106e81613872565b156110bb5760405162461bcd60e51b815260206004820152601260248201527f626174636820696e206368616c6c656e676500000000000000000000000000006044820152606401610b98565b5f81815260a4602052604090206003015460ff161561111c5760405162461bcd60e51b815260206004820152601660248201527f62617463682073686f756c6420626520726576657274000000000000000000006044820152606401610b98565b5f81815260a2602052604090206001015442101561117c5760405162461bcd60e51b815260206004820152601960248201527f626174636820696e206368616c6c656e67652077696e646f77000000000000006044820152606401610b98565b605983015160a05f61118f6001856151bf565b81526020019081526020015f2054146111ea5760405162461bcd60e51b815260206004820152601d60248201527f696e636f72726563742070726576696f757320737461746520726f6f740000006044820152606401610b98565b5f81815260a06020526040902054156112455760405162461bcd60e51b815260206004820152601660248201527f626174636820616c7265616479207665726966696564000000000000000000006044820152606401610b98565b80609d54600101146112995760405162461bcd60e51b815260206004820152601560248201527f696e636f727265637420626174636820696e64657800000000000000000000006044820152606401610b98565b609d819055600160a35f6112ae866099015190565b815260208101919091526040015f20805460ff191691151591909117905560798301515f82815260a060205260409020556112fd6112f0846011015160c01c90565b600985015160c01c613b41565b60a25f61130b6001846151bf565b815260208101919091526040015f908120818155600180820183905560028201839055600390910182905560ab919061134490846151bf565b81526020019081526020015f205f905560ad5f60018361136491906151bf565b81526020019081526020015f205f905560a45f60018361138491906151bf565b815260208082019290925260409081015f90812080547fffffffff000000000000000000000000000000000000000000000000000000001681556001810182905560028101829055600301805461ffff1916905583815260a1909252902054817f26ba82f907317eedc97d0cbef23de76a43dd6edb563bdb6e9407645b950a7a2d611410866079015190565b60998701516040805192835260208301919091520160405180910390a35050505050565b6097546001600160a01b03166368015791336040517fffffffff0000000000000000000000000000000000000000000000000000000060e084901b1681526001600160a01b039091166004820152602401602060405180830381865afa1580156114a0573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906114c491906151d2565b6115105760405162461bcd60e51b815260206004820152601a60248201527f6f6e6c7920616374697665207374616b657220616c6c6f7765640000000000006044820152606401610b98565b60a854156115605760405162461bcd60e51b815260206004820152601660248201527f70656e64696e67207265766572742072657175657374000000000000000000006044820152606401610b98565b611568613aee565b5f49156115b75760405162461bcd60e51b815260206004820152601f60248201527f636f6d6d69745374617465206d757374206e6f7420636172727920626c6f62006044820152606401610b98565b5f6115cd6115c860208501856151ed565b613a46565b5090505f6115df826001015160c01c90565b6115ea9060016151ac565b5f81815260ad602052604090205490915061166d5760405162461bcd60e51b815260206004820152602260248201527f6e6f2073746f72656420626c6f62206861736820666f7220746869732062617460448201527f63680000000000000000000000000000000000000000000000000000000000006064820152608401610b98565b60ac54609b54604080517fb59b1a7800000000000000000000000000000000000000000000000000000000815290514293926001600160a01b03169163b59b1a789160048083019260209291908290030181865afa1580156116d1573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906116f5919061524e565b6116ff91906151ac565b1015611767575f6117166080860160608701615265565b61ffff16116117675760405162461bcd60e51b815260206004820152600b60248201527f6c316d73672064656c61790000000000000000000000000000000000000000006044820152606401610b98565b6097545f906001600160a01b031663d096c3c6336040517fffffffff0000000000000000000000000000000000000000000000000000000060e084901b1681526001600160a01b039091166004820152602401602060405180830381865afa1580156117d5573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906117f9919061524e565b905061181885858360ad5f8781526020019081526020015f2054613bfe565b5050505050565b5f81815260a260205260408120541580159061184757505f82815260a1602052604090205415155b92915050565b6097546001600160a01b03166368015791336040517fffffffff0000000000000000000000000000000000000000000000000000000060e084901b1681526001600160a01b039091166004820152602401602060405180830381865afa1580156118b9573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906118dd91906151d2565b6119295760405162461bcd60e51b815260206004820152601a60248201527f6f6e6c7920616374697665207374616b657220616c6c6f7765640000000000006044820152606401610b98565b60a854156119795760405162461bcd60e51b815260206004820152601660248201527f70656e64696e67207265766572742072657175657374000000000000000000006044820152606401610b98565b611981613aee565b5f6119926115c860208501856151ed565b5090505f6119a4826001015160c01c90565b6119af9060016151ac565b5f81815260ad602052604090205490915015611a335760405162461bcd60e51b815260206004820152602860248201527f636f6d6d69744261746368207265717569726573206e6f2073746f726564206260448201527f6c6f6220686173680000000000000000000000000000000000000000000000006064820152608401610b98565b60ac54609b54604080517fb59b1a7800000000000000000000000000000000000000000000000000000000815290514293926001600160a01b03169163b59b1a789160048083019260209291908290030181865afa158015611a97573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611abb919061524e565b611ac591906151ac565b1015611b2d575f611adc6080860160608701615265565b61ffff1611611b2d5760405162461bcd60e51b815260206004820152600b60248201527f6c316d73672064656c61790000000000000000000000000000000000000000006044820152606401610b98565b6097545f906001600160a01b031663d096c3c6336040517fffffffff0000000000000000000000000000000000000000000000000000000060e084901b1681526001600160a01b039091166004820152602401602060405180830381865afa158015611b9b573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611bbf919061524e565b90505f611bda611bd26020880188615286565b60ff1661425e565b9050610f3286868484613bfe565b60a85415611c385760405162461bcd60e51b815260206004820152601660248201527f70656e64696e67207265766572742072657175657374000000000000000000006044820152606401610b98565b611c40613aee565b60ac54609e545f90815260a2602052604081205490914291611c6291906151ac565b1090505f4260ac54609b5f9054906101000a90046001600160a01b03166001600160a01b031663b59b1a786040518163ffffffff1660e01b8152600401602060405180830381865afa158015611cba573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611cde919061524e565b611ce891906151ac565b10905081158015611cf65750805b15611d5d575f611d0c60808a0160608b01615265565b61ffff1611611d5d5760405162461bcd60e51b815260206004820152600b60248201527f6c316d73672064656c61790000000000000000000000000000000000000000006044820152606401610b98565b8180611d665750805b611db25760405162461bcd60e51b815260206004820152600e60248201527f696e76616c69642074696d696e670000000000000000000000000000000000006044820152606401610b98565b5f611dc36115c860208b018b6151ed565b5090505f611dd5826001015160c01c90565b611de09060016151ac565b5f81815260ad60205260408120549192509015611e80575f4915611e6c5760405162461bcd60e51b815260206004820152602f60248201527f6d757374206e6f7420636172727920626c6f62207768656e207573696e67207360448201527f746f72656420626c6f62206861736800000000000000000000000000000000006064820152608401610b98565b505f81815260ad6020526040902054611e93565b611e90611bd260208d018d615286565b90505b611e9f8b8b5f84613bfe565b5f80611eab8b8b613a46565b915091505f611ebe836001015160c01c90565b905080609e5414611f115760405162461bcd60e51b815260206004820152601660248201527f696e636f727265637420626174636820686561646572000000000000000000006044820152606401610b98565b5f81815260a160205260409020548214611f6d5760405162461bcd60e51b815260206004820152601460248201527f696e636f727265637420626174636820686173680000000000000000000000006044820152606401610b98565b5f81815260a26020526040902042600190910155611f8c838b8b614399565b5050505050505050505050505050565b611fa46139ec565b5f81118015611fb557506099548114155b6120015760405162461bcd60e51b815260206004820152601860248201527f696e76616c6964206e65772070726f6f662077696e646f7700000000000000006044820152606401610b98565b609980549082905560408051828152602081018490527f1e3a2094feb4b696dd3d7caea38ad2f41dbdcac3fa3943c7a693aff8a64b0a6191015b60405180910390a15050565b5f54600290610100900460ff1615801561206757505f5460ff8083169116105b6120d95760405162461bcd60e51b815260206004820152602e60248201527f496e697469616c697a61626c653a20636f6e747261637420697320616c72656160448201527f647920696e697469616c697a65640000000000000000000000000000000000006064820152608401610b98565b5f805461ffff191660ff8316176101001790556120f75f5460ff1690565b60ff166002146121495760405162461bcd60e51b815260206004820152601660248201527f6d757374206861766520696e697469616c697a656421000000000000000000006044820152606401610b98565b816121bc5760405162461bcd60e51b815260206004820152602760248201527f63616e206e6f742073657420737461746520726f6f742077697468206279746560448201527f73333228302921000000000000000000000000000000000000000000000000006064820152608401610b98565b609e545f90815260ab60205260409020546121e557609e545f90815260ab602052604090208290555b5f805461ff001916905560405160ff821681527f7f26b83ff96e1f2b6a682f133852f6798a09c465da95921460cefb38474024989060200161203b565b61222a6139ec565b6001600160a01b0381165f908152609f602052604090205460ff166122915760405162461bcd60e51b815260206004820152601b60248201527f6163636f756e74206973206e6f742061206368616c6c656e67657200000000006044820152606401610b98565b6001600160a01b0381165f818152609f60209081526040808320805460ff19169055519182527f7feb653c7b1f0d23daeed494225b3f28851cdc8973fcc653866d9b6e205fc0099101610bf3565b6122e76139ec565b6122f05f61454c565b565b5f54610100900460ff161580801561231057505f54600160ff909116105b806123295750303b15801561232957505f5460ff166001145b61239b5760405162461bcd60e51b815260206004820152602e60248201527f496e697469616c697a61626c653a20636f6e747261637420697320616c72656160448201527f647920696e697469616c697a65640000000000000000000000000000000000006064820152608401610b98565b5f805460ff1916600117905580156123bc575f805461ff0019166101001790555b6001600160a01b03861615806123d957506001600160a01b038516155b15612410576040517fecc6fdf000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6001600160a01b0387166124665760405162461bcd60e51b815260206004820152601b60248201527f696e76616c6964206c31207374616b696e6720636f6e747261637400000000006044820152606401610b98565b61246e6145b5565b612476614639565b609780546001600160a01b03808a167fffffffffffffffffffffffff000000000000000000000000000000000000000092831617909255609b8054898416908316179055609c805492881692909116821790556098859055609984905560a98390556040515f907f728af3d16a5760405e27a082c98ab272e9f0a1d02f0085d41532a26093aedd96908290a3604080515f8152602081018690527fa577f4223f91f74e2dad65bbb8c30807587ae95d0d34288057bb3ec0d398a437910160405180910390a1604080515f8152602081018590527f1e3a2094feb4b696dd3d7caea38ad2f41dbdcac3fa3943c7a693aff8a64b0a61910160405180910390a1604080515f8152602081018490527ffb81bce17f015797e11949d3c332e2bf9453faf68f728447426803138f2b0223910160405180910390a180156125f2575f805461ff0019169055604051600181527f7f26b83ff96e1f2b6a682f133852f6798a09c465da95921460cefb38474024989060200160405180910390a15b50505050505050565b6126036139ec565b6001600160a01b038116158015906126295750609c546001600160a01b03828116911614155b6126755760405162461bcd60e51b815260206004820152601460248201527f696e76616c6964206e65772076657269666965720000000000000000000000006044820152606401610b98565b609c80546001600160a01b038381167fffffffffffffffffffffffff0000000000000000000000000000000000000000831681179093556040519116919082907f728af3d16a5760405e27a082c98ab272e9f0a1d02f0085d41532a26093aedd96905f90a35050565b6126e66139ec565b5f811180156126f6575060648111155b8015612704575060a9548114155b6127505760405162461bcd60e51b815260206004820152601f60248201527f696e76616c69642070726f6f66207265776172642070657263656e74616765006044820152606401610b98565b60a980549082905560408051828152602081018490527ffb81bce17f015797e11949d3c332e2bf9453faf68f728447426803138f2b0223910161203b565b60a854156127de5760405162461bcd60e51b815260206004820152601660248201527f70656e64696e67207265766572742072657175657374000000000000000000006044820152606401610b98565b6127e6613aee565b6097546001600160a01b03166368015791336040517fffffffff0000000000000000000000000000000000000000000000000000000060e084901b1681526001600160a01b039091166004820152602401602060405180830381865afa158015612852573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061287691906151d2565b6128c25760405162461bcd60e51b815260206004820152601a60248201527f6f6e6c7920616374697665207374616b657220616c6c6f7765640000000000006044820152606401610b98565b5f806128ce8686613a46565b915091505f6128e1836001015160c01c90565b5f81815260a1602052604090205490915082146129405760405162461bcd60e51b815260206004820152601460248201527f696e636f727265637420626174636820686173680000000000000000000000006044820152606401610b98565b61294981613872565b6129955760405162461bcd60e51b815260206004820152601260248201527f626174636820696e206368616c6c656e676500000000000000000000000000006044820152606401610b98565b5f81815260a46020526040902060038101805461ff00191661010017905560a6805460ff1916905560995460029091015442916129d1916151ac565b11612a47575f81815260a4602090815260408083206003908101805460ff1916600117905560a2835292819020909201548251808401909352600783527f54696d656f75740000000000000000000000000000000000000000000000000091830191909152612a42918391906146bd565b6125f2565b612a52838686614399565b6125f281336040518060400160405280600d81526020017f50726f6f66207375636365737300000000000000000000000000000000000000815250614819565b612a9a6139ec565b60aa80545f909155612aac82826148e5565b604080516001600160a01b0384168152602081018390527fb1b2058a6969e2d25e47bcaebe8ae21c29a23b2752429315b75e2f4f285f3d87910161203b565b612af36139ec565b5f805260a06020527fb84a74ec6ef4d0e83b6006dfaa014ab4026f9f3b97d186e604d29998a4e808ea5415612b6a5760405162461bcd60e51b815260206004820152601660248201527f67656e6573697320626174636820696d706f72746564000000000000000000006044820152606401610b98565b5f80612b768484613a46565b915091505f612b89836001015160c01c90565b90508015612bd95760405162461bcd60e51b815260206004820152601360248201527f696e76616c696420626174636820696e646578000000000000000000000000006044820152606401610b98565b5f612be5846079015190565b905080612c345760405162461bcd60e51b815260206004820152600f60248201527f7a65726f20737461746520726f6f7400000000000000000000000000000000006044820152606401610b98565b600984015160c01c15612c895760405162461bcd60e51b815260206004820152601d60248201527f6c31206d65737361676520706f707065642073686f756c6420626520300000006044820152606401610b98565b5f612c95856019015190565b03612ce25760405162461bcd60e51b815260206004820152600e60248201527f7a65726f206461746120686173680000000000000000000000000000000000006044820152606401610b98565b7f010657f37554c781402a22917dee2f75def7ab966d7b770905398eba3c444014612d0e856039015190565b14612d5b5760405162461bcd60e51b815260206004820152601660248201527f696e76616c69642076657273696f6e65642068617368000000000000000000006044820152606401610b98565b5f82815260a1602090815260408083208690558051608081018252428082528184019081528183018581526060830186815288875260a28652848720935184559151600184015551600283015551600390910155603987015160ad83528184205560ab825280832084905560a0909152808220839055609e849055609d84905551849184917f2c32d4ae151744d0bf0b9464a3e897a1d17ed2f1af71f7c9a75f12ce0d28238f9190a3604080518281525f6020820152849184917f26ba82f907317eedc97d0cbef23de76a43dd6edb563bdb6e9407645b950a7a2d910160405180910390a3505050505050565b5f80612e548686613a46565b915091505f612e67836001015160c01c90565b5f81815260a160205260409020549091508214612ec65760405162461bcd60e51b815260206004820152601460248201527f696e636f727265637420626174636820686173680000000000000000000000006044820152606401610b98565b6125f2838686614399565b612ed96139ec565b8015612fdd57612ee7614990565b60a65460ff1615612f9c5760a7545f90815260a460209081526040808320600181015490546801000000000000000090046001600160a01b0316845260a59092528220805491929091612f3b9084906151ac565b909155505060a7545f90815260a46020526040812080547fffffffff00000000000000000000000000000000000000000000000000000000168155600181018290556002810191909155600301805461ffff1916905560a6805460ff191690555b7f62e78cea01bee320cd4e420270b5ea74000d11b0c9f74754ebdbfc544b05a258335b6040516001600160a01b03909116815260200160405180910390a150565b612fe56149ea565b7f5db9ee0a495bf2e6ff9c91a7834c1ba4fdd244a5e8aa4e537bd38aeae4b073aa33612fbf565b50565b6130176139ec565b5f81118015613028575060ac548114155b6130745760405162461bcd60e51b815260206004820152601f60248201527f696e76616c6964206e657720726f6c6c75702064656c617920706572696f64006044820152606401610b98565b60ac80549082905560408051828152602081018490527f624e47dc9fb8f8cfeaf4ead4710277cc1757136cfa885e465514cf6d510f0ad1910161203b565b335f908152609f602052604090205460ff166131105760405162461bcd60e51b815260206004820152601760248201527f6f6e6c79206368616c6c656e67657220616c6c6f7765640000000000000000006044820152606401610b98565b60a854156131605760405162461bcd60e51b815260206004820152601660248201527f70656e64696e67207265766572742072657175657374000000000000000000006044820152606401610b98565b613168613aee565b60a65460ff16156131bb5760405162461bcd60e51b815260206004820152601460248201527f616c726561647920696e206368616c6c656e67650000000000000000000000006044820152606401610b98565b8167ffffffffffffffff16609d54106132165760405162461bcd60e51b815260206004820152601760248201527f626174636820616c72656164792066696e616c697a65640000000000000000006044820152606401610b98565b67ffffffffffffffff82165f90815260a16020526040902054811461327d5760405162461bcd60e51b815260206004820152601460248201527f696e636f727265637420626174636820686173680000000000000000000000006044820152606401610b98565b6132908267ffffffffffffffff1661181f565b6132dc5760405162461bcd60e51b815260206004820152600f60248201527f6261746368206e6f7420657869737400000000000000000000000000000000006044820152606401610b98565b67ffffffffffffffff82165f90815260a460205260409020546801000000000000000090046001600160a01b0316156133575760405162461bcd60e51b815260206004820152601860248201527f626174636820616c7265616479206368616c6c656e67656400000000000000006044820152606401610b98565b67ffffffffffffffff82165f90815260a2602052604090206001015442106133e75760405162461bcd60e51b815260206004820152603360248201527f63616e6e6f74206368616c6c656e6765206261746368206f757473696465207460448201527f6865206368616c6c656e67652077696e646f77000000000000000000000000006064820152608401610b98565b60975f9054906101000a90046001600160a01b03166001600160a01b0316630d13fd7b6040518163ffffffff1660e01b8152600401602060405180830381865afa158015613437573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061345b919061524e565b3410156134aa5760405162461bcd60e51b815260206004820152601260248201527f696e73756666696369656e742076616c756500000000000000000000000000006044820152606401610b98565b67ffffffffffffffff82811660a78190556040805160c0810182528281523360208083018281523484860190815242606086019081525f6080870181815260a0880182815299825260a4909552969096209451855492516001600160a01b031668010000000000000000027fffffffff000000000000000000000000000000000000000000000000000000009093169816979097171783559451600183015591516002820155925160039093018054925115156101000261ff00199415159490941661ffff19909316929092179290921790556001600160a01b03168267ffffffffffffffff167f3a6ea19df25b49e7624e313ce7c1ab23984238e93727260db56a81735b1b9976346040516135c291815260200190565b60405180910390a35f609d5460016135da91906151ac565b90505b609e548111613632578267ffffffffffffffff168114613620576099545f82815260a260205260408120600101805490919061361a9084906151ac565b90915550505b8061362a816152a6565b9150506135dd565b505060a6805460ff1916600117905550565b5f54600390610100900460ff1615801561366457505f5460ff8083169116105b6136d65760405162461bcd60e51b815260206004820152602e60248201527f496e697469616c697a61626c653a20636f6e747261637420697320616c72656160448201527f647920696e697469616c697a65640000000000000000000000000000000000006064820152608401610b98565b5f805461ffff191660ff8316176101001781558290036137385760405162461bcd60e51b815260206004820152601b60248201527f696e76616c696420726f6c6c75702064656c617920706572696f6400000000006044820152606401610b98565b60ac829055604080515f8152602081018490527f624e47dc9fb8f8cfeaf4ead4710277cc1757136cfa885e465514cf6d510f0ad1910160405180910390a15f805461ff001916905560405160ff821681527f7f26b83ff96e1f2b6a682f133852f6798a09c465da95921460cefb38474024989060200161203b565b335f90815260a56020526040812054908190036138125760405162461bcd60e51b815260206004820152601c60248201527f696e76616c69642062617463684368616c6c656e6765526577617264000000006044820152606401610b98565b335f90815260a5602052604081205561382b82826148e5565b816001600160a01b03167f9c25fa83f414ed363c8d39c98fb3e17567b3431cede71eb062c49d2a63ce247a8260405161386691815260200190565b60405180910390a25050565b5f81815260a460205260408120546801000000000000000090046001600160a01b0316158015906118475750505f90815260a46020526040902060030154610100900460ff161590565b6138c46139ec565b5f811180156138d557506098548114155b6139215760405162461bcd60e51b815260206004820152601b60248201527f696e76616c6964206e65772066696e616c697a6520706572696f6400000000006044820152606401610b98565b609880549082905560408051828152602081018490527fa577f4223f91f74e2dad65bbb8c30807587ae95d0d34288057bb3ec0d398a437910161203b565b6139676139ec565b6001600160a01b0381166139e35760405162461bcd60e51b815260206004820152602660248201527f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160448201527f64647265737300000000000000000000000000000000000000000000000000006064820152608401610b98565b61300c8161454c565b6033546001600160a01b031633146122f05760405162461bcd60e51b815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e65726044820152606401610b98565b5f805f613a538585614a23565b90505f8160ff165f03613a7457613a6a8686614a93565b9094509050613ae0565b8160ff1660011480613a8957508160ff166002145b15613a9857613a6a8686614afc565b60405162461bcd60e51b815260206004820152601960248201527f556e737570706f727465642062617463682076657273696f6e000000000000006044820152606401610b98565b808420925050509250929050565b60655460ff16156122f05760405162461bcd60e51b815260206004820152601060248201527f5061757361626c653a20706175736564000000000000000000000000000000006044820152606401610b98565b805f03613b4c575050565b8082035f5b82811015613bf857610100818403811115613b6b57508083035b609b546040517f3c7f528300000000000000000000000000000000000000000000000000000000815260048101859052602481018390526001600160a01b0390911690633c7f5283906044015f604051808303815f87803b158015613bce575f80fd5b505af1158015613be0573d5f803e3d5ffd5b50505050610100830192505061010081019050613b51565b50505050565b6002613c0d6020860186615286565b60ff161115613c5e5760405162461bcd60e51b815260206004820152600f60248201527f696e76616c69642076657273696f6e00000000000000000000000000000000006044820152606401610b98565b6080840135613caf5760405162461bcd60e51b815260206004820152601b60248201527f70726576696f757320737461746520726f6f74206973207a65726f00000000006044820152606401610b98565b60a0840135613d005760405162461bcd60e51b815260206004820152601660248201527f6e657720737461746520726f6f74206973207a65726f000000000000000000006044820152606401610b98565b5f80613d126115c860208801886151ed565b915091505f613d25836001015160c01c90565b90505f60a181613d368460016151ac565b81526020019081526020015f205414613d915760405162461bcd60e51b815260206004820152601760248201527f626174636820616c726561647920636f6d6d69747465640000000000000000006044820152606401610b98565b609e548114613de25760405162461bcd60e51b815260206004820152601560248201527f696e636f727265637420626174636820696e64657800000000000000000000006044820152606401610b98565b5f81815260a160205260409020548214613e3e5760405162461bcd60e51b815260206004820152601b60248201527f696e636f727265637420706172656e74206261746368206861736800000000006044820152606401610b98565b5f81815260ab6020526040902054608088013514613e9e5760405162461bcd60e51b815260206004820152601d60248201527f696e636f72726563742070726576696f757320737461746520726f6f740000006044820152606401610b98565b5f613ead846011015160c01c90565b90505f613ed9613ec360608b0160408c016152dd565b613ed360808c0160608d01615265565b84614b4f565b9050613eeb60808a0160608b01615265565b60019384019361ffff91909116929092019160f990613f0d60208c018c615286565b60ff1610613f1a57506101015b604080518281019091529550613f3f86613f3760208d018d615286565b60ff16614b93565b60c084901b6001870152613f6d86613f5d60808d0160608e01615265565b61ffff1660c01b60099190910152565b60c083811b6011880152601987018390526039870188905260808b0135605988015260a08b013560798801528a01356099870152613fd186613fb260208c018c6151ed565b604051613fc09291906152f6565b604051809103902060b99190910152565b60d986018590526001613fe760208c018c615286565b60ff1610614019576140198661400360608d0160408e016152dd565b67ffffffffffffffff1660c01b60f99190910152565b8086205f85815260a1602090815260408083209390935560ab815282822060a08e0135905560ad905290812088905560a65460ff16156140825760a7545f90815260a460205260409020600201546099544291614075916151ac565b61407f91906151bf565b90505b604051806080016040528042815260200182609854426140a291906151ac565b6140ac91906151ac565b81526020016140c160608e0160408f016152dd565b67ffffffffffffffff16815260209081018b90525f87815260a2825260409081902083518155838301516001820155908301516002820155606090920151600390920191909155609e8690556097546001600160a01b031692506374fe27b791508a359061413b90614135908d018d6151ed565b5f614b9a565b5f61414960408e018e6151ed565b6040518663ffffffff1660e01b815260040161416995949392919061534c565b602060405180830381865afa158015614184573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906141a891906151d2565b61421a5760405162461bcd60e51b815260206004820152602160248201527f746865207369676e617475726520766572696669636174696f6e206661696c6560448201527f64000000000000000000000000000000000000000000000000000000000000006064820152608401610b98565b5f83815260a16020526040808220549051909185917f2c32d4ae151744d0bf0b9464a3e897a1d17ed2f1af71f7c9a75f12ce0d28238f9190a3505050505050505050565b5f816002036142ed575f6040515f5b80498061427a5750614289565b6020820283015260010161426d565b6020810280832092016040529092509050806142e75760405162461bcd60e51b815260206004820152601b60248201527f5632207265717569726573206174206c65617374203120626c6f6200000000006044820152606401610b98565b50919050565b600149156143635760405162461bcd60e51b815260206004820152602560248201527f6c6567616379206261746368657320737570706f72742065786163746c79203160448201527f20626c6f620000000000000000000000000000000000000000000000000000006064820152608401610b98565b5f4915614371575f49611847565b507f010657f37554c781402a22917dee2f75def7ab966d7b770905398eba3c4440145b919050565b806143e65760405162461bcd60e51b815260206004820152601360248201527f496e76616c69642062617463682070726f6f66000000000000000000000000006044820152606401610b98565b5f6143f5846001015160c01c90565b90505f614403856039015190565b90505f7f0000000000000000000000000000000000000000000000000000000000000000614432876059015190565b6079880151609989015160b98a015160198b015160405160c09690961b7fffffffffffffffff000000000000000000000000000000000000000000000000166020870152602886019490945260488501929092526068840152608883015260a882015260c8810183905260e801604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529190528051602090910120609c549091506001600160a01b0316632c09a8486144f4885160f81c90565b858888866040518663ffffffff1660e01b81526004016145189594939291906153bf565b5f6040518083038186803b15801561452e575f80fd5b505afa158015614540573d5f803e3d5ffd5b50505050505050505050565b603380546001600160a01b038381167fffffffffffffffffffffffff0000000000000000000000000000000000000000831681179093556040519116919082907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0905f90a35050565b5f54610100900460ff166146315760405162461bcd60e51b815260206004820152602b60248201527f496e697469616c697a61626c653a20636f6e7472616374206973206e6f74206960448201527f6e697469616c697a696e670000000000000000000000000000000000000000006064820152608401610b98565b6122f0614bee565b5f54610100900460ff166146b55760405162461bcd60e51b815260206004820152602b60248201527f496e697469616c697a61626c653a20636f6e7472616374206973206e6f74206960448201527f6e697469616c697a696e670000000000000000000000000000000000000000006064820152608401610b98565b6122f0614c76565b60a88390555f83815260a460205260408082205460975491517f45bc4d1000000000000000000000000000000000000000000000000000000000815260048101869052680100000000000000009091046001600160a01b03908116939216906345bc4d10906024016020604051808303815f875af1158015614741573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190614765919061524e565b5f86815260a460205260409020600101549091506147849082906151ac565b5f86815260a460209081526040808320546801000000000000000090046001600160a01b0316835260a5909152812080549091906147c39084906151ac565b90915550506040516147d69084906153f0565b604051908190038120906001600160a01b0384169087907fe70d3820e244d5f71d1a6395db24f3460e8dca966edc1fd3625b6292880a877a905f90a45050505050565b5f83815260a4602052604081206001015460a95490919060649061483d908461541c565b6148479190615433565b905061485381836151bf565b60aa5f82825461486391906151ac565b90915550506001600160a01b0384165f90815260a560205260408120805483929061488f9084906151ac565b90915550506040516148a29084906153f0565b604051908190038120906001600160a01b0386169087907fe70d3820e244d5f71d1a6395db24f3460e8dca966edc1fd3625b6292880a877a905f90a45050505050565b801561498c575f826001600160a01b0316826040515f6040518083038185875af1925050503d805f8114614934576040519150601f19603f3d011682016040523d82523d5f602084013e614939565b606091505b505090508061498a5760405162461bcd60e51b815260206004820152601b60248201527f526f6c6c75703a20455448207472616e73666572206661696c656400000000006044820152606401610b98565b505b5050565b614998613aee565b6065805460ff191660011790557f62e78cea01bee320cd4e420270b5ea74000d11b0c9f74754ebdbfc544b05a2586149cd3390565b6040516001600160a01b03909116815260200160405180910390a1565b6149f2614cfb565b6065805460ff191690557f5db9ee0a495bf2e6ff9c91a7834c1ba4fdd244a5e8aa4e537bd38aeae4b073aa336149cd565b5f81614a715760405162461bcd60e51b815260206004820152601260248201527f456d7074792062617463682068656164657200000000000000000000000000006044820152606401610b98565b82825f818110614a8357614a8361546b565b919091013560f81c949350505050565b5f8160f9811015614ae65760405162461bcd60e51b815260206004820152601d60248201527f626174636820686561646572206c656e67746820746f6f20736d616c6c0000006044820152606401610b98565b6040519150808483378082016040529250929050565b5f816101018114614ae65760405162461bcd60e51b815260206004820181905260248201527f626174636820686561646572206c656e67746820697320696e636f72726563746044820152606401610b98565b6040805160c085901b815260f084901b6008820152600a60208502820181019092525f918101614b848161ffff871686614d4d565b82900390912095945050505050565b8082535050565b60605f80808080614bad888a018a615585565b9550955095509550955050818710614bcb579450614be79350505050565b838710614bdf578295505050505050614be7565b509293505050505b9392505050565b5f54610100900460ff16614c6a5760405162461bcd60e51b815260206004820152602b60248201527f496e697469616c697a61626c653a20636f6e7472616374206973206e6f74206960448201527f6e697469616c697a696e670000000000000000000000000000000000000000006064820152608401610b98565b6065805460ff19169055565b5f54610100900460ff16614cf25760405162461bcd60e51b815260206004820152602b60248201527f496e697469616c697a61626c653a20636f6e7472616374206973206e6f74206960448201527f6e697469616c697a696e670000000000000000000000000000000000000000006064820152608401610b98565b6122f03361454c565b60655460ff166122f05760405162461bcd60e51b815260206004820152601460248201527f5061757361626c653a206e6f74207061757365640000000000000000000000006044820152606401610b98565b5f825f03614d5c575082614be7565b609b546001600160a01b03165f5b84811015614e09576040517fae453cd5000000000000000000000000000000000000000000000000000000008152600481018590525f906001600160a01b0384169063ae453cd590602401602060405180830381865afa158015614dd0573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190614df4919061524e565b87525060209095019460019384019301614d6a565b5093949350505050565b5f60208284031215614e23575f80fd5b5035919050565b80356001600160a01b0381168114614394575f80fd5b5f60208284031215614e50575f80fd5b614be782614e2a565b5f8083601f840112614e69575f80fd5b50813567ffffffffffffffff811115614e80575f80fd5b602083019150836020828501011115614e97575f80fd5b9250929050565b5f805f60408486031215614eb0575f80fd5b833567ffffffffffffffff811115614ec6575f80fd5b614ed286828701614e59565b909790965060209590950135949350505050565b5f8060208385031215614ef7575f80fd5b823567ffffffffffffffff811115614f0d575f80fd5b614f1985828601614e59565b90969095509350505050565b5f60e082840312156142e7575f80fd5b5f606082840312156142e7575f80fd5b5f8060408385031215614f56575f80fd5b823567ffffffffffffffff80821115614f6d575f80fd5b614f7986838701614f25565b93506020850135915080821115614f8e575f80fd5b50614f9b85828601614f35565b9150509250929050565b5f805f805f8060808789031215614fba575f80fd5b863567ffffffffffffffff80821115614fd1575f80fd5b614fdd8a838b01614f25565b97506020890135915080821115614ff2575f80fd5b614ffe8a838b01614f35565b96506040890135915080821115615013575f80fd5b61501f8a838b01614e59565b90965094506060890135915080821115615037575f80fd5b5061504489828a01614e59565b979a9699509497509295939492505050565b5f805f805f8060c0878903121561506b575f80fd5b61507487614e2a565b955061508260208801614e2a565b945061509060408801614e2a565b9350606087013592506080870135915060a087013590509295509295509295565b5f805f80604085870312156150c4575f80fd5b843567ffffffffffffffff808211156150db575f80fd5b6150e788838901614e59565b909650945060208701359150808211156150ff575f80fd5b5061510c87828801614e59565b95989497509550505050565b801515811461300c575f80fd5b5f60208284031215615135575f80fd5b8135614be781615118565b803567ffffffffffffffff81168114614394575f80fd5b5f8060408385031215615168575f80fd5b61517183615140565b946020939093013593505050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601160045260245ffd5b808201808211156118475761184761517f565b818103818111156118475761184761517f565b5f602082840312156151e2575f80fd5b8151614be781615118565b5f8083357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe1843603018112615220575f80fd5b83018035915067ffffffffffffffff82111561523a575f80fd5b602001915036819003821315614e97575f80fd5b5f6020828403121561525e575f80fd5b5051919050565b5f60208284031215615275575f80fd5b813561ffff81168114614be7575f80fd5b5f60208284031215615296575f80fd5b813560ff81168114614be7575f80fd5b5f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff82036152d6576152d661517f565b5060010190565b5f602082840312156152ed575f80fd5b614be782615140565b818382375f9101908152919050565b81835281816020850137505f602082840101525f60207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f840116840101905092915050565b5f6080820187835260206080602085015281885180845260a08601915060208a0193505f5b818110156153965784516001600160a01b031683529383019391830191600101615371565b505087604086015284810360608601526153b1818789615305565b9a9950505050505050505050565b858152846020820152608060408201525f6153de608083018587615305565b90508260608301529695505050505050565b5f82515f5b8181101561540f57602081860181015185830152016153f5565b505f920191825250919050565b80820281158282048414176118475761184761517f565b5f82615466577f4e487b71000000000000000000000000000000000000000000000000000000005f52601260045260245ffd5b500490565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52603260045260245ffd5b7f4e487b71000000000000000000000000000000000000000000000000000000005f52604160045260245ffd5b5f82601f8301126154d4575f80fd5b8135602067ffffffffffffffff808311156154f1576154f1615498565b8260051b6040517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0603f8301168101818110848211171561553457615534615498565b6040529384526020818701810194908101925087851115615553575f80fd5b6020870191505b8482101561557a5761556b82614e2a565b8352918301919083019061555a565b979650505050505050565b5f805f805f8060c0878903121561559a575f80fd5b86359550602087013567ffffffffffffffff808211156155b8575f80fd5b6155c48a838b016154c5565b96506040890135955060608901359150808211156155e0575f80fd5b6155ec8a838b016154c5565b94506080890135935060a0890135915080821115615608575f80fd5b5061561589828a016154c5565b915050929550929550929556fea164736f6c6343000818000a +0x608060405260043610610369575f3560e01c806388b1ea09116101c8578063b3e0a509116100fd578063d279c1911161009d578063e3fff1dd1161006d578063e3fff1dd14610b2c578063f2fde38b14610b4b578063f8fa010f14610b6a578063fb1e8b0414610b89575f80fd5b8063d279c19114610ac4578063d8dc99d214610ae3578063de8b303514610af8578063dff7827e14610b17575f80fd5b8063c5553892116100d8578063c555389214610a5e578063cd4edc6914610a7d578063ce5db8d614610a90578063cf9a674514610aa5575f80fd5b8063b3e0a50914610a01578063b8d0a1b014610a20578063bedb86fb14610a3f575f80fd5b8063a415d8dc11610168578063abc8d68d11610143578063abc8d68d14610983578063b31a77d3146109ae578063b3484425146109c3578063b35dac4e146109e2575f80fd5b8063a415d8dc14610921578063a479265d1461094f578063a4f209b014610964575f80fd5b80638f1d3776116101a35780638f1d377614610802578063910129d4146108b257806397fc007c146108e3578063a3c9e94c14610902575f80fd5b806388b1ea09146107a15780638da5cb5b146107ba5780638ef622a8146107d7575f80fd5b80632b7ac3f31161029e5780635f77cf1d1161023e57806368589dfa1161021957806368589dfa146107245780636c578c1d1461074f578063715018a61461076e578063728cdbca14610782575f80fd5b80635f77cf1d1461069a57806361267290146106e657806367caa37a14610705575f80fd5b806341f756da1161027957806341f756da146105df57806357e0af6c146105f25780635c975abb146106115780635ef7a94a14610628575f80fd5b80632b7ac3f3146105745780633b70c18a146105ab5780633e001b66146105ca575f80fd5b80631544ba3a1161030957806321e2f9e0116102e457806321e2f9e0146104d45780632362f03e146104f35780632571098d1461051e5780632a213ba114610549575f80fd5b80631544ba3a1461047057806318463fb01461048f57806318af3b2b146104a4575f80fd5b806310d445831161034457806310d44583146103fb578063116a1f421461041a578063121dcd501461043c5780631336110114610451575f80fd5b806304d7721514610374578063059def61146103b75780630ceb6780146103da575f80fd5b3661037057005b5f80fd5b34801561037f575f80fd5b506103a261038e36600461529d565b60a36020525f908152604090205460ff1681565b60405190151581526020015b60405180910390f35b3480156103c2575f80fd5b506103cc609d5481565b6040519081526020016103ae565b3480156103e5575f80fd5b506103f96103f43660046152ca565b610b9e565b005b348015610406575f80fd5b506103f9610415366004615328565b610c70565b348015610425575f80fd5b506103a261043436600461529d565b609d54101590565b348015610447575f80fd5b506103cc609e5481565b34801561045c575f80fd5b506103f961046b366004615370565b610fb5565b34801561047b575f80fd5b506103f961048a3660046153bf565b6114db565b34801561049a575f80fd5b506103cc60a75481565b3480156104af575f80fd5b506103a26104be36600461529d565b5f90815260a26020526040902060010154421090565b3480156104df575f80fd5b506103a26104ee36600461529d565b61189a565b3480156104fe575f80fd5b506103cc61050d36600461529d565b60a16020525f908152604090205481565b348015610529575f80fd5b506103cc61053836600461529d565b60a06020525f908152604090205481565b348015610554575f80fd5b506103cc61056336600461529d565b60ad6020525f908152604090205481565b34801561057f575f80fd5b50609c54610593906001600160a01b031681565b6040516001600160a01b0390911681526020016103ae565b3480156105b6575f80fd5b50609b54610593906001600160a01b031681565b3480156105d5575f80fd5b506103cc60aa5481565b6103f96105ed36600461544d565b6118c8565b3480156105fd575f80fd5b506103f961060c36600461529d565b611b66565b34801561061c575f80fd5b5060655460ff166103a2565b348015610633575f80fd5b5061067161064236600461529d565b60a26020525f90815260409020805460018201546002830154600390930154919290916001600160a01b031684565b604080519485526020850193909352918301526001600160a01b031660608201526080016103ae565b3480156106a5575f80fd5b506106cd7f000000000000000000000000000000000000000000000000000000000000000081565b60405167ffffffffffffffff90911681526020016103ae565b3480156106f1575f80fd5b506103f961070036600461529d565b611c11565b348015610710575f80fd5b506103f961071f36600461544d565b611dee565b34801561072f575f80fd5b506103cc61073e36600461529d565b60ab6020525f908152604090205481565b34801561075a575f80fd5b506103f96107693660046152ca565b612139565b348015610779575f80fd5b506103f96121f6565b34801561078d575f80fd5b506103f961079c366004615487565b612209565b3480156107ac575f80fd5b5060a6546103a29060ff1681565b3480156107c5575f80fd5b506033546001600160a01b0316610593565b3480156107e2575f80fd5b506103cc6107f13660046152ca565b60ae6020525f908152604090205481565b34801561080d575f80fd5b5061086e61081c36600461529d565b60a46020525f9081526040902080546001820154600283015460039093015467ffffffffffffffff831693680100000000000000009093046001600160a01b0316929060ff8082169161010090041686565b6040805167ffffffffffffffff90971687526001600160a01b03909516602087015293850192909252606084015215156080830152151560a082015260c0016103ae565b3480156108bd575f80fd5b506103a26108cc36600461529d565b5f90815260a4602052604090206003015460ff1690565b3480156108ee575f80fd5b506103f96108fd3660046152ca565b612512565b34801561090d575f80fd5b50609754610593906001600160a01b031681565b34801561092c575f80fd5b506103a261093b3660046152ca565b609f6020525f908152604090205460ff1681565b34801561095a575f80fd5b506103cc60995481565b34801561096f575f80fd5b506103f961097e36600461529d565b6125f5565b34801561098e575f80fd5b506103cc61099d3660046152ca565b60a56020525f908152604090205481565b3480156109b9575f80fd5b506103cc60a85481565b3480156109ce575f80fd5b506103f96109dd3660046154e2565b6126a5565b3480156109ed575f80fd5b506103f96109fc3660046152ca565b6129b2565b348015610a0c575f80fd5b506103f9610a1b366004615370565b612a0b565b348015610a2b575f80fd5b506103f9610a3a3660046154e2565b612d9c565b348015610a4a575f80fd5b506103f9610a59366004615556565b612e25565b348015610a69575f80fd5b506103f9610a7836600461529d565b612f63565b6103f9610a8b366004615588565b613006565b348015610a9b575f80fd5b506103cc60985481565b348015610ab0575f80fd5b506103f9610abf36600461529d565b613598565b348015610acf575f80fd5b506103f9610ade3660046152ca565b61370f565b348015610aee575f80fd5b506103cc60ac5481565b348015610b03575f80fd5b506103a2610b1236600461529d565b6137ce565b348015610b22575f80fd5b506103cc609a5481565b348015610b37575f80fd5b506103f9610b4636600461529d565b613818565b348015610b56575f80fd5b506103f9610b653660046152ca565b6138bb565b348015610b75575f80fd5b506103f9610b843660046152ca565b613948565b348015610b94575f80fd5b506103cc60a95481565b610ba6613e4d565b6001600160a01b0381165f908152609f602052604090205460ff1615610c135760405162461bcd60e51b815260206004820152601f60248201527f6163636f756e7420697320616c72656164792061206368616c6c656e6765720060448201526064015b60405180910390fd5b6001600160a01b0381165f818152609f6020908152604091829020805460ff1916600190811790915591519182527f7feb653c7b1f0d23daeed494225b3f28851cdc8973fcc653866d9b6e205fc00991015b60405180910390a250565b610c78613e4d565b5f8111610cc75760405162461bcd60e51b815260206004820152601560248201527f636f756e74206d757374206265206e6f6e7a65726f00000000000000000000006044820152606401610c0a565b5f80610cd38585613ea7565b915091505f610ce6836001015160c01c90565b5f81815260a160205260409020549091508214610d455760405162461bcd60e51b815260206004820152601460248201527f696e636f727265637420626174636820686173680000000000000000000000006044820152606401610c0a565b5f60a181610d5387856155dd565b81526020019081526020015f205414610dd35760405162461bcd60e51b8152602060048201526024808201527f726576657274696e67206d7573742073746172742066726f6d2074686520656e60448201527f64696e67000000000000000000000000000000000000000000000000000000006064820152608401610c0a565b609d548111610e4a5760405162461bcd60e51b815260206004820152602160248201527f63616e206f6e6c792072657665727420756e46696e616c697a6564206261746360448201527f68000000000000000000000000000000000000000000000000000000000000006064820152608401610c0a565b610e556001826155f0565b609e555b8315610fad57604051829082907ecae2739091badfd91c373f0a16cede691e0cd25bb80cff77dd5caeb4710146905f90a3610e9381613f4f565b5f81815260a16020526040812055610eaa816137ce565b15610f05575f81815260a460209081526040808320600181015490546801000000000000000090046001600160a01b0316845260a59092528220805491929091610ef59084906155dd565b909155505060a6805460ff191690555b5f81815260a46020526040812080547fffffffff00000000000000000000000000000000000000000000000000000000168155600181018290556002810191909155600301805461ffff1916905560a85415801590610f65575060a85481145b15610f6f575f60a8555b6001015f81815260a160205260409020547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff90940193915081610e59575b505050505050565b60a854156110055760405162461bcd60e51b815260206004820152601660248201527f70656e64696e67207265766572742072657175657374000000000000000000006044820152606401610c0a565b61100d614002565b5f806110198484613ea7565b915091505f61102c836001015160c01c90565b5f81815260a16020526040902054909150821461108b5760405162461bcd60e51b815260206004820152601460248201527f696e636f727265637420626174636820686173680000000000000000000000006044820152606401610c0a565b6110948161189a565b6110e05760405162461bcd60e51b815260206004820152600f60248201527f6261746368206e6f7420657869737400000000000000000000000000000000006044820152606401610c0a565b6110e9816137ce565b156111365760405162461bcd60e51b815260206004820152601260248201527f626174636820696e206368616c6c656e676500000000000000000000000000006044820152606401610c0a565b5f81815260a4602052604090206003015460ff16156111975760405162461bcd60e51b815260206004820152601660248201527f62617463682073686f756c6420626520726576657274000000000000000000006044820152606401610c0a565b5f81815260a260205260409020600101544210156111f75760405162461bcd60e51b815260206004820152601960248201527f626174636820696e206368616c6c656e67652077696e646f77000000000000006044820152606401610c0a565b605983015160a05f61120a6001856155f0565b81526020019081526020015f2054146112655760405162461bcd60e51b815260206004820152601d60248201527f696e636f72726563742070726576696f757320737461746520726f6f740000006044820152606401610c0a565b5f81815260a06020526040902054156112c05760405162461bcd60e51b815260206004820152601660248201527f626174636820616c7265616479207665726966696564000000000000000000006044820152606401610c0a565b80609d54600101146113145760405162461bcd60e51b815260206004820152601560248201527f696e636f727265637420626174636820696e64657800000000000000000000006044820152606401610c0a565b609d819055600160a35f611329866099015190565b815260208101919091526040015f20805460ff191691151591909117905560798301515f82815260a0602052604090205561137861136b846011015160c01c90565b600985015160c01c614055565b61138181613f4f565b60a25f61138f6001846155f0565b815260208101919091526040015f908120818155600180820183905560028201839055600390910180547fffffffffffffffffffffffff000000000000000000000000000000000000000016905560ab91906113eb90846155f0565b81526020019081526020015f205f905560ad5f60018361140b91906155f0565b81526020019081526020015f205f905560a45f60018361142b91906155f0565b815260208082019290925260409081015f90812080547fffffffff000000000000000000000000000000000000000000000000000000001681556001810182905560028101829055600301805461ffff1916905583815260a1909252902054817f26ba82f907317eedc97d0cbef23de76a43dd6edb563bdb6e9407645b950a7a2d6114b7866079015190565b60998701516040805192835260208301919091520160405180910390a35050505050565b60a8541561152b5760405162461bcd60e51b815260206004820152601660248201527f70656e64696e67207265766572742072657175657374000000000000000000006044820152606401610c0a565b611533614002565b60ac54609e545f90815260a260205260408120549091429161155591906155dd565b1090505f4260ac54609b5f9054906101000a90046001600160a01b03166001600160a01b031663b59b1a786040518163ffffffff1660e01b8152600401602060405180830381865afa1580156115ad573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906115d19190615603565b6115db91906155dd565b109050811580156115e95750805b15611650575f6115ff6080890160608a0161561a565b61ffff16116116505760405162461bcd60e51b815260206004820152600b60248201527f6c316d73672064656c61790000000000000000000000000000000000000000006044820152606401610c0a565b81806116595750805b6116a55760405162461bcd60e51b815260206004820152600e60248201527f696e76616c69642074696d696e670000000000000000000000000000000000006044820152606401610c0a565b5f6116bb6116b660208a018a61563b565b613ea7565b5090505f6116cd826001015160c01c90565b6116d89060016155dd565b5f81815260ad60205260408120549192509015611778575f49156117645760405162461bcd60e51b815260206004820152602f60248201527f6d757374206e6f7420636172727920626c6f62207768656e207573696e67207360448201527f746f72656420626c6f62206861736800000000000000000000000000000000006064820152608401610c0a565b505f81815260ad6020526040902054611793565b61179061178860208c018c61569c565b60ff16614112565b90505b61179e8a5f8361424d565b5f806117aa8b8b613ea7565b915091505f6117bd836001015160c01c90565b905080609e54146118105760405162461bcd60e51b815260206004820152601660248201527f696e636f727265637420626174636820686561646572000000000000000000006044820152606401610c0a565b5f81815260a16020526040902054821461186c5760405162461bcd60e51b815260206004820152601460248201527f696e636f727265637420626174636820686173680000000000000000000000006044820152606401610c0a565b5f81815260a2602052604090204260019091015561188b838b8b6147af565b50505050505050505050505050565b5f81815260a26020526040812054158015906118c257505f82815260a1602052604090205415155b92915050565b6097546001600160a01b0316639f8a13d7336040517fffffffff0000000000000000000000000000000000000000000000000000000060e084901b1681526001600160a01b039091166004820152602401602060405180830381865afa158015611934573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061195891906156bc565b6119a45760405162461bcd60e51b815260206004820152601d60248201527f6f6e6c7920616374697665207375626d697474657220616c6c6f7765640000006044820152606401610c0a565b60a854156119f45760405162461bcd60e51b815260206004820152601660248201527f70656e64696e67207265766572742072657175657374000000000000000000006044820152606401610c0a565b6119fc614002565b5f49611a4a5760405162461bcd60e51b815260206004820152600f60248201527f6d75737420636172727920626c6f6200000000000000000000000000000000006044820152606401610c0a565b60ac54609b54604080517fb59b1a7800000000000000000000000000000000000000000000000000000000815290514293926001600160a01b03169163b59b1a789160048083019260209291908290030181865afa158015611aae573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611ad29190615603565b611adc91906155dd565b1015611b44575f611af3608083016060840161561a565b61ffff1611611b445760405162461bcd60e51b815260206004820152600b60248201527f6c316d73672064656c61790000000000000000000000000000000000000000006044820152606401610c0a565b5f611b55611788602084018461569c565b9050611b6282338361424d565b5050565b611b6e613e4d565b5f81118015611b7f57506099548114155b611bcb5760405162461bcd60e51b815260206004820152601860248201527f696e76616c6964206e65772070726f6f662077696e646f7700000000000000006044820152606401610c0a565b609980549082905560408051828152602081018490527f1e3a2094feb4b696dd3d7caea38ad2f41dbdcac3fa3943c7a693aff8a64b0a6191015b60405180910390a15050565b5f54600290610100900460ff16158015611c3157505f5460ff8083169116105b611ca35760405162461bcd60e51b815260206004820152602e60248201527f496e697469616c697a61626c653a20636f6e747261637420697320616c72656160448201527f647920696e697469616c697a65640000000000000000000000000000000000006064820152608401610c0a565b5f805461ffff191660ff831617610100179055611cbe613e4d565b5f5460ff1660ff16600214611d155760405162461bcd60e51b815260206004820152601660248201527f6d757374206861766520696e697469616c697a656421000000000000000000006044820152606401610c0a565b81611d885760405162461bcd60e51b815260206004820152602760248201527f63616e206e6f742073657420737461746520726f6f742077697468206279746560448201527f73333228302921000000000000000000000000000000000000000000000000006064820152608401610c0a565b609e545f90815260ab6020526040902054611db157609e545f90815260ab602052604090208290555b5f805461ff001916905560405160ff821681527f7f26b83ff96e1f2b6a682f133852f6798a09c465da95921460cefb384740249890602001611c05565b6097546001600160a01b0316639f8a13d7336040517fffffffff0000000000000000000000000000000000000000000000000000000060e084901b1681526001600160a01b039091166004820152602401602060405180830381865afa158015611e5a573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611e7e91906156bc565b611eca5760405162461bcd60e51b815260206004820152601d60248201527f6f6e6c7920616374697665207375626d697474657220616c6c6f7765640000006044820152606401610c0a565b60a85415611f1a5760405162461bcd60e51b815260206004820152601660248201527f70656e64696e67207265766572742072657175657374000000000000000000006044820152606401610c0a565b611f22614002565b5f4915611f715760405162461bcd60e51b815260206004820152601f60248201527f636f6d6d69745374617465206d757374206e6f7420636172727920626c6f62006044820152606401610c0a565b5f611f826116b6602084018461563b565b5090505f611f94826001015160c01c90565b611f9f9060016155dd565b5f81815260ad60205260409020549091506120225760405162461bcd60e51b815260206004820152602260248201527f6e6f2073746f72656420626c6f62206861736820666f7220746869732062617460448201527f63680000000000000000000000000000000000000000000000000000000000006064820152608401610c0a565b60ac54609b54604080517fb59b1a7800000000000000000000000000000000000000000000000000000000815290514293926001600160a01b03169163b59b1a789160048083019260209291908290030181865afa158015612086573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906120aa9190615603565b6120b491906155dd565b101561211c575f6120cb608085016060860161561a565b61ffff161161211c5760405162461bcd60e51b815260206004820152600b60248201527f6c316d73672064656c61790000000000000000000000000000000000000000006044820152606401610c0a565b61213483335f84815260ad602052604090205461424d565b505050565b612141613e4d565b6001600160a01b0381165f908152609f602052604090205460ff166121a85760405162461bcd60e51b815260206004820152601b60248201527f6163636f756e74206973206e6f742061206368616c6c656e67657200000000006044820152606401610c0a565b6001600160a01b0381165f818152609f60209081526040808320805460ff19169055519182527f7feb653c7b1f0d23daeed494225b3f28851cdc8973fcc653866d9b6e205fc0099101610c65565b6121fe613e4d565b6122075f614962565b565b5f54610100900460ff161580801561222757505f54600160ff909116105b806122405750303b15801561224057505f5460ff166001145b6122b25760405162461bcd60e51b815260206004820152602e60248201527f496e697469616c697a61626c653a20636f6e747261637420697320616c72656160448201527f647920696e697469616c697a65640000000000000000000000000000000000006064820152608401610c0a565b5f805460ff1916600117905580156122d3575f805461ff0019166101001790555b6001600160a01b03861615806122f057506001600160a01b038516155b15612327576040517fecc6fdf000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6001600160a01b03871661237d5760405162461bcd60e51b815260206004820152601a60248201527f696e76616c6964207375626d697474657220636f6e74726163740000000000006044820152606401610c0a565b6123856149cb565b61238d614a4f565b609780546001600160a01b03808a167fffffffffffffffffffffffff000000000000000000000000000000000000000092831617909255609b8054898416908316179055609c805492881692909116821790556098859055609984905560a98390556040515f907f728af3d16a5760405e27a082c98ab272e9f0a1d02f0085d41532a26093aedd96908290a3604080515f8152602081018690527fa577f4223f91f74e2dad65bbb8c30807587ae95d0d34288057bb3ec0d398a437910160405180910390a1604080515f8152602081018590527f1e3a2094feb4b696dd3d7caea38ad2f41dbdcac3fa3943c7a693aff8a64b0a61910160405180910390a1604080515f8152602081018490527ffb81bce17f015797e11949d3c332e2bf9453faf68f728447426803138f2b0223910160405180910390a18015612509575f805461ff0019169055604051600181527f7f26b83ff96e1f2b6a682f133852f6798a09c465da95921460cefb38474024989060200160405180910390a15b50505050505050565b61251a613e4d565b6001600160a01b038116158015906125405750609c546001600160a01b03828116911614155b61258c5760405162461bcd60e51b815260206004820152601460248201527f696e76616c6964206e65772076657269666965720000000000000000000000006044820152606401610c0a565b609c80546001600160a01b038381167fffffffffffffffffffffffff0000000000000000000000000000000000000000831681179093556040519116919082907f728af3d16a5760405e27a082c98ab272e9f0a1d02f0085d41532a26093aedd96905f90a35050565b6125fd613e4d565b5f8111801561260d575060648111155b801561261b575060a9548114155b6126675760405162461bcd60e51b815260206004820152601f60248201527f696e76616c69642070726f6f66207265776172642070657263656e74616765006044820152606401610c0a565b60a980549082905560408051828152602081018490527ffb81bce17f015797e11949d3c332e2bf9453faf68f728447426803138f2b02239101611c05565b60a854156126f55760405162461bcd60e51b815260206004820152601660248201527f70656e64696e67207265766572742072657175657374000000000000000000006044820152606401610c0a565b6126fd614002565b6097546001600160a01b0316639f8a13d7336040517fffffffff0000000000000000000000000000000000000000000000000000000060e084901b1681526001600160a01b039091166004820152602401602060405180830381865afa158015612769573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061278d91906156bc565b6127d95760405162461bcd60e51b815260206004820152601d60248201527f6f6e6c7920616374697665207375626d697474657220616c6c6f7765640000006044820152606401610c0a565b5f806127e58686613ea7565b915091505f6127f8836001015160c01c90565b5f81815260a1602052604090205490915082146128575760405162461bcd60e51b815260206004820152601460248201527f696e636f727265637420626174636820686173680000000000000000000000006044820152606401610c0a565b612860816137ce565b6128ac5760405162461bcd60e51b815260206004820152601260248201527f626174636820696e206368616c6c656e676500000000000000000000000000006044820152606401610c0a565b5f81815260a46020526040902060038101805461ff00191661010017905560a6805460ff1916905560995460029091015442916128e8916155dd565b11612967575f81815260a4602090815260408083206003908101805460ff1916600117905560a2835292819020909201548251808401909352600783527f54696d656f757400000000000000000000000000000000000000000000000000918301919091526129629183916001600160a01b031690614ad3565b612509565b6129728386866147af565b61250981336040518060400160405280600d81526020017f50726f6f66207375636365737300000000000000000000000000000000000000815250614c32565b6129ba613e4d565b60aa80545f9091556129cc8282614cfe565b604080516001600160a01b0384168152602081018390527fb1b2058a6969e2d25e47bcaebe8ae21c29a23b2752429315b75e2f4f285f3d879101611c05565b612a13613e4d565b5f805260a06020527fb84a74ec6ef4d0e83b6006dfaa014ab4026f9f3b97d186e604d29998a4e808ea5415612a8a5760405162461bcd60e51b815260206004820152601660248201527f67656e6573697320626174636820696d706f72746564000000000000000000006044820152606401610c0a565b5f80612a968484613ea7565b915091505f612aa9836001015160c01c90565b90508015612af95760405162461bcd60e51b815260206004820152601360248201527f696e76616c696420626174636820696e646578000000000000000000000000006044820152606401610c0a565b5f612b05846079015190565b905080612b545760405162461bcd60e51b815260206004820152600f60248201527f7a65726f20737461746520726f6f7400000000000000000000000000000000006044820152606401610c0a565b600984015160c01c15612ba95760405162461bcd60e51b815260206004820152601d60248201527f6c31206d65737361676520706f707065642073686f756c6420626520300000006044820152606401610c0a565b5f612bb5856019015190565b03612c025760405162461bcd60e51b815260206004820152600e60248201527f7a65726f206461746120686173680000000000000000000000000000000000006044820152606401610c0a565b7f010657f37554c781402a22917dee2f75def7ab966d7b770905398eba3c444014612c2e856039015190565b14612c7b5760405162461bcd60e51b815260206004820152601660248201527f696e76616c69642076657273696f6e65642068617368000000000000000000006044820152606401610c0a565b5f82815260a1602090815260408083208690558051608081018252428082528184019081528183018581526060830186815288875260a28652848720935184559151600184015551600283015551600390910180547fffffffffffffffffffffffff0000000000000000000000000000000000000000166001600160a01b03909216919091179055603987015160ad83528184205560ab825280832084905560a0909152808220839055609e849055609d84905551849184917f2c32d4ae151744d0bf0b9464a3e897a1d17ed2f1af71f7c9a75f12ce0d28238f9190a3604080518281525f6020820152849184917f26ba82f907317eedc97d0cbef23de76a43dd6edb563bdb6e9407645b950a7a2d910160405180910390a3505050505050565b5f80612da88686613ea7565b915091505f612dbb836001015160c01c90565b5f81815260a160205260409020549091508214612e1a5760405162461bcd60e51b815260206004820152601460248201527f696e636f727265637420626174636820686173680000000000000000000000006044820152606401610c0a565b6125098386866147af565b612e2d613e4d565b8015612f3157612e3b614da3565b60a65460ff1615612ef05760a7545f90815260a460209081526040808320600181015490546801000000000000000090046001600160a01b0316845260a59092528220805491929091612e8f9084906155dd565b909155505060a7545f90815260a46020526040812080547fffffffff00000000000000000000000000000000000000000000000000000000168155600181018290556002810191909155600301805461ffff1916905560a6805460ff191690555b7f62e78cea01bee320cd4e420270b5ea74000d11b0c9f74754ebdbfc544b05a258335b6040516001600160a01b03909116815260200160405180910390a150565b612f39614dfd565b7f5db9ee0a495bf2e6ff9c91a7834c1ba4fdd244a5e8aa4e537bd38aeae4b073aa33612f13565b50565b612f6b613e4d565b5f81118015612f7c575060ac548114155b612fc85760405162461bcd60e51b815260206004820152601f60248201527f696e76616c6964206e657720726f6c6c75702064656c617920706572696f64006044820152606401610c0a565b60ac80549082905560408051828152602081018490527f624e47dc9fb8f8cfeaf4ead4710277cc1757136cfa885e465514cf6d510f0ad19101611c05565b335f908152609f602052604090205460ff166130645760405162461bcd60e51b815260206004820152601760248201527f6f6e6c79206368616c6c656e67657220616c6c6f7765640000000000000000006044820152606401610c0a565b60a854156130b45760405162461bcd60e51b815260206004820152601660248201527f70656e64696e67207265766572742072657175657374000000000000000000006044820152606401610c0a565b6130bc614002565b60a65460ff161561310f5760405162461bcd60e51b815260206004820152601460248201527f616c726561647920696e206368616c6c656e67650000000000000000000000006044820152606401610c0a565b8167ffffffffffffffff16609d541061316a5760405162461bcd60e51b815260206004820152601760248201527f626174636820616c72656164792066696e616c697a65640000000000000000006044820152606401610c0a565b67ffffffffffffffff82165f90815260a1602052604090205481146131d15760405162461bcd60e51b815260206004820152601460248201527f696e636f727265637420626174636820686173680000000000000000000000006044820152606401610c0a565b6131e48267ffffffffffffffff1661189a565b6132305760405162461bcd60e51b815260206004820152600f60248201527f6261746368206e6f7420657869737400000000000000000000000000000000006044820152606401610c0a565b67ffffffffffffffff82165f90815260a460205260409020546801000000000000000090046001600160a01b0316156132ab5760405162461bcd60e51b815260206004820152601860248201527f626174636820616c7265616479206368616c6c656e67656400000000000000006044820152606401610c0a565b67ffffffffffffffff82165f90815260a26020526040902060010154421061333b5760405162461bcd60e51b815260206004820152603360248201527f63616e6e6f74206368616c6c656e6765206261746368206f757473696465207460448201527f6865206368616c6c656e67652077696e646f77000000000000000000000000006064820152608401610c0a565b60975f9054906101000a90046001600160a01b03166001600160a01b0316630d13fd7b6040518163ffffffff1660e01b8152600401602060405180830381865afa15801561338b573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906133af9190615603565b3410156133fe5760405162461bcd60e51b815260206004820152601260248201527f696e73756666696369656e742076616c756500000000000000000000000000006044820152606401610c0a565b67ffffffffffffffff82811660a78190556040805160c0810182528281523360208083018281523484860190815242606086019081525f6080870181815260a0880182815299825260a4909552969096209451855492516001600160a01b031668010000000000000000027fffffffff000000000000000000000000000000000000000000000000000000009093169816979097171783559451600183015591516002820155925160039093018054925115156101000261ff00199415159490941661ffff19909316929092179290921790556001600160a01b03168267ffffffffffffffff167f3a6ea19df25b49e7624e313ce7c1ab23984238e93727260db56a81735b1b99763460405161351691815260200190565b60405180910390a35f609d54600161352e91906155dd565b90505b609e548111613586578267ffffffffffffffff168114613574576099545f82815260a260205260408120600101805490919061356e9084906155dd565b90915550505b8061357e816156d7565b915050613531565b505060a6805460ff1916600117905550565b5f54600390610100900460ff161580156135b857505f5460ff8083169116105b61362a5760405162461bcd60e51b815260206004820152602e60248201527f496e697469616c697a61626c653a20636f6e747261637420697320616c72656160448201527f647920696e697469616c697a65640000000000000000000000000000000000006064820152608401610c0a565b5f805461ffff191660ff831617610100179055613645613e4d565b815f036136945760405162461bcd60e51b815260206004820152601b60248201527f696e76616c696420726f6c6c75702064656c617920706572696f6400000000006044820152606401610c0a565b60ac829055604080515f8152602081018490527f624e47dc9fb8f8cfeaf4ead4710277cc1757136cfa885e465514cf6d510f0ad1910160405180910390a15f805461ff001916905560405160ff821681527f7f26b83ff96e1f2b6a682f133852f6798a09c465da95921460cefb384740249890602001611c05565b335f90815260a560205260408120549081900361376e5760405162461bcd60e51b815260206004820152601c60248201527f696e76616c69642062617463684368616c6c656e6765526577617264000000006044820152606401610c0a565b335f90815260a560205260408120556137878282614cfe565b816001600160a01b03167f9c25fa83f414ed363c8d39c98fb3e17567b3431cede71eb062c49d2a63ce247a826040516137c291815260200190565b60405180910390a25050565b5f81815260a460205260408120546801000000000000000090046001600160a01b0316158015906118c25750505f90815260a46020526040902060030154610100900460ff161590565b613820613e4d565b5f8111801561383157506098548114155b61387d5760405162461bcd60e51b815260206004820152601b60248201527f696e76616c6964206e65772066696e616c697a6520706572696f6400000000006044820152606401610c0a565b609880549082905560408051828152602081018490527fa577f4223f91f74e2dad65bbb8c30807587ae95d0d34288057bb3ec0d398a4379101611c05565b6138c3613e4d565b6001600160a01b03811661393f5760405162461bcd60e51b815260206004820152602660248201527f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160448201527f64647265737300000000000000000000000000000000000000000000000000006064820152608401610c0a565b612f6081614962565b5f54600490610100900460ff1615801561396857505f5460ff8083169116105b6139da5760405162461bcd60e51b815260206004820152602e60248201527f496e697469616c697a61626c653a20636f6e747261637420697320616c72656160448201527f647920696e697469616c697a65640000000000000000000000000000000000006064820152608401610c0a565b5f805461ffff191660ff8316176101001790556139f5613e4d565b6001600160a01b03821615801590613a1657505f826001600160a01b03163b115b613a625760405162461bcd60e51b815260206004820152601160248201527f696e76616c6964207375626d69747465720000000000000000000000000000006044820152606401610c0a565b6040805160048152602481019091526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fbfa02ba9000000000000000000000000000000000000000000000000000000001790525f90613ac6908490614e36565b905060a081901c15613b1a5760405162461bcd60e51b815260206004820152601760248201527f696e76616c696420726f6c6c757020656e636f64696e670000000000000000006044820152606401610c0a565b6040515f602482018190528291613bab908690604401604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529190526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167f9f8a13d700000000000000000000000000000000000000000000000000000000179052614e36565b6040805160048152602481019091526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167f0d13fd7b000000000000000000000000000000000000000000000000000000001790529091505f90613c12908790614e36565b90506001600160a01b0383163014613c6c5760405162461bcd60e51b815260206004820152601960248201527f7375626d697474657220726f6c6c7570206d69736d61746368000000000000006044820152606401610c0a565b81158015613c7957505f81115b613cc55760405162461bcd60e51b815260206004820152601760248201527f696e76616c6964207375626d69747465722073746174650000000000000000006044820152606401610c0a565b609d54609e5414613d185760405162461bcd60e51b815260206004820152600f60248201527f70656e64696e67206261746368657300000000000000000000000000000000006044820152606401610c0a565b60a85415613d685760405162461bcd60e51b815260206004820152601660248201527f70656e64696e67207265766572742072657175657374000000000000000000006044820152606401610c0a565b609d545f90815260a2602052604080822060030180547fffffffffffffffffffffffff000000000000000000000000000000000000000016905560975490516001600160a01b03898116939216917fde3ada76ae1aa387cc4c586c70304b9fae54b251958786c828aaa0ce0290f93991a35050609780547fffffffffffffffffffffffff0000000000000000000000000000000000000000166001600160a01b03861617905550505f805461ff001916905560405160ff821681527f7f26b83ff96e1f2b6a682f133852f6798a09c465da95921460cefb384740249890602001611c05565b6033546001600160a01b031633146122075760405162461bcd60e51b815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e65726044820152606401610c0a565b5f805f613eb48585614ebf565b90505f8160ff165f03613ed557613ecb8686614f2f565b9094509050613f41565b8160ff1660011480613eea57508160ff166002145b15613ef957613ecb8686614f98565b60405162461bcd60e51b815260206004820152601960248201527f556e737570706f727465642062617463682076657273696f6e000000000000006044820152606401610c0a565b808420925050509250929050565b5f81815260a260205260409020600301546001600160a01b031680613f72575050565b6001600160a01b0381165f90815260ae602052604090205480613fd75760405162461bcd60e51b815260206004820152601d60248201527f70656e64696e6720626174636820636f756e7420756e646572666c6f770000006044820152606401610c0a565b613fe26001826155f0565b6001600160a01b039092165f90815260ae60205260409020919091555050565b60655460ff16156122075760405162461bcd60e51b815260206004820152601060248201527f5061757361626c653a20706175736564000000000000000000000000000000006044820152606401610c0a565b805f03614060575050565b8082035f5b8281101561410c5761010081840381111561407f57508083035b609b546040517f3c7f528300000000000000000000000000000000000000000000000000000000815260048101859052602481018390526001600160a01b0390911690633c7f5283906044015f604051808303815f87803b1580156140e2575f80fd5b505af11580156140f4573d5f803e3d5ffd5b50505050610100830192505061010081019050614065565b50505050565b5f816002036141a1575f6040515f5b80498061412e575061413d565b60208202830152600101614121565b60208102808320920160405290925090508061419b5760405162461bcd60e51b815260206004820152601b60248201527f5632207265717569726573206174206c65617374203120626c6f6200000000006044820152606401610c0a565b50919050565b600149156142175760405162461bcd60e51b815260206004820152602560248201527f6c6567616379206261746368657320737570706f72742065786163746c79203160448201527f20626c6f620000000000000000000000000000000000000000000000000000006064820152608401610c0a565b5f4915614225575f496118c2565b507f010657f37554c781402a22917dee2f75def7ab966d7b770905398eba3c4440145b919050565b600261425c602085018561569c565b60ff1611156142ad5760405162461bcd60e51b815260206004820152600f60248201527f696e76616c69642076657273696f6e00000000000000000000000000000000006044820152606401610c0a565b60808301356142fe5760405162461bcd60e51b815260206004820152601b60248201527f70726576696f757320737461746520726f6f74206973207a65726f00000000006044820152606401610c0a565b60a083013561434f5760405162461bcd60e51b815260206004820152601660248201527f6e657720737461746520726f6f74206973207a65726f000000000000000000006044820152606401610c0a565b5f806143616116b6602087018761563b565b915091505f614374836001015160c01c90565b90505f60a1816143858460016155dd565b81526020019081526020015f2054146143e05760405162461bcd60e51b815260206004820152601760248201527f626174636820616c726561647920636f6d6d69747465640000000000000000006044820152606401610c0a565b609e5481146144315760405162461bcd60e51b815260206004820152601560248201527f696e636f727265637420626174636820696e64657800000000000000000000006044820152606401610c0a565b5f81815260a16020526040902054821461448d5760405162461bcd60e51b815260206004820152601b60248201527f696e636f727265637420706172656e74206261746368206861736800000000006044820152606401610c0a565b5f81815260ab60205260409020546080870135146144ed5760405162461bcd60e51b815260206004820152601d60248201527f696e636f72726563742070726576696f757320737461746520726f6f740000006044820152606401610c0a565b5f6144fc846011015160c01c90565b90505f61452861451260608a0160408b0161570e565b61452260808b0160608c0161561a565b84614feb565b905061453a6080890160608a0161561a565b60019384019361ffff91909116929092019160f99061455c60208b018b61569c565b60ff161061456957506101015b60408051828101909152955061458e8661458660208c018c61569c565b60ff1661502f565b60c084901b60018701526145bc866145ac60808c0160608d0161561a565b61ffff1660c01b60099190910152565b60c083811b6011880152601987018390526039870188905260808a0135605988015260a08a0135607988015289013560998701525f60b987015260d98601859052600161460c60208b018b61569c565b60ff161061463e5761463e8661462860608c0160408d0161570e565b67ffffffffffffffff1660c01b60f99190910152565b8086205f85815260a1602090815260408083209390935560ab815282822060a08d0135905560ad905290812088905560a65460ff16156146a75760a7545f90815260a46020526040902060020154609954429161469a916155dd565b6146a491906155f0565b90505b604051806080016040528042815260200182609854426146c791906155dd565b6146d191906155dd565b81526020016146e660608d0160408e0161570e565b67ffffffffffffffff1681526001600160a01b038b81166020928301525f88815260a28352604090819020845181559284015160018401558301516002830155606090920151600390910180547fffffffffffffffffffffffff0000000000000000000000000000000000000000169190921617905561476589615036565b5050609e8390555f83815260a16020526040808220549051909185917f2c32d4ae151744d0bf0b9464a3e897a1d17ed2f1af71f7c9a75f12ce0d28238f9190a35050505050505050565b806147fc5760405162461bcd60e51b815260206004820152601360248201527f496e76616c69642062617463682070726f6f66000000000000000000000000006044820152606401610c0a565b5f61480b846001015160c01c90565b90505f614819856039015190565b90505f7f0000000000000000000000000000000000000000000000000000000000000000614848876059015190565b6079880151609989015160b98a015160198b015160405160c09690961b7fffffffffffffffff000000000000000000000000000000000000000000000000166020870152602886019490945260488501929092526068840152608883015260a882015260c8810183905260e801604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529190528051602090910120609c549091506001600160a01b0316632c09a84861490a885160f81c90565b858888866040518663ffffffff1660e01b815260040161492e959493929190615727565b5f6040518083038186803b158015614944575f80fd5b505afa158015614956573d5f803e3d5ffd5b50505050505050505050565b603380546001600160a01b038381167fffffffffffffffffffffffff0000000000000000000000000000000000000000831681179093556040519116919082907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0905f90a35050565b5f54610100900460ff16614a475760405162461bcd60e51b815260206004820152602b60248201527f496e697469616c697a61626c653a20636f6e7472616374206973206e6f74206960448201527f6e697469616c697a696e670000000000000000000000000000000000000000006064820152608401610c0a565b612207615075565b5f54610100900460ff16614acb5760405162461bcd60e51b815260206004820152602b60248201527f496e697469616c697a61626c653a20636f6e7472616374206973206e6f74206960448201527f6e697469616c697a696e670000000000000000000000000000000000000000006064820152608401610c0a565b6122076150fd565b60a88390555f83815260a460205260408082205460975491517fc96be4cb0000000000000000000000000000000000000000000000000000000081526001600160a01b0386811660048301526801000000000000000090920482169392919091169063c96be4cb906024016020604051808303815f875af1158015614b5a573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190614b7e9190615603565b5f86815260a46020526040902060010154909150614b9d9082906155dd565b5f86815260a460209081526040808320546801000000000000000090046001600160a01b0316835260a590915281208054909190614bdc9084906155dd565b9091555050604051614bef90849061578a565b604051908190038120906001600160a01b0384169087907fe70d3820e244d5f71d1a6395db24f3460e8dca966edc1fd3625b6292880a877a905f90a45050505050565b5f83815260a4602052604081206001015460a954909190606490614c5690846157b6565b614c6091906157cd565b9050614c6c81836155f0565b60aa5f828254614c7c91906155dd565b90915550506001600160a01b0384165f90815260a5602052604081208054839290614ca89084906155dd565b9091555050604051614cbb90849061578a565b604051908190038120906001600160a01b0386169087907fe70d3820e244d5f71d1a6395db24f3460e8dca966edc1fd3625b6292880a877a905f90a45050505050565b8015611b62575f826001600160a01b0316826040515f6040518083038185875af1925050503d805f8114614d4d576040519150601f19603f3d011682016040523d82523d5f602084013e614d52565b606091505b50509050806121345760405162461bcd60e51b815260206004820152601b60248201527f526f6c6c75703a20455448207472616e73666572206661696c656400000000006044820152606401610c0a565b614dab614002565b6065805460ff191660011790557f62e78cea01bee320cd4e420270b5ea74000d11b0c9f74754ebdbfc544b05a258614de03390565b6040516001600160a01b03909116815260200160405180910390a1565b614e05615182565b6065805460ff191690557f5db9ee0a495bf2e6ff9c91a7834c1ba4fdd244a5e8aa4e537bd38aeae4b073aa33614de0565b5f805f805f855160208701885afa91503d905060208114821615614e5e5760205f803e5f5192505b818015614e6b5750806020145b614eb75760405162461bcd60e51b815260206004820152601b60248201527f696e76616c6964207375626d697474657220696e7465726661636500000000006044820152606401610c0a565b505092915050565b5f81614f0d5760405162461bcd60e51b815260206004820152601260248201527f456d7074792062617463682068656164657200000000000000000000000000006044820152606401610c0a565b82825f818110614f1f57614f1f615805565b919091013560f81c949350505050565b5f8160f9811015614f825760405162461bcd60e51b815260206004820152601d60248201527f626174636820686561646572206c656e67746820746f6f20736d616c6c0000006044820152606401610c0a565b6040519150808483378082016040529250929050565b5f816101018114614f825760405162461bcd60e51b815260206004820181905260248201527f626174636820686561646572206c656e67746820697320696e636f72726563746044820152606401610c0a565b6040805160c085901b815260f084901b6008820152600a60208502820181019092525f9181016150208161ffff8716866151d4565b82900390912095945050505050565b8082535050565b6001600160a01b03811615612f60576001600160a01b0381165f90815260ae6020526040812080546001929061506d9084906155dd565b909155505050565b5f54610100900460ff166150f15760405162461bcd60e51b815260206004820152602b60248201527f496e697469616c697a61626c653a20636f6e7472616374206973206e6f74206960448201527f6e697469616c697a696e670000000000000000000000000000000000000000006064820152608401610c0a565b6065805460ff19169055565b5f54610100900460ff166151795760405162461bcd60e51b815260206004820152602b60248201527f496e697469616c697a61626c653a20636f6e7472616374206973206e6f74206960448201527f6e697469616c697a696e670000000000000000000000000000000000000000006064820152608401610c0a565b61220733614962565b60655460ff166122075760405162461bcd60e51b815260206004820152601460248201527f5061757361626c653a206e6f74207061757365640000000000000000000000006044820152606401610c0a565b5f825f036151e3575082615296565b609b546001600160a01b03165f5b84811015615290576040517fae453cd5000000000000000000000000000000000000000000000000000000008152600481018590525f906001600160a01b0384169063ae453cd590602401602060405180830381865afa158015615257573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061527b9190615603565b875250602090950194600193840193016151f1565b50849150505b9392505050565b5f602082840312156152ad575f80fd5b5035919050565b80356001600160a01b0381168114614248575f80fd5b5f602082840312156152da575f80fd5b615296826152b4565b5f8083601f8401126152f3575f80fd5b50813567ffffffffffffffff81111561530a575f80fd5b602083019150836020828501011115615321575f80fd5b9250929050565b5f805f6040848603121561533a575f80fd5b833567ffffffffffffffff811115615350575f80fd5b61535c868287016152e3565b909790965060209590950135949350505050565b5f8060208385031215615381575f80fd5b823567ffffffffffffffff811115615397575f80fd5b6153a3858286016152e3565b90969095509350505050565b5f60e0828403121561419b575f80fd5b5f805f805f606086880312156153d3575f80fd5b853567ffffffffffffffff808211156153ea575f80fd5b6153f689838a016153af565b9650602088013591508082111561540b575f80fd5b61541789838a016152e3565b9096509450604088013591508082111561542f575f80fd5b5061543c888289016152e3565b969995985093965092949392505050565b5f6020828403121561545d575f80fd5b813567ffffffffffffffff811115615473575f80fd5b61547f848285016153af565b949350505050565b5f805f805f8060c0878903121561549c575f80fd5b6154a5876152b4565b95506154b3602088016152b4565b94506154c1604088016152b4565b9350606087013592506080870135915060a087013590509295509295509295565b5f805f80604085870312156154f5575f80fd5b843567ffffffffffffffff8082111561550c575f80fd5b615518888389016152e3565b90965094506020870135915080821115615530575f80fd5b5061553d878288016152e3565b95989497509550505050565b8015158114612f60575f80fd5b5f60208284031215615566575f80fd5b813561529681615549565b803567ffffffffffffffff81168114614248575f80fd5b5f8060408385031215615599575f80fd5b6155a283615571565b946020939093013593505050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601160045260245ffd5b808201808211156118c2576118c26155b0565b818103818111156118c2576118c26155b0565b5f60208284031215615613575f80fd5b5051919050565b5f6020828403121561562a575f80fd5b813561ffff81168114615296575f80fd5b5f8083357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe184360301811261566e575f80fd5b83018035915067ffffffffffffffff821115615688575f80fd5b602001915036819003821315615321575f80fd5b5f602082840312156156ac575f80fd5b813560ff81168114615296575f80fd5b5f602082840312156156cc575f80fd5b815161529681615549565b5f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8203615707576157076155b0565b5060010190565b5f6020828403121561571e575f80fd5b61529682615571565b85815284602082015260806040820152826080820152828460a08301375f60a084830101525f60a07fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f86011683010190508260608301529695505050505050565b5f82515f5b818110156157a9576020818601810151858301520161578f565b505f920191825250919050565b80820281158282048414176118c2576118c26155b0565b5f82615800577f4e487b71000000000000000000000000000000000000000000000000000000005f52601260045260245ffd5b500490565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52603260045260245ffdfea164736f6c6343000818000a diff --git a/bindings/bin/submitter_deployed.hex b/bindings/bin/submitter_deployed.hex new file mode 100644 index 000000000..b62e9ebe8 --- /dev/null +++ b/bindings/bin/submitter_deployed.hex @@ -0,0 +1 @@ +0x60806040526004361061018e575f3560e01c80639f8a13d7116100dc578063bfa02ba911610087578063cde4cd1111610062578063cde4cd111461044a578063d13f90b414610469578063ec5ffac214610488578063f2fde38b1461049d575f80fd5b8063bfa02ba9146103ed578063c36f41e21461040c578063c96be4cb1461042b575f80fd5b8063ab8c53dc116100b7578063ab8c53dc1461037c578063b2dd5c0714610391578063bbfefd38146103bf575f80fd5b80639f8a13d71461030f578063a3066aab1461033e578063a4f209b01461035d575f80fd5b8063426233601161013c57806360e274641161011757806360e27464146102ab578063715018a6146102ca5780638da5cb5b146102de575f80fd5b8063426233601461024c57806351cff8d91461027757806352d472eb14610296575f80fd5b8063359289911161016c57806335928991146101fa5780633a4b66f114610219578063402914f514610221575f80fd5b8063072900f9146101925780630d13fd7b146101b3578063233e9903146101db575b5f80fd5b34801561019d575f80fd5b506101b16101ac366004611cca565b6104bc565b005b3480156101be575f80fd5b506101c860995481565b6040519081526020015b60405180910390f35b3480156101e6575f80fd5b506101b16101f5366004611cea565b6105b4565b348015610205575f80fd5b506101b1610214366004611cea565b61065a565b6101b1610700565b34801561022c575f80fd5b506101c861023b366004611cca565b609f6020525f908152604090205481565b348015610257575f80fd5b506101c8610266366004611cca565b609d6020525f908152604090205481565b348015610282575f80fd5b506101b1610291366004611cca565b61086e565b3480156102a1575f80fd5b506101c8609a5481565b3480156102b6575f80fd5b506101b16102c5366004611cca565b610a06565b3480156102d5575f80fd5b506101b1610b45565b3480156102e9575f80fd5b506033546001600160a01b03165b6040516001600160a01b0390911681526020016101d2565b34801561031a575f80fd5b5061032e610329366004611cca565b610b56565b60405190151581526020016101d2565b348015610349575f80fd5b506101b1610358366004611cca565b610bc0565b348015610368575f80fd5b506101b1610377366004611cea565b610ce5565b348015610387575f80fd5b506101c8609b5481565b34801561039c575f80fd5b5061032e6103ab366004611cca565b609c6020525f908152604090205460ff1681565b3480156103ca575f80fd5b5061032e6103d9366004611cca565b609e6020525f908152604090205460ff1681565b3480156103f8575f80fd5b506097546102f7906001600160a01b031681565b348015610417575f80fd5b506101b1610426366004611cca565b610d98565b348015610436575f80fd5b506101c8610445366004611cca565b610ef8565b348015610455575f80fd5b506101b1610464366004611cca565b611064565b348015610474575f80fd5b506101b1610483366004611d01565b611176565b348015610493575f80fd5b506101c860985481565b3480156104a8575f80fd5b506101b16104b7366004611cca565b61158a565b6104c4611617565b6001600160a01b038116158015906104f457506001600160a01b0381165f908152609c602052604090205460ff16155b801561051857506001600160a01b0381165f908152609e602052604090205460ff16155b6105695760405162461bcd60e51b815260206004820152601160248201527f696e76616c6964207375626d697474657200000000000000000000000000000060448201526064015b60405180910390fd5b6001600160a01b0381165f818152609c6020526040808220805460ff19166001179055517fb079bc2cbde1f186e0b351d4a87c4597e3ed098f571548617449e73506428d8b9190a250565b6105bc611617565b5f811180156105cd57506098548114155b6106195760405162461bcd60e51b815260206004820152601560248201527f696e76616c6964206d696e696d756d207374616b6500000000000000000000006044820152606401610560565b60985460408051918252602082018390527fd67ed534faf3e0dcda08ac6043ec96dcf9b6ebec56055fd14bd0cb40a27c0e49910160405180910390a1609855565b610662611617565b5f8111801561067357506099548114155b6106bf5760405162461bcd60e51b815260206004820152601960248201527f696e76616c6964206368616c6c656e6765206465706f736974000000000000006044820152606401610560565b60995460408051918252602082018390527f36f971a40478225aeb80cfbf5e80306e8cb76d3bf7d56fdc5e490945cddb7d55910160405180910390a1609955565b610708611671565b335f818152609c602052604090205460ff16801561073e57506001600160a01b0381165f908152609e602052604090205460ff16155b61078a5760405162461bcd60e51b815260206004820152600d60248201527f6e6f74207374616b6561626c65000000000000000000000000000000000000006044820152606401610560565b6001600160a01b0381165f908152609d60205260408120546107ad903490611d77565b90505f341180156107c057506098548110155b61080c5760405162461bcd60e51b815260206004820152601360248201527f62656c6f77206d696e696d756d207374616b65000000000000000000000000006044820152606401610560565b6001600160a01b0382165f818152609d602090815260409182902084905581513481529081018490527f1449c6dd7851abc30abf37f57715f492010519147cc2652fbc38202c18a6ee90910160405180910390a2505061086c6001606555565b565b610876611671565b336001600160a01b0382166108cd5760405162461bcd60e51b815260206004820152601060248201527f696e76616c6964207265636569766572000000000000000000000000000000006044820152606401610560565b6001600160a01b0381165f908152609c602052604090205460ff16801561090c57506001600160a01b0381165f908152609e602052604090205460ff16155b801561092e57506001600160a01b0381165f908152609d602052604090205415155b61097a5760405162461bcd60e51b815260206004820152601060248201527f6e6f7420776974686472617761626c65000000000000000000000000000000006044820152606401610560565b6001600160a01b038181165f818152609c60209081526040808320805460ff19908116909155609e835281842080549091166001179055609d825291829020549151918252928516927f04c56a409d50971e45c5a2d96e5d557d2b0f1d66d40f14b141e4c958b0f39b32910160405180910390a36109f881836116d1565b50610a036001606555565b50565b610a0e611671565b336001600160a01b038216610a655760405162461bcd60e51b815260206004820152601060248201527f696e76616c6964207265636569766572000000000000000000000000000000006044820152606401610560565b6001600160a01b0381165f908152609f602052604090205480610aca5760405162461bcd60e51b815260206004820152600960248201527f6e6f2063726564697400000000000000000000000000000000000000000000006044820152606401610560565b6001600160a01b0382165f908152609f6020526040812055610aec83826116ed565b826001600160a01b0316826001600160a01b03167fc4b5041307ee38be8c4f25df5d980a372ac8a9a73ea8f459891000a5386f532283604051610b3191815260200190565b60405180910390a35050610a036001606555565b610b4d611617565b61086c5f61179c565b6001600160a01b0381165f908152609c602052604081205460ff168015610b9557506001600160a01b0382165f908152609e602052604090205460ff16155b8015610bba57506098546001600160a01b0383165f908152609d602052604090205410155b92915050565b610bc8611671565b336001600160a01b038216610c1f5760405162461bcd60e51b815260206004820152601060248201527f696e76616c6964207265636569766572000000000000000000000000000000006044820152606401610560565b6001600160a01b0381165f908152609e602052604090205460ff16610c865760405162461bcd60e51b815260206004820152600b60248201527f6e6f742065786974696e670000000000000000000000000000000000000000006044820152606401610560565b610c8f81611805565b610cdb5760405162461bcd60e51b815260206004820152601760248201527f70656e64696e67206261746368206c696162696c6974790000000000000000006044820152606401610560565b6109f881836118f0565b610ced611617565b5f81118015610cfd575060648111155b8015610d0b5750609a548114155b610d575760405162461bcd60e51b815260206004820152601960248201527f696e76616c6964207265776172642070657263656e74616765000000000000006044820152606401610560565b609a5460408051918252602082018390527fa46de936426e045703b2d34a292a19fde92b329018db8e0da750033876b655ba910160405180910390a1609a55565b610da0611617565b610da8611671565b6001600160a01b0381165f908152609c602052604090205460ff168015610de757506001600160a01b0381165f908152609e602052604090205460ff16155b610e335760405162461bcd60e51b815260206004820152601160248201527f696e76616c6964207375626d69747465720000000000000000000000000000006044820152606401610560565b6001600160a01b0381165f818152609c60209081526040808320805460ff19908116909155609e9092528083208054909216600117909155517ff84a004e1673d2f349a7c93c72b3794b8eba6d2f9338044d8c8cd260e51a57a19190a26001600160a01b0381165f818152609d60205260409081902054905182917f04c56a409d50971e45c5a2d96e5d557d2b0f1d66d40f14b141e4c958b0f39b3291610edc91815260200190565b60405180910390a3610eee81826116d1565b610a036001606555565b6097545f906001600160a01b0316336001600160a01b031614610f5d5760405162461bcd60e51b815260206004820152601460248201527f6f6e6c7920726f6c6c757020636f6e74726163740000000000000000000000006044820152606401610560565b610f65611671565b6001600160a01b038216610f7a57505f611055565b6001600160a01b0382165f908152609d602090815260408083208054609c8452828520805460ff1990811690915591859055609e90935292208054909216600117909155609a54606490610fce9083611d8a565b610fd89190611da1565b9150610fe48282611dd9565b609b5f828254610ff49190611d77565b909155505060975461100f906001600160a01b0316836116ed565b60408051828152602081018490526001600160a01b038516917f45a371af55b0726877a30f464edc14db5879ab096590bacce682cf6c18223596910160405180910390a2505b61105f6001606555565b919050565b61106c611617565b611074611671565b6001600160a01b0381166110ca5760405162461bcd60e51b815260206004820152601060248201527f696e76616c6964207265636569766572000000000000000000000000000000006044820152606401610560565b609b548061111a5760405162461bcd60e51b815260206004820152601260248201527f6e6f20736c6173682072656d61696e696e6700000000000000000000000000006044820152606401610560565b5f609b5561112882826116ed565b816001600160a01b03167fa1fefb6c5328a92a416e321ed50997303fe7135fd88c28b0592b21ce42b5cdd98260405161116391815260200190565b60405180910390a250610a036001606555565b5f54610100900460ff161580801561119457505f54600160ff909116105b806111ad5750303b1580156111ad57505f5460ff166001145b61121f5760405162461bcd60e51b815260206004820152602e60248201527f496e697469616c697a61626c653a20636f6e747261637420697320616c72656160448201527f647920696e697469616c697a65640000000000000000000000000000000000006064820152608401610560565b5f805460ff19166001179055801561125d575f80547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ff166101001790555b6001600160a01b0386166112b35760405162461bcd60e51b815260206004820152600d60248201527f696e76616c6964206f776e6572000000000000000000000000000000000000006044820152606401610560565b6001600160a01b038516158015906112d457505f856001600160a01b03163b115b6113205760405162461bcd60e51b815260206004820152601760248201527f696e76616c696420726f6c6c757020636f6e74726163740000000000000000006044820152606401610560565b5f841161136f5760405162461bcd60e51b815260206004820152601560248201527f696e76616c6964206d696e696d756d207374616b6500000000000000000000006044820152606401610560565b5f83116113be5760405162461bcd60e51b815260206004820152601960248201527f696e76616c6964206368616c6c656e6765206465706f736974000000000000006044820152606401610560565b5f821180156113ce575060648211155b61141a5760405162461bcd60e51b815260206004820152601960248201527f696e76616c6964207265776172642070657263656e74616765000000000000006044820152606401610560565b611422611aab565b61142a611b2f565b6114338661179c565b609780547fffffffffffffffffffffffff0000000000000000000000000000000000000000166001600160a01b03871617905560988490556099839055609a829055604080515f8152602081018690527fd67ed534faf3e0dcda08ac6043ec96dcf9b6ebec56055fd14bd0cb40a27c0e49910160405180910390a1604080515f8152602081018590527f36f971a40478225aeb80cfbf5e80306e8cb76d3bf7d56fdc5e490945cddb7d55910160405180910390a1604080515f8152602081018490527fa46de936426e045703b2d34a292a19fde92b329018db8e0da750033876b655ba910160405180910390a18015611582575f80547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ff169055604051600181527f7f26b83ff96e1f2b6a682f133852f6798a09c465da95921460cefb38474024989060200160405180910390a15b505050505050565b611592611617565b6001600160a01b03811661160e5760405162461bcd60e51b815260206004820152602660248201527f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160448201527f64647265737300000000000000000000000000000000000000000000000000006064820152608401610560565b610a038161179c565b6033546001600160a01b0316331461086c5760405162461bcd60e51b815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e65726044820152606401610560565b6002606554036116c35760405162461bcd60e51b815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c006044820152606401610560565b6002606555565b6001606555565b6116da82611805565b156116e9576116e982826118f0565b5050565b805f036116f8575050565b5f826001600160a01b0316826040515f6040518083038185875af1925050503d805f8114611741576040519150601f19603f3d011682016040523d82523d5f602084013e611746565b606091505b50509050806117975760405162461bcd60e51b815260206004820152601360248201527f455448207472616e73666572206661696c6564000000000000000000000000006044820152606401610560565b505050565b603380546001600160a01b038381167fffffffffffffffffffffffff0000000000000000000000000000000000000000831681179093556040519116919082907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0905f90a35050565b6040516001600160a01b03821660248201525f908190604401604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529190526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167f8ef622a80000000000000000000000000000000000000000000000000000000017815260975482519293506001600160a01b0316915f9182918291829182918761c350fa92503d9150602082148316156118ce5760205f803e505f515b8280156118db5750816020145b80156118e5575080155b979650505050505050565b6001600160a01b0382165f908152609d60209081526040808320805490849055609e9092528220805460ff191690559081900361197957816001600160a01b0316836001600160a01b03167febb524abf6b48e81bfe3e2a2ba3d4050bc91b988d1cfaf677a91dc6652865f465f60405161196c91815260200190565b60405180910390a3505050565b5f826001600160a01b03168261c350906040515f60405180830381858888f193505050503d805f81146119c7576040519150601f19603f3d011682016040523d82523d5f602084013e6119cc565b606091505b505090508015611a2857826001600160a01b0316846001600160a01b03167febb524abf6b48e81bfe3e2a2ba3d4050bc91b988d1cfaf677a91dc6652865f4684604051611a1b91815260200190565b60405180910390a3611aa5565b6001600160a01b0384165f908152609f602052604081208054849290611a4f908490611d77565b90915550506001600160a01b0384165f818152609f6020908152604091829020548251868152918201527f335ec00177f14d116df2d9836dfb84e01159f61d1d9305a2130432fb47806ae4910160405180910390a25b50505050565b5f54610100900460ff16611b275760405162461bcd60e51b815260206004820152602b60248201527f496e697469616c697a61626c653a20636f6e7472616374206973206e6f74206960448201527f6e697469616c697a696e670000000000000000000000000000000000000000006064820152608401610560565b61086c611bb3565b5f54610100900460ff16611bab5760405162461bcd60e51b815260206004820152602b60248201527f496e697469616c697a61626c653a20636f6e7472616374206973206e6f74206960448201527f6e697469616c697a696e670000000000000000000000000000000000000000006064820152608401610560565b61086c611c38565b5f54610100900460ff16611c2f5760405162461bcd60e51b815260206004820152602b60248201527f496e697469616c697a61626c653a20636f6e7472616374206973206e6f74206960448201527f6e697469616c697a696e670000000000000000000000000000000000000000006064820152608401610560565b61086c3361179c565b5f54610100900460ff166116ca5760405162461bcd60e51b815260206004820152602b60248201527f496e697469616c697a61626c653a20636f6e7472616374206973206e6f74206960448201527f6e697469616c697a696e670000000000000000000000000000000000000000006064820152608401610560565b80356001600160a01b038116811461105f575f80fd5b5f60208284031215611cda575f80fd5b611ce382611cb4565b9392505050565b5f60208284031215611cfa575f80fd5b5035919050565b5f805f805f60a08688031215611d15575f80fd5b611d1e86611cb4565b9450611d2c60208701611cb4565b94979496505050506040830135926060810135926080909101359150565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601160045260245ffd5b80820180821115610bba57610bba611d4a565b8082028115828204841417610bba57610bba611d4a565b5f82611dd4577f4e487b71000000000000000000000000000000000000000000000000000000005f52601260045260245ffd5b500490565b81810381811115610bba57610bba611d4a56fea164736f6c6343000818000a diff --git a/bindings/bindings/l1staking.go b/bindings/bindings/l1staking.go deleted file mode 100644 index 6ef653abe..000000000 --- a/bindings/bindings/l1staking.go +++ /dev/null @@ -1,3445 +0,0 @@ -// Code generated - DO NOT EDIT. -// This file is a generated binding and any manual changes will be lost. - -package bindings - -import ( - "errors" - "math/big" - "strings" - - ethereum "github.com/morph-l2/go-ethereum" - "github.com/morph-l2/go-ethereum/accounts/abi" - "github.com/morph-l2/go-ethereum/accounts/abi/bind" - "github.com/morph-l2/go-ethereum/common" - "github.com/morph-l2/go-ethereum/core/types" - "github.com/morph-l2/go-ethereum/event" -) - -// Reference imports to suppress errors if they are not otherwise used. -var ( - _ = errors.New - _ = big.NewInt - _ = strings.NewReader - _ = ethereum.NotFound - _ = bind.Bind - _ = common.Big1 - _ = types.BloomLookup - _ = event.NewSubscription - _ = abi.ConvertType -) - -// L1StakingMetaData contains all meta data concerning the L1Staking contract. -var L1StakingMetaData = &bind.MetaData{ - ABI: "[{\"inputs\":[{\"internalType\":\"addresspayable\",\"name\":\"_messenger\",\"type\":\"address\"}],\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"oldChallengeDeposit\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"newChallengeDeposit\",\"type\":\"uint256\"}],\"name\":\"ChallengeDepositUpdated\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"staker\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"receiver\",\"type\":\"address\"}],\"name\":\"Claimed\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"oldGasLimit\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"newGasLimit\",\"type\":\"uint256\"}],\"name\":\"GasLimitAddStakerUpdated\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"oldGasLimit\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"newGasLimit\",\"type\":\"uint256\"}],\"name\":\"GasLimitRemoveStakersUpdated\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint8\",\"name\":\"version\",\"type\":\"uint8\"}],\"name\":\"Initialized\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"previousOwner\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"newOwner\",\"type\":\"address\"}],\"name\":\"OwnershipTransferred\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address\",\"name\":\"addr\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"bytes32\",\"name\":\"tmKey\",\"type\":\"bytes32\"},{\"indexed\":false,\"internalType\":\"bytes\",\"name\":\"blsKey\",\"type\":\"bytes\"}],\"name\":\"Registered\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"oldPercentage\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"newPercentage\",\"type\":\"uint256\"}],\"name\":\"RewardPercentageUpdated\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address\",\"name\":\"receiver\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"SlashRemainingClaimed\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address[]\",\"name\":\"stakers\",\"type\":\"address[]\"}],\"name\":\"Slashed\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address[]\",\"name\":\"stakers\",\"type\":\"address[]\"}],\"name\":\"StakersRemoved\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"oldStakingValue\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"newStakingValue\",\"type\":\"uint256\"}],\"name\":\"StakingValueUpdated\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address[]\",\"name\":\"add\",\"type\":\"address[]\"},{\"indexed\":false,\"internalType\":\"address[]\",\"name\":\"remove\",\"type\":\"address[]\"}],\"name\":\"WhitelistUpdated\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"addr\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"unlockHeight\",\"type\":\"uint256\"}],\"name\":\"Withdrawn\",\"type\":\"event\"},{\"inputs\":[],\"name\":\"MESSENGER\",\"outputs\":[{\"internalType\":\"contractICrossDomainMessenger\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"OTHER_STAKING\",\"outputs\":[{\"internalType\":\"contractStaking\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes\",\"name\":\"blsPubkey\",\"type\":\"bytes\"}],\"name\":\"blsKeys\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"exist\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"challengeDeposit\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"receiver\",\"type\":\"address\"}],\"name\":\"claimSlashRemaining\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"receiver\",\"type\":\"address\"}],\"name\":\"claimWithdrawal\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"cleanStakerStore\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"name\":\"deleteList\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"stakerAddr\",\"type\":\"address\"}],\"name\":\"deleteableHeight\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"height\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"gasLimitAddStaker\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"gasLimitRemoveStakers\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getActiveStakers\",\"outputs\":[{\"internalType\":\"address[]\",\"name\":\"\",\"type\":\"address[]\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"_staker\",\"type\":\"address\"}],\"name\":\"getStakerBitmap\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"bitmap\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getStakers\",\"outputs\":[{\"internalType\":\"address[255]\",\"name\":\"\",\"type\":\"address[255]\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address[]\",\"name\":\"_stakers\",\"type\":\"address[]\"}],\"name\":\"getStakersBitmap\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"bitmap\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"bitmap\",\"type\":\"uint256\"}],\"name\":\"getStakersFromBitmap\",\"outputs\":[{\"internalType\":\"address[]\",\"name\":\"stakerAddrs\",\"type\":\"address[]\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"_rollupContract\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"_stakingValue\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"_challengeDeposit\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"_lockBlocks\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"_rewardPercentage\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"_gasLimitAdd\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"_gasLimitRemove\",\"type\":\"uint256\"}],\"name\":\"initialize\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"addr\",\"type\":\"address\"}],\"name\":\"isActiveStaker\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"addr\",\"type\":\"address\"}],\"name\":\"isStaker\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"addr\",\"type\":\"address\"}],\"name\":\"isStakerInDeleteList\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"messenger\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"owner\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"tmKey\",\"type\":\"bytes32\"},{\"internalType\":\"bytes\",\"name\":\"blsKey\",\"type\":\"bytes\"}],\"name\":\"register\",\"outputs\":[],\"stateMutability\":\"payable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address[]\",\"name\":\"_stakers\",\"type\":\"address[]\"}],\"name\":\"removeStaker\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"stakerAddr\",\"type\":\"address\"}],\"name\":\"removedList\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"removed\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"renounceOwnership\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"rewardPercentage\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"rollupContract\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"sequencersBitmap\",\"type\":\"uint256\"}],\"name\":\"slash\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"slashRemaining\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"stakerAddr\",\"type\":\"address\"}],\"name\":\"stakerIndexes\",\"outputs\":[{\"internalType\":\"uint8\",\"name\":\"index\",\"type\":\"uint8\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"name\":\"stakerSet\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"stakerAddr\",\"type\":\"address\"}],\"name\":\"stakers\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"addr\",\"type\":\"address\"},{\"internalType\":\"bytes32\",\"name\":\"tmKey\",\"type\":\"bytes32\"},{\"internalType\":\"bytes\",\"name\":\"blsKey\",\"type\":\"bytes\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"stakingValue\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"tmPubkey\",\"type\":\"bytes32\"}],\"name\":\"tmKeys\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"exist\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"newOwner\",\"type\":\"address\"}],\"name\":\"transferOwnership\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"_challengeDeposit\",\"type\":\"uint256\"}],\"name\":\"updateChallengeDeposit\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"_gasLimitAdd\",\"type\":\"uint256\"}],\"name\":\"updateGasLimitAddStaker\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"_gasLimitRemove\",\"type\":\"uint256\"}],\"name\":\"updateGasLimitRemoveStakers\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"_rewardPercentage\",\"type\":\"uint256\"}],\"name\":\"updateRewardPercentage\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"_stakingValue\",\"type\":\"uint256\"}],\"name\":\"updateStakingValue\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address[]\",\"name\":\"add\",\"type\":\"address[]\"},{\"internalType\":\"address[]\",\"name\":\"remove\",\"type\":\"address[]\"}],\"name\":\"updateWhitelist\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"},{\"internalType\":\"address[]\",\"name\":\"\",\"type\":\"address[]\"},{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"},{\"internalType\":\"bytes\",\"name\":\"\",\"type\":\"bytes\"}],\"name\":\"verifySignature\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"pure\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"stakerAddr\",\"type\":\"address\"}],\"name\":\"whitelist\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"inWhitelist\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"withdraw\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"withdrawalLockBlocks\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"staker\",\"type\":\"address\"}],\"name\":\"withdrawals\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"}]", - Bin: "0x60c060405234801562000010575f80fd5b5060405162003e1d38038062003e1d833981016040819052620000339162000127565b6001600160a01b03811660805273530000000000000000000000000000000000001560a0526200006262000069565b5062000156565b5f54610100900460ff1615620000d55760405162461bcd60e51b815260206004820152602760248201527f496e697469616c697a61626c653a20636f6e747261637420697320696e697469604482015266616c697a696e6760c81b606482015260840160405180910390fd5b5f5460ff9081161462000125575f805460ff191660ff9081179091556040519081527f7f26b83ff96e1f2b6a682f133852f6798a09c465da95921460cefb38474024989060200160405180910390a15b565b5f6020828403121562000138575f80fd5b81516001600160a01b03811681146200014f575f80fd5b9392505050565b60805160a051613c826200019b5f395f8181610664015281816129530152612bc701525f818161043c01528181610701015281816129240152612b980152613c825ff3fe6080604052600436106102f8575f3560e01c80637a9262a211610191578063ab8c53dc116100dc578063d51c90a911610087578063e2a6ad5f11610062578063e2a6ad5f1461090c578063f2fde38b1461092b578063f4fe7fef1461094a575f80fd5b8063d51c90a91461087f578063dd4785f514610894578063df155033146108d5575f80fd5b8063c7cd469a116100b7578063c7cd469a14610822578063cde4cd1114610841578063d096c3c614610860575f80fd5b8063ab8c53dc146107cf578063ae81de53146107e4578063bfa02ba914610803575f80fd5b8063927ede2d1161013c5780639fb82db9116101175780639fb82db91461077d578063a3066aab14610791578063a4f209b0146107b0575f80fd5b8063927ede2d146106f05780639b19251a146107235780639d48f41714610751575f80fd5b80638a565ac31161016c5780638a565ac3146106865780638da5cb5b146106a55780639168ae72146106c2575f80fd5b80637a9262a2146106085780638142951a14610634578063831cfb5814610653575f80fd5b806341de239b11610251578063692c565b116101fc57806374fe27b7116101d757806374fe27b714610588578063797adbde146105ae5780637a4e87c3146105cd575f80fd5b8063692c565b146105265780636f1e853314610555578063715018a614610574575f80fd5b80634d64903a1161022c5780634d64903a146104dd57806352d472eb146104f25780636801579114610507575f80fd5b806341de239b1461048857806343352d611461049d57806345bc4d10146104be575f80fd5b8063320ff860116102b15780633cb747bf1161028c5780633cb747bf1461042e5780633ccfd60b146104605780633ee2a1f914610474575f80fd5b8063320ff860146103b257806335928991146103d15780633a9bbede146103f0575f80fd5b80632a28e5a3116102e15780632a28e5a31461033a5780632e407a6f1461034f578063303afb9e1461037b575f80fd5b80630d13fd7b146102fc5780632108db3514610325575b5f80fd5b348015610307575f80fd5b506103126101a65481565b6040519081526020015b60405180910390f35b348015610330575f80fd5b50610312609c5481565b61034d610348366004613407565b610969565b005b34801561035a575f80fd5b50610312610369366004613461565b6101a16020525f908152604090205481565b348015610386575f80fd5b5061039a610395366004613481565b610d64565b6040516001600160a01b03909116815260200161031c565b3480156103bd575f80fd5b5061034d6103cc366004613498565b610d83565b3480156103dc575f80fd5b5061034d6103eb366004613481565b61109e565b3480156103fb575f80fd5b5061041e61040a366004613461565b609f6020525f908152604090205460ff1681565b604051901515815260200161031c565b348015610439575f80fd5b507f000000000000000000000000000000000000000000000000000000000000000061039a565b34801561046b575f80fd5b5061034d61114b565b34801561047f575f80fd5b5061034d611317565b348015610493575f80fd5b5061031260995481565b3480156104a8575f80fd5b506104b1611329565b60405161031c9190613540565b3480156104c9575f80fd5b506103126104d8366004613481565b61136f565b3480156104e8575f80fd5b5061031260985481565b3480156104fd575f80fd5b50610312609a5481565b348015610512575f80fd5b5061041e610521366004613461565b611675565b348015610531575f80fd5b5061041e610540366004613481565b6101a46020525f908152604090205460ff1681565b348015610560575f80fd5b5061041e61056f366004613461565b611710565b34801561057f575f80fd5b5061034d611789565b348015610593575f80fd5b5061041e6105a23660046135c2565b60019695505050505050565b3480156105b9575f80fd5b5061039a6105c8366004613481565b61179a565b3480156105d8575f80fd5b5061041e6105e7366004613668565b80516020818301810180516101a38252928201919093012091525460ff1681565b348015610613575f80fd5b50610312610622366004613461565b6101a56020525f908152604090205481565b34801561063f575f80fd5b5061034d61064e3660046136a2565b6117c3565b34801561065e575f80fd5b5061039a7f000000000000000000000000000000000000000000000000000000000000000081565b348015610691575f80fd5b5061034d6106a0366004613481565b611c85565b3480156106b0575f80fd5b506033546001600160a01b031661039a565b3480156106cd575f80fd5b506106e16106dc366004613461565b611d28565b60405161031c9392919061373d565b3480156106fb575f80fd5b5061039a7f000000000000000000000000000000000000000000000000000000000000000081565b34801561072e575f80fd5b5061041e61073d366004613461565b609e6020525f908152604090205460ff1681565b34801561075c575f80fd5b5061077061076b366004613481565b611ddb565b60405161031c919061376d565b348015610788575f80fd5b50610770611ef8565b34801561079c575f80fd5b5061034d6107ab366004613461565b6120a6565b3480156107bb575f80fd5b5061034d6107ca366004613481565b6121d6565b3480156107da575f80fd5b50610312609d5481565b3480156107ef575f80fd5b5061034d6107fe366004613481565b612286565b34801561080e575f80fd5b5060975461039a906001600160a01b031681565b34801561082d575f80fd5b5061034d61083c3660046137b9565b612329565b34801561084c575f80fd5b5061034d61085b366004613461565b6124ca565b34801561086b575f80fd5b5061031261087a366004613461565b612538565b34801561088a575f80fd5b50610312609b5481565b34801561089f575f80fd5b506108c36108ae366004613461565b61019f6020525f908152604090205460ff1681565b60405160ff909116815260200161031c565b3480156108e0575f80fd5b5061041e6108ef366004613461565b6001600160a01b03165f9081526101a16020526040902054151590565b348015610917575f80fd5b50610312610926366004613820565b6125b2565b348015610936575f80fd5b5061034d610945366004613461565b6126e4565b348015610955575f80fd5b5061034d610964366004613481565b612771565b335f818152609e602052604090205460ff166109cc5760405162461bcd60e51b815260206004820152601060248201527f6e6f7420696e2077686974656c6973740000000000000000000000000000000060448201526064015b60405180910390fd5b335f9081526101a260205260409020546001600160a01b031615610a325760405162461bcd60e51b815260206004820152601260248201527f616c72656164792072656769737465726564000000000000000000000000000060448201526064016109c3565b8215801590610a5057505f8381526101a4602052604090205460ff16155b610a9c5760405162461bcd60e51b815260206004820152601960248201527f696e76616c69642074656e6465726d696e74207075626b65790000000000000060448201526064016109c3565b8151610100148015610acf57506101a382604051610aba919061385f565b9081526040519081900360200190205460ff16155b610b1b5760405162461bcd60e51b815260206004820152601260248201527f696e76616c696420626c73207075626b6579000000000000000000000000000060448201526064016109c3565b6098543414610b6c5760405162461bcd60e51b815260206004820152601560248201527f696e76616c6964207374616b696e672076616c7565000000000000000000000060448201526064016109c3565b6040518060600160405280610b7e3390565b6001600160a01b03908116825260208083018790526040928301869052335f9081526101a28252839020845181547fffffffffffffffffffffffff00000000000000000000000000000000000000001693169290921782558301516001820155908201516002820190610bf1908261390f565b50905050610c04610bff3390565b612814565b60016101a383604051610c17919061385f565b90815260408051918290036020908101909220805493151560ff199485161790555f8681526101a49092529020805490911660011790557fb9c7babb56df9f2da4a30811a6c778e4e68af88b72712d56cf62c5516e20e199610c763390565b8484604051610c879392919061373d565b60405180910390a1335f9081526101a26020908152604091829020825160608101845281546001600160a01b03168152600182015492810192909252600281018054610d5f9484019190610cda9061387a565b80601f0160208091040260200160405190810160405280929190818152602001828054610d069061387a565b8015610d515780601f10610d2857610100808354040283529160200191610d51565b820191905f5260205f20905b815481529060010190602001808311610d3457829003601f168201915b505050505081525050612922565b505050565b60a08160ff8110610d73575f80fd5b01546001600160a01b0316905081565b610d8b612a4a565b5f5b815181101561105a57610db8828281518110610dab57610dab613a0d565b6020026020010151611675565b610e2a5760405162461bcd60e51b815260206004820152602160248201527f6f6e6c7920616374697665207374616b65722063616e2062652072656d6f766560448201527f640000000000000000000000000000000000000000000000000000000000000060648201526084016109c3565b6101a55f838381518110610e4057610e40613a0d565b60200260200101516001600160a01b03166001600160a01b031681526020019081526020015f20545f14610eb65760405162461bcd60e51b815260206004820152600b60248201527f7769746864726177696e6700000000000000000000000000000000000000000060448201526064016109c3565b609954610ec39043613a67565b6101a55f848481518110610ed957610ed9613a0d565b60200260200101516001600160a01b03166001600160a01b031681526020019081526020015f2081905550610f26828281518110610f1957610f19613a0d565b6020026020010151612aa4565b818181518110610f3857610f38613a0d565b60200260200101516001600160a01b03167f7084f5476618d8e60b11ef0d7d3f06914655adb8793e28ff7f018d4c76d505d56101a55f858581518110610f8057610f80613a0d565b60200260200101516001600160a01b03166001600160a01b031681526020019081526020015f2054604051610fb791815260200190565b60405180910390a2609e5f838381518110610fd457610fd4613a0d565b60200260200101516001600160a01b03166001600160a01b031681526020019081526020015f205f6101000a81549060ff02191690556001609f5f84848151811061102157611021613a0d565b6020908102919091018101516001600160a01b031682528101919091526040015f20805460ff1916911515919091179055600101610d8d565b507f3f446646c03e618be8238a586960d6d625f35c653cdca1ef59609993e8ed2c848160405161108a919061376d565b60405180910390a161109b81612b96565b50565b6110a6612a4a565b5f811180156110b857506101a6548114155b6111045760405162461bcd60e51b815260206004820152601f60248201527f696e76616c6964206368616c6c656e6765206465706f7369742076616c75650060448201526064016109c3565b6101a680549082905560408051828152602081018490527f36f971a40478225aeb80cfbf5e80306e8cb76d3bf7d56fdc5e490945cddb7d5591015b60405180910390a15050565b61115433611675565b6111a05760405162461bcd60e51b815260206004820152601260248201527f6f6e6c7920616374697665207374616b6572000000000000000000000000000060448201526064016109c3565b335f9081526101a56020526040902054156111fd5760405162461bcd60e51b815260206004820152600b60248201527f7769746864726177696e6700000000000000000000000000000000000000000060448201526064016109c3565b60995461120a9043613a67565b335f8181526101a5602052604090209190915561122690612aa4565b335f8181526101a560209081526040918290205491519182527f7084f5476618d8e60b11ef0d7d3f06914655adb8793e28ff7f018d4c76d505d5910160405180910390a2335f908152609e60209081526040808320805460ff19908116909155609f83528184208054909116600190811790915581518181528083019092529091828101908036833701905050905033815f815181106112c8576112c8613a0d565b60200260200101906001600160a01b031690816001600160a01b0316815250507f3f446646c03e618be8238a586960d6d625f35c653cdca1ef59609993e8ed2c848160405161108a919061376d565b61131f612a4a565b611327612c8e565b565b6113316132d4565b60408051611fe08101918290529060a09060ff9082845b81546001600160a01b03168152600190910190602001808311611348575050505050905090565b6097545f906001600160a01b0316336001600160a01b0316146113d45760405162461bcd60e51b815260206004820152601460248201527f6f6e6c7920726f6c6c757020636f6e747261637400000000000000000000000060448201526064016109c3565b6113dc612f59565b5f6113e683611ddb565b90505f805b8251811015611598575f6101a55f85848151811061140b5761140b613a0d565b60200260200101516001600160a01b03166001600160a01b031681526020019081526020015f2054111561148e576101a55f84838151811061144f5761144f613a0d565b60200260200101516001600160a01b03166001600160a01b031681526020019081526020015f205f9055609854826114879190613a67565b9150611590565b6114c88382815181106114a3576114a3613a0d565b60200260200101516001600160a01b03165f9081526101a16020526040902054151590565b611590576098546114d99083613a67565b91506114f0838281518110610f1957610f19613a0d565b609e5f84838151811061150557611505613a0d565b60200260200101516001600160a01b03166001600160a01b031681526020019081526020015f205f6101000a81549060ff02191690556001609f5f85848151811061155257611552613a0d565b60200260200101516001600160a01b03166001600160a01b031681526020019081526020015f205f6101000a81548160ff0219169083151502179055505b6001016113eb565b505f6064609a54836115aa9190613a7a565b6115b49190613a91565b90506115c08183613ac9565b609d5f8282546115d09190613a67565b90915550506097546115eb906001600160a01b031682612fb2565b7f654f4a61849f1b3ad10abb283d27f02d5fece7b820acc5a3b874713b58748b5a8360405161161a919061376d565b60405180910390a17f3f446646c03e618be8238a586960d6d625f35c653cdca1ef59609993e8ed2c8483604051611651919061376d565b60405180910390a161166283612b96565b925050506116706001606555565b919050565b6001600160a01b0381165f90815261019f602052604081205460ff16810361169e57505f919050565b6001600160a01b0382165f81815261019f602052604090205460a0906116c99060019060ff16613adc565b60ff1660ff81106116dc576116dc613a0d565b01546001600160a01b031614801561170a57506001600160a01b0382165f9081526101a16020526040902054155b92915050565b6001600160a01b0381165f90815261019f602052604081205460ff16810361173957505f919050565b6001600160a01b0382165f81815261019f602052604090205460a0906117649060019060ff16613adc565b60ff1660ff811061177757611777613a0d565b01546001600160a01b03161492915050565b611791612a4a565b6113275f613062565b6101a081815481106117aa575f80fd5b5f918252602090912001546001600160a01b0316905081565b5f54610100900460ff16158080156117e157505f54600160ff909116105b806117fa5750303b1580156117fa57505f5460ff166001145b61186c5760405162461bcd60e51b815260206004820152602e60248201527f496e697469616c697a61626c653a20636f6e747261637420697320616c72656160448201527f647920696e697469616c697a656400000000000000000000000000000000000060648201526084016109c3565b5f805460ff1916600117905580156118aa575f80547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ff166101001790555b6001600160a01b0388166119005760405162461bcd60e51b815260206004820152601760248201527f696e76616c696420726f6c6c757020636f6e747261637400000000000000000060448201526064016109c3565b5f871161194f5760405162461bcd60e51b815260206004820152601560248201527f696e76616c6964207374616b696e672076616c7565000000000000000000000060448201526064016109c3565b5f861161199e5760405162461bcd60e51b815260206004820152601f60248201527f696e76616c6964206368616c6c656e6765206465706f7369742076616c75650060448201526064016109c3565b5f85116119ed5760405162461bcd60e51b815260206004820152601e60248201527f696e76616c6964207769746864726177616c206c6f636b20626c6f636b73000060448201526064016109c3565b5f8311611a3c5760405162461bcd60e51b815260206004820152601c60248201527f696e76616c696420676173206c696d697420616464207374616b65720000000060448201526064016109c3565b5f8211611a8b5760405162461bcd60e51b815260206004820181905260248201527f696e76616c696420676173206c696d69742072656d6f7665207374616b65727360448201526064016109c3565b5f84118015611a9b575060648411155b611b0c5760405162461bcd60e51b8152602060048201526024808201527f696e76616c6964206368616c6c656e676572207265776172642070657263656e60448201527f746167650000000000000000000000000000000000000000000000000000000060648201526084016109c3565b611b146130cb565b611b1c61314f565b609780547fffffffffffffffffffffffff0000000000000000000000000000000000000000166001600160a01b038a16179055609a84905560988790556101a68690556099859055609b839055609c829055604080515f8152602081018590527f0ef80fb82bf5439b8591744c0fde771f5d93dce7a7970b1e9b7828cdc3970e9c910160405180910390a1604080515f8152602081018490527fdd4b37d1f14888147fe4be2cfaedcbf148fd07ececc856d0433241b8b6e4a7f7910160405180910390a1604080515f8152602081018690527fa46de936426e045703b2d34a292a19fde92b329018db8e0da750033876b655ba910160405180910390a18015611c7b575f80547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ff169055604051600181527f7f26b83ff96e1f2b6a682f133852f6798a09c465da95921460cefb38474024989060200160405180910390a15b5050505050505050565b611c8d612a4a565b5f81118015611c9e5750609c548114155b611cea5760405162461bcd60e51b815260206004820152601560248201527f696e76616c6964206e657720676173206c696d6974000000000000000000000060448201526064016109c3565b609c80549082905560408051828152602081018490527fdd4b37d1f14888147fe4be2cfaedcbf148fd07ececc856d0433241b8b6e4a7f7910161113f565b6101a26020525f90815260409020805460018201546002830180546001600160a01b03909316939192611d5a9061387a565b80601f0160208091040260200160405190810160405280929190818152602001828054611d869061387a565b8015611dd15780601f10611da857610100808354040283529160200191611dd1565b820191905f5260205f20905b815481529060010190602001808311611db457829003601f168201915b5050505050905083565b6060600182901c5f5b8115611e0b57611df5816001613a67565b9050611e02600183613ac9565b82169150611de4565b8067ffffffffffffffff811115611e2457611e2461333d565b604051908082528060200260200182016040528015611e4d578160200160208202803683370190505b5092505f60015b60ff8160ff1611611eef57600160ff82161b861615611edd5760a0611e7a600183613adc565b60ff1660ff8110611e8d57611e8d613a0d565b015485516001600160a01b0390911690869084908110611eaf57611eaf613a0d565b6001600160a01b0390921660209283029190910190910152611ed2826001613a67565b915082821015611eef575b80611ee781613af5565b915050611e54565b50505050919050565b6040805160ff80825261200082019092526060915f91829160208201611fe0803683370190505090505f5b60ff811015611fc7575f60a08260ff8110611f4057611f40613a0d565b01546001600160a01b031614801590611f8857506101a15f60a08360ff8110611f6b57611f6b613a0d565b01546001600160a01b0316815260208101919091526040015f2054155b15611fbf5782611f9781613b13565b9350506001828281518110611fae57611fae613a0d565b911515602092830291909101909101525b600101611f23565b505f8267ffffffffffffffff811115611fe257611fe261333d565b60405190808252806020026020018201604052801561200b578160200160208202803683370190505b5090505f805b60ff81101561209c5783818151811061202c5761202c613a0d565b6020026020010151156120945760a08160ff811061204c5761204c613a0d565b015483516001600160a01b039091169084908490811061206e5761206e613a0d565b6001600160a01b03909216602092830291909101909101528161209081613b13565b9250505b600101612011565b5090949350505050565b6120ae612f59565b335f9081526101a5602052604090205461210a5760405162461bcd60e51b815260206004820152601460248201527f7769746864726177616c206e6f7420657869737400000000000000000000000060448201526064016109c3565b335f9081526101a560205260409020544310156121695760405162461bcd60e51b815260206004820152601160248201527f7769746864726177616c206c6f636b656400000000000000000000000000000060448201526064016109c3565b335f9081526101a56020526040812055612181612c8e565b604080516001600160a01b0383168152905133917f89309c9b2aeaffbdce717113df9427298b20448c05919bf889e05f8c3094254b919081900360200190a26121cc81609854612fb2565b61109b6001606555565b6121de612a4a565b5f811180156121ee575060648111155b80156121fc5750609a548114155b6122485760405162461bcd60e51b815260206004820152601960248201527f696e76616c6964207265776172642070657263656e746167650000000000000060448201526064016109c3565b609a80549082905560408051828152602081018490527fa46de936426e045703b2d34a292a19fde92b329018db8e0da750033876b655ba910161113f565b61228e612a4a565b5f8111801561229f5750609b548114155b6122eb5760405162461bcd60e51b815260206004820152601560248201527f696e76616c6964206e657720676173206c696d6974000000000000000000000060448201526064016109c3565b609b80549082905560408051828152602081018490527f0ef80fb82bf5439b8591744c0fde771f5d93dce7a7970b1e9b7828cdc3970e9c910161113f565b612331612a4a565b5f5b8381101561242957609f5f86868481811061235057612350613a0d565b90506020020160208101906123659190613461565b6001600160a01b0316815260208101919091526040015f205460ff16156123ce5760405162461bcd60e51b815260206004820152600f60248201527f696e2072656d6f766564206c697374000000000000000000000000000000000060448201526064016109c3565b6001609e5f8787858181106123e5576123e5613a0d565b90506020020160208101906123fa9190613461565b6001600160a01b0316815260208101919091526040015f20805460ff1916911515919091179055600101612333565b505f5b8181101561248657609e5f84848481811061244957612449613a0d565b905060200201602081019061245e9190613461565b6001600160a01b0316815260208101919091526040015f20805460ff1916905560010161242c565b507fe375867e538b40218c1b3db2ccceaf875eb073e38b510449e6088c1539ac8622848484846040516124bc9493929190613b8f565b60405180910390a150505050565b6124d2612a4a565b6124da612f59565b609d546124e78282612fb2565b5f609d55604080516001600160a01b0384168152602081018390527fa1fefb6c5328a92a416e321ed50997303fe7135fd88c28b0592b21ce42b5cdd9910160405180910390a15061109b6001606555565b5f61254282611710565b61258e5760405162461bcd60e51b815260206004820152600e60248201527f696e76616c6964207374616b657200000000000000000000000000000000000060448201526064016109c3565b506001600160a01b03165f90815261019f6020526040902054600160ff9091161b90565b5f60ff8211156126045760405162461bcd60e51b815260206004820152601c60248201527f7374616b657273206c656e677468206f7574206f6620626f756e64730000000060448201526064016109c3565b5f5b828110156126dd5761263884848381811061262357612623613a0d565b905060200201602081019061056f9190613461565b6126845760405162461bcd60e51b815260206004820152600e60248201527f696e76616c6964207374616b657200000000000000000000000000000000000060448201526064016109c3565b61019f5f85858481811061269a5761269a613a0d565b90506020020160208101906126af9190613461565b6001600160a01b0316815260208101919091526040015f2054600160ff90911681901b929092179101612606565b5092915050565b6126ec612a4a565b6001600160a01b0381166127685760405162461bcd60e51b815260206004820152602660248201527f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160448201527f646472657373000000000000000000000000000000000000000000000000000060648201526084016109c3565b61109b81613062565b612779612a4a565b5f8111801561278a57506098548114155b6127d65760405162461bcd60e51b815260206004820152601560248201527f696e76616c6964207374616b696e672076616c7565000000000000000000000060448201526064016109c3565b609880549082905560408051828152602081018490527f91e3ed304d49776a78bbe0df6bf7156f5433763621e5e728a3c2adfe7de74368910161113f565b5f5b60ff8160ff1610156128d9575f60a060ff80841690811061283957612839613a0d565b01546001600160a01b0316036128d1578160a08260ff1660ff811061286057612860613a0d565b0180547fffffffffffffffffffffffff0000000000000000000000000000000000000000166001600160a01b03929092169190911790556128a2816001613bc0565b6001600160a01b03929092165f90815261019f60205260409020805460ff191660ff9093169290921790915550565b600101612816565b5060405162461bcd60e51b815260206004820152600960248201527f736c6f742066756c6c000000000000000000000000000000000000000000000060448201526064016109c3565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663b2267a7b7f00000000000000000000000000000000000000000000000000000000000000005f846040516024016129849190613bd9565b60408051601f198184030181529181526020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167f6d454d5100000000000000000000000000000000000000000000000000000000179052609b5490517fffffffff0000000000000000000000000000000000000000000000000000000060e087901b168152612a1a9493929190600401613c10565b5f604051808303815f87803b158015612a31575f80fd5b505af1158015612a43573d5f803e3d5ffd5b5050505050565b6033546001600160a01b031633146113275760405162461bcd60e51b815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e657260448201526064016109c3565b6001600160a01b0381165f9081526101a1602052604090205415612b0a5760405162461bcd60e51b815260206004820152601560248201527f616c726561647920696e2064656c6574654c697374000000000000000000000060448201526064016109c3565b6101a080546001810182555f919091527f7980fe0f714a613298681d64b7b8ffa7b148338dd52429f307d72798d5c317c40180547fffffffffffffffffffffffff0000000000000000000000000000000000000000166001600160a01b038316179055609954612b7a9043613a67565b6001600160a01b039091165f9081526101a16020526040902055565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663b2267a7b7f00000000000000000000000000000000000000000000000000000000000000005f84604051602401612bf8919061376d565b60408051601f198184030181529181526020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167f0be67fcc00000000000000000000000000000000000000000000000000000000179052609c5490517fffffffff0000000000000000000000000000000000000000000000000000000060e087901b168152612a1a9493929190600401613c10565b5f5b6101a05481101561109b57436101a15f6101a08481548110612cb457612cb4613a0d565b5f9182526020808320909101546001600160a01b0316835282019290925260400190205411612f475760a0600161019f5f6101a08581548110612cf957612cf9613a0d565b5f9182526020808320909101546001600160a01b03168352820192909252604001902054612d2a919060ff16613adc565b60ff1660ff8110612d3d57612d3d613a0d565b0180547fffffffffffffffffffffffff00000000000000000000000000000000000000001690556101a0805461019f915f9184908110612d7f57612d7f613a0d565b5f9182526020808320909101546001600160a01b031683528201929092526040018120805460ff191690556101a080546101a292919084908110612dc557612dc5613a0d565b5f9182526020808320909101546001600160a01b03168352820192909252604001812080547fffffffffffffffffffffffff00000000000000000000000000000000000000001681556001810182905590612e2360028301826132f3565b50506101a15f6101a08381548110612e3d57612e3d613a0d565b5f9182526020808320909101546001600160a01b031683528201929092526040018120556101a08054612e7290600190613ac9565b81548110612e8257612e82613a0d565b5f918252602090912001546101a080546001600160a01b039092169183908110612eae57612eae613a0d565b905f5260205f20015f6101000a8154816001600160a01b0302191690836001600160a01b031602179055506101a0805480612eeb57612eeb613c48565b5f8281526020902081017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff90810180547fffffffffffffffffffffffff0000000000000000000000000000000000000000169055019055612c90565b80612f5181613b13565b915050612c90565b600260655403612fab5760405162461bcd60e51b815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c0060448201526064016109c3565b6002606555565b8015613057575f826001600160a01b0316826040515f6040518083038185875af1925050503d805f8114613001576040519150601f19603f3d011682016040523d82523d5f602084013e613006565b606091505b5050905080610d5f5760405162461bcd60e51b815260206004820152601b60248201527f526f6c6c75703a20455448207472616e73666572206661696c6564000000000060448201526064016109c3565b5050565b6001606555565b603380546001600160a01b038381167fffffffffffffffffffffffff0000000000000000000000000000000000000000831681179093556040519116919082907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0905f90a35050565b5f54610100900460ff166131475760405162461bcd60e51b815260206004820152602b60248201527f496e697469616c697a61626c653a20636f6e7472616374206973206e6f74206960448201527f6e697469616c697a696e6700000000000000000000000000000000000000000060648201526084016109c3565b6113276131d3565b5f54610100900460ff166131cb5760405162461bcd60e51b815260206004820152602b60248201527f496e697469616c697a61626c653a20636f6e7472616374206973206e6f74206960448201527f6e697469616c697a696e6700000000000000000000000000000000000000000060648201526084016109c3565b611327613258565b5f54610100900460ff1661324f5760405162461bcd60e51b815260206004820152602b60248201527f496e697469616c697a61626c653a20636f6e7472616374206973206e6f74206960448201527f6e697469616c697a696e6700000000000000000000000000000000000000000060648201526084016109c3565b61132733613062565b5f54610100900460ff1661305b5760405162461bcd60e51b815260206004820152602b60248201527f496e697469616c697a61626c653a20636f6e7472616374206973206e6f74206960448201527f6e697469616c697a696e6700000000000000000000000000000000000000000060648201526084016109c3565b60405180611fe0016040528060ff906020820280368337509192915050565b5080546132ff9061387a565b5f825580601f1061330e575050565b601f0160209004905f5260205f209081019061109b91905b80821115613339575f8155600101613326565b5090565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52604160045260245ffd5b604051601f8201601f1916810167ffffffffffffffff811182821017156133935761339361333d565b604052919050565b5f82601f8301126133aa575f80fd5b813567ffffffffffffffff8111156133c4576133c461333d565b6133d76020601f19601f8401160161336a565b8181528460208386010111156133eb575f80fd5b816020850160208301375f918101602001919091529392505050565b5f8060408385031215613418575f80fd5b82359150602083013567ffffffffffffffff811115613435575f80fd5b6134418582860161339b565b9150509250929050565b80356001600160a01b0381168114611670575f80fd5b5f60208284031215613471575f80fd5b61347a8261344b565b9392505050565b5f60208284031215613491575f80fd5b5035919050565b5f60208083850312156134a9575f80fd5b823567ffffffffffffffff808211156134c0575f80fd5b818501915085601f8301126134d3575f80fd5b8135818111156134e5576134e561333d565b8060051b91506134f684830161336a565b818152918301840191848101908884111561350f575f80fd5b938501935b83851015613534576135258561344b565b82529385019390850190613514565b98975050505050505050565b611fe0810181835f5b60ff8110156135715781516001600160a01b0316835260209283019290910190600101613549565b50505092915050565b5f8083601f84011261358a575f80fd5b50813567ffffffffffffffff8111156135a1575f80fd5b6020830191508360208260051b85010111156135bb575f80fd5b9250929050565b5f805f805f80608087890312156135d7575f80fd5b86359550602087013567ffffffffffffffff808211156135f5575f80fd5b6136018a838b0161357a565b9097509550604089013594506060890135915080821115613620575f80fd5b818901915089601f830112613633575f80fd5b813581811115613641575f80fd5b8a6020828501011115613652575f80fd5b6020830194508093505050509295509295509295565b5f60208284031215613678575f80fd5b813567ffffffffffffffff81111561368e575f80fd5b61369a8482850161339b565b949350505050565b5f805f805f805f60e0888a0312156136b8575f80fd5b6136c18861344b565b9960208901359950604089013598606081013598506080810135975060a0810135965060c00135945092505050565b5f5b8381101561370a5781810151838201526020016136f2565b50505f910152565b5f81518084526137298160208601602086016136f0565b601f01601f19169290920160200192915050565b6001600160a01b0384168152826020820152606060408201525f6137646060830184613712565b95945050505050565b602080825282518282018190525f9190848201906040850190845b818110156137ad5783516001600160a01b031683529284019291840191600101613788565b50909695505050505050565b5f805f80604085870312156137cc575f80fd5b843567ffffffffffffffff808211156137e3575f80fd5b6137ef8883890161357a565b90965094506020870135915080821115613807575f80fd5b506138148782880161357a565b95989497509550505050565b5f8060208385031215613831575f80fd5b823567ffffffffffffffff811115613847575f80fd5b6138538582860161357a565b90969095509350505050565b5f82516138708184602087016136f0565b9190910192915050565b600181811c9082168061388e57607f821691505b6020821081036138c5577f4e487b71000000000000000000000000000000000000000000000000000000005f52602260045260245ffd5b50919050565b601f821115610d5f57805f5260205f20601f840160051c810160208510156138f05750805b601f840160051c820191505b81811015612a43575f81556001016138fc565b815167ffffffffffffffff8111156139295761392961333d565b61393d81613937845461387a565b846138cb565b602080601f83116001811461398f575f84156139595750858301515b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff600386901b1c1916600185901b178555613a05565b5f85815260208120601f198616915b828110156139bd5788860151825594840194600190910190840161399e565b50858210156139f957878501517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff600388901b60f8161c191681555b505060018460011b0185555b505050505050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52603260045260245ffd5b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601160045260245ffd5b8082018082111561170a5761170a613a3a565b808202811582820484141761170a5761170a613a3a565b5f82613ac4577f4e487b71000000000000000000000000000000000000000000000000000000005f52601260045260245ffd5b500490565b8181038181111561170a5761170a613a3a565b60ff828116828216039081111561170a5761170a613a3a565b5f60ff821660ff8103613b0a57613b0a613a3a565b60010192915050565b5f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8203613b4357613b43613a3a565b5060010190565b8183525f60208085019450825f5b85811015613b84576001600160a01b03613b718361344b565b1687529582019590820190600101613b58565b509495945050505050565b604081525f613ba2604083018688613b4a565b8281036020840152613bb5818587613b4a565b979650505050505050565b60ff818116838216019081111561170a5761170a613a3a565b602081526001600160a01b038251166020820152602082015160408201525f604083015160608084015261369a6080840182613712565b6001600160a01b0385168152836020820152608060408201525f613c376080830185613712565b905082606083015295945050505050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52603160045260245ffdfea164736f6c6343000818000a", -} - -// L1StakingABI is the input ABI used to generate the binding from. -// Deprecated: Use L1StakingMetaData.ABI instead. -var L1StakingABI = L1StakingMetaData.ABI - -// L1StakingBin is the compiled bytecode used for deploying new contracts. -// Deprecated: Use L1StakingMetaData.Bin instead. -var L1StakingBin = L1StakingMetaData.Bin - -// DeployL1Staking deploys a new Ethereum contract, binding an instance of L1Staking to it. -func DeployL1Staking(auth *bind.TransactOpts, backend bind.ContractBackend, _messenger common.Address) (common.Address, *types.Transaction, *L1Staking, error) { - parsed, err := L1StakingMetaData.GetAbi() - if err != nil { - return common.Address{}, nil, nil, err - } - if parsed == nil { - return common.Address{}, nil, nil, errors.New("GetABI returned nil") - } - - address, tx, contract, err := bind.DeployContract(auth, *parsed, common.FromHex(L1StakingBin), backend, _messenger) - if err != nil { - return common.Address{}, nil, nil, err - } - return address, tx, &L1Staking{L1StakingCaller: L1StakingCaller{contract: contract}, L1StakingTransactor: L1StakingTransactor{contract: contract}, L1StakingFilterer: L1StakingFilterer{contract: contract}}, nil -} - -// L1Staking is an auto generated Go binding around an Ethereum contract. -type L1Staking struct { - L1StakingCaller // Read-only binding to the contract - L1StakingTransactor // Write-only binding to the contract - L1StakingFilterer // Log filterer for contract events -} - -// L1StakingCaller is an auto generated read-only Go binding around an Ethereum contract. -type L1StakingCaller struct { - contract *bind.BoundContract // Generic contract wrapper for the low level calls -} - -// L1StakingTransactor is an auto generated write-only Go binding around an Ethereum contract. -type L1StakingTransactor struct { - contract *bind.BoundContract // Generic contract wrapper for the low level calls -} - -// L1StakingFilterer is an auto generated log filtering Go binding around an Ethereum contract events. -type L1StakingFilterer struct { - contract *bind.BoundContract // Generic contract wrapper for the low level calls -} - -// L1StakingSession is an auto generated Go binding around an Ethereum contract, -// with pre-set call and transact options. -type L1StakingSession struct { - Contract *L1Staking // Generic contract binding to set the session for - CallOpts bind.CallOpts // Call options to use throughout this session - TransactOpts bind.TransactOpts // Transaction auth options to use throughout this session -} - -// L1StakingCallerSession is an auto generated read-only Go binding around an Ethereum contract, -// with pre-set call options. -type L1StakingCallerSession struct { - Contract *L1StakingCaller // Generic contract caller binding to set the session for - CallOpts bind.CallOpts // Call options to use throughout this session -} - -// L1StakingTransactorSession is an auto generated write-only Go binding around an Ethereum contract, -// with pre-set transact options. -type L1StakingTransactorSession struct { - Contract *L1StakingTransactor // Generic contract transactor binding to set the session for - TransactOpts bind.TransactOpts // Transaction auth options to use throughout this session -} - -// L1StakingRaw is an auto generated low-level Go binding around an Ethereum contract. -type L1StakingRaw struct { - Contract *L1Staking // Generic contract binding to access the raw methods on -} - -// L1StakingCallerRaw is an auto generated low-level read-only Go binding around an Ethereum contract. -type L1StakingCallerRaw struct { - Contract *L1StakingCaller // Generic read-only contract binding to access the raw methods on -} - -// L1StakingTransactorRaw is an auto generated low-level write-only Go binding around an Ethereum contract. -type L1StakingTransactorRaw struct { - Contract *L1StakingTransactor // Generic write-only contract binding to access the raw methods on -} - -// NewL1Staking creates a new instance of L1Staking, bound to a specific deployed contract. -func NewL1Staking(address common.Address, backend bind.ContractBackend) (*L1Staking, error) { - contract, err := bindL1Staking(address, backend, backend, backend) - if err != nil { - return nil, err - } - return &L1Staking{L1StakingCaller: L1StakingCaller{contract: contract}, L1StakingTransactor: L1StakingTransactor{contract: contract}, L1StakingFilterer: L1StakingFilterer{contract: contract}}, nil -} - -// NewL1StakingCaller creates a new read-only instance of L1Staking, bound to a specific deployed contract. -func NewL1StakingCaller(address common.Address, caller bind.ContractCaller) (*L1StakingCaller, error) { - contract, err := bindL1Staking(address, caller, nil, nil) - if err != nil { - return nil, err - } - return &L1StakingCaller{contract: contract}, nil -} - -// NewL1StakingTransactor creates a new write-only instance of L1Staking, bound to a specific deployed contract. -func NewL1StakingTransactor(address common.Address, transactor bind.ContractTransactor) (*L1StakingTransactor, error) { - contract, err := bindL1Staking(address, nil, transactor, nil) - if err != nil { - return nil, err - } - return &L1StakingTransactor{contract: contract}, nil -} - -// NewL1StakingFilterer creates a new log filterer instance of L1Staking, bound to a specific deployed contract. -func NewL1StakingFilterer(address common.Address, filterer bind.ContractFilterer) (*L1StakingFilterer, error) { - contract, err := bindL1Staking(address, nil, nil, filterer) - if err != nil { - return nil, err - } - return &L1StakingFilterer{contract: contract}, nil -} - -// bindL1Staking binds a generic wrapper to an already deployed contract. -func bindL1Staking(address common.Address, caller bind.ContractCaller, transactor bind.ContractTransactor, filterer bind.ContractFilterer) (*bind.BoundContract, error) { - parsed, err := L1StakingMetaData.GetAbi() - if err != nil { - return nil, err - } - return bind.NewBoundContract(address, *parsed, caller, transactor, filterer), nil -} - -// Call invokes the (constant) contract method with params as input values and -// sets the output to result. The result type might be a single field for simple -// returns, a slice of interfaces for anonymous returns and a struct for named -// returns. -func (_L1Staking *L1StakingRaw) Call(opts *bind.CallOpts, result *[]interface{}, method string, params ...interface{}) error { - return _L1Staking.Contract.L1StakingCaller.contract.Call(opts, result, method, params...) -} - -// Transfer initiates a plain transaction to move funds to the contract, calling -// its default method if one is available. -func (_L1Staking *L1StakingRaw) Transfer(opts *bind.TransactOpts) (*types.Transaction, error) { - return _L1Staking.Contract.L1StakingTransactor.contract.Transfer(opts) -} - -// Transact invokes the (paid) contract method with params as input values. -func (_L1Staking *L1StakingRaw) Transact(opts *bind.TransactOpts, method string, params ...interface{}) (*types.Transaction, error) { - return _L1Staking.Contract.L1StakingTransactor.contract.Transact(opts, method, params...) -} - -// Call invokes the (constant) contract method with params as input values and -// sets the output to result. The result type might be a single field for simple -// returns, a slice of interfaces for anonymous returns and a struct for named -// returns. -func (_L1Staking *L1StakingCallerRaw) Call(opts *bind.CallOpts, result *[]interface{}, method string, params ...interface{}) error { - return _L1Staking.Contract.contract.Call(opts, result, method, params...) -} - -// Transfer initiates a plain transaction to move funds to the contract, calling -// its default method if one is available. -func (_L1Staking *L1StakingTransactorRaw) Transfer(opts *bind.TransactOpts) (*types.Transaction, error) { - return _L1Staking.Contract.contract.Transfer(opts) -} - -// Transact invokes the (paid) contract method with params as input values. -func (_L1Staking *L1StakingTransactorRaw) Transact(opts *bind.TransactOpts, method string, params ...interface{}) (*types.Transaction, error) { - return _L1Staking.Contract.contract.Transact(opts, method, params...) -} - -// MESSENGER is a free data retrieval call binding the contract method 0x927ede2d. -// -// Solidity: function MESSENGER() view returns(address) -func (_L1Staking *L1StakingCaller) MESSENGER(opts *bind.CallOpts) (common.Address, error) { - var out []interface{} - err := _L1Staking.contract.Call(opts, &out, "MESSENGER") - - if err != nil { - return *new(common.Address), err - } - - out0 := *abi.ConvertType(out[0], new(common.Address)).(*common.Address) - - return out0, err - -} - -// MESSENGER is a free data retrieval call binding the contract method 0x927ede2d. -// -// Solidity: function MESSENGER() view returns(address) -func (_L1Staking *L1StakingSession) MESSENGER() (common.Address, error) { - return _L1Staking.Contract.MESSENGER(&_L1Staking.CallOpts) -} - -// MESSENGER is a free data retrieval call binding the contract method 0x927ede2d. -// -// Solidity: function MESSENGER() view returns(address) -func (_L1Staking *L1StakingCallerSession) MESSENGER() (common.Address, error) { - return _L1Staking.Contract.MESSENGER(&_L1Staking.CallOpts) -} - -// OTHERSTAKING is a free data retrieval call binding the contract method 0x831cfb58. -// -// Solidity: function OTHER_STAKING() view returns(address) -func (_L1Staking *L1StakingCaller) OTHERSTAKING(opts *bind.CallOpts) (common.Address, error) { - var out []interface{} - err := _L1Staking.contract.Call(opts, &out, "OTHER_STAKING") - - if err != nil { - return *new(common.Address), err - } - - out0 := *abi.ConvertType(out[0], new(common.Address)).(*common.Address) - - return out0, err - -} - -// OTHERSTAKING is a free data retrieval call binding the contract method 0x831cfb58. -// -// Solidity: function OTHER_STAKING() view returns(address) -func (_L1Staking *L1StakingSession) OTHERSTAKING() (common.Address, error) { - return _L1Staking.Contract.OTHERSTAKING(&_L1Staking.CallOpts) -} - -// OTHERSTAKING is a free data retrieval call binding the contract method 0x831cfb58. -// -// Solidity: function OTHER_STAKING() view returns(address) -func (_L1Staking *L1StakingCallerSession) OTHERSTAKING() (common.Address, error) { - return _L1Staking.Contract.OTHERSTAKING(&_L1Staking.CallOpts) -} - -// BlsKeys is a free data retrieval call binding the contract method 0x7a4e87c3. -// -// Solidity: function blsKeys(bytes blsPubkey) view returns(bool exist) -func (_L1Staking *L1StakingCaller) BlsKeys(opts *bind.CallOpts, blsPubkey []byte) (bool, error) { - var out []interface{} - err := _L1Staking.contract.Call(opts, &out, "blsKeys", blsPubkey) - - if err != nil { - return *new(bool), err - } - - out0 := *abi.ConvertType(out[0], new(bool)).(*bool) - - return out0, err - -} - -// BlsKeys is a free data retrieval call binding the contract method 0x7a4e87c3. -// -// Solidity: function blsKeys(bytes blsPubkey) view returns(bool exist) -func (_L1Staking *L1StakingSession) BlsKeys(blsPubkey []byte) (bool, error) { - return _L1Staking.Contract.BlsKeys(&_L1Staking.CallOpts, blsPubkey) -} - -// BlsKeys is a free data retrieval call binding the contract method 0x7a4e87c3. -// -// Solidity: function blsKeys(bytes blsPubkey) view returns(bool exist) -func (_L1Staking *L1StakingCallerSession) BlsKeys(blsPubkey []byte) (bool, error) { - return _L1Staking.Contract.BlsKeys(&_L1Staking.CallOpts, blsPubkey) -} - -// ChallengeDeposit is a free data retrieval call binding the contract method 0x0d13fd7b. -// -// Solidity: function challengeDeposit() view returns(uint256) -func (_L1Staking *L1StakingCaller) ChallengeDeposit(opts *bind.CallOpts) (*big.Int, error) { - var out []interface{} - err := _L1Staking.contract.Call(opts, &out, "challengeDeposit") - - if err != nil { - return *new(*big.Int), err - } - - out0 := *abi.ConvertType(out[0], new(*big.Int)).(**big.Int) - - return out0, err - -} - -// ChallengeDeposit is a free data retrieval call binding the contract method 0x0d13fd7b. -// -// Solidity: function challengeDeposit() view returns(uint256) -func (_L1Staking *L1StakingSession) ChallengeDeposit() (*big.Int, error) { - return _L1Staking.Contract.ChallengeDeposit(&_L1Staking.CallOpts) -} - -// ChallengeDeposit is a free data retrieval call binding the contract method 0x0d13fd7b. -// -// Solidity: function challengeDeposit() view returns(uint256) -func (_L1Staking *L1StakingCallerSession) ChallengeDeposit() (*big.Int, error) { - return _L1Staking.Contract.ChallengeDeposit(&_L1Staking.CallOpts) -} - -// DeleteList is a free data retrieval call binding the contract method 0x797adbde. -// -// Solidity: function deleteList(uint256 ) view returns(address) -func (_L1Staking *L1StakingCaller) DeleteList(opts *bind.CallOpts, arg0 *big.Int) (common.Address, error) { - var out []interface{} - err := _L1Staking.contract.Call(opts, &out, "deleteList", arg0) - - if err != nil { - return *new(common.Address), err - } - - out0 := *abi.ConvertType(out[0], new(common.Address)).(*common.Address) - - return out0, err - -} - -// DeleteList is a free data retrieval call binding the contract method 0x797adbde. -// -// Solidity: function deleteList(uint256 ) view returns(address) -func (_L1Staking *L1StakingSession) DeleteList(arg0 *big.Int) (common.Address, error) { - return _L1Staking.Contract.DeleteList(&_L1Staking.CallOpts, arg0) -} - -// DeleteList is a free data retrieval call binding the contract method 0x797adbde. -// -// Solidity: function deleteList(uint256 ) view returns(address) -func (_L1Staking *L1StakingCallerSession) DeleteList(arg0 *big.Int) (common.Address, error) { - return _L1Staking.Contract.DeleteList(&_L1Staking.CallOpts, arg0) -} - -// DeleteableHeight is a free data retrieval call binding the contract method 0x2e407a6f. -// -// Solidity: function deleteableHeight(address stakerAddr) view returns(uint256 height) -func (_L1Staking *L1StakingCaller) DeleteableHeight(opts *bind.CallOpts, stakerAddr common.Address) (*big.Int, error) { - var out []interface{} - err := _L1Staking.contract.Call(opts, &out, "deleteableHeight", stakerAddr) - - if err != nil { - return *new(*big.Int), err - } - - out0 := *abi.ConvertType(out[0], new(*big.Int)).(**big.Int) - - return out0, err - -} - -// DeleteableHeight is a free data retrieval call binding the contract method 0x2e407a6f. -// -// Solidity: function deleteableHeight(address stakerAddr) view returns(uint256 height) -func (_L1Staking *L1StakingSession) DeleteableHeight(stakerAddr common.Address) (*big.Int, error) { - return _L1Staking.Contract.DeleteableHeight(&_L1Staking.CallOpts, stakerAddr) -} - -// DeleteableHeight is a free data retrieval call binding the contract method 0x2e407a6f. -// -// Solidity: function deleteableHeight(address stakerAddr) view returns(uint256 height) -func (_L1Staking *L1StakingCallerSession) DeleteableHeight(stakerAddr common.Address) (*big.Int, error) { - return _L1Staking.Contract.DeleteableHeight(&_L1Staking.CallOpts, stakerAddr) -} - -// GasLimitAddStaker is a free data retrieval call binding the contract method 0xd51c90a9. -// -// Solidity: function gasLimitAddStaker() view returns(uint256) -func (_L1Staking *L1StakingCaller) GasLimitAddStaker(opts *bind.CallOpts) (*big.Int, error) { - var out []interface{} - err := _L1Staking.contract.Call(opts, &out, "gasLimitAddStaker") - - if err != nil { - return *new(*big.Int), err - } - - out0 := *abi.ConvertType(out[0], new(*big.Int)).(**big.Int) - - return out0, err - -} - -// GasLimitAddStaker is a free data retrieval call binding the contract method 0xd51c90a9. -// -// Solidity: function gasLimitAddStaker() view returns(uint256) -func (_L1Staking *L1StakingSession) GasLimitAddStaker() (*big.Int, error) { - return _L1Staking.Contract.GasLimitAddStaker(&_L1Staking.CallOpts) -} - -// GasLimitAddStaker is a free data retrieval call binding the contract method 0xd51c90a9. -// -// Solidity: function gasLimitAddStaker() view returns(uint256) -func (_L1Staking *L1StakingCallerSession) GasLimitAddStaker() (*big.Int, error) { - return _L1Staking.Contract.GasLimitAddStaker(&_L1Staking.CallOpts) -} - -// GasLimitRemoveStakers is a free data retrieval call binding the contract method 0x2108db35. -// -// Solidity: function gasLimitRemoveStakers() view returns(uint256) -func (_L1Staking *L1StakingCaller) GasLimitRemoveStakers(opts *bind.CallOpts) (*big.Int, error) { - var out []interface{} - err := _L1Staking.contract.Call(opts, &out, "gasLimitRemoveStakers") - - if err != nil { - return *new(*big.Int), err - } - - out0 := *abi.ConvertType(out[0], new(*big.Int)).(**big.Int) - - return out0, err - -} - -// GasLimitRemoveStakers is a free data retrieval call binding the contract method 0x2108db35. -// -// Solidity: function gasLimitRemoveStakers() view returns(uint256) -func (_L1Staking *L1StakingSession) GasLimitRemoveStakers() (*big.Int, error) { - return _L1Staking.Contract.GasLimitRemoveStakers(&_L1Staking.CallOpts) -} - -// GasLimitRemoveStakers is a free data retrieval call binding the contract method 0x2108db35. -// -// Solidity: function gasLimitRemoveStakers() view returns(uint256) -func (_L1Staking *L1StakingCallerSession) GasLimitRemoveStakers() (*big.Int, error) { - return _L1Staking.Contract.GasLimitRemoveStakers(&_L1Staking.CallOpts) -} - -// GetActiveStakers is a free data retrieval call binding the contract method 0x9fb82db9. -// -// Solidity: function getActiveStakers() view returns(address[]) -func (_L1Staking *L1StakingCaller) GetActiveStakers(opts *bind.CallOpts) ([]common.Address, error) { - var out []interface{} - err := _L1Staking.contract.Call(opts, &out, "getActiveStakers") - - if err != nil { - return *new([]common.Address), err - } - - out0 := *abi.ConvertType(out[0], new([]common.Address)).(*[]common.Address) - - return out0, err - -} - -// GetActiveStakers is a free data retrieval call binding the contract method 0x9fb82db9. -// -// Solidity: function getActiveStakers() view returns(address[]) -func (_L1Staking *L1StakingSession) GetActiveStakers() ([]common.Address, error) { - return _L1Staking.Contract.GetActiveStakers(&_L1Staking.CallOpts) -} - -// GetActiveStakers is a free data retrieval call binding the contract method 0x9fb82db9. -// -// Solidity: function getActiveStakers() view returns(address[]) -func (_L1Staking *L1StakingCallerSession) GetActiveStakers() ([]common.Address, error) { - return _L1Staking.Contract.GetActiveStakers(&_L1Staking.CallOpts) -} - -// GetStakerBitmap is a free data retrieval call binding the contract method 0xd096c3c6. -// -// Solidity: function getStakerBitmap(address _staker) view returns(uint256 bitmap) -func (_L1Staking *L1StakingCaller) GetStakerBitmap(opts *bind.CallOpts, _staker common.Address) (*big.Int, error) { - var out []interface{} - err := _L1Staking.contract.Call(opts, &out, "getStakerBitmap", _staker) - - if err != nil { - return *new(*big.Int), err - } - - out0 := *abi.ConvertType(out[0], new(*big.Int)).(**big.Int) - - return out0, err - -} - -// GetStakerBitmap is a free data retrieval call binding the contract method 0xd096c3c6. -// -// Solidity: function getStakerBitmap(address _staker) view returns(uint256 bitmap) -func (_L1Staking *L1StakingSession) GetStakerBitmap(_staker common.Address) (*big.Int, error) { - return _L1Staking.Contract.GetStakerBitmap(&_L1Staking.CallOpts, _staker) -} - -// GetStakerBitmap is a free data retrieval call binding the contract method 0xd096c3c6. -// -// Solidity: function getStakerBitmap(address _staker) view returns(uint256 bitmap) -func (_L1Staking *L1StakingCallerSession) GetStakerBitmap(_staker common.Address) (*big.Int, error) { - return _L1Staking.Contract.GetStakerBitmap(&_L1Staking.CallOpts, _staker) -} - -// GetStakers is a free data retrieval call binding the contract method 0x43352d61. -// -// Solidity: function getStakers() view returns(address[255]) -func (_L1Staking *L1StakingCaller) GetStakers(opts *bind.CallOpts) ([255]common.Address, error) { - var out []interface{} - err := _L1Staking.contract.Call(opts, &out, "getStakers") - - if err != nil { - return *new([255]common.Address), err - } - - out0 := *abi.ConvertType(out[0], new([255]common.Address)).(*[255]common.Address) - - return out0, err - -} - -// GetStakers is a free data retrieval call binding the contract method 0x43352d61. -// -// Solidity: function getStakers() view returns(address[255]) -func (_L1Staking *L1StakingSession) GetStakers() ([255]common.Address, error) { - return _L1Staking.Contract.GetStakers(&_L1Staking.CallOpts) -} - -// GetStakers is a free data retrieval call binding the contract method 0x43352d61. -// -// Solidity: function getStakers() view returns(address[255]) -func (_L1Staking *L1StakingCallerSession) GetStakers() ([255]common.Address, error) { - return _L1Staking.Contract.GetStakers(&_L1Staking.CallOpts) -} - -// GetStakersBitmap is a free data retrieval call binding the contract method 0xe2a6ad5f. -// -// Solidity: function getStakersBitmap(address[] _stakers) view returns(uint256 bitmap) -func (_L1Staking *L1StakingCaller) GetStakersBitmap(opts *bind.CallOpts, _stakers []common.Address) (*big.Int, error) { - var out []interface{} - err := _L1Staking.contract.Call(opts, &out, "getStakersBitmap", _stakers) - - if err != nil { - return *new(*big.Int), err - } - - out0 := *abi.ConvertType(out[0], new(*big.Int)).(**big.Int) - - return out0, err - -} - -// GetStakersBitmap is a free data retrieval call binding the contract method 0xe2a6ad5f. -// -// Solidity: function getStakersBitmap(address[] _stakers) view returns(uint256 bitmap) -func (_L1Staking *L1StakingSession) GetStakersBitmap(_stakers []common.Address) (*big.Int, error) { - return _L1Staking.Contract.GetStakersBitmap(&_L1Staking.CallOpts, _stakers) -} - -// GetStakersBitmap is a free data retrieval call binding the contract method 0xe2a6ad5f. -// -// Solidity: function getStakersBitmap(address[] _stakers) view returns(uint256 bitmap) -func (_L1Staking *L1StakingCallerSession) GetStakersBitmap(_stakers []common.Address) (*big.Int, error) { - return _L1Staking.Contract.GetStakersBitmap(&_L1Staking.CallOpts, _stakers) -} - -// GetStakersFromBitmap is a free data retrieval call binding the contract method 0x9d48f417. -// -// Solidity: function getStakersFromBitmap(uint256 bitmap) view returns(address[] stakerAddrs) -func (_L1Staking *L1StakingCaller) GetStakersFromBitmap(opts *bind.CallOpts, bitmap *big.Int) ([]common.Address, error) { - var out []interface{} - err := _L1Staking.contract.Call(opts, &out, "getStakersFromBitmap", bitmap) - - if err != nil { - return *new([]common.Address), err - } - - out0 := *abi.ConvertType(out[0], new([]common.Address)).(*[]common.Address) - - return out0, err - -} - -// GetStakersFromBitmap is a free data retrieval call binding the contract method 0x9d48f417. -// -// Solidity: function getStakersFromBitmap(uint256 bitmap) view returns(address[] stakerAddrs) -func (_L1Staking *L1StakingSession) GetStakersFromBitmap(bitmap *big.Int) ([]common.Address, error) { - return _L1Staking.Contract.GetStakersFromBitmap(&_L1Staking.CallOpts, bitmap) -} - -// GetStakersFromBitmap is a free data retrieval call binding the contract method 0x9d48f417. -// -// Solidity: function getStakersFromBitmap(uint256 bitmap) view returns(address[] stakerAddrs) -func (_L1Staking *L1StakingCallerSession) GetStakersFromBitmap(bitmap *big.Int) ([]common.Address, error) { - return _L1Staking.Contract.GetStakersFromBitmap(&_L1Staking.CallOpts, bitmap) -} - -// IsActiveStaker is a free data retrieval call binding the contract method 0x68015791. -// -// Solidity: function isActiveStaker(address addr) view returns(bool) -func (_L1Staking *L1StakingCaller) IsActiveStaker(opts *bind.CallOpts, addr common.Address) (bool, error) { - var out []interface{} - err := _L1Staking.contract.Call(opts, &out, "isActiveStaker", addr) - - if err != nil { - return *new(bool), err - } - - out0 := *abi.ConvertType(out[0], new(bool)).(*bool) - - return out0, err - -} - -// IsActiveStaker is a free data retrieval call binding the contract method 0x68015791. -// -// Solidity: function isActiveStaker(address addr) view returns(bool) -func (_L1Staking *L1StakingSession) IsActiveStaker(addr common.Address) (bool, error) { - return _L1Staking.Contract.IsActiveStaker(&_L1Staking.CallOpts, addr) -} - -// IsActiveStaker is a free data retrieval call binding the contract method 0x68015791. -// -// Solidity: function isActiveStaker(address addr) view returns(bool) -func (_L1Staking *L1StakingCallerSession) IsActiveStaker(addr common.Address) (bool, error) { - return _L1Staking.Contract.IsActiveStaker(&_L1Staking.CallOpts, addr) -} - -// IsStaker is a free data retrieval call binding the contract method 0x6f1e8533. -// -// Solidity: function isStaker(address addr) view returns(bool) -func (_L1Staking *L1StakingCaller) IsStaker(opts *bind.CallOpts, addr common.Address) (bool, error) { - var out []interface{} - err := _L1Staking.contract.Call(opts, &out, "isStaker", addr) - - if err != nil { - return *new(bool), err - } - - out0 := *abi.ConvertType(out[0], new(bool)).(*bool) - - return out0, err - -} - -// IsStaker is a free data retrieval call binding the contract method 0x6f1e8533. -// -// Solidity: function isStaker(address addr) view returns(bool) -func (_L1Staking *L1StakingSession) IsStaker(addr common.Address) (bool, error) { - return _L1Staking.Contract.IsStaker(&_L1Staking.CallOpts, addr) -} - -// IsStaker is a free data retrieval call binding the contract method 0x6f1e8533. -// -// Solidity: function isStaker(address addr) view returns(bool) -func (_L1Staking *L1StakingCallerSession) IsStaker(addr common.Address) (bool, error) { - return _L1Staking.Contract.IsStaker(&_L1Staking.CallOpts, addr) -} - -// IsStakerInDeleteList is a free data retrieval call binding the contract method 0xdf155033. -// -// Solidity: function isStakerInDeleteList(address addr) view returns(bool) -func (_L1Staking *L1StakingCaller) IsStakerInDeleteList(opts *bind.CallOpts, addr common.Address) (bool, error) { - var out []interface{} - err := _L1Staking.contract.Call(opts, &out, "isStakerInDeleteList", addr) - - if err != nil { - return *new(bool), err - } - - out0 := *abi.ConvertType(out[0], new(bool)).(*bool) - - return out0, err - -} - -// IsStakerInDeleteList is a free data retrieval call binding the contract method 0xdf155033. -// -// Solidity: function isStakerInDeleteList(address addr) view returns(bool) -func (_L1Staking *L1StakingSession) IsStakerInDeleteList(addr common.Address) (bool, error) { - return _L1Staking.Contract.IsStakerInDeleteList(&_L1Staking.CallOpts, addr) -} - -// IsStakerInDeleteList is a free data retrieval call binding the contract method 0xdf155033. -// -// Solidity: function isStakerInDeleteList(address addr) view returns(bool) -func (_L1Staking *L1StakingCallerSession) IsStakerInDeleteList(addr common.Address) (bool, error) { - return _L1Staking.Contract.IsStakerInDeleteList(&_L1Staking.CallOpts, addr) -} - -// Messenger is a free data retrieval call binding the contract method 0x3cb747bf. -// -// Solidity: function messenger() view returns(address) -func (_L1Staking *L1StakingCaller) Messenger(opts *bind.CallOpts) (common.Address, error) { - var out []interface{} - err := _L1Staking.contract.Call(opts, &out, "messenger") - - if err != nil { - return *new(common.Address), err - } - - out0 := *abi.ConvertType(out[0], new(common.Address)).(*common.Address) - - return out0, err - -} - -// Messenger is a free data retrieval call binding the contract method 0x3cb747bf. -// -// Solidity: function messenger() view returns(address) -func (_L1Staking *L1StakingSession) Messenger() (common.Address, error) { - return _L1Staking.Contract.Messenger(&_L1Staking.CallOpts) -} - -// Messenger is a free data retrieval call binding the contract method 0x3cb747bf. -// -// Solidity: function messenger() view returns(address) -func (_L1Staking *L1StakingCallerSession) Messenger() (common.Address, error) { - return _L1Staking.Contract.Messenger(&_L1Staking.CallOpts) -} - -// Owner is a free data retrieval call binding the contract method 0x8da5cb5b. -// -// Solidity: function owner() view returns(address) -func (_L1Staking *L1StakingCaller) Owner(opts *bind.CallOpts) (common.Address, error) { - var out []interface{} - err := _L1Staking.contract.Call(opts, &out, "owner") - - if err != nil { - return *new(common.Address), err - } - - out0 := *abi.ConvertType(out[0], new(common.Address)).(*common.Address) - - return out0, err - -} - -// Owner is a free data retrieval call binding the contract method 0x8da5cb5b. -// -// Solidity: function owner() view returns(address) -func (_L1Staking *L1StakingSession) Owner() (common.Address, error) { - return _L1Staking.Contract.Owner(&_L1Staking.CallOpts) -} - -// Owner is a free data retrieval call binding the contract method 0x8da5cb5b. -// -// Solidity: function owner() view returns(address) -func (_L1Staking *L1StakingCallerSession) Owner() (common.Address, error) { - return _L1Staking.Contract.Owner(&_L1Staking.CallOpts) -} - -// RemovedList is a free data retrieval call binding the contract method 0x3a9bbede. -// -// Solidity: function removedList(address stakerAddr) view returns(bool removed) -func (_L1Staking *L1StakingCaller) RemovedList(opts *bind.CallOpts, stakerAddr common.Address) (bool, error) { - var out []interface{} - err := _L1Staking.contract.Call(opts, &out, "removedList", stakerAddr) - - if err != nil { - return *new(bool), err - } - - out0 := *abi.ConvertType(out[0], new(bool)).(*bool) - - return out0, err - -} - -// RemovedList is a free data retrieval call binding the contract method 0x3a9bbede. -// -// Solidity: function removedList(address stakerAddr) view returns(bool removed) -func (_L1Staking *L1StakingSession) RemovedList(stakerAddr common.Address) (bool, error) { - return _L1Staking.Contract.RemovedList(&_L1Staking.CallOpts, stakerAddr) -} - -// RemovedList is a free data retrieval call binding the contract method 0x3a9bbede. -// -// Solidity: function removedList(address stakerAddr) view returns(bool removed) -func (_L1Staking *L1StakingCallerSession) RemovedList(stakerAddr common.Address) (bool, error) { - return _L1Staking.Contract.RemovedList(&_L1Staking.CallOpts, stakerAddr) -} - -// RewardPercentage is a free data retrieval call binding the contract method 0x52d472eb. -// -// Solidity: function rewardPercentage() view returns(uint256) -func (_L1Staking *L1StakingCaller) RewardPercentage(opts *bind.CallOpts) (*big.Int, error) { - var out []interface{} - err := _L1Staking.contract.Call(opts, &out, "rewardPercentage") - - if err != nil { - return *new(*big.Int), err - } - - out0 := *abi.ConvertType(out[0], new(*big.Int)).(**big.Int) - - return out0, err - -} - -// RewardPercentage is a free data retrieval call binding the contract method 0x52d472eb. -// -// Solidity: function rewardPercentage() view returns(uint256) -func (_L1Staking *L1StakingSession) RewardPercentage() (*big.Int, error) { - return _L1Staking.Contract.RewardPercentage(&_L1Staking.CallOpts) -} - -// RewardPercentage is a free data retrieval call binding the contract method 0x52d472eb. -// -// Solidity: function rewardPercentage() view returns(uint256) -func (_L1Staking *L1StakingCallerSession) RewardPercentage() (*big.Int, error) { - return _L1Staking.Contract.RewardPercentage(&_L1Staking.CallOpts) -} - -// RollupContract is a free data retrieval call binding the contract method 0xbfa02ba9. -// -// Solidity: function rollupContract() view returns(address) -func (_L1Staking *L1StakingCaller) RollupContract(opts *bind.CallOpts) (common.Address, error) { - var out []interface{} - err := _L1Staking.contract.Call(opts, &out, "rollupContract") - - if err != nil { - return *new(common.Address), err - } - - out0 := *abi.ConvertType(out[0], new(common.Address)).(*common.Address) - - return out0, err - -} - -// RollupContract is a free data retrieval call binding the contract method 0xbfa02ba9. -// -// Solidity: function rollupContract() view returns(address) -func (_L1Staking *L1StakingSession) RollupContract() (common.Address, error) { - return _L1Staking.Contract.RollupContract(&_L1Staking.CallOpts) -} - -// RollupContract is a free data retrieval call binding the contract method 0xbfa02ba9. -// -// Solidity: function rollupContract() view returns(address) -func (_L1Staking *L1StakingCallerSession) RollupContract() (common.Address, error) { - return _L1Staking.Contract.RollupContract(&_L1Staking.CallOpts) -} - -// SlashRemaining is a free data retrieval call binding the contract method 0xab8c53dc. -// -// Solidity: function slashRemaining() view returns(uint256) -func (_L1Staking *L1StakingCaller) SlashRemaining(opts *bind.CallOpts) (*big.Int, error) { - var out []interface{} - err := _L1Staking.contract.Call(opts, &out, "slashRemaining") - - if err != nil { - return *new(*big.Int), err - } - - out0 := *abi.ConvertType(out[0], new(*big.Int)).(**big.Int) - - return out0, err - -} - -// SlashRemaining is a free data retrieval call binding the contract method 0xab8c53dc. -// -// Solidity: function slashRemaining() view returns(uint256) -func (_L1Staking *L1StakingSession) SlashRemaining() (*big.Int, error) { - return _L1Staking.Contract.SlashRemaining(&_L1Staking.CallOpts) -} - -// SlashRemaining is a free data retrieval call binding the contract method 0xab8c53dc. -// -// Solidity: function slashRemaining() view returns(uint256) -func (_L1Staking *L1StakingCallerSession) SlashRemaining() (*big.Int, error) { - return _L1Staking.Contract.SlashRemaining(&_L1Staking.CallOpts) -} - -// StakerIndexes is a free data retrieval call binding the contract method 0xdd4785f5. -// -// Solidity: function stakerIndexes(address stakerAddr) view returns(uint8 index) -func (_L1Staking *L1StakingCaller) StakerIndexes(opts *bind.CallOpts, stakerAddr common.Address) (uint8, error) { - var out []interface{} - err := _L1Staking.contract.Call(opts, &out, "stakerIndexes", stakerAddr) - - if err != nil { - return *new(uint8), err - } - - out0 := *abi.ConvertType(out[0], new(uint8)).(*uint8) - - return out0, err - -} - -// StakerIndexes is a free data retrieval call binding the contract method 0xdd4785f5. -// -// Solidity: function stakerIndexes(address stakerAddr) view returns(uint8 index) -func (_L1Staking *L1StakingSession) StakerIndexes(stakerAddr common.Address) (uint8, error) { - return _L1Staking.Contract.StakerIndexes(&_L1Staking.CallOpts, stakerAddr) -} - -// StakerIndexes is a free data retrieval call binding the contract method 0xdd4785f5. -// -// Solidity: function stakerIndexes(address stakerAddr) view returns(uint8 index) -func (_L1Staking *L1StakingCallerSession) StakerIndexes(stakerAddr common.Address) (uint8, error) { - return _L1Staking.Contract.StakerIndexes(&_L1Staking.CallOpts, stakerAddr) -} - -// StakerSet is a free data retrieval call binding the contract method 0x303afb9e. -// -// Solidity: function stakerSet(uint256 ) view returns(address) -func (_L1Staking *L1StakingCaller) StakerSet(opts *bind.CallOpts, arg0 *big.Int) (common.Address, error) { - var out []interface{} - err := _L1Staking.contract.Call(opts, &out, "stakerSet", arg0) - - if err != nil { - return *new(common.Address), err - } - - out0 := *abi.ConvertType(out[0], new(common.Address)).(*common.Address) - - return out0, err - -} - -// StakerSet is a free data retrieval call binding the contract method 0x303afb9e. -// -// Solidity: function stakerSet(uint256 ) view returns(address) -func (_L1Staking *L1StakingSession) StakerSet(arg0 *big.Int) (common.Address, error) { - return _L1Staking.Contract.StakerSet(&_L1Staking.CallOpts, arg0) -} - -// StakerSet is a free data retrieval call binding the contract method 0x303afb9e. -// -// Solidity: function stakerSet(uint256 ) view returns(address) -func (_L1Staking *L1StakingCallerSession) StakerSet(arg0 *big.Int) (common.Address, error) { - return _L1Staking.Contract.StakerSet(&_L1Staking.CallOpts, arg0) -} - -// Stakers is a free data retrieval call binding the contract method 0x9168ae72. -// -// Solidity: function stakers(address stakerAddr) view returns(address addr, bytes32 tmKey, bytes blsKey) -func (_L1Staking *L1StakingCaller) Stakers(opts *bind.CallOpts, stakerAddr common.Address) (struct { - Addr common.Address - TmKey [32]byte - BlsKey []byte -}, error) { - var out []interface{} - err := _L1Staking.contract.Call(opts, &out, "stakers", stakerAddr) - - outstruct := new(struct { - Addr common.Address - TmKey [32]byte - BlsKey []byte - }) - if err != nil { - return *outstruct, err - } - - outstruct.Addr = *abi.ConvertType(out[0], new(common.Address)).(*common.Address) - outstruct.TmKey = *abi.ConvertType(out[1], new([32]byte)).(*[32]byte) - outstruct.BlsKey = *abi.ConvertType(out[2], new([]byte)).(*[]byte) - - return *outstruct, err - -} - -// Stakers is a free data retrieval call binding the contract method 0x9168ae72. -// -// Solidity: function stakers(address stakerAddr) view returns(address addr, bytes32 tmKey, bytes blsKey) -func (_L1Staking *L1StakingSession) Stakers(stakerAddr common.Address) (struct { - Addr common.Address - TmKey [32]byte - BlsKey []byte -}, error) { - return _L1Staking.Contract.Stakers(&_L1Staking.CallOpts, stakerAddr) -} - -// Stakers is a free data retrieval call binding the contract method 0x9168ae72. -// -// Solidity: function stakers(address stakerAddr) view returns(address addr, bytes32 tmKey, bytes blsKey) -func (_L1Staking *L1StakingCallerSession) Stakers(stakerAddr common.Address) (struct { - Addr common.Address - TmKey [32]byte - BlsKey []byte -}, error) { - return _L1Staking.Contract.Stakers(&_L1Staking.CallOpts, stakerAddr) -} - -// StakingValue is a free data retrieval call binding the contract method 0x4d64903a. -// -// Solidity: function stakingValue() view returns(uint256) -func (_L1Staking *L1StakingCaller) StakingValue(opts *bind.CallOpts) (*big.Int, error) { - var out []interface{} - err := _L1Staking.contract.Call(opts, &out, "stakingValue") - - if err != nil { - return *new(*big.Int), err - } - - out0 := *abi.ConvertType(out[0], new(*big.Int)).(**big.Int) - - return out0, err - -} - -// StakingValue is a free data retrieval call binding the contract method 0x4d64903a. -// -// Solidity: function stakingValue() view returns(uint256) -func (_L1Staking *L1StakingSession) StakingValue() (*big.Int, error) { - return _L1Staking.Contract.StakingValue(&_L1Staking.CallOpts) -} - -// StakingValue is a free data retrieval call binding the contract method 0x4d64903a. -// -// Solidity: function stakingValue() view returns(uint256) -func (_L1Staking *L1StakingCallerSession) StakingValue() (*big.Int, error) { - return _L1Staking.Contract.StakingValue(&_L1Staking.CallOpts) -} - -// TmKeys is a free data retrieval call binding the contract method 0x692c565b. -// -// Solidity: function tmKeys(bytes32 tmPubkey) view returns(bool exist) -func (_L1Staking *L1StakingCaller) TmKeys(opts *bind.CallOpts, tmPubkey [32]byte) (bool, error) { - var out []interface{} - err := _L1Staking.contract.Call(opts, &out, "tmKeys", tmPubkey) - - if err != nil { - return *new(bool), err - } - - out0 := *abi.ConvertType(out[0], new(bool)).(*bool) - - return out0, err - -} - -// TmKeys is a free data retrieval call binding the contract method 0x692c565b. -// -// Solidity: function tmKeys(bytes32 tmPubkey) view returns(bool exist) -func (_L1Staking *L1StakingSession) TmKeys(tmPubkey [32]byte) (bool, error) { - return _L1Staking.Contract.TmKeys(&_L1Staking.CallOpts, tmPubkey) -} - -// TmKeys is a free data retrieval call binding the contract method 0x692c565b. -// -// Solidity: function tmKeys(bytes32 tmPubkey) view returns(bool exist) -func (_L1Staking *L1StakingCallerSession) TmKeys(tmPubkey [32]byte) (bool, error) { - return _L1Staking.Contract.TmKeys(&_L1Staking.CallOpts, tmPubkey) -} - -// VerifySignature is a free data retrieval call binding the contract method 0x74fe27b7. -// -// Solidity: function verifySignature(uint256 , address[] , bytes32 , bytes ) pure returns(bool) -func (_L1Staking *L1StakingCaller) VerifySignature(opts *bind.CallOpts, arg0 *big.Int, arg1 []common.Address, arg2 [32]byte, arg3 []byte) (bool, error) { - var out []interface{} - err := _L1Staking.contract.Call(opts, &out, "verifySignature", arg0, arg1, arg2, arg3) - - if err != nil { - return *new(bool), err - } - - out0 := *abi.ConvertType(out[0], new(bool)).(*bool) - - return out0, err - -} - -// VerifySignature is a free data retrieval call binding the contract method 0x74fe27b7. -// -// Solidity: function verifySignature(uint256 , address[] , bytes32 , bytes ) pure returns(bool) -func (_L1Staking *L1StakingSession) VerifySignature(arg0 *big.Int, arg1 []common.Address, arg2 [32]byte, arg3 []byte) (bool, error) { - return _L1Staking.Contract.VerifySignature(&_L1Staking.CallOpts, arg0, arg1, arg2, arg3) -} - -// VerifySignature is a free data retrieval call binding the contract method 0x74fe27b7. -// -// Solidity: function verifySignature(uint256 , address[] , bytes32 , bytes ) pure returns(bool) -func (_L1Staking *L1StakingCallerSession) VerifySignature(arg0 *big.Int, arg1 []common.Address, arg2 [32]byte, arg3 []byte) (bool, error) { - return _L1Staking.Contract.VerifySignature(&_L1Staking.CallOpts, arg0, arg1, arg2, arg3) -} - -// Whitelist is a free data retrieval call binding the contract method 0x9b19251a. -// -// Solidity: function whitelist(address stakerAddr) view returns(bool inWhitelist) -func (_L1Staking *L1StakingCaller) Whitelist(opts *bind.CallOpts, stakerAddr common.Address) (bool, error) { - var out []interface{} - err := _L1Staking.contract.Call(opts, &out, "whitelist", stakerAddr) - - if err != nil { - return *new(bool), err - } - - out0 := *abi.ConvertType(out[0], new(bool)).(*bool) - - return out0, err - -} - -// Whitelist is a free data retrieval call binding the contract method 0x9b19251a. -// -// Solidity: function whitelist(address stakerAddr) view returns(bool inWhitelist) -func (_L1Staking *L1StakingSession) Whitelist(stakerAddr common.Address) (bool, error) { - return _L1Staking.Contract.Whitelist(&_L1Staking.CallOpts, stakerAddr) -} - -// Whitelist is a free data retrieval call binding the contract method 0x9b19251a. -// -// Solidity: function whitelist(address stakerAddr) view returns(bool inWhitelist) -func (_L1Staking *L1StakingCallerSession) Whitelist(stakerAddr common.Address) (bool, error) { - return _L1Staking.Contract.Whitelist(&_L1Staking.CallOpts, stakerAddr) -} - -// WithdrawalLockBlocks is a free data retrieval call binding the contract method 0x41de239b. -// -// Solidity: function withdrawalLockBlocks() view returns(uint256) -func (_L1Staking *L1StakingCaller) WithdrawalLockBlocks(opts *bind.CallOpts) (*big.Int, error) { - var out []interface{} - err := _L1Staking.contract.Call(opts, &out, "withdrawalLockBlocks") - - if err != nil { - return *new(*big.Int), err - } - - out0 := *abi.ConvertType(out[0], new(*big.Int)).(**big.Int) - - return out0, err - -} - -// WithdrawalLockBlocks is a free data retrieval call binding the contract method 0x41de239b. -// -// Solidity: function withdrawalLockBlocks() view returns(uint256) -func (_L1Staking *L1StakingSession) WithdrawalLockBlocks() (*big.Int, error) { - return _L1Staking.Contract.WithdrawalLockBlocks(&_L1Staking.CallOpts) -} - -// WithdrawalLockBlocks is a free data retrieval call binding the contract method 0x41de239b. -// -// Solidity: function withdrawalLockBlocks() view returns(uint256) -func (_L1Staking *L1StakingCallerSession) WithdrawalLockBlocks() (*big.Int, error) { - return _L1Staking.Contract.WithdrawalLockBlocks(&_L1Staking.CallOpts) -} - -// Withdrawals is a free data retrieval call binding the contract method 0x7a9262a2. -// -// Solidity: function withdrawals(address staker) view returns(uint256 amount) -func (_L1Staking *L1StakingCaller) Withdrawals(opts *bind.CallOpts, staker common.Address) (*big.Int, error) { - var out []interface{} - err := _L1Staking.contract.Call(opts, &out, "withdrawals", staker) - - if err != nil { - return *new(*big.Int), err - } - - out0 := *abi.ConvertType(out[0], new(*big.Int)).(**big.Int) - - return out0, err - -} - -// Withdrawals is a free data retrieval call binding the contract method 0x7a9262a2. -// -// Solidity: function withdrawals(address staker) view returns(uint256 amount) -func (_L1Staking *L1StakingSession) Withdrawals(staker common.Address) (*big.Int, error) { - return _L1Staking.Contract.Withdrawals(&_L1Staking.CallOpts, staker) -} - -// Withdrawals is a free data retrieval call binding the contract method 0x7a9262a2. -// -// Solidity: function withdrawals(address staker) view returns(uint256 amount) -func (_L1Staking *L1StakingCallerSession) Withdrawals(staker common.Address) (*big.Int, error) { - return _L1Staking.Contract.Withdrawals(&_L1Staking.CallOpts, staker) -} - -// ClaimSlashRemaining is a paid mutator transaction binding the contract method 0xcde4cd11. -// -// Solidity: function claimSlashRemaining(address receiver) returns() -func (_L1Staking *L1StakingTransactor) ClaimSlashRemaining(opts *bind.TransactOpts, receiver common.Address) (*types.Transaction, error) { - return _L1Staking.contract.Transact(opts, "claimSlashRemaining", receiver) -} - -// ClaimSlashRemaining is a paid mutator transaction binding the contract method 0xcde4cd11. -// -// Solidity: function claimSlashRemaining(address receiver) returns() -func (_L1Staking *L1StakingSession) ClaimSlashRemaining(receiver common.Address) (*types.Transaction, error) { - return _L1Staking.Contract.ClaimSlashRemaining(&_L1Staking.TransactOpts, receiver) -} - -// ClaimSlashRemaining is a paid mutator transaction binding the contract method 0xcde4cd11. -// -// Solidity: function claimSlashRemaining(address receiver) returns() -func (_L1Staking *L1StakingTransactorSession) ClaimSlashRemaining(receiver common.Address) (*types.Transaction, error) { - return _L1Staking.Contract.ClaimSlashRemaining(&_L1Staking.TransactOpts, receiver) -} - -// ClaimWithdrawal is a paid mutator transaction binding the contract method 0xa3066aab. -// -// Solidity: function claimWithdrawal(address receiver) returns() -func (_L1Staking *L1StakingTransactor) ClaimWithdrawal(opts *bind.TransactOpts, receiver common.Address) (*types.Transaction, error) { - return _L1Staking.contract.Transact(opts, "claimWithdrawal", receiver) -} - -// ClaimWithdrawal is a paid mutator transaction binding the contract method 0xa3066aab. -// -// Solidity: function claimWithdrawal(address receiver) returns() -func (_L1Staking *L1StakingSession) ClaimWithdrawal(receiver common.Address) (*types.Transaction, error) { - return _L1Staking.Contract.ClaimWithdrawal(&_L1Staking.TransactOpts, receiver) -} - -// ClaimWithdrawal is a paid mutator transaction binding the contract method 0xa3066aab. -// -// Solidity: function claimWithdrawal(address receiver) returns() -func (_L1Staking *L1StakingTransactorSession) ClaimWithdrawal(receiver common.Address) (*types.Transaction, error) { - return _L1Staking.Contract.ClaimWithdrawal(&_L1Staking.TransactOpts, receiver) -} - -// CleanStakerStore is a paid mutator transaction binding the contract method 0x3ee2a1f9. -// -// Solidity: function cleanStakerStore() returns() -func (_L1Staking *L1StakingTransactor) CleanStakerStore(opts *bind.TransactOpts) (*types.Transaction, error) { - return _L1Staking.contract.Transact(opts, "cleanStakerStore") -} - -// CleanStakerStore is a paid mutator transaction binding the contract method 0x3ee2a1f9. -// -// Solidity: function cleanStakerStore() returns() -func (_L1Staking *L1StakingSession) CleanStakerStore() (*types.Transaction, error) { - return _L1Staking.Contract.CleanStakerStore(&_L1Staking.TransactOpts) -} - -// CleanStakerStore is a paid mutator transaction binding the contract method 0x3ee2a1f9. -// -// Solidity: function cleanStakerStore() returns() -func (_L1Staking *L1StakingTransactorSession) CleanStakerStore() (*types.Transaction, error) { - return _L1Staking.Contract.CleanStakerStore(&_L1Staking.TransactOpts) -} - -// Initialize is a paid mutator transaction binding the contract method 0x8142951a. -// -// Solidity: function initialize(address _rollupContract, uint256 _stakingValue, uint256 _challengeDeposit, uint256 _lockBlocks, uint256 _rewardPercentage, uint256 _gasLimitAdd, uint256 _gasLimitRemove) returns() -func (_L1Staking *L1StakingTransactor) Initialize(opts *bind.TransactOpts, _rollupContract common.Address, _stakingValue *big.Int, _challengeDeposit *big.Int, _lockBlocks *big.Int, _rewardPercentage *big.Int, _gasLimitAdd *big.Int, _gasLimitRemove *big.Int) (*types.Transaction, error) { - return _L1Staking.contract.Transact(opts, "initialize", _rollupContract, _stakingValue, _challengeDeposit, _lockBlocks, _rewardPercentage, _gasLimitAdd, _gasLimitRemove) -} - -// Initialize is a paid mutator transaction binding the contract method 0x8142951a. -// -// Solidity: function initialize(address _rollupContract, uint256 _stakingValue, uint256 _challengeDeposit, uint256 _lockBlocks, uint256 _rewardPercentage, uint256 _gasLimitAdd, uint256 _gasLimitRemove) returns() -func (_L1Staking *L1StakingSession) Initialize(_rollupContract common.Address, _stakingValue *big.Int, _challengeDeposit *big.Int, _lockBlocks *big.Int, _rewardPercentage *big.Int, _gasLimitAdd *big.Int, _gasLimitRemove *big.Int) (*types.Transaction, error) { - return _L1Staking.Contract.Initialize(&_L1Staking.TransactOpts, _rollupContract, _stakingValue, _challengeDeposit, _lockBlocks, _rewardPercentage, _gasLimitAdd, _gasLimitRemove) -} - -// Initialize is a paid mutator transaction binding the contract method 0x8142951a. -// -// Solidity: function initialize(address _rollupContract, uint256 _stakingValue, uint256 _challengeDeposit, uint256 _lockBlocks, uint256 _rewardPercentage, uint256 _gasLimitAdd, uint256 _gasLimitRemove) returns() -func (_L1Staking *L1StakingTransactorSession) Initialize(_rollupContract common.Address, _stakingValue *big.Int, _challengeDeposit *big.Int, _lockBlocks *big.Int, _rewardPercentage *big.Int, _gasLimitAdd *big.Int, _gasLimitRemove *big.Int) (*types.Transaction, error) { - return _L1Staking.Contract.Initialize(&_L1Staking.TransactOpts, _rollupContract, _stakingValue, _challengeDeposit, _lockBlocks, _rewardPercentage, _gasLimitAdd, _gasLimitRemove) -} - -// Register is a paid mutator transaction binding the contract method 0x2a28e5a3. -// -// Solidity: function register(bytes32 tmKey, bytes blsKey) payable returns() -func (_L1Staking *L1StakingTransactor) Register(opts *bind.TransactOpts, tmKey [32]byte, blsKey []byte) (*types.Transaction, error) { - return _L1Staking.contract.Transact(opts, "register", tmKey, blsKey) -} - -// Register is a paid mutator transaction binding the contract method 0x2a28e5a3. -// -// Solidity: function register(bytes32 tmKey, bytes blsKey) payable returns() -func (_L1Staking *L1StakingSession) Register(tmKey [32]byte, blsKey []byte) (*types.Transaction, error) { - return _L1Staking.Contract.Register(&_L1Staking.TransactOpts, tmKey, blsKey) -} - -// Register is a paid mutator transaction binding the contract method 0x2a28e5a3. -// -// Solidity: function register(bytes32 tmKey, bytes blsKey) payable returns() -func (_L1Staking *L1StakingTransactorSession) Register(tmKey [32]byte, blsKey []byte) (*types.Transaction, error) { - return _L1Staking.Contract.Register(&_L1Staking.TransactOpts, tmKey, blsKey) -} - -// RemoveStaker is a paid mutator transaction binding the contract method 0x320ff860. -// -// Solidity: function removeStaker(address[] _stakers) returns() -func (_L1Staking *L1StakingTransactor) RemoveStaker(opts *bind.TransactOpts, _stakers []common.Address) (*types.Transaction, error) { - return _L1Staking.contract.Transact(opts, "removeStaker", _stakers) -} - -// RemoveStaker is a paid mutator transaction binding the contract method 0x320ff860. -// -// Solidity: function removeStaker(address[] _stakers) returns() -func (_L1Staking *L1StakingSession) RemoveStaker(_stakers []common.Address) (*types.Transaction, error) { - return _L1Staking.Contract.RemoveStaker(&_L1Staking.TransactOpts, _stakers) -} - -// RemoveStaker is a paid mutator transaction binding the contract method 0x320ff860. -// -// Solidity: function removeStaker(address[] _stakers) returns() -func (_L1Staking *L1StakingTransactorSession) RemoveStaker(_stakers []common.Address) (*types.Transaction, error) { - return _L1Staking.Contract.RemoveStaker(&_L1Staking.TransactOpts, _stakers) -} - -// RenounceOwnership is a paid mutator transaction binding the contract method 0x715018a6. -// -// Solidity: function renounceOwnership() returns() -func (_L1Staking *L1StakingTransactor) RenounceOwnership(opts *bind.TransactOpts) (*types.Transaction, error) { - return _L1Staking.contract.Transact(opts, "renounceOwnership") -} - -// RenounceOwnership is a paid mutator transaction binding the contract method 0x715018a6. -// -// Solidity: function renounceOwnership() returns() -func (_L1Staking *L1StakingSession) RenounceOwnership() (*types.Transaction, error) { - return _L1Staking.Contract.RenounceOwnership(&_L1Staking.TransactOpts) -} - -// RenounceOwnership is a paid mutator transaction binding the contract method 0x715018a6. -// -// Solidity: function renounceOwnership() returns() -func (_L1Staking *L1StakingTransactorSession) RenounceOwnership() (*types.Transaction, error) { - return _L1Staking.Contract.RenounceOwnership(&_L1Staking.TransactOpts) -} - -// Slash is a paid mutator transaction binding the contract method 0x45bc4d10. -// -// Solidity: function slash(uint256 sequencersBitmap) returns(uint256) -func (_L1Staking *L1StakingTransactor) Slash(opts *bind.TransactOpts, sequencersBitmap *big.Int) (*types.Transaction, error) { - return _L1Staking.contract.Transact(opts, "slash", sequencersBitmap) -} - -// Slash is a paid mutator transaction binding the contract method 0x45bc4d10. -// -// Solidity: function slash(uint256 sequencersBitmap) returns(uint256) -func (_L1Staking *L1StakingSession) Slash(sequencersBitmap *big.Int) (*types.Transaction, error) { - return _L1Staking.Contract.Slash(&_L1Staking.TransactOpts, sequencersBitmap) -} - -// Slash is a paid mutator transaction binding the contract method 0x45bc4d10. -// -// Solidity: function slash(uint256 sequencersBitmap) returns(uint256) -func (_L1Staking *L1StakingTransactorSession) Slash(sequencersBitmap *big.Int) (*types.Transaction, error) { - return _L1Staking.Contract.Slash(&_L1Staking.TransactOpts, sequencersBitmap) -} - -// TransferOwnership is a paid mutator transaction binding the contract method 0xf2fde38b. -// -// Solidity: function transferOwnership(address newOwner) returns() -func (_L1Staking *L1StakingTransactor) TransferOwnership(opts *bind.TransactOpts, newOwner common.Address) (*types.Transaction, error) { - return _L1Staking.contract.Transact(opts, "transferOwnership", newOwner) -} - -// TransferOwnership is a paid mutator transaction binding the contract method 0xf2fde38b. -// -// Solidity: function transferOwnership(address newOwner) returns() -func (_L1Staking *L1StakingSession) TransferOwnership(newOwner common.Address) (*types.Transaction, error) { - return _L1Staking.Contract.TransferOwnership(&_L1Staking.TransactOpts, newOwner) -} - -// TransferOwnership is a paid mutator transaction binding the contract method 0xf2fde38b. -// -// Solidity: function transferOwnership(address newOwner) returns() -func (_L1Staking *L1StakingTransactorSession) TransferOwnership(newOwner common.Address) (*types.Transaction, error) { - return _L1Staking.Contract.TransferOwnership(&_L1Staking.TransactOpts, newOwner) -} - -// UpdateChallengeDeposit is a paid mutator transaction binding the contract method 0x35928991. -// -// Solidity: function updateChallengeDeposit(uint256 _challengeDeposit) returns() -func (_L1Staking *L1StakingTransactor) UpdateChallengeDeposit(opts *bind.TransactOpts, _challengeDeposit *big.Int) (*types.Transaction, error) { - return _L1Staking.contract.Transact(opts, "updateChallengeDeposit", _challengeDeposit) -} - -// UpdateChallengeDeposit is a paid mutator transaction binding the contract method 0x35928991. -// -// Solidity: function updateChallengeDeposit(uint256 _challengeDeposit) returns() -func (_L1Staking *L1StakingSession) UpdateChallengeDeposit(_challengeDeposit *big.Int) (*types.Transaction, error) { - return _L1Staking.Contract.UpdateChallengeDeposit(&_L1Staking.TransactOpts, _challengeDeposit) -} - -// UpdateChallengeDeposit is a paid mutator transaction binding the contract method 0x35928991. -// -// Solidity: function updateChallengeDeposit(uint256 _challengeDeposit) returns() -func (_L1Staking *L1StakingTransactorSession) UpdateChallengeDeposit(_challengeDeposit *big.Int) (*types.Transaction, error) { - return _L1Staking.Contract.UpdateChallengeDeposit(&_L1Staking.TransactOpts, _challengeDeposit) -} - -// UpdateGasLimitAddStaker is a paid mutator transaction binding the contract method 0xae81de53. -// -// Solidity: function updateGasLimitAddStaker(uint256 _gasLimitAdd) returns() -func (_L1Staking *L1StakingTransactor) UpdateGasLimitAddStaker(opts *bind.TransactOpts, _gasLimitAdd *big.Int) (*types.Transaction, error) { - return _L1Staking.contract.Transact(opts, "updateGasLimitAddStaker", _gasLimitAdd) -} - -// UpdateGasLimitAddStaker is a paid mutator transaction binding the contract method 0xae81de53. -// -// Solidity: function updateGasLimitAddStaker(uint256 _gasLimitAdd) returns() -func (_L1Staking *L1StakingSession) UpdateGasLimitAddStaker(_gasLimitAdd *big.Int) (*types.Transaction, error) { - return _L1Staking.Contract.UpdateGasLimitAddStaker(&_L1Staking.TransactOpts, _gasLimitAdd) -} - -// UpdateGasLimitAddStaker is a paid mutator transaction binding the contract method 0xae81de53. -// -// Solidity: function updateGasLimitAddStaker(uint256 _gasLimitAdd) returns() -func (_L1Staking *L1StakingTransactorSession) UpdateGasLimitAddStaker(_gasLimitAdd *big.Int) (*types.Transaction, error) { - return _L1Staking.Contract.UpdateGasLimitAddStaker(&_L1Staking.TransactOpts, _gasLimitAdd) -} - -// UpdateGasLimitRemoveStakers is a paid mutator transaction binding the contract method 0x8a565ac3. -// -// Solidity: function updateGasLimitRemoveStakers(uint256 _gasLimitRemove) returns() -func (_L1Staking *L1StakingTransactor) UpdateGasLimitRemoveStakers(opts *bind.TransactOpts, _gasLimitRemove *big.Int) (*types.Transaction, error) { - return _L1Staking.contract.Transact(opts, "updateGasLimitRemoveStakers", _gasLimitRemove) -} - -// UpdateGasLimitRemoveStakers is a paid mutator transaction binding the contract method 0x8a565ac3. -// -// Solidity: function updateGasLimitRemoveStakers(uint256 _gasLimitRemove) returns() -func (_L1Staking *L1StakingSession) UpdateGasLimitRemoveStakers(_gasLimitRemove *big.Int) (*types.Transaction, error) { - return _L1Staking.Contract.UpdateGasLimitRemoveStakers(&_L1Staking.TransactOpts, _gasLimitRemove) -} - -// UpdateGasLimitRemoveStakers is a paid mutator transaction binding the contract method 0x8a565ac3. -// -// Solidity: function updateGasLimitRemoveStakers(uint256 _gasLimitRemove) returns() -func (_L1Staking *L1StakingTransactorSession) UpdateGasLimitRemoveStakers(_gasLimitRemove *big.Int) (*types.Transaction, error) { - return _L1Staking.Contract.UpdateGasLimitRemoveStakers(&_L1Staking.TransactOpts, _gasLimitRemove) -} - -// UpdateRewardPercentage is a paid mutator transaction binding the contract method 0xa4f209b0. -// -// Solidity: function updateRewardPercentage(uint256 _rewardPercentage) returns() -func (_L1Staking *L1StakingTransactor) UpdateRewardPercentage(opts *bind.TransactOpts, _rewardPercentage *big.Int) (*types.Transaction, error) { - return _L1Staking.contract.Transact(opts, "updateRewardPercentage", _rewardPercentage) -} - -// UpdateRewardPercentage is a paid mutator transaction binding the contract method 0xa4f209b0. -// -// Solidity: function updateRewardPercentage(uint256 _rewardPercentage) returns() -func (_L1Staking *L1StakingSession) UpdateRewardPercentage(_rewardPercentage *big.Int) (*types.Transaction, error) { - return _L1Staking.Contract.UpdateRewardPercentage(&_L1Staking.TransactOpts, _rewardPercentage) -} - -// UpdateRewardPercentage is a paid mutator transaction binding the contract method 0xa4f209b0. -// -// Solidity: function updateRewardPercentage(uint256 _rewardPercentage) returns() -func (_L1Staking *L1StakingTransactorSession) UpdateRewardPercentage(_rewardPercentage *big.Int) (*types.Transaction, error) { - return _L1Staking.Contract.UpdateRewardPercentage(&_L1Staking.TransactOpts, _rewardPercentage) -} - -// UpdateStakingValue is a paid mutator transaction binding the contract method 0xf4fe7fef. -// -// Solidity: function updateStakingValue(uint256 _stakingValue) returns() -func (_L1Staking *L1StakingTransactor) UpdateStakingValue(opts *bind.TransactOpts, _stakingValue *big.Int) (*types.Transaction, error) { - return _L1Staking.contract.Transact(opts, "updateStakingValue", _stakingValue) -} - -// UpdateStakingValue is a paid mutator transaction binding the contract method 0xf4fe7fef. -// -// Solidity: function updateStakingValue(uint256 _stakingValue) returns() -func (_L1Staking *L1StakingSession) UpdateStakingValue(_stakingValue *big.Int) (*types.Transaction, error) { - return _L1Staking.Contract.UpdateStakingValue(&_L1Staking.TransactOpts, _stakingValue) -} - -// UpdateStakingValue is a paid mutator transaction binding the contract method 0xf4fe7fef. -// -// Solidity: function updateStakingValue(uint256 _stakingValue) returns() -func (_L1Staking *L1StakingTransactorSession) UpdateStakingValue(_stakingValue *big.Int) (*types.Transaction, error) { - return _L1Staking.Contract.UpdateStakingValue(&_L1Staking.TransactOpts, _stakingValue) -} - -// UpdateWhitelist is a paid mutator transaction binding the contract method 0xc7cd469a. -// -// Solidity: function updateWhitelist(address[] add, address[] remove) returns() -func (_L1Staking *L1StakingTransactor) UpdateWhitelist(opts *bind.TransactOpts, add []common.Address, remove []common.Address) (*types.Transaction, error) { - return _L1Staking.contract.Transact(opts, "updateWhitelist", add, remove) -} - -// UpdateWhitelist is a paid mutator transaction binding the contract method 0xc7cd469a. -// -// Solidity: function updateWhitelist(address[] add, address[] remove) returns() -func (_L1Staking *L1StakingSession) UpdateWhitelist(add []common.Address, remove []common.Address) (*types.Transaction, error) { - return _L1Staking.Contract.UpdateWhitelist(&_L1Staking.TransactOpts, add, remove) -} - -// UpdateWhitelist is a paid mutator transaction binding the contract method 0xc7cd469a. -// -// Solidity: function updateWhitelist(address[] add, address[] remove) returns() -func (_L1Staking *L1StakingTransactorSession) UpdateWhitelist(add []common.Address, remove []common.Address) (*types.Transaction, error) { - return _L1Staking.Contract.UpdateWhitelist(&_L1Staking.TransactOpts, add, remove) -} - -// Withdraw is a paid mutator transaction binding the contract method 0x3ccfd60b. -// -// Solidity: function withdraw() returns() -func (_L1Staking *L1StakingTransactor) Withdraw(opts *bind.TransactOpts) (*types.Transaction, error) { - return _L1Staking.contract.Transact(opts, "withdraw") -} - -// Withdraw is a paid mutator transaction binding the contract method 0x3ccfd60b. -// -// Solidity: function withdraw() returns() -func (_L1Staking *L1StakingSession) Withdraw() (*types.Transaction, error) { - return _L1Staking.Contract.Withdraw(&_L1Staking.TransactOpts) -} - -// Withdraw is a paid mutator transaction binding the contract method 0x3ccfd60b. -// -// Solidity: function withdraw() returns() -func (_L1Staking *L1StakingTransactorSession) Withdraw() (*types.Transaction, error) { - return _L1Staking.Contract.Withdraw(&_L1Staking.TransactOpts) -} - -// L1StakingChallengeDepositUpdatedIterator is returned from FilterChallengeDepositUpdated and is used to iterate over the raw logs and unpacked data for ChallengeDepositUpdated events raised by the L1Staking contract. -type L1StakingChallengeDepositUpdatedIterator struct { - Event *L1StakingChallengeDepositUpdated // Event containing the contract specifics and raw log - - contract *bind.BoundContract // Generic contract to use for unpacking event data - event string // Event name to use for unpacking event data - - logs chan types.Log // Log channel receiving the found contract events - sub ethereum.Subscription // Subscription for errors, completion and termination - done bool // Whether the subscription completed delivering logs - fail error // Occurred error to stop iteration -} - -// Next advances the iterator to the subsequent event, returning whether there -// are any more events found. In case of a retrieval or parsing error, false is -// returned and Error() can be queried for the exact failure. -func (it *L1StakingChallengeDepositUpdatedIterator) Next() bool { - // If the iterator failed, stop iterating - if it.fail != nil { - return false - } - // If the iterator completed, deliver directly whatever's available - if it.done { - select { - case log := <-it.logs: - it.Event = new(L1StakingChallengeDepositUpdated) - if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { - it.fail = err - return false - } - it.Event.Raw = log - return true - - default: - return false - } - } - // Iterator still in progress, wait for either a data or an error event - select { - case log := <-it.logs: - it.Event = new(L1StakingChallengeDepositUpdated) - if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { - it.fail = err - return false - } - it.Event.Raw = log - return true - - case err := <-it.sub.Err(): - it.done = true - it.fail = err - return it.Next() - } -} - -// Error returns any retrieval or parsing error occurred during filtering. -func (it *L1StakingChallengeDepositUpdatedIterator) Error() error { - return it.fail -} - -// Close terminates the iteration process, releasing any pending underlying -// resources. -func (it *L1StakingChallengeDepositUpdatedIterator) Close() error { - it.sub.Unsubscribe() - return nil -} - -// L1StakingChallengeDepositUpdated represents a ChallengeDepositUpdated event raised by the L1Staking contract. -type L1StakingChallengeDepositUpdated struct { - OldChallengeDeposit *big.Int - NewChallengeDeposit *big.Int - Raw types.Log // Blockchain specific contextual infos -} - -// FilterChallengeDepositUpdated is a free log retrieval operation binding the contract event 0x36f971a40478225aeb80cfbf5e80306e8cb76d3bf7d56fdc5e490945cddb7d55. -// -// Solidity: event ChallengeDepositUpdated(uint256 oldChallengeDeposit, uint256 newChallengeDeposit) -func (_L1Staking *L1StakingFilterer) FilterChallengeDepositUpdated(opts *bind.FilterOpts) (*L1StakingChallengeDepositUpdatedIterator, error) { - - logs, sub, err := _L1Staking.contract.FilterLogs(opts, "ChallengeDepositUpdated") - if err != nil { - return nil, err - } - return &L1StakingChallengeDepositUpdatedIterator{contract: _L1Staking.contract, event: "ChallengeDepositUpdated", logs: logs, sub: sub}, nil -} - -// WatchChallengeDepositUpdated is a free log subscription operation binding the contract event 0x36f971a40478225aeb80cfbf5e80306e8cb76d3bf7d56fdc5e490945cddb7d55. -// -// Solidity: event ChallengeDepositUpdated(uint256 oldChallengeDeposit, uint256 newChallengeDeposit) -func (_L1Staking *L1StakingFilterer) WatchChallengeDepositUpdated(opts *bind.WatchOpts, sink chan<- *L1StakingChallengeDepositUpdated) (event.Subscription, error) { - - logs, sub, err := _L1Staking.contract.WatchLogs(opts, "ChallengeDepositUpdated") - if err != nil { - return nil, err - } - return event.NewSubscription(func(quit <-chan struct{}) error { - defer sub.Unsubscribe() - for { - select { - case log := <-logs: - // New log arrived, parse the event and forward to the user - event := new(L1StakingChallengeDepositUpdated) - if err := _L1Staking.contract.UnpackLog(event, "ChallengeDepositUpdated", log); err != nil { - return err - } - event.Raw = log - - select { - case sink <- event: - case err := <-sub.Err(): - return err - case <-quit: - return nil - } - case err := <-sub.Err(): - return err - case <-quit: - return nil - } - } - }), nil -} - -// ParseChallengeDepositUpdated is a log parse operation binding the contract event 0x36f971a40478225aeb80cfbf5e80306e8cb76d3bf7d56fdc5e490945cddb7d55. -// -// Solidity: event ChallengeDepositUpdated(uint256 oldChallengeDeposit, uint256 newChallengeDeposit) -func (_L1Staking *L1StakingFilterer) ParseChallengeDepositUpdated(log types.Log) (*L1StakingChallengeDepositUpdated, error) { - event := new(L1StakingChallengeDepositUpdated) - if err := _L1Staking.contract.UnpackLog(event, "ChallengeDepositUpdated", log); err != nil { - return nil, err - } - event.Raw = log - return event, nil -} - -// L1StakingClaimedIterator is returned from FilterClaimed and is used to iterate over the raw logs and unpacked data for Claimed events raised by the L1Staking contract. -type L1StakingClaimedIterator struct { - Event *L1StakingClaimed // Event containing the contract specifics and raw log - - contract *bind.BoundContract // Generic contract to use for unpacking event data - event string // Event name to use for unpacking event data - - logs chan types.Log // Log channel receiving the found contract events - sub ethereum.Subscription // Subscription for errors, completion and termination - done bool // Whether the subscription completed delivering logs - fail error // Occurred error to stop iteration -} - -// Next advances the iterator to the subsequent event, returning whether there -// are any more events found. In case of a retrieval or parsing error, false is -// returned and Error() can be queried for the exact failure. -func (it *L1StakingClaimedIterator) Next() bool { - // If the iterator failed, stop iterating - if it.fail != nil { - return false - } - // If the iterator completed, deliver directly whatever's available - if it.done { - select { - case log := <-it.logs: - it.Event = new(L1StakingClaimed) - if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { - it.fail = err - return false - } - it.Event.Raw = log - return true - - default: - return false - } - } - // Iterator still in progress, wait for either a data or an error event - select { - case log := <-it.logs: - it.Event = new(L1StakingClaimed) - if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { - it.fail = err - return false - } - it.Event.Raw = log - return true - - case err := <-it.sub.Err(): - it.done = true - it.fail = err - return it.Next() - } -} - -// Error returns any retrieval or parsing error occurred during filtering. -func (it *L1StakingClaimedIterator) Error() error { - return it.fail -} - -// Close terminates the iteration process, releasing any pending underlying -// resources. -func (it *L1StakingClaimedIterator) Close() error { - it.sub.Unsubscribe() - return nil -} - -// L1StakingClaimed represents a Claimed event raised by the L1Staking contract. -type L1StakingClaimed struct { - Staker common.Address - Receiver common.Address - Raw types.Log // Blockchain specific contextual infos -} - -// FilterClaimed is a free log retrieval operation binding the contract event 0x89309c9b2aeaffbdce717113df9427298b20448c05919bf889e05f8c3094254b. -// -// Solidity: event Claimed(address indexed staker, address receiver) -func (_L1Staking *L1StakingFilterer) FilterClaimed(opts *bind.FilterOpts, staker []common.Address) (*L1StakingClaimedIterator, error) { - - var stakerRule []interface{} - for _, stakerItem := range staker { - stakerRule = append(stakerRule, stakerItem) - } - - logs, sub, err := _L1Staking.contract.FilterLogs(opts, "Claimed", stakerRule) - if err != nil { - return nil, err - } - return &L1StakingClaimedIterator{contract: _L1Staking.contract, event: "Claimed", logs: logs, sub: sub}, nil -} - -// WatchClaimed is a free log subscription operation binding the contract event 0x89309c9b2aeaffbdce717113df9427298b20448c05919bf889e05f8c3094254b. -// -// Solidity: event Claimed(address indexed staker, address receiver) -func (_L1Staking *L1StakingFilterer) WatchClaimed(opts *bind.WatchOpts, sink chan<- *L1StakingClaimed, staker []common.Address) (event.Subscription, error) { - - var stakerRule []interface{} - for _, stakerItem := range staker { - stakerRule = append(stakerRule, stakerItem) - } - - logs, sub, err := _L1Staking.contract.WatchLogs(opts, "Claimed", stakerRule) - if err != nil { - return nil, err - } - return event.NewSubscription(func(quit <-chan struct{}) error { - defer sub.Unsubscribe() - for { - select { - case log := <-logs: - // New log arrived, parse the event and forward to the user - event := new(L1StakingClaimed) - if err := _L1Staking.contract.UnpackLog(event, "Claimed", log); err != nil { - return err - } - event.Raw = log - - select { - case sink <- event: - case err := <-sub.Err(): - return err - case <-quit: - return nil - } - case err := <-sub.Err(): - return err - case <-quit: - return nil - } - } - }), nil -} - -// ParseClaimed is a log parse operation binding the contract event 0x89309c9b2aeaffbdce717113df9427298b20448c05919bf889e05f8c3094254b. -// -// Solidity: event Claimed(address indexed staker, address receiver) -func (_L1Staking *L1StakingFilterer) ParseClaimed(log types.Log) (*L1StakingClaimed, error) { - event := new(L1StakingClaimed) - if err := _L1Staking.contract.UnpackLog(event, "Claimed", log); err != nil { - return nil, err - } - event.Raw = log - return event, nil -} - -// L1StakingGasLimitAddStakerUpdatedIterator is returned from FilterGasLimitAddStakerUpdated and is used to iterate over the raw logs and unpacked data for GasLimitAddStakerUpdated events raised by the L1Staking contract. -type L1StakingGasLimitAddStakerUpdatedIterator struct { - Event *L1StakingGasLimitAddStakerUpdated // Event containing the contract specifics and raw log - - contract *bind.BoundContract // Generic contract to use for unpacking event data - event string // Event name to use for unpacking event data - - logs chan types.Log // Log channel receiving the found contract events - sub ethereum.Subscription // Subscription for errors, completion and termination - done bool // Whether the subscription completed delivering logs - fail error // Occurred error to stop iteration -} - -// Next advances the iterator to the subsequent event, returning whether there -// are any more events found. In case of a retrieval or parsing error, false is -// returned and Error() can be queried for the exact failure. -func (it *L1StakingGasLimitAddStakerUpdatedIterator) Next() bool { - // If the iterator failed, stop iterating - if it.fail != nil { - return false - } - // If the iterator completed, deliver directly whatever's available - if it.done { - select { - case log := <-it.logs: - it.Event = new(L1StakingGasLimitAddStakerUpdated) - if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { - it.fail = err - return false - } - it.Event.Raw = log - return true - - default: - return false - } - } - // Iterator still in progress, wait for either a data or an error event - select { - case log := <-it.logs: - it.Event = new(L1StakingGasLimitAddStakerUpdated) - if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { - it.fail = err - return false - } - it.Event.Raw = log - return true - - case err := <-it.sub.Err(): - it.done = true - it.fail = err - return it.Next() - } -} - -// Error returns any retrieval or parsing error occurred during filtering. -func (it *L1StakingGasLimitAddStakerUpdatedIterator) Error() error { - return it.fail -} - -// Close terminates the iteration process, releasing any pending underlying -// resources. -func (it *L1StakingGasLimitAddStakerUpdatedIterator) Close() error { - it.sub.Unsubscribe() - return nil -} - -// L1StakingGasLimitAddStakerUpdated represents a GasLimitAddStakerUpdated event raised by the L1Staking contract. -type L1StakingGasLimitAddStakerUpdated struct { - OldGasLimit *big.Int - NewGasLimit *big.Int - Raw types.Log // Blockchain specific contextual infos -} - -// FilterGasLimitAddStakerUpdated is a free log retrieval operation binding the contract event 0x0ef80fb82bf5439b8591744c0fde771f5d93dce7a7970b1e9b7828cdc3970e9c. -// -// Solidity: event GasLimitAddStakerUpdated(uint256 oldGasLimit, uint256 newGasLimit) -func (_L1Staking *L1StakingFilterer) FilterGasLimitAddStakerUpdated(opts *bind.FilterOpts) (*L1StakingGasLimitAddStakerUpdatedIterator, error) { - - logs, sub, err := _L1Staking.contract.FilterLogs(opts, "GasLimitAddStakerUpdated") - if err != nil { - return nil, err - } - return &L1StakingGasLimitAddStakerUpdatedIterator{contract: _L1Staking.contract, event: "GasLimitAddStakerUpdated", logs: logs, sub: sub}, nil -} - -// WatchGasLimitAddStakerUpdated is a free log subscription operation binding the contract event 0x0ef80fb82bf5439b8591744c0fde771f5d93dce7a7970b1e9b7828cdc3970e9c. -// -// Solidity: event GasLimitAddStakerUpdated(uint256 oldGasLimit, uint256 newGasLimit) -func (_L1Staking *L1StakingFilterer) WatchGasLimitAddStakerUpdated(opts *bind.WatchOpts, sink chan<- *L1StakingGasLimitAddStakerUpdated) (event.Subscription, error) { - - logs, sub, err := _L1Staking.contract.WatchLogs(opts, "GasLimitAddStakerUpdated") - if err != nil { - return nil, err - } - return event.NewSubscription(func(quit <-chan struct{}) error { - defer sub.Unsubscribe() - for { - select { - case log := <-logs: - // New log arrived, parse the event and forward to the user - event := new(L1StakingGasLimitAddStakerUpdated) - if err := _L1Staking.contract.UnpackLog(event, "GasLimitAddStakerUpdated", log); err != nil { - return err - } - event.Raw = log - - select { - case sink <- event: - case err := <-sub.Err(): - return err - case <-quit: - return nil - } - case err := <-sub.Err(): - return err - case <-quit: - return nil - } - } - }), nil -} - -// ParseGasLimitAddStakerUpdated is a log parse operation binding the contract event 0x0ef80fb82bf5439b8591744c0fde771f5d93dce7a7970b1e9b7828cdc3970e9c. -// -// Solidity: event GasLimitAddStakerUpdated(uint256 oldGasLimit, uint256 newGasLimit) -func (_L1Staking *L1StakingFilterer) ParseGasLimitAddStakerUpdated(log types.Log) (*L1StakingGasLimitAddStakerUpdated, error) { - event := new(L1StakingGasLimitAddStakerUpdated) - if err := _L1Staking.contract.UnpackLog(event, "GasLimitAddStakerUpdated", log); err != nil { - return nil, err - } - event.Raw = log - return event, nil -} - -// L1StakingGasLimitRemoveStakersUpdatedIterator is returned from FilterGasLimitRemoveStakersUpdated and is used to iterate over the raw logs and unpacked data for GasLimitRemoveStakersUpdated events raised by the L1Staking contract. -type L1StakingGasLimitRemoveStakersUpdatedIterator struct { - Event *L1StakingGasLimitRemoveStakersUpdated // Event containing the contract specifics and raw log - - contract *bind.BoundContract // Generic contract to use for unpacking event data - event string // Event name to use for unpacking event data - - logs chan types.Log // Log channel receiving the found contract events - sub ethereum.Subscription // Subscription for errors, completion and termination - done bool // Whether the subscription completed delivering logs - fail error // Occurred error to stop iteration -} - -// Next advances the iterator to the subsequent event, returning whether there -// are any more events found. In case of a retrieval or parsing error, false is -// returned and Error() can be queried for the exact failure. -func (it *L1StakingGasLimitRemoveStakersUpdatedIterator) Next() bool { - // If the iterator failed, stop iterating - if it.fail != nil { - return false - } - // If the iterator completed, deliver directly whatever's available - if it.done { - select { - case log := <-it.logs: - it.Event = new(L1StakingGasLimitRemoveStakersUpdated) - if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { - it.fail = err - return false - } - it.Event.Raw = log - return true - - default: - return false - } - } - // Iterator still in progress, wait for either a data or an error event - select { - case log := <-it.logs: - it.Event = new(L1StakingGasLimitRemoveStakersUpdated) - if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { - it.fail = err - return false - } - it.Event.Raw = log - return true - - case err := <-it.sub.Err(): - it.done = true - it.fail = err - return it.Next() - } -} - -// Error returns any retrieval or parsing error occurred during filtering. -func (it *L1StakingGasLimitRemoveStakersUpdatedIterator) Error() error { - return it.fail -} - -// Close terminates the iteration process, releasing any pending underlying -// resources. -func (it *L1StakingGasLimitRemoveStakersUpdatedIterator) Close() error { - it.sub.Unsubscribe() - return nil -} - -// L1StakingGasLimitRemoveStakersUpdated represents a GasLimitRemoveStakersUpdated event raised by the L1Staking contract. -type L1StakingGasLimitRemoveStakersUpdated struct { - OldGasLimit *big.Int - NewGasLimit *big.Int - Raw types.Log // Blockchain specific contextual infos -} - -// FilterGasLimitRemoveStakersUpdated is a free log retrieval operation binding the contract event 0xdd4b37d1f14888147fe4be2cfaedcbf148fd07ececc856d0433241b8b6e4a7f7. -// -// Solidity: event GasLimitRemoveStakersUpdated(uint256 oldGasLimit, uint256 newGasLimit) -func (_L1Staking *L1StakingFilterer) FilterGasLimitRemoveStakersUpdated(opts *bind.FilterOpts) (*L1StakingGasLimitRemoveStakersUpdatedIterator, error) { - - logs, sub, err := _L1Staking.contract.FilterLogs(opts, "GasLimitRemoveStakersUpdated") - if err != nil { - return nil, err - } - return &L1StakingGasLimitRemoveStakersUpdatedIterator{contract: _L1Staking.contract, event: "GasLimitRemoveStakersUpdated", logs: logs, sub: sub}, nil -} - -// WatchGasLimitRemoveStakersUpdated is a free log subscription operation binding the contract event 0xdd4b37d1f14888147fe4be2cfaedcbf148fd07ececc856d0433241b8b6e4a7f7. -// -// Solidity: event GasLimitRemoveStakersUpdated(uint256 oldGasLimit, uint256 newGasLimit) -func (_L1Staking *L1StakingFilterer) WatchGasLimitRemoveStakersUpdated(opts *bind.WatchOpts, sink chan<- *L1StakingGasLimitRemoveStakersUpdated) (event.Subscription, error) { - - logs, sub, err := _L1Staking.contract.WatchLogs(opts, "GasLimitRemoveStakersUpdated") - if err != nil { - return nil, err - } - return event.NewSubscription(func(quit <-chan struct{}) error { - defer sub.Unsubscribe() - for { - select { - case log := <-logs: - // New log arrived, parse the event and forward to the user - event := new(L1StakingGasLimitRemoveStakersUpdated) - if err := _L1Staking.contract.UnpackLog(event, "GasLimitRemoveStakersUpdated", log); err != nil { - return err - } - event.Raw = log - - select { - case sink <- event: - case err := <-sub.Err(): - return err - case <-quit: - return nil - } - case err := <-sub.Err(): - return err - case <-quit: - return nil - } - } - }), nil -} - -// ParseGasLimitRemoveStakersUpdated is a log parse operation binding the contract event 0xdd4b37d1f14888147fe4be2cfaedcbf148fd07ececc856d0433241b8b6e4a7f7. -// -// Solidity: event GasLimitRemoveStakersUpdated(uint256 oldGasLimit, uint256 newGasLimit) -func (_L1Staking *L1StakingFilterer) ParseGasLimitRemoveStakersUpdated(log types.Log) (*L1StakingGasLimitRemoveStakersUpdated, error) { - event := new(L1StakingGasLimitRemoveStakersUpdated) - if err := _L1Staking.contract.UnpackLog(event, "GasLimitRemoveStakersUpdated", log); err != nil { - return nil, err - } - event.Raw = log - return event, nil -} - -// L1StakingInitializedIterator is returned from FilterInitialized and is used to iterate over the raw logs and unpacked data for Initialized events raised by the L1Staking contract. -type L1StakingInitializedIterator struct { - Event *L1StakingInitialized // Event containing the contract specifics and raw log - - contract *bind.BoundContract // Generic contract to use for unpacking event data - event string // Event name to use for unpacking event data - - logs chan types.Log // Log channel receiving the found contract events - sub ethereum.Subscription // Subscription for errors, completion and termination - done bool // Whether the subscription completed delivering logs - fail error // Occurred error to stop iteration -} - -// Next advances the iterator to the subsequent event, returning whether there -// are any more events found. In case of a retrieval or parsing error, false is -// returned and Error() can be queried for the exact failure. -func (it *L1StakingInitializedIterator) Next() bool { - // If the iterator failed, stop iterating - if it.fail != nil { - return false - } - // If the iterator completed, deliver directly whatever's available - if it.done { - select { - case log := <-it.logs: - it.Event = new(L1StakingInitialized) - if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { - it.fail = err - return false - } - it.Event.Raw = log - return true - - default: - return false - } - } - // Iterator still in progress, wait for either a data or an error event - select { - case log := <-it.logs: - it.Event = new(L1StakingInitialized) - if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { - it.fail = err - return false - } - it.Event.Raw = log - return true - - case err := <-it.sub.Err(): - it.done = true - it.fail = err - return it.Next() - } -} - -// Error returns any retrieval or parsing error occurred during filtering. -func (it *L1StakingInitializedIterator) Error() error { - return it.fail -} - -// Close terminates the iteration process, releasing any pending underlying -// resources. -func (it *L1StakingInitializedIterator) Close() error { - it.sub.Unsubscribe() - return nil -} - -// L1StakingInitialized represents a Initialized event raised by the L1Staking contract. -type L1StakingInitialized struct { - Version uint8 - Raw types.Log // Blockchain specific contextual infos -} - -// FilterInitialized is a free log retrieval operation binding the contract event 0x7f26b83ff96e1f2b6a682f133852f6798a09c465da95921460cefb3847402498. -// -// Solidity: event Initialized(uint8 version) -func (_L1Staking *L1StakingFilterer) FilterInitialized(opts *bind.FilterOpts) (*L1StakingInitializedIterator, error) { - - logs, sub, err := _L1Staking.contract.FilterLogs(opts, "Initialized") - if err != nil { - return nil, err - } - return &L1StakingInitializedIterator{contract: _L1Staking.contract, event: "Initialized", logs: logs, sub: sub}, nil -} - -// WatchInitialized is a free log subscription operation binding the contract event 0x7f26b83ff96e1f2b6a682f133852f6798a09c465da95921460cefb3847402498. -// -// Solidity: event Initialized(uint8 version) -func (_L1Staking *L1StakingFilterer) WatchInitialized(opts *bind.WatchOpts, sink chan<- *L1StakingInitialized) (event.Subscription, error) { - - logs, sub, err := _L1Staking.contract.WatchLogs(opts, "Initialized") - if err != nil { - return nil, err - } - return event.NewSubscription(func(quit <-chan struct{}) error { - defer sub.Unsubscribe() - for { - select { - case log := <-logs: - // New log arrived, parse the event and forward to the user - event := new(L1StakingInitialized) - if err := _L1Staking.contract.UnpackLog(event, "Initialized", log); err != nil { - return err - } - event.Raw = log - - select { - case sink <- event: - case err := <-sub.Err(): - return err - case <-quit: - return nil - } - case err := <-sub.Err(): - return err - case <-quit: - return nil - } - } - }), nil -} - -// ParseInitialized is a log parse operation binding the contract event 0x7f26b83ff96e1f2b6a682f133852f6798a09c465da95921460cefb3847402498. -// -// Solidity: event Initialized(uint8 version) -func (_L1Staking *L1StakingFilterer) ParseInitialized(log types.Log) (*L1StakingInitialized, error) { - event := new(L1StakingInitialized) - if err := _L1Staking.contract.UnpackLog(event, "Initialized", log); err != nil { - return nil, err - } - event.Raw = log - return event, nil -} - -// L1StakingOwnershipTransferredIterator is returned from FilterOwnershipTransferred and is used to iterate over the raw logs and unpacked data for OwnershipTransferred events raised by the L1Staking contract. -type L1StakingOwnershipTransferredIterator struct { - Event *L1StakingOwnershipTransferred // Event containing the contract specifics and raw log - - contract *bind.BoundContract // Generic contract to use for unpacking event data - event string // Event name to use for unpacking event data - - logs chan types.Log // Log channel receiving the found contract events - sub ethereum.Subscription // Subscription for errors, completion and termination - done bool // Whether the subscription completed delivering logs - fail error // Occurred error to stop iteration -} - -// Next advances the iterator to the subsequent event, returning whether there -// are any more events found. In case of a retrieval or parsing error, false is -// returned and Error() can be queried for the exact failure. -func (it *L1StakingOwnershipTransferredIterator) Next() bool { - // If the iterator failed, stop iterating - if it.fail != nil { - return false - } - // If the iterator completed, deliver directly whatever's available - if it.done { - select { - case log := <-it.logs: - it.Event = new(L1StakingOwnershipTransferred) - if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { - it.fail = err - return false - } - it.Event.Raw = log - return true - - default: - return false - } - } - // Iterator still in progress, wait for either a data or an error event - select { - case log := <-it.logs: - it.Event = new(L1StakingOwnershipTransferred) - if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { - it.fail = err - return false - } - it.Event.Raw = log - return true - - case err := <-it.sub.Err(): - it.done = true - it.fail = err - return it.Next() - } -} - -// Error returns any retrieval or parsing error occurred during filtering. -func (it *L1StakingOwnershipTransferredIterator) Error() error { - return it.fail -} - -// Close terminates the iteration process, releasing any pending underlying -// resources. -func (it *L1StakingOwnershipTransferredIterator) Close() error { - it.sub.Unsubscribe() - return nil -} - -// L1StakingOwnershipTransferred represents a OwnershipTransferred event raised by the L1Staking contract. -type L1StakingOwnershipTransferred struct { - PreviousOwner common.Address - NewOwner common.Address - Raw types.Log // Blockchain specific contextual infos -} - -// FilterOwnershipTransferred is a free log retrieval operation binding the contract event 0x8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0. -// -// Solidity: event OwnershipTransferred(address indexed previousOwner, address indexed newOwner) -func (_L1Staking *L1StakingFilterer) FilterOwnershipTransferred(opts *bind.FilterOpts, previousOwner []common.Address, newOwner []common.Address) (*L1StakingOwnershipTransferredIterator, error) { - - var previousOwnerRule []interface{} - for _, previousOwnerItem := range previousOwner { - previousOwnerRule = append(previousOwnerRule, previousOwnerItem) - } - var newOwnerRule []interface{} - for _, newOwnerItem := range newOwner { - newOwnerRule = append(newOwnerRule, newOwnerItem) - } - - logs, sub, err := _L1Staking.contract.FilterLogs(opts, "OwnershipTransferred", previousOwnerRule, newOwnerRule) - if err != nil { - return nil, err - } - return &L1StakingOwnershipTransferredIterator{contract: _L1Staking.contract, event: "OwnershipTransferred", logs: logs, sub: sub}, nil -} - -// WatchOwnershipTransferred is a free log subscription operation binding the contract event 0x8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0. -// -// Solidity: event OwnershipTransferred(address indexed previousOwner, address indexed newOwner) -func (_L1Staking *L1StakingFilterer) WatchOwnershipTransferred(opts *bind.WatchOpts, sink chan<- *L1StakingOwnershipTransferred, previousOwner []common.Address, newOwner []common.Address) (event.Subscription, error) { - - var previousOwnerRule []interface{} - for _, previousOwnerItem := range previousOwner { - previousOwnerRule = append(previousOwnerRule, previousOwnerItem) - } - var newOwnerRule []interface{} - for _, newOwnerItem := range newOwner { - newOwnerRule = append(newOwnerRule, newOwnerItem) - } - - logs, sub, err := _L1Staking.contract.WatchLogs(opts, "OwnershipTransferred", previousOwnerRule, newOwnerRule) - if err != nil { - return nil, err - } - return event.NewSubscription(func(quit <-chan struct{}) error { - defer sub.Unsubscribe() - for { - select { - case log := <-logs: - // New log arrived, parse the event and forward to the user - event := new(L1StakingOwnershipTransferred) - if err := _L1Staking.contract.UnpackLog(event, "OwnershipTransferred", log); err != nil { - return err - } - event.Raw = log - - select { - case sink <- event: - case err := <-sub.Err(): - return err - case <-quit: - return nil - } - case err := <-sub.Err(): - return err - case <-quit: - return nil - } - } - }), nil -} - -// ParseOwnershipTransferred is a log parse operation binding the contract event 0x8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0. -// -// Solidity: event OwnershipTransferred(address indexed previousOwner, address indexed newOwner) -func (_L1Staking *L1StakingFilterer) ParseOwnershipTransferred(log types.Log) (*L1StakingOwnershipTransferred, error) { - event := new(L1StakingOwnershipTransferred) - if err := _L1Staking.contract.UnpackLog(event, "OwnershipTransferred", log); err != nil { - return nil, err - } - event.Raw = log - return event, nil -} - -// L1StakingRegisteredIterator is returned from FilterRegistered and is used to iterate over the raw logs and unpacked data for Registered events raised by the L1Staking contract. -type L1StakingRegisteredIterator struct { - Event *L1StakingRegistered // Event containing the contract specifics and raw log - - contract *bind.BoundContract // Generic contract to use for unpacking event data - event string // Event name to use for unpacking event data - - logs chan types.Log // Log channel receiving the found contract events - sub ethereum.Subscription // Subscription for errors, completion and termination - done bool // Whether the subscription completed delivering logs - fail error // Occurred error to stop iteration -} - -// Next advances the iterator to the subsequent event, returning whether there -// are any more events found. In case of a retrieval or parsing error, false is -// returned and Error() can be queried for the exact failure. -func (it *L1StakingRegisteredIterator) Next() bool { - // If the iterator failed, stop iterating - if it.fail != nil { - return false - } - // If the iterator completed, deliver directly whatever's available - if it.done { - select { - case log := <-it.logs: - it.Event = new(L1StakingRegistered) - if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { - it.fail = err - return false - } - it.Event.Raw = log - return true - - default: - return false - } - } - // Iterator still in progress, wait for either a data or an error event - select { - case log := <-it.logs: - it.Event = new(L1StakingRegistered) - if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { - it.fail = err - return false - } - it.Event.Raw = log - return true - - case err := <-it.sub.Err(): - it.done = true - it.fail = err - return it.Next() - } -} - -// Error returns any retrieval or parsing error occurred during filtering. -func (it *L1StakingRegisteredIterator) Error() error { - return it.fail -} - -// Close terminates the iteration process, releasing any pending underlying -// resources. -func (it *L1StakingRegisteredIterator) Close() error { - it.sub.Unsubscribe() - return nil -} - -// L1StakingRegistered represents a Registered event raised by the L1Staking contract. -type L1StakingRegistered struct { - Addr common.Address - TmKey [32]byte - BlsKey []byte - Raw types.Log // Blockchain specific contextual infos -} - -// FilterRegistered is a free log retrieval operation binding the contract event 0xb9c7babb56df9f2da4a30811a6c778e4e68af88b72712d56cf62c5516e20e199. -// -// Solidity: event Registered(address addr, bytes32 tmKey, bytes blsKey) -func (_L1Staking *L1StakingFilterer) FilterRegistered(opts *bind.FilterOpts) (*L1StakingRegisteredIterator, error) { - - logs, sub, err := _L1Staking.contract.FilterLogs(opts, "Registered") - if err != nil { - return nil, err - } - return &L1StakingRegisteredIterator{contract: _L1Staking.contract, event: "Registered", logs: logs, sub: sub}, nil -} - -// WatchRegistered is a free log subscription operation binding the contract event 0xb9c7babb56df9f2da4a30811a6c778e4e68af88b72712d56cf62c5516e20e199. -// -// Solidity: event Registered(address addr, bytes32 tmKey, bytes blsKey) -func (_L1Staking *L1StakingFilterer) WatchRegistered(opts *bind.WatchOpts, sink chan<- *L1StakingRegistered) (event.Subscription, error) { - - logs, sub, err := _L1Staking.contract.WatchLogs(opts, "Registered") - if err != nil { - return nil, err - } - return event.NewSubscription(func(quit <-chan struct{}) error { - defer sub.Unsubscribe() - for { - select { - case log := <-logs: - // New log arrived, parse the event and forward to the user - event := new(L1StakingRegistered) - if err := _L1Staking.contract.UnpackLog(event, "Registered", log); err != nil { - return err - } - event.Raw = log - - select { - case sink <- event: - case err := <-sub.Err(): - return err - case <-quit: - return nil - } - case err := <-sub.Err(): - return err - case <-quit: - return nil - } - } - }), nil -} - -// ParseRegistered is a log parse operation binding the contract event 0xb9c7babb56df9f2da4a30811a6c778e4e68af88b72712d56cf62c5516e20e199. -// -// Solidity: event Registered(address addr, bytes32 tmKey, bytes blsKey) -func (_L1Staking *L1StakingFilterer) ParseRegistered(log types.Log) (*L1StakingRegistered, error) { - event := new(L1StakingRegistered) - if err := _L1Staking.contract.UnpackLog(event, "Registered", log); err != nil { - return nil, err - } - event.Raw = log - return event, nil -} - -// L1StakingRewardPercentageUpdatedIterator is returned from FilterRewardPercentageUpdated and is used to iterate over the raw logs and unpacked data for RewardPercentageUpdated events raised by the L1Staking contract. -type L1StakingRewardPercentageUpdatedIterator struct { - Event *L1StakingRewardPercentageUpdated // Event containing the contract specifics and raw log - - contract *bind.BoundContract // Generic contract to use for unpacking event data - event string // Event name to use for unpacking event data - - logs chan types.Log // Log channel receiving the found contract events - sub ethereum.Subscription // Subscription for errors, completion and termination - done bool // Whether the subscription completed delivering logs - fail error // Occurred error to stop iteration -} - -// Next advances the iterator to the subsequent event, returning whether there -// are any more events found. In case of a retrieval or parsing error, false is -// returned and Error() can be queried for the exact failure. -func (it *L1StakingRewardPercentageUpdatedIterator) Next() bool { - // If the iterator failed, stop iterating - if it.fail != nil { - return false - } - // If the iterator completed, deliver directly whatever's available - if it.done { - select { - case log := <-it.logs: - it.Event = new(L1StakingRewardPercentageUpdated) - if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { - it.fail = err - return false - } - it.Event.Raw = log - return true - - default: - return false - } - } - // Iterator still in progress, wait for either a data or an error event - select { - case log := <-it.logs: - it.Event = new(L1StakingRewardPercentageUpdated) - if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { - it.fail = err - return false - } - it.Event.Raw = log - return true - - case err := <-it.sub.Err(): - it.done = true - it.fail = err - return it.Next() - } -} - -// Error returns any retrieval or parsing error occurred during filtering. -func (it *L1StakingRewardPercentageUpdatedIterator) Error() error { - return it.fail -} - -// Close terminates the iteration process, releasing any pending underlying -// resources. -func (it *L1StakingRewardPercentageUpdatedIterator) Close() error { - it.sub.Unsubscribe() - return nil -} - -// L1StakingRewardPercentageUpdated represents a RewardPercentageUpdated event raised by the L1Staking contract. -type L1StakingRewardPercentageUpdated struct { - OldPercentage *big.Int - NewPercentage *big.Int - Raw types.Log // Blockchain specific contextual infos -} - -// FilterRewardPercentageUpdated is a free log retrieval operation binding the contract event 0xa46de936426e045703b2d34a292a19fde92b329018db8e0da750033876b655ba. -// -// Solidity: event RewardPercentageUpdated(uint256 oldPercentage, uint256 newPercentage) -func (_L1Staking *L1StakingFilterer) FilterRewardPercentageUpdated(opts *bind.FilterOpts) (*L1StakingRewardPercentageUpdatedIterator, error) { - - logs, sub, err := _L1Staking.contract.FilterLogs(opts, "RewardPercentageUpdated") - if err != nil { - return nil, err - } - return &L1StakingRewardPercentageUpdatedIterator{contract: _L1Staking.contract, event: "RewardPercentageUpdated", logs: logs, sub: sub}, nil -} - -// WatchRewardPercentageUpdated is a free log subscription operation binding the contract event 0xa46de936426e045703b2d34a292a19fde92b329018db8e0da750033876b655ba. -// -// Solidity: event RewardPercentageUpdated(uint256 oldPercentage, uint256 newPercentage) -func (_L1Staking *L1StakingFilterer) WatchRewardPercentageUpdated(opts *bind.WatchOpts, sink chan<- *L1StakingRewardPercentageUpdated) (event.Subscription, error) { - - logs, sub, err := _L1Staking.contract.WatchLogs(opts, "RewardPercentageUpdated") - if err != nil { - return nil, err - } - return event.NewSubscription(func(quit <-chan struct{}) error { - defer sub.Unsubscribe() - for { - select { - case log := <-logs: - // New log arrived, parse the event and forward to the user - event := new(L1StakingRewardPercentageUpdated) - if err := _L1Staking.contract.UnpackLog(event, "RewardPercentageUpdated", log); err != nil { - return err - } - event.Raw = log - - select { - case sink <- event: - case err := <-sub.Err(): - return err - case <-quit: - return nil - } - case err := <-sub.Err(): - return err - case <-quit: - return nil - } - } - }), nil -} - -// ParseRewardPercentageUpdated is a log parse operation binding the contract event 0xa46de936426e045703b2d34a292a19fde92b329018db8e0da750033876b655ba. -// -// Solidity: event RewardPercentageUpdated(uint256 oldPercentage, uint256 newPercentage) -func (_L1Staking *L1StakingFilterer) ParseRewardPercentageUpdated(log types.Log) (*L1StakingRewardPercentageUpdated, error) { - event := new(L1StakingRewardPercentageUpdated) - if err := _L1Staking.contract.UnpackLog(event, "RewardPercentageUpdated", log); err != nil { - return nil, err - } - event.Raw = log - return event, nil -} - -// L1StakingSlashRemainingClaimedIterator is returned from FilterSlashRemainingClaimed and is used to iterate over the raw logs and unpacked data for SlashRemainingClaimed events raised by the L1Staking contract. -type L1StakingSlashRemainingClaimedIterator struct { - Event *L1StakingSlashRemainingClaimed // Event containing the contract specifics and raw log - - contract *bind.BoundContract // Generic contract to use for unpacking event data - event string // Event name to use for unpacking event data - - logs chan types.Log // Log channel receiving the found contract events - sub ethereum.Subscription // Subscription for errors, completion and termination - done bool // Whether the subscription completed delivering logs - fail error // Occurred error to stop iteration -} - -// Next advances the iterator to the subsequent event, returning whether there -// are any more events found. In case of a retrieval or parsing error, false is -// returned and Error() can be queried for the exact failure. -func (it *L1StakingSlashRemainingClaimedIterator) Next() bool { - // If the iterator failed, stop iterating - if it.fail != nil { - return false - } - // If the iterator completed, deliver directly whatever's available - if it.done { - select { - case log := <-it.logs: - it.Event = new(L1StakingSlashRemainingClaimed) - if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { - it.fail = err - return false - } - it.Event.Raw = log - return true - - default: - return false - } - } - // Iterator still in progress, wait for either a data or an error event - select { - case log := <-it.logs: - it.Event = new(L1StakingSlashRemainingClaimed) - if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { - it.fail = err - return false - } - it.Event.Raw = log - return true - - case err := <-it.sub.Err(): - it.done = true - it.fail = err - return it.Next() - } -} - -// Error returns any retrieval or parsing error occurred during filtering. -func (it *L1StakingSlashRemainingClaimedIterator) Error() error { - return it.fail -} - -// Close terminates the iteration process, releasing any pending underlying -// resources. -func (it *L1StakingSlashRemainingClaimedIterator) Close() error { - it.sub.Unsubscribe() - return nil -} - -// L1StakingSlashRemainingClaimed represents a SlashRemainingClaimed event raised by the L1Staking contract. -type L1StakingSlashRemainingClaimed struct { - Receiver common.Address - Amount *big.Int - Raw types.Log // Blockchain specific contextual infos -} - -// FilterSlashRemainingClaimed is a free log retrieval operation binding the contract event 0xa1fefb6c5328a92a416e321ed50997303fe7135fd88c28b0592b21ce42b5cdd9. -// -// Solidity: event SlashRemainingClaimed(address receiver, uint256 amount) -func (_L1Staking *L1StakingFilterer) FilterSlashRemainingClaimed(opts *bind.FilterOpts) (*L1StakingSlashRemainingClaimedIterator, error) { - - logs, sub, err := _L1Staking.contract.FilterLogs(opts, "SlashRemainingClaimed") - if err != nil { - return nil, err - } - return &L1StakingSlashRemainingClaimedIterator{contract: _L1Staking.contract, event: "SlashRemainingClaimed", logs: logs, sub: sub}, nil -} - -// WatchSlashRemainingClaimed is a free log subscription operation binding the contract event 0xa1fefb6c5328a92a416e321ed50997303fe7135fd88c28b0592b21ce42b5cdd9. -// -// Solidity: event SlashRemainingClaimed(address receiver, uint256 amount) -func (_L1Staking *L1StakingFilterer) WatchSlashRemainingClaimed(opts *bind.WatchOpts, sink chan<- *L1StakingSlashRemainingClaimed) (event.Subscription, error) { - - logs, sub, err := _L1Staking.contract.WatchLogs(opts, "SlashRemainingClaimed") - if err != nil { - return nil, err - } - return event.NewSubscription(func(quit <-chan struct{}) error { - defer sub.Unsubscribe() - for { - select { - case log := <-logs: - // New log arrived, parse the event and forward to the user - event := new(L1StakingSlashRemainingClaimed) - if err := _L1Staking.contract.UnpackLog(event, "SlashRemainingClaimed", log); err != nil { - return err - } - event.Raw = log - - select { - case sink <- event: - case err := <-sub.Err(): - return err - case <-quit: - return nil - } - case err := <-sub.Err(): - return err - case <-quit: - return nil - } - } - }), nil -} - -// ParseSlashRemainingClaimed is a log parse operation binding the contract event 0xa1fefb6c5328a92a416e321ed50997303fe7135fd88c28b0592b21ce42b5cdd9. -// -// Solidity: event SlashRemainingClaimed(address receiver, uint256 amount) -func (_L1Staking *L1StakingFilterer) ParseSlashRemainingClaimed(log types.Log) (*L1StakingSlashRemainingClaimed, error) { - event := new(L1StakingSlashRemainingClaimed) - if err := _L1Staking.contract.UnpackLog(event, "SlashRemainingClaimed", log); err != nil { - return nil, err - } - event.Raw = log - return event, nil -} - -// L1StakingSlashedIterator is returned from FilterSlashed and is used to iterate over the raw logs and unpacked data for Slashed events raised by the L1Staking contract. -type L1StakingSlashedIterator struct { - Event *L1StakingSlashed // Event containing the contract specifics and raw log - - contract *bind.BoundContract // Generic contract to use for unpacking event data - event string // Event name to use for unpacking event data - - logs chan types.Log // Log channel receiving the found contract events - sub ethereum.Subscription // Subscription for errors, completion and termination - done bool // Whether the subscription completed delivering logs - fail error // Occurred error to stop iteration -} - -// Next advances the iterator to the subsequent event, returning whether there -// are any more events found. In case of a retrieval or parsing error, false is -// returned and Error() can be queried for the exact failure. -func (it *L1StakingSlashedIterator) Next() bool { - // If the iterator failed, stop iterating - if it.fail != nil { - return false - } - // If the iterator completed, deliver directly whatever's available - if it.done { - select { - case log := <-it.logs: - it.Event = new(L1StakingSlashed) - if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { - it.fail = err - return false - } - it.Event.Raw = log - return true - - default: - return false - } - } - // Iterator still in progress, wait for either a data or an error event - select { - case log := <-it.logs: - it.Event = new(L1StakingSlashed) - if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { - it.fail = err - return false - } - it.Event.Raw = log - return true - - case err := <-it.sub.Err(): - it.done = true - it.fail = err - return it.Next() - } -} - -// Error returns any retrieval or parsing error occurred during filtering. -func (it *L1StakingSlashedIterator) Error() error { - return it.fail -} - -// Close terminates the iteration process, releasing any pending underlying -// resources. -func (it *L1StakingSlashedIterator) Close() error { - it.sub.Unsubscribe() - return nil -} - -// L1StakingSlashed represents a Slashed event raised by the L1Staking contract. -type L1StakingSlashed struct { - Stakers []common.Address - Raw types.Log // Blockchain specific contextual infos -} - -// FilterSlashed is a free log retrieval operation binding the contract event 0x654f4a61849f1b3ad10abb283d27f02d5fece7b820acc5a3b874713b58748b5a. -// -// Solidity: event Slashed(address[] stakers) -func (_L1Staking *L1StakingFilterer) FilterSlashed(opts *bind.FilterOpts) (*L1StakingSlashedIterator, error) { - - logs, sub, err := _L1Staking.contract.FilterLogs(opts, "Slashed") - if err != nil { - return nil, err - } - return &L1StakingSlashedIterator{contract: _L1Staking.contract, event: "Slashed", logs: logs, sub: sub}, nil -} - -// WatchSlashed is a free log subscription operation binding the contract event 0x654f4a61849f1b3ad10abb283d27f02d5fece7b820acc5a3b874713b58748b5a. -// -// Solidity: event Slashed(address[] stakers) -func (_L1Staking *L1StakingFilterer) WatchSlashed(opts *bind.WatchOpts, sink chan<- *L1StakingSlashed) (event.Subscription, error) { - - logs, sub, err := _L1Staking.contract.WatchLogs(opts, "Slashed") - if err != nil { - return nil, err - } - return event.NewSubscription(func(quit <-chan struct{}) error { - defer sub.Unsubscribe() - for { - select { - case log := <-logs: - // New log arrived, parse the event and forward to the user - event := new(L1StakingSlashed) - if err := _L1Staking.contract.UnpackLog(event, "Slashed", log); err != nil { - return err - } - event.Raw = log - - select { - case sink <- event: - case err := <-sub.Err(): - return err - case <-quit: - return nil - } - case err := <-sub.Err(): - return err - case <-quit: - return nil - } - } - }), nil -} - -// ParseSlashed is a log parse operation binding the contract event 0x654f4a61849f1b3ad10abb283d27f02d5fece7b820acc5a3b874713b58748b5a. -// -// Solidity: event Slashed(address[] stakers) -func (_L1Staking *L1StakingFilterer) ParseSlashed(log types.Log) (*L1StakingSlashed, error) { - event := new(L1StakingSlashed) - if err := _L1Staking.contract.UnpackLog(event, "Slashed", log); err != nil { - return nil, err - } - event.Raw = log - return event, nil -} - -// L1StakingStakersRemovedIterator is returned from FilterStakersRemoved and is used to iterate over the raw logs and unpacked data for StakersRemoved events raised by the L1Staking contract. -type L1StakingStakersRemovedIterator struct { - Event *L1StakingStakersRemoved // Event containing the contract specifics and raw log - - contract *bind.BoundContract // Generic contract to use for unpacking event data - event string // Event name to use for unpacking event data - - logs chan types.Log // Log channel receiving the found contract events - sub ethereum.Subscription // Subscription for errors, completion and termination - done bool // Whether the subscription completed delivering logs - fail error // Occurred error to stop iteration -} - -// Next advances the iterator to the subsequent event, returning whether there -// are any more events found. In case of a retrieval or parsing error, false is -// returned and Error() can be queried for the exact failure. -func (it *L1StakingStakersRemovedIterator) Next() bool { - // If the iterator failed, stop iterating - if it.fail != nil { - return false - } - // If the iterator completed, deliver directly whatever's available - if it.done { - select { - case log := <-it.logs: - it.Event = new(L1StakingStakersRemoved) - if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { - it.fail = err - return false - } - it.Event.Raw = log - return true - - default: - return false - } - } - // Iterator still in progress, wait for either a data or an error event - select { - case log := <-it.logs: - it.Event = new(L1StakingStakersRemoved) - if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { - it.fail = err - return false - } - it.Event.Raw = log - return true - - case err := <-it.sub.Err(): - it.done = true - it.fail = err - return it.Next() - } -} - -// Error returns any retrieval or parsing error occurred during filtering. -func (it *L1StakingStakersRemovedIterator) Error() error { - return it.fail -} - -// Close terminates the iteration process, releasing any pending underlying -// resources. -func (it *L1StakingStakersRemovedIterator) Close() error { - it.sub.Unsubscribe() - return nil -} - -// L1StakingStakersRemoved represents a StakersRemoved event raised by the L1Staking contract. -type L1StakingStakersRemoved struct { - Stakers []common.Address - Raw types.Log // Blockchain specific contextual infos -} - -// FilterStakersRemoved is a free log retrieval operation binding the contract event 0x3f446646c03e618be8238a586960d6d625f35c653cdca1ef59609993e8ed2c84. -// -// Solidity: event StakersRemoved(address[] stakers) -func (_L1Staking *L1StakingFilterer) FilterStakersRemoved(opts *bind.FilterOpts) (*L1StakingStakersRemovedIterator, error) { - - logs, sub, err := _L1Staking.contract.FilterLogs(opts, "StakersRemoved") - if err != nil { - return nil, err - } - return &L1StakingStakersRemovedIterator{contract: _L1Staking.contract, event: "StakersRemoved", logs: logs, sub: sub}, nil -} - -// WatchStakersRemoved is a free log subscription operation binding the contract event 0x3f446646c03e618be8238a586960d6d625f35c653cdca1ef59609993e8ed2c84. -// -// Solidity: event StakersRemoved(address[] stakers) -func (_L1Staking *L1StakingFilterer) WatchStakersRemoved(opts *bind.WatchOpts, sink chan<- *L1StakingStakersRemoved) (event.Subscription, error) { - - logs, sub, err := _L1Staking.contract.WatchLogs(opts, "StakersRemoved") - if err != nil { - return nil, err - } - return event.NewSubscription(func(quit <-chan struct{}) error { - defer sub.Unsubscribe() - for { - select { - case log := <-logs: - // New log arrived, parse the event and forward to the user - event := new(L1StakingStakersRemoved) - if err := _L1Staking.contract.UnpackLog(event, "StakersRemoved", log); err != nil { - return err - } - event.Raw = log - - select { - case sink <- event: - case err := <-sub.Err(): - return err - case <-quit: - return nil - } - case err := <-sub.Err(): - return err - case <-quit: - return nil - } - } - }), nil -} - -// ParseStakersRemoved is a log parse operation binding the contract event 0x3f446646c03e618be8238a586960d6d625f35c653cdca1ef59609993e8ed2c84. -// -// Solidity: event StakersRemoved(address[] stakers) -func (_L1Staking *L1StakingFilterer) ParseStakersRemoved(log types.Log) (*L1StakingStakersRemoved, error) { - event := new(L1StakingStakersRemoved) - if err := _L1Staking.contract.UnpackLog(event, "StakersRemoved", log); err != nil { - return nil, err - } - event.Raw = log - return event, nil -} - -// L1StakingStakingValueUpdatedIterator is returned from FilterStakingValueUpdated and is used to iterate over the raw logs and unpacked data for StakingValueUpdated events raised by the L1Staking contract. -type L1StakingStakingValueUpdatedIterator struct { - Event *L1StakingStakingValueUpdated // Event containing the contract specifics and raw log - - contract *bind.BoundContract // Generic contract to use for unpacking event data - event string // Event name to use for unpacking event data - - logs chan types.Log // Log channel receiving the found contract events - sub ethereum.Subscription // Subscription for errors, completion and termination - done bool // Whether the subscription completed delivering logs - fail error // Occurred error to stop iteration -} - -// Next advances the iterator to the subsequent event, returning whether there -// are any more events found. In case of a retrieval or parsing error, false is -// returned and Error() can be queried for the exact failure. -func (it *L1StakingStakingValueUpdatedIterator) Next() bool { - // If the iterator failed, stop iterating - if it.fail != nil { - return false - } - // If the iterator completed, deliver directly whatever's available - if it.done { - select { - case log := <-it.logs: - it.Event = new(L1StakingStakingValueUpdated) - if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { - it.fail = err - return false - } - it.Event.Raw = log - return true - - default: - return false - } - } - // Iterator still in progress, wait for either a data or an error event - select { - case log := <-it.logs: - it.Event = new(L1StakingStakingValueUpdated) - if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { - it.fail = err - return false - } - it.Event.Raw = log - return true - - case err := <-it.sub.Err(): - it.done = true - it.fail = err - return it.Next() - } -} - -// Error returns any retrieval or parsing error occurred during filtering. -func (it *L1StakingStakingValueUpdatedIterator) Error() error { - return it.fail -} - -// Close terminates the iteration process, releasing any pending underlying -// resources. -func (it *L1StakingStakingValueUpdatedIterator) Close() error { - it.sub.Unsubscribe() - return nil -} - -// L1StakingStakingValueUpdated represents a StakingValueUpdated event raised by the L1Staking contract. -type L1StakingStakingValueUpdated struct { - OldStakingValue *big.Int - NewStakingValue *big.Int - Raw types.Log // Blockchain specific contextual infos -} - -// FilterStakingValueUpdated is a free log retrieval operation binding the contract event 0x91e3ed304d49776a78bbe0df6bf7156f5433763621e5e728a3c2adfe7de74368. -// -// Solidity: event StakingValueUpdated(uint256 oldStakingValue, uint256 newStakingValue) -func (_L1Staking *L1StakingFilterer) FilterStakingValueUpdated(opts *bind.FilterOpts) (*L1StakingStakingValueUpdatedIterator, error) { - - logs, sub, err := _L1Staking.contract.FilterLogs(opts, "StakingValueUpdated") - if err != nil { - return nil, err - } - return &L1StakingStakingValueUpdatedIterator{contract: _L1Staking.contract, event: "StakingValueUpdated", logs: logs, sub: sub}, nil -} - -// WatchStakingValueUpdated is a free log subscription operation binding the contract event 0x91e3ed304d49776a78bbe0df6bf7156f5433763621e5e728a3c2adfe7de74368. -// -// Solidity: event StakingValueUpdated(uint256 oldStakingValue, uint256 newStakingValue) -func (_L1Staking *L1StakingFilterer) WatchStakingValueUpdated(opts *bind.WatchOpts, sink chan<- *L1StakingStakingValueUpdated) (event.Subscription, error) { - - logs, sub, err := _L1Staking.contract.WatchLogs(opts, "StakingValueUpdated") - if err != nil { - return nil, err - } - return event.NewSubscription(func(quit <-chan struct{}) error { - defer sub.Unsubscribe() - for { - select { - case log := <-logs: - // New log arrived, parse the event and forward to the user - event := new(L1StakingStakingValueUpdated) - if err := _L1Staking.contract.UnpackLog(event, "StakingValueUpdated", log); err != nil { - return err - } - event.Raw = log - - select { - case sink <- event: - case err := <-sub.Err(): - return err - case <-quit: - return nil - } - case err := <-sub.Err(): - return err - case <-quit: - return nil - } - } - }), nil -} - -// ParseStakingValueUpdated is a log parse operation binding the contract event 0x91e3ed304d49776a78bbe0df6bf7156f5433763621e5e728a3c2adfe7de74368. -// -// Solidity: event StakingValueUpdated(uint256 oldStakingValue, uint256 newStakingValue) -func (_L1Staking *L1StakingFilterer) ParseStakingValueUpdated(log types.Log) (*L1StakingStakingValueUpdated, error) { - event := new(L1StakingStakingValueUpdated) - if err := _L1Staking.contract.UnpackLog(event, "StakingValueUpdated", log); err != nil { - return nil, err - } - event.Raw = log - return event, nil -} - -// L1StakingWhitelistUpdatedIterator is returned from FilterWhitelistUpdated and is used to iterate over the raw logs and unpacked data for WhitelistUpdated events raised by the L1Staking contract. -type L1StakingWhitelistUpdatedIterator struct { - Event *L1StakingWhitelistUpdated // Event containing the contract specifics and raw log - - contract *bind.BoundContract // Generic contract to use for unpacking event data - event string // Event name to use for unpacking event data - - logs chan types.Log // Log channel receiving the found contract events - sub ethereum.Subscription // Subscription for errors, completion and termination - done bool // Whether the subscription completed delivering logs - fail error // Occurred error to stop iteration -} - -// Next advances the iterator to the subsequent event, returning whether there -// are any more events found. In case of a retrieval or parsing error, false is -// returned and Error() can be queried for the exact failure. -func (it *L1StakingWhitelistUpdatedIterator) Next() bool { - // If the iterator failed, stop iterating - if it.fail != nil { - return false - } - // If the iterator completed, deliver directly whatever's available - if it.done { - select { - case log := <-it.logs: - it.Event = new(L1StakingWhitelistUpdated) - if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { - it.fail = err - return false - } - it.Event.Raw = log - return true - - default: - return false - } - } - // Iterator still in progress, wait for either a data or an error event - select { - case log := <-it.logs: - it.Event = new(L1StakingWhitelistUpdated) - if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { - it.fail = err - return false - } - it.Event.Raw = log - return true - - case err := <-it.sub.Err(): - it.done = true - it.fail = err - return it.Next() - } -} - -// Error returns any retrieval or parsing error occurred during filtering. -func (it *L1StakingWhitelistUpdatedIterator) Error() error { - return it.fail -} - -// Close terminates the iteration process, releasing any pending underlying -// resources. -func (it *L1StakingWhitelistUpdatedIterator) Close() error { - it.sub.Unsubscribe() - return nil -} - -// L1StakingWhitelistUpdated represents a WhitelistUpdated event raised by the L1Staking contract. -type L1StakingWhitelistUpdated struct { - Add []common.Address - Remove []common.Address - Raw types.Log // Blockchain specific contextual infos -} - -// FilterWhitelistUpdated is a free log retrieval operation binding the contract event 0xe375867e538b40218c1b3db2ccceaf875eb073e38b510449e6088c1539ac8622. -// -// Solidity: event WhitelistUpdated(address[] add, address[] remove) -func (_L1Staking *L1StakingFilterer) FilterWhitelistUpdated(opts *bind.FilterOpts) (*L1StakingWhitelistUpdatedIterator, error) { - - logs, sub, err := _L1Staking.contract.FilterLogs(opts, "WhitelistUpdated") - if err != nil { - return nil, err - } - return &L1StakingWhitelistUpdatedIterator{contract: _L1Staking.contract, event: "WhitelistUpdated", logs: logs, sub: sub}, nil -} - -// WatchWhitelistUpdated is a free log subscription operation binding the contract event 0xe375867e538b40218c1b3db2ccceaf875eb073e38b510449e6088c1539ac8622. -// -// Solidity: event WhitelistUpdated(address[] add, address[] remove) -func (_L1Staking *L1StakingFilterer) WatchWhitelistUpdated(opts *bind.WatchOpts, sink chan<- *L1StakingWhitelistUpdated) (event.Subscription, error) { - - logs, sub, err := _L1Staking.contract.WatchLogs(opts, "WhitelistUpdated") - if err != nil { - return nil, err - } - return event.NewSubscription(func(quit <-chan struct{}) error { - defer sub.Unsubscribe() - for { - select { - case log := <-logs: - // New log arrived, parse the event and forward to the user - event := new(L1StakingWhitelistUpdated) - if err := _L1Staking.contract.UnpackLog(event, "WhitelistUpdated", log); err != nil { - return err - } - event.Raw = log - - select { - case sink <- event: - case err := <-sub.Err(): - return err - case <-quit: - return nil - } - case err := <-sub.Err(): - return err - case <-quit: - return nil - } - } - }), nil -} - -// ParseWhitelistUpdated is a log parse operation binding the contract event 0xe375867e538b40218c1b3db2ccceaf875eb073e38b510449e6088c1539ac8622. -// -// Solidity: event WhitelistUpdated(address[] add, address[] remove) -func (_L1Staking *L1StakingFilterer) ParseWhitelistUpdated(log types.Log) (*L1StakingWhitelistUpdated, error) { - event := new(L1StakingWhitelistUpdated) - if err := _L1Staking.contract.UnpackLog(event, "WhitelistUpdated", log); err != nil { - return nil, err - } - event.Raw = log - return event, nil -} - -// L1StakingWithdrawnIterator is returned from FilterWithdrawn and is used to iterate over the raw logs and unpacked data for Withdrawn events raised by the L1Staking contract. -type L1StakingWithdrawnIterator struct { - Event *L1StakingWithdrawn // Event containing the contract specifics and raw log - - contract *bind.BoundContract // Generic contract to use for unpacking event data - event string // Event name to use for unpacking event data - - logs chan types.Log // Log channel receiving the found contract events - sub ethereum.Subscription // Subscription for errors, completion and termination - done bool // Whether the subscription completed delivering logs - fail error // Occurred error to stop iteration -} - -// Next advances the iterator to the subsequent event, returning whether there -// are any more events found. In case of a retrieval or parsing error, false is -// returned and Error() can be queried for the exact failure. -func (it *L1StakingWithdrawnIterator) Next() bool { - // If the iterator failed, stop iterating - if it.fail != nil { - return false - } - // If the iterator completed, deliver directly whatever's available - if it.done { - select { - case log := <-it.logs: - it.Event = new(L1StakingWithdrawn) - if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { - it.fail = err - return false - } - it.Event.Raw = log - return true - - default: - return false - } - } - // Iterator still in progress, wait for either a data or an error event - select { - case log := <-it.logs: - it.Event = new(L1StakingWithdrawn) - if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { - it.fail = err - return false - } - it.Event.Raw = log - return true - - case err := <-it.sub.Err(): - it.done = true - it.fail = err - return it.Next() - } -} - -// Error returns any retrieval or parsing error occurred during filtering. -func (it *L1StakingWithdrawnIterator) Error() error { - return it.fail -} - -// Close terminates the iteration process, releasing any pending underlying -// resources. -func (it *L1StakingWithdrawnIterator) Close() error { - it.sub.Unsubscribe() - return nil -} - -// L1StakingWithdrawn represents a Withdrawn event raised by the L1Staking contract. -type L1StakingWithdrawn struct { - Addr common.Address - UnlockHeight *big.Int - Raw types.Log // Blockchain specific contextual infos -} - -// FilterWithdrawn is a free log retrieval operation binding the contract event 0x7084f5476618d8e60b11ef0d7d3f06914655adb8793e28ff7f018d4c76d505d5. -// -// Solidity: event Withdrawn(address indexed addr, uint256 unlockHeight) -func (_L1Staking *L1StakingFilterer) FilterWithdrawn(opts *bind.FilterOpts, addr []common.Address) (*L1StakingWithdrawnIterator, error) { - - var addrRule []interface{} - for _, addrItem := range addr { - addrRule = append(addrRule, addrItem) - } - - logs, sub, err := _L1Staking.contract.FilterLogs(opts, "Withdrawn", addrRule) - if err != nil { - return nil, err - } - return &L1StakingWithdrawnIterator{contract: _L1Staking.contract, event: "Withdrawn", logs: logs, sub: sub}, nil -} - -// WatchWithdrawn is a free log subscription operation binding the contract event 0x7084f5476618d8e60b11ef0d7d3f06914655adb8793e28ff7f018d4c76d505d5. -// -// Solidity: event Withdrawn(address indexed addr, uint256 unlockHeight) -func (_L1Staking *L1StakingFilterer) WatchWithdrawn(opts *bind.WatchOpts, sink chan<- *L1StakingWithdrawn, addr []common.Address) (event.Subscription, error) { - - var addrRule []interface{} - for _, addrItem := range addr { - addrRule = append(addrRule, addrItem) - } - - logs, sub, err := _L1Staking.contract.WatchLogs(opts, "Withdrawn", addrRule) - if err != nil { - return nil, err - } - return event.NewSubscription(func(quit <-chan struct{}) error { - defer sub.Unsubscribe() - for { - select { - case log := <-logs: - // New log arrived, parse the event and forward to the user - event := new(L1StakingWithdrawn) - if err := _L1Staking.contract.UnpackLog(event, "Withdrawn", log); err != nil { - return err - } - event.Raw = log - - select { - case sink <- event: - case err := <-sub.Err(): - return err - case <-quit: - return nil - } - case err := <-sub.Err(): - return err - case <-quit: - return nil - } - } - }), nil -} - -// ParseWithdrawn is a log parse operation binding the contract event 0x7084f5476618d8e60b11ef0d7d3f06914655adb8793e28ff7f018d4c76d505d5. -// -// Solidity: event Withdrawn(address indexed addr, uint256 unlockHeight) -func (_L1Staking *L1StakingFilterer) ParseWithdrawn(log types.Log) (*L1StakingWithdrawn, error) { - event := new(L1StakingWithdrawn) - if err := _L1Staking.contract.UnpackLog(event, "Withdrawn", log); err != nil { - return nil, err - } - event.Raw = log - return event, nil -} diff --git a/bindings/bindings/l1staking_more.go b/bindings/bindings/l1staking_more.go deleted file mode 100644 index c3a973380..000000000 --- a/bindings/bindings/l1staking_more.go +++ /dev/null @@ -1,25 +0,0 @@ -// Code generated - DO NOT EDIT. -// This file is a generated binding and any manual changes will be lost. - -package bindings - -import ( - "encoding/json" - - "morph-l2/bindings/solc" -) - -const L1StakingStorageLayoutJSON = "{\"storage\":[{\"astId\":1000,\"contract\":\"contracts/l1/staking/L1Staking.sol:L1Staking\",\"label\":\"_initialized\",\"offset\":0,\"slot\":\"0\",\"type\":\"t_uint8\"},{\"astId\":1001,\"contract\":\"contracts/l1/staking/L1Staking.sol:L1Staking\",\"label\":\"_initializing\",\"offset\":1,\"slot\":\"0\",\"type\":\"t_bool\"},{\"astId\":1002,\"contract\":\"contracts/l1/staking/L1Staking.sol:L1Staking\",\"label\":\"__gap\",\"offset\":0,\"slot\":\"1\",\"type\":\"t_array(t_uint256)1027_storage\"},{\"astId\":1003,\"contract\":\"contracts/l1/staking/L1Staking.sol:L1Staking\",\"label\":\"_owner\",\"offset\":0,\"slot\":\"51\",\"type\":\"t_address\"},{\"astId\":1004,\"contract\":\"contracts/l1/staking/L1Staking.sol:L1Staking\",\"label\":\"__gap\",\"offset\":0,\"slot\":\"52\",\"type\":\"t_array(t_uint256)1026_storage\"},{\"astId\":1005,\"contract\":\"contracts/l1/staking/L1Staking.sol:L1Staking\",\"label\":\"_status\",\"offset\":0,\"slot\":\"101\",\"type\":\"t_uint256\"},{\"astId\":1006,\"contract\":\"contracts/l1/staking/L1Staking.sol:L1Staking\",\"label\":\"__gap\",\"offset\":0,\"slot\":\"102\",\"type\":\"t_array(t_uint256)1026_storage\"},{\"astId\":1007,\"contract\":\"contracts/l1/staking/L1Staking.sol:L1Staking\",\"label\":\"rollupContract\",\"offset\":0,\"slot\":\"151\",\"type\":\"t_address\"},{\"astId\":1008,\"contract\":\"contracts/l1/staking/L1Staking.sol:L1Staking\",\"label\":\"stakingValue\",\"offset\":0,\"slot\":\"152\",\"type\":\"t_uint256\"},{\"astId\":1009,\"contract\":\"contracts/l1/staking/L1Staking.sol:L1Staking\",\"label\":\"withdrawalLockBlocks\",\"offset\":0,\"slot\":\"153\",\"type\":\"t_uint256\"},{\"astId\":1010,\"contract\":\"contracts/l1/staking/L1Staking.sol:L1Staking\",\"label\":\"rewardPercentage\",\"offset\":0,\"slot\":\"154\",\"type\":\"t_uint256\"},{\"astId\":1011,\"contract\":\"contracts/l1/staking/L1Staking.sol:L1Staking\",\"label\":\"gasLimitAddStaker\",\"offset\":0,\"slot\":\"155\",\"type\":\"t_uint256\"},{\"astId\":1012,\"contract\":\"contracts/l1/staking/L1Staking.sol:L1Staking\",\"label\":\"gasLimitRemoveStakers\",\"offset\":0,\"slot\":\"156\",\"type\":\"t_uint256\"},{\"astId\":1013,\"contract\":\"contracts/l1/staking/L1Staking.sol:L1Staking\",\"label\":\"slashRemaining\",\"offset\":0,\"slot\":\"157\",\"type\":\"t_uint256\"},{\"astId\":1014,\"contract\":\"contracts/l1/staking/L1Staking.sol:L1Staking\",\"label\":\"whitelist\",\"offset\":0,\"slot\":\"158\",\"type\":\"t_mapping(t_address,t_bool)\"},{\"astId\":1015,\"contract\":\"contracts/l1/staking/L1Staking.sol:L1Staking\",\"label\":\"removedList\",\"offset\":0,\"slot\":\"159\",\"type\":\"t_mapping(t_address,t_bool)\"},{\"astId\":1016,\"contract\":\"contracts/l1/staking/L1Staking.sol:L1Staking\",\"label\":\"stakerSet\",\"offset\":0,\"slot\":\"160\",\"type\":\"t_array(t_address)1025_storage\"},{\"astId\":1017,\"contract\":\"contracts/l1/staking/L1Staking.sol:L1Staking\",\"label\":\"stakerIndexes\",\"offset\":0,\"slot\":\"415\",\"type\":\"t_mapping(t_address,t_uint8)\"},{\"astId\":1018,\"contract\":\"contracts/l1/staking/L1Staking.sol:L1Staking\",\"label\":\"deleteList\",\"offset\":0,\"slot\":\"416\",\"type\":\"t_array(t_address)dyn_storage\"},{\"astId\":1019,\"contract\":\"contracts/l1/staking/L1Staking.sol:L1Staking\",\"label\":\"deleteableHeight\",\"offset\":0,\"slot\":\"417\",\"type\":\"t_mapping(t_address,t_uint256)\"},{\"astId\":1020,\"contract\":\"contracts/l1/staking/L1Staking.sol:L1Staking\",\"label\":\"stakers\",\"offset\":0,\"slot\":\"418\",\"type\":\"t_mapping(t_address,t_struct(StakerInfo)1028_storage)\"},{\"astId\":1021,\"contract\":\"contracts/l1/staking/L1Staking.sol:L1Staking\",\"label\":\"blsKeys\",\"offset\":0,\"slot\":\"419\",\"type\":\"t_mapping(t_bytes_memory_ptr,t_bool)\"},{\"astId\":1022,\"contract\":\"contracts/l1/staking/L1Staking.sol:L1Staking\",\"label\":\"tmKeys\",\"offset\":0,\"slot\":\"420\",\"type\":\"t_mapping(t_bytes32,t_bool)\"},{\"astId\":1023,\"contract\":\"contracts/l1/staking/L1Staking.sol:L1Staking\",\"label\":\"withdrawals\",\"offset\":0,\"slot\":\"421\",\"type\":\"t_mapping(t_address,t_uint256)\"},{\"astId\":1024,\"contract\":\"contracts/l1/staking/L1Staking.sol:L1Staking\",\"label\":\"challengeDeposit\",\"offset\":0,\"slot\":\"422\",\"type\":\"t_uint256\"}],\"types\":{\"t_address\":{\"encoding\":\"inplace\",\"label\":\"address\",\"numberOfBytes\":\"20\"},\"t_array(t_address)1025_storage\":{\"encoding\":\"inplace\",\"label\":\"address[255]\",\"numberOfBytes\":\"8160\"},\"t_array(t_address)dyn_storage\":{\"encoding\":\"dynamic_array\",\"label\":\"address[]\",\"numberOfBytes\":\"32\"},\"t_array(t_uint256)1026_storage\":{\"encoding\":\"inplace\",\"label\":\"uint256[49]\",\"numberOfBytes\":\"1568\"},\"t_array(t_uint256)1027_storage\":{\"encoding\":\"inplace\",\"label\":\"uint256[50]\",\"numberOfBytes\":\"1600\"},\"t_bool\":{\"encoding\":\"inplace\",\"label\":\"bool\",\"numberOfBytes\":\"1\"},\"t_bytes32\":{\"encoding\":\"inplace\",\"label\":\"bytes32\",\"numberOfBytes\":\"32\"},\"t_bytes_memory_ptr\":{\"encoding\":\"bytes\",\"label\":\"bytes\",\"numberOfBytes\":\"32\"},\"t_bytes_storage\":{\"encoding\":\"bytes\",\"label\":\"bytes\",\"numberOfBytes\":\"32\"},\"t_mapping(t_address,t_bool)\":{\"encoding\":\"mapping\",\"label\":\"mapping(address =\u003e bool)\",\"numberOfBytes\":\"32\",\"key\":\"t_address\",\"value\":\"t_bool\"},\"t_mapping(t_address,t_struct(StakerInfo)1028_storage)\":{\"encoding\":\"mapping\",\"label\":\"mapping(address =\u003e struct Types.StakerInfo)\",\"numberOfBytes\":\"32\",\"key\":\"t_address\",\"value\":\"t_struct(StakerInfo)1028_storage\"},\"t_mapping(t_address,t_uint256)\":{\"encoding\":\"mapping\",\"label\":\"mapping(address =\u003e uint256)\",\"numberOfBytes\":\"32\",\"key\":\"t_address\",\"value\":\"t_uint256\"},\"t_mapping(t_address,t_uint8)\":{\"encoding\":\"mapping\",\"label\":\"mapping(address =\u003e uint8)\",\"numberOfBytes\":\"32\",\"key\":\"t_address\",\"value\":\"t_uint8\"},\"t_mapping(t_bytes32,t_bool)\":{\"encoding\":\"mapping\",\"label\":\"mapping(bytes32 =\u003e bool)\",\"numberOfBytes\":\"32\",\"key\":\"t_bytes32\",\"value\":\"t_bool\"},\"t_mapping(t_bytes_memory_ptr,t_bool)\":{\"encoding\":\"mapping\",\"label\":\"mapping(bytes =\u003e bool)\",\"numberOfBytes\":\"32\",\"key\":\"t_bytes_memory_ptr\",\"value\":\"t_bool\"},\"t_struct(StakerInfo)1028_storage\":{\"encoding\":\"inplace\",\"label\":\"struct Types.StakerInfo\",\"numberOfBytes\":\"96\"},\"t_uint256\":{\"encoding\":\"inplace\",\"label\":\"uint256\",\"numberOfBytes\":\"32\"},\"t_uint8\":{\"encoding\":\"inplace\",\"label\":\"uint8\",\"numberOfBytes\":\"1\"}}}" - -var L1StakingStorageLayout = new(solc.StorageLayout) - -var L1StakingDeployedBin = "0x6080604052600436106102f8575f3560e01c80637a9262a211610191578063ab8c53dc116100dc578063d51c90a911610087578063e2a6ad5f11610062578063e2a6ad5f14610940578063f2fde38b1461095f578063f4fe7fef1461097e575f80fd5b8063d51c90a9146108a6578063dd4785f5146108bb578063df155033146108fc575f80fd5b8063c7cd469a116100b7578063c7cd469a14610849578063cde4cd1114610868578063d096c3c614610887575f80fd5b8063ab8c53dc146107e9578063ae81de53146107fe578063bfa02ba91461081d575f80fd5b8063927ede2d1161013c5780639fb82db9116101175780639fb82db914610797578063a3066aab146107ab578063a4f209b0146107ca575f80fd5b8063927ede2d1461070a5780639b19251a1461073d5780639d48f4171461076b575f80fd5b80638a565ac31161016c5780638a565ac3146106935780638da5cb5b146106b25780639168ae72146106dc575f80fd5b80637a9262a2146106155780638142951a14610641578063831cfb5814610660575f80fd5b806341de239b11610251578063692c565b116101fc57806374fe27b7116101d757806374fe27b714610595578063797adbde146105bb5780637a4e87c3146105da575f80fd5b8063692c565b146105335780636f1e853314610562578063715018a614610581575f80fd5b80634d64903a1161022c5780634d64903a146104ea57806352d472eb146104ff5780636801579114610514575f80fd5b806341de239b1461049557806343352d61146104aa57806345bc4d10146104cb575f80fd5b8063320ff860116102b15780633cb747bf1161028c5780633cb747bf1461043b5780633ccfd60b1461046d5780633ee2a1f914610481575f80fd5b8063320ff860146103bf57806335928991146103de5780633a9bbede146103fd575f80fd5b80632a28e5a3116102e15780632a28e5a31461033a5780632e407a6f1461034f578063303afb9e1461037b575f80fd5b80630d13fd7b146102fc5780632108db3514610325575b5f80fd5b348015610307575f80fd5b506103126101a65481565b6040519081526020015b60405180910390f35b348015610330575f80fd5b50610312609c5481565b61034d610348366004613d4e565b61099d565b005b34801561035a575f80fd5b50610312610369366004613db5565b6101a16020525f908152604090205481565b348015610386575f80fd5b5061039a610395366004613dd5565b610e5f565b60405173ffffffffffffffffffffffffffffffffffffffff909116815260200161031c565b3480156103ca575f80fd5b5061034d6103d9366004613dec565b610e8b565b3480156103e9575f80fd5b5061034d6103f8366004613dd5565b61127a565b348015610408575f80fd5b5061042b610417366004613db5565b609f6020525f908152604090205460ff1681565b604051901515815260200161031c565b348015610446575f80fd5b507f000000000000000000000000000000000000000000000000000000000000000061039a565b348015610478575f80fd5b5061034d611341565b34801561048c575f80fd5b5061034d611579565b3480156104a0575f80fd5b5061031260995481565b3480156104b5575f80fd5b506104be61158b565b60405161031c9190613e94565b3480156104d6575f80fd5b506103126104e5366004613dd5565b6115de565b3480156104f5575f80fd5b5061031260985481565b34801561050a575f80fd5b50610312609a5481565b34801561051f575f80fd5b5061042b61052e366004613db5565b61199a565b34801561053e575f80fd5b5061042b61054d366004613dd5565b6101a46020525f908152604090205460ff1681565b34801561056d575f80fd5b5061042b61057c366004613db5565b611a69565b34801561058c575f80fd5b5061034d611b09565b3480156105a0575f80fd5b5061042b6105af366004613f23565b60019695505050505050565b3480156105c6575f80fd5b5061039a6105d5366004613dd5565b611b1a565b3480156105e5575f80fd5b5061042b6105f4366004613fc9565b80516020818301810180516101a38252928201919093012091525460ff1681565b348015610620575f80fd5b5061031261062f366004613db5565b6101a56020525f908152604090205481565b34801561064c575f80fd5b5061034d61065b366004614003565b611b50565b34801561066b575f80fd5b5061039a7f000000000000000000000000000000000000000000000000000000000000000081565b34801561069e575f80fd5b5061034d6106ad366004613dd5565b61211a565b3480156106bd575f80fd5b5060335473ffffffffffffffffffffffffffffffffffffffff1661039a565b3480156106e7575f80fd5b506106fb6106f6366004613db5565b6121d7565b60405161031c939291906140bc565b348015610715575f80fd5b5061039a7f000000000000000000000000000000000000000000000000000000000000000081565b348015610748575f80fd5b5061042b610757366004613db5565b609e6020525f908152604090205460ff1681565b348015610776575f80fd5b5061078a610785366004613dd5565b612297565b60405161031c91906140f9565b3480156107a2575f80fd5b5061078a6123ce565b3480156107b6575f80fd5b5061034d6107c5366004613db5565b6125b0565b3480156107d5575f80fd5b5061034d6107e4366004613dd5565b612721565b3480156107f4575f80fd5b50610312609d5481565b348015610809575f80fd5b5061034d610818366004613dd5565b6127eb565b348015610828575f80fd5b5060975461039a9073ffffffffffffffffffffffffffffffffffffffff1681565b348015610854575f80fd5b5061034d610863366004614152565b6128a8565b348015610873575f80fd5b5061034d610882366004613db5565b612ac6565b348015610892575f80fd5b506103126108a1366004613db5565b612b41565b3480156108b1575f80fd5b50610312609b5481565b3480156108c6575f80fd5b506108ea6108d5366004613db5565b61019f6020525f908152604090205460ff1681565b60405160ff909116815260200161031c565b348015610907575f80fd5b5061042b610916366004613db5565b73ffffffffffffffffffffffffffffffffffffffff165f9081526101a16020526040902054151590565b34801561094b575f80fd5b5061031261095a3660046141b9565b612be2565b34801561096a575f80fd5b5061034d610979366004613db5565b612d55565b348015610989575f80fd5b5061034d610998366004613dd5565b612e09565b335f818152609e602052604090205460ff16610a1a576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601060248201527f6e6f7420696e2077686974656c6973740000000000000000000000000000000060448201526064015b60405180910390fd5b335f9081526101a2602052604090205473ffffffffffffffffffffffffffffffffffffffff1615610aa7576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601260248201527f616c7265616479207265676973746572656400000000000000000000000000006044820152606401610a11565b8215801590610ac557505f8381526101a4602052604090205460ff16155b610b2b576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601960248201527f696e76616c69642074656e6465726d696e74207075626b6579000000000000006044820152606401610a11565b8151610100148015610b5e57506101a382604051610b4991906141f8565b9081526040519081900360200190205460ff16155b610bc4576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601260248201527f696e76616c696420626c73207075626b657900000000000000000000000000006044820152606401610a11565b6098543414610c2f576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601560248201527f696e76616c6964207374616b696e672076616c756500000000000000000000006044820152606401610a11565b6040518060600160405280610c413390565b73ffffffffffffffffffffffffffffffffffffffff908116825260208083018790526040928301869052335f9081526101a28252839020845181547fffffffffffffffffffffffff00000000000000000000000000000000000000001693169290921782558301516001820155908201516002820190610cc190826142a8565b50905050610cd4610ccf3390565b612ec6565b60016101a383604051610ce791906141f8565b9081526040805191829003602090810190922080549315157fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff009485161790555f8681526101a49092529020805490911660011790557fb9c7babb56df9f2da4a30811a6c778e4e68af88b72712d56cf62c5516e20e199610d643390565b8484604051610d75939291906140bc565b60405180910390a1335f9081526101a260209081526040918290208251606081018452815473ffffffffffffffffffffffffffffffffffffffff168152600182015492810192909252600281018054610e5a9484019190610dd590614213565b80601f0160208091040260200160405190810160405280929190818152602001828054610e0190614213565b8015610e4c5780601f10610e2357610100808354040283529160200191610e4c565b820191905f5260205f20905b815481529060010190602001808311610e2f57829003601f168201915b505050505081525050613033565b505050565b60a08160ff8110610e6e575f80fd5b015473ffffffffffffffffffffffffffffffffffffffff16905081565b610e93613186565b5f5b815181101561123657610ec0828281518110610eb357610eb36143c4565b602002602001015161199a565b610f4c576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602160248201527f6f6e6c7920616374697665207374616b65722063616e2062652072656d6f766560448201527f64000000000000000000000000000000000000000000000000000000000000006064820152608401610a11565b6101a55f838381518110610f6257610f626143c4565b602002602001015173ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020015f20545f1461100c576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600b60248201527f7769746864726177696e670000000000000000000000000000000000000000006044820152606401610a11565b609954611019904361441e565b6101a55f84848151811061102f5761102f6143c4565b602002602001015173ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020015f2081905550611096828281518110611089576110896143c4565b6020026020010151613207565b8181815181106110a8576110a86143c4565b602002602001015173ffffffffffffffffffffffffffffffffffffffff167f7084f5476618d8e60b11ef0d7d3f06914655adb8793e28ff7f018d4c76d505d56101a55f8585815181106110fd576110fd6143c4565b602002602001015173ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020015f205460405161114e91815260200190565b60405180910390a2609e5f83838151811061116b5761116b6143c4565b602002602001015173ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020015f205f6101000a81549060ff02191690556001609f5f8484815181106111d2576111d26143c4565b60209081029190910181015173ffffffffffffffffffffffffffffffffffffffff1682528101919091526040015f2080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0016911515919091179055600101610e95565b507f3f446646c03e618be8238a586960d6d625f35c653cdca1ef59609993e8ed2c848160405161126691906140f9565b60405180910390a16112778161333a565b50565b611282613186565b5f8111801561129457506101a6548114155b6112fa576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601f60248201527f696e76616c6964206368616c6c656e6765206465706f7369742076616c7565006044820152606401610a11565b6101a680549082905560408051828152602081018490527f36f971a40478225aeb80cfbf5e80306e8cb76d3bf7d56fdc5e490945cddb7d5591015b60405180910390a15050565b61134a3361199a565b6113b0576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601260248201527f6f6e6c7920616374697665207374616b657200000000000000000000000000006044820152606401610a11565b335f9081526101a5602052604090205415611427576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600b60248201527f7769746864726177696e670000000000000000000000000000000000000000006044820152606401610a11565b609954611434904361441e565b335f8181526101a5602052604090209190915561145090613207565b335f8181526101a560209081526040918290205491519182527f7084f5476618d8e60b11ef0d7d3f06914655adb8793e28ff7f018d4c76d505d5910160405180910390a2335f908152609e6020908152604080832080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00908116909155609f83528184208054909116600190811790915581518181528083019092529091828101908036833701905050905033815f81518110611510576115106143c4565b602002602001019073ffffffffffffffffffffffffffffffffffffffff16908173ffffffffffffffffffffffffffffffffffffffff16815250507f3f446646c03e618be8238a586960d6d625f35c653cdca1ef59609993e8ed2c848160405161126691906140f9565b611581613186565b61158961345d565b565b611593613bdf565b60408051611fe08101918290529060a09060ff9082845b815473ffffffffffffffffffffffffffffffffffffffff1681526001909101906020018083116115aa575050505050905090565b6097545f9073ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614611677576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601460248201527f6f6e6c7920726f6c6c757020636f6e74726163740000000000000000000000006044820152606401610a11565b61167f6137ae565b5f61168983612297565b90505f805b82518110156118b0575f6101a55f8584815181106116ae576116ae6143c4565b602002602001015173ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020015f20541115611765576101a55f84838151811061170c5761170c6143c4565b602002602001015173ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020015f205f90556098548261175e919061441e565b91506118a8565b6117ac83828151811061177a5761177a6143c4565b602002602001015173ffffffffffffffffffffffffffffffffffffffff165f9081526101a16020526040902054151590565b6118a8576098546117bd908361441e565b91506117d4838281518110611089576110896143c4565b609e5f8483815181106117e9576117e96143c4565b602002602001015173ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020015f205f6101000a81549060ff02191690556001609f5f858481518110611850576118506143c4565b602002602001015173ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020015f205f6101000a81548160ff0219169083151502179055505b60010161168e565b505f6064609a54836118c29190614431565b6118cc9190614448565b90506118d88183614480565b609d5f8282546118e8919061441e565b90915550506097546119109073ffffffffffffffffffffffffffffffffffffffff1682613821565b7f654f4a61849f1b3ad10abb283d27f02d5fece7b820acc5a3b874713b58748b5a8360405161193f91906140f9565b60405180910390a17f3f446646c03e618be8238a586960d6d625f35c653cdca1ef59609993e8ed2c848360405161197691906140f9565b60405180910390a16119878361333a565b925050506119956001606555565b919050565b73ffffffffffffffffffffffffffffffffffffffff81165f90815261019f602052604081205460ff1681036119d057505f919050565b73ffffffffffffffffffffffffffffffffffffffff82165f81815261019f602052604090205460a090611a089060019060ff16614493565b60ff1660ff8110611a1b57611a1b6143c4565b015473ffffffffffffffffffffffffffffffffffffffff16148015611a63575073ffffffffffffffffffffffffffffffffffffffff82165f9081526101a16020526040902054155b92915050565b73ffffffffffffffffffffffffffffffffffffffff81165f90815261019f602052604081205460ff168103611a9f57505f919050565b73ffffffffffffffffffffffffffffffffffffffff82165f81815261019f602052604090205460a090611ad79060019060ff16614493565b60ff1660ff8110611aea57611aea6143c4565b015473ffffffffffffffffffffffffffffffffffffffff161492915050565b611b11613186565b6115895f6138f8565b6101a08181548110611b2a575f80fd5b5f9182526020909120015473ffffffffffffffffffffffffffffffffffffffff16905081565b5f54610100900460ff1615808015611b6e57505f54600160ff909116105b80611b875750303b158015611b8757505f5460ff166001145b611c13576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602e60248201527f496e697469616c697a61626c653a20636f6e747261637420697320616c72656160448201527f647920696e697469616c697a65640000000000000000000000000000000000006064820152608401610a11565b5f80547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001660011790558015611c6f575f80547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ff166101001790555b73ffffffffffffffffffffffffffffffffffffffff8816611cec576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601760248201527f696e76616c696420726f6c6c757020636f6e74726163740000000000000000006044820152606401610a11565b5f8711611d55576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601560248201527f696e76616c6964207374616b696e672076616c756500000000000000000000006044820152606401610a11565b5f8611611dbe576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601f60248201527f696e76616c6964206368616c6c656e6765206465706f7369742076616c7565006044820152606401610a11565b5f8511611e27576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601e60248201527f696e76616c6964207769746864726177616c206c6f636b20626c6f636b7300006044820152606401610a11565b5f8311611e90576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601c60248201527f696e76616c696420676173206c696d697420616464207374616b6572000000006044820152606401610a11565b5f8211611ef9576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820181905260248201527f696e76616c696420676173206c696d69742072656d6f7665207374616b6572736044820152606401610a11565b5f84118015611f09575060648411155b611f94576040517f08c379a0000000000000000000000000000000000000000000000000000000008152602060048201526024808201527f696e76616c6964206368616c6c656e676572207265776172642070657263656e60448201527f74616765000000000000000000000000000000000000000000000000000000006064820152608401610a11565b611f9c61396e565b611fa4613a0c565b609780547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff8a16179055609a84905560988790556101a68690556099859055609b839055609c829055604080515f8152602081018590527f0ef80fb82bf5439b8591744c0fde771f5d93dce7a7970b1e9b7828cdc3970e9c910160405180910390a1604080515f8152602081018490527fdd4b37d1f14888147fe4be2cfaedcbf148fd07ececc856d0433241b8b6e4a7f7910160405180910390a1604080515f8152602081018690527fa46de936426e045703b2d34a292a19fde92b329018db8e0da750033876b655ba910160405180910390a18015612110575f80547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ff169055604051600181527f7f26b83ff96e1f2b6a682f133852f6798a09c465da95921460cefb38474024989060200160405180910390a15b5050505050505050565b612122613186565b5f811180156121335750609c548114155b612199576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601560248201527f696e76616c6964206e657720676173206c696d697400000000000000000000006044820152606401610a11565b609c80549082905560408051828152602081018490527fdd4b37d1f14888147fe4be2cfaedcbf148fd07ececc856d0433241b8b6e4a7f79101611335565b6101a26020525f908152604090208054600182015460028301805473ffffffffffffffffffffffffffffffffffffffff90931693919261221690614213565b80601f016020809104026020016040519081016040528092919081815260200182805461224290614213565b801561228d5780601f106122645761010080835404028352916020019161228d565b820191905f5260205f20905b81548152906001019060200180831161227057829003601f168201915b5050505050905083565b6060600182901c5f5b81156122c7576122b181600161441e565b90506122be600183614480565b821691506122a0565b8067ffffffffffffffff8111156122e0576122e0613c48565b604051908082528060200260200182016040528015612309578160200160208202803683370190505b5092505f60015b60ff8160ff16116123c557600160ff82161b8616156123b35760a0612336600183614493565b60ff1660ff8110612349576123496143c4565b0154855173ffffffffffffffffffffffffffffffffffffffff90911690869084908110612378576123786143c4565b73ffffffffffffffffffffffffffffffffffffffff909216602092830291909101909101526123a882600161441e565b9150828210156123c5575b806123bd816144ac565b915050612310565b50505050919050565b6040805160ff80825261200082019092526060915f91829160208201611fe0803683370190505090505f5b60ff8110156124b7575f60a08260ff8110612416576124166143c4565b015473ffffffffffffffffffffffffffffffffffffffff161480159061247857506101a15f60a08360ff811061244e5761244e6143c4565b015473ffffffffffffffffffffffffffffffffffffffff16815260208101919091526040015f2054155b156124af5782612487816144ca565b935050600182828151811061249e5761249e6143c4565b911515602092830291909101909101525b6001016123f9565b505f8267ffffffffffffffff8111156124d2576124d2613c48565b6040519080825280602002602001820160405280156124fb578160200160208202803683370190505b5090505f805b60ff8110156125a65783818151811061251c5761251c6143c4565b60200260200101511561259e5760a08160ff811061253c5761253c6143c4565b0154835173ffffffffffffffffffffffffffffffffffffffff9091169084908490811061256b5761256b6143c4565b73ffffffffffffffffffffffffffffffffffffffff909216602092830291909101909101528161259a816144ca565b9250505b600101612501565b5090949350505050565b6125b86137ae565b335f9081526101a5602052604090205461262e576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601460248201527f7769746864726177616c206e6f742065786973740000000000000000000000006044820152606401610a11565b335f9081526101a560205260409020544310156126a7576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601160248201527f7769746864726177616c206c6f636b65640000000000000000000000000000006044820152606401610a11565b335f9081526101a560205260408120556126bf61345d565b6040805173ffffffffffffffffffffffffffffffffffffffff83168152905133917f89309c9b2aeaffbdce717113df9427298b20448c05919bf889e05f8c3094254b919081900360200190a261271781609854613821565b6112776001606555565b612729613186565b5f81118015612739575060648111155b80156127475750609a548114155b6127ad576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601960248201527f696e76616c6964207265776172642070657263656e74616765000000000000006044820152606401610a11565b609a80549082905560408051828152602081018490527fa46de936426e045703b2d34a292a19fde92b329018db8e0da750033876b655ba9101611335565b6127f3613186565b5f811180156128045750609b548114155b61286a576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601560248201527f696e76616c6964206e657720676173206c696d697400000000000000000000006044820152606401610a11565b609b80549082905560408051828152602081018490527f0ef80fb82bf5439b8591744c0fde771f5d93dce7a7970b1e9b7828cdc3970e9c9101611335565b6128b0613186565b5f5b838110156129fa57609f5f8686848181106128cf576128cf6143c4565b90506020020160208101906128e49190613db5565b73ffffffffffffffffffffffffffffffffffffffff16815260208101919091526040015f205460ff1615612974576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600f60248201527f696e2072656d6f766564206c69737400000000000000000000000000000000006044820152606401610a11565b6001609e5f87878581811061298b5761298b6143c4565b90506020020160208101906129a09190613db5565b73ffffffffffffffffffffffffffffffffffffffff16815260208101919091526040015f2080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00169115159190911790556001016128b2565b505f5b81811015612a8257609e5f848484818110612a1a57612a1a6143c4565b9050602002016020810190612a2f9190613db5565b73ffffffffffffffffffffffffffffffffffffffff16815260208101919091526040015f2080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001690556001016129fd565b507fe375867e538b40218c1b3db2ccceaf875eb073e38b510449e6088c1539ac862284848484604051612ab89493929190614553565b60405180910390a150505050565b612ace613186565b612ad66137ae565b609d54612ae38282613821565b5f609d556040805173ffffffffffffffffffffffffffffffffffffffff84168152602081018390527fa1fefb6c5328a92a416e321ed50997303fe7135fd88c28b0592b21ce42b5cdd9910160405180910390a1506112776001606555565b5f612b4b82611a69565b612bb1576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600e60248201527f696e76616c6964207374616b65720000000000000000000000000000000000006044820152606401610a11565b5073ffffffffffffffffffffffffffffffffffffffff165f90815261019f6020526040902054600160ff9091161b90565b5f60ff821115612c4e576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601c60248201527f7374616b657273206c656e677468206f7574206f6620626f756e6473000000006044820152606401610a11565b5f5b82811015612d4e57612c82848483818110612c6d57612c6d6143c4565b905060200201602081019061057c9190613db5565b612ce8576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600e60248201527f696e76616c6964207374616b65720000000000000000000000000000000000006044820152606401610a11565b61019f5f858584818110612cfe57612cfe6143c4565b9050602002016020810190612d139190613db5565b73ffffffffffffffffffffffffffffffffffffffff16815260208101919091526040015f2054600160ff90911681901b929092179101612c50565b5092915050565b612d5d613186565b73ffffffffffffffffffffffffffffffffffffffff8116612e00576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602660248201527f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160448201527f64647265737300000000000000000000000000000000000000000000000000006064820152608401610a11565b611277816138f8565b612e11613186565b5f81118015612e2257506098548114155b612e88576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601560248201527f696e76616c6964207374616b696e672076616c756500000000000000000000006044820152606401610a11565b609880549082905560408051828152602081018490527f91e3ed304d49776a78bbe0df6bf7156f5433763621e5e728a3c2adfe7de743689101611335565b5f5b60ff8160ff161015612fd0575f60a060ff808416908110612eeb57612eeb6143c4565b015473ffffffffffffffffffffffffffffffffffffffff1603612fc8578160a08260ff1660ff8110612f1f57612f1f6143c4565b0180547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff92909216919091179055612f6e816001614584565b73ffffffffffffffffffffffffffffffffffffffff929092165f90815261019f6020526040902080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001660ff9093169290921790915550565b600101612ec8565b506040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600960248201527f736c6f742066756c6c00000000000000000000000000000000000000000000006044820152606401610a11565b7f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff1663b2267a7b7f00000000000000000000000000000000000000000000000000000000000000005f846040516024016130a2919061459d565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529181526020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167f6d454d5100000000000000000000000000000000000000000000000000000000179052609b5490517fffffffff0000000000000000000000000000000000000000000000000000000060e087901b16815261315694939291906004016145e1565b5f604051808303815f87803b15801561316d575f80fd5b505af115801561317f573d5f803e3d5ffd5b5050505050565b60335473ffffffffffffffffffffffffffffffffffffffff163314611589576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e65726044820152606401610a11565b73ffffffffffffffffffffffffffffffffffffffff81165f9081526101a1602052604090205415613294576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601560248201527f616c726561647920696e2064656c6574654c69737400000000000000000000006044820152606401610a11565b6101a080546001810182555f919091527f7980fe0f714a613298681d64b7b8ffa7b148338dd52429f307d72798d5c317c40180547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff8316179055609954613311904361441e565b73ffffffffffffffffffffffffffffffffffffffff9091165f9081526101a16020526040902055565b7f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff1663b2267a7b7f00000000000000000000000000000000000000000000000000000000000000005f846040516024016133a991906140f9565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529181526020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167f0be67fcc00000000000000000000000000000000000000000000000000000000179052609c5490517fffffffff0000000000000000000000000000000000000000000000000000000060e087901b16815261315694939291906004016145e1565b5f5b6101a05481101561127757436101a15f6101a08481548110613483576134836143c4565b5f91825260208083209091015473ffffffffffffffffffffffffffffffffffffffff1683528201929092526040019020541161379c5760a0600161019f5f6101a085815481106134d5576134d56143c4565b5f91825260208083209091015473ffffffffffffffffffffffffffffffffffffffff168352820192909252604001902054613513919060ff16614493565b60ff1660ff8110613526576135266143c4565b0180547fffffffffffffffffffffffff00000000000000000000000000000000000000001690556101a0805461019f915f9184908110613568576135686143c4565b5f91825260208083209091015473ffffffffffffffffffffffffffffffffffffffff168352820192909252604001812080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001690556101a080546101a2929190849081106135d9576135d96143c4565b5f91825260208083209091015473ffffffffffffffffffffffffffffffffffffffff168352820192909252604001812080547fffffffffffffffffffffffff000000000000000000000000000000000000000016815560018101829055906136446002830182613bfe565b50506101a15f6101a0838154811061365e5761365e6143c4565b5f91825260208083209091015473ffffffffffffffffffffffffffffffffffffffff1683528201929092526040018120556101a080546136a090600190614480565b815481106136b0576136b06143c4565b5f918252602090912001546101a0805473ffffffffffffffffffffffffffffffffffffffff90921691839081106136e9576136e96143c4565b905f5260205f20015f6101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055506101a080548061374057613740614626565b5f8281526020902081017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff90810180547fffffffffffffffffffffffff000000000000000000000000000000000000000016905501905561345f565b806137a6816144ca565b91505061345f565b60026065540361381a576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c006044820152606401610a11565b6002606555565b80156138ed575f8273ffffffffffffffffffffffffffffffffffffffff16826040515f6040518083038185875af1925050503d805f811461387d576040519150601f19603f3d011682016040523d82523d5f602084013e613882565b606091505b5050905080610e5a576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601b60248201527f526f6c6c75703a20455448207472616e73666572206661696c656400000000006044820152606401610a11565b5050565b6001606555565b6033805473ffffffffffffffffffffffffffffffffffffffff8381167fffffffffffffffffffffffff0000000000000000000000000000000000000000831681179093556040519116919082907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0905f90a35050565b5f54610100900460ff16613a04576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602b60248201527f496e697469616c697a61626c653a20636f6e7472616374206973206e6f74206960448201527f6e697469616c697a696e670000000000000000000000000000000000000000006064820152608401610a11565b611589613aaa565b5f54610100900460ff16613aa2576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602b60248201527f496e697469616c697a61626c653a20636f6e7472616374206973206e6f74206960448201527f6e697469616c697a696e670000000000000000000000000000000000000000006064820152608401610a11565b611589613b49565b5f54610100900460ff16613b40576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602b60248201527f496e697469616c697a61626c653a20636f6e7472616374206973206e6f74206960448201527f6e697469616c697a696e670000000000000000000000000000000000000000006064820152608401610a11565b611589336138f8565b5f54610100900460ff166138f1576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602b60248201527f496e697469616c697a61626c653a20636f6e7472616374206973206e6f74206960448201527f6e697469616c697a696e670000000000000000000000000000000000000000006064820152608401610a11565b60405180611fe0016040528060ff906020820280368337509192915050565b508054613c0a90614213565b5f825580601f10613c19575050565b601f0160209004905f5260205f209081019061127791905b80821115613c44575f8155600101613c31565b5090565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52604160045260245ffd5b604051601f82017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016810167ffffffffffffffff81118282101715613cbc57613cbc613c48565b604052919050565b5f82601f830112613cd3575f80fd5b813567ffffffffffffffff811115613ced57613ced613c48565b613d1e60207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f84011601613c75565b818152846020838601011115613d32575f80fd5b816020850160208301375f918101602001919091529392505050565b5f8060408385031215613d5f575f80fd5b82359150602083013567ffffffffffffffff811115613d7c575f80fd5b613d8885828601613cc4565b9150509250929050565b803573ffffffffffffffffffffffffffffffffffffffff81168114611995575f80fd5b5f60208284031215613dc5575f80fd5b613dce82613d92565b9392505050565b5f60208284031215613de5575f80fd5b5035919050565b5f6020808385031215613dfd575f80fd5b823567ffffffffffffffff80821115613e14575f80fd5b818501915085601f830112613e27575f80fd5b813581811115613e3957613e39613c48565b8060051b9150613e4a848301613c75565b8181529183018401918481019088841115613e63575f80fd5b938501935b83851015613e8857613e7985613d92565b82529385019390850190613e68565b98975050505050505050565b611fe0810181835f5b60ff811015613ed257815173ffffffffffffffffffffffffffffffffffffffff16835260209283019290910190600101613e9d565b50505092915050565b5f8083601f840112613eeb575f80fd5b50813567ffffffffffffffff811115613f02575f80fd5b6020830191508360208260051b8501011115613f1c575f80fd5b9250929050565b5f805f805f8060808789031215613f38575f80fd5b86359550602087013567ffffffffffffffff80821115613f56575f80fd5b613f628a838b01613edb565b9097509550604089013594506060890135915080821115613f81575f80fd5b818901915089601f830112613f94575f80fd5b813581811115613fa2575f80fd5b8a6020828501011115613fb3575f80fd5b6020830194508093505050509295509295509295565b5f60208284031215613fd9575f80fd5b813567ffffffffffffffff811115613fef575f80fd5b613ffb84828501613cc4565b949350505050565b5f805f805f805f60e0888a031215614019575f80fd5b61402288613d92565b9960208901359950604089013598606081013598506080810135975060a0810135965060c00135945092505050565b5f5b8381101561406b578181015183820152602001614053565b50505f910152565b5f815180845261408a816020860160208601614051565b601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0169290920160200192915050565b73ffffffffffffffffffffffffffffffffffffffff84168152826020820152606060408201525f6140f06060830184614073565b95945050505050565b602080825282518282018190525f9190848201906040850190845b8181101561414657835173ffffffffffffffffffffffffffffffffffffffff1683529284019291840191600101614114565b50909695505050505050565b5f805f8060408587031215614165575f80fd5b843567ffffffffffffffff8082111561417c575f80fd5b61418888838901613edb565b909650945060208701359150808211156141a0575f80fd5b506141ad87828801613edb565b95989497509550505050565b5f80602083850312156141ca575f80fd5b823567ffffffffffffffff8111156141e0575f80fd5b6141ec85828601613edb565b90969095509350505050565b5f8251614209818460208701614051565b9190910192915050565b600181811c9082168061422757607f821691505b60208210810361425e577f4e487b71000000000000000000000000000000000000000000000000000000005f52602260045260245ffd5b50919050565b601f821115610e5a57805f5260205f20601f840160051c810160208510156142895750805b601f840160051c820191505b8181101561317f575f8155600101614295565b815167ffffffffffffffff8111156142c2576142c2613c48565b6142d6816142d08454614213565b84614264565b602080601f831160018114614328575f84156142f25750858301515b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff600386901b1c1916600185901b1785556143bc565b5f858152602081207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08616915b8281101561437457888601518255948401946001909101908401614355565b50858210156143b057878501517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff600388901b60f8161c191681555b505060018460011b0185555b505050505050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52603260045260245ffd5b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601160045260245ffd5b80820180821115611a6357611a636143f1565b8082028115828204841417611a6357611a636143f1565b5f8261447b577f4e487b71000000000000000000000000000000000000000000000000000000005f52601260045260245ffd5b500490565b81810381811115611a6357611a636143f1565b60ff8281168282160390811115611a6357611a636143f1565b5f60ff821660ff81036144c1576144c16143f1565b60010192915050565b5f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff82036144fa576144fa6143f1565b5060010190565b8183525f60208085019450825f5b858110156145485773ffffffffffffffffffffffffffffffffffffffff61453583613d92565b168752958201959082019060010161450f565b509495945050505050565b604081525f614566604083018688614501565b8281036020840152614579818587614501565b979650505050505050565b60ff8181168382160190811115611a6357611a636143f1565b6020815273ffffffffffffffffffffffffffffffffffffffff8251166020820152602082015160408201525f6040830151606080840152613ffb6080840182614073565b73ffffffffffffffffffffffffffffffffffffffff85168152836020820152608060408201525f6146156080830185614073565b905082606083015295945050505050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52603160045260245ffdfea164736f6c6343000818000a" - -func init() { - if err := json.Unmarshal([]byte(L1StakingStorageLayoutJSON), L1StakingStorageLayout); err != nil { - panic(err) - } - - layouts["L1Staking"] = L1StakingStorageLayout - deployedBytecodes["L1Staking"] = L1StakingDeployedBin -} diff --git a/bindings/bindings/rollup.go b/bindings/bindings/rollup.go index a3e5d0a88..8b52b5a21 100644 --- a/bindings/bindings/rollup.go +++ b/bindings/bindings/rollup.go @@ -40,17 +40,10 @@ type IRollupBatchDataInput struct { WithdrawalRoot [32]byte } -// IRollupBatchSignatureInput is an auto generated low-level Go binding around an user-defined struct. -type IRollupBatchSignatureInput struct { - SignedSequencersBitmap *big.Int - SequencerSets []byte - Signature []byte -} - // RollupMetaData contains all meta data concerning the Rollup contract. var RollupMetaData = &bind.MetaData{ - ABI: "[{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"_chainID\",\"type\":\"uint64\"}],\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"},{\"inputs\":[],\"name\":\"ErrZeroAddress\",\"type\":\"error\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"batchIndex\",\"type\":\"uint256\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"winner\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"string\",\"name\":\"res\",\"type\":\"string\"}],\"name\":\"ChallengeRes\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"receiver\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"ChallengeRewardClaim\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint64\",\"name\":\"batchIndex\",\"type\":\"uint64\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"challenger\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"challengeDeposit\",\"type\":\"uint256\"}],\"name\":\"ChallengeState\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"batchIndex\",\"type\":\"uint256\"},{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"batchHash\",\"type\":\"bytes32\"}],\"name\":\"CommitBatch\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"batchIndex\",\"type\":\"uint256\"},{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"batchHash\",\"type\":\"bytes32\"},{\"indexed\":false,\"internalType\":\"bytes32\",\"name\":\"stateRoot\",\"type\":\"bytes32\"},{\"indexed\":false,\"internalType\":\"bytes32\",\"name\":\"withdrawRoot\",\"type\":\"bytes32\"}],\"name\":\"FinalizeBatch\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint8\",\"name\":\"version\",\"type\":\"uint8\"}],\"name\":\"Initialized\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"previousOwner\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"newOwner\",\"type\":\"address\"}],\"name\":\"OwnershipTransferred\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address\",\"name\":\"account\",\"type\":\"address\"}],\"name\":\"Paused\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address\",\"name\":\"receiver\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"ProveRemainingClaimed\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"batchIndex\",\"type\":\"uint256\"},{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"batchHash\",\"type\":\"bytes32\"}],\"name\":\"RevertBatch\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address\",\"name\":\"account\",\"type\":\"address\"}],\"name\":\"Unpaused\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"account\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"bool\",\"name\":\"status\",\"type\":\"bool\"}],\"name\":\"UpdateChallenger\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"oldPeriod\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"newPeriod\",\"type\":\"uint256\"}],\"name\":\"UpdateFinalizationPeriodSeconds\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"oldPercent\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"newPercent\",\"type\":\"uint256\"}],\"name\":\"UpdateProofRewardPercent\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"oldWindow\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"newWindow\",\"type\":\"uint256\"}],\"name\":\"UpdateProofWindow\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"oldPeriod\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"newPeriod\",\"type\":\"uint256\"}],\"name\":\"UpdateRollupDelayPeriod\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"oldVerifier\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"newVerifier\",\"type\":\"address\"}],\"name\":\"UpdateVerifier\",\"type\":\"event\"},{\"inputs\":[],\"name\":\"LAYER_2_CHAIN_ID\",\"outputs\":[{\"internalType\":\"uint64\",\"name\":\"\",\"type\":\"uint64\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"__maxNumTxInChunk\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"_account\",\"type\":\"address\"}],\"name\":\"addChallenger\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"batchIndex\",\"type\":\"uint256\"}],\"name\":\"batchBlobVersionedHashes\",\"outputs\":[{\"internalType\":\"bytes32\",\"name\":\"blobVersionedHash\",\"type\":\"bytes32\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"owner\",\"type\":\"address\"}],\"name\":\"batchChallengeReward\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"batchChallenged\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"batchIndex\",\"type\":\"uint256\"}],\"name\":\"batchChallengedSuccess\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"batchIndex\",\"type\":\"uint256\"}],\"name\":\"batchDataStore\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"originTimestamp\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"finalizeTimestamp\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"blockNumber\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"signedSequencersBitmap\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"batchIndex\",\"type\":\"uint256\"}],\"name\":\"batchExist\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"batchIndex\",\"type\":\"uint256\"}],\"name\":\"batchInChallenge\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"batchIndex\",\"type\":\"uint256\"}],\"name\":\"batchInsideChallengeWindow\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"batchIndex\",\"type\":\"uint64\"},{\"internalType\":\"bytes32\",\"name\":\"_batchHash\",\"type\":\"bytes32\"}],\"name\":\"challengeState\",\"outputs\":[],\"stateMutability\":\"payable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"batchIndex\",\"type\":\"uint256\"}],\"name\":\"challenges\",\"outputs\":[{\"internalType\":\"uint64\",\"name\":\"batchIndex\",\"type\":\"uint64\"},{\"internalType\":\"address\",\"name\":\"challenger\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"challengeDeposit\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"startTime\",\"type\":\"uint256\"},{\"internalType\":\"bool\",\"name\":\"challengeSuccess\",\"type\":\"bool\"},{\"internalType\":\"bool\",\"name\":\"finished\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"receiver\",\"type\":\"address\"}],\"name\":\"claimProveRemaining\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"receiver\",\"type\":\"address\"}],\"name\":\"claimReward\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"internalType\":\"uint8\",\"name\":\"version\",\"type\":\"uint8\"},{\"internalType\":\"bytes\",\"name\":\"parentBatchHeader\",\"type\":\"bytes\"},{\"internalType\":\"uint64\",\"name\":\"lastBlockNumber\",\"type\":\"uint64\"},{\"internalType\":\"uint16\",\"name\":\"numL1Messages\",\"type\":\"uint16\"},{\"internalType\":\"bytes32\",\"name\":\"prevStateRoot\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32\",\"name\":\"postStateRoot\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32\",\"name\":\"withdrawalRoot\",\"type\":\"bytes32\"}],\"internalType\":\"structIRollup.BatchDataInput\",\"name\":\"batchDataInput\",\"type\":\"tuple\"},{\"components\":[{\"internalType\":\"uint256\",\"name\":\"signedSequencersBitmap\",\"type\":\"uint256\"},{\"internalType\":\"bytes\",\"name\":\"sequencerSets\",\"type\":\"bytes\"},{\"internalType\":\"bytes\",\"name\":\"signature\",\"type\":\"bytes\"}],\"internalType\":\"structIRollup.BatchSignatureInput\",\"name\":\"batchSignatureInput\",\"type\":\"tuple\"}],\"name\":\"commitBatch\",\"outputs\":[],\"stateMutability\":\"payable\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"internalType\":\"uint8\",\"name\":\"version\",\"type\":\"uint8\"},{\"internalType\":\"bytes\",\"name\":\"parentBatchHeader\",\"type\":\"bytes\"},{\"internalType\":\"uint64\",\"name\":\"lastBlockNumber\",\"type\":\"uint64\"},{\"internalType\":\"uint16\",\"name\":\"numL1Messages\",\"type\":\"uint16\"},{\"internalType\":\"bytes32\",\"name\":\"prevStateRoot\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32\",\"name\":\"postStateRoot\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32\",\"name\":\"withdrawalRoot\",\"type\":\"bytes32\"}],\"internalType\":\"structIRollup.BatchDataInput\",\"name\":\"batchDataInput\",\"type\":\"tuple\"},{\"components\":[{\"internalType\":\"uint256\",\"name\":\"signedSequencersBitmap\",\"type\":\"uint256\"},{\"internalType\":\"bytes\",\"name\":\"sequencerSets\",\"type\":\"bytes\"},{\"internalType\":\"bytes\",\"name\":\"signature\",\"type\":\"bytes\"}],\"internalType\":\"structIRollup.BatchSignatureInput\",\"name\":\"batchSignatureInput\",\"type\":\"tuple\"},{\"internalType\":\"bytes\",\"name\":\"_batchHeader\",\"type\":\"bytes\"},{\"internalType\":\"bytes\",\"name\":\"_batchProof\",\"type\":\"bytes\"}],\"name\":\"commitBatchWithProof\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"internalType\":\"uint8\",\"name\":\"version\",\"type\":\"uint8\"},{\"internalType\":\"bytes\",\"name\":\"parentBatchHeader\",\"type\":\"bytes\"},{\"internalType\":\"uint64\",\"name\":\"lastBlockNumber\",\"type\":\"uint64\"},{\"internalType\":\"uint16\",\"name\":\"numL1Messages\",\"type\":\"uint16\"},{\"internalType\":\"bytes32\",\"name\":\"prevStateRoot\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32\",\"name\":\"postStateRoot\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32\",\"name\":\"withdrawalRoot\",\"type\":\"bytes32\"}],\"internalType\":\"structIRollup.BatchDataInput\",\"name\":\"batchDataInput\",\"type\":\"tuple\"},{\"components\":[{\"internalType\":\"uint256\",\"name\":\"signedSequencersBitmap\",\"type\":\"uint256\"},{\"internalType\":\"bytes\",\"name\":\"sequencerSets\",\"type\":\"bytes\"},{\"internalType\":\"bytes\",\"name\":\"signature\",\"type\":\"bytes\"}],\"internalType\":\"structIRollup.BatchSignatureInput\",\"name\":\"batchSignatureInput\",\"type\":\"tuple\"}],\"name\":\"commitState\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"batchIndex\",\"type\":\"uint256\"}],\"name\":\"committedBatches\",\"outputs\":[{\"internalType\":\"bytes32\",\"name\":\"batchHash\",\"type\":\"bytes32\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"batchIndex\",\"type\":\"uint256\"}],\"name\":\"committedStateRoots\",\"outputs\":[{\"internalType\":\"bytes32\",\"name\":\"stateRoot\",\"type\":\"bytes32\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"finalizationPeriodSeconds\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes\",\"name\":\"_batchHeader\",\"type\":\"bytes\"}],\"name\":\"finalizeBatch\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"batchIndex\",\"type\":\"uint256\"}],\"name\":\"finalizedStateRoots\",\"outputs\":[{\"internalType\":\"bytes32\",\"name\":\"stateRoot\",\"type\":\"bytes32\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes\",\"name\":\"_batchHeader\",\"type\":\"bytes\"}],\"name\":\"importGenesisBatch\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"inChallenge\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"_l1StakingContract\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"_messageQueue\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"_verifier\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"_finalizationPeriodSeconds\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"_proofWindow\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"_proofRewardPercent\",\"type\":\"uint256\"}],\"name\":\"initialize\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"_prevStateRoot\",\"type\":\"bytes32\"}],\"name\":\"initialize2\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"_rollupDelayPeriod\",\"type\":\"uint256\"}],\"name\":\"initialize3\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"_batchIndex\",\"type\":\"uint256\"}],\"name\":\"isBatchFinalized\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"challengerAddress\",\"type\":\"address\"}],\"name\":\"isChallenger\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"isChallenger\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"l1StakingContract\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"lastCommittedBatchIndex\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"lastFinalizedBatchIndex\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"messageQueue\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"owner\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"paused\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"proofRewardPercent\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"proofWindow\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes\",\"name\":\"_batchHeader\",\"type\":\"bytes\"},{\"internalType\":\"bytes\",\"name\":\"_batchProof\",\"type\":\"bytes\"}],\"name\":\"proveCommittedBatchState\",\"outputs\":[],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"proveRemaining\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes\",\"name\":\"_batchHeader\",\"type\":\"bytes\"},{\"internalType\":\"bytes\",\"name\":\"_batchProof\",\"type\":\"bytes\"}],\"name\":\"proveState\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"_account\",\"type\":\"address\"}],\"name\":\"removeChallenger\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"renounceOwnership\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes\",\"name\":\"_batchHeader\",\"type\":\"bytes\"},{\"internalType\":\"uint256\",\"name\":\"_count\",\"type\":\"uint256\"}],\"name\":\"revertBatch\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"revertReqIndex\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"rollupDelayPeriod\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bool\",\"name\":\"_status\",\"type\":\"bool\"}],\"name\":\"setPause\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"newOwner\",\"type\":\"address\"}],\"name\":\"transferOwnership\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"_newPeriod\",\"type\":\"uint256\"}],\"name\":\"updateFinalizePeriodSeconds\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"_newWindow\",\"type\":\"uint256\"}],\"name\":\"updateProofWindow\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"_newProofRewardPercent\",\"type\":\"uint256\"}],\"name\":\"updateRewardPercentage\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"_newPeriod\",\"type\":\"uint256\"}],\"name\":\"updateRollupDelayPeriod\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"_newVerifier\",\"type\":\"address\"}],\"name\":\"updateVerifier\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"verifier\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"withdrawalRoot\",\"type\":\"bytes32\"}],\"name\":\"withdrawalRoots\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"exist\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"stateMutability\":\"payable\",\"type\":\"receive\"}]", - Bin: "0x60a060405234801562000010575f80fd5b506040516200578d3803806200578d83398101604081905262000033916200010f565b6001600160401b0381166080526200004a62000051565b506200013e565b5f54610100900460ff1615620000bd5760405162461bcd60e51b815260206004820152602760248201527f496e697469616c697a61626c653a20636f6e747261637420697320696e697469604482015266616c697a696e6760c81b606482015260840160405180910390fd5b5f5460ff908116146200010d575f805460ff191660ff9081179091556040519081527f7f26b83ff96e1f2b6a682f133852f6798a09c465da95921460cefb38474024989060200160405180910390a15b565b5f6020828403121562000120575f80fd5b81516001600160401b038116811462000137575f80fd5b9392505050565b60805161562f6200015e5f395f81816106a20152614408015261562f5ff3fe608060405260043610610353575f3560e01c8063728cdbca116101bd578063b8d0a1b0116100f2578063d8dc99d211610092578063dff7827e1161006d578063dff7827e14610ac4578063e3fff1dd14610ad9578063f2fde38b14610af8578063fb1e8b0414610b17575f80fd5b8063d8dc99d214610a71578063ddd8a3dc14610a86578063de8b303514610aa5575f80fd5b8063cd4edc69116100cd578063cd4edc6914610a0b578063ce5db8d614610a1e578063cf9a674514610a33578063d279c19114610a52575f80fd5b8063b8d0a1b0146109ae578063bedb86fb146109cd578063c5553892146109ec575f80fd5b8063a479265d1161015d578063b31a77d311610138578063b31a77d31461093c578063b348442514610951578063b35dac4e14610970578063b3e0a5091461098f575f80fd5b8063a479265d146108dd578063a4f209b0146108f2578063abc8d68d14610911575f80fd5b80638f1d3776116101985780638f1d3776146107af578063910129d41461085f57806397fc007c14610890578063a415d8dc146108af575f80fd5b8063728cdbca1461075a57806388b1ea09146107795780638da5cb5b14610792575f80fd5b80632a213ba1116102935780635c975abb11610233578063612672901161020e57806361267290146106dd57806368589dfa146106fc5780636c578c1d14610727578063715018a614610746575f80fd5b80635c975abb1461061a5780635ef7a94a146106315780635f77cf1d14610691575f80fd5b80633e001b661161026e5780633e001b66146105b4578063428868b5146105c95780634e8f1d67146105dc57806357e0af6c146105fb575f80fd5b80632a213ba1146105335780632b7ac3f31461055e5780633b70c18a14610595575f80fd5b806313361101116102fe5780631e8825be116102d95780631e8825be1461049f57806321e2f9e0146104be5780632362f03e146104dd5780632571098d14610508575f80fd5b8063133611011461043b57806318463fb01461045a57806318af3b2b1461046f575f80fd5b806310d445831161032e57806310d44583146103e5578063116a1f4214610404578063121dcd5014610426575f80fd5b806304d772151461035e578063059def61146103a15780630ceb6780146103c4575f80fd5b3661035a57005b5f80fd5b348015610369575f80fd5b5061038c610378366004614e13565b60a36020525f908152604090205460ff1681565b60405190151581526020015b60405180910390f35b3480156103ac575f80fd5b506103b6609d5481565b604051908152602001610398565b3480156103cf575f80fd5b506103e36103de366004614e40565b610b2c565b005b3480156103f0575f80fd5b506103e36103ff366004614e9e565b610bfe565b34801561040f575f80fd5b5061038c61041e366004614e13565b609d54101590565b348015610431575f80fd5b506103b6609e5481565b348015610446575f80fd5b506103e3610455366004614ee6565b610f3a565b348015610465575f80fd5b506103b660a75481565b34801561047a575f80fd5b5061038c610489366004614e13565b5f90815260a26020526040902060010154421090565b3480156104aa575f80fd5b506103e36104b9366004614f45565b611434565b3480156104c9575f80fd5b5061038c6104d8366004614e13565b61181f565b3480156104e8575f80fd5b506103b66104f7366004614e13565b60a16020525f908152604090205481565b348015610513575f80fd5b506103b6610522366004614e13565b60a06020525f908152604090205481565b34801561053e575f80fd5b506103b661054d366004614e13565b60ad6020525f908152604090205481565b348015610569575f80fd5b50609c5461057d906001600160a01b031681565b6040516001600160a01b039091168152602001610398565b3480156105a0575f80fd5b50609b5461057d906001600160a01b031681565b3480156105bf575f80fd5b506103b660aa5481565b6103e36105d7366004614f45565b61184d565b3480156105e7575f80fd5b506103e36105f6366004614fa5565b611be8565b348015610606575f80fd5b506103e3610615366004614e13565b611f9c565b348015610625575f80fd5b5060655460ff1661038c565b34801561063c575f80fd5b5061067161064b366004614e13565b60a26020525f908152604090208054600182015460028301546003909301549192909184565b604080519485526020850193909352918301526060820152608001610398565b34801561069c575f80fd5b506106c47f000000000000000000000000000000000000000000000000000000000000000081565b60405167ffffffffffffffff9091168152602001610398565b3480156106e8575f80fd5b506103e36106f7366004614e13565b612047565b348015610707575f80fd5b506103b6610716366004614e13565b60ab6020525f908152604090205481565b348015610732575f80fd5b506103e3610741366004614e40565b612222565b348015610751575f80fd5b506103e36122df565b348015610765575f80fd5b506103e3610774366004615056565b6122f2565b348015610784575f80fd5b5060a65461038c9060ff1681565b34801561079d575f80fd5b506033546001600160a01b031661057d565b3480156107ba575f80fd5b5061081b6107c9366004614e13565b60a46020525f9081526040902080546001820154600283015460039093015467ffffffffffffffff831693680100000000000000009093046001600160a01b0316929060ff8082169161010090041686565b6040805167ffffffffffffffff90971687526001600160a01b03909516602087015293850192909252606084015215156080830152151560a082015260c001610398565b34801561086a575f80fd5b5061038c610879366004614e13565b5f90815260a4602052604090206003015460ff1690565b34801561089b575f80fd5b506103e36108aa366004614e40565b6125fb565b3480156108ba575f80fd5b5061038c6108c9366004614e40565b609f6020525f908152604090205460ff1681565b3480156108e8575f80fd5b506103b660995481565b3480156108fd575f80fd5b506103e361090c366004614e13565b6126de565b34801561091c575f80fd5b506103b661092b366004614e40565b60a56020525f908152604090205481565b348015610947575f80fd5b506103b660a85481565b34801561095c575f80fd5b506103e361096b3660046150b1565b61278e565b34801561097b575f80fd5b506103e361098a366004614e40565b612a92565b34801561099a575f80fd5b506103e36109a9366004614ee6565b612aeb565b3480156109b9575f80fd5b506103e36109c83660046150b1565b612e48565b3480156109d8575f80fd5b506103e36109e7366004615125565b612ed1565b3480156109f7575f80fd5b506103e3610a06366004614e13565b61300f565b6103e3610a19366004615157565b6130b2565b348015610a29575f80fd5b506103b660985481565b348015610a3e575f80fd5b506103e3610a4d366004614e13565b613644565b348015610a5d575f80fd5b506103e3610a6c366004614e40565b6137b3565b348015610a7c575f80fd5b506103b660ac5481565b348015610a91575f80fd5b5060975461057d906001600160a01b031681565b348015610ab0575f80fd5b5061038c610abf366004614e13565b613872565b348015610acf575f80fd5b506103b6609a5481565b348015610ae4575f80fd5b506103e3610af3366004614e13565b6138bc565b348015610b03575f80fd5b506103e3610b12366004614e40565b61395f565b348015610b22575f80fd5b506103b660a95481565b610b346139ec565b6001600160a01b0381165f908152609f602052604090205460ff1615610ba15760405162461bcd60e51b815260206004820152601f60248201527f6163636f756e7420697320616c72656164792061206368616c6c656e6765720060448201526064015b60405180910390fd5b6001600160a01b0381165f818152609f6020908152604091829020805460ff1916600190811790915591519182527f7feb653c7b1f0d23daeed494225b3f28851cdc8973fcc653866d9b6e205fc00991015b60405180910390a250565b610c066139ec565b5f8111610c555760405162461bcd60e51b815260206004820152601560248201527f636f756e74206d757374206265206e6f6e7a65726f00000000000000000000006044820152606401610b98565b5f80610c618585613a46565b915091505f610c74836001015160c01c90565b5f81815260a160205260409020549091508214610cd35760405162461bcd60e51b815260206004820152601460248201527f696e636f727265637420626174636820686173680000000000000000000000006044820152606401610b98565b5f60a181610ce187856151ac565b81526020019081526020015f205414610d615760405162461bcd60e51b8152602060048201526024808201527f726576657274696e67206d7573742073746172742066726f6d2074686520656e60448201527f64696e67000000000000000000000000000000000000000000000000000000006064820152608401610b98565b609d548111610dd85760405162461bcd60e51b815260206004820152602160248201527f63616e206f6e6c792072657665727420756e46696e616c697a6564206261746360448201527f68000000000000000000000000000000000000000000000000000000000000006064820152608401610b98565b610de36001826151bf565b609e555b8315610f3257604051829082907ecae2739091badfd91c373f0a16cede691e0cd25bb80cff77dd5caeb4710146905f90a35f81815260a16020526040812055610e2f81613872565b15610e8a575f81815260a460209081526040808320600181015490546801000000000000000090046001600160a01b0316845260a59092528220805491929091610e7a9084906151ac565b909155505060a6805460ff191690555b5f81815260a46020526040812080547fffffffff00000000000000000000000000000000000000000000000000000000168155600181018290556002810191909155600301805461ffff1916905560a85415801590610eea575060a85481145b15610ef4575f60a8555b6001015f81815260a160205260409020547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff90940193915081610de7575b505050505050565b60a85415610f8a5760405162461bcd60e51b815260206004820152601660248201527f70656e64696e67207265766572742072657175657374000000000000000000006044820152606401610b98565b610f92613aee565b5f80610f9e8484613a46565b915091505f610fb1836001015160c01c90565b5f81815260a1602052604090205490915082146110105760405162461bcd60e51b815260206004820152601460248201527f696e636f727265637420626174636820686173680000000000000000000000006044820152606401610b98565b6110198161181f565b6110655760405162461bcd60e51b815260206004820152600f60248201527f6261746368206e6f7420657869737400000000000000000000000000000000006044820152606401610b98565b61106e81613872565b156110bb5760405162461bcd60e51b815260206004820152601260248201527f626174636820696e206368616c6c656e676500000000000000000000000000006044820152606401610b98565b5f81815260a4602052604090206003015460ff161561111c5760405162461bcd60e51b815260206004820152601660248201527f62617463682073686f756c6420626520726576657274000000000000000000006044820152606401610b98565b5f81815260a2602052604090206001015442101561117c5760405162461bcd60e51b815260206004820152601960248201527f626174636820696e206368616c6c656e67652077696e646f77000000000000006044820152606401610b98565b605983015160a05f61118f6001856151bf565b81526020019081526020015f2054146111ea5760405162461bcd60e51b815260206004820152601d60248201527f696e636f72726563742070726576696f757320737461746520726f6f740000006044820152606401610b98565b5f81815260a06020526040902054156112455760405162461bcd60e51b815260206004820152601660248201527f626174636820616c7265616479207665726966696564000000000000000000006044820152606401610b98565b80609d54600101146112995760405162461bcd60e51b815260206004820152601560248201527f696e636f727265637420626174636820696e64657800000000000000000000006044820152606401610b98565b609d819055600160a35f6112ae866099015190565b815260208101919091526040015f20805460ff191691151591909117905560798301515f82815260a060205260409020556112fd6112f0846011015160c01c90565b600985015160c01c613b41565b60a25f61130b6001846151bf565b815260208101919091526040015f908120818155600180820183905560028201839055600390910182905560ab919061134490846151bf565b81526020019081526020015f205f905560ad5f60018361136491906151bf565b81526020019081526020015f205f905560a45f60018361138491906151bf565b815260208082019290925260409081015f90812080547fffffffff000000000000000000000000000000000000000000000000000000001681556001810182905560028101829055600301805461ffff1916905583815260a1909252902054817f26ba82f907317eedc97d0cbef23de76a43dd6edb563bdb6e9407645b950a7a2d611410866079015190565b60998701516040805192835260208301919091520160405180910390a35050505050565b6097546001600160a01b03166368015791336040517fffffffff0000000000000000000000000000000000000000000000000000000060e084901b1681526001600160a01b039091166004820152602401602060405180830381865afa1580156114a0573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906114c491906151d2565b6115105760405162461bcd60e51b815260206004820152601a60248201527f6f6e6c7920616374697665207374616b657220616c6c6f7765640000000000006044820152606401610b98565b60a854156115605760405162461bcd60e51b815260206004820152601660248201527f70656e64696e67207265766572742072657175657374000000000000000000006044820152606401610b98565b611568613aee565b5f49156115b75760405162461bcd60e51b815260206004820152601f60248201527f636f6d6d69745374617465206d757374206e6f7420636172727920626c6f62006044820152606401610b98565b5f6115cd6115c860208501856151ed565b613a46565b5090505f6115df826001015160c01c90565b6115ea9060016151ac565b5f81815260ad602052604090205490915061166d5760405162461bcd60e51b815260206004820152602260248201527f6e6f2073746f72656420626c6f62206861736820666f7220746869732062617460448201527f63680000000000000000000000000000000000000000000000000000000000006064820152608401610b98565b60ac54609b54604080517fb59b1a7800000000000000000000000000000000000000000000000000000000815290514293926001600160a01b03169163b59b1a789160048083019260209291908290030181865afa1580156116d1573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906116f5919061524e565b6116ff91906151ac565b1015611767575f6117166080860160608701615265565b61ffff16116117675760405162461bcd60e51b815260206004820152600b60248201527f6c316d73672064656c61790000000000000000000000000000000000000000006044820152606401610b98565b6097545f906001600160a01b031663d096c3c6336040517fffffffff0000000000000000000000000000000000000000000000000000000060e084901b1681526001600160a01b039091166004820152602401602060405180830381865afa1580156117d5573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906117f9919061524e565b905061181885858360ad5f8781526020019081526020015f2054613bfe565b5050505050565b5f81815260a260205260408120541580159061184757505f82815260a1602052604090205415155b92915050565b6097546001600160a01b03166368015791336040517fffffffff0000000000000000000000000000000000000000000000000000000060e084901b1681526001600160a01b039091166004820152602401602060405180830381865afa1580156118b9573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906118dd91906151d2565b6119295760405162461bcd60e51b815260206004820152601a60248201527f6f6e6c7920616374697665207374616b657220616c6c6f7765640000000000006044820152606401610b98565b60a854156119795760405162461bcd60e51b815260206004820152601660248201527f70656e64696e67207265766572742072657175657374000000000000000000006044820152606401610b98565b611981613aee565b5f6119926115c860208501856151ed565b5090505f6119a4826001015160c01c90565b6119af9060016151ac565b5f81815260ad602052604090205490915015611a335760405162461bcd60e51b815260206004820152602860248201527f636f6d6d69744261746368207265717569726573206e6f2073746f726564206260448201527f6c6f6220686173680000000000000000000000000000000000000000000000006064820152608401610b98565b60ac54609b54604080517fb59b1a7800000000000000000000000000000000000000000000000000000000815290514293926001600160a01b03169163b59b1a789160048083019260209291908290030181865afa158015611a97573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611abb919061524e565b611ac591906151ac565b1015611b2d575f611adc6080860160608701615265565b61ffff1611611b2d5760405162461bcd60e51b815260206004820152600b60248201527f6c316d73672064656c61790000000000000000000000000000000000000000006044820152606401610b98565b6097545f906001600160a01b031663d096c3c6336040517fffffffff0000000000000000000000000000000000000000000000000000000060e084901b1681526001600160a01b039091166004820152602401602060405180830381865afa158015611b9b573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611bbf919061524e565b90505f611bda611bd26020880188615286565b60ff1661425e565b9050610f3286868484613bfe565b60a85415611c385760405162461bcd60e51b815260206004820152601660248201527f70656e64696e67207265766572742072657175657374000000000000000000006044820152606401610b98565b611c40613aee565b60ac54609e545f90815260a2602052604081205490914291611c6291906151ac565b1090505f4260ac54609b5f9054906101000a90046001600160a01b03166001600160a01b031663b59b1a786040518163ffffffff1660e01b8152600401602060405180830381865afa158015611cba573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611cde919061524e565b611ce891906151ac565b10905081158015611cf65750805b15611d5d575f611d0c60808a0160608b01615265565b61ffff1611611d5d5760405162461bcd60e51b815260206004820152600b60248201527f6c316d73672064656c61790000000000000000000000000000000000000000006044820152606401610b98565b8180611d665750805b611db25760405162461bcd60e51b815260206004820152600e60248201527f696e76616c69642074696d696e670000000000000000000000000000000000006044820152606401610b98565b5f611dc36115c860208b018b6151ed565b5090505f611dd5826001015160c01c90565b611de09060016151ac565b5f81815260ad60205260408120549192509015611e80575f4915611e6c5760405162461bcd60e51b815260206004820152602f60248201527f6d757374206e6f7420636172727920626c6f62207768656e207573696e67207360448201527f746f72656420626c6f62206861736800000000000000000000000000000000006064820152608401610b98565b505f81815260ad6020526040902054611e93565b611e90611bd260208d018d615286565b90505b611e9f8b8b5f84613bfe565b5f80611eab8b8b613a46565b915091505f611ebe836001015160c01c90565b905080609e5414611f115760405162461bcd60e51b815260206004820152601660248201527f696e636f727265637420626174636820686561646572000000000000000000006044820152606401610b98565b5f81815260a160205260409020548214611f6d5760405162461bcd60e51b815260206004820152601460248201527f696e636f727265637420626174636820686173680000000000000000000000006044820152606401610b98565b5f81815260a26020526040902042600190910155611f8c838b8b614399565b5050505050505050505050505050565b611fa46139ec565b5f81118015611fb557506099548114155b6120015760405162461bcd60e51b815260206004820152601860248201527f696e76616c6964206e65772070726f6f662077696e646f7700000000000000006044820152606401610b98565b609980549082905560408051828152602081018490527f1e3a2094feb4b696dd3d7caea38ad2f41dbdcac3fa3943c7a693aff8a64b0a6191015b60405180910390a15050565b5f54600290610100900460ff1615801561206757505f5460ff8083169116105b6120d95760405162461bcd60e51b815260206004820152602e60248201527f496e697469616c697a61626c653a20636f6e747261637420697320616c72656160448201527f647920696e697469616c697a65640000000000000000000000000000000000006064820152608401610b98565b5f805461ffff191660ff8316176101001790556120f75f5460ff1690565b60ff166002146121495760405162461bcd60e51b815260206004820152601660248201527f6d757374206861766520696e697469616c697a656421000000000000000000006044820152606401610b98565b816121bc5760405162461bcd60e51b815260206004820152602760248201527f63616e206e6f742073657420737461746520726f6f742077697468206279746560448201527f73333228302921000000000000000000000000000000000000000000000000006064820152608401610b98565b609e545f90815260ab60205260409020546121e557609e545f90815260ab602052604090208290555b5f805461ff001916905560405160ff821681527f7f26b83ff96e1f2b6a682f133852f6798a09c465da95921460cefb38474024989060200161203b565b61222a6139ec565b6001600160a01b0381165f908152609f602052604090205460ff166122915760405162461bcd60e51b815260206004820152601b60248201527f6163636f756e74206973206e6f742061206368616c6c656e67657200000000006044820152606401610b98565b6001600160a01b0381165f818152609f60209081526040808320805460ff19169055519182527f7feb653c7b1f0d23daeed494225b3f28851cdc8973fcc653866d9b6e205fc0099101610bf3565b6122e76139ec565b6122f05f61454c565b565b5f54610100900460ff161580801561231057505f54600160ff909116105b806123295750303b15801561232957505f5460ff166001145b61239b5760405162461bcd60e51b815260206004820152602e60248201527f496e697469616c697a61626c653a20636f6e747261637420697320616c72656160448201527f647920696e697469616c697a65640000000000000000000000000000000000006064820152608401610b98565b5f805460ff1916600117905580156123bc575f805461ff0019166101001790555b6001600160a01b03861615806123d957506001600160a01b038516155b15612410576040517fecc6fdf000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6001600160a01b0387166124665760405162461bcd60e51b815260206004820152601b60248201527f696e76616c6964206c31207374616b696e6720636f6e747261637400000000006044820152606401610b98565b61246e6145b5565b612476614639565b609780546001600160a01b03808a167fffffffffffffffffffffffff000000000000000000000000000000000000000092831617909255609b8054898416908316179055609c805492881692909116821790556098859055609984905560a98390556040515f907f728af3d16a5760405e27a082c98ab272e9f0a1d02f0085d41532a26093aedd96908290a3604080515f8152602081018690527fa577f4223f91f74e2dad65bbb8c30807587ae95d0d34288057bb3ec0d398a437910160405180910390a1604080515f8152602081018590527f1e3a2094feb4b696dd3d7caea38ad2f41dbdcac3fa3943c7a693aff8a64b0a61910160405180910390a1604080515f8152602081018490527ffb81bce17f015797e11949d3c332e2bf9453faf68f728447426803138f2b0223910160405180910390a180156125f2575f805461ff0019169055604051600181527f7f26b83ff96e1f2b6a682f133852f6798a09c465da95921460cefb38474024989060200160405180910390a15b50505050505050565b6126036139ec565b6001600160a01b038116158015906126295750609c546001600160a01b03828116911614155b6126755760405162461bcd60e51b815260206004820152601460248201527f696e76616c6964206e65772076657269666965720000000000000000000000006044820152606401610b98565b609c80546001600160a01b038381167fffffffffffffffffffffffff0000000000000000000000000000000000000000831681179093556040519116919082907f728af3d16a5760405e27a082c98ab272e9f0a1d02f0085d41532a26093aedd96905f90a35050565b6126e66139ec565b5f811180156126f6575060648111155b8015612704575060a9548114155b6127505760405162461bcd60e51b815260206004820152601f60248201527f696e76616c69642070726f6f66207265776172642070657263656e74616765006044820152606401610b98565b60a980549082905560408051828152602081018490527ffb81bce17f015797e11949d3c332e2bf9453faf68f728447426803138f2b0223910161203b565b60a854156127de5760405162461bcd60e51b815260206004820152601660248201527f70656e64696e67207265766572742072657175657374000000000000000000006044820152606401610b98565b6127e6613aee565b6097546001600160a01b03166368015791336040517fffffffff0000000000000000000000000000000000000000000000000000000060e084901b1681526001600160a01b039091166004820152602401602060405180830381865afa158015612852573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061287691906151d2565b6128c25760405162461bcd60e51b815260206004820152601a60248201527f6f6e6c7920616374697665207374616b657220616c6c6f7765640000000000006044820152606401610b98565b5f806128ce8686613a46565b915091505f6128e1836001015160c01c90565b5f81815260a1602052604090205490915082146129405760405162461bcd60e51b815260206004820152601460248201527f696e636f727265637420626174636820686173680000000000000000000000006044820152606401610b98565b61294981613872565b6129955760405162461bcd60e51b815260206004820152601260248201527f626174636820696e206368616c6c656e676500000000000000000000000000006044820152606401610b98565b5f81815260a46020526040902060038101805461ff00191661010017905560a6805460ff1916905560995460029091015442916129d1916151ac565b11612a47575f81815260a4602090815260408083206003908101805460ff1916600117905560a2835292819020909201548251808401909352600783527f54696d656f75740000000000000000000000000000000000000000000000000091830191909152612a42918391906146bd565b6125f2565b612a52838686614399565b6125f281336040518060400160405280600d81526020017f50726f6f66207375636365737300000000000000000000000000000000000000815250614819565b612a9a6139ec565b60aa80545f909155612aac82826148e5565b604080516001600160a01b0384168152602081018390527fb1b2058a6969e2d25e47bcaebe8ae21c29a23b2752429315b75e2f4f285f3d87910161203b565b612af36139ec565b5f805260a06020527fb84a74ec6ef4d0e83b6006dfaa014ab4026f9f3b97d186e604d29998a4e808ea5415612b6a5760405162461bcd60e51b815260206004820152601660248201527f67656e6573697320626174636820696d706f72746564000000000000000000006044820152606401610b98565b5f80612b768484613a46565b915091505f612b89836001015160c01c90565b90508015612bd95760405162461bcd60e51b815260206004820152601360248201527f696e76616c696420626174636820696e646578000000000000000000000000006044820152606401610b98565b5f612be5846079015190565b905080612c345760405162461bcd60e51b815260206004820152600f60248201527f7a65726f20737461746520726f6f7400000000000000000000000000000000006044820152606401610b98565b600984015160c01c15612c895760405162461bcd60e51b815260206004820152601d60248201527f6c31206d65737361676520706f707065642073686f756c6420626520300000006044820152606401610b98565b5f612c95856019015190565b03612ce25760405162461bcd60e51b815260206004820152600e60248201527f7a65726f206461746120686173680000000000000000000000000000000000006044820152606401610b98565b7f010657f37554c781402a22917dee2f75def7ab966d7b770905398eba3c444014612d0e856039015190565b14612d5b5760405162461bcd60e51b815260206004820152601660248201527f696e76616c69642076657273696f6e65642068617368000000000000000000006044820152606401610b98565b5f82815260a1602090815260408083208690558051608081018252428082528184019081528183018581526060830186815288875260a28652848720935184559151600184015551600283015551600390910155603987015160ad83528184205560ab825280832084905560a0909152808220839055609e849055609d84905551849184917f2c32d4ae151744d0bf0b9464a3e897a1d17ed2f1af71f7c9a75f12ce0d28238f9190a3604080518281525f6020820152849184917f26ba82f907317eedc97d0cbef23de76a43dd6edb563bdb6e9407645b950a7a2d910160405180910390a3505050505050565b5f80612e548686613a46565b915091505f612e67836001015160c01c90565b5f81815260a160205260409020549091508214612ec65760405162461bcd60e51b815260206004820152601460248201527f696e636f727265637420626174636820686173680000000000000000000000006044820152606401610b98565b6125f2838686614399565b612ed96139ec565b8015612fdd57612ee7614990565b60a65460ff1615612f9c5760a7545f90815260a460209081526040808320600181015490546801000000000000000090046001600160a01b0316845260a59092528220805491929091612f3b9084906151ac565b909155505060a7545f90815260a46020526040812080547fffffffff00000000000000000000000000000000000000000000000000000000168155600181018290556002810191909155600301805461ffff1916905560a6805460ff191690555b7f62e78cea01bee320cd4e420270b5ea74000d11b0c9f74754ebdbfc544b05a258335b6040516001600160a01b03909116815260200160405180910390a150565b612fe56149ea565b7f5db9ee0a495bf2e6ff9c91a7834c1ba4fdd244a5e8aa4e537bd38aeae4b073aa33612fbf565b50565b6130176139ec565b5f81118015613028575060ac548114155b6130745760405162461bcd60e51b815260206004820152601f60248201527f696e76616c6964206e657720726f6c6c75702064656c617920706572696f64006044820152606401610b98565b60ac80549082905560408051828152602081018490527f624e47dc9fb8f8cfeaf4ead4710277cc1757136cfa885e465514cf6d510f0ad1910161203b565b335f908152609f602052604090205460ff166131105760405162461bcd60e51b815260206004820152601760248201527f6f6e6c79206368616c6c656e67657220616c6c6f7765640000000000000000006044820152606401610b98565b60a854156131605760405162461bcd60e51b815260206004820152601660248201527f70656e64696e67207265766572742072657175657374000000000000000000006044820152606401610b98565b613168613aee565b60a65460ff16156131bb5760405162461bcd60e51b815260206004820152601460248201527f616c726561647920696e206368616c6c656e67650000000000000000000000006044820152606401610b98565b8167ffffffffffffffff16609d54106132165760405162461bcd60e51b815260206004820152601760248201527f626174636820616c72656164792066696e616c697a65640000000000000000006044820152606401610b98565b67ffffffffffffffff82165f90815260a16020526040902054811461327d5760405162461bcd60e51b815260206004820152601460248201527f696e636f727265637420626174636820686173680000000000000000000000006044820152606401610b98565b6132908267ffffffffffffffff1661181f565b6132dc5760405162461bcd60e51b815260206004820152600f60248201527f6261746368206e6f7420657869737400000000000000000000000000000000006044820152606401610b98565b67ffffffffffffffff82165f90815260a460205260409020546801000000000000000090046001600160a01b0316156133575760405162461bcd60e51b815260206004820152601860248201527f626174636820616c7265616479206368616c6c656e67656400000000000000006044820152606401610b98565b67ffffffffffffffff82165f90815260a2602052604090206001015442106133e75760405162461bcd60e51b815260206004820152603360248201527f63616e6e6f74206368616c6c656e6765206261746368206f757473696465207460448201527f6865206368616c6c656e67652077696e646f77000000000000000000000000006064820152608401610b98565b60975f9054906101000a90046001600160a01b03166001600160a01b0316630d13fd7b6040518163ffffffff1660e01b8152600401602060405180830381865afa158015613437573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061345b919061524e565b3410156134aa5760405162461bcd60e51b815260206004820152601260248201527f696e73756666696369656e742076616c756500000000000000000000000000006044820152606401610b98565b67ffffffffffffffff82811660a78190556040805160c0810182528281523360208083018281523484860190815242606086019081525f6080870181815260a0880182815299825260a4909552969096209451855492516001600160a01b031668010000000000000000027fffffffff000000000000000000000000000000000000000000000000000000009093169816979097171783559451600183015591516002820155925160039093018054925115156101000261ff00199415159490941661ffff19909316929092179290921790556001600160a01b03168267ffffffffffffffff167f3a6ea19df25b49e7624e313ce7c1ab23984238e93727260db56a81735b1b9976346040516135c291815260200190565b60405180910390a35f609d5460016135da91906151ac565b90505b609e548111613632578267ffffffffffffffff168114613620576099545f82815260a260205260408120600101805490919061361a9084906151ac565b90915550505b8061362a816152a6565b9150506135dd565b505060a6805460ff1916600117905550565b5f54600390610100900460ff1615801561366457505f5460ff8083169116105b6136d65760405162461bcd60e51b815260206004820152602e60248201527f496e697469616c697a61626c653a20636f6e747261637420697320616c72656160448201527f647920696e697469616c697a65640000000000000000000000000000000000006064820152608401610b98565b5f805461ffff191660ff8316176101001781558290036137385760405162461bcd60e51b815260206004820152601b60248201527f696e76616c696420726f6c6c75702064656c617920706572696f6400000000006044820152606401610b98565b60ac829055604080515f8152602081018490527f624e47dc9fb8f8cfeaf4ead4710277cc1757136cfa885e465514cf6d510f0ad1910160405180910390a15f805461ff001916905560405160ff821681527f7f26b83ff96e1f2b6a682f133852f6798a09c465da95921460cefb38474024989060200161203b565b335f90815260a56020526040812054908190036138125760405162461bcd60e51b815260206004820152601c60248201527f696e76616c69642062617463684368616c6c656e6765526577617264000000006044820152606401610b98565b335f90815260a5602052604081205561382b82826148e5565b816001600160a01b03167f9c25fa83f414ed363c8d39c98fb3e17567b3431cede71eb062c49d2a63ce247a8260405161386691815260200190565b60405180910390a25050565b5f81815260a460205260408120546801000000000000000090046001600160a01b0316158015906118475750505f90815260a46020526040902060030154610100900460ff161590565b6138c46139ec565b5f811180156138d557506098548114155b6139215760405162461bcd60e51b815260206004820152601b60248201527f696e76616c6964206e65772066696e616c697a6520706572696f6400000000006044820152606401610b98565b609880549082905560408051828152602081018490527fa577f4223f91f74e2dad65bbb8c30807587ae95d0d34288057bb3ec0d398a437910161203b565b6139676139ec565b6001600160a01b0381166139e35760405162461bcd60e51b815260206004820152602660248201527f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160448201527f64647265737300000000000000000000000000000000000000000000000000006064820152608401610b98565b61300c8161454c565b6033546001600160a01b031633146122f05760405162461bcd60e51b815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e65726044820152606401610b98565b5f805f613a538585614a23565b90505f8160ff165f03613a7457613a6a8686614a93565b9094509050613ae0565b8160ff1660011480613a8957508160ff166002145b15613a9857613a6a8686614afc565b60405162461bcd60e51b815260206004820152601960248201527f556e737570706f727465642062617463682076657273696f6e000000000000006044820152606401610b98565b808420925050509250929050565b60655460ff16156122f05760405162461bcd60e51b815260206004820152601060248201527f5061757361626c653a20706175736564000000000000000000000000000000006044820152606401610b98565b805f03613b4c575050565b8082035f5b82811015613bf857610100818403811115613b6b57508083035b609b546040517f3c7f528300000000000000000000000000000000000000000000000000000000815260048101859052602481018390526001600160a01b0390911690633c7f5283906044015f604051808303815f87803b158015613bce575f80fd5b505af1158015613be0573d5f803e3d5ffd5b50505050610100830192505061010081019050613b51565b50505050565b6002613c0d6020860186615286565b60ff161115613c5e5760405162461bcd60e51b815260206004820152600f60248201527f696e76616c69642076657273696f6e00000000000000000000000000000000006044820152606401610b98565b6080840135613caf5760405162461bcd60e51b815260206004820152601b60248201527f70726576696f757320737461746520726f6f74206973207a65726f00000000006044820152606401610b98565b60a0840135613d005760405162461bcd60e51b815260206004820152601660248201527f6e657720737461746520726f6f74206973207a65726f000000000000000000006044820152606401610b98565b5f80613d126115c860208801886151ed565b915091505f613d25836001015160c01c90565b90505f60a181613d368460016151ac565b81526020019081526020015f205414613d915760405162461bcd60e51b815260206004820152601760248201527f626174636820616c726561647920636f6d6d69747465640000000000000000006044820152606401610b98565b609e548114613de25760405162461bcd60e51b815260206004820152601560248201527f696e636f727265637420626174636820696e64657800000000000000000000006044820152606401610b98565b5f81815260a160205260409020548214613e3e5760405162461bcd60e51b815260206004820152601b60248201527f696e636f727265637420706172656e74206261746368206861736800000000006044820152606401610b98565b5f81815260ab6020526040902054608088013514613e9e5760405162461bcd60e51b815260206004820152601d60248201527f696e636f72726563742070726576696f757320737461746520726f6f740000006044820152606401610b98565b5f613ead846011015160c01c90565b90505f613ed9613ec360608b0160408c016152dd565b613ed360808c0160608d01615265565b84614b4f565b9050613eeb60808a0160608b01615265565b60019384019361ffff91909116929092019160f990613f0d60208c018c615286565b60ff1610613f1a57506101015b604080518281019091529550613f3f86613f3760208d018d615286565b60ff16614b93565b60c084901b6001870152613f6d86613f5d60808d0160608e01615265565b61ffff1660c01b60099190910152565b60c083811b6011880152601987018390526039870188905260808b0135605988015260a08b013560798801528a01356099870152613fd186613fb260208c018c6151ed565b604051613fc09291906152f6565b604051809103902060b99190910152565b60d986018590526001613fe760208c018c615286565b60ff1610614019576140198661400360608d0160408e016152dd565b67ffffffffffffffff1660c01b60f99190910152565b8086205f85815260a1602090815260408083209390935560ab815282822060a08e0135905560ad905290812088905560a65460ff16156140825760a7545f90815260a460205260409020600201546099544291614075916151ac565b61407f91906151bf565b90505b604051806080016040528042815260200182609854426140a291906151ac565b6140ac91906151ac565b81526020016140c160608e0160408f016152dd565b67ffffffffffffffff16815260209081018b90525f87815260a2825260409081902083518155838301516001820155908301516002820155606090920151600390920191909155609e8690556097546001600160a01b031692506374fe27b791508a359061413b90614135908d018d6151ed565b5f614b9a565b5f61414960408e018e6151ed565b6040518663ffffffff1660e01b815260040161416995949392919061534c565b602060405180830381865afa158015614184573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906141a891906151d2565b61421a5760405162461bcd60e51b815260206004820152602160248201527f746865207369676e617475726520766572696669636174696f6e206661696c6560448201527f64000000000000000000000000000000000000000000000000000000000000006064820152608401610b98565b5f83815260a16020526040808220549051909185917f2c32d4ae151744d0bf0b9464a3e897a1d17ed2f1af71f7c9a75f12ce0d28238f9190a3505050505050505050565b5f816002036142ed575f6040515f5b80498061427a5750614289565b6020820283015260010161426d565b6020810280832092016040529092509050806142e75760405162461bcd60e51b815260206004820152601b60248201527f5632207265717569726573206174206c65617374203120626c6f6200000000006044820152606401610b98565b50919050565b600149156143635760405162461bcd60e51b815260206004820152602560248201527f6c6567616379206261746368657320737570706f72742065786163746c79203160448201527f20626c6f620000000000000000000000000000000000000000000000000000006064820152608401610b98565b5f4915614371575f49611847565b507f010657f37554c781402a22917dee2f75def7ab966d7b770905398eba3c4440145b919050565b806143e65760405162461bcd60e51b815260206004820152601360248201527f496e76616c69642062617463682070726f6f66000000000000000000000000006044820152606401610b98565b5f6143f5846001015160c01c90565b90505f614403856039015190565b90505f7f0000000000000000000000000000000000000000000000000000000000000000614432876059015190565b6079880151609989015160b98a015160198b015160405160c09690961b7fffffffffffffffff000000000000000000000000000000000000000000000000166020870152602886019490945260488501929092526068840152608883015260a882015260c8810183905260e801604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529190528051602090910120609c549091506001600160a01b0316632c09a8486144f4885160f81c90565b858888866040518663ffffffff1660e01b81526004016145189594939291906153bf565b5f6040518083038186803b15801561452e575f80fd5b505afa158015614540573d5f803e3d5ffd5b50505050505050505050565b603380546001600160a01b038381167fffffffffffffffffffffffff0000000000000000000000000000000000000000831681179093556040519116919082907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0905f90a35050565b5f54610100900460ff166146315760405162461bcd60e51b815260206004820152602b60248201527f496e697469616c697a61626c653a20636f6e7472616374206973206e6f74206960448201527f6e697469616c697a696e670000000000000000000000000000000000000000006064820152608401610b98565b6122f0614bee565b5f54610100900460ff166146b55760405162461bcd60e51b815260206004820152602b60248201527f496e697469616c697a61626c653a20636f6e7472616374206973206e6f74206960448201527f6e697469616c697a696e670000000000000000000000000000000000000000006064820152608401610b98565b6122f0614c76565b60a88390555f83815260a460205260408082205460975491517f45bc4d1000000000000000000000000000000000000000000000000000000000815260048101869052680100000000000000009091046001600160a01b03908116939216906345bc4d10906024016020604051808303815f875af1158015614741573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190614765919061524e565b5f86815260a460205260409020600101549091506147849082906151ac565b5f86815260a460209081526040808320546801000000000000000090046001600160a01b0316835260a5909152812080549091906147c39084906151ac565b90915550506040516147d69084906153f0565b604051908190038120906001600160a01b0384169087907fe70d3820e244d5f71d1a6395db24f3460e8dca966edc1fd3625b6292880a877a905f90a45050505050565b5f83815260a4602052604081206001015460a95490919060649061483d908461541c565b6148479190615433565b905061485381836151bf565b60aa5f82825461486391906151ac565b90915550506001600160a01b0384165f90815260a560205260408120805483929061488f9084906151ac565b90915550506040516148a29084906153f0565b604051908190038120906001600160a01b0386169087907fe70d3820e244d5f71d1a6395db24f3460e8dca966edc1fd3625b6292880a877a905f90a45050505050565b801561498c575f826001600160a01b0316826040515f6040518083038185875af1925050503d805f8114614934576040519150601f19603f3d011682016040523d82523d5f602084013e614939565b606091505b505090508061498a5760405162461bcd60e51b815260206004820152601b60248201527f526f6c6c75703a20455448207472616e73666572206661696c656400000000006044820152606401610b98565b505b5050565b614998613aee565b6065805460ff191660011790557f62e78cea01bee320cd4e420270b5ea74000d11b0c9f74754ebdbfc544b05a2586149cd3390565b6040516001600160a01b03909116815260200160405180910390a1565b6149f2614cfb565b6065805460ff191690557f5db9ee0a495bf2e6ff9c91a7834c1ba4fdd244a5e8aa4e537bd38aeae4b073aa336149cd565b5f81614a715760405162461bcd60e51b815260206004820152601260248201527f456d7074792062617463682068656164657200000000000000000000000000006044820152606401610b98565b82825f818110614a8357614a8361546b565b919091013560f81c949350505050565b5f8160f9811015614ae65760405162461bcd60e51b815260206004820152601d60248201527f626174636820686561646572206c656e67746820746f6f20736d616c6c0000006044820152606401610b98565b6040519150808483378082016040529250929050565b5f816101018114614ae65760405162461bcd60e51b815260206004820181905260248201527f626174636820686561646572206c656e67746820697320696e636f72726563746044820152606401610b98565b6040805160c085901b815260f084901b6008820152600a60208502820181019092525f918101614b848161ffff871686614d4d565b82900390912095945050505050565b8082535050565b60605f80808080614bad888a018a615585565b9550955095509550955050818710614bcb579450614be79350505050565b838710614bdf578295505050505050614be7565b509293505050505b9392505050565b5f54610100900460ff16614c6a5760405162461bcd60e51b815260206004820152602b60248201527f496e697469616c697a61626c653a20636f6e7472616374206973206e6f74206960448201527f6e697469616c697a696e670000000000000000000000000000000000000000006064820152608401610b98565b6065805460ff19169055565b5f54610100900460ff16614cf25760405162461bcd60e51b815260206004820152602b60248201527f496e697469616c697a61626c653a20636f6e7472616374206973206e6f74206960448201527f6e697469616c697a696e670000000000000000000000000000000000000000006064820152608401610b98565b6122f03361454c565b60655460ff166122f05760405162461bcd60e51b815260206004820152601460248201527f5061757361626c653a206e6f74207061757365640000000000000000000000006044820152606401610b98565b5f825f03614d5c575082614be7565b609b546001600160a01b03165f5b84811015614e09576040517fae453cd5000000000000000000000000000000000000000000000000000000008152600481018590525f906001600160a01b0384169063ae453cd590602401602060405180830381865afa158015614dd0573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190614df4919061524e565b87525060209095019460019384019301614d6a565b5093949350505050565b5f60208284031215614e23575f80fd5b5035919050565b80356001600160a01b0381168114614394575f80fd5b5f60208284031215614e50575f80fd5b614be782614e2a565b5f8083601f840112614e69575f80fd5b50813567ffffffffffffffff811115614e80575f80fd5b602083019150836020828501011115614e97575f80fd5b9250929050565b5f805f60408486031215614eb0575f80fd5b833567ffffffffffffffff811115614ec6575f80fd5b614ed286828701614e59565b909790965060209590950135949350505050565b5f8060208385031215614ef7575f80fd5b823567ffffffffffffffff811115614f0d575f80fd5b614f1985828601614e59565b90969095509350505050565b5f60e082840312156142e7575f80fd5b5f606082840312156142e7575f80fd5b5f8060408385031215614f56575f80fd5b823567ffffffffffffffff80821115614f6d575f80fd5b614f7986838701614f25565b93506020850135915080821115614f8e575f80fd5b50614f9b85828601614f35565b9150509250929050565b5f805f805f8060808789031215614fba575f80fd5b863567ffffffffffffffff80821115614fd1575f80fd5b614fdd8a838b01614f25565b97506020890135915080821115614ff2575f80fd5b614ffe8a838b01614f35565b96506040890135915080821115615013575f80fd5b61501f8a838b01614e59565b90965094506060890135915080821115615037575f80fd5b5061504489828a01614e59565b979a9699509497509295939492505050565b5f805f805f8060c0878903121561506b575f80fd5b61507487614e2a565b955061508260208801614e2a565b945061509060408801614e2a565b9350606087013592506080870135915060a087013590509295509295509295565b5f805f80604085870312156150c4575f80fd5b843567ffffffffffffffff808211156150db575f80fd5b6150e788838901614e59565b909650945060208701359150808211156150ff575f80fd5b5061510c87828801614e59565b95989497509550505050565b801515811461300c575f80fd5b5f60208284031215615135575f80fd5b8135614be781615118565b803567ffffffffffffffff81168114614394575f80fd5b5f8060408385031215615168575f80fd5b61517183615140565b946020939093013593505050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601160045260245ffd5b808201808211156118475761184761517f565b818103818111156118475761184761517f565b5f602082840312156151e2575f80fd5b8151614be781615118565b5f8083357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe1843603018112615220575f80fd5b83018035915067ffffffffffffffff82111561523a575f80fd5b602001915036819003821315614e97575f80fd5b5f6020828403121561525e575f80fd5b5051919050565b5f60208284031215615275575f80fd5b813561ffff81168114614be7575f80fd5b5f60208284031215615296575f80fd5b813560ff81168114614be7575f80fd5b5f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff82036152d6576152d661517f565b5060010190565b5f602082840312156152ed575f80fd5b614be782615140565b818382375f9101908152919050565b81835281816020850137505f602082840101525f60207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f840116840101905092915050565b5f6080820187835260206080602085015281885180845260a08601915060208a0193505f5b818110156153965784516001600160a01b031683529383019391830191600101615371565b505087604086015284810360608601526153b1818789615305565b9a9950505050505050505050565b858152846020820152608060408201525f6153de608083018587615305565b90508260608301529695505050505050565b5f82515f5b8181101561540f57602081860181015185830152016153f5565b505f920191825250919050565b80820281158282048414176118475761184761517f565b5f82615466577f4e487b71000000000000000000000000000000000000000000000000000000005f52601260045260245ffd5b500490565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52603260045260245ffd5b7f4e487b71000000000000000000000000000000000000000000000000000000005f52604160045260245ffd5b5f82601f8301126154d4575f80fd5b8135602067ffffffffffffffff808311156154f1576154f1615498565b8260051b6040517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0603f8301168101818110848211171561553457615534615498565b6040529384526020818701810194908101925087851115615553575f80fd5b6020870191505b8482101561557a5761556b82614e2a565b8352918301919083019061555a565b979650505050505050565b5f805f805f8060c0878903121561559a575f80fd5b86359550602087013567ffffffffffffffff808211156155b8575f80fd5b6155c48a838b016154c5565b96506040890135955060608901359150808211156155e0575f80fd5b6155ec8a838b016154c5565b94506080890135935060a0890135915080821115615608575f80fd5b5061561589828a016154c5565b915050929550929550929556fea164736f6c6343000818000a", + ABI: "[{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"_chainID\",\"type\":\"uint64\"}],\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"},{\"inputs\":[],\"name\":\"ErrZeroAddress\",\"type\":\"error\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"batchIndex\",\"type\":\"uint256\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"winner\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"string\",\"name\":\"res\",\"type\":\"string\"}],\"name\":\"ChallengeRes\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"receiver\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"ChallengeRewardClaim\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint64\",\"name\":\"batchIndex\",\"type\":\"uint64\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"challenger\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"challengeDeposit\",\"type\":\"uint256\"}],\"name\":\"ChallengeState\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"batchIndex\",\"type\":\"uint256\"},{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"batchHash\",\"type\":\"bytes32\"}],\"name\":\"CommitBatch\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"batchIndex\",\"type\":\"uint256\"},{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"batchHash\",\"type\":\"bytes32\"},{\"indexed\":false,\"internalType\":\"bytes32\",\"name\":\"stateRoot\",\"type\":\"bytes32\"},{\"indexed\":false,\"internalType\":\"bytes32\",\"name\":\"withdrawRoot\",\"type\":\"bytes32\"}],\"name\":\"FinalizeBatch\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint8\",\"name\":\"version\",\"type\":\"uint8\"}],\"name\":\"Initialized\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"previousOwner\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"newOwner\",\"type\":\"address\"}],\"name\":\"OwnershipTransferred\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address\",\"name\":\"account\",\"type\":\"address\"}],\"name\":\"Paused\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address\",\"name\":\"receiver\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"ProveRemainingClaimed\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"batchIndex\",\"type\":\"uint256\"},{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"batchHash\",\"type\":\"bytes32\"}],\"name\":\"RevertBatch\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"oldAddr\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"newAddr\",\"type\":\"address\"}],\"name\":\"SubmitterContractUpdated\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address\",\"name\":\"account\",\"type\":\"address\"}],\"name\":\"Unpaused\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"account\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"bool\",\"name\":\"status\",\"type\":\"bool\"}],\"name\":\"UpdateChallenger\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"oldPeriod\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"newPeriod\",\"type\":\"uint256\"}],\"name\":\"UpdateFinalizationPeriodSeconds\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"oldPercent\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"newPercent\",\"type\":\"uint256\"}],\"name\":\"UpdateProofRewardPercent\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"oldWindow\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"newWindow\",\"type\":\"uint256\"}],\"name\":\"UpdateProofWindow\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"oldPeriod\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"newPeriod\",\"type\":\"uint256\"}],\"name\":\"UpdateRollupDelayPeriod\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"oldVerifier\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"newVerifier\",\"type\":\"address\"}],\"name\":\"UpdateVerifier\",\"type\":\"event\"},{\"inputs\":[],\"name\":\"LAYER_2_CHAIN_ID\",\"outputs\":[{\"internalType\":\"uint64\",\"name\":\"\",\"type\":\"uint64\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"__maxNumTxInChunk\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"_account\",\"type\":\"address\"}],\"name\":\"addChallenger\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"batchIndex\",\"type\":\"uint256\"}],\"name\":\"batchBlobVersionedHashes\",\"outputs\":[{\"internalType\":\"bytes32\",\"name\":\"blobVersionedHash\",\"type\":\"bytes32\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"owner\",\"type\":\"address\"}],\"name\":\"batchChallengeReward\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"batchChallenged\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"batchIndex\",\"type\":\"uint256\"}],\"name\":\"batchChallengedSuccess\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"batchIndex\",\"type\":\"uint256\"}],\"name\":\"batchDataStore\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"originTimestamp\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"finalizeTimestamp\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"blockNumber\",\"type\":\"uint256\"},{\"internalType\":\"address\",\"name\":\"submitter\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"batchIndex\",\"type\":\"uint256\"}],\"name\":\"batchExist\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"batchIndex\",\"type\":\"uint256\"}],\"name\":\"batchInChallenge\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"batchIndex\",\"type\":\"uint256\"}],\"name\":\"batchInsideChallengeWindow\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"batchIndex\",\"type\":\"uint64\"},{\"internalType\":\"bytes32\",\"name\":\"_batchHash\",\"type\":\"bytes32\"}],\"name\":\"challengeState\",\"outputs\":[],\"stateMutability\":\"payable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"batchIndex\",\"type\":\"uint256\"}],\"name\":\"challenges\",\"outputs\":[{\"internalType\":\"uint64\",\"name\":\"batchIndex\",\"type\":\"uint64\"},{\"internalType\":\"address\",\"name\":\"challenger\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"challengeDeposit\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"startTime\",\"type\":\"uint256\"},{\"internalType\":\"bool\",\"name\":\"challengeSuccess\",\"type\":\"bool\"},{\"internalType\":\"bool\",\"name\":\"finished\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"receiver\",\"type\":\"address\"}],\"name\":\"claimProveRemaining\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"receiver\",\"type\":\"address\"}],\"name\":\"claimReward\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"internalType\":\"uint8\",\"name\":\"version\",\"type\":\"uint8\"},{\"internalType\":\"bytes\",\"name\":\"parentBatchHeader\",\"type\":\"bytes\"},{\"internalType\":\"uint64\",\"name\":\"lastBlockNumber\",\"type\":\"uint64\"},{\"internalType\":\"uint16\",\"name\":\"numL1Messages\",\"type\":\"uint16\"},{\"internalType\":\"bytes32\",\"name\":\"prevStateRoot\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32\",\"name\":\"postStateRoot\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32\",\"name\":\"withdrawalRoot\",\"type\":\"bytes32\"}],\"internalType\":\"structIRollup.BatchDataInput\",\"name\":\"batchDataInput\",\"type\":\"tuple\"}],\"name\":\"commitBatch\",\"outputs\":[],\"stateMutability\":\"payable\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"internalType\":\"uint8\",\"name\":\"version\",\"type\":\"uint8\"},{\"internalType\":\"bytes\",\"name\":\"parentBatchHeader\",\"type\":\"bytes\"},{\"internalType\":\"uint64\",\"name\":\"lastBlockNumber\",\"type\":\"uint64\"},{\"internalType\":\"uint16\",\"name\":\"numL1Messages\",\"type\":\"uint16\"},{\"internalType\":\"bytes32\",\"name\":\"prevStateRoot\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32\",\"name\":\"postStateRoot\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32\",\"name\":\"withdrawalRoot\",\"type\":\"bytes32\"}],\"internalType\":\"structIRollup.BatchDataInput\",\"name\":\"batchDataInput\",\"type\":\"tuple\"},{\"internalType\":\"bytes\",\"name\":\"_batchHeader\",\"type\":\"bytes\"},{\"internalType\":\"bytes\",\"name\":\"_batchProof\",\"type\":\"bytes\"}],\"name\":\"commitBatchWithProof\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"internalType\":\"uint8\",\"name\":\"version\",\"type\":\"uint8\"},{\"internalType\":\"bytes\",\"name\":\"parentBatchHeader\",\"type\":\"bytes\"},{\"internalType\":\"uint64\",\"name\":\"lastBlockNumber\",\"type\":\"uint64\"},{\"internalType\":\"uint16\",\"name\":\"numL1Messages\",\"type\":\"uint16\"},{\"internalType\":\"bytes32\",\"name\":\"prevStateRoot\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32\",\"name\":\"postStateRoot\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32\",\"name\":\"withdrawalRoot\",\"type\":\"bytes32\"}],\"internalType\":\"structIRollup.BatchDataInput\",\"name\":\"batchDataInput\",\"type\":\"tuple\"}],\"name\":\"commitState\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"batchIndex\",\"type\":\"uint256\"}],\"name\":\"committedBatches\",\"outputs\":[{\"internalType\":\"bytes32\",\"name\":\"batchHash\",\"type\":\"bytes32\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"batchIndex\",\"type\":\"uint256\"}],\"name\":\"committedStateRoots\",\"outputs\":[{\"internalType\":\"bytes32\",\"name\":\"stateRoot\",\"type\":\"bytes32\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"finalizationPeriodSeconds\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes\",\"name\":\"_batchHeader\",\"type\":\"bytes\"}],\"name\":\"finalizeBatch\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"batchIndex\",\"type\":\"uint256\"}],\"name\":\"finalizedStateRoots\",\"outputs\":[{\"internalType\":\"bytes32\",\"name\":\"stateRoot\",\"type\":\"bytes32\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes\",\"name\":\"_batchHeader\",\"type\":\"bytes\"}],\"name\":\"importGenesisBatch\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"inChallenge\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"_submitterContract\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"_messageQueue\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"_verifier\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"_finalizationPeriodSeconds\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"_proofWindow\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"_proofRewardPercent\",\"type\":\"uint256\"}],\"name\":\"initialize\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"_prevStateRoot\",\"type\":\"bytes32\"}],\"name\":\"initialize2\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"_rollupDelayPeriod\",\"type\":\"uint256\"}],\"name\":\"initialize3\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"_newSubmitter\",\"type\":\"address\"}],\"name\":\"initialize4\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"_batchIndex\",\"type\":\"uint256\"}],\"name\":\"isBatchFinalized\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"challengerAddress\",\"type\":\"address\"}],\"name\":\"isChallenger\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"isChallenger\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"lastCommittedBatchIndex\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"lastFinalizedBatchIndex\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"messageQueue\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"owner\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"paused\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"submitter\",\"type\":\"address\"}],\"name\":\"pendingBatchCount\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"count\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"proofRewardPercent\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"proofWindow\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes\",\"name\":\"_batchHeader\",\"type\":\"bytes\"},{\"internalType\":\"bytes\",\"name\":\"_batchProof\",\"type\":\"bytes\"}],\"name\":\"proveCommittedBatchState\",\"outputs\":[],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"proveRemaining\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes\",\"name\":\"_batchHeader\",\"type\":\"bytes\"},{\"internalType\":\"bytes\",\"name\":\"_batchProof\",\"type\":\"bytes\"}],\"name\":\"proveState\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"_account\",\"type\":\"address\"}],\"name\":\"removeChallenger\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"renounceOwnership\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes\",\"name\":\"_batchHeader\",\"type\":\"bytes\"},{\"internalType\":\"uint256\",\"name\":\"_count\",\"type\":\"uint256\"}],\"name\":\"revertBatch\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"revertReqIndex\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"rollupDelayPeriod\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bool\",\"name\":\"_status\",\"type\":\"bool\"}],\"name\":\"setPause\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"submitterContract\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"newOwner\",\"type\":\"address\"}],\"name\":\"transferOwnership\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"_newPeriod\",\"type\":\"uint256\"}],\"name\":\"updateFinalizePeriodSeconds\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"_newWindow\",\"type\":\"uint256\"}],\"name\":\"updateProofWindow\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"_newProofRewardPercent\",\"type\":\"uint256\"}],\"name\":\"updateRewardPercentage\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"_newPeriod\",\"type\":\"uint256\"}],\"name\":\"updateRollupDelayPeriod\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"_newVerifier\",\"type\":\"address\"}],\"name\":\"updateVerifier\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"verifier\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"withdrawalRoot\",\"type\":\"bytes32\"}],\"name\":\"withdrawalRoots\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"exist\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"stateMutability\":\"payable\",\"type\":\"receive\"}]", + Bin: "0x60a060405234801562000010575f80fd5b506040516200599d3803806200599d83398101604081905262000033916200010f565b6001600160401b0381166080526200004a62000051565b506200013e565b5f54610100900460ff1615620000bd5760405162461bcd60e51b815260206004820152602760248201527f496e697469616c697a61626c653a20636f6e747261637420697320696e697469604482015266616c697a696e6760c81b606482015260840160405180910390fd5b5f5460ff908116146200010d575f805460ff191660ff9081179091556040519081527f7f26b83ff96e1f2b6a682f133852f6798a09c465da95921460cefb38474024989060200160405180910390a15b565b5f6020828403121562000120575f80fd5b81516001600160401b038116811462000137575f80fd5b9392505050565b60805161583f6200015e5f395f81816106ab015261481e015261583f5ff3fe608060405260043610610369575f3560e01c806388b1ea09116101c8578063b3e0a509116100fd578063d279c1911161009d578063e3fff1dd1161006d578063e3fff1dd14610b2c578063f2fde38b14610b4b578063f8fa010f14610b6a578063fb1e8b0414610b89575f80fd5b8063d279c19114610ac4578063d8dc99d214610ae3578063de8b303514610af8578063dff7827e14610b17575f80fd5b8063c5553892116100d8578063c555389214610a5e578063cd4edc6914610a7d578063ce5db8d614610a90578063cf9a674514610aa5575f80fd5b8063b3e0a50914610a01578063b8d0a1b014610a20578063bedb86fb14610a3f575f80fd5b8063a415d8dc11610168578063abc8d68d11610143578063abc8d68d14610983578063b31a77d3146109ae578063b3484425146109c3578063b35dac4e146109e2575f80fd5b8063a415d8dc14610921578063a479265d1461094f578063a4f209b014610964575f80fd5b80638f1d3776116101a35780638f1d377614610802578063910129d4146108b257806397fc007c146108e3578063a3c9e94c14610902575f80fd5b806388b1ea09146107a15780638da5cb5b146107ba5780638ef622a8146107d7575f80fd5b80632b7ac3f31161029e5780635f77cf1d1161023e57806368589dfa1161021957806368589dfa146107245780636c578c1d1461074f578063715018a61461076e578063728cdbca14610782575f80fd5b80635f77cf1d1461069a57806361267290146106e657806367caa37a14610705575f80fd5b806341f756da1161027957806341f756da146105df57806357e0af6c146105f25780635c975abb146106115780635ef7a94a14610628575f80fd5b80632b7ac3f3146105745780633b70c18a146105ab5780633e001b66146105ca575f80fd5b80631544ba3a1161030957806321e2f9e0116102e457806321e2f9e0146104d45780632362f03e146104f35780632571098d1461051e5780632a213ba114610549575f80fd5b80631544ba3a1461047057806318463fb01461048f57806318af3b2b146104a4575f80fd5b806310d445831161034457806310d44583146103fb578063116a1f421461041a578063121dcd501461043c5780631336110114610451575f80fd5b806304d7721514610374578063059def61146103b75780630ceb6780146103da575f80fd5b3661037057005b5f80fd5b34801561037f575f80fd5b506103a261038e36600461529d565b60a36020525f908152604090205460ff1681565b60405190151581526020015b60405180910390f35b3480156103c2575f80fd5b506103cc609d5481565b6040519081526020016103ae565b3480156103e5575f80fd5b506103f96103f43660046152ca565b610b9e565b005b348015610406575f80fd5b506103f9610415366004615328565b610c70565b348015610425575f80fd5b506103a261043436600461529d565b609d54101590565b348015610447575f80fd5b506103cc609e5481565b34801561045c575f80fd5b506103f961046b366004615370565b610fb5565b34801561047b575f80fd5b506103f961048a3660046153bf565b6114db565b34801561049a575f80fd5b506103cc60a75481565b3480156104af575f80fd5b506103a26104be36600461529d565b5f90815260a26020526040902060010154421090565b3480156104df575f80fd5b506103a26104ee36600461529d565b61189a565b3480156104fe575f80fd5b506103cc61050d36600461529d565b60a16020525f908152604090205481565b348015610529575f80fd5b506103cc61053836600461529d565b60a06020525f908152604090205481565b348015610554575f80fd5b506103cc61056336600461529d565b60ad6020525f908152604090205481565b34801561057f575f80fd5b50609c54610593906001600160a01b031681565b6040516001600160a01b0390911681526020016103ae565b3480156105b6575f80fd5b50609b54610593906001600160a01b031681565b3480156105d5575f80fd5b506103cc60aa5481565b6103f96105ed36600461544d565b6118c8565b3480156105fd575f80fd5b506103f961060c36600461529d565b611b66565b34801561061c575f80fd5b5060655460ff166103a2565b348015610633575f80fd5b5061067161064236600461529d565b60a26020525f90815260409020805460018201546002830154600390930154919290916001600160a01b031684565b604080519485526020850193909352918301526001600160a01b031660608201526080016103ae565b3480156106a5575f80fd5b506106cd7f000000000000000000000000000000000000000000000000000000000000000081565b60405167ffffffffffffffff90911681526020016103ae565b3480156106f1575f80fd5b506103f961070036600461529d565b611c11565b348015610710575f80fd5b506103f961071f36600461544d565b611dee565b34801561072f575f80fd5b506103cc61073e36600461529d565b60ab6020525f908152604090205481565b34801561075a575f80fd5b506103f96107693660046152ca565b612139565b348015610779575f80fd5b506103f96121f6565b34801561078d575f80fd5b506103f961079c366004615487565b612209565b3480156107ac575f80fd5b5060a6546103a29060ff1681565b3480156107c5575f80fd5b506033546001600160a01b0316610593565b3480156107e2575f80fd5b506103cc6107f13660046152ca565b60ae6020525f908152604090205481565b34801561080d575f80fd5b5061086e61081c36600461529d565b60a46020525f9081526040902080546001820154600283015460039093015467ffffffffffffffff831693680100000000000000009093046001600160a01b0316929060ff8082169161010090041686565b6040805167ffffffffffffffff90971687526001600160a01b03909516602087015293850192909252606084015215156080830152151560a082015260c0016103ae565b3480156108bd575f80fd5b506103a26108cc36600461529d565b5f90815260a4602052604090206003015460ff1690565b3480156108ee575f80fd5b506103f96108fd3660046152ca565b612512565b34801561090d575f80fd5b50609754610593906001600160a01b031681565b34801561092c575f80fd5b506103a261093b3660046152ca565b609f6020525f908152604090205460ff1681565b34801561095a575f80fd5b506103cc60995481565b34801561096f575f80fd5b506103f961097e36600461529d565b6125f5565b34801561098e575f80fd5b506103cc61099d3660046152ca565b60a56020525f908152604090205481565b3480156109b9575f80fd5b506103cc60a85481565b3480156109ce575f80fd5b506103f96109dd3660046154e2565b6126a5565b3480156109ed575f80fd5b506103f96109fc3660046152ca565b6129b2565b348015610a0c575f80fd5b506103f9610a1b366004615370565b612a0b565b348015610a2b575f80fd5b506103f9610a3a3660046154e2565b612d9c565b348015610a4a575f80fd5b506103f9610a59366004615556565b612e25565b348015610a69575f80fd5b506103f9610a7836600461529d565b612f63565b6103f9610a8b366004615588565b613006565b348015610a9b575f80fd5b506103cc60985481565b348015610ab0575f80fd5b506103f9610abf36600461529d565b613598565b348015610acf575f80fd5b506103f9610ade3660046152ca565b61370f565b348015610aee575f80fd5b506103cc60ac5481565b348015610b03575f80fd5b506103a2610b1236600461529d565b6137ce565b348015610b22575f80fd5b506103cc609a5481565b348015610b37575f80fd5b506103f9610b4636600461529d565b613818565b348015610b56575f80fd5b506103f9610b653660046152ca565b6138bb565b348015610b75575f80fd5b506103f9610b843660046152ca565b613948565b348015610b94575f80fd5b506103cc60a95481565b610ba6613e4d565b6001600160a01b0381165f908152609f602052604090205460ff1615610c135760405162461bcd60e51b815260206004820152601f60248201527f6163636f756e7420697320616c72656164792061206368616c6c656e6765720060448201526064015b60405180910390fd5b6001600160a01b0381165f818152609f6020908152604091829020805460ff1916600190811790915591519182527f7feb653c7b1f0d23daeed494225b3f28851cdc8973fcc653866d9b6e205fc00991015b60405180910390a250565b610c78613e4d565b5f8111610cc75760405162461bcd60e51b815260206004820152601560248201527f636f756e74206d757374206265206e6f6e7a65726f00000000000000000000006044820152606401610c0a565b5f80610cd38585613ea7565b915091505f610ce6836001015160c01c90565b5f81815260a160205260409020549091508214610d455760405162461bcd60e51b815260206004820152601460248201527f696e636f727265637420626174636820686173680000000000000000000000006044820152606401610c0a565b5f60a181610d5387856155dd565b81526020019081526020015f205414610dd35760405162461bcd60e51b8152602060048201526024808201527f726576657274696e67206d7573742073746172742066726f6d2074686520656e60448201527f64696e67000000000000000000000000000000000000000000000000000000006064820152608401610c0a565b609d548111610e4a5760405162461bcd60e51b815260206004820152602160248201527f63616e206f6e6c792072657665727420756e46696e616c697a6564206261746360448201527f68000000000000000000000000000000000000000000000000000000000000006064820152608401610c0a565b610e556001826155f0565b609e555b8315610fad57604051829082907ecae2739091badfd91c373f0a16cede691e0cd25bb80cff77dd5caeb4710146905f90a3610e9381613f4f565b5f81815260a16020526040812055610eaa816137ce565b15610f05575f81815260a460209081526040808320600181015490546801000000000000000090046001600160a01b0316845260a59092528220805491929091610ef59084906155dd565b909155505060a6805460ff191690555b5f81815260a46020526040812080547fffffffff00000000000000000000000000000000000000000000000000000000168155600181018290556002810191909155600301805461ffff1916905560a85415801590610f65575060a85481145b15610f6f575f60a8555b6001015f81815260a160205260409020547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff90940193915081610e59575b505050505050565b60a854156110055760405162461bcd60e51b815260206004820152601660248201527f70656e64696e67207265766572742072657175657374000000000000000000006044820152606401610c0a565b61100d614002565b5f806110198484613ea7565b915091505f61102c836001015160c01c90565b5f81815260a16020526040902054909150821461108b5760405162461bcd60e51b815260206004820152601460248201527f696e636f727265637420626174636820686173680000000000000000000000006044820152606401610c0a565b6110948161189a565b6110e05760405162461bcd60e51b815260206004820152600f60248201527f6261746368206e6f7420657869737400000000000000000000000000000000006044820152606401610c0a565b6110e9816137ce565b156111365760405162461bcd60e51b815260206004820152601260248201527f626174636820696e206368616c6c656e676500000000000000000000000000006044820152606401610c0a565b5f81815260a4602052604090206003015460ff16156111975760405162461bcd60e51b815260206004820152601660248201527f62617463682073686f756c6420626520726576657274000000000000000000006044820152606401610c0a565b5f81815260a260205260409020600101544210156111f75760405162461bcd60e51b815260206004820152601960248201527f626174636820696e206368616c6c656e67652077696e646f77000000000000006044820152606401610c0a565b605983015160a05f61120a6001856155f0565b81526020019081526020015f2054146112655760405162461bcd60e51b815260206004820152601d60248201527f696e636f72726563742070726576696f757320737461746520726f6f740000006044820152606401610c0a565b5f81815260a06020526040902054156112c05760405162461bcd60e51b815260206004820152601660248201527f626174636820616c7265616479207665726966696564000000000000000000006044820152606401610c0a565b80609d54600101146113145760405162461bcd60e51b815260206004820152601560248201527f696e636f727265637420626174636820696e64657800000000000000000000006044820152606401610c0a565b609d819055600160a35f611329866099015190565b815260208101919091526040015f20805460ff191691151591909117905560798301515f82815260a0602052604090205561137861136b846011015160c01c90565b600985015160c01c614055565b61138181613f4f565b60a25f61138f6001846155f0565b815260208101919091526040015f908120818155600180820183905560028201839055600390910180547fffffffffffffffffffffffff000000000000000000000000000000000000000016905560ab91906113eb90846155f0565b81526020019081526020015f205f905560ad5f60018361140b91906155f0565b81526020019081526020015f205f905560a45f60018361142b91906155f0565b815260208082019290925260409081015f90812080547fffffffff000000000000000000000000000000000000000000000000000000001681556001810182905560028101829055600301805461ffff1916905583815260a1909252902054817f26ba82f907317eedc97d0cbef23de76a43dd6edb563bdb6e9407645b950a7a2d6114b7866079015190565b60998701516040805192835260208301919091520160405180910390a35050505050565b60a8541561152b5760405162461bcd60e51b815260206004820152601660248201527f70656e64696e67207265766572742072657175657374000000000000000000006044820152606401610c0a565b611533614002565b60ac54609e545f90815260a260205260408120549091429161155591906155dd565b1090505f4260ac54609b5f9054906101000a90046001600160a01b03166001600160a01b031663b59b1a786040518163ffffffff1660e01b8152600401602060405180830381865afa1580156115ad573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906115d19190615603565b6115db91906155dd565b109050811580156115e95750805b15611650575f6115ff6080890160608a0161561a565b61ffff16116116505760405162461bcd60e51b815260206004820152600b60248201527f6c316d73672064656c61790000000000000000000000000000000000000000006044820152606401610c0a565b81806116595750805b6116a55760405162461bcd60e51b815260206004820152600e60248201527f696e76616c69642074696d696e670000000000000000000000000000000000006044820152606401610c0a565b5f6116bb6116b660208a018a61563b565b613ea7565b5090505f6116cd826001015160c01c90565b6116d89060016155dd565b5f81815260ad60205260408120549192509015611778575f49156117645760405162461bcd60e51b815260206004820152602f60248201527f6d757374206e6f7420636172727920626c6f62207768656e207573696e67207360448201527f746f72656420626c6f62206861736800000000000000000000000000000000006064820152608401610c0a565b505f81815260ad6020526040902054611793565b61179061178860208c018c61569c565b60ff16614112565b90505b61179e8a5f8361424d565b5f806117aa8b8b613ea7565b915091505f6117bd836001015160c01c90565b905080609e54146118105760405162461bcd60e51b815260206004820152601660248201527f696e636f727265637420626174636820686561646572000000000000000000006044820152606401610c0a565b5f81815260a16020526040902054821461186c5760405162461bcd60e51b815260206004820152601460248201527f696e636f727265637420626174636820686173680000000000000000000000006044820152606401610c0a565b5f81815260a2602052604090204260019091015561188b838b8b6147af565b50505050505050505050505050565b5f81815260a26020526040812054158015906118c257505f82815260a1602052604090205415155b92915050565b6097546001600160a01b0316639f8a13d7336040517fffffffff0000000000000000000000000000000000000000000000000000000060e084901b1681526001600160a01b039091166004820152602401602060405180830381865afa158015611934573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061195891906156bc565b6119a45760405162461bcd60e51b815260206004820152601d60248201527f6f6e6c7920616374697665207375626d697474657220616c6c6f7765640000006044820152606401610c0a565b60a854156119f45760405162461bcd60e51b815260206004820152601660248201527f70656e64696e67207265766572742072657175657374000000000000000000006044820152606401610c0a565b6119fc614002565b5f49611a4a5760405162461bcd60e51b815260206004820152600f60248201527f6d75737420636172727920626c6f6200000000000000000000000000000000006044820152606401610c0a565b60ac54609b54604080517fb59b1a7800000000000000000000000000000000000000000000000000000000815290514293926001600160a01b03169163b59b1a789160048083019260209291908290030181865afa158015611aae573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611ad29190615603565b611adc91906155dd565b1015611b44575f611af3608083016060840161561a565b61ffff1611611b445760405162461bcd60e51b815260206004820152600b60248201527f6c316d73672064656c61790000000000000000000000000000000000000000006044820152606401610c0a565b5f611b55611788602084018461569c565b9050611b6282338361424d565b5050565b611b6e613e4d565b5f81118015611b7f57506099548114155b611bcb5760405162461bcd60e51b815260206004820152601860248201527f696e76616c6964206e65772070726f6f662077696e646f7700000000000000006044820152606401610c0a565b609980549082905560408051828152602081018490527f1e3a2094feb4b696dd3d7caea38ad2f41dbdcac3fa3943c7a693aff8a64b0a6191015b60405180910390a15050565b5f54600290610100900460ff16158015611c3157505f5460ff8083169116105b611ca35760405162461bcd60e51b815260206004820152602e60248201527f496e697469616c697a61626c653a20636f6e747261637420697320616c72656160448201527f647920696e697469616c697a65640000000000000000000000000000000000006064820152608401610c0a565b5f805461ffff191660ff831617610100179055611cbe613e4d565b5f5460ff1660ff16600214611d155760405162461bcd60e51b815260206004820152601660248201527f6d757374206861766520696e697469616c697a656421000000000000000000006044820152606401610c0a565b81611d885760405162461bcd60e51b815260206004820152602760248201527f63616e206e6f742073657420737461746520726f6f742077697468206279746560448201527f73333228302921000000000000000000000000000000000000000000000000006064820152608401610c0a565b609e545f90815260ab6020526040902054611db157609e545f90815260ab602052604090208290555b5f805461ff001916905560405160ff821681527f7f26b83ff96e1f2b6a682f133852f6798a09c465da95921460cefb384740249890602001611c05565b6097546001600160a01b0316639f8a13d7336040517fffffffff0000000000000000000000000000000000000000000000000000000060e084901b1681526001600160a01b039091166004820152602401602060405180830381865afa158015611e5a573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611e7e91906156bc565b611eca5760405162461bcd60e51b815260206004820152601d60248201527f6f6e6c7920616374697665207375626d697474657220616c6c6f7765640000006044820152606401610c0a565b60a85415611f1a5760405162461bcd60e51b815260206004820152601660248201527f70656e64696e67207265766572742072657175657374000000000000000000006044820152606401610c0a565b611f22614002565b5f4915611f715760405162461bcd60e51b815260206004820152601f60248201527f636f6d6d69745374617465206d757374206e6f7420636172727920626c6f62006044820152606401610c0a565b5f611f826116b6602084018461563b565b5090505f611f94826001015160c01c90565b611f9f9060016155dd565b5f81815260ad60205260409020549091506120225760405162461bcd60e51b815260206004820152602260248201527f6e6f2073746f72656420626c6f62206861736820666f7220746869732062617460448201527f63680000000000000000000000000000000000000000000000000000000000006064820152608401610c0a565b60ac54609b54604080517fb59b1a7800000000000000000000000000000000000000000000000000000000815290514293926001600160a01b03169163b59b1a789160048083019260209291908290030181865afa158015612086573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906120aa9190615603565b6120b491906155dd565b101561211c575f6120cb608085016060860161561a565b61ffff161161211c5760405162461bcd60e51b815260206004820152600b60248201527f6c316d73672064656c61790000000000000000000000000000000000000000006044820152606401610c0a565b61213483335f84815260ad602052604090205461424d565b505050565b612141613e4d565b6001600160a01b0381165f908152609f602052604090205460ff166121a85760405162461bcd60e51b815260206004820152601b60248201527f6163636f756e74206973206e6f742061206368616c6c656e67657200000000006044820152606401610c0a565b6001600160a01b0381165f818152609f60209081526040808320805460ff19169055519182527f7feb653c7b1f0d23daeed494225b3f28851cdc8973fcc653866d9b6e205fc0099101610c65565b6121fe613e4d565b6122075f614962565b565b5f54610100900460ff161580801561222757505f54600160ff909116105b806122405750303b15801561224057505f5460ff166001145b6122b25760405162461bcd60e51b815260206004820152602e60248201527f496e697469616c697a61626c653a20636f6e747261637420697320616c72656160448201527f647920696e697469616c697a65640000000000000000000000000000000000006064820152608401610c0a565b5f805460ff1916600117905580156122d3575f805461ff0019166101001790555b6001600160a01b03861615806122f057506001600160a01b038516155b15612327576040517fecc6fdf000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6001600160a01b03871661237d5760405162461bcd60e51b815260206004820152601a60248201527f696e76616c6964207375626d697474657220636f6e74726163740000000000006044820152606401610c0a565b6123856149cb565b61238d614a4f565b609780546001600160a01b03808a167fffffffffffffffffffffffff000000000000000000000000000000000000000092831617909255609b8054898416908316179055609c805492881692909116821790556098859055609984905560a98390556040515f907f728af3d16a5760405e27a082c98ab272e9f0a1d02f0085d41532a26093aedd96908290a3604080515f8152602081018690527fa577f4223f91f74e2dad65bbb8c30807587ae95d0d34288057bb3ec0d398a437910160405180910390a1604080515f8152602081018590527f1e3a2094feb4b696dd3d7caea38ad2f41dbdcac3fa3943c7a693aff8a64b0a61910160405180910390a1604080515f8152602081018490527ffb81bce17f015797e11949d3c332e2bf9453faf68f728447426803138f2b0223910160405180910390a18015612509575f805461ff0019169055604051600181527f7f26b83ff96e1f2b6a682f133852f6798a09c465da95921460cefb38474024989060200160405180910390a15b50505050505050565b61251a613e4d565b6001600160a01b038116158015906125405750609c546001600160a01b03828116911614155b61258c5760405162461bcd60e51b815260206004820152601460248201527f696e76616c6964206e65772076657269666965720000000000000000000000006044820152606401610c0a565b609c80546001600160a01b038381167fffffffffffffffffffffffff0000000000000000000000000000000000000000831681179093556040519116919082907f728af3d16a5760405e27a082c98ab272e9f0a1d02f0085d41532a26093aedd96905f90a35050565b6125fd613e4d565b5f8111801561260d575060648111155b801561261b575060a9548114155b6126675760405162461bcd60e51b815260206004820152601f60248201527f696e76616c69642070726f6f66207265776172642070657263656e74616765006044820152606401610c0a565b60a980549082905560408051828152602081018490527ffb81bce17f015797e11949d3c332e2bf9453faf68f728447426803138f2b02239101611c05565b60a854156126f55760405162461bcd60e51b815260206004820152601660248201527f70656e64696e67207265766572742072657175657374000000000000000000006044820152606401610c0a565b6126fd614002565b6097546001600160a01b0316639f8a13d7336040517fffffffff0000000000000000000000000000000000000000000000000000000060e084901b1681526001600160a01b039091166004820152602401602060405180830381865afa158015612769573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061278d91906156bc565b6127d95760405162461bcd60e51b815260206004820152601d60248201527f6f6e6c7920616374697665207375626d697474657220616c6c6f7765640000006044820152606401610c0a565b5f806127e58686613ea7565b915091505f6127f8836001015160c01c90565b5f81815260a1602052604090205490915082146128575760405162461bcd60e51b815260206004820152601460248201527f696e636f727265637420626174636820686173680000000000000000000000006044820152606401610c0a565b612860816137ce565b6128ac5760405162461bcd60e51b815260206004820152601260248201527f626174636820696e206368616c6c656e676500000000000000000000000000006044820152606401610c0a565b5f81815260a46020526040902060038101805461ff00191661010017905560a6805460ff1916905560995460029091015442916128e8916155dd565b11612967575f81815260a4602090815260408083206003908101805460ff1916600117905560a2835292819020909201548251808401909352600783527f54696d656f757400000000000000000000000000000000000000000000000000918301919091526129629183916001600160a01b031690614ad3565b612509565b6129728386866147af565b61250981336040518060400160405280600d81526020017f50726f6f66207375636365737300000000000000000000000000000000000000815250614c32565b6129ba613e4d565b60aa80545f9091556129cc8282614cfe565b604080516001600160a01b0384168152602081018390527fb1b2058a6969e2d25e47bcaebe8ae21c29a23b2752429315b75e2f4f285f3d879101611c05565b612a13613e4d565b5f805260a06020527fb84a74ec6ef4d0e83b6006dfaa014ab4026f9f3b97d186e604d29998a4e808ea5415612a8a5760405162461bcd60e51b815260206004820152601660248201527f67656e6573697320626174636820696d706f72746564000000000000000000006044820152606401610c0a565b5f80612a968484613ea7565b915091505f612aa9836001015160c01c90565b90508015612af95760405162461bcd60e51b815260206004820152601360248201527f696e76616c696420626174636820696e646578000000000000000000000000006044820152606401610c0a565b5f612b05846079015190565b905080612b545760405162461bcd60e51b815260206004820152600f60248201527f7a65726f20737461746520726f6f7400000000000000000000000000000000006044820152606401610c0a565b600984015160c01c15612ba95760405162461bcd60e51b815260206004820152601d60248201527f6c31206d65737361676520706f707065642073686f756c6420626520300000006044820152606401610c0a565b5f612bb5856019015190565b03612c025760405162461bcd60e51b815260206004820152600e60248201527f7a65726f206461746120686173680000000000000000000000000000000000006044820152606401610c0a565b7f010657f37554c781402a22917dee2f75def7ab966d7b770905398eba3c444014612c2e856039015190565b14612c7b5760405162461bcd60e51b815260206004820152601660248201527f696e76616c69642076657273696f6e65642068617368000000000000000000006044820152606401610c0a565b5f82815260a1602090815260408083208690558051608081018252428082528184019081528183018581526060830186815288875260a28652848720935184559151600184015551600283015551600390910180547fffffffffffffffffffffffff0000000000000000000000000000000000000000166001600160a01b03909216919091179055603987015160ad83528184205560ab825280832084905560a0909152808220839055609e849055609d84905551849184917f2c32d4ae151744d0bf0b9464a3e897a1d17ed2f1af71f7c9a75f12ce0d28238f9190a3604080518281525f6020820152849184917f26ba82f907317eedc97d0cbef23de76a43dd6edb563bdb6e9407645b950a7a2d910160405180910390a3505050505050565b5f80612da88686613ea7565b915091505f612dbb836001015160c01c90565b5f81815260a160205260409020549091508214612e1a5760405162461bcd60e51b815260206004820152601460248201527f696e636f727265637420626174636820686173680000000000000000000000006044820152606401610c0a565b6125098386866147af565b612e2d613e4d565b8015612f3157612e3b614da3565b60a65460ff1615612ef05760a7545f90815260a460209081526040808320600181015490546801000000000000000090046001600160a01b0316845260a59092528220805491929091612e8f9084906155dd565b909155505060a7545f90815260a46020526040812080547fffffffff00000000000000000000000000000000000000000000000000000000168155600181018290556002810191909155600301805461ffff1916905560a6805460ff191690555b7f62e78cea01bee320cd4e420270b5ea74000d11b0c9f74754ebdbfc544b05a258335b6040516001600160a01b03909116815260200160405180910390a150565b612f39614dfd565b7f5db9ee0a495bf2e6ff9c91a7834c1ba4fdd244a5e8aa4e537bd38aeae4b073aa33612f13565b50565b612f6b613e4d565b5f81118015612f7c575060ac548114155b612fc85760405162461bcd60e51b815260206004820152601f60248201527f696e76616c6964206e657720726f6c6c75702064656c617920706572696f64006044820152606401610c0a565b60ac80549082905560408051828152602081018490527f624e47dc9fb8f8cfeaf4ead4710277cc1757136cfa885e465514cf6d510f0ad19101611c05565b335f908152609f602052604090205460ff166130645760405162461bcd60e51b815260206004820152601760248201527f6f6e6c79206368616c6c656e67657220616c6c6f7765640000000000000000006044820152606401610c0a565b60a854156130b45760405162461bcd60e51b815260206004820152601660248201527f70656e64696e67207265766572742072657175657374000000000000000000006044820152606401610c0a565b6130bc614002565b60a65460ff161561310f5760405162461bcd60e51b815260206004820152601460248201527f616c726561647920696e206368616c6c656e67650000000000000000000000006044820152606401610c0a565b8167ffffffffffffffff16609d541061316a5760405162461bcd60e51b815260206004820152601760248201527f626174636820616c72656164792066696e616c697a65640000000000000000006044820152606401610c0a565b67ffffffffffffffff82165f90815260a1602052604090205481146131d15760405162461bcd60e51b815260206004820152601460248201527f696e636f727265637420626174636820686173680000000000000000000000006044820152606401610c0a565b6131e48267ffffffffffffffff1661189a565b6132305760405162461bcd60e51b815260206004820152600f60248201527f6261746368206e6f7420657869737400000000000000000000000000000000006044820152606401610c0a565b67ffffffffffffffff82165f90815260a460205260409020546801000000000000000090046001600160a01b0316156132ab5760405162461bcd60e51b815260206004820152601860248201527f626174636820616c7265616479206368616c6c656e67656400000000000000006044820152606401610c0a565b67ffffffffffffffff82165f90815260a26020526040902060010154421061333b5760405162461bcd60e51b815260206004820152603360248201527f63616e6e6f74206368616c6c656e6765206261746368206f757473696465207460448201527f6865206368616c6c656e67652077696e646f77000000000000000000000000006064820152608401610c0a565b60975f9054906101000a90046001600160a01b03166001600160a01b0316630d13fd7b6040518163ffffffff1660e01b8152600401602060405180830381865afa15801561338b573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906133af9190615603565b3410156133fe5760405162461bcd60e51b815260206004820152601260248201527f696e73756666696369656e742076616c756500000000000000000000000000006044820152606401610c0a565b67ffffffffffffffff82811660a78190556040805160c0810182528281523360208083018281523484860190815242606086019081525f6080870181815260a0880182815299825260a4909552969096209451855492516001600160a01b031668010000000000000000027fffffffff000000000000000000000000000000000000000000000000000000009093169816979097171783559451600183015591516002820155925160039093018054925115156101000261ff00199415159490941661ffff19909316929092179290921790556001600160a01b03168267ffffffffffffffff167f3a6ea19df25b49e7624e313ce7c1ab23984238e93727260db56a81735b1b99763460405161351691815260200190565b60405180910390a35f609d54600161352e91906155dd565b90505b609e548111613586578267ffffffffffffffff168114613574576099545f82815260a260205260408120600101805490919061356e9084906155dd565b90915550505b8061357e816156d7565b915050613531565b505060a6805460ff1916600117905550565b5f54600390610100900460ff161580156135b857505f5460ff8083169116105b61362a5760405162461bcd60e51b815260206004820152602e60248201527f496e697469616c697a61626c653a20636f6e747261637420697320616c72656160448201527f647920696e697469616c697a65640000000000000000000000000000000000006064820152608401610c0a565b5f805461ffff191660ff831617610100179055613645613e4d565b815f036136945760405162461bcd60e51b815260206004820152601b60248201527f696e76616c696420726f6c6c75702064656c617920706572696f6400000000006044820152606401610c0a565b60ac829055604080515f8152602081018490527f624e47dc9fb8f8cfeaf4ead4710277cc1757136cfa885e465514cf6d510f0ad1910160405180910390a15f805461ff001916905560405160ff821681527f7f26b83ff96e1f2b6a682f133852f6798a09c465da95921460cefb384740249890602001611c05565b335f90815260a560205260408120549081900361376e5760405162461bcd60e51b815260206004820152601c60248201527f696e76616c69642062617463684368616c6c656e6765526577617264000000006044820152606401610c0a565b335f90815260a560205260408120556137878282614cfe565b816001600160a01b03167f9c25fa83f414ed363c8d39c98fb3e17567b3431cede71eb062c49d2a63ce247a826040516137c291815260200190565b60405180910390a25050565b5f81815260a460205260408120546801000000000000000090046001600160a01b0316158015906118c25750505f90815260a46020526040902060030154610100900460ff161590565b613820613e4d565b5f8111801561383157506098548114155b61387d5760405162461bcd60e51b815260206004820152601b60248201527f696e76616c6964206e65772066696e616c697a6520706572696f6400000000006044820152606401610c0a565b609880549082905560408051828152602081018490527fa577f4223f91f74e2dad65bbb8c30807587ae95d0d34288057bb3ec0d398a4379101611c05565b6138c3613e4d565b6001600160a01b03811661393f5760405162461bcd60e51b815260206004820152602660248201527f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160448201527f64647265737300000000000000000000000000000000000000000000000000006064820152608401610c0a565b612f6081614962565b5f54600490610100900460ff1615801561396857505f5460ff8083169116105b6139da5760405162461bcd60e51b815260206004820152602e60248201527f496e697469616c697a61626c653a20636f6e747261637420697320616c72656160448201527f647920696e697469616c697a65640000000000000000000000000000000000006064820152608401610c0a565b5f805461ffff191660ff8316176101001790556139f5613e4d565b6001600160a01b03821615801590613a1657505f826001600160a01b03163b115b613a625760405162461bcd60e51b815260206004820152601160248201527f696e76616c6964207375626d69747465720000000000000000000000000000006044820152606401610c0a565b6040805160048152602481019091526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fbfa02ba9000000000000000000000000000000000000000000000000000000001790525f90613ac6908490614e36565b905060a081901c15613b1a5760405162461bcd60e51b815260206004820152601760248201527f696e76616c696420726f6c6c757020656e636f64696e670000000000000000006044820152606401610c0a565b6040515f602482018190528291613bab908690604401604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529190526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167f9f8a13d700000000000000000000000000000000000000000000000000000000179052614e36565b6040805160048152602481019091526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167f0d13fd7b000000000000000000000000000000000000000000000000000000001790529091505f90613c12908790614e36565b90506001600160a01b0383163014613c6c5760405162461bcd60e51b815260206004820152601960248201527f7375626d697474657220726f6c6c7570206d69736d61746368000000000000006044820152606401610c0a565b81158015613c7957505f81115b613cc55760405162461bcd60e51b815260206004820152601760248201527f696e76616c6964207375626d69747465722073746174650000000000000000006044820152606401610c0a565b609d54609e5414613d185760405162461bcd60e51b815260206004820152600f60248201527f70656e64696e67206261746368657300000000000000000000000000000000006044820152606401610c0a565b60a85415613d685760405162461bcd60e51b815260206004820152601660248201527f70656e64696e67207265766572742072657175657374000000000000000000006044820152606401610c0a565b609d545f90815260a2602052604080822060030180547fffffffffffffffffffffffff000000000000000000000000000000000000000016905560975490516001600160a01b03898116939216917fde3ada76ae1aa387cc4c586c70304b9fae54b251958786c828aaa0ce0290f93991a35050609780547fffffffffffffffffffffffff0000000000000000000000000000000000000000166001600160a01b03861617905550505f805461ff001916905560405160ff821681527f7f26b83ff96e1f2b6a682f133852f6798a09c465da95921460cefb384740249890602001611c05565b6033546001600160a01b031633146122075760405162461bcd60e51b815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e65726044820152606401610c0a565b5f805f613eb48585614ebf565b90505f8160ff165f03613ed557613ecb8686614f2f565b9094509050613f41565b8160ff1660011480613eea57508160ff166002145b15613ef957613ecb8686614f98565b60405162461bcd60e51b815260206004820152601960248201527f556e737570706f727465642062617463682076657273696f6e000000000000006044820152606401610c0a565b808420925050509250929050565b5f81815260a260205260409020600301546001600160a01b031680613f72575050565b6001600160a01b0381165f90815260ae602052604090205480613fd75760405162461bcd60e51b815260206004820152601d60248201527f70656e64696e6720626174636820636f756e7420756e646572666c6f770000006044820152606401610c0a565b613fe26001826155f0565b6001600160a01b039092165f90815260ae60205260409020919091555050565b60655460ff16156122075760405162461bcd60e51b815260206004820152601060248201527f5061757361626c653a20706175736564000000000000000000000000000000006044820152606401610c0a565b805f03614060575050565b8082035f5b8281101561410c5761010081840381111561407f57508083035b609b546040517f3c7f528300000000000000000000000000000000000000000000000000000000815260048101859052602481018390526001600160a01b0390911690633c7f5283906044015f604051808303815f87803b1580156140e2575f80fd5b505af11580156140f4573d5f803e3d5ffd5b50505050610100830192505061010081019050614065565b50505050565b5f816002036141a1575f6040515f5b80498061412e575061413d565b60208202830152600101614121565b60208102808320920160405290925090508061419b5760405162461bcd60e51b815260206004820152601b60248201527f5632207265717569726573206174206c65617374203120626c6f6200000000006044820152606401610c0a565b50919050565b600149156142175760405162461bcd60e51b815260206004820152602560248201527f6c6567616379206261746368657320737570706f72742065786163746c79203160448201527f20626c6f620000000000000000000000000000000000000000000000000000006064820152608401610c0a565b5f4915614225575f496118c2565b507f010657f37554c781402a22917dee2f75def7ab966d7b770905398eba3c4440145b919050565b600261425c602085018561569c565b60ff1611156142ad5760405162461bcd60e51b815260206004820152600f60248201527f696e76616c69642076657273696f6e00000000000000000000000000000000006044820152606401610c0a565b60808301356142fe5760405162461bcd60e51b815260206004820152601b60248201527f70726576696f757320737461746520726f6f74206973207a65726f00000000006044820152606401610c0a565b60a083013561434f5760405162461bcd60e51b815260206004820152601660248201527f6e657720737461746520726f6f74206973207a65726f000000000000000000006044820152606401610c0a565b5f806143616116b6602087018761563b565b915091505f614374836001015160c01c90565b90505f60a1816143858460016155dd565b81526020019081526020015f2054146143e05760405162461bcd60e51b815260206004820152601760248201527f626174636820616c726561647920636f6d6d69747465640000000000000000006044820152606401610c0a565b609e5481146144315760405162461bcd60e51b815260206004820152601560248201527f696e636f727265637420626174636820696e64657800000000000000000000006044820152606401610c0a565b5f81815260a16020526040902054821461448d5760405162461bcd60e51b815260206004820152601b60248201527f696e636f727265637420706172656e74206261746368206861736800000000006044820152606401610c0a565b5f81815260ab60205260409020546080870135146144ed5760405162461bcd60e51b815260206004820152601d60248201527f696e636f72726563742070726576696f757320737461746520726f6f740000006044820152606401610c0a565b5f6144fc846011015160c01c90565b90505f61452861451260608a0160408b0161570e565b61452260808b0160608c0161561a565b84614feb565b905061453a6080890160608a0161561a565b60019384019361ffff91909116929092019160f99061455c60208b018b61569c565b60ff161061456957506101015b60408051828101909152955061458e8661458660208c018c61569c565b60ff1661502f565b60c084901b60018701526145bc866145ac60808c0160608d0161561a565b61ffff1660c01b60099190910152565b60c083811b6011880152601987018390526039870188905260808a0135605988015260a08a0135607988015289013560998701525f60b987015260d98601859052600161460c60208b018b61569c565b60ff161061463e5761463e8661462860608c0160408d0161570e565b67ffffffffffffffff1660c01b60f99190910152565b8086205f85815260a1602090815260408083209390935560ab815282822060a08d0135905560ad905290812088905560a65460ff16156146a75760a7545f90815260a46020526040902060020154609954429161469a916155dd565b6146a491906155f0565b90505b604051806080016040528042815260200182609854426146c791906155dd565b6146d191906155dd565b81526020016146e660608d0160408e0161570e565b67ffffffffffffffff1681526001600160a01b038b81166020928301525f88815260a28352604090819020845181559284015160018401558301516002830155606090920151600390910180547fffffffffffffffffffffffff0000000000000000000000000000000000000000169190921617905561476589615036565b5050609e8390555f83815260a16020526040808220549051909185917f2c32d4ae151744d0bf0b9464a3e897a1d17ed2f1af71f7c9a75f12ce0d28238f9190a35050505050505050565b806147fc5760405162461bcd60e51b815260206004820152601360248201527f496e76616c69642062617463682070726f6f66000000000000000000000000006044820152606401610c0a565b5f61480b846001015160c01c90565b90505f614819856039015190565b90505f7f0000000000000000000000000000000000000000000000000000000000000000614848876059015190565b6079880151609989015160b98a015160198b015160405160c09690961b7fffffffffffffffff000000000000000000000000000000000000000000000000166020870152602886019490945260488501929092526068840152608883015260a882015260c8810183905260e801604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529190528051602090910120609c549091506001600160a01b0316632c09a84861490a885160f81c90565b858888866040518663ffffffff1660e01b815260040161492e959493929190615727565b5f6040518083038186803b158015614944575f80fd5b505afa158015614956573d5f803e3d5ffd5b50505050505050505050565b603380546001600160a01b038381167fffffffffffffffffffffffff0000000000000000000000000000000000000000831681179093556040519116919082907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0905f90a35050565b5f54610100900460ff16614a475760405162461bcd60e51b815260206004820152602b60248201527f496e697469616c697a61626c653a20636f6e7472616374206973206e6f74206960448201527f6e697469616c697a696e670000000000000000000000000000000000000000006064820152608401610c0a565b612207615075565b5f54610100900460ff16614acb5760405162461bcd60e51b815260206004820152602b60248201527f496e697469616c697a61626c653a20636f6e7472616374206973206e6f74206960448201527f6e697469616c697a696e670000000000000000000000000000000000000000006064820152608401610c0a565b6122076150fd565b60a88390555f83815260a460205260408082205460975491517fc96be4cb0000000000000000000000000000000000000000000000000000000081526001600160a01b0386811660048301526801000000000000000090920482169392919091169063c96be4cb906024016020604051808303815f875af1158015614b5a573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190614b7e9190615603565b5f86815260a46020526040902060010154909150614b9d9082906155dd565b5f86815260a460209081526040808320546801000000000000000090046001600160a01b0316835260a590915281208054909190614bdc9084906155dd565b9091555050604051614bef90849061578a565b604051908190038120906001600160a01b0384169087907fe70d3820e244d5f71d1a6395db24f3460e8dca966edc1fd3625b6292880a877a905f90a45050505050565b5f83815260a4602052604081206001015460a954909190606490614c5690846157b6565b614c6091906157cd565b9050614c6c81836155f0565b60aa5f828254614c7c91906155dd565b90915550506001600160a01b0384165f90815260a5602052604081208054839290614ca89084906155dd565b9091555050604051614cbb90849061578a565b604051908190038120906001600160a01b0386169087907fe70d3820e244d5f71d1a6395db24f3460e8dca966edc1fd3625b6292880a877a905f90a45050505050565b8015611b62575f826001600160a01b0316826040515f6040518083038185875af1925050503d805f8114614d4d576040519150601f19603f3d011682016040523d82523d5f602084013e614d52565b606091505b50509050806121345760405162461bcd60e51b815260206004820152601b60248201527f526f6c6c75703a20455448207472616e73666572206661696c656400000000006044820152606401610c0a565b614dab614002565b6065805460ff191660011790557f62e78cea01bee320cd4e420270b5ea74000d11b0c9f74754ebdbfc544b05a258614de03390565b6040516001600160a01b03909116815260200160405180910390a1565b614e05615182565b6065805460ff191690557f5db9ee0a495bf2e6ff9c91a7834c1ba4fdd244a5e8aa4e537bd38aeae4b073aa33614de0565b5f805f805f855160208701885afa91503d905060208114821615614e5e5760205f803e5f5192505b818015614e6b5750806020145b614eb75760405162461bcd60e51b815260206004820152601b60248201527f696e76616c6964207375626d697474657220696e7465726661636500000000006044820152606401610c0a565b505092915050565b5f81614f0d5760405162461bcd60e51b815260206004820152601260248201527f456d7074792062617463682068656164657200000000000000000000000000006044820152606401610c0a565b82825f818110614f1f57614f1f615805565b919091013560f81c949350505050565b5f8160f9811015614f825760405162461bcd60e51b815260206004820152601d60248201527f626174636820686561646572206c656e67746820746f6f20736d616c6c0000006044820152606401610c0a565b6040519150808483378082016040529250929050565b5f816101018114614f825760405162461bcd60e51b815260206004820181905260248201527f626174636820686561646572206c656e67746820697320696e636f72726563746044820152606401610c0a565b6040805160c085901b815260f084901b6008820152600a60208502820181019092525f9181016150208161ffff8716866151d4565b82900390912095945050505050565b8082535050565b6001600160a01b03811615612f60576001600160a01b0381165f90815260ae6020526040812080546001929061506d9084906155dd565b909155505050565b5f54610100900460ff166150f15760405162461bcd60e51b815260206004820152602b60248201527f496e697469616c697a61626c653a20636f6e7472616374206973206e6f74206960448201527f6e697469616c697a696e670000000000000000000000000000000000000000006064820152608401610c0a565b6065805460ff19169055565b5f54610100900460ff166151795760405162461bcd60e51b815260206004820152602b60248201527f496e697469616c697a61626c653a20636f6e7472616374206973206e6f74206960448201527f6e697469616c697a696e670000000000000000000000000000000000000000006064820152608401610c0a565b61220733614962565b60655460ff166122075760405162461bcd60e51b815260206004820152601460248201527f5061757361626c653a206e6f74207061757365640000000000000000000000006044820152606401610c0a565b5f825f036151e3575082615296565b609b546001600160a01b03165f5b84811015615290576040517fae453cd5000000000000000000000000000000000000000000000000000000008152600481018590525f906001600160a01b0384169063ae453cd590602401602060405180830381865afa158015615257573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061527b9190615603565b875250602090950194600193840193016151f1565b50849150505b9392505050565b5f602082840312156152ad575f80fd5b5035919050565b80356001600160a01b0381168114614248575f80fd5b5f602082840312156152da575f80fd5b615296826152b4565b5f8083601f8401126152f3575f80fd5b50813567ffffffffffffffff81111561530a575f80fd5b602083019150836020828501011115615321575f80fd5b9250929050565b5f805f6040848603121561533a575f80fd5b833567ffffffffffffffff811115615350575f80fd5b61535c868287016152e3565b909790965060209590950135949350505050565b5f8060208385031215615381575f80fd5b823567ffffffffffffffff811115615397575f80fd5b6153a3858286016152e3565b90969095509350505050565b5f60e0828403121561419b575f80fd5b5f805f805f606086880312156153d3575f80fd5b853567ffffffffffffffff808211156153ea575f80fd5b6153f689838a016153af565b9650602088013591508082111561540b575f80fd5b61541789838a016152e3565b9096509450604088013591508082111561542f575f80fd5b5061543c888289016152e3565b969995985093965092949392505050565b5f6020828403121561545d575f80fd5b813567ffffffffffffffff811115615473575f80fd5b61547f848285016153af565b949350505050565b5f805f805f8060c0878903121561549c575f80fd5b6154a5876152b4565b95506154b3602088016152b4565b94506154c1604088016152b4565b9350606087013592506080870135915060a087013590509295509295509295565b5f805f80604085870312156154f5575f80fd5b843567ffffffffffffffff8082111561550c575f80fd5b615518888389016152e3565b90965094506020870135915080821115615530575f80fd5b5061553d878288016152e3565b95989497509550505050565b8015158114612f60575f80fd5b5f60208284031215615566575f80fd5b813561529681615549565b803567ffffffffffffffff81168114614248575f80fd5b5f8060408385031215615599575f80fd5b6155a283615571565b946020939093013593505050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601160045260245ffd5b808201808211156118c2576118c26155b0565b818103818111156118c2576118c26155b0565b5f60208284031215615613575f80fd5b5051919050565b5f6020828403121561562a575f80fd5b813561ffff81168114615296575f80fd5b5f8083357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe184360301811261566e575f80fd5b83018035915067ffffffffffffffff821115615688575f80fd5b602001915036819003821315615321575f80fd5b5f602082840312156156ac575f80fd5b813560ff81168114615296575f80fd5b5f602082840312156156cc575f80fd5b815161529681615549565b5f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8203615707576157076155b0565b5060010190565b5f6020828403121561571e575f80fd5b61529682615571565b85815284602082015260806040820152826080820152828460a08301375f60a084830101525f60a07fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f86011683010190508260608301529695505050505050565b5f82515f5b818110156157a9576020818601810151858301520161578f565b505f920191825250919050565b80820281158282048414176118c2576118c26155b0565b5f82615800577f4e487b71000000000000000000000000000000000000000000000000000000005f52601260045260245ffd5b500490565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52603260045260245ffdfea164736f6c6343000818000a", } // RollupABI is the input ABI used to generate the binding from. @@ -408,21 +401,21 @@ func (_Rollup *RollupCallerSession) BatchChallengedSuccess(batchIndex *big.Int) // BatchDataStore is a free data retrieval call binding the contract method 0x5ef7a94a. // -// Solidity: function batchDataStore(uint256 batchIndex) view returns(uint256 originTimestamp, uint256 finalizeTimestamp, uint256 blockNumber, uint256 signedSequencersBitmap) +// Solidity: function batchDataStore(uint256 batchIndex) view returns(uint256 originTimestamp, uint256 finalizeTimestamp, uint256 blockNumber, address submitter) func (_Rollup *RollupCaller) BatchDataStore(opts *bind.CallOpts, batchIndex *big.Int) (struct { - OriginTimestamp *big.Int - FinalizeTimestamp *big.Int - BlockNumber *big.Int - SignedSequencersBitmap *big.Int + OriginTimestamp *big.Int + FinalizeTimestamp *big.Int + BlockNumber *big.Int + Submitter common.Address }, error) { var out []interface{} err := _Rollup.contract.Call(opts, &out, "batchDataStore", batchIndex) outstruct := new(struct { - OriginTimestamp *big.Int - FinalizeTimestamp *big.Int - BlockNumber *big.Int - SignedSequencersBitmap *big.Int + OriginTimestamp *big.Int + FinalizeTimestamp *big.Int + BlockNumber *big.Int + Submitter common.Address }) if err != nil { return *outstruct, err @@ -431,7 +424,7 @@ func (_Rollup *RollupCaller) BatchDataStore(opts *bind.CallOpts, batchIndex *big outstruct.OriginTimestamp = *abi.ConvertType(out[0], new(*big.Int)).(**big.Int) outstruct.FinalizeTimestamp = *abi.ConvertType(out[1], new(*big.Int)).(**big.Int) outstruct.BlockNumber = *abi.ConvertType(out[2], new(*big.Int)).(**big.Int) - outstruct.SignedSequencersBitmap = *abi.ConvertType(out[3], new(*big.Int)).(**big.Int) + outstruct.Submitter = *abi.ConvertType(out[3], new(common.Address)).(*common.Address) return *outstruct, err @@ -439,24 +432,24 @@ func (_Rollup *RollupCaller) BatchDataStore(opts *bind.CallOpts, batchIndex *big // BatchDataStore is a free data retrieval call binding the contract method 0x5ef7a94a. // -// Solidity: function batchDataStore(uint256 batchIndex) view returns(uint256 originTimestamp, uint256 finalizeTimestamp, uint256 blockNumber, uint256 signedSequencersBitmap) +// Solidity: function batchDataStore(uint256 batchIndex) view returns(uint256 originTimestamp, uint256 finalizeTimestamp, uint256 blockNumber, address submitter) func (_Rollup *RollupSession) BatchDataStore(batchIndex *big.Int) (struct { - OriginTimestamp *big.Int - FinalizeTimestamp *big.Int - BlockNumber *big.Int - SignedSequencersBitmap *big.Int + OriginTimestamp *big.Int + FinalizeTimestamp *big.Int + BlockNumber *big.Int + Submitter common.Address }, error) { return _Rollup.Contract.BatchDataStore(&_Rollup.CallOpts, batchIndex) } // BatchDataStore is a free data retrieval call binding the contract method 0x5ef7a94a. // -// Solidity: function batchDataStore(uint256 batchIndex) view returns(uint256 originTimestamp, uint256 finalizeTimestamp, uint256 blockNumber, uint256 signedSequencersBitmap) +// Solidity: function batchDataStore(uint256 batchIndex) view returns(uint256 originTimestamp, uint256 finalizeTimestamp, uint256 blockNumber, address submitter) func (_Rollup *RollupCallerSession) BatchDataStore(batchIndex *big.Int) (struct { - OriginTimestamp *big.Int - FinalizeTimestamp *big.Int - BlockNumber *big.Int - SignedSequencersBitmap *big.Int + OriginTimestamp *big.Int + FinalizeTimestamp *big.Int + BlockNumber *big.Int + Submitter common.Address }, error) { return _Rollup.Contract.BatchDataStore(&_Rollup.CallOpts, batchIndex) } @@ -836,37 +829,6 @@ func (_Rollup *RollupCallerSession) IsChallenger(challengerAddress common.Addres return _Rollup.Contract.IsChallenger(&_Rollup.CallOpts, challengerAddress) } -// L1StakingContract is a free data retrieval call binding the contract method 0xddd8a3dc. -// -// Solidity: function l1StakingContract() view returns(address) -func (_Rollup *RollupCaller) L1StakingContract(opts *bind.CallOpts) (common.Address, error) { - var out []interface{} - err := _Rollup.contract.Call(opts, &out, "l1StakingContract") - - if err != nil { - return *new(common.Address), err - } - - out0 := *abi.ConvertType(out[0], new(common.Address)).(*common.Address) - - return out0, err - -} - -// L1StakingContract is a free data retrieval call binding the contract method 0xddd8a3dc. -// -// Solidity: function l1StakingContract() view returns(address) -func (_Rollup *RollupSession) L1StakingContract() (common.Address, error) { - return _Rollup.Contract.L1StakingContract(&_Rollup.CallOpts) -} - -// L1StakingContract is a free data retrieval call binding the contract method 0xddd8a3dc. -// -// Solidity: function l1StakingContract() view returns(address) -func (_Rollup *RollupCallerSession) L1StakingContract() (common.Address, error) { - return _Rollup.Contract.L1StakingContract(&_Rollup.CallOpts) -} - // LastCommittedBatchIndex is a free data retrieval call binding the contract method 0x121dcd50. // // Solidity: function lastCommittedBatchIndex() view returns(uint256) @@ -1022,6 +984,37 @@ func (_Rollup *RollupCallerSession) Paused() (bool, error) { return _Rollup.Contract.Paused(&_Rollup.CallOpts) } +// PendingBatchCount is a free data retrieval call binding the contract method 0x8ef622a8. +// +// Solidity: function pendingBatchCount(address submitter) view returns(uint256 count) +func (_Rollup *RollupCaller) PendingBatchCount(opts *bind.CallOpts, submitter common.Address) (*big.Int, error) { + var out []interface{} + err := _Rollup.contract.Call(opts, &out, "pendingBatchCount", submitter) + + if err != nil { + return *new(*big.Int), err + } + + out0 := *abi.ConvertType(out[0], new(*big.Int)).(**big.Int) + + return out0, err + +} + +// PendingBatchCount is a free data retrieval call binding the contract method 0x8ef622a8. +// +// Solidity: function pendingBatchCount(address submitter) view returns(uint256 count) +func (_Rollup *RollupSession) PendingBatchCount(submitter common.Address) (*big.Int, error) { + return _Rollup.Contract.PendingBatchCount(&_Rollup.CallOpts, submitter) +} + +// PendingBatchCount is a free data retrieval call binding the contract method 0x8ef622a8. +// +// Solidity: function pendingBatchCount(address submitter) view returns(uint256 count) +func (_Rollup *RollupCallerSession) PendingBatchCount(submitter common.Address) (*big.Int, error) { + return _Rollup.Contract.PendingBatchCount(&_Rollup.CallOpts, submitter) +} + // ProofRewardPercent is a free data retrieval call binding the contract method 0xfb1e8b04. // // Solidity: function proofRewardPercent() view returns(uint256) @@ -1206,6 +1199,37 @@ func (_Rollup *RollupCallerSession) RollupDelayPeriod() (*big.Int, error) { return _Rollup.Contract.RollupDelayPeriod(&_Rollup.CallOpts) } +// SubmitterContract is a free data retrieval call binding the contract method 0xa3c9e94c. +// +// Solidity: function submitterContract() view returns(address) +func (_Rollup *RollupCaller) SubmitterContract(opts *bind.CallOpts) (common.Address, error) { + var out []interface{} + err := _Rollup.contract.Call(opts, &out, "submitterContract") + + if err != nil { + return *new(common.Address), err + } + + out0 := *abi.ConvertType(out[0], new(common.Address)).(*common.Address) + + return out0, err + +} + +// SubmitterContract is a free data retrieval call binding the contract method 0xa3c9e94c. +// +// Solidity: function submitterContract() view returns(address) +func (_Rollup *RollupSession) SubmitterContract() (common.Address, error) { + return _Rollup.Contract.SubmitterContract(&_Rollup.CallOpts) +} + +// SubmitterContract is a free data retrieval call binding the contract method 0xa3c9e94c. +// +// Solidity: function submitterContract() view returns(address) +func (_Rollup *RollupCallerSession) SubmitterContract() (common.Address, error) { + return _Rollup.Contract.SubmitterContract(&_Rollup.CallOpts) +} + // Verifier is a free data retrieval call binding the contract method 0x2b7ac3f3. // // Solidity: function verifier() view returns(address) @@ -1352,67 +1376,67 @@ func (_Rollup *RollupTransactorSession) ClaimReward(receiver common.Address) (*t return _Rollup.Contract.ClaimReward(&_Rollup.TransactOpts, receiver) } -// CommitBatch is a paid mutator transaction binding the contract method 0x428868b5. +// CommitBatch is a paid mutator transaction binding the contract method 0x41f756da. // -// Solidity: function commitBatch((uint8,bytes,uint64,uint16,bytes32,bytes32,bytes32) batchDataInput, (uint256,bytes,bytes) batchSignatureInput) payable returns() -func (_Rollup *RollupTransactor) CommitBatch(opts *bind.TransactOpts, batchDataInput IRollupBatchDataInput, batchSignatureInput IRollupBatchSignatureInput) (*types.Transaction, error) { - return _Rollup.contract.Transact(opts, "commitBatch", batchDataInput, batchSignatureInput) +// Solidity: function commitBatch((uint8,bytes,uint64,uint16,bytes32,bytes32,bytes32) batchDataInput) payable returns() +func (_Rollup *RollupTransactor) CommitBatch(opts *bind.TransactOpts, batchDataInput IRollupBatchDataInput) (*types.Transaction, error) { + return _Rollup.contract.Transact(opts, "commitBatch", batchDataInput) } -// CommitBatch is a paid mutator transaction binding the contract method 0x428868b5. +// CommitBatch is a paid mutator transaction binding the contract method 0x41f756da. // -// Solidity: function commitBatch((uint8,bytes,uint64,uint16,bytes32,bytes32,bytes32) batchDataInput, (uint256,bytes,bytes) batchSignatureInput) payable returns() -func (_Rollup *RollupSession) CommitBatch(batchDataInput IRollupBatchDataInput, batchSignatureInput IRollupBatchSignatureInput) (*types.Transaction, error) { - return _Rollup.Contract.CommitBatch(&_Rollup.TransactOpts, batchDataInput, batchSignatureInput) +// Solidity: function commitBatch((uint8,bytes,uint64,uint16,bytes32,bytes32,bytes32) batchDataInput) payable returns() +func (_Rollup *RollupSession) CommitBatch(batchDataInput IRollupBatchDataInput) (*types.Transaction, error) { + return _Rollup.Contract.CommitBatch(&_Rollup.TransactOpts, batchDataInput) } -// CommitBatch is a paid mutator transaction binding the contract method 0x428868b5. +// CommitBatch is a paid mutator transaction binding the contract method 0x41f756da. // -// Solidity: function commitBatch((uint8,bytes,uint64,uint16,bytes32,bytes32,bytes32) batchDataInput, (uint256,bytes,bytes) batchSignatureInput) payable returns() -func (_Rollup *RollupTransactorSession) CommitBatch(batchDataInput IRollupBatchDataInput, batchSignatureInput IRollupBatchSignatureInput) (*types.Transaction, error) { - return _Rollup.Contract.CommitBatch(&_Rollup.TransactOpts, batchDataInput, batchSignatureInput) +// Solidity: function commitBatch((uint8,bytes,uint64,uint16,bytes32,bytes32,bytes32) batchDataInput) payable returns() +func (_Rollup *RollupTransactorSession) CommitBatch(batchDataInput IRollupBatchDataInput) (*types.Transaction, error) { + return _Rollup.Contract.CommitBatch(&_Rollup.TransactOpts, batchDataInput) } -// CommitBatchWithProof is a paid mutator transaction binding the contract method 0x4e8f1d67. +// CommitBatchWithProof is a paid mutator transaction binding the contract method 0x1544ba3a. // -// Solidity: function commitBatchWithProof((uint8,bytes,uint64,uint16,bytes32,bytes32,bytes32) batchDataInput, (uint256,bytes,bytes) batchSignatureInput, bytes _batchHeader, bytes _batchProof) returns() -func (_Rollup *RollupTransactor) CommitBatchWithProof(opts *bind.TransactOpts, batchDataInput IRollupBatchDataInput, batchSignatureInput IRollupBatchSignatureInput, _batchHeader []byte, _batchProof []byte) (*types.Transaction, error) { - return _Rollup.contract.Transact(opts, "commitBatchWithProof", batchDataInput, batchSignatureInput, _batchHeader, _batchProof) +// Solidity: function commitBatchWithProof((uint8,bytes,uint64,uint16,bytes32,bytes32,bytes32) batchDataInput, bytes _batchHeader, bytes _batchProof) returns() +func (_Rollup *RollupTransactor) CommitBatchWithProof(opts *bind.TransactOpts, batchDataInput IRollupBatchDataInput, _batchHeader []byte, _batchProof []byte) (*types.Transaction, error) { + return _Rollup.contract.Transact(opts, "commitBatchWithProof", batchDataInput, _batchHeader, _batchProof) } -// CommitBatchWithProof is a paid mutator transaction binding the contract method 0x4e8f1d67. +// CommitBatchWithProof is a paid mutator transaction binding the contract method 0x1544ba3a. // -// Solidity: function commitBatchWithProof((uint8,bytes,uint64,uint16,bytes32,bytes32,bytes32) batchDataInput, (uint256,bytes,bytes) batchSignatureInput, bytes _batchHeader, bytes _batchProof) returns() -func (_Rollup *RollupSession) CommitBatchWithProof(batchDataInput IRollupBatchDataInput, batchSignatureInput IRollupBatchSignatureInput, _batchHeader []byte, _batchProof []byte) (*types.Transaction, error) { - return _Rollup.Contract.CommitBatchWithProof(&_Rollup.TransactOpts, batchDataInput, batchSignatureInput, _batchHeader, _batchProof) +// Solidity: function commitBatchWithProof((uint8,bytes,uint64,uint16,bytes32,bytes32,bytes32) batchDataInput, bytes _batchHeader, bytes _batchProof) returns() +func (_Rollup *RollupSession) CommitBatchWithProof(batchDataInput IRollupBatchDataInput, _batchHeader []byte, _batchProof []byte) (*types.Transaction, error) { + return _Rollup.Contract.CommitBatchWithProof(&_Rollup.TransactOpts, batchDataInput, _batchHeader, _batchProof) } -// CommitBatchWithProof is a paid mutator transaction binding the contract method 0x4e8f1d67. +// CommitBatchWithProof is a paid mutator transaction binding the contract method 0x1544ba3a. // -// Solidity: function commitBatchWithProof((uint8,bytes,uint64,uint16,bytes32,bytes32,bytes32) batchDataInput, (uint256,bytes,bytes) batchSignatureInput, bytes _batchHeader, bytes _batchProof) returns() -func (_Rollup *RollupTransactorSession) CommitBatchWithProof(batchDataInput IRollupBatchDataInput, batchSignatureInput IRollupBatchSignatureInput, _batchHeader []byte, _batchProof []byte) (*types.Transaction, error) { - return _Rollup.Contract.CommitBatchWithProof(&_Rollup.TransactOpts, batchDataInput, batchSignatureInput, _batchHeader, _batchProof) +// Solidity: function commitBatchWithProof((uint8,bytes,uint64,uint16,bytes32,bytes32,bytes32) batchDataInput, bytes _batchHeader, bytes _batchProof) returns() +func (_Rollup *RollupTransactorSession) CommitBatchWithProof(batchDataInput IRollupBatchDataInput, _batchHeader []byte, _batchProof []byte) (*types.Transaction, error) { + return _Rollup.Contract.CommitBatchWithProof(&_Rollup.TransactOpts, batchDataInput, _batchHeader, _batchProof) } -// CommitState is a paid mutator transaction binding the contract method 0x1e8825be. +// CommitState is a paid mutator transaction binding the contract method 0x67caa37a. // -// Solidity: function commitState((uint8,bytes,uint64,uint16,bytes32,bytes32,bytes32) batchDataInput, (uint256,bytes,bytes) batchSignatureInput) returns() -func (_Rollup *RollupTransactor) CommitState(opts *bind.TransactOpts, batchDataInput IRollupBatchDataInput, batchSignatureInput IRollupBatchSignatureInput) (*types.Transaction, error) { - return _Rollup.contract.Transact(opts, "commitState", batchDataInput, batchSignatureInput) +// Solidity: function commitState((uint8,bytes,uint64,uint16,bytes32,bytes32,bytes32) batchDataInput) returns() +func (_Rollup *RollupTransactor) CommitState(opts *bind.TransactOpts, batchDataInput IRollupBatchDataInput) (*types.Transaction, error) { + return _Rollup.contract.Transact(opts, "commitState", batchDataInput) } -// CommitState is a paid mutator transaction binding the contract method 0x1e8825be. +// CommitState is a paid mutator transaction binding the contract method 0x67caa37a. // -// Solidity: function commitState((uint8,bytes,uint64,uint16,bytes32,bytes32,bytes32) batchDataInput, (uint256,bytes,bytes) batchSignatureInput) returns() -func (_Rollup *RollupSession) CommitState(batchDataInput IRollupBatchDataInput, batchSignatureInput IRollupBatchSignatureInput) (*types.Transaction, error) { - return _Rollup.Contract.CommitState(&_Rollup.TransactOpts, batchDataInput, batchSignatureInput) +// Solidity: function commitState((uint8,bytes,uint64,uint16,bytes32,bytes32,bytes32) batchDataInput) returns() +func (_Rollup *RollupSession) CommitState(batchDataInput IRollupBatchDataInput) (*types.Transaction, error) { + return _Rollup.Contract.CommitState(&_Rollup.TransactOpts, batchDataInput) } -// CommitState is a paid mutator transaction binding the contract method 0x1e8825be. +// CommitState is a paid mutator transaction binding the contract method 0x67caa37a. // -// Solidity: function commitState((uint8,bytes,uint64,uint16,bytes32,bytes32,bytes32) batchDataInput, (uint256,bytes,bytes) batchSignatureInput) returns() -func (_Rollup *RollupTransactorSession) CommitState(batchDataInput IRollupBatchDataInput, batchSignatureInput IRollupBatchSignatureInput) (*types.Transaction, error) { - return _Rollup.Contract.CommitState(&_Rollup.TransactOpts, batchDataInput, batchSignatureInput) +// Solidity: function commitState((uint8,bytes,uint64,uint16,bytes32,bytes32,bytes32) batchDataInput) returns() +func (_Rollup *RollupTransactorSession) CommitState(batchDataInput IRollupBatchDataInput) (*types.Transaction, error) { + return _Rollup.Contract.CommitState(&_Rollup.TransactOpts, batchDataInput) } // FinalizeBatch is a paid mutator transaction binding the contract method 0x13361101. @@ -1459,23 +1483,23 @@ func (_Rollup *RollupTransactorSession) ImportGenesisBatch(_batchHeader []byte) // Initialize is a paid mutator transaction binding the contract method 0x728cdbca. // -// Solidity: function initialize(address _l1StakingContract, address _messageQueue, address _verifier, uint256 _finalizationPeriodSeconds, uint256 _proofWindow, uint256 _proofRewardPercent) returns() -func (_Rollup *RollupTransactor) Initialize(opts *bind.TransactOpts, _l1StakingContract common.Address, _messageQueue common.Address, _verifier common.Address, _finalizationPeriodSeconds *big.Int, _proofWindow *big.Int, _proofRewardPercent *big.Int) (*types.Transaction, error) { - return _Rollup.contract.Transact(opts, "initialize", _l1StakingContract, _messageQueue, _verifier, _finalizationPeriodSeconds, _proofWindow, _proofRewardPercent) +// Solidity: function initialize(address _submitterContract, address _messageQueue, address _verifier, uint256 _finalizationPeriodSeconds, uint256 _proofWindow, uint256 _proofRewardPercent) returns() +func (_Rollup *RollupTransactor) Initialize(opts *bind.TransactOpts, _submitterContract common.Address, _messageQueue common.Address, _verifier common.Address, _finalizationPeriodSeconds *big.Int, _proofWindow *big.Int, _proofRewardPercent *big.Int) (*types.Transaction, error) { + return _Rollup.contract.Transact(opts, "initialize", _submitterContract, _messageQueue, _verifier, _finalizationPeriodSeconds, _proofWindow, _proofRewardPercent) } // Initialize is a paid mutator transaction binding the contract method 0x728cdbca. // -// Solidity: function initialize(address _l1StakingContract, address _messageQueue, address _verifier, uint256 _finalizationPeriodSeconds, uint256 _proofWindow, uint256 _proofRewardPercent) returns() -func (_Rollup *RollupSession) Initialize(_l1StakingContract common.Address, _messageQueue common.Address, _verifier common.Address, _finalizationPeriodSeconds *big.Int, _proofWindow *big.Int, _proofRewardPercent *big.Int) (*types.Transaction, error) { - return _Rollup.Contract.Initialize(&_Rollup.TransactOpts, _l1StakingContract, _messageQueue, _verifier, _finalizationPeriodSeconds, _proofWindow, _proofRewardPercent) +// Solidity: function initialize(address _submitterContract, address _messageQueue, address _verifier, uint256 _finalizationPeriodSeconds, uint256 _proofWindow, uint256 _proofRewardPercent) returns() +func (_Rollup *RollupSession) Initialize(_submitterContract common.Address, _messageQueue common.Address, _verifier common.Address, _finalizationPeriodSeconds *big.Int, _proofWindow *big.Int, _proofRewardPercent *big.Int) (*types.Transaction, error) { + return _Rollup.Contract.Initialize(&_Rollup.TransactOpts, _submitterContract, _messageQueue, _verifier, _finalizationPeriodSeconds, _proofWindow, _proofRewardPercent) } // Initialize is a paid mutator transaction binding the contract method 0x728cdbca. // -// Solidity: function initialize(address _l1StakingContract, address _messageQueue, address _verifier, uint256 _finalizationPeriodSeconds, uint256 _proofWindow, uint256 _proofRewardPercent) returns() -func (_Rollup *RollupTransactorSession) Initialize(_l1StakingContract common.Address, _messageQueue common.Address, _verifier common.Address, _finalizationPeriodSeconds *big.Int, _proofWindow *big.Int, _proofRewardPercent *big.Int) (*types.Transaction, error) { - return _Rollup.Contract.Initialize(&_Rollup.TransactOpts, _l1StakingContract, _messageQueue, _verifier, _finalizationPeriodSeconds, _proofWindow, _proofRewardPercent) +// Solidity: function initialize(address _submitterContract, address _messageQueue, address _verifier, uint256 _finalizationPeriodSeconds, uint256 _proofWindow, uint256 _proofRewardPercent) returns() +func (_Rollup *RollupTransactorSession) Initialize(_submitterContract common.Address, _messageQueue common.Address, _verifier common.Address, _finalizationPeriodSeconds *big.Int, _proofWindow *big.Int, _proofRewardPercent *big.Int) (*types.Transaction, error) { + return _Rollup.Contract.Initialize(&_Rollup.TransactOpts, _submitterContract, _messageQueue, _verifier, _finalizationPeriodSeconds, _proofWindow, _proofRewardPercent) } // Initialize2 is a paid mutator transaction binding the contract method 0x61267290. @@ -1520,6 +1544,27 @@ func (_Rollup *RollupTransactorSession) Initialize3(_rollupDelayPeriod *big.Int) return _Rollup.Contract.Initialize3(&_Rollup.TransactOpts, _rollupDelayPeriod) } +// Initialize4 is a paid mutator transaction binding the contract method 0xf8fa010f. +// +// Solidity: function initialize4(address _newSubmitter) returns() +func (_Rollup *RollupTransactor) Initialize4(opts *bind.TransactOpts, _newSubmitter common.Address) (*types.Transaction, error) { + return _Rollup.contract.Transact(opts, "initialize4", _newSubmitter) +} + +// Initialize4 is a paid mutator transaction binding the contract method 0xf8fa010f. +// +// Solidity: function initialize4(address _newSubmitter) returns() +func (_Rollup *RollupSession) Initialize4(_newSubmitter common.Address) (*types.Transaction, error) { + return _Rollup.Contract.Initialize4(&_Rollup.TransactOpts, _newSubmitter) +} + +// Initialize4 is a paid mutator transaction binding the contract method 0xf8fa010f. +// +// Solidity: function initialize4(address _newSubmitter) returns() +func (_Rollup *RollupTransactorSession) Initialize4(_newSubmitter common.Address) (*types.Transaction, error) { + return _Rollup.Contract.Initialize4(&_Rollup.TransactOpts, _newSubmitter) +} + // ProveState is a paid mutator transaction binding the contract method 0xb3484425. // // Solidity: function proveState(bytes _batchHeader, bytes _batchProof) returns() @@ -3250,6 +3295,159 @@ func (_Rollup *RollupFilterer) ParseRevertBatch(log types.Log) (*RollupRevertBat return event, nil } +// RollupSubmitterContractUpdatedIterator is returned from FilterSubmitterContractUpdated and is used to iterate over the raw logs and unpacked data for SubmitterContractUpdated events raised by the Rollup contract. +type RollupSubmitterContractUpdatedIterator struct { + Event *RollupSubmitterContractUpdated // Event containing the contract specifics and raw log + + contract *bind.BoundContract // Generic contract to use for unpacking event data + event string // Event name to use for unpacking event data + + logs chan types.Log // Log channel receiving the found contract events + sub ethereum.Subscription // Subscription for errors, completion and termination + done bool // Whether the subscription completed delivering logs + fail error // Occurred error to stop iteration +} + +// Next advances the iterator to the subsequent event, returning whether there +// are any more events found. In case of a retrieval or parsing error, false is +// returned and Error() can be queried for the exact failure. +func (it *RollupSubmitterContractUpdatedIterator) Next() bool { + // If the iterator failed, stop iterating + if it.fail != nil { + return false + } + // If the iterator completed, deliver directly whatever's available + if it.done { + select { + case log := <-it.logs: + it.Event = new(RollupSubmitterContractUpdated) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + default: + return false + } + } + // Iterator still in progress, wait for either a data or an error event + select { + case log := <-it.logs: + it.Event = new(RollupSubmitterContractUpdated) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + case err := <-it.sub.Err(): + it.done = true + it.fail = err + return it.Next() + } +} + +// Error returns any retrieval or parsing error occurred during filtering. +func (it *RollupSubmitterContractUpdatedIterator) Error() error { + return it.fail +} + +// Close terminates the iteration process, releasing any pending underlying +// resources. +func (it *RollupSubmitterContractUpdatedIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +// RollupSubmitterContractUpdated represents a SubmitterContractUpdated event raised by the Rollup contract. +type RollupSubmitterContractUpdated struct { + OldAddr common.Address + NewAddr common.Address + Raw types.Log // Blockchain specific contextual infos +} + +// FilterSubmitterContractUpdated is a free log retrieval operation binding the contract event 0xde3ada76ae1aa387cc4c586c70304b9fae54b251958786c828aaa0ce0290f939. +// +// Solidity: event SubmitterContractUpdated(address indexed oldAddr, address indexed newAddr) +func (_Rollup *RollupFilterer) FilterSubmitterContractUpdated(opts *bind.FilterOpts, oldAddr []common.Address, newAddr []common.Address) (*RollupSubmitterContractUpdatedIterator, error) { + + var oldAddrRule []interface{} + for _, oldAddrItem := range oldAddr { + oldAddrRule = append(oldAddrRule, oldAddrItem) + } + var newAddrRule []interface{} + for _, newAddrItem := range newAddr { + newAddrRule = append(newAddrRule, newAddrItem) + } + + logs, sub, err := _Rollup.contract.FilterLogs(opts, "SubmitterContractUpdated", oldAddrRule, newAddrRule) + if err != nil { + return nil, err + } + return &RollupSubmitterContractUpdatedIterator{contract: _Rollup.contract, event: "SubmitterContractUpdated", logs: logs, sub: sub}, nil +} + +// WatchSubmitterContractUpdated is a free log subscription operation binding the contract event 0xde3ada76ae1aa387cc4c586c70304b9fae54b251958786c828aaa0ce0290f939. +// +// Solidity: event SubmitterContractUpdated(address indexed oldAddr, address indexed newAddr) +func (_Rollup *RollupFilterer) WatchSubmitterContractUpdated(opts *bind.WatchOpts, sink chan<- *RollupSubmitterContractUpdated, oldAddr []common.Address, newAddr []common.Address) (event.Subscription, error) { + + var oldAddrRule []interface{} + for _, oldAddrItem := range oldAddr { + oldAddrRule = append(oldAddrRule, oldAddrItem) + } + var newAddrRule []interface{} + for _, newAddrItem := range newAddr { + newAddrRule = append(newAddrRule, newAddrItem) + } + + logs, sub, err := _Rollup.contract.WatchLogs(opts, "SubmitterContractUpdated", oldAddrRule, newAddrRule) + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + // New log arrived, parse the event and forward to the user + event := new(RollupSubmitterContractUpdated) + if err := _Rollup.contract.UnpackLog(event, "SubmitterContractUpdated", log); err != nil { + return err + } + event.Raw = log + + select { + case sink <- event: + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + } + }), nil +} + +// ParseSubmitterContractUpdated is a log parse operation binding the contract event 0xde3ada76ae1aa387cc4c586c70304b9fae54b251958786c828aaa0ce0290f939. +// +// Solidity: event SubmitterContractUpdated(address indexed oldAddr, address indexed newAddr) +func (_Rollup *RollupFilterer) ParseSubmitterContractUpdated(log types.Log) (*RollupSubmitterContractUpdated, error) { + event := new(RollupSubmitterContractUpdated) + if err := _Rollup.contract.UnpackLog(event, "SubmitterContractUpdated", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + // RollupUnpausedIterator is returned from FilterUnpaused and is used to iterate over the raw logs and unpacked data for Unpaused events raised by the Rollup contract. type RollupUnpausedIterator struct { Event *RollupUnpaused // Event containing the contract specifics and raw log diff --git a/bindings/bindings/rollup_more.go b/bindings/bindings/rollup_more.go index a78f509fd..e0a97d7fc 100644 --- a/bindings/bindings/rollup_more.go +++ b/bindings/bindings/rollup_more.go @@ -9,11 +9,11 @@ import ( "morph-l2/bindings/solc" ) -const RollupStorageLayoutJSON = "{\"storage\":[{\"astId\":1000,\"contract\":\"contracts/l1/rollup/Rollup.sol:Rollup\",\"label\":\"_initialized\",\"offset\":0,\"slot\":\"0\",\"type\":\"t_uint8\"},{\"astId\":1001,\"contract\":\"contracts/l1/rollup/Rollup.sol:Rollup\",\"label\":\"_initializing\",\"offset\":1,\"slot\":\"0\",\"type\":\"t_bool\"},{\"astId\":1002,\"contract\":\"contracts/l1/rollup/Rollup.sol:Rollup\",\"label\":\"__gap\",\"offset\":0,\"slot\":\"1\",\"type\":\"t_array(t_uint256)1031_storage\"},{\"astId\":1003,\"contract\":\"contracts/l1/rollup/Rollup.sol:Rollup\",\"label\":\"_owner\",\"offset\":0,\"slot\":\"51\",\"type\":\"t_address\"},{\"astId\":1004,\"contract\":\"contracts/l1/rollup/Rollup.sol:Rollup\",\"label\":\"__gap\",\"offset\":0,\"slot\":\"52\",\"type\":\"t_array(t_uint256)1030_storage\"},{\"astId\":1005,\"contract\":\"contracts/l1/rollup/Rollup.sol:Rollup\",\"label\":\"_paused\",\"offset\":0,\"slot\":\"101\",\"type\":\"t_bool\"},{\"astId\":1006,\"contract\":\"contracts/l1/rollup/Rollup.sol:Rollup\",\"label\":\"__gap\",\"offset\":0,\"slot\":\"102\",\"type\":\"t_array(t_uint256)1030_storage\"},{\"astId\":1007,\"contract\":\"contracts/l1/rollup/Rollup.sol:Rollup\",\"label\":\"l1StakingContract\",\"offset\":0,\"slot\":\"151\",\"type\":\"t_address\"},{\"astId\":1008,\"contract\":\"contracts/l1/rollup/Rollup.sol:Rollup\",\"label\":\"finalizationPeriodSeconds\",\"offset\":0,\"slot\":\"152\",\"type\":\"t_uint256\"},{\"astId\":1009,\"contract\":\"contracts/l1/rollup/Rollup.sol:Rollup\",\"label\":\"proofWindow\",\"offset\":0,\"slot\":\"153\",\"type\":\"t_uint256\"},{\"astId\":1010,\"contract\":\"contracts/l1/rollup/Rollup.sol:Rollup\",\"label\":\"__maxNumTxInChunk\",\"offset\":0,\"slot\":\"154\",\"type\":\"t_uint256\"},{\"astId\":1011,\"contract\":\"contracts/l1/rollup/Rollup.sol:Rollup\",\"label\":\"messageQueue\",\"offset\":0,\"slot\":\"155\",\"type\":\"t_address\"},{\"astId\":1012,\"contract\":\"contracts/l1/rollup/Rollup.sol:Rollup\",\"label\":\"verifier\",\"offset\":0,\"slot\":\"156\",\"type\":\"t_address\"},{\"astId\":1013,\"contract\":\"contracts/l1/rollup/Rollup.sol:Rollup\",\"label\":\"lastFinalizedBatchIndex\",\"offset\":0,\"slot\":\"157\",\"type\":\"t_uint256\"},{\"astId\":1014,\"contract\":\"contracts/l1/rollup/Rollup.sol:Rollup\",\"label\":\"lastCommittedBatchIndex\",\"offset\":0,\"slot\":\"158\",\"type\":\"t_uint256\"},{\"astId\":1015,\"contract\":\"contracts/l1/rollup/Rollup.sol:Rollup\",\"label\":\"isChallenger\",\"offset\":0,\"slot\":\"159\",\"type\":\"t_mapping(t_address,t_bool)\"},{\"astId\":1016,\"contract\":\"contracts/l1/rollup/Rollup.sol:Rollup\",\"label\":\"finalizedStateRoots\",\"offset\":0,\"slot\":\"160\",\"type\":\"t_mapping(t_uint256,t_bytes32)\"},{\"astId\":1017,\"contract\":\"contracts/l1/rollup/Rollup.sol:Rollup\",\"label\":\"committedBatches\",\"offset\":0,\"slot\":\"161\",\"type\":\"t_mapping(t_uint256,t_bytes32)\"},{\"astId\":1018,\"contract\":\"contracts/l1/rollup/Rollup.sol:Rollup\",\"label\":\"batchDataStore\",\"offset\":0,\"slot\":\"162\",\"type\":\"t_mapping(t_uint256,t_struct(BatchData)1033_storage)\"},{\"astId\":1019,\"contract\":\"contracts/l1/rollup/Rollup.sol:Rollup\",\"label\":\"withdrawalRoots\",\"offset\":0,\"slot\":\"163\",\"type\":\"t_mapping(t_bytes32,t_bool)\"},{\"astId\":1020,\"contract\":\"contracts/l1/rollup/Rollup.sol:Rollup\",\"label\":\"challenges\",\"offset\":0,\"slot\":\"164\",\"type\":\"t_mapping(t_uint256,t_struct(BatchChallenge)1032_storage)\"},{\"astId\":1021,\"contract\":\"contracts/l1/rollup/Rollup.sol:Rollup\",\"label\":\"batchChallengeReward\",\"offset\":0,\"slot\":\"165\",\"type\":\"t_mapping(t_address,t_uint256)\"},{\"astId\":1022,\"contract\":\"contracts/l1/rollup/Rollup.sol:Rollup\",\"label\":\"inChallenge\",\"offset\":0,\"slot\":\"166\",\"type\":\"t_bool\"},{\"astId\":1023,\"contract\":\"contracts/l1/rollup/Rollup.sol:Rollup\",\"label\":\"batchChallenged\",\"offset\":0,\"slot\":\"167\",\"type\":\"t_uint256\"},{\"astId\":1024,\"contract\":\"contracts/l1/rollup/Rollup.sol:Rollup\",\"label\":\"revertReqIndex\",\"offset\":0,\"slot\":\"168\",\"type\":\"t_uint256\"},{\"astId\":1025,\"contract\":\"contracts/l1/rollup/Rollup.sol:Rollup\",\"label\":\"proofRewardPercent\",\"offset\":0,\"slot\":\"169\",\"type\":\"t_uint256\"},{\"astId\":1026,\"contract\":\"contracts/l1/rollup/Rollup.sol:Rollup\",\"label\":\"proveRemaining\",\"offset\":0,\"slot\":\"170\",\"type\":\"t_uint256\"},{\"astId\":1027,\"contract\":\"contracts/l1/rollup/Rollup.sol:Rollup\",\"label\":\"committedStateRoots\",\"offset\":0,\"slot\":\"171\",\"type\":\"t_mapping(t_uint256,t_bytes32)\"},{\"astId\":1028,\"contract\":\"contracts/l1/rollup/Rollup.sol:Rollup\",\"label\":\"rollupDelayPeriod\",\"offset\":0,\"slot\":\"172\",\"type\":\"t_uint256\"},{\"astId\":1029,\"contract\":\"contracts/l1/rollup/Rollup.sol:Rollup\",\"label\":\"batchBlobVersionedHashes\",\"offset\":0,\"slot\":\"173\",\"type\":\"t_mapping(t_uint256,t_bytes32)\"}],\"types\":{\"t_address\":{\"encoding\":\"inplace\",\"label\":\"address\",\"numberOfBytes\":\"20\"},\"t_array(t_uint256)1030_storage\":{\"encoding\":\"inplace\",\"label\":\"uint256[49]\",\"numberOfBytes\":\"1568\"},\"t_array(t_uint256)1031_storage\":{\"encoding\":\"inplace\",\"label\":\"uint256[50]\",\"numberOfBytes\":\"1600\"},\"t_bool\":{\"encoding\":\"inplace\",\"label\":\"bool\",\"numberOfBytes\":\"1\"},\"t_bytes32\":{\"encoding\":\"inplace\",\"label\":\"bytes32\",\"numberOfBytes\":\"32\"},\"t_mapping(t_address,t_bool)\":{\"encoding\":\"mapping\",\"label\":\"mapping(address =\u003e bool)\",\"numberOfBytes\":\"32\",\"key\":\"t_address\",\"value\":\"t_bool\"},\"t_mapping(t_address,t_uint256)\":{\"encoding\":\"mapping\",\"label\":\"mapping(address =\u003e uint256)\",\"numberOfBytes\":\"32\",\"key\":\"t_address\",\"value\":\"t_uint256\"},\"t_mapping(t_bytes32,t_bool)\":{\"encoding\":\"mapping\",\"label\":\"mapping(bytes32 =\u003e bool)\",\"numberOfBytes\":\"32\",\"key\":\"t_bytes32\",\"value\":\"t_bool\"},\"t_mapping(t_uint256,t_bytes32)\":{\"encoding\":\"mapping\",\"label\":\"mapping(uint256 =\u003e bytes32)\",\"numberOfBytes\":\"32\",\"key\":\"t_uint256\",\"value\":\"t_bytes32\"},\"t_mapping(t_uint256,t_struct(BatchChallenge)1032_storage)\":{\"encoding\":\"mapping\",\"label\":\"mapping(uint256 =\u003e struct IRollup.BatchChallenge)\",\"numberOfBytes\":\"32\",\"key\":\"t_uint256\",\"value\":\"t_struct(BatchChallenge)1032_storage\"},\"t_mapping(t_uint256,t_struct(BatchData)1033_storage)\":{\"encoding\":\"mapping\",\"label\":\"mapping(uint256 =\u003e struct IRollup.BatchData)\",\"numberOfBytes\":\"32\",\"key\":\"t_uint256\",\"value\":\"t_struct(BatchData)1033_storage\"},\"t_struct(BatchChallenge)1032_storage\":{\"encoding\":\"inplace\",\"label\":\"struct IRollup.BatchChallenge\",\"numberOfBytes\":\"128\"},\"t_struct(BatchData)1033_storage\":{\"encoding\":\"inplace\",\"label\":\"struct IRollup.BatchData\",\"numberOfBytes\":\"128\"},\"t_uint256\":{\"encoding\":\"inplace\",\"label\":\"uint256\",\"numberOfBytes\":\"32\"},\"t_uint64\":{\"encoding\":\"inplace\",\"label\":\"uint64\",\"numberOfBytes\":\"8\"},\"t_uint8\":{\"encoding\":\"inplace\",\"label\":\"uint8\",\"numberOfBytes\":\"1\"}}}" +const RollupStorageLayoutJSON = "{\"storage\":[{\"astId\":1000,\"contract\":\"contracts/l1/rollup/Rollup.sol:Rollup\",\"label\":\"_initialized\",\"offset\":0,\"slot\":\"0\",\"type\":\"t_uint8\"},{\"astId\":1001,\"contract\":\"contracts/l1/rollup/Rollup.sol:Rollup\",\"label\":\"_initializing\",\"offset\":1,\"slot\":\"0\",\"type\":\"t_bool\"},{\"astId\":1002,\"contract\":\"contracts/l1/rollup/Rollup.sol:Rollup\",\"label\":\"__gap\",\"offset\":0,\"slot\":\"1\",\"type\":\"t_array(t_uint256)1032_storage\"},{\"astId\":1003,\"contract\":\"contracts/l1/rollup/Rollup.sol:Rollup\",\"label\":\"_owner\",\"offset\":0,\"slot\":\"51\",\"type\":\"t_address\"},{\"astId\":1004,\"contract\":\"contracts/l1/rollup/Rollup.sol:Rollup\",\"label\":\"__gap\",\"offset\":0,\"slot\":\"52\",\"type\":\"t_array(t_uint256)1031_storage\"},{\"astId\":1005,\"contract\":\"contracts/l1/rollup/Rollup.sol:Rollup\",\"label\":\"_paused\",\"offset\":0,\"slot\":\"101\",\"type\":\"t_bool\"},{\"astId\":1006,\"contract\":\"contracts/l1/rollup/Rollup.sol:Rollup\",\"label\":\"__gap\",\"offset\":0,\"slot\":\"102\",\"type\":\"t_array(t_uint256)1031_storage\"},{\"astId\":1007,\"contract\":\"contracts/l1/rollup/Rollup.sol:Rollup\",\"label\":\"submitterContract\",\"offset\":0,\"slot\":\"151\",\"type\":\"t_address\"},{\"astId\":1008,\"contract\":\"contracts/l1/rollup/Rollup.sol:Rollup\",\"label\":\"finalizationPeriodSeconds\",\"offset\":0,\"slot\":\"152\",\"type\":\"t_uint256\"},{\"astId\":1009,\"contract\":\"contracts/l1/rollup/Rollup.sol:Rollup\",\"label\":\"proofWindow\",\"offset\":0,\"slot\":\"153\",\"type\":\"t_uint256\"},{\"astId\":1010,\"contract\":\"contracts/l1/rollup/Rollup.sol:Rollup\",\"label\":\"__maxNumTxInChunk\",\"offset\":0,\"slot\":\"154\",\"type\":\"t_uint256\"},{\"astId\":1011,\"contract\":\"contracts/l1/rollup/Rollup.sol:Rollup\",\"label\":\"messageQueue\",\"offset\":0,\"slot\":\"155\",\"type\":\"t_address\"},{\"astId\":1012,\"contract\":\"contracts/l1/rollup/Rollup.sol:Rollup\",\"label\":\"verifier\",\"offset\":0,\"slot\":\"156\",\"type\":\"t_address\"},{\"astId\":1013,\"contract\":\"contracts/l1/rollup/Rollup.sol:Rollup\",\"label\":\"lastFinalizedBatchIndex\",\"offset\":0,\"slot\":\"157\",\"type\":\"t_uint256\"},{\"astId\":1014,\"contract\":\"contracts/l1/rollup/Rollup.sol:Rollup\",\"label\":\"lastCommittedBatchIndex\",\"offset\":0,\"slot\":\"158\",\"type\":\"t_uint256\"},{\"astId\":1015,\"contract\":\"contracts/l1/rollup/Rollup.sol:Rollup\",\"label\":\"isChallenger\",\"offset\":0,\"slot\":\"159\",\"type\":\"t_mapping(t_address,t_bool)\"},{\"astId\":1016,\"contract\":\"contracts/l1/rollup/Rollup.sol:Rollup\",\"label\":\"finalizedStateRoots\",\"offset\":0,\"slot\":\"160\",\"type\":\"t_mapping(t_uint256,t_bytes32)\"},{\"astId\":1017,\"contract\":\"contracts/l1/rollup/Rollup.sol:Rollup\",\"label\":\"committedBatches\",\"offset\":0,\"slot\":\"161\",\"type\":\"t_mapping(t_uint256,t_bytes32)\"},{\"astId\":1018,\"contract\":\"contracts/l1/rollup/Rollup.sol:Rollup\",\"label\":\"batchDataStore\",\"offset\":0,\"slot\":\"162\",\"type\":\"t_mapping(t_uint256,t_struct(BatchData)1034_storage)\"},{\"astId\":1019,\"contract\":\"contracts/l1/rollup/Rollup.sol:Rollup\",\"label\":\"withdrawalRoots\",\"offset\":0,\"slot\":\"163\",\"type\":\"t_mapping(t_bytes32,t_bool)\"},{\"astId\":1020,\"contract\":\"contracts/l1/rollup/Rollup.sol:Rollup\",\"label\":\"challenges\",\"offset\":0,\"slot\":\"164\",\"type\":\"t_mapping(t_uint256,t_struct(BatchChallenge)1033_storage)\"},{\"astId\":1021,\"contract\":\"contracts/l1/rollup/Rollup.sol:Rollup\",\"label\":\"batchChallengeReward\",\"offset\":0,\"slot\":\"165\",\"type\":\"t_mapping(t_address,t_uint256)\"},{\"astId\":1022,\"contract\":\"contracts/l1/rollup/Rollup.sol:Rollup\",\"label\":\"inChallenge\",\"offset\":0,\"slot\":\"166\",\"type\":\"t_bool\"},{\"astId\":1023,\"contract\":\"contracts/l1/rollup/Rollup.sol:Rollup\",\"label\":\"batchChallenged\",\"offset\":0,\"slot\":\"167\",\"type\":\"t_uint256\"},{\"astId\":1024,\"contract\":\"contracts/l1/rollup/Rollup.sol:Rollup\",\"label\":\"revertReqIndex\",\"offset\":0,\"slot\":\"168\",\"type\":\"t_uint256\"},{\"astId\":1025,\"contract\":\"contracts/l1/rollup/Rollup.sol:Rollup\",\"label\":\"proofRewardPercent\",\"offset\":0,\"slot\":\"169\",\"type\":\"t_uint256\"},{\"astId\":1026,\"contract\":\"contracts/l1/rollup/Rollup.sol:Rollup\",\"label\":\"proveRemaining\",\"offset\":0,\"slot\":\"170\",\"type\":\"t_uint256\"},{\"astId\":1027,\"contract\":\"contracts/l1/rollup/Rollup.sol:Rollup\",\"label\":\"committedStateRoots\",\"offset\":0,\"slot\":\"171\",\"type\":\"t_mapping(t_uint256,t_bytes32)\"},{\"astId\":1028,\"contract\":\"contracts/l1/rollup/Rollup.sol:Rollup\",\"label\":\"rollupDelayPeriod\",\"offset\":0,\"slot\":\"172\",\"type\":\"t_uint256\"},{\"astId\":1029,\"contract\":\"contracts/l1/rollup/Rollup.sol:Rollup\",\"label\":\"batchBlobVersionedHashes\",\"offset\":0,\"slot\":\"173\",\"type\":\"t_mapping(t_uint256,t_bytes32)\"},{\"astId\":1030,\"contract\":\"contracts/l1/rollup/Rollup.sol:Rollup\",\"label\":\"pendingBatchCount\",\"offset\":0,\"slot\":\"174\",\"type\":\"t_mapping(t_address,t_uint256)\"}],\"types\":{\"t_address\":{\"encoding\":\"inplace\",\"label\":\"address\",\"numberOfBytes\":\"20\"},\"t_array(t_uint256)1031_storage\":{\"encoding\":\"inplace\",\"label\":\"uint256[49]\",\"numberOfBytes\":\"1568\"},\"t_array(t_uint256)1032_storage\":{\"encoding\":\"inplace\",\"label\":\"uint256[50]\",\"numberOfBytes\":\"1600\"},\"t_bool\":{\"encoding\":\"inplace\",\"label\":\"bool\",\"numberOfBytes\":\"1\"},\"t_bytes32\":{\"encoding\":\"inplace\",\"label\":\"bytes32\",\"numberOfBytes\":\"32\"},\"t_mapping(t_address,t_bool)\":{\"encoding\":\"mapping\",\"label\":\"mapping(address =\u003e bool)\",\"numberOfBytes\":\"32\",\"key\":\"t_address\",\"value\":\"t_bool\"},\"t_mapping(t_address,t_uint256)\":{\"encoding\":\"mapping\",\"label\":\"mapping(address =\u003e uint256)\",\"numberOfBytes\":\"32\",\"key\":\"t_address\",\"value\":\"t_uint256\"},\"t_mapping(t_bytes32,t_bool)\":{\"encoding\":\"mapping\",\"label\":\"mapping(bytes32 =\u003e bool)\",\"numberOfBytes\":\"32\",\"key\":\"t_bytes32\",\"value\":\"t_bool\"},\"t_mapping(t_uint256,t_bytes32)\":{\"encoding\":\"mapping\",\"label\":\"mapping(uint256 =\u003e bytes32)\",\"numberOfBytes\":\"32\",\"key\":\"t_uint256\",\"value\":\"t_bytes32\"},\"t_mapping(t_uint256,t_struct(BatchChallenge)1033_storage)\":{\"encoding\":\"mapping\",\"label\":\"mapping(uint256 =\u003e struct IRollup.BatchChallenge)\",\"numberOfBytes\":\"32\",\"key\":\"t_uint256\",\"value\":\"t_struct(BatchChallenge)1033_storage\"},\"t_mapping(t_uint256,t_struct(BatchData)1034_storage)\":{\"encoding\":\"mapping\",\"label\":\"mapping(uint256 =\u003e struct IRollup.BatchData)\",\"numberOfBytes\":\"32\",\"key\":\"t_uint256\",\"value\":\"t_struct(BatchData)1034_storage\"},\"t_struct(BatchChallenge)1033_storage\":{\"encoding\":\"inplace\",\"label\":\"struct IRollup.BatchChallenge\",\"numberOfBytes\":\"128\"},\"t_struct(BatchData)1034_storage\":{\"encoding\":\"inplace\",\"label\":\"struct IRollup.BatchData\",\"numberOfBytes\":\"128\"},\"t_uint256\":{\"encoding\":\"inplace\",\"label\":\"uint256\",\"numberOfBytes\":\"32\"},\"t_uint64\":{\"encoding\":\"inplace\",\"label\":\"uint64\",\"numberOfBytes\":\"8\"},\"t_uint8\":{\"encoding\":\"inplace\",\"label\":\"uint8\",\"numberOfBytes\":\"1\"}}}" var RollupStorageLayout = new(solc.StorageLayout) -var RollupDeployedBin = "0x608060405260043610610353575f3560e01c8063728cdbca116101bd578063b8d0a1b0116100f2578063d8dc99d211610092578063dff7827e1161006d578063dff7827e14610b1f578063e3fff1dd14610b34578063f2fde38b14610b53578063fb1e8b0414610b72575f80fd5b8063d8dc99d214610abf578063ddd8a3dc14610ad4578063de8b303514610b00575f80fd5b8063cd4edc69116100cd578063cd4edc6914610a59578063ce5db8d614610a6c578063cf9a674514610a81578063d279c19114610aa0575f80fd5b8063b8d0a1b0146109fc578063bedb86fb14610a1b578063c555389214610a3a575f80fd5b8063a479265d1161015d578063b31a77d311610138578063b31a77d31461098a578063b34844251461099f578063b35dac4e146109be578063b3e0a509146109dd575f80fd5b8063a479265d1461092b578063a4f209b014610940578063abc8d68d1461095f575f80fd5b80638f1d3776116101985780638f1d3776146107e3578063910129d4146108ad57806397fc007c146108de578063a415d8dc146108fd575f80fd5b8063728cdbca1461078157806388b1ea09146107a05780638da5cb5b146107b9575f80fd5b80632a213ba1116102935780635c975abb11610233578063612672901161020e578063612672901461070457806368589dfa146107235780636c578c1d1461074e578063715018a61461076d575f80fd5b80635c975abb146106415780635ef7a94a146106585780635f77cf1d146106b8575f80fd5b80633e001b661161026e5780633e001b66146105db578063428868b5146105f05780634e8f1d671461060357806357e0af6c14610622575f80fd5b80632a213ba1146105335780632b7ac3f31461055e5780633b70c18a146105af575f80fd5b806313361101116102fe5780631e8825be116102d95780631e8825be1461049f57806321e2f9e0146104be5780632362f03e146104dd5780632571098d14610508575f80fd5b8063133611011461043b57806318463fb01461045a57806318af3b2b1461046f575f80fd5b806310d445831161032e57806310d44583146103e5578063116a1f4214610404578063121dcd5014610426575f80fd5b806304d772151461035e578063059def61146103a15780630ceb6780146103c4575f80fd5b3661035a57005b5f80fd5b348015610369575f80fd5b5061038c610378366004615c9f565b60a36020525f908152604090205460ff1681565b60405190151581526020015b60405180910390f35b3480156103ac575f80fd5b506103b6609d5481565b604051908152602001610398565b3480156103cf575f80fd5b506103e36103de366004615cd9565b610b87565b005b3480156103f0575f80fd5b506103e36103ff366004615d37565b610cab565b34801561040f575f80fd5b5061038c61041e366004615c9f565b609d54101590565b348015610431575f80fd5b506103b6609e5481565b348015610446575f80fd5b506103e3610455366004615d7f565b611097565b348015610465575f80fd5b506103b660a75481565b34801561047a575f80fd5b5061038c610489366004615c9f565b5f90815260a26020526040902060010154421090565b3480156104aa575f80fd5b506103e36104b9366004615dde565b6116b6565b3480156104c9575f80fd5b5061038c6104d8366004615c9f565b611b64565b3480156104e8575f80fd5b506103b66104f7366004615c9f565b60a16020525f908152604090205481565b348015610513575f80fd5b506103b6610522366004615c9f565b60a06020525f908152604090205481565b34801561053e575f80fd5b506103b661054d366004615c9f565b60ad6020525f908152604090205481565b348015610569575f80fd5b50609c5461058a9073ffffffffffffffffffffffffffffffffffffffff1681565b60405173ffffffffffffffffffffffffffffffffffffffff9091168152602001610398565b3480156105ba575f80fd5b50609b5461058a9073ffffffffffffffffffffffffffffffffffffffff1681565b3480156105e6575f80fd5b506103b660aa5481565b6103e36105fe366004615dde565b611b92565b34801561060e575f80fd5b506103e361061d366004615e3e565b611fd6565b34801561062d575f80fd5b506103e361063c366004615c9f565b612440565b34801561064c575f80fd5b5060655460ff1661038c565b348015610663575f80fd5b50610698610672366004615c9f565b60a26020525f908152604090208054600182015460028301546003909301549192909184565b604080519485526020850193909352918301526060820152608001610398565b3480156106c3575f80fd5b506106eb7f000000000000000000000000000000000000000000000000000000000000000081565b60405167ffffffffffffffff9091168152602001610398565b34801561070f575f80fd5b506103e361071e366004615c9f565b612505565b34801561072e575f80fd5b506103b661073d366004615c9f565b60ab6020525f908152604090205481565b348015610759575f80fd5b506103e3610768366004615cd9565b612768565b348015610778575f80fd5b506103e3612877565b34801561078c575f80fd5b506103e361079b366004615eef565b61288a565b3480156107ab575f80fd5b5060a65461038c9060ff1681565b3480156107c4575f80fd5b5060335473ffffffffffffffffffffffffffffffffffffffff1661058a565b3480156107ee575f80fd5b5061085c6107fd366004615c9f565b60a46020525f9081526040902080546001820154600283015460039093015467ffffffffffffffff8316936801000000000000000090930473ffffffffffffffffffffffffffffffffffffffff16929060ff8082169161010090041686565b6040805167ffffffffffffffff909716875273ffffffffffffffffffffffffffffffffffffffff909516602087015293850192909252606084015215156080830152151560a082015260c001610398565b3480156108b8575f80fd5b5061038c6108c7366004615c9f565b5f90815260a4602052604090206003015460ff1690565b3480156108e9575f80fd5b506103e36108f8366004615cd9565b612c53565b348015610908575f80fd5b5061038c610917366004615cd9565b609f6020525f908152604090205460ff1681565b348015610936575f80fd5b506103b660995481565b34801561094b575f80fd5b506103e361095a366004615c9f565b612d77565b34801561096a575f80fd5b506103b6610979366004615cd9565b60a56020525f908152604090205481565b348015610995575f80fd5b506103b660a85481565b3480156109aa575f80fd5b506103e36109b9366004615f4a565b612e41565b3480156109c9575f80fd5b506103e36109d8366004615cd9565b613220565b3480156109e8575f80fd5b506103e36109f7366004615d7f565b613286565b348015610a07575f80fd5b506103e3610a16366004615f4a565b61367f565b348015610a26575f80fd5b506103e3610a35366004615fbe565b613722565b348015610a45575f80fd5b506103e3610a54366004615c9f565b6138b5565b6103e3610a67366004615ff0565b613972565b348015610a77575f80fd5b506103b660985481565b348015610a8c575f80fd5b506103e3610a9b366004615c9f565b614087565b348015610aab575f80fd5b506103e3610aba366004615cd9565b614264565b348015610aca575f80fd5b506103b660ac5481565b348015610adf575f80fd5b5060975461058a9073ffffffffffffffffffffffffffffffffffffffff1681565b348015610b0b575f80fd5b5061038c610b1a366004615c9f565b61434a565b348015610b2a575f80fd5b506103b6609a5481565b348015610b3f575f80fd5b506103e3610b4e366004615c9f565b6143a1565b348015610b5e575f80fd5b506103e3610b6d366004615cd9565b61445e565b348015610b7d575f80fd5b506103b660a95481565b610b8f614512565b73ffffffffffffffffffffffffffffffffffffffff81165f908152609f602052604090205460ff1615610c23576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601f60248201527f6163636f756e7420697320616c72656164792061206368616c6c656e6765720060448201526064015b60405180910390fd5b73ffffffffffffffffffffffffffffffffffffffff81165f818152609f602090815260409182902080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0016600190811790915591519182527f7feb653c7b1f0d23daeed494225b3f28851cdc8973fcc653866d9b6e205fc00991015b60405180910390a250565b610cb3614512565b5f8111610d1c576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601560248201527f636f756e74206d757374206265206e6f6e7a65726f00000000000000000000006044820152606401610c1a565b5f80610d288585614593565b915091505f610d3b836001015160c01c90565b5f81815260a160205260409020549091508214610db4576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601460248201527f696e636f727265637420626174636820686173680000000000000000000000006044820152606401610c1a565b5f60a181610dc28785616045565b81526020019081526020015f205414610e5c576040517f08c379a0000000000000000000000000000000000000000000000000000000008152602060048201526024808201527f726576657274696e67206d7573742073746172742066726f6d2074686520656e60448201527f64696e67000000000000000000000000000000000000000000000000000000006064820152608401610c1a565b609d548111610eed576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602160248201527f63616e206f6e6c792072657665727420756e46696e616c697a6564206261746360448201527f68000000000000000000000000000000000000000000000000000000000000006064820152608401610c1a565b610ef8600182616058565b609e555b831561108f57604051829082907ecae2739091badfd91c373f0a16cede691e0cd25bb80cff77dd5caeb4710146905f90a35f81815260a16020526040812055610f448161434a565b15610fca575f81815260a4602090815260408083206001810154905468010000000000000000900473ffffffffffffffffffffffffffffffffffffffff16845260a59092528220805491929091610f9c908490616045565b909155505060a680547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001690555b5f81815260a46020526040812080547fffffffff0000000000000000000000000000000000000000000000000000000016815560018101829055600281019190915560030180547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff000016905560a85415801590611047575060a85481145b15611051575f60a8555b6001015f81815260a160205260409020547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff90940193915081610efc575b505050505050565b60a85415611101576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601660248201527f70656e64696e67207265766572742072657175657374000000000000000000006044820152606401610c1a565b611109614655565b5f806111158484614593565b915091505f611128836001015160c01c90565b5f81815260a1602052604090205490915082146111a1576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601460248201527f696e636f727265637420626174636820686173680000000000000000000000006044820152606401610c1a565b6111aa81611b64565b611210576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600f60248201527f6261746368206e6f7420657869737400000000000000000000000000000000006044820152606401610c1a565b6112198161434a565b15611280576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601260248201527f626174636820696e206368616c6c656e676500000000000000000000000000006044820152606401610c1a565b5f81815260a4602052604090206003015460ff16156112fb576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601660248201527f62617463682073686f756c6420626520726576657274000000000000000000006044820152606401610c1a565b5f81815260a26020526040902060010154421015611375576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601960248201527f626174636820696e206368616c6c656e67652077696e646f77000000000000006044820152606401610c1a565b605983015160a05f611388600185616058565b81526020019081526020015f2054146113fd576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601d60248201527f696e636f72726563742070726576696f757320737461746520726f6f740000006044820152606401610c1a565b5f81815260a0602052604090205415611472576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601660248201527f626174636820616c7265616479207665726966696564000000000000000000006044820152606401610c1a565b80609d54600101146114e0576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601560248201527f696e636f727265637420626174636820696e64657800000000000000000000006044820152606401610c1a565b609d819055600160a35f6114f5866099015190565b815260208101919091526040015f2080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001691151591909117905560798301515f82815260a06020526040902055611562611555846011015160c01c90565b600985015160c01c6146c2565b60a25f611570600184616058565b815260208101919091526040015f908120818155600180820183905560028201839055600390910182905560ab91906115a99084616058565b81526020019081526020015f205f905560ad5f6001836115c99190616058565b81526020019081526020015f205f905560a45f6001836115e99190616058565b815260208082019290925260409081015f90812080547fffffffff00000000000000000000000000000000000000000000000000000000168155600181018290556002810182905560030180547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff000016905583815260a1909252902054817f26ba82f907317eedc97d0cbef23de76a43dd6edb563bdb6e9407645b950a7a2d611692866079015190565b60998701516040805192835260208301919091520160405180910390a35050505050565b60975473ffffffffffffffffffffffffffffffffffffffff166368015791336040517fffffffff0000000000000000000000000000000000000000000000000000000060e084901b16815273ffffffffffffffffffffffffffffffffffffffff9091166004820152602401602060405180830381865afa15801561173c573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611760919061606b565b6117c6576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601a60248201527f6f6e6c7920616374697665207374616b657220616c6c6f7765640000000000006044820152606401610c1a565b60a85415611830576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601660248201527f70656e64696e67207265766572742072657175657374000000000000000000006044820152606401610c1a565b611838614655565b5f49156118a1576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601f60248201527f636f6d6d69745374617465206d757374206e6f7420636172727920626c6f62006044820152606401610c1a565b5f6118b76118b26020850185616086565b614593565b5090505f6118c9826001015160c01c90565b6118d4906001616045565b5f81815260ad6020526040902054909150611971576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602260248201527f6e6f2073746f72656420626c6f62206861736820666f7220746869732062617460448201527f63680000000000000000000000000000000000000000000000000000000000006064820152608401610c1a565b60ac54609b54604080517fb59b1a78000000000000000000000000000000000000000000000000000000008152905142939273ffffffffffffffffffffffffffffffffffffffff169163b59b1a789160048083019260209291908290030181865afa1580156119e2573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611a0691906160e7565b611a109190616045565b1015611a92575f611a2760808601606087016160fe565b61ffff1611611a92576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600b60248201527f6c316d73672064656c61790000000000000000000000000000000000000000006044820152606401610c1a565b6097545f9073ffffffffffffffffffffffffffffffffffffffff1663d096c3c6336040517fffffffff0000000000000000000000000000000000000000000000000000000060e084901b16815273ffffffffffffffffffffffffffffffffffffffff9091166004820152602401602060405180830381865afa158015611b1a573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611b3e91906160e7565b9050611b5d85858360ad5f8781526020019081526020015f205461478c565b5050505050565b5f81815260a2602052604081205415801590611b8c57505f82815260a1602052604090205415155b92915050565b60975473ffffffffffffffffffffffffffffffffffffffff166368015791336040517fffffffff0000000000000000000000000000000000000000000000000000000060e084901b16815273ffffffffffffffffffffffffffffffffffffffff9091166004820152602401602060405180830381865afa158015611c18573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611c3c919061606b565b611ca2576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601a60248201527f6f6e6c7920616374697665207374616b657220616c6c6f7765640000000000006044820152606401610c1a565b60a85415611d0c576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601660248201527f70656e64696e67207265766572742072657175657374000000000000000000006044820152606401610c1a565b611d14614655565b5f611d256118b26020850185616086565b5090505f611d37826001015160c01c90565b611d42906001616045565b5f81815260ad602052604090205490915015611de0576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602860248201527f636f6d6d69744261746368207265717569726573206e6f2073746f726564206260448201527f6c6f6220686173680000000000000000000000000000000000000000000000006064820152608401610c1a565b60ac54609b54604080517fb59b1a78000000000000000000000000000000000000000000000000000000008152905142939273ffffffffffffffffffffffffffffffffffffffff169163b59b1a789160048083019260209291908290030181865afa158015611e51573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611e7591906160e7565b611e7f9190616045565b1015611f01575f611e9660808601606087016160fe565b61ffff1611611f01576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600b60248201527f6c316d73672064656c61790000000000000000000000000000000000000000006044820152606401610c1a565b6097545f9073ffffffffffffffffffffffffffffffffffffffff1663d096c3c6336040517fffffffff0000000000000000000000000000000000000000000000000000000060e084901b16815273ffffffffffffffffffffffffffffffffffffffff9091166004820152602401602060405180830381865afa158015611f89573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611fad91906160e7565b90505f611fc8611fc0602088018861611f565b60ff16614ec9565b905061108f8686848461478c565b60a85415612040576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601660248201527f70656e64696e67207265766572742072657175657374000000000000000000006044820152606401610c1a565b612048614655565b60ac54609e545f90815260a260205260408120549091429161206a9190616045565b1090505f4260ac54609b5f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663b59b1a786040518163ffffffff1660e01b8152600401602060405180830381865afa1580156120dc573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061210091906160e7565b61210a9190616045565b109050811580156121185750805b15612199575f61212e60808a0160608b016160fe565b61ffff1611612199576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600b60248201527f6c316d73672064656c61790000000000000000000000000000000000000000006044820152606401610c1a565b81806121a25750805b612208576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600e60248201527f696e76616c69642074696d696e670000000000000000000000000000000000006044820152606401610c1a565b5f6122196118b260208b018b616086565b5090505f61222b826001015160c01c90565b612236906001616045565b5f81815260ad602052604081205491925090156122f0575f49156122dc576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602f60248201527f6d757374206e6f7420636172727920626c6f62207768656e207573696e67207360448201527f746f72656420626c6f62206861736800000000000000000000000000000000006064820152608401610c1a565b505f81815260ad6020526040902054612303565b612300611fc060208d018d61611f565b90505b61230f8b8b5f8461478c565b5f8061231b8b8b614593565b915091505f61232e836001015160c01c90565b905080609e541461239b576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601660248201527f696e636f727265637420626174636820686561646572000000000000000000006044820152606401610c1a565b5f81815260a160205260409020548214612411576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601460248201527f696e636f727265637420626174636820686173680000000000000000000000006044820152606401610c1a565b5f81815260a26020526040902042600190910155612430838b8b615038565b5050505050505050505050505050565b612448614512565b5f8111801561245957506099548114155b6124bf576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601860248201527f696e76616c6964206e65772070726f6f662077696e646f7700000000000000006044820152606401610c1a565b609980549082905560408051828152602081018490527f1e3a2094feb4b696dd3d7caea38ad2f41dbdcac3fa3943c7a693aff8a64b0a6191015b60405180910390a15050565b5f54600290610100900460ff1615801561252557505f5460ff8083169116105b6125b1576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602e60248201527f496e697469616c697a61626c653a20636f6e747261637420697320616c72656160448201527f647920696e697469616c697a65640000000000000000000000000000000000006064820152608401610c1a565b5f80547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00001660ff8316176101001790556125ec5f5460ff1690565b60ff16600214612658576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601660248201527f6d757374206861766520696e697469616c697a656421000000000000000000006044820152606401610c1a565b816126e5576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602760248201527f63616e206e6f742073657420737461746520726f6f742077697468206279746560448201527f73333228302921000000000000000000000000000000000000000000000000006064820152608401610c1a565b609e545f90815260ab602052604090205461270e57609e545f90815260ab602052604090208290555b5f80547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ff16905560405160ff821681527f7f26b83ff96e1f2b6a682f133852f6798a09c465da95921460cefb3847402498906020016124f9565b612770614512565b73ffffffffffffffffffffffffffffffffffffffff81165f908152609f602052604090205460ff166127fe576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601b60248201527f6163636f756e74206973206e6f742061206368616c6c656e67657200000000006044820152606401610c1a565b73ffffffffffffffffffffffffffffffffffffffff81165f818152609f6020908152604080832080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00169055519182527f7feb653c7b1f0d23daeed494225b3f28851cdc8973fcc653866d9b6e205fc0099101610ca0565b61287f614512565b6128885f615212565b565b5f54610100900460ff16158080156128a857505f54600160ff909116105b806128c15750303b1580156128c157505f5460ff166001145b61294d576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602e60248201527f496e697469616c697a61626c653a20636f6e747261637420697320616c72656160448201527f647920696e697469616c697a65640000000000000000000000000000000000006064820152608401610c1a565b5f80547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0016600117905580156129a9575f80547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ff166101001790555b73ffffffffffffffffffffffffffffffffffffffff861615806129e0575073ffffffffffffffffffffffffffffffffffffffff8516155b15612a17576040517fecc6fdf000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b73ffffffffffffffffffffffffffffffffffffffff8716612a94576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601b60248201527f696e76616c6964206c31207374616b696e6720636f6e747261637400000000006044820152606401610c1a565b612a9c615288565b612aa4615326565b6097805473ffffffffffffffffffffffffffffffffffffffff808a167fffffffffffffffffffffffff000000000000000000000000000000000000000092831617909255609b8054898416908316179055609c805492881692909116821790556098859055609984905560a98390556040515f907f728af3d16a5760405e27a082c98ab272e9f0a1d02f0085d41532a26093aedd96908290a3604080515f8152602081018690527fa577f4223f91f74e2dad65bbb8c30807587ae95d0d34288057bb3ec0d398a437910160405180910390a1604080515f8152602081018590527f1e3a2094feb4b696dd3d7caea38ad2f41dbdcac3fa3943c7a693aff8a64b0a61910160405180910390a1604080515f8152602081018490527ffb81bce17f015797e11949d3c332e2bf9453faf68f728447426803138f2b0223910160405180910390a18015612c4a575f80547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ff169055604051600181527f7f26b83ff96e1f2b6a682f133852f6798a09c465da95921460cefb38474024989060200160405180910390a15b50505050505050565b612c5b614512565b73ffffffffffffffffffffffffffffffffffffffff811615801590612c9b5750609c5473ffffffffffffffffffffffffffffffffffffffff828116911614155b612d01576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601460248201527f696e76616c6964206e65772076657269666965720000000000000000000000006044820152606401610c1a565b609c805473ffffffffffffffffffffffffffffffffffffffff8381167fffffffffffffffffffffffff0000000000000000000000000000000000000000831681179093556040519116919082907f728af3d16a5760405e27a082c98ab272e9f0a1d02f0085d41532a26093aedd96905f90a35050565b612d7f614512565b5f81118015612d8f575060648111155b8015612d9d575060a9548114155b612e03576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601f60248201527f696e76616c69642070726f6f66207265776172642070657263656e74616765006044820152606401610c1a565b60a980549082905560408051828152602081018490527ffb81bce17f015797e11949d3c332e2bf9453faf68f728447426803138f2b022391016124f9565b60a85415612eab576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601660248201527f70656e64696e67207265766572742072657175657374000000000000000000006044820152606401610c1a565b612eb3614655565b60975473ffffffffffffffffffffffffffffffffffffffff166368015791336040517fffffffff0000000000000000000000000000000000000000000000000000000060e084901b16815273ffffffffffffffffffffffffffffffffffffffff9091166004820152602401602060405180830381865afa158015612f39573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190612f5d919061606b565b612fc3576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601a60248201527f6f6e6c7920616374697665207374616b657220616c6c6f7765640000000000006044820152606401610c1a565b5f80612fcf8686614593565b915091505f612fe2836001015160c01c90565b5f81815260a16020526040902054909150821461305b576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601460248201527f696e636f727265637420626174636820686173680000000000000000000000006044820152606401610c1a565b6130648161434a565b6130ca576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601260248201527f626174636820696e206368616c6c656e676500000000000000000000000000006044820152606401610c1a565b5f81815260a4602052604090206003810180547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ff1661010017905560a680547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00169055609954600290910154429161314191616045565b116131d5575f81815260a460209081526040808320600390810180547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0016600117905560a2835292819020909201548251808401909352600783527f54696d656f757400000000000000000000000000000000000000000000000000918301919091526131d0918391906153c4565b612c4a565b6131e0838686615038565b612c4a81336040518060400160405280600d81526020017f50726f6f66207375636365737300000000000000000000000000000000000000815250615547565b613228614512565b60aa80545f90915561323a828261562d565b6040805173ffffffffffffffffffffffffffffffffffffffff84168152602081018390527fb1b2058a6969e2d25e47bcaebe8ae21c29a23b2752429315b75e2f4f285f3d8791016124f9565b61328e614512565b5f805260a06020527fb84a74ec6ef4d0e83b6006dfaa014ab4026f9f3b97d186e604d29998a4e808ea541561331f576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601660248201527f67656e6573697320626174636820696d706f72746564000000000000000000006044820152606401610c1a565b5f8061332b8484614593565b915091505f61333e836001015160c01c90565b905080156133a8576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601360248201527f696e76616c696420626174636820696e646578000000000000000000000000006044820152606401610c1a565b5f6133b4846079015190565b90508061341d576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600f60248201527f7a65726f20737461746520726f6f7400000000000000000000000000000000006044820152606401610c1a565b600984015160c01c1561348c576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601d60248201527f6c31206d65737361676520706f707065642073686f756c6420626520300000006044820152606401610c1a565b5f613498856019015190565b036134ff576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600e60248201527f7a65726f206461746120686173680000000000000000000000000000000000006044820152606401610c1a565b7f010657f37554c781402a22917dee2f75def7ab966d7b770905398eba3c44401461352b856039015190565b14613592576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601660248201527f696e76616c69642076657273696f6e65642068617368000000000000000000006044820152606401610c1a565b5f82815260a1602090815260408083208690558051608081018252428082528184019081528183018581526060830186815288875260a28652848720935184559151600184015551600283015551600390910155603987015160ad83528184205560ab825280832084905560a0909152808220839055609e849055609d84905551849184917f2c32d4ae151744d0bf0b9464a3e897a1d17ed2f1af71f7c9a75f12ce0d28238f9190a3604080518281525f6020820152849184917f26ba82f907317eedc97d0cbef23de76a43dd6edb563bdb6e9407645b950a7a2d910160405180910390a3505050505050565b5f8061368b8686614593565b915091505f61369e836001015160c01c90565b5f81815260a160205260409020549091508214613717576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601460248201527f696e636f727265637420626174636820686173680000000000000000000000006044820152606401610c1a565b612c4a838686615038565b61372a614512565b8015613883576137386156ff565b60a65460ff16156138355760a7545f90815260a4602090815260408083206001810154905468010000000000000000900473ffffffffffffffffffffffffffffffffffffffff16845260a59092528220805491929091613799908490616045565b909155505060a7545f90815260a46020526040812080547fffffffff0000000000000000000000000000000000000000000000000000000016815560018101829055600281019190915560030180547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff000016905560a680547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001690555b7f62e78cea01bee320cd4e420270b5ea74000d11b0c9f74754ebdbfc544b05a258335b60405173ffffffffffffffffffffffffffffffffffffffff909116815260200160405180910390a150565b61388b615784565b7f5db9ee0a495bf2e6ff9c91a7834c1ba4fdd244a5e8aa4e537bd38aeae4b073aa33613858565b50565b6138bd614512565b5f811180156138ce575060ac548114155b613934576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601f60248201527f696e76616c6964206e657720726f6c6c75702064656c617920706572696f64006044820152606401610c1a565b60ac80549082905560408051828152602081018490527f624e47dc9fb8f8cfeaf4ead4710277cc1757136cfa885e465514cf6d510f0ad191016124f9565b335f908152609f602052604090205460ff166139ea576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601760248201527f6f6e6c79206368616c6c656e67657220616c6c6f7765640000000000000000006044820152606401610c1a565b60a85415613a54576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601660248201527f70656e64696e67207265766572742072657175657374000000000000000000006044820152606401610c1a565b613a5c614655565b60a65460ff1615613ac9576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601460248201527f616c726561647920696e206368616c6c656e67650000000000000000000000006044820152606401610c1a565b8167ffffffffffffffff16609d5410613b3e576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601760248201527f626174636820616c72656164792066696e616c697a65640000000000000000006044820152606401610c1a565b67ffffffffffffffff82165f90815260a160205260409020548114613bbf576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601460248201527f696e636f727265637420626174636820686173680000000000000000000000006044820152606401610c1a565b613bd28267ffffffffffffffff16611b64565b613c38576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600f60248201527f6261746368206e6f7420657869737400000000000000000000000000000000006044820152606401610c1a565b67ffffffffffffffff82165f90815260a4602052604090205468010000000000000000900473ffffffffffffffffffffffffffffffffffffffff1615613cda576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601860248201527f626174636820616c7265616479206368616c6c656e67656400000000000000006044820152606401610c1a565b67ffffffffffffffff82165f90815260a260205260409020600101544210613d84576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152603360248201527f63616e6e6f74206368616c6c656e6765206261746368206f757473696465207460448201527f6865206368616c6c656e67652077696e646f77000000000000000000000000006064820152608401610c1a565b60975f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16630d13fd7b6040518163ffffffff1660e01b8152600401602060405180830381865afa158015613dee573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190613e1291906160e7565b341015613e7b576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601260248201527f696e73756666696369656e742076616c756500000000000000000000000000006044820152606401610c1a565b67ffffffffffffffff82811660a78190556040805160c0810182528281523360208083018281523484860190815242606086019081525f6080870181815260a0880182815299825260a49095529690962094518554925173ffffffffffffffffffffffffffffffffffffffff1668010000000000000000027fffffffff00000000000000000000000000000000000000000000000000000000909316981697909717178355945160018301559151600282015592516003909301805492511515610100027fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ff941515949094167fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00009093169290921792909217905573ffffffffffffffffffffffffffffffffffffffff168267ffffffffffffffff167f3a6ea19df25b49e7624e313ce7c1ab23984238e93727260db56a81735b1b997634604051613fe791815260200190565b60405180910390a35f609d546001613fff9190616045565b90505b609e548111614057578267ffffffffffffffff168114614045576099545f82815260a260205260408120600101805490919061403f908490616045565b90915550505b8061404f8161613f565b915050614002565b505060a680547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0016600117905550565b5f54600390610100900460ff161580156140a757505f5460ff8083169116105b614133576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602e60248201527f496e697469616c697a61626c653a20636f6e747261637420697320616c72656160448201527f647920696e697469616c697a65640000000000000000000000000000000000006064820152608401610c1a565b5f80547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00001660ff8316176101001781558290036141cc576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601b60248201527f696e76616c696420726f6c6c75702064656c617920706572696f6400000000006044820152606401610c1a565b60ac829055604080515f8152602081018490527f624e47dc9fb8f8cfeaf4ead4710277cc1757136cfa885e465514cf6d510f0ad1910160405180910390a15f80547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ff16905560405160ff821681527f7f26b83ff96e1f2b6a682f133852f6798a09c465da95921460cefb3847402498906020016124f9565b335f90815260a56020526040812054908190036142dd576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601c60248201527f696e76616c69642062617463684368616c6c656e6765526577617264000000006044820152606401610c1a565b335f90815260a560205260408120556142f6828261562d565b8173ffffffffffffffffffffffffffffffffffffffff167f9c25fa83f414ed363c8d39c98fb3e17567b3431cede71eb062c49d2a63ce247a8260405161433e91815260200190565b60405180910390a25050565b5f81815260a4602052604081205468010000000000000000900473ffffffffffffffffffffffffffffffffffffffff1615801590611b8c5750505f90815260a46020526040902060030154610100900460ff161590565b6143a9614512565b5f811180156143ba57506098548114155b614420576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601b60248201527f696e76616c6964206e65772066696e616c697a6520706572696f6400000000006044820152606401610c1a565b609880549082905560408051828152602081018490527fa577f4223f91f74e2dad65bbb8c30807587ae95d0d34288057bb3ec0d398a43791016124f9565b614466614512565b73ffffffffffffffffffffffffffffffffffffffff8116614509576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602660248201527f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160448201527f64647265737300000000000000000000000000000000000000000000000000006064820152608401610c1a565b6138b281615212565b60335473ffffffffffffffffffffffffffffffffffffffff163314612888576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e65726044820152606401610c1a565b5f805f6145a085856157db565b90505f8160ff165f036145c1576145b78686615865565b9094509050614647565b8160ff16600114806145d657508160ff166002145b156145e5576145b786866158e8565b6040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601960248201527f556e737570706f727465642062617463682076657273696f6e000000000000006044820152606401610c1a565b808420925050509250929050565b60655460ff1615612888576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601060248201527f5061757361626c653a20706175736564000000000000000000000000000000006044820152606401610c1a565b805f036146cd575050565b8082035f5b82811015614786576101008184038111156146ec57508083035b609b546040517f3c7f5283000000000000000000000000000000000000000000000000000000008152600481018590526024810183905273ffffffffffffffffffffffffffffffffffffffff90911690633c7f5283906044015f604051808303815f87803b15801561475c575f80fd5b505af115801561476e573d5f803e3d5ffd5b505050506101008301925050610100810190506146d2565b50505050565b600261479b602086018661611f565b60ff161115614806576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600f60248201527f696e76616c69642076657273696f6e00000000000000000000000000000000006044820152606401610c1a565b6080840135614871576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601b60248201527f70726576696f757320737461746520726f6f74206973207a65726f00000000006044820152606401610c1a565b60a08401356148dc576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601660248201527f6e657720737461746520726f6f74206973207a65726f000000000000000000006044820152606401610c1a565b5f806148ee6118b26020880188616086565b915091505f614901836001015160c01c90565b90505f60a181614912846001616045565b81526020019081526020015f205414614987576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601760248201527f626174636820616c726561647920636f6d6d69747465640000000000000000006044820152606401610c1a565b609e5481146149f2576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601560248201527f696e636f727265637420626174636820696e64657800000000000000000000006044820152606401610c1a565b5f81815260a160205260409020548214614a68576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601b60248201527f696e636f727265637420706172656e74206261746368206861736800000000006044820152606401610c1a565b5f81815260ab6020526040902054608088013514614ae2576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601d60248201527f696e636f72726563742070726576696f757320737461746520726f6f740000006044820152606401610c1a565b5f614af1846011015160c01c90565b90505f614b1d614b0760608b0160408c01616176565b614b1760808c0160608d016160fe565b84615955565b9050614b2f60808a0160608b016160fe565b60019384019361ffff91909116929092019160f990614b5160208c018c61611f565b60ff1610614b5e57506101015b604080518281019091529550614b8386614b7b60208d018d61611f565b60ff16615999565b60c084901b6001870152614bb186614ba160808d0160608e016160fe565b61ffff1660c01b60099190910152565b60c083811b6011880152601987018390526039870188905260808b0135605988015260a08b013560798801528a01356099870152614c1586614bf660208c018c616086565b604051614c0492919061618f565b604051809103902060b99190910152565b60d986018590526001614c2b60208c018c61611f565b60ff1610614c5d57614c5d86614c4760608d0160408e01616176565b67ffffffffffffffff1660c01b60f99190910152565b8086205f85815260a1602090815260408083209390935560ab815282822060a08e0135905560ad905290812088905560a65460ff1615614cc65760a7545f90815260a460205260409020600201546099544291614cb991616045565b614cc39190616058565b90505b60405180608001604052804281526020018260985442614ce69190616045565b614cf09190616045565b8152602001614d0560608e0160408f01616176565b67ffffffffffffffff16815260209081018b90525f87815260a2825260409081902083518155838301516001820155908301516002820155606090920151600390920191909155609e86905560975473ffffffffffffffffffffffffffffffffffffffff1692506374fe27b791508a3590614d8c90614d86908d018d616086565b5f6159a0565b5f614d9a60408e018e616086565b6040518663ffffffff1660e01b8152600401614dba9594939291906161e5565b602060405180830381865afa158015614dd5573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190614df9919061606b565b614e85576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602160248201527f746865207369676e617475726520766572696669636174696f6e206661696c6560448201527f64000000000000000000000000000000000000000000000000000000000000006064820152608401610c1a565b5f83815260a16020526040808220549051909185917f2c32d4ae151744d0bf0b9464a3e897a1d17ed2f1af71f7c9a75f12ce0d28238f9190a3505050505050505050565b5f81600203614f72575f6040515f5b804980614ee55750614ef4565b60208202830152600101614ed8565b602081028083209201604052909250905080614f6c576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601b60248201527f5632207265717569726573206174206c65617374203120626c6f6200000000006044820152606401610c1a565b50919050565b60014915615002576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602560248201527f6c6567616379206261746368657320737570706f72742065786163746c79203160448201527f20626c6f620000000000000000000000000000000000000000000000000000006064820152608401610c1a565b5f4915615010575f49611b8c565b507f010657f37554c781402a22917dee2f75def7ab966d7b770905398eba3c4440145b919050565b8061509f576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601360248201527f496e76616c69642062617463682070726f6f66000000000000000000000000006044820152606401610c1a565b5f6150ae846001015160c01c90565b90505f6150bc856039015190565b90505f7f00000000000000000000000000000000000000000000000000000000000000006150eb876059015190565b6079880151609989015160b98a015160198b015160405160c09690961b7fffffffffffffffff000000000000000000000000000000000000000000000000166020870152602886019490945260488501929092526068840152608883015260a882015260c8810183905260e801604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529190528051602090910120609c5490915073ffffffffffffffffffffffffffffffffffffffff16632c09a8486151ba885160f81c90565b858888866040518663ffffffff1660e01b81526004016151de959493929190616265565b5f6040518083038186803b1580156151f4575f80fd5b505afa158015615206573d5f803e3d5ffd5b50505050505050505050565b6033805473ffffffffffffffffffffffffffffffffffffffff8381167fffffffffffffffffffffffff0000000000000000000000000000000000000000831681179093556040519116919082907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0905f90a35050565b5f54610100900460ff1661531e576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602b60248201527f496e697469616c697a61626c653a20636f6e7472616374206973206e6f74206960448201527f6e697469616c697a696e670000000000000000000000000000000000000000006064820152608401610c1a565b6128886159f4565b5f54610100900460ff166153bc576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602b60248201527f496e697469616c697a61626c653a20636f6e7472616374206973206e6f74206960448201527f6e697469616c697a696e670000000000000000000000000000000000000000006064820152608401610c1a565b612888615ab4565b60a88390555f83815260a460205260408082205460975491517f45bc4d10000000000000000000000000000000000000000000000000000000008152600481018690526801000000000000000090910473ffffffffffffffffffffffffffffffffffffffff908116939216906345bc4d10906024016020604051808303815f875af1158015615455573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061547991906160e7565b5f86815260a46020526040902060010154909150615498908290616045565b5f86815260a4602090815260408083205468010000000000000000900473ffffffffffffffffffffffffffffffffffffffff16835260a5909152812080549091906154e4908490616045565b90915550506040516154f7908490616296565b6040519081900381209073ffffffffffffffffffffffffffffffffffffffff84169087907fe70d3820e244d5f71d1a6395db24f3460e8dca966edc1fd3625b6292880a877a905f90a45050505050565b5f83815260a4602052604081206001015460a95490919060649061556b90846162c2565b61557591906162d9565b90506155818183616058565b60aa5f8282546155919190616045565b909155505073ffffffffffffffffffffffffffffffffffffffff84165f90815260a56020526040812080548392906155ca908490616045565b90915550506040516155dd908490616296565b6040519081900381209073ffffffffffffffffffffffffffffffffffffffff86169087907fe70d3820e244d5f71d1a6395db24f3460e8dca966edc1fd3625b6292880a877a905f90a45050505050565b80156156fb575f8273ffffffffffffffffffffffffffffffffffffffff16826040515f6040518083038185875af1925050503d805f8114615689576040519150601f19603f3d011682016040523d82523d5f602084013e61568e565b606091505b50509050806156f9576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601b60248201527f526f6c6c75703a20455448207472616e73666572206661696c656400000000006044820152606401610c1a565b505b5050565b615707614655565b606580547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001660011790557f62e78cea01bee320cd4e420270b5ea74000d11b0c9f74754ebdbfc544b05a25861575a3390565b60405173ffffffffffffffffffffffffffffffffffffffff909116815260200160405180910390a1565b61578c615b53565b606580547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001690557f5db9ee0a495bf2e6ff9c91a7834c1ba4fdd244a5e8aa4e537bd38aeae4b073aa3361575a565b5f81615843576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601260248201527f456d7074792062617463682068656164657200000000000000000000000000006044820152606401610c1a565b82825f81811061585557615855616311565b919091013560f81c949350505050565b5f8160f98110156158d2576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601d60248201527f626174636820686561646572206c656e67746820746f6f20736d616c6c0000006044820152606401610c1a565b6040519150808483378082016040529250929050565b5f8161010181146158d2576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820181905260248201527f626174636820686561646572206c656e67746820697320696e636f72726563746044820152606401610c1a565b6040805160c085901b815260f084901b6008820152600a60208502820181019092525f91810161598a8161ffff871686615bbf565b82900390912095945050505050565b8082535050565b60605f808080806159b3888a018a61642b565b95509550955095509550508187106159d15794506159ed9350505050565b8387106159e55782955050505050506159ed565b509293505050505b9392505050565b5f54610100900460ff16615a8a576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602b60248201527f496e697469616c697a61626c653a20636f6e7472616374206973206e6f74206960448201527f6e697469616c697a696e670000000000000000000000000000000000000000006064820152608401610c1a565b606580547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00169055565b5f54610100900460ff16615b4a576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602b60248201527f496e697469616c697a61626c653a20636f6e7472616374206973206e6f74206960448201527f6e697469616c697a696e670000000000000000000000000000000000000000006064820152608401610c1a565b61288833615212565b60655460ff16612888576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601460248201527f5061757361626c653a206e6f74207061757365640000000000000000000000006044820152606401610c1a565b5f825f03615bce5750826159ed565b609b5473ffffffffffffffffffffffffffffffffffffffff165f5b84811015615c95576040517fae453cd5000000000000000000000000000000000000000000000000000000008152600481018590525f9073ffffffffffffffffffffffffffffffffffffffff84169063ae453cd590602401602060405180830381865afa158015615c5c573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190615c8091906160e7565b87525060209095019460019384019301615be9565b5093949350505050565b5f60208284031215615caf575f80fd5b5035919050565b803573ffffffffffffffffffffffffffffffffffffffff81168114615033575f80fd5b5f60208284031215615ce9575f80fd5b6159ed82615cb6565b5f8083601f840112615d02575f80fd5b50813567ffffffffffffffff811115615d19575f80fd5b602083019150836020828501011115615d30575f80fd5b9250929050565b5f805f60408486031215615d49575f80fd5b833567ffffffffffffffff811115615d5f575f80fd5b615d6b86828701615cf2565b909790965060209590950135949350505050565b5f8060208385031215615d90575f80fd5b823567ffffffffffffffff811115615da6575f80fd5b615db285828601615cf2565b90969095509350505050565b5f60e08284031215614f6c575f80fd5b5f60608284031215614f6c575f80fd5b5f8060408385031215615def575f80fd5b823567ffffffffffffffff80821115615e06575f80fd5b615e1286838701615dbe565b93506020850135915080821115615e27575f80fd5b50615e3485828601615dce565b9150509250929050565b5f805f805f8060808789031215615e53575f80fd5b863567ffffffffffffffff80821115615e6a575f80fd5b615e768a838b01615dbe565b97506020890135915080821115615e8b575f80fd5b615e978a838b01615dce565b96506040890135915080821115615eac575f80fd5b615eb88a838b01615cf2565b90965094506060890135915080821115615ed0575f80fd5b50615edd89828a01615cf2565b979a9699509497509295939492505050565b5f805f805f8060c08789031215615f04575f80fd5b615f0d87615cb6565b9550615f1b60208801615cb6565b9450615f2960408801615cb6565b9350606087013592506080870135915060a087013590509295509295509295565b5f805f8060408587031215615f5d575f80fd5b843567ffffffffffffffff80821115615f74575f80fd5b615f8088838901615cf2565b90965094506020870135915080821115615f98575f80fd5b50615fa587828801615cf2565b95989497509550505050565b80151581146138b2575f80fd5b5f60208284031215615fce575f80fd5b81356159ed81615fb1565b803567ffffffffffffffff81168114615033575f80fd5b5f8060408385031215616001575f80fd5b61600a83615fd9565b946020939093013593505050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601160045260245ffd5b80820180821115611b8c57611b8c616018565b81810381811115611b8c57611b8c616018565b5f6020828403121561607b575f80fd5b81516159ed81615fb1565b5f8083357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe18436030181126160b9575f80fd5b83018035915067ffffffffffffffff8211156160d3575f80fd5b602001915036819003821315615d30575f80fd5b5f602082840312156160f7575f80fd5b5051919050565b5f6020828403121561610e575f80fd5b813561ffff811681146159ed575f80fd5b5f6020828403121561612f575f80fd5b813560ff811681146159ed575f80fd5b5f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff820361616f5761616f616018565b5060010190565b5f60208284031215616186575f80fd5b6159ed82615fd9565b818382375f9101908152919050565b81835281816020850137505f602082840101525f60207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f840116840101905092915050565b5f6080820187835260206080602085015281885180845260a08601915060208a0193505f5b8181101561623c57845173ffffffffffffffffffffffffffffffffffffffff168352938301939183019160010161620a565b5050876040860152848103606086015261625781878961619e565b9a9950505050505050505050565b858152846020820152608060408201525f61628460808301858761619e565b90508260608301529695505050505050565b5f82515f5b818110156162b5576020818601810151858301520161629b565b505f920191825250919050565b8082028115828204841417611b8c57611b8c616018565b5f8261630c577f4e487b71000000000000000000000000000000000000000000000000000000005f52601260045260245ffd5b500490565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52603260045260245ffd5b7f4e487b71000000000000000000000000000000000000000000000000000000005f52604160045260245ffd5b5f82601f83011261637a575f80fd5b8135602067ffffffffffffffff808311156163975761639761633e565b8260051b6040517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0603f830116810181811084821117156163da576163da61633e565b60405293845260208187018101949081019250878511156163f9575f80fd5b6020870191505b848210156164205761641182615cb6565b83529183019190830190616400565b979650505050505050565b5f805f805f8060c08789031215616440575f80fd5b86359550602087013567ffffffffffffffff8082111561645e575f80fd5b61646a8a838b0161636b565b9650604089013595506060890135915080821115616486575f80fd5b6164928a838b0161636b565b94506080890135935060a08901359150808211156164ae575f80fd5b506164bb89828a0161636b565b915050929550929550929556fea164736f6c6343000818000a" +var RollupDeployedBin = "0x608060405260043610610369575f3560e01c806388b1ea09116101c8578063b3e0a509116100fd578063d279c1911161009d578063e3fff1dd1161006d578063e3fff1dd14610ba1578063f2fde38b14610bc0578063f8fa010f14610bdf578063fb1e8b0414610bfe575f80fd5b8063d279c19114610b39578063d8dc99d214610b58578063de8b303514610b6d578063dff7827e14610b8c575f80fd5b8063c5553892116100d8578063c555389214610ad3578063cd4edc6914610af2578063ce5db8d614610b05578063cf9a674514610b1a575f80fd5b8063b3e0a50914610a76578063b8d0a1b014610a95578063bedb86fb14610ab4575f80fd5b8063a415d8dc11610168578063abc8d68d11610143578063abc8d68d146109f8578063b31a77d314610a23578063b348442514610a38578063b35dac4e14610a57575f80fd5b8063a415d8dc14610996578063a479265d146109c4578063a4f209b0146109d9575f80fd5b80638f1d3776116101a35780638f1d377614610850578063910129d41461091a57806397fc007c1461094b578063a3c9e94c1461096a575f80fd5b806388b1ea09146107e25780638da5cb5b146107fb5780638ef622a814610825575f80fd5b80632b7ac3f31161029e5780635f77cf1d1161023e57806368589dfa1161021957806368589dfa146107655780636c578c1d14610790578063715018a6146107af578063728cdbca146107c3575f80fd5b80635f77cf1d146106db578063612672901461072757806367caa37a14610746575f80fd5b806341f756da1161027957806341f756da1461060657806357e0af6c146106195780635c975abb146106385780635ef7a94a1461064f575f80fd5b80632b7ac3f3146105745780633b70c18a146105c55780633e001b66146105f1575f80fd5b80631544ba3a1161030957806321e2f9e0116102e457806321e2f9e0146104d45780632362f03e146104f35780632571098d1461051e5780632a213ba114610549575f80fd5b80631544ba3a1461047057806318463fb01461048f57806318af3b2b146104a4575f80fd5b806310d445831161034457806310d44583146103fb578063116a1f421461041a578063121dcd501461043c5780631336110114610451575f80fd5b806304d7721514610374578063059def61146103b75780630ceb6780146103da575f80fd5b3661037057005b5f80fd5b34801561037f575f80fd5b506103a261038e3660046162b5565b60a36020525f908152604090205460ff1681565b60405190151581526020015b60405180910390f35b3480156103c2575f80fd5b506103cc609d5481565b6040519081526020016103ae565b3480156103e5575f80fd5b506103f96103f43660046162ef565b610c13565b005b348015610406575f80fd5b506103f961041536600461634d565b610d37565b348015610425575f80fd5b506103a26104343660046162b5565b609d54101590565b348015610447575f80fd5b506103cc609e5481565b34801561045c575f80fd5b506103f961046b366004616395565b61112c565b34801561047b575f80fd5b506103f961048a3660046163e4565b611777565b34801561049a575f80fd5b506103cc60a75481565b3480156104af575f80fd5b506103a26104be3660046162b5565b5f90815260a26020526040902060010154421090565b3480156104df575f80fd5b506103a26104ee3660046162b5565b611bec565b3480156104fe575f80fd5b506103cc61050d3660046162b5565b60a16020525f908152604090205481565b348015610529575f80fd5b506103cc6105383660046162b5565b60a06020525f908152604090205481565b348015610554575f80fd5b506103cc6105633660046162b5565b60ad6020525f908152604090205481565b34801561057f575f80fd5b50609c546105a09073ffffffffffffffffffffffffffffffffffffffff1681565b60405173ffffffffffffffffffffffffffffffffffffffff90911681526020016103ae565b3480156105d0575f80fd5b50609b546105a09073ffffffffffffffffffffffffffffffffffffffff1681565b3480156105fc575f80fd5b506103cc60aa5481565b6103f9610614366004616472565b611c1a565b348015610624575f80fd5b506103f96106333660046162b5565b611f47565b348015610643575f80fd5b5060655460ff166103a2565b34801561065a575f80fd5b506106a56106693660046162b5565b60a26020525f908152604090208054600182015460028301546003909301549192909173ffffffffffffffffffffffffffffffffffffffff1684565b6040805194855260208501939093529183015273ffffffffffffffffffffffffffffffffffffffff1660608201526080016103ae565b3480156106e6575f80fd5b5061070e7f000000000000000000000000000000000000000000000000000000000000000081565b60405167ffffffffffffffff90911681526020016103ae565b348015610732575f80fd5b506103f96107413660046162b5565b61200c565b348015610751575f80fd5b506103f9610760366004616472565b612271565b348015610770575f80fd5b506103cc61077f3660046162b5565b60ab6020525f908152604090205481565b34801561079b575f80fd5b506103f96107aa3660046162ef565b612665565b3480156107ba575f80fd5b506103f9612774565b3480156107ce575f80fd5b506103f96107dd3660046164ac565b612787565b3480156107ed575f80fd5b5060a6546103a29060ff1681565b348015610806575f80fd5b5060335473ffffffffffffffffffffffffffffffffffffffff166105a0565b348015610830575f80fd5b506103cc61083f3660046162ef565b60ae6020525f908152604090205481565b34801561085b575f80fd5b506108c961086a3660046162b5565b60a46020525f9081526040902080546001820154600283015460039093015467ffffffffffffffff8316936801000000000000000090930473ffffffffffffffffffffffffffffffffffffffff16929060ff8082169161010090041686565b6040805167ffffffffffffffff909716875273ffffffffffffffffffffffffffffffffffffffff909516602087015293850192909252606084015215156080830152151560a082015260c0016103ae565b348015610925575f80fd5b506103a26109343660046162b5565b5f90815260a4602052604090206003015460ff1690565b348015610956575f80fd5b506103f96109653660046162ef565b612b50565b348015610975575f80fd5b506097546105a09073ffffffffffffffffffffffffffffffffffffffff1681565b3480156109a1575f80fd5b506103a26109b03660046162ef565b609f6020525f908152604090205460ff1681565b3480156109cf575f80fd5b506103cc60995481565b3480156109e4575f80fd5b506103f96109f33660046162b5565b612c74565b348015610a03575f80fd5b506103cc610a123660046162ef565b60a56020525f908152604090205481565b348015610a2e575f80fd5b506103cc60a85481565b348015610a43575f80fd5b506103f9610a52366004616507565b612d3e565b348015610a62575f80fd5b506103f9610a713660046162ef565b613133565b348015610a81575f80fd5b506103f9610a90366004616395565b613199565b348015610aa0575f80fd5b506103f9610aaf366004616507565b6135d3565b348015610abf575f80fd5b506103f9610ace36600461657b565b613676565b348015610ade575f80fd5b506103f9610aed3660046162b5565b613809565b6103f9610b003660046165ad565b6138c6565b348015610b10575f80fd5b506103cc60985481565b348015610b25575f80fd5b506103f9610b343660046162b5565b613fdb565b348015610b44575f80fd5b506103f9610b533660046162ef565b6141c0565b348015610b63575f80fd5b506103cc60ac5481565b348015610b78575f80fd5b506103a2610b873660046162b5565b6142a6565b348015610b97575f80fd5b506103cc609a5481565b348015610bac575f80fd5b506103f9610bbb3660046162b5565b6142fd565b348015610bcb575f80fd5b506103f9610bda3660046162ef565b6143ba565b348015610bea575f80fd5b506103f9610bf93660046162ef565b61446e565b348015610c09575f80fd5b506103cc60a95481565b610c1b614aa4565b73ffffffffffffffffffffffffffffffffffffffff81165f908152609f602052604090205460ff1615610caf576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601f60248201527f6163636f756e7420697320616c72656164792061206368616c6c656e6765720060448201526064015b60405180910390fd5b73ffffffffffffffffffffffffffffffffffffffff81165f818152609f602090815260409182902080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0016600190811790915591519182527f7feb653c7b1f0d23daeed494225b3f28851cdc8973fcc653866d9b6e205fc00991015b60405180910390a250565b610d3f614aa4565b5f8111610da8576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601560248201527f636f756e74206d757374206265206e6f6e7a65726f00000000000000000000006044820152606401610ca6565b5f80610db48585614b25565b915091505f610dc7836001015160c01c90565b5f81815260a160205260409020549091508214610e40576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601460248201527f696e636f727265637420626174636820686173680000000000000000000000006044820152606401610ca6565b5f60a181610e4e8785616602565b81526020019081526020015f205414610ee8576040517f08c379a0000000000000000000000000000000000000000000000000000000008152602060048201526024808201527f726576657274696e67206d7573742073746172742066726f6d2074686520656e60448201527f64696e67000000000000000000000000000000000000000000000000000000006064820152608401610ca6565b609d548111610f79576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602160248201527f63616e206f6e6c792072657665727420756e46696e616c697a6564206261746360448201527f68000000000000000000000000000000000000000000000000000000000000006064820152608401610ca6565b610f84600182616615565b609e555b831561112457604051829082907ecae2739091badfd91c373f0a16cede691e0cd25bb80cff77dd5caeb4710146905f90a3610fc281614be7565b5f81815260a16020526040812055610fd9816142a6565b1561105f575f81815260a4602090815260408083206001810154905468010000000000000000900473ffffffffffffffffffffffffffffffffffffffff16845260a59092528220805491929091611031908490616602565b909155505060a680547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001690555b5f81815260a46020526040812080547fffffffff0000000000000000000000000000000000000000000000000000000016815560018101829055600281019190915560030180547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff000016905560a854158015906110dc575060a85481145b156110e6575f60a8555b6001015f81815260a160205260409020547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff90940193915081610f88575b505050505050565b60a85415611196576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601660248201527f70656e64696e67207265766572742072657175657374000000000000000000006044820152606401610ca6565b61119e614cdb565b5f806111aa8484614b25565b915091505f6111bd836001015160c01c90565b5f81815260a160205260409020549091508214611236576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601460248201527f696e636f727265637420626174636820686173680000000000000000000000006044820152606401610ca6565b61123f81611bec565b6112a5576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600f60248201527f6261746368206e6f7420657869737400000000000000000000000000000000006044820152606401610ca6565b6112ae816142a6565b15611315576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601260248201527f626174636820696e206368616c6c656e676500000000000000000000000000006044820152606401610ca6565b5f81815260a4602052604090206003015460ff1615611390576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601660248201527f62617463682073686f756c6420626520726576657274000000000000000000006044820152606401610ca6565b5f81815260a2602052604090206001015442101561140a576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601960248201527f626174636820696e206368616c6c656e67652077696e646f77000000000000006044820152606401610ca6565b605983015160a05f61141d600185616615565b81526020019081526020015f205414611492576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601d60248201527f696e636f72726563742070726576696f757320737461746520726f6f740000006044820152606401610ca6565b5f81815260a0602052604090205415611507576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601660248201527f626174636820616c7265616479207665726966696564000000000000000000006044820152606401610ca6565b80609d5460010114611575576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601560248201527f696e636f727265637420626174636820696e64657800000000000000000000006044820152606401610ca6565b609d819055600160a35f61158a866099015190565b815260208101919091526040015f2080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001691151591909117905560798301515f82815260a060205260409020556115f76115ea846011015160c01c90565b600985015160c01c614d48565b61160081614be7565b60a25f61160e600184616615565b815260208101919091526040015f908120818155600180820183905560028201839055600390910180547fffffffffffffffffffffffff000000000000000000000000000000000000000016905560ab919061166a9084616615565b81526020019081526020015f205f905560ad5f60018361168a9190616615565b81526020019081526020015f205f905560a45f6001836116aa9190616615565b815260208082019290925260409081015f90812080547fffffffff00000000000000000000000000000000000000000000000000000000168155600181018290556002810182905560030180547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff000016905583815260a1909252902054817f26ba82f907317eedc97d0cbef23de76a43dd6edb563bdb6e9407645b950a7a2d611753866079015190565b60998701516040805192835260208301919091520160405180910390a35050505050565b60a854156117e1576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601660248201527f70656e64696e67207265766572742072657175657374000000000000000000006044820152606401610ca6565b6117e9614cdb565b60ac54609e545f90815260a260205260408120549091429161180b9190616602565b1090505f4260ac54609b5f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663b59b1a786040518163ffffffff1660e01b8152600401602060405180830381865afa15801561187d573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906118a19190616628565b6118ab9190616602565b109050811580156118b95750805b1561193a575f6118cf6080890160608a0161663f565b61ffff161161193a576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600b60248201527f6c316d73672064656c61790000000000000000000000000000000000000000006044820152606401610ca6565b81806119435750805b6119a9576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600e60248201527f696e76616c69642074696d696e670000000000000000000000000000000000006044820152606401610ca6565b5f6119bf6119ba60208a018a616660565b614b25565b5090505f6119d1826001015160c01c90565b6119dc906001616602565b5f81815260ad60205260408120549192509015611a96575f4915611a82576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602f60248201527f6d757374206e6f7420636172727920626c6f62207768656e207573696e67207360448201527f746f72656420626c6f62206861736800000000000000000000000000000000006064820152608401610ca6565b505f81815260ad6020526040902054611ab1565b611aae611aa660208c018c6166c1565b60ff16614e12565b90505b611abc8a5f83614f81565b5f80611ac88b8b614b25565b915091505f611adb836001015160c01c90565b905080609e5414611b48576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601660248201527f696e636f727265637420626174636820686561646572000000000000000000006044820152606401610ca6565b5f81815260a160205260409020548214611bbe576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601460248201527f696e636f727265637420626174636820686173680000000000000000000000006044820152606401610ca6565b5f81815260a26020526040902042600190910155611bdd838b8b6155a6565b50505050505050505050505050565b5f81815260a2602052604081205415801590611c1457505f82815260a1602052604090205415155b92915050565b60975473ffffffffffffffffffffffffffffffffffffffff16639f8a13d7336040517fffffffff0000000000000000000000000000000000000000000000000000000060e084901b16815273ffffffffffffffffffffffffffffffffffffffff9091166004820152602401602060405180830381865afa158015611ca0573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611cc491906166e1565b611d2a576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601d60248201527f6f6e6c7920616374697665207375626d697474657220616c6c6f7765640000006044820152606401610ca6565b60a85415611d94576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601660248201527f70656e64696e67207265766572742072657175657374000000000000000000006044820152606401610ca6565b611d9c614cdb565b5f49611e04576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600f60248201527f6d75737420636172727920626c6f6200000000000000000000000000000000006044820152606401610ca6565b60ac54609b54604080517fb59b1a78000000000000000000000000000000000000000000000000000000008152905142939273ffffffffffffffffffffffffffffffffffffffff169163b59b1a789160048083019260209291908290030181865afa158015611e75573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611e999190616628565b611ea39190616602565b1015611f25575f611eba608083016060840161663f565b61ffff1611611f25576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600b60248201527f6c316d73672064656c61790000000000000000000000000000000000000000006044820152606401610ca6565b5f611f36611aa660208401846166c1565b9050611f43823383614f81565b5050565b611f4f614aa4565b5f81118015611f6057506099548114155b611fc6576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601860248201527f696e76616c6964206e65772070726f6f662077696e646f7700000000000000006044820152606401610ca6565b609980549082905560408051828152602081018490527f1e3a2094feb4b696dd3d7caea38ad2f41dbdcac3fa3943c7a693aff8a64b0a6191015b60405180910390a15050565b5f54600290610100900460ff1615801561202c57505f5460ff8083169116105b6120b8576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602e60248201527f496e697469616c697a61626c653a20636f6e747261637420697320616c72656160448201527f647920696e697469616c697a65640000000000000000000000000000000000006064820152608401610ca6565b5f80547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00001660ff8316176101001790556120f0614aa4565b5f5460ff1660ff16600214612161576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601660248201527f6d757374206861766520696e697469616c697a656421000000000000000000006044820152606401610ca6565b816121ee576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602760248201527f63616e206e6f742073657420737461746520726f6f742077697468206279746560448201527f73333228302921000000000000000000000000000000000000000000000000006064820152608401610ca6565b609e545f90815260ab602052604090205461221757609e545f90815260ab602052604090208290555b5f80547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ff16905560405160ff821681527f7f26b83ff96e1f2b6a682f133852f6798a09c465da95921460cefb384740249890602001612000565b60975473ffffffffffffffffffffffffffffffffffffffff16639f8a13d7336040517fffffffff0000000000000000000000000000000000000000000000000000000060e084901b16815273ffffffffffffffffffffffffffffffffffffffff9091166004820152602401602060405180830381865afa1580156122f7573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061231b91906166e1565b612381576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601d60248201527f6f6e6c7920616374697665207375626d697474657220616c6c6f7765640000006044820152606401610ca6565b60a854156123eb576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601660248201527f70656e64696e67207265766572742072657175657374000000000000000000006044820152606401610ca6565b6123f3614cdb565b5f491561245c576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601f60248201527f636f6d6d69745374617465206d757374206e6f7420636172727920626c6f62006044820152606401610ca6565b5f61246d6119ba6020840184616660565b5090505f61247f826001015160c01c90565b61248a906001616602565b5f81815260ad6020526040902054909150612527576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602260248201527f6e6f2073746f72656420626c6f62206861736820666f7220746869732062617460448201527f63680000000000000000000000000000000000000000000000000000000000006064820152608401610ca6565b60ac54609b54604080517fb59b1a78000000000000000000000000000000000000000000000000000000008152905142939273ffffffffffffffffffffffffffffffffffffffff169163b59b1a789160048083019260209291908290030181865afa158015612598573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906125bc9190616628565b6125c69190616602565b1015612648575f6125dd608085016060860161663f565b61ffff1611612648576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600b60248201527f6c316d73672064656c61790000000000000000000000000000000000000000006044820152606401610ca6565b61266083335f84815260ad6020526040902054614f81565b505050565b61266d614aa4565b73ffffffffffffffffffffffffffffffffffffffff81165f908152609f602052604090205460ff166126fb576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601b60248201527f6163636f756e74206973206e6f742061206368616c6c656e67657200000000006044820152606401610ca6565b73ffffffffffffffffffffffffffffffffffffffff81165f818152609f6020908152604080832080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00169055519182527f7feb653c7b1f0d23daeed494225b3f28851cdc8973fcc653866d9b6e205fc0099101610d2c565b61277c614aa4565b6127855f615780565b565b5f54610100900460ff16158080156127a557505f54600160ff909116105b806127be5750303b1580156127be57505f5460ff166001145b61284a576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602e60248201527f496e697469616c697a61626c653a20636f6e747261637420697320616c72656160448201527f647920696e697469616c697a65640000000000000000000000000000000000006064820152608401610ca6565b5f80547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0016600117905580156128a6575f80547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ff166101001790555b73ffffffffffffffffffffffffffffffffffffffff861615806128dd575073ffffffffffffffffffffffffffffffffffffffff8516155b15612914576040517fecc6fdf000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b73ffffffffffffffffffffffffffffffffffffffff8716612991576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601a60248201527f696e76616c6964207375626d697474657220636f6e74726163740000000000006044820152606401610ca6565b6129996157f6565b6129a1615894565b6097805473ffffffffffffffffffffffffffffffffffffffff808a167fffffffffffffffffffffffff000000000000000000000000000000000000000092831617909255609b8054898416908316179055609c805492881692909116821790556098859055609984905560a98390556040515f907f728af3d16a5760405e27a082c98ab272e9f0a1d02f0085d41532a26093aedd96908290a3604080515f8152602081018690527fa577f4223f91f74e2dad65bbb8c30807587ae95d0d34288057bb3ec0d398a437910160405180910390a1604080515f8152602081018590527f1e3a2094feb4b696dd3d7caea38ad2f41dbdcac3fa3943c7a693aff8a64b0a61910160405180910390a1604080515f8152602081018490527ffb81bce17f015797e11949d3c332e2bf9453faf68f728447426803138f2b0223910160405180910390a18015612b47575f80547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ff169055604051600181527f7f26b83ff96e1f2b6a682f133852f6798a09c465da95921460cefb38474024989060200160405180910390a15b50505050505050565b612b58614aa4565b73ffffffffffffffffffffffffffffffffffffffff811615801590612b985750609c5473ffffffffffffffffffffffffffffffffffffffff828116911614155b612bfe576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601460248201527f696e76616c6964206e65772076657269666965720000000000000000000000006044820152606401610ca6565b609c805473ffffffffffffffffffffffffffffffffffffffff8381167fffffffffffffffffffffffff0000000000000000000000000000000000000000831681179093556040519116919082907f728af3d16a5760405e27a082c98ab272e9f0a1d02f0085d41532a26093aedd96905f90a35050565b612c7c614aa4565b5f81118015612c8c575060648111155b8015612c9a575060a9548114155b612d00576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601f60248201527f696e76616c69642070726f6f66207265776172642070657263656e74616765006044820152606401610ca6565b60a980549082905560408051828152602081018490527ffb81bce17f015797e11949d3c332e2bf9453faf68f728447426803138f2b02239101612000565b60a85415612da8576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601660248201527f70656e64696e67207265766572742072657175657374000000000000000000006044820152606401610ca6565b612db0614cdb565b60975473ffffffffffffffffffffffffffffffffffffffff16639f8a13d7336040517fffffffff0000000000000000000000000000000000000000000000000000000060e084901b16815273ffffffffffffffffffffffffffffffffffffffff9091166004820152602401602060405180830381865afa158015612e36573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190612e5a91906166e1565b612ec0576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601d60248201527f6f6e6c7920616374697665207375626d697474657220616c6c6f7765640000006044820152606401610ca6565b5f80612ecc8686614b25565b915091505f612edf836001015160c01c90565b5f81815260a160205260409020549091508214612f58576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601460248201527f696e636f727265637420626174636820686173680000000000000000000000006044820152606401610ca6565b612f61816142a6565b612fc7576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601260248201527f626174636820696e206368616c6c656e676500000000000000000000000000006044820152606401610ca6565b5f81815260a4602052604090206003810180547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ff1661010017905560a680547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00169055609954600290910154429161303e91616602565b116130e8575f81815260a460209081526040808320600390810180547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0016600117905560a2835292819020909201548251808401909352600783527f54696d656f757400000000000000000000000000000000000000000000000000918301919091526130e391839173ffffffffffffffffffffffffffffffffffffffff1690615932565b612b47565b6130f38386866155a6565b612b4781336040518060400160405280600d81526020017f50726f6f66207375636365737300000000000000000000000000000000000000815250615ab8565b61313b614aa4565b60aa80545f90915561314d8282615b9e565b6040805173ffffffffffffffffffffffffffffffffffffffff84168152602081018390527fb1b2058a6969e2d25e47bcaebe8ae21c29a23b2752429315b75e2f4f285f3d879101612000565b6131a1614aa4565b5f805260a06020527fb84a74ec6ef4d0e83b6006dfaa014ab4026f9f3b97d186e604d29998a4e808ea5415613232576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601660248201527f67656e6573697320626174636820696d706f72746564000000000000000000006044820152606401610ca6565b5f8061323e8484614b25565b915091505f613251836001015160c01c90565b905080156132bb576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601360248201527f696e76616c696420626174636820696e646578000000000000000000000000006044820152606401610ca6565b5f6132c7846079015190565b905080613330576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600f60248201527f7a65726f20737461746520726f6f7400000000000000000000000000000000006044820152606401610ca6565b600984015160c01c1561339f576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601d60248201527f6c31206d65737361676520706f707065642073686f756c6420626520300000006044820152606401610ca6565b5f6133ab856019015190565b03613412576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600e60248201527f7a65726f206461746120686173680000000000000000000000000000000000006044820152606401610ca6565b7f010657f37554c781402a22917dee2f75def7ab966d7b770905398eba3c44401461343e856039015190565b146134a5576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601660248201527f696e76616c69642076657273696f6e65642068617368000000000000000000006044820152606401610ca6565b5f82815260a1602090815260408083208690558051608081018252428082528184019081528183018581526060830186815288875260a28652848720935184559151600184015551600283015551600390910180547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff909216919091179055603987015160ad83528184205560ab825280832084905560a0909152808220839055609e849055609d84905551849184917f2c32d4ae151744d0bf0b9464a3e897a1d17ed2f1af71f7c9a75f12ce0d28238f9190a3604080518281525f6020820152849184917f26ba82f907317eedc97d0cbef23de76a43dd6edb563bdb6e9407645b950a7a2d910160405180910390a3505050505050565b5f806135df8686614b25565b915091505f6135f2836001015160c01c90565b5f81815260a16020526040902054909150821461366b576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601460248201527f696e636f727265637420626174636820686173680000000000000000000000006044820152606401610ca6565b612b478386866155a6565b61367e614aa4565b80156137d75761368c615c6a565b60a65460ff16156137895760a7545f90815260a4602090815260408083206001810154905468010000000000000000900473ffffffffffffffffffffffffffffffffffffffff16845260a590925282208054919290916136ed908490616602565b909155505060a7545f90815260a46020526040812080547fffffffff0000000000000000000000000000000000000000000000000000000016815560018101829055600281019190915560030180547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff000016905560a680547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001690555b7f62e78cea01bee320cd4e420270b5ea74000d11b0c9f74754ebdbfc544b05a258335b60405173ffffffffffffffffffffffffffffffffffffffff909116815260200160405180910390a150565b6137df615cef565b7f5db9ee0a495bf2e6ff9c91a7834c1ba4fdd244a5e8aa4e537bd38aeae4b073aa336137ac565b50565b613811614aa4565b5f81118015613822575060ac548114155b613888576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601f60248201527f696e76616c6964206e657720726f6c6c75702064656c617920706572696f64006044820152606401610ca6565b60ac80549082905560408051828152602081018490527f624e47dc9fb8f8cfeaf4ead4710277cc1757136cfa885e465514cf6d510f0ad19101612000565b335f908152609f602052604090205460ff1661393e576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601760248201527f6f6e6c79206368616c6c656e67657220616c6c6f7765640000000000000000006044820152606401610ca6565b60a854156139a8576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601660248201527f70656e64696e67207265766572742072657175657374000000000000000000006044820152606401610ca6565b6139b0614cdb565b60a65460ff1615613a1d576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601460248201527f616c726561647920696e206368616c6c656e67650000000000000000000000006044820152606401610ca6565b8167ffffffffffffffff16609d5410613a92576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601760248201527f626174636820616c72656164792066696e616c697a65640000000000000000006044820152606401610ca6565b67ffffffffffffffff82165f90815260a160205260409020548114613b13576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601460248201527f696e636f727265637420626174636820686173680000000000000000000000006044820152606401610ca6565b613b268267ffffffffffffffff16611bec565b613b8c576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600f60248201527f6261746368206e6f7420657869737400000000000000000000000000000000006044820152606401610ca6565b67ffffffffffffffff82165f90815260a4602052604090205468010000000000000000900473ffffffffffffffffffffffffffffffffffffffff1615613c2e576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601860248201527f626174636820616c7265616479206368616c6c656e67656400000000000000006044820152606401610ca6565b67ffffffffffffffff82165f90815260a260205260409020600101544210613cd8576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152603360248201527f63616e6e6f74206368616c6c656e6765206261746368206f757473696465207460448201527f6865206368616c6c656e67652077696e646f77000000000000000000000000006064820152608401610ca6565b60975f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16630d13fd7b6040518163ffffffff1660e01b8152600401602060405180830381865afa158015613d42573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190613d669190616628565b341015613dcf576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601260248201527f696e73756666696369656e742076616c756500000000000000000000000000006044820152606401610ca6565b67ffffffffffffffff82811660a78190556040805160c0810182528281523360208083018281523484860190815242606086019081525f6080870181815260a0880182815299825260a49095529690962094518554925173ffffffffffffffffffffffffffffffffffffffff1668010000000000000000027fffffffff00000000000000000000000000000000000000000000000000000000909316981697909717178355945160018301559151600282015592516003909301805492511515610100027fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ff941515949094167fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00009093169290921792909217905573ffffffffffffffffffffffffffffffffffffffff168267ffffffffffffffff167f3a6ea19df25b49e7624e313ce7c1ab23984238e93727260db56a81735b1b997634604051613f3b91815260200190565b60405180910390a35f609d546001613f539190616602565b90505b609e548111613fab578267ffffffffffffffff168114613f99576099545f82815260a2602052604081206001018054909190613f93908490616602565b90915550505b80613fa3816166fc565b915050613f56565b505060a680547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0016600117905550565b5f54600390610100900460ff16158015613ffb57505f5460ff8083169116105b614087576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602e60248201527f496e697469616c697a61626c653a20636f6e747261637420697320616c72656160448201527f647920696e697469616c697a65640000000000000000000000000000000000006064820152608401610ca6565b5f80547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00001660ff8316176101001790556140bf614aa4565b815f03614128576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601b60248201527f696e76616c696420726f6c6c75702064656c617920706572696f6400000000006044820152606401610ca6565b60ac829055604080515f8152602081018490527f624e47dc9fb8f8cfeaf4ead4710277cc1757136cfa885e465514cf6d510f0ad1910160405180910390a15f80547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ff16905560405160ff821681527f7f26b83ff96e1f2b6a682f133852f6798a09c465da95921460cefb384740249890602001612000565b335f90815260a5602052604081205490819003614239576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601c60248201527f696e76616c69642062617463684368616c6c656e6765526577617264000000006044820152606401610ca6565b335f90815260a560205260408120556142528282615b9e565b8173ffffffffffffffffffffffffffffffffffffffff167f9c25fa83f414ed363c8d39c98fb3e17567b3431cede71eb062c49d2a63ce247a8260405161429a91815260200190565b60405180910390a25050565b5f81815260a4602052604081205468010000000000000000900473ffffffffffffffffffffffffffffffffffffffff1615801590611c145750505f90815260a46020526040902060030154610100900460ff161590565b614305614aa4565b5f8111801561431657506098548114155b61437c576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601b60248201527f696e76616c6964206e65772066696e616c697a6520706572696f6400000000006044820152606401610ca6565b609880549082905560408051828152602081018490527fa577f4223f91f74e2dad65bbb8c30807587ae95d0d34288057bb3ec0d398a4379101612000565b6143c2614aa4565b73ffffffffffffffffffffffffffffffffffffffff8116614465576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602660248201527f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160448201527f64647265737300000000000000000000000000000000000000000000000000006064820152608401610ca6565b61380681615780565b5f54600490610100900460ff1615801561448e57505f5460ff8083169116105b61451a576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602e60248201527f496e697469616c697a61626c653a20636f6e747261637420697320616c72656160448201527f647920696e697469616c697a65640000000000000000000000000000000000006064820152608401610ca6565b5f80547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00001660ff831617610100179055614552614aa4565b73ffffffffffffffffffffffffffffffffffffffff82161580159061458d57505f8273ffffffffffffffffffffffffffffffffffffffff163b115b6145f3576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601160248201527f696e76616c6964207375626d69747465720000000000000000000000000000006044820152606401610ca6565b6040805160048152602481019091526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fbfa02ba9000000000000000000000000000000000000000000000000000000001790525f90614657908490615d46565b905060a081901c156146c5576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601760248201527f696e76616c696420726f6c6c757020656e636f64696e670000000000000000006044820152606401610ca6565b6040515f602482018190528291614756908690604401604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529190526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167f9f8a13d700000000000000000000000000000000000000000000000000000000179052615d46565b6040805160048152602481019091526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167f0d13fd7b000000000000000000000000000000000000000000000000000000001790529091505f906147bd908790615d46565b905073ffffffffffffffffffffffffffffffffffffffff8316301461483e576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601960248201527f7375626d697474657220726f6c6c7570206d69736d61746368000000000000006044820152606401610ca6565b8115801561484b57505f81115b6148b1576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601760248201527f696e76616c6964207375626d69747465722073746174650000000000000000006044820152606401610ca6565b609d54609e541461491e576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600f60248201527f70656e64696e67206261746368657300000000000000000000000000000000006044820152606401610ca6565b60a85415614988576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601660248201527f70656e64696e67207265766572742072657175657374000000000000000000006044820152606401610ca6565b609d545f90815260a2602052604080822060030180547fffffffffffffffffffffffff0000000000000000000000000000000000000000169055609754905173ffffffffffffffffffffffffffffffffffffffff898116939216917fde3ada76ae1aa387cc4c586c70304b9fae54b251958786c828aaa0ce0290f93991a35050609780547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff861617905550505f80547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ff16905560405160ff821681527f7f26b83ff96e1f2b6a682f133852f6798a09c465da95921460cefb384740249890602001612000565b60335473ffffffffffffffffffffffffffffffffffffffff163314612785576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e65726044820152606401610ca6565b5f805f614b328585615de9565b90505f8160ff165f03614b5357614b498686615e73565b9094509050614bd9565b8160ff1660011480614b6857508160ff166002145b15614b7757614b498686615ef6565b6040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601960248201527f556e737570706f727465642062617463682076657273696f6e000000000000006044820152606401610ca6565b808420925050509250929050565b5f81815260a2602052604090206003015473ffffffffffffffffffffffffffffffffffffffff1680614c17575050565b73ffffffffffffffffffffffffffffffffffffffff81165f90815260ae602052604090205480614ca3576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601d60248201527f70656e64696e6720626174636820636f756e7420756e646572666c6f770000006044820152606401610ca6565b614cae600182616615565b73ffffffffffffffffffffffffffffffffffffffff9092165f90815260ae60205260409020919091555050565b60655460ff1615612785576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601060248201527f5061757361626c653a20706175736564000000000000000000000000000000006044820152606401610ca6565b805f03614d53575050565b8082035f5b82811015614e0c57610100818403811115614d7257508083035b609b546040517f3c7f5283000000000000000000000000000000000000000000000000000000008152600481018590526024810183905273ffffffffffffffffffffffffffffffffffffffff90911690633c7f5283906044015f604051808303815f87803b158015614de2575f80fd5b505af1158015614df4573d5f803e3d5ffd5b50505050610100830192505061010081019050614d58565b50505050565b5f81600203614ebb575f6040515f5b804980614e2e5750614e3d565b60208202830152600101614e21565b602081028083209201604052909250905080614eb5576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601b60248201527f5632207265717569726573206174206c65617374203120626c6f6200000000006044820152606401610ca6565b50919050565b60014915614f4b576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602560248201527f6c6567616379206261746368657320737570706f72742065786163746c79203160448201527f20626c6f620000000000000000000000000000000000000000000000000000006064820152608401610ca6565b5f4915614f59575f49611c14565b507f010657f37554c781402a22917dee2f75def7ab966d7b770905398eba3c4440145b919050565b6002614f9060208501856166c1565b60ff161115614ffb576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600f60248201527f696e76616c69642076657273696f6e00000000000000000000000000000000006044820152606401610ca6565b6080830135615066576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601b60248201527f70726576696f757320737461746520726f6f74206973207a65726f00000000006044820152606401610ca6565b60a08301356150d1576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601660248201527f6e657720737461746520726f6f74206973207a65726f000000000000000000006044820152606401610ca6565b5f806150e36119ba6020870187616660565b915091505f6150f6836001015160c01c90565b90505f60a181615107846001616602565b81526020019081526020015f20541461517c576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601760248201527f626174636820616c726561647920636f6d6d69747465640000000000000000006044820152606401610ca6565b609e5481146151e7576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601560248201527f696e636f727265637420626174636820696e64657800000000000000000000006044820152606401610ca6565b5f81815260a16020526040902054821461525d576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601b60248201527f696e636f727265637420706172656e74206261746368206861736800000000006044820152606401610ca6565b5f81815260ab60205260409020546080870135146152d7576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601d60248201527f696e636f72726563742070726576696f757320737461746520726f6f740000006044820152606401610ca6565b5f6152e6846011015160c01c90565b90505f6153126152fc60608a0160408b01616733565b61530c60808b0160608c0161663f565b84615f63565b90506153246080890160608a0161663f565b60019384019361ffff91909116929092019160f99061534660208b018b6166c1565b60ff161061535357506101015b6040805182810190915295506153788661537060208c018c6166c1565b60ff16615fa7565b60c084901b60018701526153a68661539660808c0160608d0161663f565b61ffff1660c01b60099190910152565b60c083811b6011880152601987018390526039870188905260808a0135605988015260a08a0135607988015289013560998701525f60b987015260d9860185905260016153f660208b018b6166c1565b60ff1610615428576154288661541260608c0160408d01616733565b67ffffffffffffffff1660c01b60f99190910152565b8086205f85815260a1602090815260408083209390935560ab815282822060a08d0135905560ad905290812088905560a65460ff16156154915760a7545f90815260a46020526040902060020154609954429161548491616602565b61548e9190616615565b90505b604051806080016040528042815260200182609854426154b19190616602565b6154bb9190616602565b81526020016154d060608d0160408e01616733565b67ffffffffffffffff16815273ffffffffffffffffffffffffffffffffffffffff8b81166020928301525f88815260a28352604090819020845181559284015160018401558301516002830155606090920151600390910180547fffffffffffffffffffffffff0000000000000000000000000000000000000000169190921617905561555c89615fae565b5050609e8390555f83815260a16020526040808220549051909185917f2c32d4ae151744d0bf0b9464a3e897a1d17ed2f1af71f7c9a75f12ce0d28238f9190a35050505050505050565b8061560d576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601360248201527f496e76616c69642062617463682070726f6f66000000000000000000000000006044820152606401610ca6565b5f61561c846001015160c01c90565b90505f61562a856039015190565b90505f7f0000000000000000000000000000000000000000000000000000000000000000615659876059015190565b6079880151609989015160b98a015160198b015160405160c09690961b7fffffffffffffffff000000000000000000000000000000000000000000000000166020870152602886019490945260488501929092526068840152608883015260a882015260c8810183905260e801604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529190528051602090910120609c5490915073ffffffffffffffffffffffffffffffffffffffff16632c09a848615728885160f81c90565b858888866040518663ffffffff1660e01b815260040161574c95949392919061674c565b5f6040518083038186803b158015615762575f80fd5b505afa158015615774573d5f803e3d5ffd5b50505050505050505050565b6033805473ffffffffffffffffffffffffffffffffffffffff8381167fffffffffffffffffffffffff0000000000000000000000000000000000000000831681179093556040519116919082907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0905f90a35050565b5f54610100900460ff1661588c576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602b60248201527f496e697469616c697a61626c653a20636f6e7472616374206973206e6f74206960448201527f6e697469616c697a696e670000000000000000000000000000000000000000006064820152608401610ca6565b612785616007565b5f54610100900460ff1661592a576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602b60248201527f496e697469616c697a61626c653a20636f6e7472616374206973206e6f74206960448201527f6e697469616c697a696e670000000000000000000000000000000000000000006064820152608401610ca6565b6127856160c7565b60a88390555f83815260a460205260408082205460975491517fc96be4cb00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff86811660048301526801000000000000000090920482169392919091169063c96be4cb906024016020604051808303815f875af11580156159c6573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906159ea9190616628565b5f86815260a46020526040902060010154909150615a09908290616602565b5f86815260a4602090815260408083205468010000000000000000900473ffffffffffffffffffffffffffffffffffffffff16835260a590915281208054909190615a55908490616602565b9091555050604051615a689084906167af565b6040519081900381209073ffffffffffffffffffffffffffffffffffffffff84169087907fe70d3820e244d5f71d1a6395db24f3460e8dca966edc1fd3625b6292880a877a905f90a45050505050565b5f83815260a4602052604081206001015460a954909190606490615adc90846167db565b615ae691906167f2565b9050615af28183616615565b60aa5f828254615b029190616602565b909155505073ffffffffffffffffffffffffffffffffffffffff84165f90815260a5602052604081208054839290615b3b908490616602565b9091555050604051615b4e9084906167af565b6040519081900381209073ffffffffffffffffffffffffffffffffffffffff86169087907fe70d3820e244d5f71d1a6395db24f3460e8dca966edc1fd3625b6292880a877a905f90a45050505050565b8015611f43575f8273ffffffffffffffffffffffffffffffffffffffff16826040515f6040518083038185875af1925050503d805f8114615bfa576040519150601f19603f3d011682016040523d82523d5f602084013e615bff565b606091505b5050905080612660576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601b60248201527f526f6c6c75703a20455448207472616e73666572206661696c656400000000006044820152606401610ca6565b615c72614cdb565b606580547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001660011790557f62e78cea01bee320cd4e420270b5ea74000d11b0c9f74754ebdbfc544b05a258615cc53390565b60405173ffffffffffffffffffffffffffffffffffffffff909116815260200160405180910390a1565b615cf7616166565b606580547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001690557f5db9ee0a495bf2e6ff9c91a7834c1ba4fdd244a5e8aa4e537bd38aeae4b073aa33615cc5565b5f805f805f855160208701885afa91503d905060208114821615615d6e5760205f803e5f5192505b818015615d7b5750806020145b615de1576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601b60248201527f696e76616c6964207375626d697474657220696e7465726661636500000000006044820152606401610ca6565b505092915050565b5f81615e51576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601260248201527f456d7074792062617463682068656164657200000000000000000000000000006044820152606401610ca6565b82825f818110615e6357615e6361682a565b919091013560f81c949350505050565b5f8160f9811015615ee0576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601d60248201527f626174636820686561646572206c656e67746820746f6f20736d616c6c0000006044820152606401610ca6565b6040519150808483378082016040529250929050565b5f816101018114615ee0576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820181905260248201527f626174636820686561646572206c656e67746820697320696e636f72726563746044820152606401610ca6565b6040805160c085901b815260f084901b6008820152600a60208502820181019092525f918101615f988161ffff8716866161d2565b82900390912095945050505050565b8082535050565b73ffffffffffffffffffffffffffffffffffffffff8116156138065773ffffffffffffffffffffffffffffffffffffffff81165f90815260ae60205260408120805460019290615fff908490616602565b909155505050565b5f54610100900460ff1661609d576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602b60248201527f496e697469616c697a61626c653a20636f6e7472616374206973206e6f74206960448201527f6e697469616c697a696e670000000000000000000000000000000000000000006064820152608401610ca6565b606580547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00169055565b5f54610100900460ff1661615d576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602b60248201527f496e697469616c697a61626c653a20636f6e7472616374206973206e6f74206960448201527f6e697469616c697a696e670000000000000000000000000000000000000000006064820152608401610ca6565b61278533615780565b60655460ff16612785576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601460248201527f5061757361626c653a206e6f74207061757365640000000000000000000000006044820152606401610ca6565b5f825f036161e15750826162ae565b609b5473ffffffffffffffffffffffffffffffffffffffff165f5b848110156162a8576040517fae453cd5000000000000000000000000000000000000000000000000000000008152600481018590525f9073ffffffffffffffffffffffffffffffffffffffff84169063ae453cd590602401602060405180830381865afa15801561626f573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906162939190616628565b875250602090950194600193840193016161fc565b50849150505b9392505050565b5f602082840312156162c5575f80fd5b5035919050565b803573ffffffffffffffffffffffffffffffffffffffff81168114614f7c575f80fd5b5f602082840312156162ff575f80fd5b6162ae826162cc565b5f8083601f840112616318575f80fd5b50813567ffffffffffffffff81111561632f575f80fd5b602083019150836020828501011115616346575f80fd5b9250929050565b5f805f6040848603121561635f575f80fd5b833567ffffffffffffffff811115616375575f80fd5b61638186828701616308565b909790965060209590950135949350505050565b5f80602083850312156163a6575f80fd5b823567ffffffffffffffff8111156163bc575f80fd5b6163c885828601616308565b90969095509350505050565b5f60e08284031215614eb5575f80fd5b5f805f805f606086880312156163f8575f80fd5b853567ffffffffffffffff8082111561640f575f80fd5b61641b89838a016163d4565b96506020880135915080821115616430575f80fd5b61643c89838a01616308565b90965094506040880135915080821115616454575f80fd5b5061646188828901616308565b969995985093965092949392505050565b5f60208284031215616482575f80fd5b813567ffffffffffffffff811115616498575f80fd5b6164a4848285016163d4565b949350505050565b5f805f805f8060c087890312156164c1575f80fd5b6164ca876162cc565b95506164d8602088016162cc565b94506164e6604088016162cc565b9350606087013592506080870135915060a087013590509295509295509295565b5f805f806040858703121561651a575f80fd5b843567ffffffffffffffff80821115616531575f80fd5b61653d88838901616308565b90965094506020870135915080821115616555575f80fd5b5061656287828801616308565b95989497509550505050565b8015158114613806575f80fd5b5f6020828403121561658b575f80fd5b81356162ae8161656e565b803567ffffffffffffffff81168114614f7c575f80fd5b5f80604083850312156165be575f80fd5b6165c783616596565b946020939093013593505050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601160045260245ffd5b80820180821115611c1457611c146165d5565b81810381811115611c1457611c146165d5565b5f60208284031215616638575f80fd5b5051919050565b5f6020828403121561664f575f80fd5b813561ffff811681146162ae575f80fd5b5f8083357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe1843603018112616693575f80fd5b83018035915067ffffffffffffffff8211156166ad575f80fd5b602001915036819003821315616346575f80fd5b5f602082840312156166d1575f80fd5b813560ff811681146162ae575f80fd5b5f602082840312156166f1575f80fd5b81516162ae8161656e565b5f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff820361672c5761672c6165d5565b5060010190565b5f60208284031215616743575f80fd5b6162ae82616596565b85815284602082015260806040820152826080820152828460a08301375f60a084830101525f60a07fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f86011683010190508260608301529695505050505050565b5f82515f5b818110156167ce57602081860181015185830152016167b4565b505f920191825250919050565b8082028115828204841417611c1457611c146165d5565b5f82616825577f4e487b71000000000000000000000000000000000000000000000000000000005f52601260045260245ffd5b500490565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52603260045260245ffdfea164736f6c6343000818000a" func init() { if err := json.Unmarshal([]byte(RollupStorageLayoutJSON), RollupStorageLayout); err != nil { diff --git a/bindings/bindings/submitter.go b/bindings/bindings/submitter.go new file mode 100644 index 000000000..9361dc2f0 --- /dev/null +++ b/bindings/bindings/submitter.go @@ -0,0 +1,2863 @@ +// Code generated - DO NOT EDIT. +// This file is a generated binding and any manual changes will be lost. + +package bindings + +import ( + "errors" + "math/big" + "strings" + + ethereum "github.com/morph-l2/go-ethereum" + "github.com/morph-l2/go-ethereum/accounts/abi" + "github.com/morph-l2/go-ethereum/accounts/abi/bind" + "github.com/morph-l2/go-ethereum/common" + "github.com/morph-l2/go-ethereum/core/types" + "github.com/morph-l2/go-ethereum/event" +) + +// Reference imports to suppress errors if they are not otherwise used. +var ( + _ = errors.New + _ = big.NewInt + _ = strings.NewReader + _ = ethereum.NotFound + _ = bind.Bind + _ = common.Big1 + _ = types.BloomLookup + _ = event.NewSubscription + _ = abi.ConvertType +) + +// SubmitterMetaData contains all meta data concerning the Submitter contract. +var SubmitterMetaData = &bind.MetaData{ + ABI: "[{\"inputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"oldValue\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"newValue\",\"type\":\"uint256\"}],\"name\":\"ChallengeDepositUpdated\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"submitter\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"receiver\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"CreditClaimed\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint8\",\"name\":\"version\",\"type\":\"uint8\"}],\"name\":\"Initialized\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"oldValue\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"newValue\",\"type\":\"uint256\"}],\"name\":\"MinimumStakeUpdated\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"previousOwner\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"newOwner\",\"type\":\"address\"}],\"name\":\"OwnershipTransferred\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"oldValue\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"newValue\",\"type\":\"uint256\"}],\"name\":\"RewardPercentageUpdated\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"receiver\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"SlashRemainingClaimed\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"submitter\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"reward\",\"type\":\"uint256\"}],\"name\":\"Slashed\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"submitter\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"total\",\"type\":\"uint256\"}],\"name\":\"Staked\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"submitter\",\"type\":\"address\"}],\"name\":\"SubmitterAdded\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"submitter\",\"type\":\"address\"}],\"name\":\"SubmitterRemoved\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"submitter\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"totalCredit\",\"type\":\"uint256\"}],\"name\":\"WithdrawalCredited\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"submitter\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"receiver\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"WithdrawalPaid\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"submitter\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"receiver\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"WithdrawalRequested\",\"type\":\"event\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"submitter\",\"type\":\"address\"}],\"name\":\"addSubmitter\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"challengeDeposit\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"receiver\",\"type\":\"address\"}],\"name\":\"claimCredit\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"receiver\",\"type\":\"address\"}],\"name\":\"claimSlashRemaining\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"receiver\",\"type\":\"address\"}],\"name\":\"claimWithdrawal\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"submitter\",\"type\":\"address\"}],\"name\":\"claimable\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"submitter\",\"type\":\"address\"}],\"name\":\"exiting\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"isExiting\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"owner_\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"rollupContract_\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"minimumStake_\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"challengeDeposit_\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"rewardPercentage_\",\"type\":\"uint256\"}],\"name\":\"initialize\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"submitter\",\"type\":\"address\"}],\"name\":\"isActive\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"minimumStake\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"owner\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"submitter\",\"type\":\"address\"}],\"name\":\"registered\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"isRegistered\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"submitter\",\"type\":\"address\"}],\"name\":\"removeSubmitter\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"renounceOwnership\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"rewardPercentage\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"rollupContract\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"value\",\"type\":\"uint256\"}],\"name\":\"setMinimumStake\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"submitter\",\"type\":\"address\"}],\"name\":\"slash\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"reward\",\"type\":\"uint256\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"slashRemaining\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"stake\",\"outputs\":[],\"stateMutability\":\"payable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"submitter\",\"type\":\"address\"}],\"name\":\"stakeOf\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"newOwner\",\"type\":\"address\"}],\"name\":\"transferOwnership\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"value\",\"type\":\"uint256\"}],\"name\":\"updateChallengeDeposit\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"value\",\"type\":\"uint256\"}],\"name\":\"updateRewardPercentage\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"receiver\",\"type\":\"address\"}],\"name\":\"withdraw\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"}]", + Bin: "0x608060405234801561000f575f80fd5b5061001861001d565b6100d9565b5f54610100900460ff16156100885760405162461bcd60e51b815260206004820152602760248201527f496e697469616c697a61626c653a20636f6e747261637420697320696e697469604482015266616c697a696e6760c81b606482015260840160405180910390fd5b5f5460ff908116146100d7575f805460ff191660ff9081179091556040519081527f7f26b83ff96e1f2b6a682f133852f6798a09c465da95921460cefb38474024989060200160405180910390a15b565b611df9806100e65f395ff3fe60806040526004361061018e575f3560e01c80639f8a13d7116100dc578063bfa02ba911610087578063cde4cd1111610062578063cde4cd111461044a578063d13f90b414610469578063ec5ffac214610488578063f2fde38b1461049d575f80fd5b8063bfa02ba9146103ed578063c36f41e21461040c578063c96be4cb1461042b575f80fd5b8063ab8c53dc116100b7578063ab8c53dc1461037c578063b2dd5c0714610391578063bbfefd38146103bf575f80fd5b80639f8a13d71461030f578063a3066aab1461033e578063a4f209b01461035d575f80fd5b8063426233601161013c57806360e274641161011757806360e27464146102ab578063715018a6146102ca5780638da5cb5b146102de575f80fd5b8063426233601461024c57806351cff8d91461027757806352d472eb14610296575f80fd5b8063359289911161016c57806335928991146101fa5780633a4b66f114610219578063402914f514610221575f80fd5b8063072900f9146101925780630d13fd7b146101b3578063233e9903146101db575b5f80fd5b34801561019d575f80fd5b506101b16101ac366004611cca565b6104bc565b005b3480156101be575f80fd5b506101c860995481565b6040519081526020015b60405180910390f35b3480156101e6575f80fd5b506101b16101f5366004611cea565b6105b4565b348015610205575f80fd5b506101b1610214366004611cea565b61065a565b6101b1610700565b34801561022c575f80fd5b506101c861023b366004611cca565b609f6020525f908152604090205481565b348015610257575f80fd5b506101c8610266366004611cca565b609d6020525f908152604090205481565b348015610282575f80fd5b506101b1610291366004611cca565b61086e565b3480156102a1575f80fd5b506101c8609a5481565b3480156102b6575f80fd5b506101b16102c5366004611cca565b610a06565b3480156102d5575f80fd5b506101b1610b45565b3480156102e9575f80fd5b506033546001600160a01b03165b6040516001600160a01b0390911681526020016101d2565b34801561031a575f80fd5b5061032e610329366004611cca565b610b56565b60405190151581526020016101d2565b348015610349575f80fd5b506101b1610358366004611cca565b610bc0565b348015610368575f80fd5b506101b1610377366004611cea565b610ce5565b348015610387575f80fd5b506101c8609b5481565b34801561039c575f80fd5b5061032e6103ab366004611cca565b609c6020525f908152604090205460ff1681565b3480156103ca575f80fd5b5061032e6103d9366004611cca565b609e6020525f908152604090205460ff1681565b3480156103f8575f80fd5b506097546102f7906001600160a01b031681565b348015610417575f80fd5b506101b1610426366004611cca565b610d98565b348015610436575f80fd5b506101c8610445366004611cca565b610ef8565b348015610455575f80fd5b506101b1610464366004611cca565b611064565b348015610474575f80fd5b506101b1610483366004611d01565b611176565b348015610493575f80fd5b506101c860985481565b3480156104a8575f80fd5b506101b16104b7366004611cca565b61158a565b6104c4611617565b6001600160a01b038116158015906104f457506001600160a01b0381165f908152609c602052604090205460ff16155b801561051857506001600160a01b0381165f908152609e602052604090205460ff16155b6105695760405162461bcd60e51b815260206004820152601160248201527f696e76616c6964207375626d697474657200000000000000000000000000000060448201526064015b60405180910390fd5b6001600160a01b0381165f818152609c6020526040808220805460ff19166001179055517fb079bc2cbde1f186e0b351d4a87c4597e3ed098f571548617449e73506428d8b9190a250565b6105bc611617565b5f811180156105cd57506098548114155b6106195760405162461bcd60e51b815260206004820152601560248201527f696e76616c6964206d696e696d756d207374616b6500000000000000000000006044820152606401610560565b60985460408051918252602082018390527fd67ed534faf3e0dcda08ac6043ec96dcf9b6ebec56055fd14bd0cb40a27c0e49910160405180910390a1609855565b610662611617565b5f8111801561067357506099548114155b6106bf5760405162461bcd60e51b815260206004820152601960248201527f696e76616c6964206368616c6c656e6765206465706f736974000000000000006044820152606401610560565b60995460408051918252602082018390527f36f971a40478225aeb80cfbf5e80306e8cb76d3bf7d56fdc5e490945cddb7d55910160405180910390a1609955565b610708611671565b335f818152609c602052604090205460ff16801561073e57506001600160a01b0381165f908152609e602052604090205460ff16155b61078a5760405162461bcd60e51b815260206004820152600d60248201527f6e6f74207374616b6561626c65000000000000000000000000000000000000006044820152606401610560565b6001600160a01b0381165f908152609d60205260408120546107ad903490611d77565b90505f341180156107c057506098548110155b61080c5760405162461bcd60e51b815260206004820152601360248201527f62656c6f77206d696e696d756d207374616b65000000000000000000000000006044820152606401610560565b6001600160a01b0382165f818152609d602090815260409182902084905581513481529081018490527f1449c6dd7851abc30abf37f57715f492010519147cc2652fbc38202c18a6ee90910160405180910390a2505061086c6001606555565b565b610876611671565b336001600160a01b0382166108cd5760405162461bcd60e51b815260206004820152601060248201527f696e76616c6964207265636569766572000000000000000000000000000000006044820152606401610560565b6001600160a01b0381165f908152609c602052604090205460ff16801561090c57506001600160a01b0381165f908152609e602052604090205460ff16155b801561092e57506001600160a01b0381165f908152609d602052604090205415155b61097a5760405162461bcd60e51b815260206004820152601060248201527f6e6f7420776974686472617761626c65000000000000000000000000000000006044820152606401610560565b6001600160a01b038181165f818152609c60209081526040808320805460ff19908116909155609e835281842080549091166001179055609d825291829020549151918252928516927f04c56a409d50971e45c5a2d96e5d557d2b0f1d66d40f14b141e4c958b0f39b32910160405180910390a36109f881836116d1565b50610a036001606555565b50565b610a0e611671565b336001600160a01b038216610a655760405162461bcd60e51b815260206004820152601060248201527f696e76616c6964207265636569766572000000000000000000000000000000006044820152606401610560565b6001600160a01b0381165f908152609f602052604090205480610aca5760405162461bcd60e51b815260206004820152600960248201527f6e6f2063726564697400000000000000000000000000000000000000000000006044820152606401610560565b6001600160a01b0382165f908152609f6020526040812055610aec83826116ed565b826001600160a01b0316826001600160a01b03167fc4b5041307ee38be8c4f25df5d980a372ac8a9a73ea8f459891000a5386f532283604051610b3191815260200190565b60405180910390a35050610a036001606555565b610b4d611617565b61086c5f61179c565b6001600160a01b0381165f908152609c602052604081205460ff168015610b9557506001600160a01b0382165f908152609e602052604090205460ff16155b8015610bba57506098546001600160a01b0383165f908152609d602052604090205410155b92915050565b610bc8611671565b336001600160a01b038216610c1f5760405162461bcd60e51b815260206004820152601060248201527f696e76616c6964207265636569766572000000000000000000000000000000006044820152606401610560565b6001600160a01b0381165f908152609e602052604090205460ff16610c865760405162461bcd60e51b815260206004820152600b60248201527f6e6f742065786974696e670000000000000000000000000000000000000000006044820152606401610560565b610c8f81611805565b610cdb5760405162461bcd60e51b815260206004820152601760248201527f70656e64696e67206261746368206c696162696c6974790000000000000000006044820152606401610560565b6109f881836118f0565b610ced611617565b5f81118015610cfd575060648111155b8015610d0b5750609a548114155b610d575760405162461bcd60e51b815260206004820152601960248201527f696e76616c6964207265776172642070657263656e74616765000000000000006044820152606401610560565b609a5460408051918252602082018390527fa46de936426e045703b2d34a292a19fde92b329018db8e0da750033876b655ba910160405180910390a1609a55565b610da0611617565b610da8611671565b6001600160a01b0381165f908152609c602052604090205460ff168015610de757506001600160a01b0381165f908152609e602052604090205460ff16155b610e335760405162461bcd60e51b815260206004820152601160248201527f696e76616c6964207375626d69747465720000000000000000000000000000006044820152606401610560565b6001600160a01b0381165f818152609c60209081526040808320805460ff19908116909155609e9092528083208054909216600117909155517ff84a004e1673d2f349a7c93c72b3794b8eba6d2f9338044d8c8cd260e51a57a19190a26001600160a01b0381165f818152609d60205260409081902054905182917f04c56a409d50971e45c5a2d96e5d557d2b0f1d66d40f14b141e4c958b0f39b3291610edc91815260200190565b60405180910390a3610eee81826116d1565b610a036001606555565b6097545f906001600160a01b0316336001600160a01b031614610f5d5760405162461bcd60e51b815260206004820152601460248201527f6f6e6c7920726f6c6c757020636f6e74726163740000000000000000000000006044820152606401610560565b610f65611671565b6001600160a01b038216610f7a57505f611055565b6001600160a01b0382165f908152609d602090815260408083208054609c8452828520805460ff1990811690915591859055609e90935292208054909216600117909155609a54606490610fce9083611d8a565b610fd89190611da1565b9150610fe48282611dd9565b609b5f828254610ff49190611d77565b909155505060975461100f906001600160a01b0316836116ed565b60408051828152602081018490526001600160a01b038516917f45a371af55b0726877a30f464edc14db5879ab096590bacce682cf6c18223596910160405180910390a2505b61105f6001606555565b919050565b61106c611617565b611074611671565b6001600160a01b0381166110ca5760405162461bcd60e51b815260206004820152601060248201527f696e76616c6964207265636569766572000000000000000000000000000000006044820152606401610560565b609b548061111a5760405162461bcd60e51b815260206004820152601260248201527f6e6f20736c6173682072656d61696e696e6700000000000000000000000000006044820152606401610560565b5f609b5561112882826116ed565b816001600160a01b03167fa1fefb6c5328a92a416e321ed50997303fe7135fd88c28b0592b21ce42b5cdd98260405161116391815260200190565b60405180910390a250610a036001606555565b5f54610100900460ff161580801561119457505f54600160ff909116105b806111ad5750303b1580156111ad57505f5460ff166001145b61121f5760405162461bcd60e51b815260206004820152602e60248201527f496e697469616c697a61626c653a20636f6e747261637420697320616c72656160448201527f647920696e697469616c697a65640000000000000000000000000000000000006064820152608401610560565b5f805460ff19166001179055801561125d575f80547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ff166101001790555b6001600160a01b0386166112b35760405162461bcd60e51b815260206004820152600d60248201527f696e76616c6964206f776e6572000000000000000000000000000000000000006044820152606401610560565b6001600160a01b038516158015906112d457505f856001600160a01b03163b115b6113205760405162461bcd60e51b815260206004820152601760248201527f696e76616c696420726f6c6c757020636f6e74726163740000000000000000006044820152606401610560565b5f841161136f5760405162461bcd60e51b815260206004820152601560248201527f696e76616c6964206d696e696d756d207374616b6500000000000000000000006044820152606401610560565b5f83116113be5760405162461bcd60e51b815260206004820152601960248201527f696e76616c6964206368616c6c656e6765206465706f736974000000000000006044820152606401610560565b5f821180156113ce575060648211155b61141a5760405162461bcd60e51b815260206004820152601960248201527f696e76616c6964207265776172642070657263656e74616765000000000000006044820152606401610560565b611422611aab565b61142a611b2f565b6114338661179c565b609780547fffffffffffffffffffffffff0000000000000000000000000000000000000000166001600160a01b03871617905560988490556099839055609a829055604080515f8152602081018690527fd67ed534faf3e0dcda08ac6043ec96dcf9b6ebec56055fd14bd0cb40a27c0e49910160405180910390a1604080515f8152602081018590527f36f971a40478225aeb80cfbf5e80306e8cb76d3bf7d56fdc5e490945cddb7d55910160405180910390a1604080515f8152602081018490527fa46de936426e045703b2d34a292a19fde92b329018db8e0da750033876b655ba910160405180910390a18015611582575f80547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ff169055604051600181527f7f26b83ff96e1f2b6a682f133852f6798a09c465da95921460cefb38474024989060200160405180910390a15b505050505050565b611592611617565b6001600160a01b03811661160e5760405162461bcd60e51b815260206004820152602660248201527f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160448201527f64647265737300000000000000000000000000000000000000000000000000006064820152608401610560565b610a038161179c565b6033546001600160a01b0316331461086c5760405162461bcd60e51b815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e65726044820152606401610560565b6002606554036116c35760405162461bcd60e51b815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c006044820152606401610560565b6002606555565b6001606555565b6116da82611805565b156116e9576116e982826118f0565b5050565b805f036116f8575050565b5f826001600160a01b0316826040515f6040518083038185875af1925050503d805f8114611741576040519150601f19603f3d011682016040523d82523d5f602084013e611746565b606091505b50509050806117975760405162461bcd60e51b815260206004820152601360248201527f455448207472616e73666572206661696c6564000000000000000000000000006044820152606401610560565b505050565b603380546001600160a01b038381167fffffffffffffffffffffffff0000000000000000000000000000000000000000831681179093556040519116919082907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0905f90a35050565b6040516001600160a01b03821660248201525f908190604401604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529190526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167f8ef622a80000000000000000000000000000000000000000000000000000000017815260975482519293506001600160a01b0316915f9182918291829182918761c350fa92503d9150602082148316156118ce5760205f803e505f515b8280156118db5750816020145b80156118e5575080155b979650505050505050565b6001600160a01b0382165f908152609d60209081526040808320805490849055609e9092528220805460ff191690559081900361197957816001600160a01b0316836001600160a01b03167febb524abf6b48e81bfe3e2a2ba3d4050bc91b988d1cfaf677a91dc6652865f465f60405161196c91815260200190565b60405180910390a3505050565b5f826001600160a01b03168261c350906040515f60405180830381858888f193505050503d805f81146119c7576040519150601f19603f3d011682016040523d82523d5f602084013e6119cc565b606091505b505090508015611a2857826001600160a01b0316846001600160a01b03167febb524abf6b48e81bfe3e2a2ba3d4050bc91b988d1cfaf677a91dc6652865f4684604051611a1b91815260200190565b60405180910390a3611aa5565b6001600160a01b0384165f908152609f602052604081208054849290611a4f908490611d77565b90915550506001600160a01b0384165f818152609f6020908152604091829020548251868152918201527f335ec00177f14d116df2d9836dfb84e01159f61d1d9305a2130432fb47806ae4910160405180910390a25b50505050565b5f54610100900460ff16611b275760405162461bcd60e51b815260206004820152602b60248201527f496e697469616c697a61626c653a20636f6e7472616374206973206e6f74206960448201527f6e697469616c697a696e670000000000000000000000000000000000000000006064820152608401610560565b61086c611bb3565b5f54610100900460ff16611bab5760405162461bcd60e51b815260206004820152602b60248201527f496e697469616c697a61626c653a20636f6e7472616374206973206e6f74206960448201527f6e697469616c697a696e670000000000000000000000000000000000000000006064820152608401610560565b61086c611c38565b5f54610100900460ff16611c2f5760405162461bcd60e51b815260206004820152602b60248201527f496e697469616c697a61626c653a20636f6e7472616374206973206e6f74206960448201527f6e697469616c697a696e670000000000000000000000000000000000000000006064820152608401610560565b61086c3361179c565b5f54610100900460ff166116ca5760405162461bcd60e51b815260206004820152602b60248201527f496e697469616c697a61626c653a20636f6e7472616374206973206e6f74206960448201527f6e697469616c697a696e670000000000000000000000000000000000000000006064820152608401610560565b80356001600160a01b038116811461105f575f80fd5b5f60208284031215611cda575f80fd5b611ce382611cb4565b9392505050565b5f60208284031215611cfa575f80fd5b5035919050565b5f805f805f60a08688031215611d15575f80fd5b611d1e86611cb4565b9450611d2c60208701611cb4565b94979496505050506040830135926060810135926080909101359150565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601160045260245ffd5b80820180821115610bba57610bba611d4a565b8082028115828204841417610bba57610bba611d4a565b5f82611dd4577f4e487b71000000000000000000000000000000000000000000000000000000005f52601260045260245ffd5b500490565b81810381811115610bba57610bba611d4a56fea164736f6c6343000818000a", +} + +// SubmitterABI is the input ABI used to generate the binding from. +// Deprecated: Use SubmitterMetaData.ABI instead. +var SubmitterABI = SubmitterMetaData.ABI + +// SubmitterBin is the compiled bytecode used for deploying new contracts. +// Deprecated: Use SubmitterMetaData.Bin instead. +var SubmitterBin = SubmitterMetaData.Bin + +// DeploySubmitter deploys a new Ethereum contract, binding an instance of Submitter to it. +func DeploySubmitter(auth *bind.TransactOpts, backend bind.ContractBackend) (common.Address, *types.Transaction, *Submitter, error) { + parsed, err := SubmitterMetaData.GetAbi() + if err != nil { + return common.Address{}, nil, nil, err + } + if parsed == nil { + return common.Address{}, nil, nil, errors.New("GetABI returned nil") + } + + address, tx, contract, err := bind.DeployContract(auth, *parsed, common.FromHex(SubmitterBin), backend) + if err != nil { + return common.Address{}, nil, nil, err + } + return address, tx, &Submitter{SubmitterCaller: SubmitterCaller{contract: contract}, SubmitterTransactor: SubmitterTransactor{contract: contract}, SubmitterFilterer: SubmitterFilterer{contract: contract}}, nil +} + +// Submitter is an auto generated Go binding around an Ethereum contract. +type Submitter struct { + SubmitterCaller // Read-only binding to the contract + SubmitterTransactor // Write-only binding to the contract + SubmitterFilterer // Log filterer for contract events +} + +// SubmitterCaller is an auto generated read-only Go binding around an Ethereum contract. +type SubmitterCaller struct { + contract *bind.BoundContract // Generic contract wrapper for the low level calls +} + +// SubmitterTransactor is an auto generated write-only Go binding around an Ethereum contract. +type SubmitterTransactor struct { + contract *bind.BoundContract // Generic contract wrapper for the low level calls +} + +// SubmitterFilterer is an auto generated log filtering Go binding around an Ethereum contract events. +type SubmitterFilterer struct { + contract *bind.BoundContract // Generic contract wrapper for the low level calls +} + +// SubmitterSession is an auto generated Go binding around an Ethereum contract, +// with pre-set call and transact options. +type SubmitterSession struct { + Contract *Submitter // Generic contract binding to set the session for + CallOpts bind.CallOpts // Call options to use throughout this session + TransactOpts bind.TransactOpts // Transaction auth options to use throughout this session +} + +// SubmitterCallerSession is an auto generated read-only Go binding around an Ethereum contract, +// with pre-set call options. +type SubmitterCallerSession struct { + Contract *SubmitterCaller // Generic contract caller binding to set the session for + CallOpts bind.CallOpts // Call options to use throughout this session +} + +// SubmitterTransactorSession is an auto generated write-only Go binding around an Ethereum contract, +// with pre-set transact options. +type SubmitterTransactorSession struct { + Contract *SubmitterTransactor // Generic contract transactor binding to set the session for + TransactOpts bind.TransactOpts // Transaction auth options to use throughout this session +} + +// SubmitterRaw is an auto generated low-level Go binding around an Ethereum contract. +type SubmitterRaw struct { + Contract *Submitter // Generic contract binding to access the raw methods on +} + +// SubmitterCallerRaw is an auto generated low-level read-only Go binding around an Ethereum contract. +type SubmitterCallerRaw struct { + Contract *SubmitterCaller // Generic read-only contract binding to access the raw methods on +} + +// SubmitterTransactorRaw is an auto generated low-level write-only Go binding around an Ethereum contract. +type SubmitterTransactorRaw struct { + Contract *SubmitterTransactor // Generic write-only contract binding to access the raw methods on +} + +// NewSubmitter creates a new instance of Submitter, bound to a specific deployed contract. +func NewSubmitter(address common.Address, backend bind.ContractBackend) (*Submitter, error) { + contract, err := bindSubmitter(address, backend, backend, backend) + if err != nil { + return nil, err + } + return &Submitter{SubmitterCaller: SubmitterCaller{contract: contract}, SubmitterTransactor: SubmitterTransactor{contract: contract}, SubmitterFilterer: SubmitterFilterer{contract: contract}}, nil +} + +// NewSubmitterCaller creates a new read-only instance of Submitter, bound to a specific deployed contract. +func NewSubmitterCaller(address common.Address, caller bind.ContractCaller) (*SubmitterCaller, error) { + contract, err := bindSubmitter(address, caller, nil, nil) + if err != nil { + return nil, err + } + return &SubmitterCaller{contract: contract}, nil +} + +// NewSubmitterTransactor creates a new write-only instance of Submitter, bound to a specific deployed contract. +func NewSubmitterTransactor(address common.Address, transactor bind.ContractTransactor) (*SubmitterTransactor, error) { + contract, err := bindSubmitter(address, nil, transactor, nil) + if err != nil { + return nil, err + } + return &SubmitterTransactor{contract: contract}, nil +} + +// NewSubmitterFilterer creates a new log filterer instance of Submitter, bound to a specific deployed contract. +func NewSubmitterFilterer(address common.Address, filterer bind.ContractFilterer) (*SubmitterFilterer, error) { + contract, err := bindSubmitter(address, nil, nil, filterer) + if err != nil { + return nil, err + } + return &SubmitterFilterer{contract: contract}, nil +} + +// bindSubmitter binds a generic wrapper to an already deployed contract. +func bindSubmitter(address common.Address, caller bind.ContractCaller, transactor bind.ContractTransactor, filterer bind.ContractFilterer) (*bind.BoundContract, error) { + parsed, err := SubmitterMetaData.GetAbi() + if err != nil { + return nil, err + } + return bind.NewBoundContract(address, *parsed, caller, transactor, filterer), nil +} + +// Call invokes the (constant) contract method with params as input values and +// sets the output to result. The result type might be a single field for simple +// returns, a slice of interfaces for anonymous returns and a struct for named +// returns. +func (_Submitter *SubmitterRaw) Call(opts *bind.CallOpts, result *[]interface{}, method string, params ...interface{}) error { + return _Submitter.Contract.SubmitterCaller.contract.Call(opts, result, method, params...) +} + +// Transfer initiates a plain transaction to move funds to the contract, calling +// its default method if one is available. +func (_Submitter *SubmitterRaw) Transfer(opts *bind.TransactOpts) (*types.Transaction, error) { + return _Submitter.Contract.SubmitterTransactor.contract.Transfer(opts) +} + +// Transact invokes the (paid) contract method with params as input values. +func (_Submitter *SubmitterRaw) Transact(opts *bind.TransactOpts, method string, params ...interface{}) (*types.Transaction, error) { + return _Submitter.Contract.SubmitterTransactor.contract.Transact(opts, method, params...) +} + +// Call invokes the (constant) contract method with params as input values and +// sets the output to result. The result type might be a single field for simple +// returns, a slice of interfaces for anonymous returns and a struct for named +// returns. +func (_Submitter *SubmitterCallerRaw) Call(opts *bind.CallOpts, result *[]interface{}, method string, params ...interface{}) error { + return _Submitter.Contract.contract.Call(opts, result, method, params...) +} + +// Transfer initiates a plain transaction to move funds to the contract, calling +// its default method if one is available. +func (_Submitter *SubmitterTransactorRaw) Transfer(opts *bind.TransactOpts) (*types.Transaction, error) { + return _Submitter.Contract.contract.Transfer(opts) +} + +// Transact invokes the (paid) contract method with params as input values. +func (_Submitter *SubmitterTransactorRaw) Transact(opts *bind.TransactOpts, method string, params ...interface{}) (*types.Transaction, error) { + return _Submitter.Contract.contract.Transact(opts, method, params...) +} + +// ChallengeDeposit is a free data retrieval call binding the contract method 0x0d13fd7b. +// +// Solidity: function challengeDeposit() view returns(uint256) +func (_Submitter *SubmitterCaller) ChallengeDeposit(opts *bind.CallOpts) (*big.Int, error) { + var out []interface{} + err := _Submitter.contract.Call(opts, &out, "challengeDeposit") + + if err != nil { + return *new(*big.Int), err + } + + out0 := *abi.ConvertType(out[0], new(*big.Int)).(**big.Int) + + return out0, err + +} + +// ChallengeDeposit is a free data retrieval call binding the contract method 0x0d13fd7b. +// +// Solidity: function challengeDeposit() view returns(uint256) +func (_Submitter *SubmitterSession) ChallengeDeposit() (*big.Int, error) { + return _Submitter.Contract.ChallengeDeposit(&_Submitter.CallOpts) +} + +// ChallengeDeposit is a free data retrieval call binding the contract method 0x0d13fd7b. +// +// Solidity: function challengeDeposit() view returns(uint256) +func (_Submitter *SubmitterCallerSession) ChallengeDeposit() (*big.Int, error) { + return _Submitter.Contract.ChallengeDeposit(&_Submitter.CallOpts) +} + +// Claimable is a free data retrieval call binding the contract method 0x402914f5. +// +// Solidity: function claimable(address submitter) view returns(uint256 amount) +func (_Submitter *SubmitterCaller) Claimable(opts *bind.CallOpts, submitter common.Address) (*big.Int, error) { + var out []interface{} + err := _Submitter.contract.Call(opts, &out, "claimable", submitter) + + if err != nil { + return *new(*big.Int), err + } + + out0 := *abi.ConvertType(out[0], new(*big.Int)).(**big.Int) + + return out0, err + +} + +// Claimable is a free data retrieval call binding the contract method 0x402914f5. +// +// Solidity: function claimable(address submitter) view returns(uint256 amount) +func (_Submitter *SubmitterSession) Claimable(submitter common.Address) (*big.Int, error) { + return _Submitter.Contract.Claimable(&_Submitter.CallOpts, submitter) +} + +// Claimable is a free data retrieval call binding the contract method 0x402914f5. +// +// Solidity: function claimable(address submitter) view returns(uint256 amount) +func (_Submitter *SubmitterCallerSession) Claimable(submitter common.Address) (*big.Int, error) { + return _Submitter.Contract.Claimable(&_Submitter.CallOpts, submitter) +} + +// Exiting is a free data retrieval call binding the contract method 0xbbfefd38. +// +// Solidity: function exiting(address submitter) view returns(bool isExiting) +func (_Submitter *SubmitterCaller) Exiting(opts *bind.CallOpts, submitter common.Address) (bool, error) { + var out []interface{} + err := _Submitter.contract.Call(opts, &out, "exiting", submitter) + + if err != nil { + return *new(bool), err + } + + out0 := *abi.ConvertType(out[0], new(bool)).(*bool) + + return out0, err + +} + +// Exiting is a free data retrieval call binding the contract method 0xbbfefd38. +// +// Solidity: function exiting(address submitter) view returns(bool isExiting) +func (_Submitter *SubmitterSession) Exiting(submitter common.Address) (bool, error) { + return _Submitter.Contract.Exiting(&_Submitter.CallOpts, submitter) +} + +// Exiting is a free data retrieval call binding the contract method 0xbbfefd38. +// +// Solidity: function exiting(address submitter) view returns(bool isExiting) +func (_Submitter *SubmitterCallerSession) Exiting(submitter common.Address) (bool, error) { + return _Submitter.Contract.Exiting(&_Submitter.CallOpts, submitter) +} + +// IsActive is a free data retrieval call binding the contract method 0x9f8a13d7. +// +// Solidity: function isActive(address submitter) view returns(bool) +func (_Submitter *SubmitterCaller) IsActive(opts *bind.CallOpts, submitter common.Address) (bool, error) { + var out []interface{} + err := _Submitter.contract.Call(opts, &out, "isActive", submitter) + + if err != nil { + return *new(bool), err + } + + out0 := *abi.ConvertType(out[0], new(bool)).(*bool) + + return out0, err + +} + +// IsActive is a free data retrieval call binding the contract method 0x9f8a13d7. +// +// Solidity: function isActive(address submitter) view returns(bool) +func (_Submitter *SubmitterSession) IsActive(submitter common.Address) (bool, error) { + return _Submitter.Contract.IsActive(&_Submitter.CallOpts, submitter) +} + +// IsActive is a free data retrieval call binding the contract method 0x9f8a13d7. +// +// Solidity: function isActive(address submitter) view returns(bool) +func (_Submitter *SubmitterCallerSession) IsActive(submitter common.Address) (bool, error) { + return _Submitter.Contract.IsActive(&_Submitter.CallOpts, submitter) +} + +// MinimumStake is a free data retrieval call binding the contract method 0xec5ffac2. +// +// Solidity: function minimumStake() view returns(uint256) +func (_Submitter *SubmitterCaller) MinimumStake(opts *bind.CallOpts) (*big.Int, error) { + var out []interface{} + err := _Submitter.contract.Call(opts, &out, "minimumStake") + + if err != nil { + return *new(*big.Int), err + } + + out0 := *abi.ConvertType(out[0], new(*big.Int)).(**big.Int) + + return out0, err + +} + +// MinimumStake is a free data retrieval call binding the contract method 0xec5ffac2. +// +// Solidity: function minimumStake() view returns(uint256) +func (_Submitter *SubmitterSession) MinimumStake() (*big.Int, error) { + return _Submitter.Contract.MinimumStake(&_Submitter.CallOpts) +} + +// MinimumStake is a free data retrieval call binding the contract method 0xec5ffac2. +// +// Solidity: function minimumStake() view returns(uint256) +func (_Submitter *SubmitterCallerSession) MinimumStake() (*big.Int, error) { + return _Submitter.Contract.MinimumStake(&_Submitter.CallOpts) +} + +// Owner is a free data retrieval call binding the contract method 0x8da5cb5b. +// +// Solidity: function owner() view returns(address) +func (_Submitter *SubmitterCaller) Owner(opts *bind.CallOpts) (common.Address, error) { + var out []interface{} + err := _Submitter.contract.Call(opts, &out, "owner") + + if err != nil { + return *new(common.Address), err + } + + out0 := *abi.ConvertType(out[0], new(common.Address)).(*common.Address) + + return out0, err + +} + +// Owner is a free data retrieval call binding the contract method 0x8da5cb5b. +// +// Solidity: function owner() view returns(address) +func (_Submitter *SubmitterSession) Owner() (common.Address, error) { + return _Submitter.Contract.Owner(&_Submitter.CallOpts) +} + +// Owner is a free data retrieval call binding the contract method 0x8da5cb5b. +// +// Solidity: function owner() view returns(address) +func (_Submitter *SubmitterCallerSession) Owner() (common.Address, error) { + return _Submitter.Contract.Owner(&_Submitter.CallOpts) +} + +// Registered is a free data retrieval call binding the contract method 0xb2dd5c07. +// +// Solidity: function registered(address submitter) view returns(bool isRegistered) +func (_Submitter *SubmitterCaller) Registered(opts *bind.CallOpts, submitter common.Address) (bool, error) { + var out []interface{} + err := _Submitter.contract.Call(opts, &out, "registered", submitter) + + if err != nil { + return *new(bool), err + } + + out0 := *abi.ConvertType(out[0], new(bool)).(*bool) + + return out0, err + +} + +// Registered is a free data retrieval call binding the contract method 0xb2dd5c07. +// +// Solidity: function registered(address submitter) view returns(bool isRegistered) +func (_Submitter *SubmitterSession) Registered(submitter common.Address) (bool, error) { + return _Submitter.Contract.Registered(&_Submitter.CallOpts, submitter) +} + +// Registered is a free data retrieval call binding the contract method 0xb2dd5c07. +// +// Solidity: function registered(address submitter) view returns(bool isRegistered) +func (_Submitter *SubmitterCallerSession) Registered(submitter common.Address) (bool, error) { + return _Submitter.Contract.Registered(&_Submitter.CallOpts, submitter) +} + +// RewardPercentage is a free data retrieval call binding the contract method 0x52d472eb. +// +// Solidity: function rewardPercentage() view returns(uint256) +func (_Submitter *SubmitterCaller) RewardPercentage(opts *bind.CallOpts) (*big.Int, error) { + var out []interface{} + err := _Submitter.contract.Call(opts, &out, "rewardPercentage") + + if err != nil { + return *new(*big.Int), err + } + + out0 := *abi.ConvertType(out[0], new(*big.Int)).(**big.Int) + + return out0, err + +} + +// RewardPercentage is a free data retrieval call binding the contract method 0x52d472eb. +// +// Solidity: function rewardPercentage() view returns(uint256) +func (_Submitter *SubmitterSession) RewardPercentage() (*big.Int, error) { + return _Submitter.Contract.RewardPercentage(&_Submitter.CallOpts) +} + +// RewardPercentage is a free data retrieval call binding the contract method 0x52d472eb. +// +// Solidity: function rewardPercentage() view returns(uint256) +func (_Submitter *SubmitterCallerSession) RewardPercentage() (*big.Int, error) { + return _Submitter.Contract.RewardPercentage(&_Submitter.CallOpts) +} + +// RollupContract is a free data retrieval call binding the contract method 0xbfa02ba9. +// +// Solidity: function rollupContract() view returns(address) +func (_Submitter *SubmitterCaller) RollupContract(opts *bind.CallOpts) (common.Address, error) { + var out []interface{} + err := _Submitter.contract.Call(opts, &out, "rollupContract") + + if err != nil { + return *new(common.Address), err + } + + out0 := *abi.ConvertType(out[0], new(common.Address)).(*common.Address) + + return out0, err + +} + +// RollupContract is a free data retrieval call binding the contract method 0xbfa02ba9. +// +// Solidity: function rollupContract() view returns(address) +func (_Submitter *SubmitterSession) RollupContract() (common.Address, error) { + return _Submitter.Contract.RollupContract(&_Submitter.CallOpts) +} + +// RollupContract is a free data retrieval call binding the contract method 0xbfa02ba9. +// +// Solidity: function rollupContract() view returns(address) +func (_Submitter *SubmitterCallerSession) RollupContract() (common.Address, error) { + return _Submitter.Contract.RollupContract(&_Submitter.CallOpts) +} + +// SlashRemaining is a free data retrieval call binding the contract method 0xab8c53dc. +// +// Solidity: function slashRemaining() view returns(uint256) +func (_Submitter *SubmitterCaller) SlashRemaining(opts *bind.CallOpts) (*big.Int, error) { + var out []interface{} + err := _Submitter.contract.Call(opts, &out, "slashRemaining") + + if err != nil { + return *new(*big.Int), err + } + + out0 := *abi.ConvertType(out[0], new(*big.Int)).(**big.Int) + + return out0, err + +} + +// SlashRemaining is a free data retrieval call binding the contract method 0xab8c53dc. +// +// Solidity: function slashRemaining() view returns(uint256) +func (_Submitter *SubmitterSession) SlashRemaining() (*big.Int, error) { + return _Submitter.Contract.SlashRemaining(&_Submitter.CallOpts) +} + +// SlashRemaining is a free data retrieval call binding the contract method 0xab8c53dc. +// +// Solidity: function slashRemaining() view returns(uint256) +func (_Submitter *SubmitterCallerSession) SlashRemaining() (*big.Int, error) { + return _Submitter.Contract.SlashRemaining(&_Submitter.CallOpts) +} + +// StakeOf is a free data retrieval call binding the contract method 0x42623360. +// +// Solidity: function stakeOf(address submitter) view returns(uint256 amount) +func (_Submitter *SubmitterCaller) StakeOf(opts *bind.CallOpts, submitter common.Address) (*big.Int, error) { + var out []interface{} + err := _Submitter.contract.Call(opts, &out, "stakeOf", submitter) + + if err != nil { + return *new(*big.Int), err + } + + out0 := *abi.ConvertType(out[0], new(*big.Int)).(**big.Int) + + return out0, err + +} + +// StakeOf is a free data retrieval call binding the contract method 0x42623360. +// +// Solidity: function stakeOf(address submitter) view returns(uint256 amount) +func (_Submitter *SubmitterSession) StakeOf(submitter common.Address) (*big.Int, error) { + return _Submitter.Contract.StakeOf(&_Submitter.CallOpts, submitter) +} + +// StakeOf is a free data retrieval call binding the contract method 0x42623360. +// +// Solidity: function stakeOf(address submitter) view returns(uint256 amount) +func (_Submitter *SubmitterCallerSession) StakeOf(submitter common.Address) (*big.Int, error) { + return _Submitter.Contract.StakeOf(&_Submitter.CallOpts, submitter) +} + +// AddSubmitter is a paid mutator transaction binding the contract method 0x072900f9. +// +// Solidity: function addSubmitter(address submitter) returns() +func (_Submitter *SubmitterTransactor) AddSubmitter(opts *bind.TransactOpts, submitter common.Address) (*types.Transaction, error) { + return _Submitter.contract.Transact(opts, "addSubmitter", submitter) +} + +// AddSubmitter is a paid mutator transaction binding the contract method 0x072900f9. +// +// Solidity: function addSubmitter(address submitter) returns() +func (_Submitter *SubmitterSession) AddSubmitter(submitter common.Address) (*types.Transaction, error) { + return _Submitter.Contract.AddSubmitter(&_Submitter.TransactOpts, submitter) +} + +// AddSubmitter is a paid mutator transaction binding the contract method 0x072900f9. +// +// Solidity: function addSubmitter(address submitter) returns() +func (_Submitter *SubmitterTransactorSession) AddSubmitter(submitter common.Address) (*types.Transaction, error) { + return _Submitter.Contract.AddSubmitter(&_Submitter.TransactOpts, submitter) +} + +// ClaimCredit is a paid mutator transaction binding the contract method 0x60e27464. +// +// Solidity: function claimCredit(address receiver) returns() +func (_Submitter *SubmitterTransactor) ClaimCredit(opts *bind.TransactOpts, receiver common.Address) (*types.Transaction, error) { + return _Submitter.contract.Transact(opts, "claimCredit", receiver) +} + +// ClaimCredit is a paid mutator transaction binding the contract method 0x60e27464. +// +// Solidity: function claimCredit(address receiver) returns() +func (_Submitter *SubmitterSession) ClaimCredit(receiver common.Address) (*types.Transaction, error) { + return _Submitter.Contract.ClaimCredit(&_Submitter.TransactOpts, receiver) +} + +// ClaimCredit is a paid mutator transaction binding the contract method 0x60e27464. +// +// Solidity: function claimCredit(address receiver) returns() +func (_Submitter *SubmitterTransactorSession) ClaimCredit(receiver common.Address) (*types.Transaction, error) { + return _Submitter.Contract.ClaimCredit(&_Submitter.TransactOpts, receiver) +} + +// ClaimSlashRemaining is a paid mutator transaction binding the contract method 0xcde4cd11. +// +// Solidity: function claimSlashRemaining(address receiver) returns() +func (_Submitter *SubmitterTransactor) ClaimSlashRemaining(opts *bind.TransactOpts, receiver common.Address) (*types.Transaction, error) { + return _Submitter.contract.Transact(opts, "claimSlashRemaining", receiver) +} + +// ClaimSlashRemaining is a paid mutator transaction binding the contract method 0xcde4cd11. +// +// Solidity: function claimSlashRemaining(address receiver) returns() +func (_Submitter *SubmitterSession) ClaimSlashRemaining(receiver common.Address) (*types.Transaction, error) { + return _Submitter.Contract.ClaimSlashRemaining(&_Submitter.TransactOpts, receiver) +} + +// ClaimSlashRemaining is a paid mutator transaction binding the contract method 0xcde4cd11. +// +// Solidity: function claimSlashRemaining(address receiver) returns() +func (_Submitter *SubmitterTransactorSession) ClaimSlashRemaining(receiver common.Address) (*types.Transaction, error) { + return _Submitter.Contract.ClaimSlashRemaining(&_Submitter.TransactOpts, receiver) +} + +// ClaimWithdrawal is a paid mutator transaction binding the contract method 0xa3066aab. +// +// Solidity: function claimWithdrawal(address receiver) returns() +func (_Submitter *SubmitterTransactor) ClaimWithdrawal(opts *bind.TransactOpts, receiver common.Address) (*types.Transaction, error) { + return _Submitter.contract.Transact(opts, "claimWithdrawal", receiver) +} + +// ClaimWithdrawal is a paid mutator transaction binding the contract method 0xa3066aab. +// +// Solidity: function claimWithdrawal(address receiver) returns() +func (_Submitter *SubmitterSession) ClaimWithdrawal(receiver common.Address) (*types.Transaction, error) { + return _Submitter.Contract.ClaimWithdrawal(&_Submitter.TransactOpts, receiver) +} + +// ClaimWithdrawal is a paid mutator transaction binding the contract method 0xa3066aab. +// +// Solidity: function claimWithdrawal(address receiver) returns() +func (_Submitter *SubmitterTransactorSession) ClaimWithdrawal(receiver common.Address) (*types.Transaction, error) { + return _Submitter.Contract.ClaimWithdrawal(&_Submitter.TransactOpts, receiver) +} + +// Initialize is a paid mutator transaction binding the contract method 0xd13f90b4. +// +// Solidity: function initialize(address owner_, address rollupContract_, uint256 minimumStake_, uint256 challengeDeposit_, uint256 rewardPercentage_) returns() +func (_Submitter *SubmitterTransactor) Initialize(opts *bind.TransactOpts, owner_ common.Address, rollupContract_ common.Address, minimumStake_ *big.Int, challengeDeposit_ *big.Int, rewardPercentage_ *big.Int) (*types.Transaction, error) { + return _Submitter.contract.Transact(opts, "initialize", owner_, rollupContract_, minimumStake_, challengeDeposit_, rewardPercentage_) +} + +// Initialize is a paid mutator transaction binding the contract method 0xd13f90b4. +// +// Solidity: function initialize(address owner_, address rollupContract_, uint256 minimumStake_, uint256 challengeDeposit_, uint256 rewardPercentage_) returns() +func (_Submitter *SubmitterSession) Initialize(owner_ common.Address, rollupContract_ common.Address, minimumStake_ *big.Int, challengeDeposit_ *big.Int, rewardPercentage_ *big.Int) (*types.Transaction, error) { + return _Submitter.Contract.Initialize(&_Submitter.TransactOpts, owner_, rollupContract_, minimumStake_, challengeDeposit_, rewardPercentage_) +} + +// Initialize is a paid mutator transaction binding the contract method 0xd13f90b4. +// +// Solidity: function initialize(address owner_, address rollupContract_, uint256 minimumStake_, uint256 challengeDeposit_, uint256 rewardPercentage_) returns() +func (_Submitter *SubmitterTransactorSession) Initialize(owner_ common.Address, rollupContract_ common.Address, minimumStake_ *big.Int, challengeDeposit_ *big.Int, rewardPercentage_ *big.Int) (*types.Transaction, error) { + return _Submitter.Contract.Initialize(&_Submitter.TransactOpts, owner_, rollupContract_, minimumStake_, challengeDeposit_, rewardPercentage_) +} + +// RemoveSubmitter is a paid mutator transaction binding the contract method 0xc36f41e2. +// +// Solidity: function removeSubmitter(address submitter) returns() +func (_Submitter *SubmitterTransactor) RemoveSubmitter(opts *bind.TransactOpts, submitter common.Address) (*types.Transaction, error) { + return _Submitter.contract.Transact(opts, "removeSubmitter", submitter) +} + +// RemoveSubmitter is a paid mutator transaction binding the contract method 0xc36f41e2. +// +// Solidity: function removeSubmitter(address submitter) returns() +func (_Submitter *SubmitterSession) RemoveSubmitter(submitter common.Address) (*types.Transaction, error) { + return _Submitter.Contract.RemoveSubmitter(&_Submitter.TransactOpts, submitter) +} + +// RemoveSubmitter is a paid mutator transaction binding the contract method 0xc36f41e2. +// +// Solidity: function removeSubmitter(address submitter) returns() +func (_Submitter *SubmitterTransactorSession) RemoveSubmitter(submitter common.Address) (*types.Transaction, error) { + return _Submitter.Contract.RemoveSubmitter(&_Submitter.TransactOpts, submitter) +} + +// RenounceOwnership is a paid mutator transaction binding the contract method 0x715018a6. +// +// Solidity: function renounceOwnership() returns() +func (_Submitter *SubmitterTransactor) RenounceOwnership(opts *bind.TransactOpts) (*types.Transaction, error) { + return _Submitter.contract.Transact(opts, "renounceOwnership") +} + +// RenounceOwnership is a paid mutator transaction binding the contract method 0x715018a6. +// +// Solidity: function renounceOwnership() returns() +func (_Submitter *SubmitterSession) RenounceOwnership() (*types.Transaction, error) { + return _Submitter.Contract.RenounceOwnership(&_Submitter.TransactOpts) +} + +// RenounceOwnership is a paid mutator transaction binding the contract method 0x715018a6. +// +// Solidity: function renounceOwnership() returns() +func (_Submitter *SubmitterTransactorSession) RenounceOwnership() (*types.Transaction, error) { + return _Submitter.Contract.RenounceOwnership(&_Submitter.TransactOpts) +} + +// SetMinimumStake is a paid mutator transaction binding the contract method 0x233e9903. +// +// Solidity: function setMinimumStake(uint256 value) returns() +func (_Submitter *SubmitterTransactor) SetMinimumStake(opts *bind.TransactOpts, value *big.Int) (*types.Transaction, error) { + return _Submitter.contract.Transact(opts, "setMinimumStake", value) +} + +// SetMinimumStake is a paid mutator transaction binding the contract method 0x233e9903. +// +// Solidity: function setMinimumStake(uint256 value) returns() +func (_Submitter *SubmitterSession) SetMinimumStake(value *big.Int) (*types.Transaction, error) { + return _Submitter.Contract.SetMinimumStake(&_Submitter.TransactOpts, value) +} + +// SetMinimumStake is a paid mutator transaction binding the contract method 0x233e9903. +// +// Solidity: function setMinimumStake(uint256 value) returns() +func (_Submitter *SubmitterTransactorSession) SetMinimumStake(value *big.Int) (*types.Transaction, error) { + return _Submitter.Contract.SetMinimumStake(&_Submitter.TransactOpts, value) +} + +// Slash is a paid mutator transaction binding the contract method 0xc96be4cb. +// +// Solidity: function slash(address submitter) returns(uint256 reward) +func (_Submitter *SubmitterTransactor) Slash(opts *bind.TransactOpts, submitter common.Address) (*types.Transaction, error) { + return _Submitter.contract.Transact(opts, "slash", submitter) +} + +// Slash is a paid mutator transaction binding the contract method 0xc96be4cb. +// +// Solidity: function slash(address submitter) returns(uint256 reward) +func (_Submitter *SubmitterSession) Slash(submitter common.Address) (*types.Transaction, error) { + return _Submitter.Contract.Slash(&_Submitter.TransactOpts, submitter) +} + +// Slash is a paid mutator transaction binding the contract method 0xc96be4cb. +// +// Solidity: function slash(address submitter) returns(uint256 reward) +func (_Submitter *SubmitterTransactorSession) Slash(submitter common.Address) (*types.Transaction, error) { + return _Submitter.Contract.Slash(&_Submitter.TransactOpts, submitter) +} + +// Stake is a paid mutator transaction binding the contract method 0x3a4b66f1. +// +// Solidity: function stake() payable returns() +func (_Submitter *SubmitterTransactor) Stake(opts *bind.TransactOpts) (*types.Transaction, error) { + return _Submitter.contract.Transact(opts, "stake") +} + +// Stake is a paid mutator transaction binding the contract method 0x3a4b66f1. +// +// Solidity: function stake() payable returns() +func (_Submitter *SubmitterSession) Stake() (*types.Transaction, error) { + return _Submitter.Contract.Stake(&_Submitter.TransactOpts) +} + +// Stake is a paid mutator transaction binding the contract method 0x3a4b66f1. +// +// Solidity: function stake() payable returns() +func (_Submitter *SubmitterTransactorSession) Stake() (*types.Transaction, error) { + return _Submitter.Contract.Stake(&_Submitter.TransactOpts) +} + +// TransferOwnership is a paid mutator transaction binding the contract method 0xf2fde38b. +// +// Solidity: function transferOwnership(address newOwner) returns() +func (_Submitter *SubmitterTransactor) TransferOwnership(opts *bind.TransactOpts, newOwner common.Address) (*types.Transaction, error) { + return _Submitter.contract.Transact(opts, "transferOwnership", newOwner) +} + +// TransferOwnership is a paid mutator transaction binding the contract method 0xf2fde38b. +// +// Solidity: function transferOwnership(address newOwner) returns() +func (_Submitter *SubmitterSession) TransferOwnership(newOwner common.Address) (*types.Transaction, error) { + return _Submitter.Contract.TransferOwnership(&_Submitter.TransactOpts, newOwner) +} + +// TransferOwnership is a paid mutator transaction binding the contract method 0xf2fde38b. +// +// Solidity: function transferOwnership(address newOwner) returns() +func (_Submitter *SubmitterTransactorSession) TransferOwnership(newOwner common.Address) (*types.Transaction, error) { + return _Submitter.Contract.TransferOwnership(&_Submitter.TransactOpts, newOwner) +} + +// UpdateChallengeDeposit is a paid mutator transaction binding the contract method 0x35928991. +// +// Solidity: function updateChallengeDeposit(uint256 value) returns() +func (_Submitter *SubmitterTransactor) UpdateChallengeDeposit(opts *bind.TransactOpts, value *big.Int) (*types.Transaction, error) { + return _Submitter.contract.Transact(opts, "updateChallengeDeposit", value) +} + +// UpdateChallengeDeposit is a paid mutator transaction binding the contract method 0x35928991. +// +// Solidity: function updateChallengeDeposit(uint256 value) returns() +func (_Submitter *SubmitterSession) UpdateChallengeDeposit(value *big.Int) (*types.Transaction, error) { + return _Submitter.Contract.UpdateChallengeDeposit(&_Submitter.TransactOpts, value) +} + +// UpdateChallengeDeposit is a paid mutator transaction binding the contract method 0x35928991. +// +// Solidity: function updateChallengeDeposit(uint256 value) returns() +func (_Submitter *SubmitterTransactorSession) UpdateChallengeDeposit(value *big.Int) (*types.Transaction, error) { + return _Submitter.Contract.UpdateChallengeDeposit(&_Submitter.TransactOpts, value) +} + +// UpdateRewardPercentage is a paid mutator transaction binding the contract method 0xa4f209b0. +// +// Solidity: function updateRewardPercentage(uint256 value) returns() +func (_Submitter *SubmitterTransactor) UpdateRewardPercentage(opts *bind.TransactOpts, value *big.Int) (*types.Transaction, error) { + return _Submitter.contract.Transact(opts, "updateRewardPercentage", value) +} + +// UpdateRewardPercentage is a paid mutator transaction binding the contract method 0xa4f209b0. +// +// Solidity: function updateRewardPercentage(uint256 value) returns() +func (_Submitter *SubmitterSession) UpdateRewardPercentage(value *big.Int) (*types.Transaction, error) { + return _Submitter.Contract.UpdateRewardPercentage(&_Submitter.TransactOpts, value) +} + +// UpdateRewardPercentage is a paid mutator transaction binding the contract method 0xa4f209b0. +// +// Solidity: function updateRewardPercentage(uint256 value) returns() +func (_Submitter *SubmitterTransactorSession) UpdateRewardPercentage(value *big.Int) (*types.Transaction, error) { + return _Submitter.Contract.UpdateRewardPercentage(&_Submitter.TransactOpts, value) +} + +// Withdraw is a paid mutator transaction binding the contract method 0x51cff8d9. +// +// Solidity: function withdraw(address receiver) returns() +func (_Submitter *SubmitterTransactor) Withdraw(opts *bind.TransactOpts, receiver common.Address) (*types.Transaction, error) { + return _Submitter.contract.Transact(opts, "withdraw", receiver) +} + +// Withdraw is a paid mutator transaction binding the contract method 0x51cff8d9. +// +// Solidity: function withdraw(address receiver) returns() +func (_Submitter *SubmitterSession) Withdraw(receiver common.Address) (*types.Transaction, error) { + return _Submitter.Contract.Withdraw(&_Submitter.TransactOpts, receiver) +} + +// Withdraw is a paid mutator transaction binding the contract method 0x51cff8d9. +// +// Solidity: function withdraw(address receiver) returns() +func (_Submitter *SubmitterTransactorSession) Withdraw(receiver common.Address) (*types.Transaction, error) { + return _Submitter.Contract.Withdraw(&_Submitter.TransactOpts, receiver) +} + +// SubmitterChallengeDepositUpdatedIterator is returned from FilterChallengeDepositUpdated and is used to iterate over the raw logs and unpacked data for ChallengeDepositUpdated events raised by the Submitter contract. +type SubmitterChallengeDepositUpdatedIterator struct { + Event *SubmitterChallengeDepositUpdated // Event containing the contract specifics and raw log + + contract *bind.BoundContract // Generic contract to use for unpacking event data + event string // Event name to use for unpacking event data + + logs chan types.Log // Log channel receiving the found contract events + sub ethereum.Subscription // Subscription for errors, completion and termination + done bool // Whether the subscription completed delivering logs + fail error // Occurred error to stop iteration +} + +// Next advances the iterator to the subsequent event, returning whether there +// are any more events found. In case of a retrieval or parsing error, false is +// returned and Error() can be queried for the exact failure. +func (it *SubmitterChallengeDepositUpdatedIterator) Next() bool { + // If the iterator failed, stop iterating + if it.fail != nil { + return false + } + // If the iterator completed, deliver directly whatever's available + if it.done { + select { + case log := <-it.logs: + it.Event = new(SubmitterChallengeDepositUpdated) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + default: + return false + } + } + // Iterator still in progress, wait for either a data or an error event + select { + case log := <-it.logs: + it.Event = new(SubmitterChallengeDepositUpdated) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + case err := <-it.sub.Err(): + it.done = true + it.fail = err + return it.Next() + } +} + +// Error returns any retrieval or parsing error occurred during filtering. +func (it *SubmitterChallengeDepositUpdatedIterator) Error() error { + return it.fail +} + +// Close terminates the iteration process, releasing any pending underlying +// resources. +func (it *SubmitterChallengeDepositUpdatedIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +// SubmitterChallengeDepositUpdated represents a ChallengeDepositUpdated event raised by the Submitter contract. +type SubmitterChallengeDepositUpdated struct { + OldValue *big.Int + NewValue *big.Int + Raw types.Log // Blockchain specific contextual infos +} + +// FilterChallengeDepositUpdated is a free log retrieval operation binding the contract event 0x36f971a40478225aeb80cfbf5e80306e8cb76d3bf7d56fdc5e490945cddb7d55. +// +// Solidity: event ChallengeDepositUpdated(uint256 oldValue, uint256 newValue) +func (_Submitter *SubmitterFilterer) FilterChallengeDepositUpdated(opts *bind.FilterOpts) (*SubmitterChallengeDepositUpdatedIterator, error) { + + logs, sub, err := _Submitter.contract.FilterLogs(opts, "ChallengeDepositUpdated") + if err != nil { + return nil, err + } + return &SubmitterChallengeDepositUpdatedIterator{contract: _Submitter.contract, event: "ChallengeDepositUpdated", logs: logs, sub: sub}, nil +} + +// WatchChallengeDepositUpdated is a free log subscription operation binding the contract event 0x36f971a40478225aeb80cfbf5e80306e8cb76d3bf7d56fdc5e490945cddb7d55. +// +// Solidity: event ChallengeDepositUpdated(uint256 oldValue, uint256 newValue) +func (_Submitter *SubmitterFilterer) WatchChallengeDepositUpdated(opts *bind.WatchOpts, sink chan<- *SubmitterChallengeDepositUpdated) (event.Subscription, error) { + + logs, sub, err := _Submitter.contract.WatchLogs(opts, "ChallengeDepositUpdated") + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + // New log arrived, parse the event and forward to the user + event := new(SubmitterChallengeDepositUpdated) + if err := _Submitter.contract.UnpackLog(event, "ChallengeDepositUpdated", log); err != nil { + return err + } + event.Raw = log + + select { + case sink <- event: + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + } + }), nil +} + +// ParseChallengeDepositUpdated is a log parse operation binding the contract event 0x36f971a40478225aeb80cfbf5e80306e8cb76d3bf7d56fdc5e490945cddb7d55. +// +// Solidity: event ChallengeDepositUpdated(uint256 oldValue, uint256 newValue) +func (_Submitter *SubmitterFilterer) ParseChallengeDepositUpdated(log types.Log) (*SubmitterChallengeDepositUpdated, error) { + event := new(SubmitterChallengeDepositUpdated) + if err := _Submitter.contract.UnpackLog(event, "ChallengeDepositUpdated", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +// SubmitterCreditClaimedIterator is returned from FilterCreditClaimed and is used to iterate over the raw logs and unpacked data for CreditClaimed events raised by the Submitter contract. +type SubmitterCreditClaimedIterator struct { + Event *SubmitterCreditClaimed // Event containing the contract specifics and raw log + + contract *bind.BoundContract // Generic contract to use for unpacking event data + event string // Event name to use for unpacking event data + + logs chan types.Log // Log channel receiving the found contract events + sub ethereum.Subscription // Subscription for errors, completion and termination + done bool // Whether the subscription completed delivering logs + fail error // Occurred error to stop iteration +} + +// Next advances the iterator to the subsequent event, returning whether there +// are any more events found. In case of a retrieval or parsing error, false is +// returned and Error() can be queried for the exact failure. +func (it *SubmitterCreditClaimedIterator) Next() bool { + // If the iterator failed, stop iterating + if it.fail != nil { + return false + } + // If the iterator completed, deliver directly whatever's available + if it.done { + select { + case log := <-it.logs: + it.Event = new(SubmitterCreditClaimed) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + default: + return false + } + } + // Iterator still in progress, wait for either a data or an error event + select { + case log := <-it.logs: + it.Event = new(SubmitterCreditClaimed) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + case err := <-it.sub.Err(): + it.done = true + it.fail = err + return it.Next() + } +} + +// Error returns any retrieval or parsing error occurred during filtering. +func (it *SubmitterCreditClaimedIterator) Error() error { + return it.fail +} + +// Close terminates the iteration process, releasing any pending underlying +// resources. +func (it *SubmitterCreditClaimedIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +// SubmitterCreditClaimed represents a CreditClaimed event raised by the Submitter contract. +type SubmitterCreditClaimed struct { + Submitter common.Address + Receiver common.Address + Amount *big.Int + Raw types.Log // Blockchain specific contextual infos +} + +// FilterCreditClaimed is a free log retrieval operation binding the contract event 0xc4b5041307ee38be8c4f25df5d980a372ac8a9a73ea8f459891000a5386f5322. +// +// Solidity: event CreditClaimed(address indexed submitter, address indexed receiver, uint256 amount) +func (_Submitter *SubmitterFilterer) FilterCreditClaimed(opts *bind.FilterOpts, submitter []common.Address, receiver []common.Address) (*SubmitterCreditClaimedIterator, error) { + + var submitterRule []interface{} + for _, submitterItem := range submitter { + submitterRule = append(submitterRule, submitterItem) + } + var receiverRule []interface{} + for _, receiverItem := range receiver { + receiverRule = append(receiverRule, receiverItem) + } + + logs, sub, err := _Submitter.contract.FilterLogs(opts, "CreditClaimed", submitterRule, receiverRule) + if err != nil { + return nil, err + } + return &SubmitterCreditClaimedIterator{contract: _Submitter.contract, event: "CreditClaimed", logs: logs, sub: sub}, nil +} + +// WatchCreditClaimed is a free log subscription operation binding the contract event 0xc4b5041307ee38be8c4f25df5d980a372ac8a9a73ea8f459891000a5386f5322. +// +// Solidity: event CreditClaimed(address indexed submitter, address indexed receiver, uint256 amount) +func (_Submitter *SubmitterFilterer) WatchCreditClaimed(opts *bind.WatchOpts, sink chan<- *SubmitterCreditClaimed, submitter []common.Address, receiver []common.Address) (event.Subscription, error) { + + var submitterRule []interface{} + for _, submitterItem := range submitter { + submitterRule = append(submitterRule, submitterItem) + } + var receiverRule []interface{} + for _, receiverItem := range receiver { + receiverRule = append(receiverRule, receiverItem) + } + + logs, sub, err := _Submitter.contract.WatchLogs(opts, "CreditClaimed", submitterRule, receiverRule) + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + // New log arrived, parse the event and forward to the user + event := new(SubmitterCreditClaimed) + if err := _Submitter.contract.UnpackLog(event, "CreditClaimed", log); err != nil { + return err + } + event.Raw = log + + select { + case sink <- event: + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + } + }), nil +} + +// ParseCreditClaimed is a log parse operation binding the contract event 0xc4b5041307ee38be8c4f25df5d980a372ac8a9a73ea8f459891000a5386f5322. +// +// Solidity: event CreditClaimed(address indexed submitter, address indexed receiver, uint256 amount) +func (_Submitter *SubmitterFilterer) ParseCreditClaimed(log types.Log) (*SubmitterCreditClaimed, error) { + event := new(SubmitterCreditClaimed) + if err := _Submitter.contract.UnpackLog(event, "CreditClaimed", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +// SubmitterInitializedIterator is returned from FilterInitialized and is used to iterate over the raw logs and unpacked data for Initialized events raised by the Submitter contract. +type SubmitterInitializedIterator struct { + Event *SubmitterInitialized // Event containing the contract specifics and raw log + + contract *bind.BoundContract // Generic contract to use for unpacking event data + event string // Event name to use for unpacking event data + + logs chan types.Log // Log channel receiving the found contract events + sub ethereum.Subscription // Subscription for errors, completion and termination + done bool // Whether the subscription completed delivering logs + fail error // Occurred error to stop iteration +} + +// Next advances the iterator to the subsequent event, returning whether there +// are any more events found. In case of a retrieval or parsing error, false is +// returned and Error() can be queried for the exact failure. +func (it *SubmitterInitializedIterator) Next() bool { + // If the iterator failed, stop iterating + if it.fail != nil { + return false + } + // If the iterator completed, deliver directly whatever's available + if it.done { + select { + case log := <-it.logs: + it.Event = new(SubmitterInitialized) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + default: + return false + } + } + // Iterator still in progress, wait for either a data or an error event + select { + case log := <-it.logs: + it.Event = new(SubmitterInitialized) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + case err := <-it.sub.Err(): + it.done = true + it.fail = err + return it.Next() + } +} + +// Error returns any retrieval or parsing error occurred during filtering. +func (it *SubmitterInitializedIterator) Error() error { + return it.fail +} + +// Close terminates the iteration process, releasing any pending underlying +// resources. +func (it *SubmitterInitializedIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +// SubmitterInitialized represents a Initialized event raised by the Submitter contract. +type SubmitterInitialized struct { + Version uint8 + Raw types.Log // Blockchain specific contextual infos +} + +// FilterInitialized is a free log retrieval operation binding the contract event 0x7f26b83ff96e1f2b6a682f133852f6798a09c465da95921460cefb3847402498. +// +// Solidity: event Initialized(uint8 version) +func (_Submitter *SubmitterFilterer) FilterInitialized(opts *bind.FilterOpts) (*SubmitterInitializedIterator, error) { + + logs, sub, err := _Submitter.contract.FilterLogs(opts, "Initialized") + if err != nil { + return nil, err + } + return &SubmitterInitializedIterator{contract: _Submitter.contract, event: "Initialized", logs: logs, sub: sub}, nil +} + +// WatchInitialized is a free log subscription operation binding the contract event 0x7f26b83ff96e1f2b6a682f133852f6798a09c465da95921460cefb3847402498. +// +// Solidity: event Initialized(uint8 version) +func (_Submitter *SubmitterFilterer) WatchInitialized(opts *bind.WatchOpts, sink chan<- *SubmitterInitialized) (event.Subscription, error) { + + logs, sub, err := _Submitter.contract.WatchLogs(opts, "Initialized") + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + // New log arrived, parse the event and forward to the user + event := new(SubmitterInitialized) + if err := _Submitter.contract.UnpackLog(event, "Initialized", log); err != nil { + return err + } + event.Raw = log + + select { + case sink <- event: + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + } + }), nil +} + +// ParseInitialized is a log parse operation binding the contract event 0x7f26b83ff96e1f2b6a682f133852f6798a09c465da95921460cefb3847402498. +// +// Solidity: event Initialized(uint8 version) +func (_Submitter *SubmitterFilterer) ParseInitialized(log types.Log) (*SubmitterInitialized, error) { + event := new(SubmitterInitialized) + if err := _Submitter.contract.UnpackLog(event, "Initialized", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +// SubmitterMinimumStakeUpdatedIterator is returned from FilterMinimumStakeUpdated and is used to iterate over the raw logs and unpacked data for MinimumStakeUpdated events raised by the Submitter contract. +type SubmitterMinimumStakeUpdatedIterator struct { + Event *SubmitterMinimumStakeUpdated // Event containing the contract specifics and raw log + + contract *bind.BoundContract // Generic contract to use for unpacking event data + event string // Event name to use for unpacking event data + + logs chan types.Log // Log channel receiving the found contract events + sub ethereum.Subscription // Subscription for errors, completion and termination + done bool // Whether the subscription completed delivering logs + fail error // Occurred error to stop iteration +} + +// Next advances the iterator to the subsequent event, returning whether there +// are any more events found. In case of a retrieval or parsing error, false is +// returned and Error() can be queried for the exact failure. +func (it *SubmitterMinimumStakeUpdatedIterator) Next() bool { + // If the iterator failed, stop iterating + if it.fail != nil { + return false + } + // If the iterator completed, deliver directly whatever's available + if it.done { + select { + case log := <-it.logs: + it.Event = new(SubmitterMinimumStakeUpdated) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + default: + return false + } + } + // Iterator still in progress, wait for either a data or an error event + select { + case log := <-it.logs: + it.Event = new(SubmitterMinimumStakeUpdated) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + case err := <-it.sub.Err(): + it.done = true + it.fail = err + return it.Next() + } +} + +// Error returns any retrieval or parsing error occurred during filtering. +func (it *SubmitterMinimumStakeUpdatedIterator) Error() error { + return it.fail +} + +// Close terminates the iteration process, releasing any pending underlying +// resources. +func (it *SubmitterMinimumStakeUpdatedIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +// SubmitterMinimumStakeUpdated represents a MinimumStakeUpdated event raised by the Submitter contract. +type SubmitterMinimumStakeUpdated struct { + OldValue *big.Int + NewValue *big.Int + Raw types.Log // Blockchain specific contextual infos +} + +// FilterMinimumStakeUpdated is a free log retrieval operation binding the contract event 0xd67ed534faf3e0dcda08ac6043ec96dcf9b6ebec56055fd14bd0cb40a27c0e49. +// +// Solidity: event MinimumStakeUpdated(uint256 oldValue, uint256 newValue) +func (_Submitter *SubmitterFilterer) FilterMinimumStakeUpdated(opts *bind.FilterOpts) (*SubmitterMinimumStakeUpdatedIterator, error) { + + logs, sub, err := _Submitter.contract.FilterLogs(opts, "MinimumStakeUpdated") + if err != nil { + return nil, err + } + return &SubmitterMinimumStakeUpdatedIterator{contract: _Submitter.contract, event: "MinimumStakeUpdated", logs: logs, sub: sub}, nil +} + +// WatchMinimumStakeUpdated is a free log subscription operation binding the contract event 0xd67ed534faf3e0dcda08ac6043ec96dcf9b6ebec56055fd14bd0cb40a27c0e49. +// +// Solidity: event MinimumStakeUpdated(uint256 oldValue, uint256 newValue) +func (_Submitter *SubmitterFilterer) WatchMinimumStakeUpdated(opts *bind.WatchOpts, sink chan<- *SubmitterMinimumStakeUpdated) (event.Subscription, error) { + + logs, sub, err := _Submitter.contract.WatchLogs(opts, "MinimumStakeUpdated") + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + // New log arrived, parse the event and forward to the user + event := new(SubmitterMinimumStakeUpdated) + if err := _Submitter.contract.UnpackLog(event, "MinimumStakeUpdated", log); err != nil { + return err + } + event.Raw = log + + select { + case sink <- event: + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + } + }), nil +} + +// ParseMinimumStakeUpdated is a log parse operation binding the contract event 0xd67ed534faf3e0dcda08ac6043ec96dcf9b6ebec56055fd14bd0cb40a27c0e49. +// +// Solidity: event MinimumStakeUpdated(uint256 oldValue, uint256 newValue) +func (_Submitter *SubmitterFilterer) ParseMinimumStakeUpdated(log types.Log) (*SubmitterMinimumStakeUpdated, error) { + event := new(SubmitterMinimumStakeUpdated) + if err := _Submitter.contract.UnpackLog(event, "MinimumStakeUpdated", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +// SubmitterOwnershipTransferredIterator is returned from FilterOwnershipTransferred and is used to iterate over the raw logs and unpacked data for OwnershipTransferred events raised by the Submitter contract. +type SubmitterOwnershipTransferredIterator struct { + Event *SubmitterOwnershipTransferred // Event containing the contract specifics and raw log + + contract *bind.BoundContract // Generic contract to use for unpacking event data + event string // Event name to use for unpacking event data + + logs chan types.Log // Log channel receiving the found contract events + sub ethereum.Subscription // Subscription for errors, completion and termination + done bool // Whether the subscription completed delivering logs + fail error // Occurred error to stop iteration +} + +// Next advances the iterator to the subsequent event, returning whether there +// are any more events found. In case of a retrieval or parsing error, false is +// returned and Error() can be queried for the exact failure. +func (it *SubmitterOwnershipTransferredIterator) Next() bool { + // If the iterator failed, stop iterating + if it.fail != nil { + return false + } + // If the iterator completed, deliver directly whatever's available + if it.done { + select { + case log := <-it.logs: + it.Event = new(SubmitterOwnershipTransferred) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + default: + return false + } + } + // Iterator still in progress, wait for either a data or an error event + select { + case log := <-it.logs: + it.Event = new(SubmitterOwnershipTransferred) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + case err := <-it.sub.Err(): + it.done = true + it.fail = err + return it.Next() + } +} + +// Error returns any retrieval or parsing error occurred during filtering. +func (it *SubmitterOwnershipTransferredIterator) Error() error { + return it.fail +} + +// Close terminates the iteration process, releasing any pending underlying +// resources. +func (it *SubmitterOwnershipTransferredIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +// SubmitterOwnershipTransferred represents a OwnershipTransferred event raised by the Submitter contract. +type SubmitterOwnershipTransferred struct { + PreviousOwner common.Address + NewOwner common.Address + Raw types.Log // Blockchain specific contextual infos +} + +// FilterOwnershipTransferred is a free log retrieval operation binding the contract event 0x8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0. +// +// Solidity: event OwnershipTransferred(address indexed previousOwner, address indexed newOwner) +func (_Submitter *SubmitterFilterer) FilterOwnershipTransferred(opts *bind.FilterOpts, previousOwner []common.Address, newOwner []common.Address) (*SubmitterOwnershipTransferredIterator, error) { + + var previousOwnerRule []interface{} + for _, previousOwnerItem := range previousOwner { + previousOwnerRule = append(previousOwnerRule, previousOwnerItem) + } + var newOwnerRule []interface{} + for _, newOwnerItem := range newOwner { + newOwnerRule = append(newOwnerRule, newOwnerItem) + } + + logs, sub, err := _Submitter.contract.FilterLogs(opts, "OwnershipTransferred", previousOwnerRule, newOwnerRule) + if err != nil { + return nil, err + } + return &SubmitterOwnershipTransferredIterator{contract: _Submitter.contract, event: "OwnershipTransferred", logs: logs, sub: sub}, nil +} + +// WatchOwnershipTransferred is a free log subscription operation binding the contract event 0x8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0. +// +// Solidity: event OwnershipTransferred(address indexed previousOwner, address indexed newOwner) +func (_Submitter *SubmitterFilterer) WatchOwnershipTransferred(opts *bind.WatchOpts, sink chan<- *SubmitterOwnershipTransferred, previousOwner []common.Address, newOwner []common.Address) (event.Subscription, error) { + + var previousOwnerRule []interface{} + for _, previousOwnerItem := range previousOwner { + previousOwnerRule = append(previousOwnerRule, previousOwnerItem) + } + var newOwnerRule []interface{} + for _, newOwnerItem := range newOwner { + newOwnerRule = append(newOwnerRule, newOwnerItem) + } + + logs, sub, err := _Submitter.contract.WatchLogs(opts, "OwnershipTransferred", previousOwnerRule, newOwnerRule) + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + // New log arrived, parse the event and forward to the user + event := new(SubmitterOwnershipTransferred) + if err := _Submitter.contract.UnpackLog(event, "OwnershipTransferred", log); err != nil { + return err + } + event.Raw = log + + select { + case sink <- event: + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + } + }), nil +} + +// ParseOwnershipTransferred is a log parse operation binding the contract event 0x8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0. +// +// Solidity: event OwnershipTransferred(address indexed previousOwner, address indexed newOwner) +func (_Submitter *SubmitterFilterer) ParseOwnershipTransferred(log types.Log) (*SubmitterOwnershipTransferred, error) { + event := new(SubmitterOwnershipTransferred) + if err := _Submitter.contract.UnpackLog(event, "OwnershipTransferred", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +// SubmitterRewardPercentageUpdatedIterator is returned from FilterRewardPercentageUpdated and is used to iterate over the raw logs and unpacked data for RewardPercentageUpdated events raised by the Submitter contract. +type SubmitterRewardPercentageUpdatedIterator struct { + Event *SubmitterRewardPercentageUpdated // Event containing the contract specifics and raw log + + contract *bind.BoundContract // Generic contract to use for unpacking event data + event string // Event name to use for unpacking event data + + logs chan types.Log // Log channel receiving the found contract events + sub ethereum.Subscription // Subscription for errors, completion and termination + done bool // Whether the subscription completed delivering logs + fail error // Occurred error to stop iteration +} + +// Next advances the iterator to the subsequent event, returning whether there +// are any more events found. In case of a retrieval or parsing error, false is +// returned and Error() can be queried for the exact failure. +func (it *SubmitterRewardPercentageUpdatedIterator) Next() bool { + // If the iterator failed, stop iterating + if it.fail != nil { + return false + } + // If the iterator completed, deliver directly whatever's available + if it.done { + select { + case log := <-it.logs: + it.Event = new(SubmitterRewardPercentageUpdated) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + default: + return false + } + } + // Iterator still in progress, wait for either a data or an error event + select { + case log := <-it.logs: + it.Event = new(SubmitterRewardPercentageUpdated) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + case err := <-it.sub.Err(): + it.done = true + it.fail = err + return it.Next() + } +} + +// Error returns any retrieval or parsing error occurred during filtering. +func (it *SubmitterRewardPercentageUpdatedIterator) Error() error { + return it.fail +} + +// Close terminates the iteration process, releasing any pending underlying +// resources. +func (it *SubmitterRewardPercentageUpdatedIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +// SubmitterRewardPercentageUpdated represents a RewardPercentageUpdated event raised by the Submitter contract. +type SubmitterRewardPercentageUpdated struct { + OldValue *big.Int + NewValue *big.Int + Raw types.Log // Blockchain specific contextual infos +} + +// FilterRewardPercentageUpdated is a free log retrieval operation binding the contract event 0xa46de936426e045703b2d34a292a19fde92b329018db8e0da750033876b655ba. +// +// Solidity: event RewardPercentageUpdated(uint256 oldValue, uint256 newValue) +func (_Submitter *SubmitterFilterer) FilterRewardPercentageUpdated(opts *bind.FilterOpts) (*SubmitterRewardPercentageUpdatedIterator, error) { + + logs, sub, err := _Submitter.contract.FilterLogs(opts, "RewardPercentageUpdated") + if err != nil { + return nil, err + } + return &SubmitterRewardPercentageUpdatedIterator{contract: _Submitter.contract, event: "RewardPercentageUpdated", logs: logs, sub: sub}, nil +} + +// WatchRewardPercentageUpdated is a free log subscription operation binding the contract event 0xa46de936426e045703b2d34a292a19fde92b329018db8e0da750033876b655ba. +// +// Solidity: event RewardPercentageUpdated(uint256 oldValue, uint256 newValue) +func (_Submitter *SubmitterFilterer) WatchRewardPercentageUpdated(opts *bind.WatchOpts, sink chan<- *SubmitterRewardPercentageUpdated) (event.Subscription, error) { + + logs, sub, err := _Submitter.contract.WatchLogs(opts, "RewardPercentageUpdated") + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + // New log arrived, parse the event and forward to the user + event := new(SubmitterRewardPercentageUpdated) + if err := _Submitter.contract.UnpackLog(event, "RewardPercentageUpdated", log); err != nil { + return err + } + event.Raw = log + + select { + case sink <- event: + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + } + }), nil +} + +// ParseRewardPercentageUpdated is a log parse operation binding the contract event 0xa46de936426e045703b2d34a292a19fde92b329018db8e0da750033876b655ba. +// +// Solidity: event RewardPercentageUpdated(uint256 oldValue, uint256 newValue) +func (_Submitter *SubmitterFilterer) ParseRewardPercentageUpdated(log types.Log) (*SubmitterRewardPercentageUpdated, error) { + event := new(SubmitterRewardPercentageUpdated) + if err := _Submitter.contract.UnpackLog(event, "RewardPercentageUpdated", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +// SubmitterSlashRemainingClaimedIterator is returned from FilterSlashRemainingClaimed and is used to iterate over the raw logs and unpacked data for SlashRemainingClaimed events raised by the Submitter contract. +type SubmitterSlashRemainingClaimedIterator struct { + Event *SubmitterSlashRemainingClaimed // Event containing the contract specifics and raw log + + contract *bind.BoundContract // Generic contract to use for unpacking event data + event string // Event name to use for unpacking event data + + logs chan types.Log // Log channel receiving the found contract events + sub ethereum.Subscription // Subscription for errors, completion and termination + done bool // Whether the subscription completed delivering logs + fail error // Occurred error to stop iteration +} + +// Next advances the iterator to the subsequent event, returning whether there +// are any more events found. In case of a retrieval or parsing error, false is +// returned and Error() can be queried for the exact failure. +func (it *SubmitterSlashRemainingClaimedIterator) Next() bool { + // If the iterator failed, stop iterating + if it.fail != nil { + return false + } + // If the iterator completed, deliver directly whatever's available + if it.done { + select { + case log := <-it.logs: + it.Event = new(SubmitterSlashRemainingClaimed) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + default: + return false + } + } + // Iterator still in progress, wait for either a data or an error event + select { + case log := <-it.logs: + it.Event = new(SubmitterSlashRemainingClaimed) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + case err := <-it.sub.Err(): + it.done = true + it.fail = err + return it.Next() + } +} + +// Error returns any retrieval or parsing error occurred during filtering. +func (it *SubmitterSlashRemainingClaimedIterator) Error() error { + return it.fail +} + +// Close terminates the iteration process, releasing any pending underlying +// resources. +func (it *SubmitterSlashRemainingClaimedIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +// SubmitterSlashRemainingClaimed represents a SlashRemainingClaimed event raised by the Submitter contract. +type SubmitterSlashRemainingClaimed struct { + Receiver common.Address + Amount *big.Int + Raw types.Log // Blockchain specific contextual infos +} + +// FilterSlashRemainingClaimed is a free log retrieval operation binding the contract event 0xa1fefb6c5328a92a416e321ed50997303fe7135fd88c28b0592b21ce42b5cdd9. +// +// Solidity: event SlashRemainingClaimed(address indexed receiver, uint256 amount) +func (_Submitter *SubmitterFilterer) FilterSlashRemainingClaimed(opts *bind.FilterOpts, receiver []common.Address) (*SubmitterSlashRemainingClaimedIterator, error) { + + var receiverRule []interface{} + for _, receiverItem := range receiver { + receiverRule = append(receiverRule, receiverItem) + } + + logs, sub, err := _Submitter.contract.FilterLogs(opts, "SlashRemainingClaimed", receiverRule) + if err != nil { + return nil, err + } + return &SubmitterSlashRemainingClaimedIterator{contract: _Submitter.contract, event: "SlashRemainingClaimed", logs: logs, sub: sub}, nil +} + +// WatchSlashRemainingClaimed is a free log subscription operation binding the contract event 0xa1fefb6c5328a92a416e321ed50997303fe7135fd88c28b0592b21ce42b5cdd9. +// +// Solidity: event SlashRemainingClaimed(address indexed receiver, uint256 amount) +func (_Submitter *SubmitterFilterer) WatchSlashRemainingClaimed(opts *bind.WatchOpts, sink chan<- *SubmitterSlashRemainingClaimed, receiver []common.Address) (event.Subscription, error) { + + var receiverRule []interface{} + for _, receiverItem := range receiver { + receiverRule = append(receiverRule, receiverItem) + } + + logs, sub, err := _Submitter.contract.WatchLogs(opts, "SlashRemainingClaimed", receiverRule) + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + // New log arrived, parse the event and forward to the user + event := new(SubmitterSlashRemainingClaimed) + if err := _Submitter.contract.UnpackLog(event, "SlashRemainingClaimed", log); err != nil { + return err + } + event.Raw = log + + select { + case sink <- event: + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + } + }), nil +} + +// ParseSlashRemainingClaimed is a log parse operation binding the contract event 0xa1fefb6c5328a92a416e321ed50997303fe7135fd88c28b0592b21ce42b5cdd9. +// +// Solidity: event SlashRemainingClaimed(address indexed receiver, uint256 amount) +func (_Submitter *SubmitterFilterer) ParseSlashRemainingClaimed(log types.Log) (*SubmitterSlashRemainingClaimed, error) { + event := new(SubmitterSlashRemainingClaimed) + if err := _Submitter.contract.UnpackLog(event, "SlashRemainingClaimed", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +// SubmitterSlashedIterator is returned from FilterSlashed and is used to iterate over the raw logs and unpacked data for Slashed events raised by the Submitter contract. +type SubmitterSlashedIterator struct { + Event *SubmitterSlashed // Event containing the contract specifics and raw log + + contract *bind.BoundContract // Generic contract to use for unpacking event data + event string // Event name to use for unpacking event data + + logs chan types.Log // Log channel receiving the found contract events + sub ethereum.Subscription // Subscription for errors, completion and termination + done bool // Whether the subscription completed delivering logs + fail error // Occurred error to stop iteration +} + +// Next advances the iterator to the subsequent event, returning whether there +// are any more events found. In case of a retrieval or parsing error, false is +// returned and Error() can be queried for the exact failure. +func (it *SubmitterSlashedIterator) Next() bool { + // If the iterator failed, stop iterating + if it.fail != nil { + return false + } + // If the iterator completed, deliver directly whatever's available + if it.done { + select { + case log := <-it.logs: + it.Event = new(SubmitterSlashed) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + default: + return false + } + } + // Iterator still in progress, wait for either a data or an error event + select { + case log := <-it.logs: + it.Event = new(SubmitterSlashed) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + case err := <-it.sub.Err(): + it.done = true + it.fail = err + return it.Next() + } +} + +// Error returns any retrieval or parsing error occurred during filtering. +func (it *SubmitterSlashedIterator) Error() error { + return it.fail +} + +// Close terminates the iteration process, releasing any pending underlying +// resources. +func (it *SubmitterSlashedIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +// SubmitterSlashed represents a Slashed event raised by the Submitter contract. +type SubmitterSlashed struct { + Submitter common.Address + Amount *big.Int + Reward *big.Int + Raw types.Log // Blockchain specific contextual infos +} + +// FilterSlashed is a free log retrieval operation binding the contract event 0x45a371af55b0726877a30f464edc14db5879ab096590bacce682cf6c18223596. +// +// Solidity: event Slashed(address indexed submitter, uint256 amount, uint256 reward) +func (_Submitter *SubmitterFilterer) FilterSlashed(opts *bind.FilterOpts, submitter []common.Address) (*SubmitterSlashedIterator, error) { + + var submitterRule []interface{} + for _, submitterItem := range submitter { + submitterRule = append(submitterRule, submitterItem) + } + + logs, sub, err := _Submitter.contract.FilterLogs(opts, "Slashed", submitterRule) + if err != nil { + return nil, err + } + return &SubmitterSlashedIterator{contract: _Submitter.contract, event: "Slashed", logs: logs, sub: sub}, nil +} + +// WatchSlashed is a free log subscription operation binding the contract event 0x45a371af55b0726877a30f464edc14db5879ab096590bacce682cf6c18223596. +// +// Solidity: event Slashed(address indexed submitter, uint256 amount, uint256 reward) +func (_Submitter *SubmitterFilterer) WatchSlashed(opts *bind.WatchOpts, sink chan<- *SubmitterSlashed, submitter []common.Address) (event.Subscription, error) { + + var submitterRule []interface{} + for _, submitterItem := range submitter { + submitterRule = append(submitterRule, submitterItem) + } + + logs, sub, err := _Submitter.contract.WatchLogs(opts, "Slashed", submitterRule) + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + // New log arrived, parse the event and forward to the user + event := new(SubmitterSlashed) + if err := _Submitter.contract.UnpackLog(event, "Slashed", log); err != nil { + return err + } + event.Raw = log + + select { + case sink <- event: + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + } + }), nil +} + +// ParseSlashed is a log parse operation binding the contract event 0x45a371af55b0726877a30f464edc14db5879ab096590bacce682cf6c18223596. +// +// Solidity: event Slashed(address indexed submitter, uint256 amount, uint256 reward) +func (_Submitter *SubmitterFilterer) ParseSlashed(log types.Log) (*SubmitterSlashed, error) { + event := new(SubmitterSlashed) + if err := _Submitter.contract.UnpackLog(event, "Slashed", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +// SubmitterStakedIterator is returned from FilterStaked and is used to iterate over the raw logs and unpacked data for Staked events raised by the Submitter contract. +type SubmitterStakedIterator struct { + Event *SubmitterStaked // Event containing the contract specifics and raw log + + contract *bind.BoundContract // Generic contract to use for unpacking event data + event string // Event name to use for unpacking event data + + logs chan types.Log // Log channel receiving the found contract events + sub ethereum.Subscription // Subscription for errors, completion and termination + done bool // Whether the subscription completed delivering logs + fail error // Occurred error to stop iteration +} + +// Next advances the iterator to the subsequent event, returning whether there +// are any more events found. In case of a retrieval or parsing error, false is +// returned and Error() can be queried for the exact failure. +func (it *SubmitterStakedIterator) Next() bool { + // If the iterator failed, stop iterating + if it.fail != nil { + return false + } + // If the iterator completed, deliver directly whatever's available + if it.done { + select { + case log := <-it.logs: + it.Event = new(SubmitterStaked) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + default: + return false + } + } + // Iterator still in progress, wait for either a data or an error event + select { + case log := <-it.logs: + it.Event = new(SubmitterStaked) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + case err := <-it.sub.Err(): + it.done = true + it.fail = err + return it.Next() + } +} + +// Error returns any retrieval or parsing error occurred during filtering. +func (it *SubmitterStakedIterator) Error() error { + return it.fail +} + +// Close terminates the iteration process, releasing any pending underlying +// resources. +func (it *SubmitterStakedIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +// SubmitterStaked represents a Staked event raised by the Submitter contract. +type SubmitterStaked struct { + Submitter common.Address + Amount *big.Int + Total *big.Int + Raw types.Log // Blockchain specific contextual infos +} + +// FilterStaked is a free log retrieval operation binding the contract event 0x1449c6dd7851abc30abf37f57715f492010519147cc2652fbc38202c18a6ee90. +// +// Solidity: event Staked(address indexed submitter, uint256 amount, uint256 total) +func (_Submitter *SubmitterFilterer) FilterStaked(opts *bind.FilterOpts, submitter []common.Address) (*SubmitterStakedIterator, error) { + + var submitterRule []interface{} + for _, submitterItem := range submitter { + submitterRule = append(submitterRule, submitterItem) + } + + logs, sub, err := _Submitter.contract.FilterLogs(opts, "Staked", submitterRule) + if err != nil { + return nil, err + } + return &SubmitterStakedIterator{contract: _Submitter.contract, event: "Staked", logs: logs, sub: sub}, nil +} + +// WatchStaked is a free log subscription operation binding the contract event 0x1449c6dd7851abc30abf37f57715f492010519147cc2652fbc38202c18a6ee90. +// +// Solidity: event Staked(address indexed submitter, uint256 amount, uint256 total) +func (_Submitter *SubmitterFilterer) WatchStaked(opts *bind.WatchOpts, sink chan<- *SubmitterStaked, submitter []common.Address) (event.Subscription, error) { + + var submitterRule []interface{} + for _, submitterItem := range submitter { + submitterRule = append(submitterRule, submitterItem) + } + + logs, sub, err := _Submitter.contract.WatchLogs(opts, "Staked", submitterRule) + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + // New log arrived, parse the event and forward to the user + event := new(SubmitterStaked) + if err := _Submitter.contract.UnpackLog(event, "Staked", log); err != nil { + return err + } + event.Raw = log + + select { + case sink <- event: + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + } + }), nil +} + +// ParseStaked is a log parse operation binding the contract event 0x1449c6dd7851abc30abf37f57715f492010519147cc2652fbc38202c18a6ee90. +// +// Solidity: event Staked(address indexed submitter, uint256 amount, uint256 total) +func (_Submitter *SubmitterFilterer) ParseStaked(log types.Log) (*SubmitterStaked, error) { + event := new(SubmitterStaked) + if err := _Submitter.contract.UnpackLog(event, "Staked", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +// SubmitterSubmitterAddedIterator is returned from FilterSubmitterAdded and is used to iterate over the raw logs and unpacked data for SubmitterAdded events raised by the Submitter contract. +type SubmitterSubmitterAddedIterator struct { + Event *SubmitterSubmitterAdded // Event containing the contract specifics and raw log + + contract *bind.BoundContract // Generic contract to use for unpacking event data + event string // Event name to use for unpacking event data + + logs chan types.Log // Log channel receiving the found contract events + sub ethereum.Subscription // Subscription for errors, completion and termination + done bool // Whether the subscription completed delivering logs + fail error // Occurred error to stop iteration +} + +// Next advances the iterator to the subsequent event, returning whether there +// are any more events found. In case of a retrieval or parsing error, false is +// returned and Error() can be queried for the exact failure. +func (it *SubmitterSubmitterAddedIterator) Next() bool { + // If the iterator failed, stop iterating + if it.fail != nil { + return false + } + // If the iterator completed, deliver directly whatever's available + if it.done { + select { + case log := <-it.logs: + it.Event = new(SubmitterSubmitterAdded) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + default: + return false + } + } + // Iterator still in progress, wait for either a data or an error event + select { + case log := <-it.logs: + it.Event = new(SubmitterSubmitterAdded) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + case err := <-it.sub.Err(): + it.done = true + it.fail = err + return it.Next() + } +} + +// Error returns any retrieval or parsing error occurred during filtering. +func (it *SubmitterSubmitterAddedIterator) Error() error { + return it.fail +} + +// Close terminates the iteration process, releasing any pending underlying +// resources. +func (it *SubmitterSubmitterAddedIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +// SubmitterSubmitterAdded represents a SubmitterAdded event raised by the Submitter contract. +type SubmitterSubmitterAdded struct { + Submitter common.Address + Raw types.Log // Blockchain specific contextual infos +} + +// FilterSubmitterAdded is a free log retrieval operation binding the contract event 0xb079bc2cbde1f186e0b351d4a87c4597e3ed098f571548617449e73506428d8b. +// +// Solidity: event SubmitterAdded(address indexed submitter) +func (_Submitter *SubmitterFilterer) FilterSubmitterAdded(opts *bind.FilterOpts, submitter []common.Address) (*SubmitterSubmitterAddedIterator, error) { + + var submitterRule []interface{} + for _, submitterItem := range submitter { + submitterRule = append(submitterRule, submitterItem) + } + + logs, sub, err := _Submitter.contract.FilterLogs(opts, "SubmitterAdded", submitterRule) + if err != nil { + return nil, err + } + return &SubmitterSubmitterAddedIterator{contract: _Submitter.contract, event: "SubmitterAdded", logs: logs, sub: sub}, nil +} + +// WatchSubmitterAdded is a free log subscription operation binding the contract event 0xb079bc2cbde1f186e0b351d4a87c4597e3ed098f571548617449e73506428d8b. +// +// Solidity: event SubmitterAdded(address indexed submitter) +func (_Submitter *SubmitterFilterer) WatchSubmitterAdded(opts *bind.WatchOpts, sink chan<- *SubmitterSubmitterAdded, submitter []common.Address) (event.Subscription, error) { + + var submitterRule []interface{} + for _, submitterItem := range submitter { + submitterRule = append(submitterRule, submitterItem) + } + + logs, sub, err := _Submitter.contract.WatchLogs(opts, "SubmitterAdded", submitterRule) + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + // New log arrived, parse the event and forward to the user + event := new(SubmitterSubmitterAdded) + if err := _Submitter.contract.UnpackLog(event, "SubmitterAdded", log); err != nil { + return err + } + event.Raw = log + + select { + case sink <- event: + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + } + }), nil +} + +// ParseSubmitterAdded is a log parse operation binding the contract event 0xb079bc2cbde1f186e0b351d4a87c4597e3ed098f571548617449e73506428d8b. +// +// Solidity: event SubmitterAdded(address indexed submitter) +func (_Submitter *SubmitterFilterer) ParseSubmitterAdded(log types.Log) (*SubmitterSubmitterAdded, error) { + event := new(SubmitterSubmitterAdded) + if err := _Submitter.contract.UnpackLog(event, "SubmitterAdded", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +// SubmitterSubmitterRemovedIterator is returned from FilterSubmitterRemoved and is used to iterate over the raw logs and unpacked data for SubmitterRemoved events raised by the Submitter contract. +type SubmitterSubmitterRemovedIterator struct { + Event *SubmitterSubmitterRemoved // Event containing the contract specifics and raw log + + contract *bind.BoundContract // Generic contract to use for unpacking event data + event string // Event name to use for unpacking event data + + logs chan types.Log // Log channel receiving the found contract events + sub ethereum.Subscription // Subscription for errors, completion and termination + done bool // Whether the subscription completed delivering logs + fail error // Occurred error to stop iteration +} + +// Next advances the iterator to the subsequent event, returning whether there +// are any more events found. In case of a retrieval or parsing error, false is +// returned and Error() can be queried for the exact failure. +func (it *SubmitterSubmitterRemovedIterator) Next() bool { + // If the iterator failed, stop iterating + if it.fail != nil { + return false + } + // If the iterator completed, deliver directly whatever's available + if it.done { + select { + case log := <-it.logs: + it.Event = new(SubmitterSubmitterRemoved) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + default: + return false + } + } + // Iterator still in progress, wait for either a data or an error event + select { + case log := <-it.logs: + it.Event = new(SubmitterSubmitterRemoved) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + case err := <-it.sub.Err(): + it.done = true + it.fail = err + return it.Next() + } +} + +// Error returns any retrieval or parsing error occurred during filtering. +func (it *SubmitterSubmitterRemovedIterator) Error() error { + return it.fail +} + +// Close terminates the iteration process, releasing any pending underlying +// resources. +func (it *SubmitterSubmitterRemovedIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +// SubmitterSubmitterRemoved represents a SubmitterRemoved event raised by the Submitter contract. +type SubmitterSubmitterRemoved struct { + Submitter common.Address + Raw types.Log // Blockchain specific contextual infos +} + +// FilterSubmitterRemoved is a free log retrieval operation binding the contract event 0xf84a004e1673d2f349a7c93c72b3794b8eba6d2f9338044d8c8cd260e51a57a1. +// +// Solidity: event SubmitterRemoved(address indexed submitter) +func (_Submitter *SubmitterFilterer) FilterSubmitterRemoved(opts *bind.FilterOpts, submitter []common.Address) (*SubmitterSubmitterRemovedIterator, error) { + + var submitterRule []interface{} + for _, submitterItem := range submitter { + submitterRule = append(submitterRule, submitterItem) + } + + logs, sub, err := _Submitter.contract.FilterLogs(opts, "SubmitterRemoved", submitterRule) + if err != nil { + return nil, err + } + return &SubmitterSubmitterRemovedIterator{contract: _Submitter.contract, event: "SubmitterRemoved", logs: logs, sub: sub}, nil +} + +// WatchSubmitterRemoved is a free log subscription operation binding the contract event 0xf84a004e1673d2f349a7c93c72b3794b8eba6d2f9338044d8c8cd260e51a57a1. +// +// Solidity: event SubmitterRemoved(address indexed submitter) +func (_Submitter *SubmitterFilterer) WatchSubmitterRemoved(opts *bind.WatchOpts, sink chan<- *SubmitterSubmitterRemoved, submitter []common.Address) (event.Subscription, error) { + + var submitterRule []interface{} + for _, submitterItem := range submitter { + submitterRule = append(submitterRule, submitterItem) + } + + logs, sub, err := _Submitter.contract.WatchLogs(opts, "SubmitterRemoved", submitterRule) + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + // New log arrived, parse the event and forward to the user + event := new(SubmitterSubmitterRemoved) + if err := _Submitter.contract.UnpackLog(event, "SubmitterRemoved", log); err != nil { + return err + } + event.Raw = log + + select { + case sink <- event: + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + } + }), nil +} + +// ParseSubmitterRemoved is a log parse operation binding the contract event 0xf84a004e1673d2f349a7c93c72b3794b8eba6d2f9338044d8c8cd260e51a57a1. +// +// Solidity: event SubmitterRemoved(address indexed submitter) +func (_Submitter *SubmitterFilterer) ParseSubmitterRemoved(log types.Log) (*SubmitterSubmitterRemoved, error) { + event := new(SubmitterSubmitterRemoved) + if err := _Submitter.contract.UnpackLog(event, "SubmitterRemoved", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +// SubmitterWithdrawalCreditedIterator is returned from FilterWithdrawalCredited and is used to iterate over the raw logs and unpacked data for WithdrawalCredited events raised by the Submitter contract. +type SubmitterWithdrawalCreditedIterator struct { + Event *SubmitterWithdrawalCredited // Event containing the contract specifics and raw log + + contract *bind.BoundContract // Generic contract to use for unpacking event data + event string // Event name to use for unpacking event data + + logs chan types.Log // Log channel receiving the found contract events + sub ethereum.Subscription // Subscription for errors, completion and termination + done bool // Whether the subscription completed delivering logs + fail error // Occurred error to stop iteration +} + +// Next advances the iterator to the subsequent event, returning whether there +// are any more events found. In case of a retrieval or parsing error, false is +// returned and Error() can be queried for the exact failure. +func (it *SubmitterWithdrawalCreditedIterator) Next() bool { + // If the iterator failed, stop iterating + if it.fail != nil { + return false + } + // If the iterator completed, deliver directly whatever's available + if it.done { + select { + case log := <-it.logs: + it.Event = new(SubmitterWithdrawalCredited) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + default: + return false + } + } + // Iterator still in progress, wait for either a data or an error event + select { + case log := <-it.logs: + it.Event = new(SubmitterWithdrawalCredited) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + case err := <-it.sub.Err(): + it.done = true + it.fail = err + return it.Next() + } +} + +// Error returns any retrieval or parsing error occurred during filtering. +func (it *SubmitterWithdrawalCreditedIterator) Error() error { + return it.fail +} + +// Close terminates the iteration process, releasing any pending underlying +// resources. +func (it *SubmitterWithdrawalCreditedIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +// SubmitterWithdrawalCredited represents a WithdrawalCredited event raised by the Submitter contract. +type SubmitterWithdrawalCredited struct { + Submitter common.Address + Amount *big.Int + TotalCredit *big.Int + Raw types.Log // Blockchain specific contextual infos +} + +// FilterWithdrawalCredited is a free log retrieval operation binding the contract event 0x335ec00177f14d116df2d9836dfb84e01159f61d1d9305a2130432fb47806ae4. +// +// Solidity: event WithdrawalCredited(address indexed submitter, uint256 amount, uint256 totalCredit) +func (_Submitter *SubmitterFilterer) FilterWithdrawalCredited(opts *bind.FilterOpts, submitter []common.Address) (*SubmitterWithdrawalCreditedIterator, error) { + + var submitterRule []interface{} + for _, submitterItem := range submitter { + submitterRule = append(submitterRule, submitterItem) + } + + logs, sub, err := _Submitter.contract.FilterLogs(opts, "WithdrawalCredited", submitterRule) + if err != nil { + return nil, err + } + return &SubmitterWithdrawalCreditedIterator{contract: _Submitter.contract, event: "WithdrawalCredited", logs: logs, sub: sub}, nil +} + +// WatchWithdrawalCredited is a free log subscription operation binding the contract event 0x335ec00177f14d116df2d9836dfb84e01159f61d1d9305a2130432fb47806ae4. +// +// Solidity: event WithdrawalCredited(address indexed submitter, uint256 amount, uint256 totalCredit) +func (_Submitter *SubmitterFilterer) WatchWithdrawalCredited(opts *bind.WatchOpts, sink chan<- *SubmitterWithdrawalCredited, submitter []common.Address) (event.Subscription, error) { + + var submitterRule []interface{} + for _, submitterItem := range submitter { + submitterRule = append(submitterRule, submitterItem) + } + + logs, sub, err := _Submitter.contract.WatchLogs(opts, "WithdrawalCredited", submitterRule) + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + // New log arrived, parse the event and forward to the user + event := new(SubmitterWithdrawalCredited) + if err := _Submitter.contract.UnpackLog(event, "WithdrawalCredited", log); err != nil { + return err + } + event.Raw = log + + select { + case sink <- event: + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + } + }), nil +} + +// ParseWithdrawalCredited is a log parse operation binding the contract event 0x335ec00177f14d116df2d9836dfb84e01159f61d1d9305a2130432fb47806ae4. +// +// Solidity: event WithdrawalCredited(address indexed submitter, uint256 amount, uint256 totalCredit) +func (_Submitter *SubmitterFilterer) ParseWithdrawalCredited(log types.Log) (*SubmitterWithdrawalCredited, error) { + event := new(SubmitterWithdrawalCredited) + if err := _Submitter.contract.UnpackLog(event, "WithdrawalCredited", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +// SubmitterWithdrawalPaidIterator is returned from FilterWithdrawalPaid and is used to iterate over the raw logs and unpacked data for WithdrawalPaid events raised by the Submitter contract. +type SubmitterWithdrawalPaidIterator struct { + Event *SubmitterWithdrawalPaid // Event containing the contract specifics and raw log + + contract *bind.BoundContract // Generic contract to use for unpacking event data + event string // Event name to use for unpacking event data + + logs chan types.Log // Log channel receiving the found contract events + sub ethereum.Subscription // Subscription for errors, completion and termination + done bool // Whether the subscription completed delivering logs + fail error // Occurred error to stop iteration +} + +// Next advances the iterator to the subsequent event, returning whether there +// are any more events found. In case of a retrieval or parsing error, false is +// returned and Error() can be queried for the exact failure. +func (it *SubmitterWithdrawalPaidIterator) Next() bool { + // If the iterator failed, stop iterating + if it.fail != nil { + return false + } + // If the iterator completed, deliver directly whatever's available + if it.done { + select { + case log := <-it.logs: + it.Event = new(SubmitterWithdrawalPaid) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + default: + return false + } + } + // Iterator still in progress, wait for either a data or an error event + select { + case log := <-it.logs: + it.Event = new(SubmitterWithdrawalPaid) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + case err := <-it.sub.Err(): + it.done = true + it.fail = err + return it.Next() + } +} + +// Error returns any retrieval or parsing error occurred during filtering. +func (it *SubmitterWithdrawalPaidIterator) Error() error { + return it.fail +} + +// Close terminates the iteration process, releasing any pending underlying +// resources. +func (it *SubmitterWithdrawalPaidIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +// SubmitterWithdrawalPaid represents a WithdrawalPaid event raised by the Submitter contract. +type SubmitterWithdrawalPaid struct { + Submitter common.Address + Receiver common.Address + Amount *big.Int + Raw types.Log // Blockchain specific contextual infos +} + +// FilterWithdrawalPaid is a free log retrieval operation binding the contract event 0xebb524abf6b48e81bfe3e2a2ba3d4050bc91b988d1cfaf677a91dc6652865f46. +// +// Solidity: event WithdrawalPaid(address indexed submitter, address indexed receiver, uint256 amount) +func (_Submitter *SubmitterFilterer) FilterWithdrawalPaid(opts *bind.FilterOpts, submitter []common.Address, receiver []common.Address) (*SubmitterWithdrawalPaidIterator, error) { + + var submitterRule []interface{} + for _, submitterItem := range submitter { + submitterRule = append(submitterRule, submitterItem) + } + var receiverRule []interface{} + for _, receiverItem := range receiver { + receiverRule = append(receiverRule, receiverItem) + } + + logs, sub, err := _Submitter.contract.FilterLogs(opts, "WithdrawalPaid", submitterRule, receiverRule) + if err != nil { + return nil, err + } + return &SubmitterWithdrawalPaidIterator{contract: _Submitter.contract, event: "WithdrawalPaid", logs: logs, sub: sub}, nil +} + +// WatchWithdrawalPaid is a free log subscription operation binding the contract event 0xebb524abf6b48e81bfe3e2a2ba3d4050bc91b988d1cfaf677a91dc6652865f46. +// +// Solidity: event WithdrawalPaid(address indexed submitter, address indexed receiver, uint256 amount) +func (_Submitter *SubmitterFilterer) WatchWithdrawalPaid(opts *bind.WatchOpts, sink chan<- *SubmitterWithdrawalPaid, submitter []common.Address, receiver []common.Address) (event.Subscription, error) { + + var submitterRule []interface{} + for _, submitterItem := range submitter { + submitterRule = append(submitterRule, submitterItem) + } + var receiverRule []interface{} + for _, receiverItem := range receiver { + receiverRule = append(receiverRule, receiverItem) + } + + logs, sub, err := _Submitter.contract.WatchLogs(opts, "WithdrawalPaid", submitterRule, receiverRule) + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + // New log arrived, parse the event and forward to the user + event := new(SubmitterWithdrawalPaid) + if err := _Submitter.contract.UnpackLog(event, "WithdrawalPaid", log); err != nil { + return err + } + event.Raw = log + + select { + case sink <- event: + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + } + }), nil +} + +// ParseWithdrawalPaid is a log parse operation binding the contract event 0xebb524abf6b48e81bfe3e2a2ba3d4050bc91b988d1cfaf677a91dc6652865f46. +// +// Solidity: event WithdrawalPaid(address indexed submitter, address indexed receiver, uint256 amount) +func (_Submitter *SubmitterFilterer) ParseWithdrawalPaid(log types.Log) (*SubmitterWithdrawalPaid, error) { + event := new(SubmitterWithdrawalPaid) + if err := _Submitter.contract.UnpackLog(event, "WithdrawalPaid", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +// SubmitterWithdrawalRequestedIterator is returned from FilterWithdrawalRequested and is used to iterate over the raw logs and unpacked data for WithdrawalRequested events raised by the Submitter contract. +type SubmitterWithdrawalRequestedIterator struct { + Event *SubmitterWithdrawalRequested // Event containing the contract specifics and raw log + + contract *bind.BoundContract // Generic contract to use for unpacking event data + event string // Event name to use for unpacking event data + + logs chan types.Log // Log channel receiving the found contract events + sub ethereum.Subscription // Subscription for errors, completion and termination + done bool // Whether the subscription completed delivering logs + fail error // Occurred error to stop iteration +} + +// Next advances the iterator to the subsequent event, returning whether there +// are any more events found. In case of a retrieval or parsing error, false is +// returned and Error() can be queried for the exact failure. +func (it *SubmitterWithdrawalRequestedIterator) Next() bool { + // If the iterator failed, stop iterating + if it.fail != nil { + return false + } + // If the iterator completed, deliver directly whatever's available + if it.done { + select { + case log := <-it.logs: + it.Event = new(SubmitterWithdrawalRequested) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + default: + return false + } + } + // Iterator still in progress, wait for either a data or an error event + select { + case log := <-it.logs: + it.Event = new(SubmitterWithdrawalRequested) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + case err := <-it.sub.Err(): + it.done = true + it.fail = err + return it.Next() + } +} + +// Error returns any retrieval or parsing error occurred during filtering. +func (it *SubmitterWithdrawalRequestedIterator) Error() error { + return it.fail +} + +// Close terminates the iteration process, releasing any pending underlying +// resources. +func (it *SubmitterWithdrawalRequestedIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +// SubmitterWithdrawalRequested represents a WithdrawalRequested event raised by the Submitter contract. +type SubmitterWithdrawalRequested struct { + Submitter common.Address + Receiver common.Address + Amount *big.Int + Raw types.Log // Blockchain specific contextual infos +} + +// FilterWithdrawalRequested is a free log retrieval operation binding the contract event 0x04c56a409d50971e45c5a2d96e5d557d2b0f1d66d40f14b141e4c958b0f39b32. +// +// Solidity: event WithdrawalRequested(address indexed submitter, address indexed receiver, uint256 amount) +func (_Submitter *SubmitterFilterer) FilterWithdrawalRequested(opts *bind.FilterOpts, submitter []common.Address, receiver []common.Address) (*SubmitterWithdrawalRequestedIterator, error) { + + var submitterRule []interface{} + for _, submitterItem := range submitter { + submitterRule = append(submitterRule, submitterItem) + } + var receiverRule []interface{} + for _, receiverItem := range receiver { + receiverRule = append(receiverRule, receiverItem) + } + + logs, sub, err := _Submitter.contract.FilterLogs(opts, "WithdrawalRequested", submitterRule, receiverRule) + if err != nil { + return nil, err + } + return &SubmitterWithdrawalRequestedIterator{contract: _Submitter.contract, event: "WithdrawalRequested", logs: logs, sub: sub}, nil +} + +// WatchWithdrawalRequested is a free log subscription operation binding the contract event 0x04c56a409d50971e45c5a2d96e5d557d2b0f1d66d40f14b141e4c958b0f39b32. +// +// Solidity: event WithdrawalRequested(address indexed submitter, address indexed receiver, uint256 amount) +func (_Submitter *SubmitterFilterer) WatchWithdrawalRequested(opts *bind.WatchOpts, sink chan<- *SubmitterWithdrawalRequested, submitter []common.Address, receiver []common.Address) (event.Subscription, error) { + + var submitterRule []interface{} + for _, submitterItem := range submitter { + submitterRule = append(submitterRule, submitterItem) + } + var receiverRule []interface{} + for _, receiverItem := range receiver { + receiverRule = append(receiverRule, receiverItem) + } + + logs, sub, err := _Submitter.contract.WatchLogs(opts, "WithdrawalRequested", submitterRule, receiverRule) + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + // New log arrived, parse the event and forward to the user + event := new(SubmitterWithdrawalRequested) + if err := _Submitter.contract.UnpackLog(event, "WithdrawalRequested", log); err != nil { + return err + } + event.Raw = log + + select { + case sink <- event: + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + } + }), nil +} + +// ParseWithdrawalRequested is a log parse operation binding the contract event 0x04c56a409d50971e45c5a2d96e5d557d2b0f1d66d40f14b141e4c958b0f39b32. +// +// Solidity: event WithdrawalRequested(address indexed submitter, address indexed receiver, uint256 amount) +func (_Submitter *SubmitterFilterer) ParseWithdrawalRequested(log types.Log) (*SubmitterWithdrawalRequested, error) { + event := new(SubmitterWithdrawalRequested) + if err := _Submitter.contract.UnpackLog(event, "WithdrawalRequested", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} diff --git a/bindings/bindings/submitter_more.go b/bindings/bindings/submitter_more.go new file mode 100644 index 000000000..1adc44989 --- /dev/null +++ b/bindings/bindings/submitter_more.go @@ -0,0 +1,25 @@ +// Code generated - DO NOT EDIT. +// This file is a generated binding and any manual changes will be lost. + +package bindings + +import ( + "encoding/json" + + "morph-l2/bindings/solc" +) + +const SubmitterStorageLayoutJSON = "{\"storage\":[{\"astId\":1000,\"contract\":\"contracts/l1/rollup/Submitter.sol:Submitter\",\"label\":\"_initialized\",\"offset\":0,\"slot\":\"0\",\"type\":\"t_uint8\"},{\"astId\":1001,\"contract\":\"contracts/l1/rollup/Submitter.sol:Submitter\",\"label\":\"_initializing\",\"offset\":1,\"slot\":\"0\",\"type\":\"t_bool\"},{\"astId\":1002,\"contract\":\"contracts/l1/rollup/Submitter.sol:Submitter\",\"label\":\"__gap\",\"offset\":0,\"slot\":\"1\",\"type\":\"t_array(t_uint256)1018_storage\"},{\"astId\":1003,\"contract\":\"contracts/l1/rollup/Submitter.sol:Submitter\",\"label\":\"_owner\",\"offset\":0,\"slot\":\"51\",\"type\":\"t_address\"},{\"astId\":1004,\"contract\":\"contracts/l1/rollup/Submitter.sol:Submitter\",\"label\":\"__gap\",\"offset\":0,\"slot\":\"52\",\"type\":\"t_array(t_uint256)1017_storage\"},{\"astId\":1005,\"contract\":\"contracts/l1/rollup/Submitter.sol:Submitter\",\"label\":\"_status\",\"offset\":0,\"slot\":\"101\",\"type\":\"t_uint256\"},{\"astId\":1006,\"contract\":\"contracts/l1/rollup/Submitter.sol:Submitter\",\"label\":\"__gap\",\"offset\":0,\"slot\":\"102\",\"type\":\"t_array(t_uint256)1017_storage\"},{\"astId\":1007,\"contract\":\"contracts/l1/rollup/Submitter.sol:Submitter\",\"label\":\"rollupContract\",\"offset\":0,\"slot\":\"151\",\"type\":\"t_address\"},{\"astId\":1008,\"contract\":\"contracts/l1/rollup/Submitter.sol:Submitter\",\"label\":\"minimumStake\",\"offset\":0,\"slot\":\"152\",\"type\":\"t_uint256\"},{\"astId\":1009,\"contract\":\"contracts/l1/rollup/Submitter.sol:Submitter\",\"label\":\"challengeDeposit\",\"offset\":0,\"slot\":\"153\",\"type\":\"t_uint256\"},{\"astId\":1010,\"contract\":\"contracts/l1/rollup/Submitter.sol:Submitter\",\"label\":\"rewardPercentage\",\"offset\":0,\"slot\":\"154\",\"type\":\"t_uint256\"},{\"astId\":1011,\"contract\":\"contracts/l1/rollup/Submitter.sol:Submitter\",\"label\":\"slashRemaining\",\"offset\":0,\"slot\":\"155\",\"type\":\"t_uint256\"},{\"astId\":1012,\"contract\":\"contracts/l1/rollup/Submitter.sol:Submitter\",\"label\":\"registered\",\"offset\":0,\"slot\":\"156\",\"type\":\"t_mapping(t_address,t_bool)\"},{\"astId\":1013,\"contract\":\"contracts/l1/rollup/Submitter.sol:Submitter\",\"label\":\"stakeOf\",\"offset\":0,\"slot\":\"157\",\"type\":\"t_mapping(t_address,t_uint256)\"},{\"astId\":1014,\"contract\":\"contracts/l1/rollup/Submitter.sol:Submitter\",\"label\":\"exiting\",\"offset\":0,\"slot\":\"158\",\"type\":\"t_mapping(t_address,t_bool)\"},{\"astId\":1015,\"contract\":\"contracts/l1/rollup/Submitter.sol:Submitter\",\"label\":\"claimable\",\"offset\":0,\"slot\":\"159\",\"type\":\"t_mapping(t_address,t_uint256)\"},{\"astId\":1016,\"contract\":\"contracts/l1/rollup/Submitter.sol:Submitter\",\"label\":\"__gap\",\"offset\":0,\"slot\":\"160\",\"type\":\"t_array(t_uint256)1018_storage\"}],\"types\":{\"t_address\":{\"encoding\":\"inplace\",\"label\":\"address\",\"numberOfBytes\":\"20\"},\"t_array(t_uint256)1017_storage\":{\"encoding\":\"inplace\",\"label\":\"uint256[49]\",\"numberOfBytes\":\"1568\"},\"t_array(t_uint256)1018_storage\":{\"encoding\":\"inplace\",\"label\":\"uint256[50]\",\"numberOfBytes\":\"1600\"},\"t_bool\":{\"encoding\":\"inplace\",\"label\":\"bool\",\"numberOfBytes\":\"1\"},\"t_mapping(t_address,t_bool)\":{\"encoding\":\"mapping\",\"label\":\"mapping(address =\u003e bool)\",\"numberOfBytes\":\"32\",\"key\":\"t_address\",\"value\":\"t_bool\"},\"t_mapping(t_address,t_uint256)\":{\"encoding\":\"mapping\",\"label\":\"mapping(address =\u003e uint256)\",\"numberOfBytes\":\"32\",\"key\":\"t_address\",\"value\":\"t_uint256\"},\"t_uint256\":{\"encoding\":\"inplace\",\"label\":\"uint256\",\"numberOfBytes\":\"32\"},\"t_uint8\":{\"encoding\":\"inplace\",\"label\":\"uint8\",\"numberOfBytes\":\"1\"}}}" + +var SubmitterStorageLayout = new(solc.StorageLayout) + +var SubmitterDeployedBin = "0x60806040526004361061018e575f3560e01c80639f8a13d7116100dc578063bfa02ba911610087578063cde4cd1111610062578063cde4cd1114610471578063d13f90b414610490578063ec5ffac2146104af578063f2fde38b146104c4575f80fd5b8063bfa02ba914610407578063c36f41e214610433578063c96be4cb14610452575f80fd5b8063ab8c53dc116100b7578063ab8c53dc14610396578063b2dd5c07146103ab578063bbfefd38146103d9575f80fd5b80639f8a13d714610329578063a3066aab14610358578063a4f209b014610377575f80fd5b8063426233601161013c57806360e274641161011757806360e27464146102ab578063715018a6146102ca5780638da5cb5b146102de575f80fd5b8063426233601461024c57806351cff8d91461027757806352d472eb14610296575f80fd5b8063359289911161016c57806335928991146101fa5780633a4b66f114610219578063402914f514610221575f80fd5b8063072900f9146101925780630d13fd7b146101b3578063233e9903146101db575b5f80fd5b34801561019d575f80fd5b506101b16101ac36600461237c565b6104e3565b005b3480156101be575f80fd5b506101c860995481565b6040519081526020015b60405180910390f35b3480156101e6575f80fd5b506101b16101f536600461239c565b610647565b348015610205575f80fd5b506101b161021436600461239c565b610707565b6101b16107c7565b34801561022c575f80fd5b506101c861023b36600461237c565b609f6020525f908152604090205481565b348015610257575f80fd5b506101c861026636600461237c565b609d6020525f908152604090205481565b348015610282575f80fd5b506101b161029136600461237c565b610990565b3480156102a1575f80fd5b506101c8609a5481565b3480156102b6575f80fd5b506101b16102c536600461237c565b610bbb565b3480156102d5575f80fd5b506101b1610d6f565b3480156102e9575f80fd5b5060335473ffffffffffffffffffffffffffffffffffffffff165b60405173ffffffffffffffffffffffffffffffffffffffff90911681526020016101d2565b348015610334575f80fd5b5061034861034336600461237c565b610d80565b60405190151581526020016101d2565b348015610363575f80fd5b506101b161037236600461237c565b610e11565b348015610382575f80fd5b506101b161039136600461239c565b610f9e565b3480156103a1575f80fd5b506101c8609b5481565b3480156103b6575f80fd5b506103486103c536600461237c565b609c6020525f908152604090205460ff1681565b3480156103e4575f80fd5b506103486103f336600461237c565b609e6020525f908152604090205460ff1681565b348015610412575f80fd5b506097546103049073ffffffffffffffffffffffffffffffffffffffff1681565b34801561043e575f80fd5b506101b161044d36600461237c565b61106b565b34801561045d575f80fd5b506101c861046c36600461237c565b611237565b34801561047c575f80fd5b506101b161048b36600461237c565b611429565b34801561049b575f80fd5b506101b16104aa3660046123b3565b611589565b3480156104ba575f80fd5b506101c860985481565b3480156104cf575f80fd5b506101b16104de36600461237c565b611a8b565b6104eb611b3f565b73ffffffffffffffffffffffffffffffffffffffff811615801590610535575073ffffffffffffffffffffffffffffffffffffffff81165f908152609c602052604090205460ff16155b8015610566575073ffffffffffffffffffffffffffffffffffffffff81165f908152609e602052604090205460ff16155b6105d1576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601160248201527f696e76616c6964207375626d697474657200000000000000000000000000000060448201526064015b60405180910390fd5b73ffffffffffffffffffffffffffffffffffffffff81165f818152609c602052604080822080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00166001179055517fb079bc2cbde1f186e0b351d4a87c4597e3ed098f571548617449e73506428d8b9190a250565b61064f611b3f565b5f8111801561066057506098548114155b6106c6576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601560248201527f696e76616c6964206d696e696d756d207374616b65000000000000000000000060448201526064016105c8565b60985460408051918252602082018390527fd67ed534faf3e0dcda08ac6043ec96dcf9b6ebec56055fd14bd0cb40a27c0e49910160405180910390a1609855565b61070f611b3f565b5f8111801561072057506099548114155b610786576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601960248201527f696e76616c6964206368616c6c656e6765206465706f7369740000000000000060448201526064016105c8565b60995460408051918252602082018390527f36f971a40478225aeb80cfbf5e80306e8cb76d3bf7d56fdc5e490945cddb7d55910160405180910390a1609955565b6107cf611bc0565b335f818152609c602052604090205460ff168015610812575073ffffffffffffffffffffffffffffffffffffffff81165f908152609e602052604090205460ff16155b610878576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600d60248201527f6e6f74207374616b6561626c650000000000000000000000000000000000000060448201526064016105c8565b73ffffffffffffffffffffffffffffffffffffffff81165f908152609d60205260408120546108a8903490612429565b90505f341180156108bb57506098548110155b610921576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601360248201527f62656c6f77206d696e696d756d207374616b650000000000000000000000000060448201526064016105c8565b73ffffffffffffffffffffffffffffffffffffffff82165f818152609d602090815260409182902084905581513481529081018490527f1449c6dd7851abc30abf37f57715f492010519147cc2652fbc38202c18a6ee90910160405180910390a2505061098e6001606555565b565b610998611bc0565b3373ffffffffffffffffffffffffffffffffffffffff8216610a16576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601060248201527f696e76616c69642072656365697665720000000000000000000000000000000060448201526064016105c8565b73ffffffffffffffffffffffffffffffffffffffff81165f908152609c602052604090205460ff168015610a6f575073ffffffffffffffffffffffffffffffffffffffff81165f908152609e602052604090205460ff16155b8015610a9e575073ffffffffffffffffffffffffffffffffffffffff81165f908152609d602052604090205415155b610b04576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601060248201527f6e6f7420776974686472617761626c650000000000000000000000000000000060448201526064016105c8565b73ffffffffffffffffffffffffffffffffffffffff8181165f818152609c6020908152604080832080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00908116909155609e835281842080549091166001179055609d825291829020549151918252928516927f04c56a409d50971e45c5a2d96e5d557d2b0f1d66d40f14b141e4c958b0f39b32910160405180910390a3610bad8183611c3a565b50610bb86001606555565b50565b610bc3611bc0565b3373ffffffffffffffffffffffffffffffffffffffff8216610c41576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601060248201527f696e76616c69642072656365697665720000000000000000000000000000000060448201526064016105c8565b73ffffffffffffffffffffffffffffffffffffffff81165f908152609f602052604090205480610ccd576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600960248201527f6e6f20637265646974000000000000000000000000000000000000000000000060448201526064016105c8565b73ffffffffffffffffffffffffffffffffffffffff82165f908152609f6020526040812055610cfc8382611c56565b8273ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff167fc4b5041307ee38be8c4f25df5d980a372ac8a9a73ea8f459891000a5386f532283604051610d5b91815260200190565b60405180910390a35050610bb86001606555565b610d77611b3f565b61098e5f611d2c565b73ffffffffffffffffffffffffffffffffffffffff81165f908152609c602052604081205460ff168015610dd9575073ffffffffffffffffffffffffffffffffffffffff82165f908152609e602052604090205460ff16155b8015610e0b575060985473ffffffffffffffffffffffffffffffffffffffff83165f908152609d602052604090205410155b92915050565b610e19611bc0565b3373ffffffffffffffffffffffffffffffffffffffff8216610e97576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601060248201527f696e76616c69642072656365697665720000000000000000000000000000000060448201526064016105c8565b73ffffffffffffffffffffffffffffffffffffffff81165f908152609e602052604090205460ff16610f25576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600b60248201527f6e6f742065786974696e6700000000000000000000000000000000000000000060448201526064016105c8565b610f2e81611da2565b610f94576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601760248201527f70656e64696e67206261746368206c696162696c69747900000000000000000060448201526064016105c8565b610bad8183611ea7565b610fa6611b3f565b5f81118015610fb6575060648111155b8015610fc45750609a548114155b61102a576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601960248201527f696e76616c6964207265776172642070657263656e746167650000000000000060448201526064016105c8565b609a5460408051918252602082018390527fa46de936426e045703b2d34a292a19fde92b329018db8e0da750033876b655ba910160405180910390a1609a55565b611073611b3f565b61107b611bc0565b73ffffffffffffffffffffffffffffffffffffffff81165f908152609c602052604090205460ff1680156110d4575073ffffffffffffffffffffffffffffffffffffffff81165f908152609e602052604090205460ff16155b61113a576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601160248201527f696e76616c6964207375626d697474657200000000000000000000000000000060448201526064016105c8565b73ffffffffffffffffffffffffffffffffffffffff81165f818152609c6020908152604080832080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00908116909155609e9092528083208054909216600117909155517ff84a004e1673d2f349a7c93c72b3794b8eba6d2f9338044d8c8cd260e51a57a19190a273ffffffffffffffffffffffffffffffffffffffff81165f818152609d60205260409081902054905182917f04c56a409d50971e45c5a2d96e5d557d2b0f1d66d40f14b141e4c958b0f39b329161121b91815260200190565b60405180910390a361122d8182611c3a565b610bb86001606555565b6097545f9073ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16146112d0576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601460248201527f6f6e6c7920726f6c6c757020636f6e747261637400000000000000000000000060448201526064016105c8565b6112d8611bc0565b73ffffffffffffffffffffffffffffffffffffffff82166112fa57505f61141a565b73ffffffffffffffffffffffffffffffffffffffff82165f908152609d602090815260408083208054609c845282852080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0090811690915591859055609e90935292208054909216600117909155609a54606490611379908361243c565b6113839190612453565b915061138f828261248b565b609b5f82825461139f9190612429565b90915550506097546113c79073ffffffffffffffffffffffffffffffffffffffff1683611c56565b604080518281526020810184905273ffffffffffffffffffffffffffffffffffffffff8516917f45a371af55b0726877a30f464edc14db5879ab096590bacce682cf6c18223596910160405180910390a2505b6114246001606555565b919050565b611431611b3f565b611439611bc0565b73ffffffffffffffffffffffffffffffffffffffff81166114b6576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601060248201527f696e76616c69642072656365697665720000000000000000000000000000000060448201526064016105c8565b609b5480611520576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601260248201527f6e6f20736c6173682072656d61696e696e67000000000000000000000000000060448201526064016105c8565b5f609b5561152e8282611c56565b8173ffffffffffffffffffffffffffffffffffffffff167fa1fefb6c5328a92a416e321ed50997303fe7135fd88c28b0592b21ce42b5cdd98260405161157691815260200190565b60405180910390a250610bb86001606555565b5f54610100900460ff16158080156115a757505f54600160ff909116105b806115c05750303b1580156115c057505f5460ff166001145b61164c576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602e60248201527f496e697469616c697a61626c653a20636f6e747261637420697320616c72656160448201527f647920696e697469616c697a656400000000000000000000000000000000000060648201526084016105c8565b5f80547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0016600117905580156116a8575f80547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ff166101001790555b73ffffffffffffffffffffffffffffffffffffffff8616611725576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600d60248201527f696e76616c6964206f776e65720000000000000000000000000000000000000060448201526064016105c8565b73ffffffffffffffffffffffffffffffffffffffff85161580159061176057505f8573ffffffffffffffffffffffffffffffffffffffff163b115b6117c6576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601760248201527f696e76616c696420726f6c6c757020636f6e747261637400000000000000000060448201526064016105c8565b5f841161182f576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601560248201527f696e76616c6964206d696e696d756d207374616b65000000000000000000000060448201526064016105c8565b5f8311611898576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601960248201527f696e76616c6964206368616c6c656e6765206465706f7369740000000000000060448201526064016105c8565b5f821180156118a8575060648211155b61190e576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601960248201527f696e76616c6964207265776172642070657263656e746167650000000000000060448201526064016105c8565b6119166120e8565b61191e612186565b61192786611d2c565b609780547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff871617905560988490556099839055609a829055604080515f8152602081018690527fd67ed534faf3e0dcda08ac6043ec96dcf9b6ebec56055fd14bd0cb40a27c0e49910160405180910390a1604080515f8152602081018590527f36f971a40478225aeb80cfbf5e80306e8cb76d3bf7d56fdc5e490945cddb7d55910160405180910390a1604080515f8152602081018490527fa46de936426e045703b2d34a292a19fde92b329018db8e0da750033876b655ba910160405180910390a18015611a83575f80547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ff169055604051600181527f7f26b83ff96e1f2b6a682f133852f6798a09c465da95921460cefb38474024989060200160405180910390a15b505050505050565b611a93611b3f565b73ffffffffffffffffffffffffffffffffffffffff8116611b36576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602660248201527f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160448201527f646472657373000000000000000000000000000000000000000000000000000060648201526084016105c8565b610bb881611d2c565b60335473ffffffffffffffffffffffffffffffffffffffff16331461098e576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e657260448201526064016105c8565b600260655403611c2c576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c0060448201526064016105c8565b6002606555565b6001606555565b611c4382611da2565b15611c5257611c528282611ea7565b5050565b805f03611c61575050565b5f8273ffffffffffffffffffffffffffffffffffffffff16826040515f6040518083038185875af1925050503d805f8114611cb7576040519150601f19603f3d011682016040523d82523d5f602084013e611cbc565b606091505b5050905080611d27576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601360248201527f455448207472616e73666572206661696c65640000000000000000000000000060448201526064016105c8565b505050565b6033805473ffffffffffffffffffffffffffffffffffffffff8381167fffffffffffffffffffffffff0000000000000000000000000000000000000000831681179093556040519116919082907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0905f90a35050565b60405173ffffffffffffffffffffffffffffffffffffffff821660248201525f908190604401604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529190526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167f8ef622a800000000000000000000000000000000000000000000000000000000178152609754825192935073ffffffffffffffffffffffffffffffffffffffff16915f9182918291829182918761c350fa92503d915060208214831615611e855760205f803e505f515b828015611e925750816020145b8015611e9c575080155b979650505050505050565b73ffffffffffffffffffffffffffffffffffffffff82165f908152609d60209081526040808320805490849055609e909252822080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0016905590819003611f75578173ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff167febb524abf6b48e81bfe3e2a2ba3d4050bc91b988d1cfaf677a91dc6652865f465f604051611f6891815260200190565b60405180910390a3505050565b5f8273ffffffffffffffffffffffffffffffffffffffff168261c350906040515f60405180830381858888f193505050503d805f8114611fd0576040519150601f19603f3d011682016040523d82523d5f602084013e611fd5565b606091505b50509050801561204b578273ffffffffffffffffffffffffffffffffffffffff168473ffffffffffffffffffffffffffffffffffffffff167febb524abf6b48e81bfe3e2a2ba3d4050bc91b988d1cfaf677a91dc6652865f468460405161203e91815260200190565b60405180910390a36120e2565b73ffffffffffffffffffffffffffffffffffffffff84165f908152609f60205260408120805484929061207f908490612429565b909155505073ffffffffffffffffffffffffffffffffffffffff84165f818152609f6020908152604091829020548251868152918201527f335ec00177f14d116df2d9836dfb84e01159f61d1d9305a2130432fb47806ae4910160405180910390a25b50505050565b5f54610100900460ff1661217e576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602b60248201527f496e697469616c697a61626c653a20636f6e7472616374206973206e6f74206960448201527f6e697469616c697a696e6700000000000000000000000000000000000000000060648201526084016105c8565b61098e612224565b5f54610100900460ff1661221c576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602b60248201527f496e697469616c697a61626c653a20636f6e7472616374206973206e6f74206960448201527f6e697469616c697a696e6700000000000000000000000000000000000000000060648201526084016105c8565b61098e6122c3565b5f54610100900460ff166122ba576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602b60248201527f496e697469616c697a61626c653a20636f6e7472616374206973206e6f74206960448201527f6e697469616c697a696e6700000000000000000000000000000000000000000060648201526084016105c8565b61098e33611d2c565b5f54610100900460ff16611c33576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602b60248201527f496e697469616c697a61626c653a20636f6e7472616374206973206e6f74206960448201527f6e697469616c697a696e6700000000000000000000000000000000000000000060648201526084016105c8565b803573ffffffffffffffffffffffffffffffffffffffff81168114611424575f80fd5b5f6020828403121561238c575f80fd5b61239582612359565b9392505050565b5f602082840312156123ac575f80fd5b5035919050565b5f805f805f60a086880312156123c7575f80fd5b6123d086612359565b94506123de60208701612359565b94979496505050506040830135926060810135926080909101359150565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601160045260245ffd5b80820180821115610e0b57610e0b6123fc565b8082028115828204841417610e0b57610e0b6123fc565b5f82612486577f4e487b71000000000000000000000000000000000000000000000000000000005f52601260045260245ffd5b500490565b81810381811115610e0b57610e0b6123fc56fea164736f6c6343000818000a" + +func init() { + if err := json.Unmarshal([]byte(SubmitterStorageLayoutJSON), SubmitterStorageLayout); err != nil { + panic(err) + } + + layouts["Submitter"] = SubmitterStorageLayout + deployedBytecodes["Submitter"] = SubmitterDeployedBin +} diff --git a/contracts/deploy/013-DeployProxys.ts b/contracts/deploy/013-DeployProxys.ts index 6b3a4a5ce..35dd0a2ab 100644 --- a/contracts/deploy/013-DeployProxys.ts +++ b/contracts/deploy/013-DeployProxys.ts @@ -47,7 +47,7 @@ export const deployContractProxies = async ( ProxyStorageName.L1MessageQueueWithGasPriceOracleProxyStorageName const RollupProxyStorageName = ProxyStorageName.RollupProxyStorageName - const L1StakingProxyStorageName = ProxyStorageName.L1StakingProxyStorageName + const SubmitterProxyStorageName = ProxyStorageName.SubmitterProxyStorageName const L1SequencerProxyStorageName = ProxyStorageName.L1SequencerProxyStorageName const L1GatewayRouterProxyStorageName = ProxyStorageName.L1GatewayRouterProxyStorageName @@ -106,9 +106,9 @@ export const deployContractProxies = async ( return err } - // ************************ staking contracts deploy ************************ - // StakingProxy deploy - err = await deployContractProxyByStorageName(hre, path, deployer, L1StakingProxyStorageName) + // ************************ batch submitter contract deploy ************************ + // SubmitterProxy deploy + err = await deployContractProxyByStorageName(hre, path, deployer, SubmitterProxyStorageName) if (err != "") { return err } @@ -281,7 +281,7 @@ export const deployContractProxiesConcurrently = async ( const proxyStorageNames = [ ProxyStorageName.L1CrossDomainMessengerProxyStorageName, ProxyStorageName.L1MessageQueueWithGasPriceOracleProxyStorageName, - ProxyStorageName.L1StakingProxyStorageName, + ProxyStorageName.SubmitterProxyStorageName, ProxyStorageName.L1SequencerProxyStorageName, ProxyStorageName.RollupProxyStorageName, ProxyStorageName.L1GatewayRouterProxyStorageName, diff --git a/contracts/deploy/014-DeployImpls.ts b/contracts/deploy/014-DeployImpls.ts index b32613018..f095e1258 100644 --- a/contracts/deploy/014-DeployImpls.ts +++ b/contracts/deploy/014-DeployImpls.ts @@ -23,7 +23,7 @@ export const deployContractImplsConcurrently = async ( ): Promise => { // factory name const L1CrossDomainMessengerFactoryName = ContractFactoryName.L1CrossDomainMessenger - const L1StakingFactoryName = ContractFactoryName.L1Staking + const SubmitterFactoryName = ContractFactoryName.Submitter const L1MessageQueueWithGasPriceOracleFactoryName = ContractFactoryName.L1MessageQueueWithGasPriceOracle const RollupFactoryName = ContractFactoryName.Rollup const WhitelistFactoryName = ContractFactoryName.Whitelist @@ -41,7 +41,7 @@ export const deployContractImplsConcurrently = async ( // implement storage name const L1CrossDomainMessengerImplStorageName = ImplStorageName.L1CrossDomainMessengerStorageName - const StakingImplStorageName = ImplStorageName.L1StakingStorageName + const SubmitterImplStorageName = ImplStorageName.SubmitterStorageName const L1MessageQueueWithGasPriceOracleImplStorageName = ImplStorageName.L1MessageQueueWithGasPriceOracle const RollupImplStorageName = ImplStorageName.RollupStorageName const L1GatewayRouterImplStorageName = ImplStorageName.L1GatewayRouterStorageName @@ -80,18 +80,6 @@ export const deployContractImplsConcurrently = async ( await contract.deployed() console.log("%s=%s ; TX_HASH: %s", storageName, contract.address.toLocaleLowerCase(), contract.deployTransaction.hash); - if (factoryName == L1StakingFactoryName) { - await assertContractVariable( - contract, - 'MESSENGER', - L1CrossDomainMessengerProxyAddress - ) - await assertContractVariable( - contract, - 'OTHER_STAKING', - predeploys.L2Staking.toLowerCase() - ) - } const blockNumber = await hre.ethers.provider.getBlockNumber() console.log("BLOCK_NUMBER: %s", blockNumber) console.log(`Deployment completed for: ${storageName}`); @@ -120,7 +108,7 @@ export const deployContractImplsConcurrently = async ( deployPromises.push(deployContract(L1ERC721GatewayFactoryName, L1ERC721GatewayImplStorageName)) deployPromises.push(deployContract(L1ERC1155GatewayFactoryName, L1ERC1155GatewayImplStorageName)) - deployPromises.push(deployContract(L1StakingFactoryName, StakingImplStorageName, [L1CrossDomainMessengerProxyAddress])) + deployPromises.push(deployContract(SubmitterFactoryName, SubmitterImplStorageName)) // L1Sequencer deploy (no constructor args) const L1SequencerFactoryName = ContractFactoryName.L1Sequencer @@ -151,7 +139,7 @@ export const deployContractImpls = async ( ): Promise => { // factory name const L1CrossDomainMessengerFactoryName = ContractFactoryName.L1CrossDomainMessenger - const L1StakingFactoryName = ContractFactoryName.L1Staking + const SubmitterFactoryName = ContractFactoryName.Submitter const L1MessageQueueWithGasPriceOracleFactoryName = ContractFactoryName.L1MessageQueueWithGasPriceOracle const RollupFactoryName = ContractFactoryName.Rollup const WhitelistFactoryName = ContractFactoryName.Whitelist @@ -169,7 +157,7 @@ export const deployContractImpls = async ( // implement storage name const L1CrossDomainMessengerImplStorageName = ImplStorageName.L1CrossDomainMessengerStorageName - const StakingImplStorageName = ImplStorageName.L1StakingStorageName + const SubmitterImplStorageName = ImplStorageName.SubmitterStorageName const L1MessageQueueWithGasPriceOracleImplStorageName = ImplStorageName.L1MessageQueueWithGasPriceOracle const RollupImplStorageName = ImplStorageName.RollupStorageName const L1GatewayRouterImplStorageName = ImplStorageName.L1GatewayRouterStorageName @@ -364,25 +352,14 @@ export const deployContractImpls = async ( return err } - // ************************ staking contracts deploy ************************ - // Staking deploy - Factory = await hre.ethers.getContractFactory(L1StakingFactoryName) - contract = await Factory.deploy(L1CrossDomainMessengerProxyAddress) + // ************************ batch submitter contract deploy ************************ + Factory = await hre.ethers.getContractFactory(SubmitterFactoryName) + contract = await Factory.deploy() await contract.deployed() - console.log("%s=%s ; TX_HASH: %s", StakingImplStorageName, contract.address.toLocaleLowerCase(), contract.deployTransaction.hash); - await assertContractVariable( - contract, - 'MESSENGER', - L1CrossDomainMessengerProxyAddress - ) - await assertContractVariable( - contract, - 'OTHER_STAKING', - predeploys.L2Staking.toLowerCase() - ) + console.log("%s=%s ; TX_HASH: %s", SubmitterImplStorageName, contract.address.toLocaleLowerCase(), contract.deployTransaction.hash); blockNumber = await hre.ethers.provider.getBlockNumber() console.log("BLOCK_NUMBER: %s", blockNumber) - err = await storage(path, StakingImplStorageName, contract.address.toLocaleLowerCase(), blockNumber || 0) + err = await storage(path, SubmitterImplStorageName, contract.address.toLocaleLowerCase(), blockNumber || 0) if (err != '') { return err } diff --git a/contracts/deploy/015-SubmitterInstall.ts b/contracts/deploy/015-SubmitterInstall.ts new file mode 100644 index 000000000..871824c42 --- /dev/null +++ b/contracts/deploy/015-SubmitterInstall.ts @@ -0,0 +1,109 @@ +import "@nomiclabs/hardhat-ethers"; + +import { HardhatRuntimeEnvironment } from "hardhat/types"; +import { ethers } from "ethers"; + +import { getContractAddressByName, storage } from "../src/deploy-utils"; +import { ContractFactoryName, ImplStorageName, ProxyStorageName } from "../src/types"; + +const IMPLEMENTATION_SLOT = "0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc"; + +export const SubmitterInstall = async ( + hre: HardhatRuntimeEnvironment, + path: string, + deployer: any, + config: any +): Promise => { + try { + const proxyAddress = getContractAddressByName(path, ProxyStorageName.SubmitterProxyStorageName); + const implementationAddress = getContractAddressByName(path, ImplStorageName.SubmitterStorageName); + const emptyImplementation = getContractAddressByName(path, ImplStorageName.EmptyContract); + const rollupAddress = getContractAddressByName(path, ProxyStorageName.RollupProxyStorageName); + const owner = config.submitterOwner || config.contractAdmin; + const minimumStake = config.submitterMinimumStake; + const challengeDeposit = config.submitterChallengeDeposit; + const rewardPercentage = config.submitterRewardPercentage; + const plannedSubmitters: string[] = config.batchSubmitterAddresses || []; + + if ( + !ethers.utils.isAddress(proxyAddress) || + !ethers.utils.isAddress(implementationAddress) || + !ethers.utils.isAddress(rollupAddress) || + !ethers.utils.isAddress(owner) || + !(minimumStake > 0) || + !(challengeDeposit > 0) || + !(rewardPercentage > 0 && rewardPercentage <= 100) || + !Array.isArray(plannedSubmitters) || + plannedSubmitters.some((address) => !ethers.utils.isAddress(address)) + ) { + return "invalid Submitter install configuration"; + } + + const proxy = await hre.ethers.getContractAt( + ContractFactoryName.DefaultProxyInterface, + proxyAddress, + deployer + ); + const currentImplementation = await proxy.implementation(); + if (currentImplementation.toLowerCase() !== implementationAddress.toLowerCase()) { + if (currentImplementation.toLowerCase() !== emptyImplementation.toLowerCase()) { + return `Submitter proxy has unexpected implementation ${currentImplementation}`; + } + const factory = await hre.ethers.getContractFactory(ContractFactoryName.Submitter); + const initializer = factory.interface.encodeFunctionData("initialize", [ + owner, + rollupAddress, + hre.ethers.utils.parseEther(minimumStake.toString()), + hre.ethers.utils.parseEther(challengeDeposit.toString()), + rewardPercentage, + ]); + const response = await proxy.upgradeToAndCall(implementationAddress, initializer); + await response.wait(); + } + + // A transparent proxy admin cannot call business getters. Use a non-admin eth_call sender. + const reader = hre.ethers.Wallet.createRandom().connect(hre.ethers.provider); + const submitter = await hre.ethers.getContractAt(ContractFactoryName.Submitter, proxyAddress, reader); + if ((await submitter.owner()).toLowerCase() !== owner.toLowerCase()) { + return "Submitter owner verification failed"; + } + if ((await submitter.rollupContract()).toLowerCase() !== rollupAddress.toLowerCase()) { + return "Submitter rollup binding verification failed"; + } + if (!(await submitter.minimumStake()).eq(hre.ethers.utils.parseEther(minimumStake.toString()))) { + return "Submitter minimum stake verification failed"; + } + if (!(await submitter.challengeDeposit()).eq(hre.ethers.utils.parseEther(challengeDeposit.toString()))) { + return "Submitter challenge deposit verification failed"; + } + if (!(await submitter.rewardPercentage()).eq(rewardPercentage)) { + return "Submitter reward percentage verification failed"; + } + for (const account of plannedSubmitters) { + if ((await submitter.registered(account)) || !(await submitter.stakeOf(account)).isZero()) { + return `Submitter ${account} must remain unregistered and unstaked before Rollup cutover`; + } + } + + const rawImplementation = await hre.ethers.provider.getStorageAt(proxyAddress, IMPLEMENTATION_SLOT); + const implementationInSlot = ethers.utils.getAddress(ethers.utils.hexDataSlice(rawImplementation, 12)); + if (implementationInSlot.toLowerCase() !== implementationAddress.toLowerCase()) { + return "Submitter EIP-1967 implementation slot verification failed"; + } + const implementationCode = await hre.ethers.provider.getCode(implementationAddress); + if (implementationCode === "0x") return "Submitter implementation has no code"; + const blockNumber = await hre.ethers.provider.getBlockNumber(); + return storage(path, ImplStorageName.SubmitterInstallStorageName, proxyAddress.toLowerCase(), blockNumber, { + implementationSlot: IMPLEMENTATION_SLOT, + implementation: implementationAddress.toLowerCase(), + implementationCodeHash: ethers.utils.keccak256(implementationCode), + rollupContract: rollupAddress.toLowerCase(), + owner: owner.toLowerCase(), + }); + } catch (error) { + console.error("Submitter install failed", error); + return error instanceof Error ? error.message : "Submitter install failed"; + } +}; + +export default SubmitterInstall; diff --git a/contracts/deploy/016-RollupInit.ts b/contracts/deploy/016-RollupInit.ts index 82036f186..9043d2b34 100644 --- a/contracts/deploy/016-RollupInit.ts +++ b/contracts/deploy/016-RollupInit.ts @@ -23,7 +23,7 @@ export const RollupInit = async ( // Load the contracts we need to interact with. const ZkEvmVerifierV1Address = getContractAddressByName(path, ImplStorageName.ZkEvmVerifierV1StorageName) const L1MessageQueueWithGasPriceOracleProxyAddress = getContractAddressByName(path, ProxyStorageName.L1MessageQueueWithGasPriceOracleProxyStorageName) - const L1StakingProxyAddress = getContractAddressByName(path, ProxyStorageName.L1StakingProxyStorageName) + const SubmitterProxyAddress = getContractAddressByName(path, ProxyStorageName.SubmitterProxyStorageName) // Rollup config const RollupProxyAddress = getContractAddressByName(path, ProxyStorageName.RollupProxyStorageName) @@ -60,7 +60,7 @@ export const RollupInit = async ( if (!ethers.utils.isAddress(L1MessageQueueWithGasPriceOracleProxyAddress) || !ethers.utils.isAddress(MultipleVersionRollupVerifierContract.address) - || !ethers.utils.isAddress(L1StakingProxyAddress) + || !ethers.utils.isAddress(SubmitterProxyAddress) ) { console.error('please check your address') @@ -70,7 +70,7 @@ export const RollupInit = async ( await IRollupProxy.upgradeToAndCall( RollupImplAddress, RollupFactory.interface.encodeFunctionData('initialize', [ - L1StakingProxyAddress, + SubmitterProxyAddress, L1MessageQueueWithGasPriceOracleProxyAddress, MultipleVersionRollupVerifierContract.address, finalizationPeriodSeconds, @@ -97,8 +97,8 @@ export const RollupInit = async ( ) await assertContractVariable( contractTmp, - 'l1StakingContract', - L1StakingProxyAddress + 'submitterContract', + SubmitterProxyAddress ) await assertContractVariable( contractTmp, diff --git a/contracts/deploy/018-StakingInit.ts b/contracts/deploy/018-StakingInit.ts deleted file mode 100644 index 5cf151a42..000000000 --- a/contracts/deploy/018-StakingInit.ts +++ /dev/null @@ -1,128 +0,0 @@ -import "@nomiclabs/hardhat-web3"; -import "@nomiclabs/hardhat-ethers"; -import "@nomiclabs/hardhat-waffle"; - -import { - HardhatRuntimeEnvironment -} from 'hardhat/types'; -import { assertContractVariable, getContractAddressByName, awaitCondition } from "../src/deploy-utils"; -import { ethers } from 'ethers' - -import { - ImplStorageName, - ProxyStorageName, - ContractFactoryName, -} from "../src/types" - -export const StakingInit = async ( - hre: HardhatRuntimeEnvironment, - path: string, - deployer: any, - configTmp: any -): Promise => { - // Load the contracts we need to interact with. - const RollupProxyAddress = getContractAddressByName(path, ProxyStorageName.RollupProxyStorageName) - - // Staking config - const L1StakingProxyAddress = getContractAddressByName(path, ProxyStorageName.L1StakingProxyStorageName) - const L1StakingImplAddress = getContractAddressByName(path, ImplStorageName.L1StakingStorageName) - const L1StakingFactory = await hre.ethers.getContractFactory(ContractFactoryName.L1Staking) - - const IL1StakingProxy = await hre.ethers.getContractAt(ContractFactoryName.DefaultProxyInterface, L1StakingProxyAddress, deployer) - if ( - (await IL1StakingProxy.implementation()).toLocaleLowerCase() !== L1StakingImplAddress.toLocaleLowerCase() - ) { - console.log('Upgrading the Staking proxy...') - const admin: string = configTmp.contractAdmin - const stakingChallengerRewardPercentage: number = configTmp.stakingChallengerRewardPercentage - const limit: number = configTmp.stakingMinDeposit - const challengeDeposit: number = configTmp.stakingChallengeDeposit - const lock: number = configTmp.stakingLockNumber - const gasLimitAdd: number = configTmp.stakingCrossChainGaslimitAdd - const gasLimitRemove: number = configTmp.stakingCrossChainGaslimitRemove - - if (!ethers.utils.isAddress(admin) - || lock <= 0 - || limit <= 0 - || challengeDeposit <= 0 - || gasLimitAdd <= 0 - || gasLimitRemove <= 0 - || stakingChallengerRewardPercentage > 100 - || stakingChallengerRewardPercentage <= 0 - ) { - console.error('please check your address') - return '' - } - - // Upgrade and initialize the proxy. - await IL1StakingProxy.upgradeToAndCall( - L1StakingImplAddress, - L1StakingFactory.interface.encodeFunctionData('initialize', [ - RollupProxyAddress, - hre.ethers.utils.parseEther(limit.toString()), - hre.ethers.utils.parseEther(challengeDeposit.toString()), - lock.toString(), - stakingChallengerRewardPercentage, - gasLimitAdd, - gasLimitRemove, - ]) - ) - - await awaitCondition( - async () => { - return ( - (await IL1StakingProxy.implementation()).toLocaleLowerCase() === L1StakingImplAddress.toLocaleLowerCase() - ) - }, - 3000, - 1000 - ) - - const contractTmp = new ethers.Contract( - L1StakingProxyAddress, - L1StakingFactory.interface, - deployer, - ) - - await assertContractVariable( - contractTmp, - 'rollupContract', - RollupProxyAddress - ) - await assertContractVariable( - contractTmp, - 'rewardPercentage', - stakingChallengerRewardPercentage - ) - await assertContractVariable( - contractTmp, - 'stakingValue', - hre.ethers.utils.parseEther(limit.toString()) - ) - await assertContractVariable( - contractTmp, - 'withdrawalLockBlocks', - lock.toString() - ) - await assertContractVariable( - contractTmp, - 'gasLimitAddStaker', - gasLimitAdd - ) - await assertContractVariable( - contractTmp, - 'gasLimitRemoveStakers', - gasLimitRemove - ) - await assertContractVariable( - contractTmp, - 'owner', - await deployer.getAddress(), - ) - // Wait for the transaction to execute properly. - console.log('StakingProxy upgrade success') - } - return '' -} - -export default StakingInit diff --git a/contracts/deploy/019-AdminTransfer.ts b/contracts/deploy/019-AdminTransfer.ts index a0cd1b58a..491248494 100644 --- a/contracts/deploy/019-AdminTransfer.ts +++ b/contracts/deploy/019-AdminTransfer.ts @@ -108,7 +108,7 @@ export const AdminTransferConcurrently = async ( const contractsToChange = [ ProxyStorageName.L1CrossDomainMessengerProxyStorageName, ProxyStorageName.L1MessageQueueWithGasPriceOracleProxyStorageName, - ProxyStorageName.L1StakingProxyStorageName, + ProxyStorageName.SubmitterProxyStorageName, ProxyStorageName.L1SequencerProxyStorageName, // Added L1Sequencer ProxyStorageName.RollupProxyStorageName, ProxyStorageName.L1GatewayRouterProxyStorageName, @@ -159,7 +159,7 @@ export const AdminTransfer = async ( const L1MessageQueueWithGasPriceOracleProxyStorageName = ProxyStorageName.L1MessageQueueWithGasPriceOracleProxyStorageName const RollupProxyStorageName = ProxyStorageName.RollupProxyStorageName - const L1StakingProxyStorageName = ProxyStorageName.L1StakingProxyStorageName + const SubmitterProxyStorageName = ProxyStorageName.SubmitterProxyStorageName const L1SequencerProxyStorageName = ProxyStorageName.L1SequencerProxyStorageName const L1GatewayRouterProxyStorageName = ProxyStorageName.L1GatewayRouterProxyStorageName @@ -187,9 +187,9 @@ export const AdminTransfer = async ( return err } - // ************************ staking contracts admin change ************************ - // StakingProxy admin change - err = await AdminTransferByProxyStorageName(hre, path, deployer, L1StakingProxyStorageName) + // ************************ batch submitter contract admin change ************************ + // SubmitterProxy admin change + err = await AdminTransferByProxyStorageName(hre, path, deployer, SubmitterProxyStorageName) if (err != '') { return err } diff --git a/contracts/deploy/020-ContractInit.ts b/contracts/deploy/020-ContractInit.ts index 96bf3637b..22ed49663 100644 --- a/contracts/deploy/020-ContractInit.ts +++ b/contracts/deploy/020-ContractInit.ts @@ -31,20 +31,6 @@ export const ContractInit = async ( let rec = await res.wait() console.log(`set base fee ${rec.status === 1} setL2BaseFee(${await GasPriceOracle.l2BaseFee()}) gwei`) - const WhitelistImplAddress = getContractAddressByName(path, ImplStorageName.Whitelist) - const L1StakingProxyAddress = getContractAddressByName(path, ProxyStorageName.L1StakingProxyStorageName) - const WhitelistCheckerImpl = await hre.ethers.getContractAt(ContractFactoryName.Whitelist, WhitelistImplAddress, deployer) - let addList = [L1StakingProxyAddress] - res = await WhitelistCheckerImpl.updateWhitelistStatus(addList, true) - rec = await res.wait() - for (let i = 0; i < addList.length; i++) { - let res = await WhitelistCheckerImpl.isSenderAllowed(addList[i]) - if (res != true) { - console.error('whitelist check failed') - return '' - } - } - console.log(`add ${addList} to whitelist success`) } // ------------------ rollup init ----------------- @@ -54,14 +40,11 @@ export const ContractInit = async ( // import genesis batch const batchHeader: string = config.batchHeader - // submitter and challenger - const submitter: string = config.rollupProposer + // challenger const challenger: string = config.rollupChallenger const rollupDelayPeriod: number = config.rollupDelayPeriod - if (!ethers.utils.isAddress(submitter) - || !ethers.utils.isAddress(challenger) - ) { + if (!ethers.utils.isAddress(challenger)) { console.error('please check your address') return '' } @@ -85,29 +68,6 @@ export const ContractInit = async ( console.log(`initialize3(%s) ${rec.status == 1 ? "success" : "failed"}`) } - // ------------------ staking init ----------------- - { - const L1StakingProxyAddress = getContractAddressByName(path, ProxyStorageName.L1StakingProxyStorageName) - const L1Staking = await hre.ethers.getContractAt(ContractFactoryName.L1Staking, L1StakingProxyAddress, deployer) - const whiteListAdd = config.l2SequencerAddresses - console.log("Add sequencer address to white list ", config.l2SequencerAddresses) - // set sequencer to white list - await L1Staking.updateWhitelist(whiteListAdd, []) - for (let i = 0; i < config.l2SequencerAddresses.length; i++) { - // Wait for the transaction to execute properly. - await awaitCondition( - async () => { - return ( - await L1Staking.whitelist(config.l2SequencerAddresses[i]) === true - ) - }, - 3000, - 1000 - ) - console.log(`address ${config.l2SequencerAddresses[i]} is in white list`) - } - } - // ------------------ router init ----------------- { const L1WETHAddress = getContractAddressByName(path, ImplStorageName.WETH) diff --git a/contracts/deploy/021-StakingRegister.ts b/contracts/deploy/021-StakingRegister.ts deleted file mode 100644 index 9e69e0bf8..000000000 --- a/contracts/deploy/021-StakingRegister.ts +++ /dev/null @@ -1,57 +0,0 @@ -import "@nomiclabs/hardhat-web3"; -import "@nomiclabs/hardhat-ethers"; -import "@nomiclabs/hardhat-waffle"; - -import { - HardhatRuntimeEnvironment -} from 'hardhat/types'; -import { getContractAddressByName, awaitCondition } from "../src/deploy-utils"; -import { ethers } from 'ethers' -import { - ProxyStorageName, - ContractFactoryName, -} from "../src/types" - -const two = BigInt(2) -const gwei = BigInt(1e9) -const eth = gwei * gwei - -export const StakingRegister = async ( - hre: HardhatRuntimeEnvironment, - path: string, - signer: any, - tmKey: string, - blsKey: string -): Promise => { - const L1StakingProxyAddress = getContractAddressByName(path, ProxyStorageName.L1StakingProxyStorageName) - const L1StakingFactory = await hre.ethers.getContractFactory(ContractFactoryName.L1Staking) - - const StakingProxyWithSigner = new ethers.Contract( - L1StakingProxyAddress, - L1StakingFactory.interface, - signer, - ) - - // just for devnet register value may set params to config - const response = await StakingProxyWithSigner.register(tmKey, blsKey, { - value: eth - }) - console.log(`Transaction hash (on L1): ${response.hash}`) - const receipt = await response.wait() - console.log('Transaction in L1 height', receipt.blockNumber) - - await awaitCondition( - async () => { - const sequencerInfo = (await StakingProxyWithSigner.stakers(signer.address)) - if (sequencerInfo && sequencerInfo.tmKey === tmKey) { - return true - } - return false - }, - 30000, - 1000 - ) - return '' -} - -export default StakingRegister \ No newline at end of file diff --git a/contracts/deploy/021-SubmitterRegister.ts b/contracts/deploy/021-SubmitterRegister.ts new file mode 100644 index 000000000..76a71b524 --- /dev/null +++ b/contracts/deploy/021-SubmitterRegister.ts @@ -0,0 +1,38 @@ +import "@nomiclabs/hardhat-ethers"; + +import { HardhatRuntimeEnvironment } from "hardhat/types"; +import { ethers } from "ethers"; + +import { getContractAddressByName } from "../src/deploy-utils"; +import { ContractFactoryName, ProxyStorageName } from "../src/types"; + +export const SubmitterRegister = async ( + hre: HardhatRuntimeEnvironment, + path: string, + ownerSigner: any, + submitterSigner: any +): Promise => { + const proxyAddress = getContractAddressByName(path, ProxyStorageName.SubmitterProxyStorageName); + if (!ethers.utils.isAddress(proxyAddress)) return "invalid Submitter proxy address"; + + const ownerView = await hre.ethers.getContractAt(ContractFactoryName.Submitter, proxyAddress, ownerSigner); + const account = await submitterSigner.getAddress(); + if ((await ownerView.owner()).toLowerCase() !== (await ownerSigner.getAddress()).toLowerCase()) { + return "registration signer is not Submitter owner"; + } + if (!(await ownerView.registered(account))) { + await (await ownerView.addSubmitter(account)).wait(); + } + + const submitterView = ownerView.connect(submitterSigner); + const minimumStake = await submitterView.minimumStake(); + const currentStake = await submitterView.stakeOf(account); + if (currentStake.lt(minimumStake)) { + await (await submitterView.stake({ value: minimumStake.sub(currentStake) })).wait(); + } + if (!(await submitterView.isActive(account))) return "Submitter did not become active"; + console.log(`Submitter ${account} registered and active with stake ${await submitterView.stakeOf(account)}`); + return ""; +}; + +export default SubmitterRegister; diff --git a/contracts/deploy/index.ts b/contracts/deploy/index.ts index a30ab90fb..b0d41da05 100644 --- a/contracts/deploy/index.ts +++ b/contracts/deploy/index.ts @@ -3,13 +3,13 @@ import deployEmptyContract from './011-EmptyContract' import {deployContractProxies,deployContractProxiesConcurrently} from './013-DeployProxys' import deployZkEvmVerifierV1 from './012-Verifier' import {deployContractImpls, deployContractImplsConcurrently} from './014-DeployImpls' +import SubmitterInstall from './015-SubmitterInstall' import MessengerInit from './015-MessengerInit' import RollupInit from './016-RollupInit' import GatewayInit from './017-GatewayInit' -import StakingInit from './018-StakingInit' import {AdminTransfer,AdminTransferByProxyStorageName, AdminTransferConcurrently} from './019-AdminTransfer' import ContractInit from './020-ContractInit' -import StakingRegister from './021-StakingRegister' +import SubmitterRegister from './021-SubmitterRegister' import SequencerInit from './022-SequencerInit' @@ -21,14 +21,14 @@ export { deployContractProxiesConcurrently, deployContractImpls, deployContractImplsConcurrently, + SubmitterInstall, MessengerInit, RollupInit, GatewayInit, - StakingInit, AdminTransfer, AdminTransferByProxyStorageName, AdminTransferConcurrently, ContractInit, - StakingRegister, + SubmitterRegister, SequencerInit -} \ No newline at end of file +} diff --git a/contracts/hardhat.config.ts b/contracts/hardhat.config.ts index c60bf6ec7..af8adc51b 100644 --- a/contracts/hardhat.config.ts +++ b/contracts/hardhat.config.ts @@ -9,13 +9,11 @@ import fs from "fs" import { TASK_COMPILE_SOLIDITY_GET_SOURCE_PATHS } from "hardhat/builtin-tasks/task-names"; import { subtask } from "hardhat/config"; - import "./tasks/deploy" import "./tasks/overflow_test" import "./tasks/check" import "./tasks/query" import "./tasks/proxy_upgrade" -import "./tasks/staking_upgrade" import "./tasks/verifier_upgrade" import "./tasks/token_deploy" import "./tasks/mainnet_upgrade" @@ -51,7 +49,9 @@ module.exports = { abiExporter: { path: './abi', runOnCompile: true, - clear: true, + // The immutable pre-submitter and legacy retirement ABIs live under abi/archive. + // Never let a routine current-ABI export erase those cutover artifacts. + clear: false, }, bytecodeExporter: { path: './data', @@ -156,4 +156,4 @@ module.exports = { artifacts: "./artifacts", deployConfig: './src/deploy-config', } -} \ No newline at end of file +} diff --git a/contracts/integration-test/retire-l1-staking.spec.ts b/contracts/integration-test/retire-l1-staking.spec.ts new file mode 100644 index 000000000..f7eabd5b0 --- /dev/null +++ b/contracts/integration-test/retire-l1-staking.spec.ts @@ -0,0 +1,122 @@ +import { expect } from "chai"; +import { BigNumber, ethers } from "ethers"; + +import { + ReplayEvent, + RetirementManifest, + rebuildLegacyState, + retirementBlockers, + verifyExecutePreconditions, +} from "../scripts/retire-l1-staking"; + +const alice = "0x00000000000000000000000000000000000000a1"; +const bob = "0x00000000000000000000000000000000000000b2"; + +function event( + blockNumber: number, + blockHash: string, + name: string, + args: Record, + logIndex = 0 +): ReplayEvent { + return { + blockNumber, + blockHash, + transactionHash: `0x${blockNumber.toString(16).padStart(64, "0")}`, + logIndex, + name, + args, + }; +} + +function manifest(): RetirementManifest { + return { + version: 1, + legacy: { + proxy: "0x0000000000000000000000000000000000000100", + deployBlock: 1, + deployBlockHash: "0x01", + proxyAdminIsDedicated: false, + burnAdmin: "0x000000000000000000000000000000000000dEaD", + }, + retirement: { confirmations: 1, stakingValue: "100", treasury: alice }, + snapshot: { + chainId: 1, + safeHead: 10, + safeHeadHash: "0x10", + stakingValue: "100", + contractBalance: "0", + expectedLiabilities: "0", + unexplainedBalance: "0", + slashRemaining: "0", + owner: alice, + activeStakers: [], + }, + candidates: [], + canonicalEventIds: [], + checkpoints: [], + relays: [], + operations: [], + }; +} + +describe("legacy L1Staking retirement planning", () => { + it("aborts when stakingValue changes during the withdrawal window", () => { + const plan = manifest(); + expect(() => verifyExecutePreconditions(plan, { stakingValue: BigNumber.from(101) })).to.throw( + "stakingValue changed during retirement" + ); + }); + + it("rebuilds ever-whitelisted state from the canonical branch after a short reorg", () => { + const orphanHash = `0x${"aa".repeat(32)}`; + const canonicalHash = `0x${"bb".repeat(32)}`; + const events = [ + event(10, orphanHash, "WhitelistUpdated", { add: [alice], remove: [] }), + event(10, canonicalHash, "WhitelistUpdated", { add: [bob], remove: [] }), + event(10, canonicalHash, "Registered", { addr: bob }, 1), + event(10, canonicalHash, "Withdrawn", { addr: bob }, 2), + event(10, canonicalHash, "StakersRemoved", { stakers: [bob] }, 3), + ]; + + const rebuilt = rebuildLegacyState(events, { 10: canonicalHash }); + expect(rebuilt.candidates.map((candidate) => candidate.address)).to.deep.equal([ethers.utils.getAddress(bob)]); + expect(rebuilt.candidates[0].everRegistered).to.equal(true); + expect(rebuilt.candidates[0].withdrawn).to.equal(true); + expect(rebuilt.candidates[0].removed).to.equal(true); + expect(rebuilt.canonicalEventIds).to.have.length(4); + }); + + it("blocks owner/admin retirement until whitelist, withdrawals, relays, and admin scope are clean", () => { + const plan = manifest(); + plan.candidates = [ + { + address: alice, + everRegistered: true, + withdrawn: true, + claimed: false, + slashed: false, + removed: true, + currentWhitelist: true, + currentActive: false, + withdrawalUnlockBlock: "12", + }, + ]; + plan.relays = [ + { + messageHash: "0x01", + addresses: [alice], + transactionHash: "0x02", + blockHash: "0x03", + success: false, + canonical: false, + }, + ]; + expect(retirementBlockers(plan)).to.include.members([ + `${alice}: whitelist still enabled`, + `${alice}: withdrawal still pending`, + "0x01: removeStakers relay incomplete", + "legacy proxy admin is not confirmed dedicated", + ]); + }); +}); diff --git a/contracts/integration-test/submitter-deploy.spec.ts b/contracts/integration-test/submitter-deploy.spec.ts new file mode 100644 index 000000000..b713ea961 --- /dev/null +++ b/contracts/integration-test/submitter-deploy.spec.ts @@ -0,0 +1,72 @@ +import { expect } from "chai"; +import { ethers } from "hardhat"; + +const EIP1967_ADMIN_SLOT = "0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103"; + +describe("Submitter deployment order", () => { + it("installs and initializes Submitter before Rollup and admin transfer", async () => { + const [proxyAdminSigner, submitterOwner, other] = await ethers.getSigners(); + const empty = await (await ethers.getContractFactory("EmptyContract")).deploy(); + const proxyFactory = await ethers.getContractFactory("TransparentUpgradeableProxy"); + const rollupProxy = await proxyFactory.deploy(empty.address, proxyAdminSigner.address, "0x"); + const submitterProxy = await proxyFactory.deploy(empty.address, proxyAdminSigner.address, "0x"); + const submitterImpl = await (await ethers.getContractFactory("Submitter")).deploy(); + const rollupImpl = await (await ethers.getContractFactory("Rollup")).deploy(53077); + + const proxyAdminView = await ethers.getContractAt( + "ITransparentUpgradeableProxy", + submitterProxy.address, + proxyAdminSigner + ); + const submitterFactory = await ethers.getContractFactory("Submitter"); + await proxyAdminView.upgradeToAndCall( + submitterImpl.address, + submitterFactory.interface.encodeFunctionData("initialize", [ + submitterOwner.address, + rollupProxy.address, + ethers.utils.parseEther("1"), + ethers.utils.parseEther("1"), + 20, + ]) + ); + expect(await proxyAdminView.implementation()).to.equal(submitterImpl.address); + + const submitter = await ethers.getContractAt("Submitter", submitterProxy.address, submitterOwner); + expect(await submitter.owner()).to.equal(submitterOwner.address); + expect(await submitter.rollupContract()).to.equal(rollupProxy.address); + await expect( + submitter.initialize( + submitterOwner.address, + rollupProxy.address, + ethers.utils.parseEther("1"), + ethers.utils.parseEther("1"), + 20 + ) + ).to.be.revertedWith("Initializable: contract is already initialized"); + + const rollupProxyAdminView = await ethers.getContractAt( + "ITransparentUpgradeableProxy", + rollupProxy.address, + proxyAdminSigner + ); + const rollupFactory = await ethers.getContractFactory("Rollup"); + await rollupProxyAdminView.upgradeToAndCall( + rollupImpl.address, + rollupFactory.interface.encodeFunctionData("initialize", [ + submitterProxy.address, + other.address, + other.address, + 10, + 100, + 70, + ]) + ); + const rollup = await ethers.getContractAt("Rollup", rollupProxy.address, submitterOwner); + expect(await rollup.submitterContract()).to.equal(submitterProxy.address); + + const proxyAdmin = await (await ethers.getContractFactory("ProxyAdmin")).deploy(); + await proxyAdminView.changeAdmin(proxyAdmin.address); + const rawAdmin = await ethers.provider.getStorageAt(submitterProxy.address, EIP1967_ADMIN_SLOT); + expect(ethers.utils.getAddress(ethers.utils.hexDataSlice(rawAdmin, 12))).to.equal(proxyAdmin.address); + }); +}); diff --git a/contracts/ops/legacy-l1-staking-retirement.example.json b/contracts/ops/legacy-l1-staking-retirement.example.json new file mode 100644 index 000000000..c338e09f9 --- /dev/null +++ b/contracts/ops/legacy-l1-staking-retirement.example.json @@ -0,0 +1,18 @@ +{ + "version": 1, + "legacy": { + "proxy": "0x0000000000000000000000000000000000000000", + "deployBlock": 0, + "proxyAdminIsDedicated": false, + "burnAdmin": "0x000000000000000000000000000000000000dEaD" + }, + "retirement": { + "confirmations": 64, + "treasury": "0x0000000000000000000000000000000000000000" + }, + "candidates": [], + "canonicalEventIds": [], + "checkpoints": [], + "relays": [], + "operations": [] +} diff --git a/contracts/scripts/retire-l1-staking.ts b/contracts/scripts/retire-l1-staking.ts new file mode 100644 index 000000000..e3371c1e6 --- /dev/null +++ b/contracts/scripts/retire-l1-staking.ts @@ -0,0 +1,664 @@ +import { createHash } from "crypto"; +import fs from "fs"; +import path from "path"; + +import { BigNumber, Contract, Wallet, ethers } from "ethers"; + +const ADMIN_SLOT = "0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103"; +const IMPLEMENTATION_SLOT = "0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc"; +const ZERO_ADDRESS = ethers.constants.AddressZero; + +export type RetirementMode = "plan" | "dry-run" | "execute" | "verify"; + +export interface ReplayEvent { + name: string; + args: Record; + blockNumber: number; + blockHash: string; + transactionHash: string; + logIndex: number; +} + +export interface CandidateState { + address: string; + firstEvent?: string; + lastEvent?: string; + everRegistered: boolean; + withdrawn: boolean; + claimed: boolean; + slashed: boolean; + removed: boolean; + currentWhitelist?: boolean; + currentActive?: boolean; + withdrawalUnlockBlock?: string; +} + +export interface RetirementOperation { + kind: "freeze" | "withdraw" | "claim" | "claim-slash" | "renounce-owner" | "burn-admin"; + actor: string; + target: string; + value: string; + data: string; + description: string; + nonce?: number; + expectedState: string[]; + txHash?: string; + receiptBlockHash?: string; +} + +export interface RelayEvidence { + messageHash: string; + addresses?: string[]; + transactionHash: string; + blockHash: string; + success: boolean; + canonical: boolean; +} + +export interface RetirementManifest { + version: number; + legacy: { + proxy: string; + implementation?: string; + deployBlock: number; + deployBlockHash?: string; + proxyAdmin?: string; + proxyAdminOwner?: string; + proxyAdminIsDedicated: boolean; + burnAdmin: string; + }; + retirement: { + confirmations: number; + stakingValue?: string; + treasury: string; + }; + snapshot?: { + chainId: number; + safeHead: number; + safeHeadHash: string; + stakingValue: string; + contractBalance: string; + expectedLiabilities: string; + unexplainedBalance: string; + slashRemaining: string; + owner: string; + activeStakers: string[]; + }; + candidates: CandidateState[]; + canonicalEventIds: string[]; + checkpoints: Array<{ blockNumber: number; blockHash: string }>; + relays: RelayEvidence[]; + operations: RetirementOperation[]; + verifiedAt?: string; +} + +function normalizeAddress(value: string): string { + return ethers.utils.getAddress(value); +} + +function eventId(event: ReplayEvent): string { + return `${event.blockHash}:${event.transactionHash}:${event.logIndex}`; +} + +function eventAddresses(value: unknown): string[] { + if (!Array.isArray(value)) return []; + return value.filter((item): item is string => typeof item === "string").map(normalizeAddress); +} + +/** + * Rebuilds state from scratch against canonical block hashes. Rebuilding instead of appending + * means orphaned tip events disappear automatically after an L1 reorg. + */ +export function rebuildLegacyState( + events: ReplayEvent[], + canonicalBlockHashes: Record +): { candidates: CandidateState[]; canonicalEventIds: string[] } { + const candidates = new Map(); + const canonicalEventIds: string[] = []; + + const get = (address: string, identity: string): CandidateState => { + const normalized = normalizeAddress(address); + const key = normalized.toLowerCase(); + let state = candidates.get(key); + if (!state) { + state = { + address: normalized, + firstEvent: identity, + lastEvent: identity, + everRegistered: false, + withdrawn: false, + claimed: false, + slashed: false, + removed: false, + }; + candidates.set(key, state); + } + state.lastEvent = identity; + return state; + }; + + for (const event of [...events].sort((a, b) => a.blockNumber - b.blockNumber || a.logIndex - b.logIndex)) { + const canonicalHash = canonicalBlockHashes[event.blockNumber]; + if (!canonicalHash || canonicalHash.toLowerCase() !== event.blockHash.toLowerCase()) continue; + const identity = eventId(event); + canonicalEventIds.push(identity); + + if (event.name === "WhitelistUpdated") { + for (const address of eventAddresses(event.args.add)) get(address, identity); + for (const address of eventAddresses(event.args.remove)) get(address, identity); + } else if (event.name === "Registered") { + const state = get(String(event.args.addr), identity); + state.everRegistered = true; + state.withdrawn = false; + state.claimed = false; + state.slashed = false; + state.removed = false; + } else if (event.name === "Withdrawn") { + get(String(event.args.addr), identity).withdrawn = true; + } else if (event.name === "Claimed") { + const state = get(String(event.args.staker), identity); + state.claimed = true; + state.withdrawn = false; + } else if (event.name === "Slashed") { + for (const address of eventAddresses(event.args.stakers)) { + const state = get(address, identity); + state.slashed = true; + state.removed = true; + } + } else if (event.name === "StakersRemoved") { + for (const address of eventAddresses(event.args.stakers)) get(address, identity).removed = true; + } + } + + return { + candidates: [...candidates.values()].sort((a, b) => a.address.localeCompare(b.address)), + canonicalEventIds, + }; +} + +export function verifyExecutePreconditions( + manifest: RetirementManifest, + current: { stakingValue: BigNumber | string; deployBlockHash?: string } +): void { + if (!manifest.retirement.stakingValue) throw new Error("retirement stakingValue snapshot missing"); + if (!BigNumber.from(current.stakingValue).eq(manifest.retirement.stakingValue)) { + throw new Error("stakingValue changed during retirement"); + } + if ( + manifest.legacy.deployBlockHash && + current.deployBlockHash && + manifest.legacy.deployBlockHash.toLowerCase() !== current.deployBlockHash.toLowerCase() + ) { + throw new Error("legacy deployment block is no longer canonical"); + } +} + +export function retirementBlockers(manifest: RetirementManifest): string[] { + const blockers: string[] = []; + if (!manifest.snapshot) return ["snapshot missing"]; + for (const candidate of manifest.candidates) { + if (candidate.currentWhitelist) blockers.push(`${candidate.address}: whitelist still enabled`); + if (candidate.currentActive) blockers.push(`${candidate.address}: staker still active`); + if (BigNumber.from(candidate.withdrawalUnlockBlock || 0).gt(0)) { + blockers.push(`${candidate.address}: withdrawal still pending`); + } + } + if (!BigNumber.from(manifest.snapshot.slashRemaining).isZero()) blockers.push("slashRemaining is nonzero"); + if (!BigNumber.from(manifest.snapshot.contractBalance).isZero()) blockers.push("legacy contract balance is nonzero"); + for (const relay of manifest.relays) { + if (!relay.success || !relay.canonical) blockers.push(`${relay.messageHash}: removeStakers relay incomplete`); + } + for (const candidate of manifest.candidates.filter( + (item) => item.removed || item.withdrawn || item.slashed + )) { + const covered = manifest.relays.some( + (relay) => + relay.success && + relay.canonical && + (relay.addresses || []).some( + (address) => address.toLowerCase() === candidate.address.toLowerCase() + ) + ); + if (!covered) blockers.push(`${candidate.address}: canonical removeStakers relay evidence missing`); + } + if (!manifest.legacy.proxyAdminIsDedicated) blockers.push("legacy proxy admin is not confirmed dedicated"); + return blockers; +} + +function archivedAbiPath(): string { + return path.resolve(__dirname, "../abi/archive/legacy-l1-staking/L1Staking.json"); +} + +function loadFrozenAbi(): unknown[] { + const abiPath = archivedAbiPath(); + const archiveManifestPath = path.resolve(__dirname, "../abi/archive/manifest.json"); + if (!fs.existsSync(abiPath) || !fs.existsSync(archiveManifestPath)) { + throw new Error("frozen legacy L1Staking ABI archive is missing"); + } + const raw = fs.readFileSync(abiPath); + const archiveManifest = JSON.parse(fs.readFileSync(archiveManifestPath, "utf8")); + const expected = archiveManifest.artifacts?.["legacy-l1-staking/L1Staking.json"]; + const actual = createHash("sha256").update(raw).digest("hex"); + if (!expected || actual !== expected) throw new Error("frozen legacy L1Staking ABI hash mismatch"); + return JSON.parse(raw.toString("utf8")); +} + +function addressFromSlot(raw: string): string { + return normalizeAddress(ethers.utils.hexDataSlice(raw, 12)); +} + +async function collectCanonicalEvents( + provider: ethers.providers.Provider, + contractAddress: string, + deployBlock: number, + safeHead: number, + abi: unknown[] +): Promise<{ events: ReplayEvent[]; hashes: Record; checkpoints: RetirementManifest["checkpoints"] }> { + const iface = new ethers.utils.Interface(abi); + const logs = await provider.getLogs({ address: contractAddress, fromBlock: deployBlock, toBlock: safeHead }); + const blockNumbers = [...new Set([deployBlock, safeHead, ...logs.map((log) => log.blockNumber)])]; + const blocks = await Promise.all(blockNumbers.map((number) => provider.getBlock(number))); + const hashes: Record = {}; + const checkpoints: RetirementManifest["checkpoints"] = []; + for (const block of blocks) { + if (!block) throw new Error("canonical block lookup failed"); + hashes[block.number] = block.hash; + checkpoints.push({ blockNumber: block.number, blockHash: block.hash }); + } + + const events: ReplayEvent[] = []; + for (const log of logs) { + try { + const parsed = iface.parseLog(log); + if (!["WhitelistUpdated", "Registered", "Withdrawn", "Claimed", "Slashed", "StakersRemoved"].includes(parsed.name)) { + continue; + } + const args: Record = {}; + for (const [key, value] of Object.entries(parsed.args)) { + if (!/^\d+$/.test(key)) args[key] = value; + } + events.push({ + name: parsed.name, + args, + blockNumber: log.blockNumber, + blockHash: log.blockHash, + transactionHash: log.transactionHash, + logIndex: log.logIndex, + }); + } catch { + // The proxy may emit unrelated proxy-admin events; the frozen business ABI does not parse them. + } + } + return { events, hashes, checkpoints }; +} + +async function refreshManifest( + provider: ethers.providers.JsonRpcProvider, + manifest: RetirementManifest, + abi: unknown[] +): Promise { + const latest = await provider.getBlockNumber(); + const safeHead = latest - manifest.retirement.confirmations; + if (safeHead < manifest.legacy.deployBlock) throw new Error("safe head precedes legacy deployment"); + const chain = await provider.getNetwork(); + const contract = new Contract(manifest.legacy.proxy, abi, provider); + const replay = await collectCanonicalEvents(provider, manifest.legacy.proxy, manifest.legacy.deployBlock, safeHead, abi); + const rebuilt = rebuildLegacyState(replay.events, replay.hashes); + const activeStakers: string[] = (await contract.getActiveStakers({ blockTag: safeHead })).map(normalizeAddress); + const candidateMap = new Map(rebuilt.candidates.map((candidate) => [candidate.address.toLowerCase(), candidate])); + for (const address of activeStakers) { + if (!candidateMap.has(address.toLowerCase())) { + candidateMap.set(address.toLowerCase(), { + address, + everRegistered: true, + withdrawn: false, + claimed: false, + slashed: false, + removed: false, + }); + } + } + const candidates = [...candidateMap.values()].sort((a, b) => a.address.localeCompare(b.address)); + for (const candidate of candidates) { + const [whitelist, active, withdrawal] = await Promise.all([ + contract.whitelist(candidate.address, { blockTag: safeHead }), + contract.isActiveStaker(candidate.address, { blockTag: safeHead }), + contract.withdrawals(candidate.address, { blockTag: safeHead }), + ]); + candidate.currentWhitelist = whitelist; + candidate.currentActive = active; + candidate.withdrawalUnlockBlock = withdrawal.toString(); + } + + const [stakingValue, slashRemaining, owner, balance] = await Promise.all([ + contract.stakingValue({ blockTag: safeHead }), + contract.slashRemaining({ blockTag: safeHead }), + contract.owner({ blockTag: safeHead }), + provider.getBalance(manifest.legacy.proxy, safeHead), + ]); + const liabilityCount = candidates.filter( + (candidate) => candidate.currentActive || BigNumber.from(candidate.withdrawalUnlockBlock || 0).gt(0) + ).length; + const expectedLiabilities = stakingValue.mul(liabilityCount).add(slashRemaining); + const unexplainedBalance = balance.gt(expectedLiabilities) ? balance.sub(expectedLiabilities) : BigNumber.from(0); + const safeBlock = await provider.getBlock(safeHead); + const deployBlock = await provider.getBlock(manifest.legacy.deployBlock); + if (!safeBlock || !deployBlock) throw new Error("snapshot block lookup failed"); + + const rawAdmin = await provider.getStorageAt(manifest.legacy.proxy, ADMIN_SLOT, safeHead); + const rawImplementation = await provider.getStorageAt(manifest.legacy.proxy, IMPLEMENTATION_SLOT, safeHead); + manifest.legacy.proxyAdmin = addressFromSlot(rawAdmin); + manifest.legacy.implementation = addressFromSlot(rawImplementation); + manifest.legacy.deployBlockHash = deployBlock.hash; + if (!manifest.retirement.stakingValue) manifest.retirement.stakingValue = stakingValue.toString(); + verifyExecutePreconditions(manifest, { stakingValue, deployBlockHash: deployBlock.hash }); + + manifest.snapshot = { + chainId: chain.chainId, + safeHead, + safeHeadHash: safeBlock.hash, + stakingValue: stakingValue.toString(), + contractBalance: balance.toString(), + expectedLiabilities: expectedLiabilities.toString(), + unexplainedBalance: unexplainedBalance.toString(), + slashRemaining: slashRemaining.toString(), + owner: normalizeAddress(owner), + activeStakers, + }; + manifest.candidates = candidates; + manifest.canonicalEventIds = rebuilt.canonicalEventIds; + manifest.checkpoints = replay.checkpoints; + manifest.operations = await buildOperations(provider, contract, manifest, abi); + return manifest; +} + +async function buildOperations( + provider: ethers.providers.Provider, + contract: Contract, + manifest: RetirementManifest, + abi: unknown[] +): Promise { + if (!manifest.snapshot) throw new Error("snapshot missing"); + const iface = new ethers.utils.Interface(abi); + const owner = manifest.snapshot.owner; + const operations: RetirementOperation[] = []; + const whitelisted = manifest.candidates.filter((candidate) => candidate.currentWhitelist).map((candidate) => candidate.address); + if (whitelisted.length > 0) { + operations.push({ + kind: "freeze", + actor: owner, + target: manifest.legacy.proxy, + value: "0", + data: iface.encodeFunctionData("updateWhitelist", [[], whitelisted]), + description: `remove ${whitelisted.length} canonical ever-whitelisted address(es) from whitelist`, + expectedState: whitelisted.map((address) => `whitelist(${address}) == false`), + }); + } + for (const candidate of manifest.candidates) { + if (candidate.currentActive) { + operations.push({ + kind: "withdraw", + actor: candidate.address, + target: manifest.legacy.proxy, + value: "0", + data: iface.encodeFunctionData("withdraw"), + description: `withdraw legacy stake for ${candidate.address}`, + expectedState: [ + `isActiveStaker(${candidate.address}) == false`, + `withdrawals(${candidate.address}) > 0`, + ], + }); + } else if ( + BigNumber.from(candidate.withdrawalUnlockBlock || 0).gt(0) && + BigNumber.from(candidate.withdrawalUnlockBlock || 0).lte(manifest.snapshot.safeHead) + ) { + operations.push({ + kind: "claim", + actor: candidate.address, + target: manifest.legacy.proxy, + value: "0", + data: iface.encodeFunctionData("claimWithdrawal", [candidate.address]), + description: `claim matured legacy withdrawal for ${candidate.address}`, + expectedState: [`withdrawals(${candidate.address}) == 0`], + }); + } + } + if (!BigNumber.from(manifest.snapshot.slashRemaining).isZero()) { + operations.push({ + kind: "claim-slash", + actor: owner, + target: manifest.legacy.proxy, + value: "0", + data: iface.encodeFunctionData("claimSlashRemaining", [manifest.retirement.treasury]), + description: "reconcile legacy slashRemaining to retirement treasury", + expectedState: ["slashRemaining() == 0"], + }); + } + + if (retirementBlockers(manifest).length === 0 && owner !== ZERO_ADDRESS) { + operations.push({ + kind: "renounce-owner", + actor: owner, + target: manifest.legacy.proxy, + value: "0", + data: iface.encodeFunctionData("renounceOwnership"), + description: "renounce legacy L1Staking business ownership", + expectedState: [`owner() == ${ZERO_ADDRESS}`], + }); + const admin = normalizeAddress(manifest.legacy.proxyAdmin || ZERO_ADDRESS); + if (admin === ZERO_ADDRESS || admin.toLowerCase() === manifest.legacy.burnAdmin.toLowerCase()) { + return assignOperationNonces(provider, operations); + } + const adminCode = await provider.getCode(admin); + if (adminCode === "0x") { + const proxyIface = new ethers.utils.Interface(["function changeAdmin(address newAdmin)"]); + operations.push({ + kind: "burn-admin", + actor: admin, + target: manifest.legacy.proxy, + value: "0", + data: proxyIface.encodeFunctionData("changeAdmin", [manifest.legacy.burnAdmin]), + description: "move the dedicated legacy proxy admin to the configured burn address", + expectedState: [`proxy admin == ${manifest.legacy.burnAdmin}`], + }); + } else { + const proxyAdmin = new Contract(admin, ["function owner() view returns (address)"], provider); + manifest.legacy.proxyAdminOwner = normalizeAddress( + await proxyAdmin.owner({ blockTag: manifest.snapshot.safeHead }) + ); + const proxyAdminIface = new ethers.utils.Interface([ + "function changeProxyAdmin(address proxy,address newAdmin)", + ]); + operations.push({ + kind: "burn-admin", + actor: manifest.legacy.proxyAdminOwner, + target: admin, + value: "0", + data: proxyAdminIface.encodeFunctionData("changeProxyAdmin", [ + manifest.legacy.proxy, + manifest.legacy.burnAdmin, + ]), + description: "detach only the legacy proxy from its confirmed-dedicated ProxyAdmin", + expectedState: [`proxy admin == ${manifest.legacy.burnAdmin}`], + }); + } + } + return assignOperationNonces(provider, operations); +} + +async function assignOperationNonces( + provider: ethers.providers.Provider, + operations: RetirementOperation[] +): Promise { + const nextNonce = new Map(); + for (const operation of operations) { + const actor = operation.actor.toLowerCase(); + let nonce = nextNonce.get(actor); + if (nonce === undefined) nonce = await provider.getTransactionCount(operation.actor, "pending"); + operation.nonce = nonce; + nextNonce.set(actor, nonce + 1); + } + return operations; +} + +function signerMap(provider: ethers.providers.JsonRpcProvider): Map { + const keys: string[] = JSON.parse(process.env.RETIRE_SIGNER_KEYS || "[]"); + const result = new Map(); + for (const key of keys) { + const wallet = new Wallet(key, provider); + result.set(wallet.address.toLowerCase(), wallet); + } + return result; +} + +async function executeOperations( + provider: ethers.providers.JsonRpcProvider, + manifest: RetirementManifest, + manifestPath: string, + abi: unknown[] +): Promise { + if (process.env.RETIRE_CONFIRM_EXECUTE !== "YES") { + throw new Error("execute mode requires RETIRE_CONFIRM_EXECUTE=YES"); + } + if (!manifest.snapshot) throw new Error("snapshot missing"); + const contract = new Contract(manifest.legacy.proxy, abi, provider); + const deployBlock = await provider.getBlock(manifest.legacy.deployBlock); + verifyExecutePreconditions(manifest, { + stakingValue: await contract.stakingValue(), + deployBlockHash: deployBlock?.hash, + }); + if (manifest.operations.some((operation) => operation.kind === "renounce-owner" || operation.kind === "burn-admin")) { + const [active, balance, slashRemaining] = await Promise.all([ + contract.getActiveStakers(), + provider.getBalance(manifest.legacy.proxy), + contract.slashRemaining(), + ]); + if (active.length > 0 || !balance.isZero() || !slashRemaining.isZero()) { + throw new Error("irreversible retirement gate changed after planning"); + } + for (const candidate of manifest.candidates) { + const [whitelisted, withdrawal] = await Promise.all([ + contract.whitelist(candidate.address), + contract.withdrawals(candidate.address), + ]); + if (whitelisted || !withdrawal.isZero()) { + throw new Error(`irreversible retirement gate changed for ${candidate.address}`); + } + } + } + const signers = signerMap(provider); + for (const operation of manifest.operations) { + if (!signers.has(operation.actor.toLowerCase())) { + throw new Error(`missing signer for ${operation.kind} actor ${operation.actor}`); + } + if (!Number.isSafeInteger(operation.nonce) || operation.nonce! < 0) { + throw new Error(`missing deterministic nonce for ${operation.kind} actor ${operation.actor}`); + } + } + // Resolve every actor and nonce before broadcasting so execution never partially applies a stale plan. + const firstNonceByActor = new Map(); + for (const operation of manifest.operations) { + const actor = operation.actor.toLowerCase(); + if (!firstNonceByActor.has(actor)) firstNonceByActor.set(actor, operation.nonce!); + } + for (const [actor, nonce] of firstNonceByActor) { + const current = await provider.getTransactionCount(actor, "pending"); + if (current !== nonce) throw new Error(`planned nonce changed for ${actor}: expected ${nonce}, got ${current}`); + } + for (const operation of manifest.operations) { + const signer = signers.get(operation.actor.toLowerCase())!; + const response = await signer.sendTransaction({ + to: operation.target, + data: operation.data, + value: BigNumber.from(operation.value), + nonce: operation.nonce, + }); + const receipt = await response.wait(); + if (receipt.status !== 1) throw new Error(`${operation.kind} transaction failed`); + operation.txHash = receipt.transactionHash; + operation.receiptBlockHash = receipt.blockHash; + fs.writeFileSync(manifestPath, JSON.stringify(manifest, null, 2)); + } +} + +async function dryRunOperations( + provider: ethers.providers.JsonRpcProvider, + manifest: RetirementManifest +): Promise { + if (!manifest.snapshot) throw new Error("snapshot missing"); + for (const operation of manifest.operations) { + try { + await provider.call( + { + from: operation.actor, + to: operation.target, + data: operation.data, + value: BigNumber.from(operation.value), + }, + manifest.snapshot.safeHead + ); + } catch (error) { + throw new Error( + `dry-run simulation failed for ${operation.kind} (${operation.description}): ${ + error instanceof Error ? error.message : String(error) + }` + ); + } + } +} + +async function verifyRelayEvidence( + provider: ethers.providers.Provider, + relays: RelayEvidence[] +): Promise { + for (const relay of relays) { + if (!relay.transactionHash) continue; + const receipt = await provider.getTransactionReceipt(relay.transactionHash); + relay.success = Boolean(receipt && receipt.status === 1); + relay.canonical = Boolean(receipt && receipt.blockHash.toLowerCase() === relay.blockHash.toLowerCase()); + } +} + +function parseArgs(): { mode: RetirementMode; manifestPath: string } { + const mode = (process.env.RETIRE_MODE || process.argv[2] || "plan") as RetirementMode; + if (!["plan", "dry-run", "execute", "verify"].includes(mode)) throw new Error(`invalid mode ${mode}`); + const manifestPath = path.resolve(process.env.RETIRE_MANIFEST || process.argv[3] || "legacy-l1-staking-retirement.json"); + return { mode, manifestPath }; +} + +async function main(): Promise { + const { mode, manifestPath } = parseArgs(); + if (!process.env.L1_RPC_URL) throw new Error("L1_RPC_URL is required"); + if (!fs.existsSync(manifestPath)) throw new Error(`retirement manifest not found: ${manifestPath}`); + const provider = new ethers.providers.JsonRpcProvider(process.env.L1_RPC_URL); + const manifest: RetirementManifest = JSON.parse(fs.readFileSync(manifestPath, "utf8")); + if ( + !ethers.utils.isAddress(manifest.legacy.proxy) || + manifest.legacy.proxy === ZERO_ADDRESS || + !ethers.utils.isAddress(manifest.legacy.burnAdmin) || + manifest.legacy.burnAdmin === ZERO_ADDRESS || + !ethers.utils.isAddress(manifest.retirement.treasury) + ) { + throw new Error("retirement manifest contains invalid addresses"); + } + const abi = loadFrozenAbi(); + await verifyRelayEvidence(provider, manifest.relays); + await refreshManifest(provider, manifest, abi); + + console.log(JSON.stringify({ mode, snapshot: manifest.snapshot, operations: manifest.operations }, null, 2)); + if (mode === "dry-run") await dryRunOperations(provider, manifest); + if (mode === "execute") await executeOperations(provider, manifest, manifestPath, abi); + if (mode === "verify") { + const blockers = retirementBlockers(manifest); + if (blockers.length > 0) throw new Error(`retirement verification blocked:\n${blockers.join("\n")}`); + manifest.verifiedAt = new Date().toISOString(); + } + fs.writeFileSync(manifestPath, JSON.stringify(manifest, null, 2)); +} + +if (require.main === module) { + main().catch((error) => { + console.error(error); + process.exitCode = 1; + }); +} diff --git a/contracts/src/deploy-config/holesky.ts b/contracts/src/deploy-config/holesky.ts index 353d9ced9..3624dcb1b 100644 --- a/contracts/src/deploy-config/holesky.ts +++ b/contracts/src/deploy-config/holesky.ts @@ -23,22 +23,16 @@ const config = { proofRewardPercent: 70, // challenge config // TODO to be removed later - rollupProposer: '0x48446b07A04307F320fa34790116d73aD4A51810', + batchSubmitterAddresses: ['0x48446b07A04307F320fa34790116d73aD4A51810'], rollupChallenger: '0x48445d5DDb25a4f23C6d85D87f15B39Fe94e6867', // genesis config rollupGenesisStateRoot: '', - // staking config - // staking Cross-Chain config - stakingCrossChainGaslimitAdd: 1000000, - stakingCrossChainGaslimitRemove: 8000000, - - // staking initialize config - stakingLockNumber: 3, - stakingChallengeDeposit: 1, // 1 ether - stakingMinDeposit: 1, // 1 ether - stakingChallengerRewardPercentage: 100, + // submitter initialize config + submitterChallengeDeposit: 1, // 1 ether + submitterMinimumStake: 1, // 1 ether + submitterRewardPercentage: 100, // register sequencers l2SequencerAddresses: [ "0x48446b07A04307F320fa34790116d73aD4A51810", diff --git a/contracts/src/deploy-config/hoodi.ts b/contracts/src/deploy-config/hoodi.ts index d589f50a6..31db491e5 100644 --- a/contracts/src/deploy-config/hoodi.ts +++ b/contracts/src/deploy-config/hoodi.ts @@ -24,22 +24,16 @@ const config = { rollupProofWindow: 86400, proofRewardPercent: 70, // challenge config - rollupProposer: '0x48446b07A04307F320fa34790116d73aD4A51810', + batchSubmitterAddresses: ['0x48446b07A04307F320fa34790116d73aD4A51810'], rollupChallenger: '0x2910DB00C9537B90337b40D9732f4c356cbb84D0', // genesis config batchHeader: '00000000000000000000000000000000000000000000000000aa0feb482ae04f2a6b6b422932b5a2d537cd657ceb83008e87fce6ed6d290031010657f37554c781402a22917dee2f75def7ab966d7b770905398eba3c44401400000000000000000000000000000000000000000000000000000000000000000a31941eb1853862c0c38f378eb0c519e9e66f0942e39b47dca38c0437ab6b3e000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000', - // staking config - // staking Cross-Chain config - stakingCrossChainGaslimitAdd: 1000000, - stakingCrossChainGaslimitRemove: 8000000, - - // staking initialize config - stakingLockNumber: 3, - stakingChallengeDeposit: 1, // 1 ether - stakingMinDeposit: 1, // 1 ether - stakingChallengerRewardPercentage: 100, + // submitter initialize config + submitterChallengeDeposit: 1, // 1 ether + submitterMinimumStake: 1, // 1 ether + submitterRewardPercentage: 100, // register sequencers l2SequencerAddresses: [ "0x48446b07A04307F320fa34790116d73aD4A51810", diff --git a/contracts/src/deploy-config/l1.ts b/contracts/src/deploy-config/l1.ts index 285ca9c59..12525b6af 100644 --- a/contracts/src/deploy-config/l1.ts +++ b/contracts/src/deploy-config/l1.ts @@ -26,21 +26,15 @@ const config = { rollupDelayPeriod: 86400, // challenge config - rollupProposer: '0x70997970C51812dc3A010C7d01b50e0d17dc79C8', + batchSubmitterAddresses: ['0x70997970C51812dc3A010C7d01b50e0d17dc79C8'], rollupChallenger: '0x15d34AAf54267DB7D7c367839AAf71A00a2C6A65', // genesis config batchHeader: '0x00000000000000000000000000000000000000000000000000886e14341b355178d11a2c9f985f60a1a195973078b688a11aeaebb0c95db595010657f37554c781402a22917dee2f75def7ab966d7b770905398eba3c44401400000000000000000000000000000000000000000000000000000000000000002d20dde82426d971e398b3cba11ebb60d0d740b799f85e2f95fd12a1faad8e2f000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000', - // staking config - // staking Cross-Chain config - stakingCrossChainGaslimitAdd: 1000000, - stakingCrossChainGaslimitRemove: 8000000, - - // staking initialize config - stakingLockNumber: 3, - stakingChallengeDeposit: 1, // 1 ether - stakingMinDeposit: 1, // 1 ether - stakingChallengerRewardPercentage: 100, + // submitter initialize config + submitterChallengeDeposit: 1, // 1 ether + submitterMinimumStake: 1, // 1 ether + submitterRewardPercentage: 100, // register sequencers l2SequencerAddresses: [ "0x783698dCDEBdc96785c5c60ED96113612bA09c2b", diff --git a/contracts/src/deploy-config/qanetl1.ts b/contracts/src/deploy-config/qanetl1.ts index b0740d0b9..abbc0b834 100644 --- a/contracts/src/deploy-config/qanetl1.ts +++ b/contracts/src/deploy-config/qanetl1.ts @@ -21,21 +21,15 @@ const config = { rollupProofWindow: 86400, proofRewardPercent: 70, // challenge config - rollupProposer: '0x7161aE5B767Df97BAc34e5776311Ded4Cb01691d', + batchSubmitterAddresses: ['0x7161aE5B767Df97BAc34e5776311Ded4Cb01691d'], rollupChallenger: '0x71616250f7CAaa8a5DC295dc5851D6b1E49188a7', // genesis config batchHeader: '', - // staking config - // staking Cross-Chain config - stakingCrossChainGaslimitAdd: 1000000, - stakingCrossChainGaslimitRemove: 8000000, - - // staking initialize config - stakingLockNumber: 3, - stakingChallengeDeposit: 1, // 1 ether - stakingMinDeposit: 1, // 1 ether - stakingChallengerRewardPercentage: 50, + // submitter initialize config + submitterChallengeDeposit: 1, // 1 ether + submitterMinimumStake: 1, // 1 ether + submitterRewardPercentage: 50, // register sequencers l2SequencerAddresses: [ "0x25d36d94b64165f72a817ae0ff9bad2ce6914c70", diff --git a/contracts/src/deploy-config/sepolia.ts b/contracts/src/deploy-config/sepolia.ts index 3657c5811..849ff3fe2 100644 --- a/contracts/src/deploy-config/sepolia.ts +++ b/contracts/src/deploy-config/sepolia.ts @@ -22,16 +22,16 @@ const config = { rollupMinDeposit: 0.0001, rollupProofWindow: 86400, rollupGenesisBlockNumber: 0, - rollupProposer: '0x77774B33aFa9Ecef8835D25ff749ff84950e83af', + batchSubmitterAddresses: ['0x77774B33aFa9Ecef8835D25ff749ff84950e83af'], rollupChallenger: '0x77776ab9Ed7479Ff446f1E3135145e245f2B71B1', rollupGenesisStateRoot: '', withdrawRoot: '', batchHeader: '', finalizationPeriodSeconds: 600, - stakingSequencerSize: 7, - stakingLockNumber: 3, - stakingMinDeposit: 0.1, + submitterMinimumStake: 0.1, + submitterChallengeDeposit: 0.1, + submitterRewardPercentage: 100, baseFeeVaultRecipient: '0x77772Fb3715C4B8FC621e23Eda5ABbb34a9D6EAd', l1FeeVaultRecipient: '0x77772Fb3715C4B8FC621e23Eda5ABbb34a9D6EAd', diff --git a/contracts/src/deploy-config/testnetl1.ts b/contracts/src/deploy-config/testnetl1.ts index 8e0c9786f..af76da02b 100644 --- a/contracts/src/deploy-config/testnetl1.ts +++ b/contracts/src/deploy-config/testnetl1.ts @@ -18,14 +18,14 @@ const config = { rollupProofWindow: 100, rollupGenesisBlockNumber: 0, proofRewardPercent: 70, - rollupProposer: '0xE70a4102e4caA3d9B8968FFb142E6A2ceFd22Ec3', + batchSubmitterAddresses: ['0xE70a4102e4caA3d9B8968FFb142E6A2ceFd22Ec3'], rollupChallenger: '0xF730477971E88b3162ed7FD950Be63474975bdC3', rollupGenesisStateRoot: '', finalizationPeriodSeconds: 2, - stakingMinDeposit: 1, - stakingSequencerSize: 3, - stakingLockNumber: 3, + submitterMinimumStake: 1, + submitterChallengeDeposit: 1, + submitterRewardPercentage: 100, /** * Only used for SystemConfig * --------from-------- diff --git a/contracts/src/deploy-utils.ts b/contracts/src/deploy-utils.ts index ddb59efe5..8a1a2d8b3 100644 --- a/contracts/src/deploy-utils.ts +++ b/contracts/src/deploy-utils.ts @@ -114,12 +114,14 @@ export const storage = async ( contractsName: string, contractAddress: string, deployedBlockNumber: number, + metadata: Record = {}, ): Promise => { let Contract = { name: contractsName, address: contractAddress, time: new Date().toISOString(), - number: deployedBlockNumber + number: deployedBlockNumber, + ...metadata, } if (fs.existsSync(path)) { let data = fs.readFileSync(path) @@ -170,4 +172,3 @@ export const sleep = async (ms: number): Promise => { }, ms); }); }; - diff --git a/contracts/src/types.ts b/contracts/src/types.ts index 7e518addd..74574ddf2 100644 --- a/contracts/src/types.ts +++ b/contracts/src/types.ts @@ -16,8 +16,8 @@ const ContractFactoryName = { Rollup: 'Rollup', ZkEvmVerifierV1: 'ZkEvmVerifierV1', MultipleVersionRollupVerifier: 'MultipleVersionRollupVerifier', - // staking - L1Staking: 'L1Staking', + // batch submitter + Submitter: 'Submitter', // L1 sequencer L1Sequencer: 'L1Sequencer', // gateway @@ -40,8 +40,8 @@ const ProxyStorageName = { L1MessageQueueWithGasPriceOracleProxyStorageName: 'Proxy__L1MessageQueueWithGasPriceOracle', // rollup RollupProxyStorageName: 'Proxy__Rollup', - // staking - L1StakingProxyStorageName: 'Proxy__L1Staking', + // batch submitter + SubmitterProxyStorageName: 'Proxy__Submitter', // L1 sequencer L1SequencerProxyStorageName: 'Proxy__L1Sequencer', // gateway @@ -73,8 +73,9 @@ const ImplStorageName = { RollupStorageName: 'Impl__Rollup', ZkEvmVerifierV1StorageName: 'Impl__ZkEvmVerifierV1', MultipleVersionRollupVerifierStorageName: 'Impl__MultipleVersionRollupVerifier', - // staking - L1StakingStorageName: 'Impl__L1Staking', + // batch submitter + SubmitterStorageName: 'Impl__Submitter', + SubmitterInstallStorageName: 'Install__Submitter', // L1 sequencer L1SequencerStorageName: 'Impl__L1Sequencer', // gateway @@ -95,4 +96,4 @@ export { ImplStorageName, ProxyStorageName, ContractFactoryName -} \ No newline at end of file +} diff --git a/contracts/tasks/deploy.ts b/contracts/tasks/deploy.ts index a7ea6bb6a..527f9d837 100644 --- a/contracts/tasks/deploy.ts +++ b/contracts/tasks/deploy.ts @@ -13,14 +13,14 @@ import { deployContractProxiesConcurrently, deployContractImpls, deployContractImplsConcurrently, + SubmitterInstall, MessengerInit, RollupInit, GatewayInit, - StakingInit, AdminTransfer, AdminTransferConcurrently, ContractInit, - StakingRegister, + SubmitterRegister, SequencerInit, } from '../deploy/index' import { ethers } from "ethers"; @@ -97,6 +97,13 @@ task("initialize") return } + console.log('\n---------------------------------- Submitter install ----------------------------------') + err = await SubmitterInstall(hre, storagePath, deployer, config) + if (err != '') { + console.log('Submitter install failed, err: ', err) + return + } + console.log('\n---------------------------------- Messenger init ----------------------------------') err = await MessengerInit(hre, storagePath, deployer, config) if (err != '') { @@ -115,12 +122,6 @@ task("initialize") console.log('Rollup init failed, err: ', err) return } - console.log('\n---------------------------------- Staking init ----------------------------------') - err = await StakingInit(hre, storagePath, deployer, config) - if (err != '') { - console.log('Staking init failed, err: ', err) - return - } console.log('\n---------------------------------- Sequencer init ----------------------------------') err = await SequencerInit(hre, storagePath, deployer, config) if (err != '') { @@ -152,25 +153,23 @@ task("initialize") task("fund") .setAction(async (taskArgs, hre) => { - console.log('\n---------------------------------- Fund Staking ----------------------------------') + console.log('\n---------------------------------- Fund Submitters ----------------------------------') const signer = await hre.ethers.getSigners() - console.log(process.env.l2SequencerPks) - let l2SequencerPkList = JSON.parse(process.env.l2SequencerPks); - console.log(l2SequencerPkList) - for (let i = 0; i < l2SequencerPkList.length; i++) { - let sequencer = new ethers.Wallet(l2SequencerPkList[i], hre.ethers.provider) + const batchSubmitterPkList: string[] = JSON.parse(process.env.batchSubmitterPks || "[]"); + for (let i = 0; i < batchSubmitterPkList.length; i++) { + const submitter = new ethers.Wallet(batchSubmitterPkList[i], hre.ethers.provider) const tx = { - to: sequencer.address, + to: submitter.address, value: ethers.utils.parseEther("100") } - let balance = (await sequencer.getBalance()).toString() + let balance = (await submitter.getBalance()).toString() if (balance.length < 20) { let receipt = await signer[0].sendTransaction(tx) await receipt.wait() } - balance = (await sequencer.getBalance()).toString() - console.log(`${sequencer.address} has balance: ${balance}`) + balance = (await submitter.getBalance()).toString() + console.log(`${submitter.address} has balance: ${balance}`) } }) @@ -181,14 +180,19 @@ task("register") // Initialization parameters const storagePath = taskArgs.storagepath const config = hre.deployConfig - let l2SequencerPkList = JSON.parse(process.env.l2SequencerPks); - for (let i = 0; i < l2SequencerPkList.length; i++) { - let sequencer = new ethers.Wallet(l2SequencerPkList[i], hre.ethers.provider) - console.log(`\n---------------------------------- register sequencer-${i} ----------------------------------`) - console.log(`sequencer-${i}:` + await sequencer.getAddress() + ', Balance: ' + await sequencer.getBalance()) - let err = await StakingRegister(hre, storagePath, sequencer, config.l2SequencerTmKeys[i], config.l2SequencerBlsKeys[i]) + const owner = await hre.ethers.provider.getSigner(); + const batchSubmitterPkList: string[] = JSON.parse(process.env.batchSubmitterPks || "[]"); + const configuredAddresses: string[] = config.batchSubmitterAddresses || []; + for (let i = 0; i < batchSubmitterPkList.length; i++) { + const submitter = new ethers.Wallet(batchSubmitterPkList[i], hre.ethers.provider) + if (configuredAddresses[i] && configuredAddresses[i].toLowerCase() !== submitter.address.toLowerCase()) { + throw new Error(`batch submitter key ${i} does not match configured address`) + } + console.log(`\n---------------------------------- register submitter-${i} ----------------------------------`) + console.log(`submitter-${i}:` + submitter.address + ', Balance: ' + await submitter.getBalance()) + const err = await SubmitterRegister(hre, storagePath, owner, submitter) if (err != '') { - console.log(`Deploy Staking Sequencer-${i} failed, err: `, err) + console.log(`Register Submitter-${i} failed, err: `, err) return } } diff --git a/contracts/tasks/staking_upgrade.ts b/contracts/tasks/staking_upgrade.ts deleted file mode 100644 index 9a237e62d..000000000 --- a/contracts/tasks/staking_upgrade.ts +++ /dev/null @@ -1,901 +0,0 @@ -import "@nomiclabs/hardhat-web3" -import "@nomiclabs/hardhat-ethers" -import "@nomiclabs/hardhat-waffle" - -import fs from "fs"; -import assert from "assert" -import { task } from "hardhat/config" -import { ethers } from "ethers" -import { assertContractVariable, getContractAddressByName, awaitCondition, storage } from "../src/deploy-utils" -import { ImplStorageName, ProxyStorageName, ContractFactoryName } from "../src/types" -import { predeploys } from "../src" -import { hexlify } from "ethers/lib/utils"; - -task("rollup-deploy-init") - .addParam("storagepath") - .addParam("newpath") - .setAction(async (taskArgs, hre) => { - // deploy config - const storagePath = taskArgs.storagepath - const newPath = taskArgs.newpath - const config = hre.deployConfig - const chainId = config.l2ChainID - const deployer = hre.ethers.provider.getSigner() - - const ProxyFactoryName = ContractFactoryName.DefaultProxy - const IProxyFactoryName = ContractFactoryName.DefaultProxyInterface - const RollupFactoryName = ContractFactoryName.Rollup - const RollupImplStorageName = ImplStorageName.RollupStorageName - const RollupProxyStorageName = ProxyStorageName.RollupProxyStorageName - const EmptyContractAddr = getContractAddressByName(storagePath, ImplStorageName.EmptyContract) - - const RollupFactory = await hre.ethers.getContractFactory(RollupFactoryName) - const rollupNewImpl = await RollupFactory.deploy(chainId) - await rollupNewImpl.deployed() - let blockNumber = await hre.ethers.provider.getBlockNumber() - let err = await storage( - newPath, - RollupImplStorageName, - rollupNewImpl.address.toLocaleLowerCase(), - blockNumber || 0 - ) - if (err != "") { - return err - } - console.log(`Rollup new impl deploy at ${rollupNewImpl.address}`) - - // Proxy deploy - const ProxyFactory = await hre.ethers.getContractFactory(ProxyFactoryName) - // TransparentUpgradeableProxy deploy with emptyContract as impl, deployer as admin - const proxy = await ProxyFactory.deploy(EmptyContractAddr, await deployer.getAddress(), "0x") - await proxy.deployed() - blockNumber = await hre.ethers.provider.getBlockNumber() - err = await storage(newPath, RollupProxyStorageName, proxy.address.toLocaleLowerCase(), blockNumber || 0) - if (err != "") { - return err - } - console.log(`Rollup proxy deploy at ${proxy.address}`) - - // ------------------ deploy MultipleVersionRollupVerifier ----------------- - { - // deploy ZkEvmVerifierV1 - const ZkEvmVerifierV1ContractFactoryName = ContractFactoryName.ZkEvmVerifierV1 - const ZkEvmVerifierV1ImplStorageName = ImplStorageName.ZkEvmVerifierV1StorageName - const Factory = await hre.ethers.getContractFactory(ZkEvmVerifierV1ContractFactoryName) - const contract = await Factory.deploy(config.programVkey) - await contract.deployed() - console.log("%s=%s ; TX_HASH: %s", ZkEvmVerifierV1ImplStorageName, contract.address.toLocaleLowerCase(), contract.deployTransaction.hash); - let blockNumber = await hre.ethers.provider.getBlockNumber() - console.log("BLOCK_NUMBER: %s", blockNumber) - let err = await storage(newPath, ZkEvmVerifierV1ImplStorageName, contract.address.toLocaleLowerCase(), blockNumber || 0) - if (err != '') { - return err - } - - - // deploy MultipleVersionRollupVerifier - const ZkEvmVerifierV1Address = getContractAddressByName( - newPath, - ImplStorageName.ZkEvmVerifierV1StorageName - ) - const MultipleVersionRollupVerifierFactoryName = ContractFactoryName.MultipleVersionRollupVerifier - const MultipleVersionRollupVerifierImplStorageName = - ImplStorageName.MultipleVersionRollupVerifierStorageName - console.log("Deploy the MultipleVersionRollupVerifier ...") - const MultipleVersionRollupVerifierFactory = await hre.ethers.getContractFactory( - MultipleVersionRollupVerifierFactoryName - ) - const version = [0] - const verifiers = [ZkEvmVerifierV1Address] - const MultipleVersionRollupVerifierContract = await MultipleVersionRollupVerifierFactory.deploy( - version, - verifiers - ) - await MultipleVersionRollupVerifierContract.deployed() - await MultipleVersionRollupVerifierContract.initialize(proxy.address) - console.log( - "%s=%s ; TX_HASH: %s", - MultipleVersionRollupVerifierImplStorageName, - MultipleVersionRollupVerifierContract.address.toLocaleLowerCase(), - MultipleVersionRollupVerifierContract.deployTransaction.hash - ) - blockNumber = await hre.ethers.provider.getBlockNumber() - console.log("BLOCK_NUMBER: %s", blockNumber) - err = await storage( - newPath, - MultipleVersionRollupVerifierImplStorageName, - MultipleVersionRollupVerifierContract.address.toLocaleLowerCase(), - blockNumber || 0 - ) - if (err != "") { - return err - } - } - - // ------------------ rollup initialize ----------------- - { - const IRollupProxy = await hre.ethers.getContractAt(IProxyFactoryName, proxy.address, deployer) - console.log("Upgrading the Rollup proxy...") - const finalizationPeriodSeconds: number = config.finalizationPeriodSeconds - const proofWindow: number = config.rollupProofWindow - - const L1MessageQueueWithGasPriceOracleProxyAddress = getContractAddressByName( - storagePath, - ProxyStorageName.L1MessageQueueWithGasPriceOracleProxyStorageName - ) - const L1StakingProxyAddress = getContractAddressByName( - storagePath, - ProxyStorageName.L1StakingProxyStorageName - ) - const MultipleVersionRollupVerifierContractAddress = getContractAddressByName( - newPath, - ImplStorageName.MultipleVersionRollupVerifierStorageName - ) - if ( - !ethers.utils.isAddress(L1MessageQueueWithGasPriceOracleProxyAddress) || - !ethers.utils.isAddress(MultipleVersionRollupVerifierContractAddress) || - !ethers.utils.isAddress(L1StakingProxyAddress) - ) { - console.error("please check your address") - return "" - } - - // Upgrade and initialize the proxy. - await IRollupProxy.upgradeToAndCall( - rollupNewImpl.address, - RollupFactory.interface.encodeFunctionData("initialize", [ - L1StakingProxyAddress, - L1MessageQueueWithGasPriceOracleProxyAddress, - MultipleVersionRollupVerifierContractAddress, - finalizationPeriodSeconds, - proofWindow, - ]) - ) - - await awaitCondition( - async () => { - return ( - (await IRollupProxy.implementation()).toLocaleLowerCase() === - rollupNewImpl.address.toLocaleLowerCase() - ) - }, - 3000, - 1000 - ) - // params check - const contractTmp = new ethers.Contract(proxy.address, RollupFactory.interface, deployer) - await assertContractVariable(contractTmp, "l1StakingContract", L1StakingProxyAddress) - await assertContractVariable(contractTmp, "messageQueue", L1MessageQueueWithGasPriceOracleProxyAddress) - await assertContractVariable(contractTmp, "verifier", MultipleVersionRollupVerifierContractAddress) - await assertContractVariable(contractTmp, "finalizationPeriodSeconds", finalizationPeriodSeconds) - await assertContractVariable(contractTmp, "proofWindow", proofWindow) - await assertContractVariable(contractTmp, "owner", await deployer.getAddress()) - - // Wait for the transaction to execute properly. - console.log(`RollupProxy upgrade success, Rollup address at ${proxy.address}`) - } - - // ------------------ Admin transfer ----------------- - { - const deployerAddr = (await deployer.getAddress()).toLocaleLowerCase() - const ProxyAdminImplAddr = getContractAddressByName(storagePath, ImplStorageName.ProxyAdmin) - const IProxyContract = await hre.ethers.getContractAt( - ContractFactoryName.DefaultProxyInterface, - proxy.address, - deployer - ) - { - const implAddr = (await IProxyContract.implementation()).toLocaleLowerCase() - const admin = (await IProxyContract.admin()).toLocaleLowerCase() - if (implAddr === EmptyContractAddr.toLocaleLowerCase()) { - return `Proxy implementation address ${implAddr} should not be empty contract address ${EmptyContractAddr}` - } - if (admin !== deployerAddr) { - return `Proxy admin address ${admin} should deployer address ${deployerAddr}` - } - } - console.log(`change rollup admin transfer from ${deployerAddr} to ProxyAdmin ${ProxyAdminImplAddr}`) - const res = await IProxyContract.changeAdmin(ProxyAdminImplAddr) - await res.wait() - await assertContractVariable( - IProxyContract, - "admin", - ProxyAdminImplAddr, - ProxyAdminImplAddr // caller - ) - console.log(`admin transfer successful`) - } - }) - -task("rollup-import-genesis-batch") - .addParam("newpath") - .setAction(async (taskArgs, hre) => { - // deploy config - const newPath = taskArgs.newpath - const config = hre.deployConfig - const deployer = hre.ethers.provider.getSigner() - // ------------------ rollup import genesis batch ----------------- - { - const NewRollupContractAddr = getContractAddressByName(newPath, ProxyStorageName.RollupProxyStorageName) - - const Rollup = await hre.ethers.getContractAt(ContractFactoryName.Rollup, NewRollupContractAddr, deployer) - // import genesis batch - const batchHeader: string = config.batchHeader - - // submitter and challenger - const submitter: string = config.rollupProposer - const challenger: string = config.rollupChallenger - if (!ethers.utils.isAddress(submitter) || !ethers.utils.isAddress(challenger)) { - console.error("please check your address") - return "" - } - let res = await Rollup.importGenesisBatch(batchHeader) - let rec = await res.wait() - console.log( - `importGenesisBatch(%s) ${rec.status == 1 ? "success" : "failed"}`, - batchHeader, - ) - res = await Rollup.addChallenger(challenger) - rec = await res.wait() - console.log(`addChallenger(%s) ${rec.status == 1 ? "success" : "failed"}`, challenger) - const batch = await Rollup.batchBaseStore(0) - assert(batch.batchHash.toLowerCase() != "", `[FATAL] import genesis batch failed`) - } - }) - -task("l1mq-upgrade") - .addParam("storagepath") - .addParam("newpath") - .setAction(async (taskArgs, hre) => { - // deploy config - const storagePath = taskArgs.storagepath - const newPath = taskArgs.newpath - - const L1MessageQueueWithGasPriceOracleFactoryName = ContractFactoryName.L1MessageQueueWithGasPriceOracle - const L1MessageQueueWithGasPriceOracleProxyAddress = getContractAddressByName( - storagePath, - ProxyStorageName.L1MessageQueueWithGasPriceOracleProxyStorageName - ) - const L1CrossDomainMessengerProxyAddress = getContractAddressByName( - storagePath, - ProxyStorageName.L1CrossDomainMessengerProxyStorageName - ) - const EnforcedTxGatewayAddress = getContractAddressByName( - storagePath, - ProxyStorageName.EnforcedTxGatewayProxyStorageName - ) - const ProxyAdminAddress = getContractAddressByName(storagePath, ImplStorageName.ProxyAdmin) - - // deploy L1MessageQueueWithGasPriceOracle impl - { - const NewRollupProxyAddress = getContractAddressByName(newPath, ProxyStorageName.RollupProxyStorageName) - - const Factory = await hre.ethers.getContractFactory(L1MessageQueueWithGasPriceOracleFactoryName) - const contract = await Factory.deploy( - L1CrossDomainMessengerProxyAddress, - NewRollupProxyAddress, - EnforcedTxGatewayAddress - ) - await contract.deployed() - console.log( - "%s=%s ; TX_HASH: %s", - ImplStorageName.L1MessageQueueWithGasPriceOracle, - contract.address.toLocaleLowerCase(), - contract.deployTransaction.hash - ) - const blockNumber = await hre.ethers.provider.getBlockNumber() - console.log("BLOCK_NUMBER: %s", blockNumber) - const err = await storage( - newPath, - ImplStorageName.L1MessageQueueWithGasPriceOracle, - contract.address.toLocaleLowerCase(), - blockNumber || 0 - ) - if (err != "") { - return err - } - } - - // L1MessageQueueWithGasPriceOracle proxy upgrade - { - const L1MessageQueueWithGasPriceOracleNewImplAddress = getContractAddressByName( - newPath, - ImplStorageName.L1MessageQueueWithGasPriceOracle - ) - - const ProxyAdminFactory = await hre.ethers.getContractFactory(ContractFactoryName.ProxyAdmin) - const proxyAdmin = ProxyAdminFactory.attach(ProxyAdminAddress) - const res = await proxyAdmin.upgrade( - L1MessageQueueWithGasPriceOracleProxyAddress, - L1MessageQueueWithGasPriceOracleNewImplAddress - ) - const rec = await res.wait() - console.log(`upgrade l1MessageQueueWithGasPriceOracle.rollup ${rec.status === 1}`) - } - }) - -task("l1cdm-upgrade") - .addParam("storagepath") - .addParam("newpath") - .setAction(async (taskArgs, hre) => { - // deploy config - const storagePath = taskArgs.storagepath - const newPath = taskArgs.newpath - - const L1CrossDomainMessengerFactoryName = ContractFactoryName.L1CrossDomainMessenger - const L1CrossDomainMessengerImplStorageName = ImplStorageName.L1CrossDomainMessengerStorageName - const ProxyAdminAddress = getContractAddressByName(storagePath, ImplStorageName.ProxyAdmin) - const L1CrossDomainMessengerProxyAddress = getContractAddressByName( - storagePath, - ProxyStorageName.L1CrossDomainMessengerProxyStorageName - ) - - // deploy l1CrossDomainMessenger impl - { - const Factory = await hre.ethers.getContractFactory(L1CrossDomainMessengerFactoryName) - const contract = await Factory.deploy() - await contract.deployed() - console.log( - "%s=%s ; TX_HASH: %s", - L1CrossDomainMessengerImplStorageName, - contract.address.toLocaleLowerCase(), - contract.deployTransaction.hash - ) - const blockNumber = await hre.ethers.provider.getBlockNumber() - console.log("BLOCK_NUMBER: %s", blockNumber) - const err = await storage( - newPath, - L1CrossDomainMessengerImplStorageName, - contract.address.toLocaleLowerCase(), - blockNumber || 0 - ) - if (err != "") { - return err - } - } - - // l1CrossDomainMessenger proxy upgrade - { - const L1CrossDomainMessengerNewImplAddress = getContractAddressByName( - newPath, - L1CrossDomainMessengerImplStorageName - ) - const ProxyAdminFactory = await hre.ethers.getContractFactory(ContractFactoryName.ProxyAdmin) - const proxyAdmin = ProxyAdminFactory.attach(ProxyAdminAddress) - const res = await proxyAdmin.upgrade( - L1CrossDomainMessengerProxyAddress, - L1CrossDomainMessengerNewImplAddress - ) - const rec = await res.wait() - console.log(`upgrade l1CrossDomainMessenger ${rec.status === 1}`) - } - - // l1CrossDomainMessenger update Rollup address - { - const NewRollupProxyAddress = getContractAddressByName(newPath, ProxyStorageName.RollupProxyStorageName) - - const L1CDMFactory = await hre.ethers.getContractFactory(L1CrossDomainMessengerFactoryName) - const l1CrossDomainMessenger = L1CDMFactory.attach(L1CrossDomainMessengerProxyAddress) - const res = await l1CrossDomainMessenger.updateRollup(NewRollupProxyAddress) - const rec = await res.wait() - console.log(`update l1CrossDomainMessenger.rollup ${rec.status === 1}`) - } - }) - - -task("l1staking-deploy-init") - .addParam("storagepath") - .addParam("newpath") - .setAction(async (taskArgs, hre) => { - // deploy config - const storagePath = taskArgs.storagepath - const newPath = taskArgs.newpath - const config = hre.deployConfig - const deployer = hre.ethers.provider.getSigner() - - const ProxyFactoryName = ContractFactoryName.DefaultProxy - const L1StakingProxyStorageName = ProxyStorageName.L1StakingProxyStorageName - const WhitelistImplAddress = getContractAddressByName(storagePath, ImplStorageName.Whitelist) - const EmptyContractAddr = getContractAddressByName(storagePath, ImplStorageName.EmptyContract) - const L1CrossDomainMessengerProxyAddress = getContractAddressByName(storagePath, ProxyStorageName.L1CrossDomainMessengerProxyStorageName) - - // deploy L1Staking proxy - { - const ProxyFactory = await hre.ethers.getContractFactory(ProxyFactoryName) - // TransparentUpgradeableProxy deploy with emptyContract as impl, deployer as admin - const proxy = await ProxyFactory.deploy(EmptyContractAddr, await deployer.getAddress(), "0x") - await proxy.deployed() - const blockNumber = await hre.ethers.provider.getBlockNumber() - const err = await storage(newPath, L1StakingProxyStorageName, proxy.address.toLocaleLowerCase(), blockNumber || 0) - if (err != "") { - console.log(`deploy L1Staking proxy failed ${err}`) - return err - } - console.log(`L1Staking proxy deploy at ${proxy.address}`) - } - - // deploy impl - { - const Factory = await hre.ethers.getContractFactory(ContractFactoryName.L1Staking) - const contract = await Factory.deploy(L1CrossDomainMessengerProxyAddress) - await contract.deployed() - console.log("%s=%s ; TX_HASH: %s", ImplStorageName.L1StakingStorageName, contract.address.toLocaleLowerCase(), contract.deployTransaction.hash); - await assertContractVariable( - contract, - 'MESSENGER', - L1CrossDomainMessengerProxyAddress - ) - await assertContractVariable( - contract, - 'OTHER_STAKING', - predeploys.L2Staking.toLowerCase() - ) - const blockNumber = await hre.ethers.provider.getBlockNumber() - console.log("BLOCK_NUMBER: %s", blockNumber) - const err = await storage(newPath, ImplStorageName.L1StakingStorageName, contract.address.toLocaleLowerCase(), blockNumber || 0) - if (err != '') { - console.log(`deploy L1Staking implemention failed ${err}`) - return err - } - } - - // upgrade - { - const RollupProxyAddress = getContractAddressByName(newPath, ProxyStorageName.RollupProxyStorageName) - // Staking config - const L1StakingProxyAddress = getContractAddressByName(newPath, ProxyStorageName.L1StakingProxyStorageName) - const L1StakingImplAddress = getContractAddressByName(newPath, ImplStorageName.L1StakingStorageName) - const L1StakingFactory = await hre.ethers.getContractFactory(ContractFactoryName.L1Staking) - const IL1StakingProxy = await hre.ethers.getContractAt(ContractFactoryName.DefaultProxyInterface, L1StakingProxyAddress, deployer) - - console.log('Upgrading the Staking proxy...') - const admin: string = config.contractAdmin - const stakingChallengerRewardPercentage: number = config.stakingChallengerRewardPercentage - const limit: number = config.stakingMinDeposit - const lock: number = config.stakingLockNumber - const gasLimitAdd: number = config.stakingCrossChainGaslimitAdd - const gasLimitRemove: number = config.stakingCrossChainGaslimitRemove - - if (!ethers.utils.isAddress(admin) - || lock <= 0 - || limit <= 0 - || gasLimitAdd <= 0 - || gasLimitRemove <= 0 - || stakingChallengerRewardPercentage > 100 - || stakingChallengerRewardPercentage <= 0 - ) { - console.error('please check your address') - return '' - } - - // Upgrade and initialize the proxy. - await IL1StakingProxy.upgradeToAndCall( - L1StakingImplAddress, - L1StakingFactory.interface.encodeFunctionData('initialize', [ - RollupProxyAddress, - hre.ethers.utils.parseEther(limit.toString()), - hre.ethers.utils.parseEther(lock.toString()), - stakingChallengerRewardPercentage, - gasLimitAdd, - gasLimitRemove, - ]) - ) - - await awaitCondition( - async () => { - return ( - (await IL1StakingProxy.implementation()).toLocaleLowerCase() === L1StakingImplAddress.toLocaleLowerCase() - ) - }, - 3000, - 1000 - ) - - const contractTmp = new ethers.Contract( - L1StakingProxyAddress, - L1StakingFactory.interface, - deployer, - ) - - await assertContractVariable( - contractTmp, - 'rollupContract', - RollupProxyAddress - ) - await assertContractVariable( - contractTmp, - 'rewardPercentage', - stakingChallengerRewardPercentage - ) - await assertContractVariable( - contractTmp, - 'stakingValue', - hre.ethers.utils.parseEther(limit.toString()) - ) - await assertContractVariable( - contractTmp, - 'withdrawalLockBlocks', - hre.ethers.utils.parseEther(lock.toString()) - ) - await assertContractVariable( - contractTmp, - 'gasLimitAddStaker', - gasLimitAdd - ) - await assertContractVariable( - contractTmp, - 'gasLimitRemoveStakers', - gasLimitRemove - ) - await assertContractVariable( - contractTmp, - 'owner', - await deployer.getAddress(), - ) - // Wait for the transaction to execute properly. - console.log('StakingProxy upgrade success') - } - - // ------------------ Admin transfer ----------------- - { - const L1StakingProxyAddress = getContractAddressByName(newPath, ProxyStorageName.L1StakingProxyStorageName) - const deployerAddr = (await deployer.getAddress()).toLocaleLowerCase() - const ProxyAdminImplAddr = getContractAddressByName(storagePath, ImplStorageName.ProxyAdmin) - const IProxyContract = await hre.ethers.getContractAt( - ContractFactoryName.DefaultProxyInterface, - L1StakingProxyAddress, - deployer - ) - { - const implAddr = (await IProxyContract.implementation()).toLocaleLowerCase() - const admin = (await IProxyContract.admin()).toLocaleLowerCase() - if (implAddr === EmptyContractAddr.toLocaleLowerCase()) { - return `Proxy implementation address ${implAddr} should not be empty contract address ${EmptyContractAddr}` - } - if (admin !== deployerAddr) { - return `Proxy admin address ${admin} should deployer address ${deployerAddr}` - } - } - console.log(`change L1Staking admin transfer from ${deployerAddr} to ProxyAdmin ${ProxyAdminImplAddr}`) - const res = await IProxyContract.changeAdmin(ProxyAdminImplAddr) - await res.wait() - await assertContractVariable( - IProxyContract, - "admin", - ProxyAdminImplAddr, - ProxyAdminImplAddr // caller - ) - console.log(`admin transfer successful`) - } - - // set L1Staking address to gasPriceOracle whitelist - { - const L1StakingProxyAddress = getContractAddressByName(newPath, ProxyStorageName.L1StakingProxyStorageName) - const WhitelistCheckerImpl = await hre.ethers.getContractAt(ContractFactoryName.Whitelist, WhitelistImplAddress, deployer) - let addList = [L1StakingProxyAddress] - const res = await WhitelistCheckerImpl.updateWhitelistStatus(addList, true) - await res.wait() - for (let i = 0; i < addList.length; i++) { - let res = await WhitelistCheckerImpl.isSenderAllowed(addList[i]) - if (res != true) { - console.error('whitelist check failed') - return '' - } - } - console.log(`add ${addList} to whitelist success`) - } - - // set staker whitelist - { - const L1StakingProxyAddress = getContractAddressByName(newPath, ProxyStorageName.L1StakingProxyStorageName) - const L1Staking = await hre.ethers.getContractAt(ContractFactoryName.L1Staking, L1StakingProxyAddress, deployer) - const whiteListAdd = config.l2SequencerAddresses - // set sequencer to white list - await L1Staking.updateWhitelist(whiteListAdd, []) - for (let i = 0; i < config.l2SequencerAddresses.length; i++) { - // Wait for the transaction to execute properly. - await awaitCondition( - async () => { - return ( - await L1Staking.whitelist(config.l2SequencerAddresses[i]) === true - ) - }, - 3000, - 1000 - ) - console.log(`address ${config.l2SequencerAddresses[i]} is in white list`) - } - } - }) - -task("check-params") - .addParam("storagepath") - .addParam("newpath") - .setAction(async (taskArgs, hre) => { - const storagePath = taskArgs.storagepath - const newPath = taskArgs.newpath - - const L1MessageQueueWithGasPriceOracleProxyAddress = getContractAddressByName( - storagePath, - ProxyStorageName.L1MessageQueueWithGasPriceOracleProxyStorageName - ) - const L1CrossDomainMessengerProxyAddress = getContractAddressByName( - storagePath, - ProxyStorageName.L1CrossDomainMessengerProxyStorageName - ) - const RollupNewProxyAddress = getContractAddressByName(newPath, ProxyStorageName.RollupProxyStorageName) - - const L1CDMFactory = await hre.ethers.getContractFactory(ContractFactoryName.L1CrossDomainMessenger) - const l1CrossDomainMessenger = L1CDMFactory.attach(L1CrossDomainMessengerProxyAddress) - let res = await l1CrossDomainMessenger.rollup() - assert( - RollupNewProxyAddress.toLowerCase() === res.toLowerCase(), - `l1CrossDomainMessenger assert rollup address assert failed, expect ${RollupNewProxyAddress}, actual ${res}` - ) - - const L1MQFactory = await hre.ethers.getContractFactory(ContractFactoryName.L1MessageQueueWithGasPriceOracle) - const l1mq = L1MQFactory.attach(L1MessageQueueWithGasPriceOracleProxyAddress) - res = await l1mq.ROLLUP_CONTRACT() - assert( - RollupNewProxyAddress.toLowerCase() === res.toLowerCase(), - `l1mq assert rollup address assert failed, expect ${RollupNewProxyAddress}, actual ${res}` - ) - console.log("Check new rollup address success") - }) - -// test command -// rm -rf ./deployFile.json && \ -// yarn hardhat deploy --storagepath ./deployFile.json --network l1 && \ -// yarn hardhat initialize --storagepath ./deployFile.json --network l1 && \ -// yarn hardhat fund --network l1 && \ -// yarn hardhat register --storagepath ./deployFile.json --network l1 && \ -// rm -rf ./newFile.json && \ -// yarn hardhat rollup-deploy-init --storagepath ./deployFile.json --newpath ./newFile.json --network l1 && \ -// yarn hardhat l1mq-upgrade --storagepath ./deployFile.json --newpath ./newFile.json --network l1 && \ -// yarn hardhat l1cdm-upgrade --storagepath ./deployFile.json --newpath ./newFile.json --network l1 && \ -// yarn hardhat check-params --storagepath ./deployFile.json --newpath ./newFile.json --network l1 && \ -// yarn hardhat l1staking-deploy-init --storagepath ./deployFile.json --newpath ./newFile.json --network l1 && \ -// yarn hardhat register --storagepath ./newFile.json --network l1 - -// immutable upgrade test -task("impl-test") - .setAction(async (taskArgs, hre) => { - const deployer = hre.ethers.provider.getSigner() - const V1Factory = await hre.ethers.getContractFactory("TestUpgradeV1") - const V2Factory = await hre.ethers.getContractFactory("TestUpgradeV2") - - const v1Impl = await V1Factory.deploy() - await v1Impl.deployed() - const v2Impl = await V2Factory.deploy() - await v2Impl.deployed() - console.log(`V1 and V2 impl deploy success and v1: ${v1Impl.address} , v2: ${v2Impl.address}`) - - const ProxyAdminFactory = await hre.ethers.getContractFactory(ContractFactoryName.ProxyAdmin) - const proxyAdmin = await ProxyAdminFactory.deploy() - await proxyAdmin.deployed() - const ProxyFactory = await hre.ethers.getContractFactory(ContractFactoryName.DefaultProxy) - const proxy = await ProxyFactory.deploy(v1Impl.address, await deployer.getAddress(), "0x") - await proxy.deployed() - const IProxyContract = await hre.ethers.getContractAt( - ContractFactoryName.DefaultProxyInterface, - proxy.address, - deployer - ) - - // transfer owner to proxy admin - { - const res = await IProxyContract.changeAdmin(proxyAdmin.address) - const rec = await res.wait() - console.log(`proxy admin change to proxy admin ${rec.status === 1}`) - } - - const consoleParams = async (factory) => { - const contractTmp = new ethers.Contract( - proxy.address, - factory.interface, - hre.ethers.provider, - ) - let va = await contractTmp.va({ from: hre.ethers.constants.AddressZero }) - let vb = await contractTmp.vb({ from: hre.ethers.constants.AddressZero }) - let vc = await contractTmp.vc({ from: hre.ethers.constants.AddressZero }) - let version = await contractTmp.version({ from: hre.ethers.constants.AddressZero }) - console.log(`va ${va} ; vb ${vb} ; vc ${vc} ; version ${version}`) - } - let contract = new ethers.Contract( - proxy.address, - V1Factory.interface, - deployer, - ) - let res = await contract.setVersion(100) - let rec = await res.wait() - console.log(`update version to 100 ${rec.status === 1}`) - await consoleParams(V1Factory) - - // upgrade - { - const res = await proxyAdmin.upgrade(proxy.address, v2Impl.address) - const rec = await res.wait() - console.log(`upgrade to v2 impl ${rec.status === 1}`) - } - contract = new ethers.Contract( - proxy.address, - V2Factory.interface, - deployer, - ) - res = await contract.setVersion(101) - rec = await res.wait() - console.log(`update version to 101 ${rec.status === 1}`) - res = await contract.setOtherVersion(99) - rec = await res.wait() - console.log(`update otherVersion to 99 ${rec.status === 1}`) - console.log("upgrade success") - await consoleParams(V2Factory) - }) - -// ------------------------------------ L2 Upgrade ------------------------------------ -task("gov-upgrade") - .addParam("l2config") - .setAction(async (taskArgs, hre) => { - const data = fs.readFileSync(taskArgs.l2config); - // @ts-ignore - const l2Config = JSON.parse(data); - const ProxyAdminFactory = await hre.ethers.getContractFactory(ContractFactoryName.ProxyAdmin) - const proxyAdmin = ProxyAdminFactory.attach(predeploys.ProxyAdmin) - - const GovFactory = await hre.ethers.getContractFactory("Gov") - const newGovImpl = await GovFactory.deploy() - await newGovImpl.deployed() - const res = await proxyAdmin.upgradeAndCall( - predeploys.Gov, - newGovImpl.address, - GovFactory.interface.encodeFunctionData('initializeV2', [ - l2Config.govVotingDuration, - l2Config.govBatchBlockInterval, - l2Config.govBatchMaxBytes, - l2Config.govBatchTimeout, - l2Config.govBatchMaxChunks, - l2Config.govRollupEpoch, - ]) - ) - const rec = await res.wait() - console.log(`gov upgrade ${rec.status === 1}`) - }) - -task("distribute-deploy") - .setAction(async (taskArgs, hre) => { - const ProxyAdminFactory = await hre.ethers.getContractFactory(ContractFactoryName.ProxyAdmin) - const proxyAdmin = ProxyAdminFactory.attach(predeploys.ProxyAdmin) - - const DistributeFactory = await hre.ethers.getContractFactory("Distribute") - const distribute = await DistributeFactory.deploy() - await distribute.deployed() - const res = await proxyAdmin.upgradeAndCall( - predeploys.Distribute, - distribute.address, - DistributeFactory.interface.encodeFunctionData('initialize', []) - ) - const rec = await res.wait() - console.log(`distribute upgrade ${rec.status === 1}, new impl ${distribute.address}`) - }) - -task("morph-token-deploy") - .addParam("l2config") - .setAction(async (taskArgs, hre) => { - const data = fs.readFileSync(taskArgs.l2config); - // @ts-ignore - const l2Config = JSON.parse(data); - const ProxyAdminFactory = await hre.ethers.getContractFactory(ContractFactoryName.ProxyAdmin) - const proxyAdmin = ProxyAdminFactory.attach(predeploys.ProxyAdmin) - - const MorphTokenFactory = await hre.ethers.getContractFactory("MorphToken") - const morphToken = await MorphTokenFactory.deploy() - await morphToken.deployed() - - const res = await proxyAdmin.upgradeAndCall( - predeploys.MorphToken, - morphToken.address, - MorphTokenFactory.interface.encodeFunctionData('initialize', [ - l2Config.morphTokenName, - l2Config.morphTokenSymbol, - l2Config.morphTokenOwner, - hre.ethers.utils.parseEther(l2Config.morphTokenInitialSupply.toString()), - l2Config.morphTokenDailyInflationRate, - ]) - ) - const rec = await res.wait() - console.log(`morphToken upgrade ${rec.status === 1}, new impl ${morphToken.address}`) - }) - -task("sequencer-deploy") - .addParam("l2config") - .setAction(async (taskArgs, hre) => { - const data = fs.readFileSync(taskArgs.l2config); - // @ts-ignore - const l2Config = JSON.parse(data); - const ProxyAdminFactory = await hre.ethers.getContractFactory(ContractFactoryName.ProxyAdmin) - const proxyAdmin = ProxyAdminFactory.attach(predeploys.ProxyAdmin) - - const SequencerFactory = await hre.ethers.getContractFactory("Sequencer") - const sequencer = await SequencerFactory.deploy() - await sequencer.deployed() - - // l2Config - let addresses = [] - for (let i = 0; i < l2Config.l2StakingAddresses.length; i++) { - addresses.push(l2Config.l2StakingAddresses[i]) - } - const res = await proxyAdmin.upgradeAndCall( - predeploys.Sequencer, - sequencer.address, - SequencerFactory.interface.encodeFunctionData( - 'initialize', - [addresses]) - ) - const rec = await res.wait() - console.log(`sequencer upgrade ${rec.status === 1}, new impl ${sequencer.address}`) - }) - -task("record-deploy") - .addParam("l2config") - .setAction(async (taskArgs, hre) => { - const data = fs.readFileSync(taskArgs.l2config); - // @ts-ignore - const l2Config = JSON.parse(data); - - const ProxyAdminFactory = await hre.ethers.getContractFactory(ContractFactoryName.ProxyAdmin) - const proxyAdmin = ProxyAdminFactory.attach(predeploys.ProxyAdmin) - - const RecordFactory = await hre.ethers.getContractFactory("Record") - const record = await RecordFactory.deploy() - await record.deployed() - const res = await proxyAdmin.upgradeAndCall( - predeploys.Record, - record.address, - RecordFactory.interface.encodeFunctionData('initialize', [ - l2Config.recordOracleAddress, - l2Config.recordNextBatchSubmissionIndex, - ]) - ) - const rec = await res.wait() - console.log(`Record upgrade ${rec.status === 1}, new impl ${record.address}`) - }) - -task("l2-staking-deploy") - .addParam("newpath") - .addParam("l2config") - .setAction(async (taskArgs, hre) => { - const newPath = taskArgs.newpath - const data = fs.readFileSync(taskArgs.l2config); - // @ts-ignore - const l2Config = JSON.parse(data); - const L1StakingProxyAddr = getContractAddressByName(newPath, ProxyStorageName.L1StakingProxyStorageName) - - const ProxyAdminFactory = await hre.ethers.getContractFactory(ContractFactoryName.ProxyAdmin) - const proxyAdmin = ProxyAdminFactory.attach(predeploys.ProxyAdmin) - - const L2StakingFactory = await hre.ethers.getContractFactory("L2Staking") - const staking = await L2StakingFactory.deploy(L1StakingProxyAddr) - await staking.deployed() - - let infos = [] - for (let i = 0; i < l2Config.l2StakingAddresses.length; i++) { - let info = { - addr: l2Config.l2StakingAddresses[i], - tmKey: l2Config.l2StakingTmKeys[i], - blsKey: l2Config.l2StakingBlsKeys[i], - } - infos.push(info) - } - const res = await proxyAdmin.upgradeAndCall( - predeploys.L2Staking, - staking.address, - L2StakingFactory.interface.encodeFunctionData('initialize', [ - l2Config.l2StakingSequencerMaxSize, - l2Config.l2StakingUnDelegatedLockEpochs, - l2Config.l2StakingRewardStartTime, - infos, - ]) - ) - const rec = await res.wait() - console.log(`L2Staking upgrade ${rec.status === 1}, new impl ${staking.address}`) - }) \ No newline at end of file From d91248953a742e08475acda64b617d5c26a18610 Mon Sep 17 00:00:00 2001 From: Segue Date: Tue, 11 Aug 2026 20:31:47 +0800 Subject: [PATCH 04/16] feat(node): use static validators after upgrade --- node/cmd/node/main.go | 60 +++++++++--- node/core/centralization_cleanup_test.go | 113 +++++++++++++++++++++++ node/core/config.go | 60 +++++++++++- node/core/executor.go | 92 +++++++++++------- node/core/sequencers.go | 36 +++++--- node/derivation/config.go | 5 - node/derivation/config_test.go | 26 ------ node/flags/flags.go | 17 +--- 8 files changed, 305 insertions(+), 104 deletions(-) create mode 100644 node/core/centralization_cleanup_test.go diff --git a/node/cmd/node/main.go b/node/cmd/node/main.go index 08c1c5c4d..e9148ffaf 100644 --- a/node/cmd/node/main.go +++ b/node/cmd/node/main.go @@ -2,6 +2,7 @@ package main import ( "context" + "encoding/binary" "fmt" "net" "net/http" @@ -17,6 +18,7 @@ import ( "github.com/morph-l2/go-ethereum/crypto" "github.com/morph-l2/go-ethereum/ethclient" "github.com/prometheus/client_golang/prometheus/promhttp" + tmconfig "github.com/tendermint/tendermint/config" tmcrypto "github.com/tendermint/tendermint/crypto" tmlog "github.com/tendermint/tendermint/libs/log" tmnode "github.com/tendermint/tendermint/node" @@ -136,6 +138,9 @@ func L2NodeMain(ctx *cli.Context) error { if err != nil { return err } + if err := restorePersistedUpgradeHeight(tmCfg); err != nil { + return fmt.Errorf("restore persisted sequencer upgrade height: %w", err) + } // Load derivation config here (not just before the switch below): its // VerifyMode decides whether this node runs the Tendermint consensus node, @@ -161,25 +166,14 @@ func L2NodeMain(ctx *cli.Context) error { pubKey, _ = tmVal.GetPubKey() } - // Reuse the shared syncer instance -- DevSequencer mode is the only path - // that pulls a syncer out of NewExecutor, so we hand back the same one - // rather than letting NewExecutor open a second store + syncer. + // Reuse the shared syncer instance. NewExecutor starts it independently of + // validator membership, and Derivation.Start may safely invoke Start again. newSyncerFunc := func() (*sync.Syncer, error) { return syncer, nil } executor, err = node.NewExecutor(newSyncerFunc, nodeConfig, pubKey) if err != nil { return err } - // Eagerly start the L1 message syncer for post-upgrade sequencer nodes that - // are NOT in the PBFT validator set (separated-deployment / HA cluster). - // In the combined-deployment case, updateSequencerSet already started the - // syncer inside NewExecutor, so SetSyncer is a no-op there. - if signer != nil && executor.Syncer() == nil { - executor.SetSyncer(syncer) - syncer.Start() - nodeConfig.Logger.Info("L1 syncer start", "reason", "post-upgrade always start") - } - haService, err = initHAService(ctx, home, nodeConfig.Logger) if err != nil { return err @@ -296,6 +290,46 @@ func L2NodeMain(ctx *cli.Context) error { return nil } +// upgradeHeightSnapshotStore holds the height restored from Tendermint's state +// DB while NewExecutor is constructed. SetupNode subsequently rewires the +// upgrade package to the live DB. An unexpected early write fails hard instead +// of pretending that critical consensus state was persisted. +type upgradeHeightSnapshotStore struct { + height int64 +} + +func (s upgradeHeightSnapshotStore) Get([]byte) ([]byte, error) { + if s.height < 0 { + return nil, nil + } + var encoded [8]byte + binary.BigEndian.PutUint64(encoded[:], uint64(s.height)) + return encoded[:], nil +} + +func (upgradeHeightSnapshotStore) Set([]byte, []byte) error { + return fmt.Errorf("live Tendermint upgrade store is not wired yet") +} + +// restorePersistedUpgradeHeight makes the persisted H available before +// NewExecutor performs its initial validator lookup. Tendermint normally wires +// this store inside NewNode, which is necessarily constructed after Executor. +func restorePersistedUpgradeHeight(tmCfg *tmconfig.Config) error { + stateDB, err := tmnode.DefaultDBProvider(&tmnode.DBContext{ID: "state", Config: tmCfg}) + if err != nil { + return err + } + if err := upgrade.SetStore(stateDB); err != nil { + _ = stateDB.Close() + return err + } + height := upgrade.UpgradeBlockHeight() + if err := stateDB.Close(); err != nil { + return err + } + return upgrade.SetStore(upgradeHeightSnapshotStore{height: height}) +} + // initHAService builds the HA config and creates the HAService. // Loading order: defaults → config file → flag overrides → auto-resolve → validate. // Returns nil (no error) if HA is not enabled. diff --git a/node/core/centralization_cleanup_test.go b/node/core/centralization_cleanup_test.go new file mode 100644 index 000000000..c7f0ea859 --- /dev/null +++ b/node/core/centralization_cleanup_test.go @@ -0,0 +1,113 @@ +package node + +import ( + "bytes" + "errors" + "os" + "strings" + "testing" + + "github.com/stretchr/testify/require" + "github.com/tendermint/tendermint/upgrade" + + nodesync "morph-l2/node/sync" +) + +func TestParseStaticValidatorTmKeys(t *testing.T) { + keyA := strings.Repeat("11", tmKeySize) + keyB := strings.Repeat("22", tmKeySize) + + keysAB, hashAB, err := parseStaticValidatorTmKeys([]string{keyB, "0x" + keyA}) + require.NoError(t, err) + require.Len(t, keysAB, 2) + require.True(t, bytes.Compare(keysAB[0], keysAB[1]) < 0, "keys must be canonicalized lexicographically") + + keysBA, hashBA, err := parseStaticValidatorTmKeys([]string{keyA + "," + keyB}) + require.NoError(t, err) + require.Equal(t, keysAB, keysBA) + require.Equal(t, hashAB, hashBA, "input order must not change the network key-set hash") + + for _, tc := range []struct { + name string + values []string + }{ + {name: "empty"}, + {name: "short", values: []string{"01"}}, + {name: "invalid hex", values: []string{strings.Repeat("zz", tmKeySize)}}, + {name: "duplicate", values: []string{keyA, "0x" + keyA}}, + } { + t.Run(tc.name, func(t *testing.T) { + _, _, err := parseStaticValidatorTmKeys(tc.values) + require.Error(t, err) + }) + } +} + +func TestValidatorSetAtHeightUpgradeBoundary(t *testing.T) { + originalHeight := upgrade.UpgradeBlockHeight() + upgrade.SetUpgradeBlockHeight(100) + t.Cleanup(func() { upgrade.SetUpgradeBlockHeight(originalHeight) }) + + legacyKeys := [][]byte{bytes.Repeat([]byte{0x11}, tmKeySize)} + staticKeys := [][]byte{bytes.Repeat([]byte{0x22}, tmKeySize)} + legacyCalls := 0 + executor := &Executor{ + staticValidatorTmKeys: staticKeys, + legacyValidatorSetAtHeightFunc: func(int64) ([][]byte, error) { + legacyCalls++ + return cloneValidatorKeys(legacyKeys), nil + }, + } + + for _, height := range []uint64{99, 100} { + got, err := executor.validatorSetAtHeight(height) + require.NoError(t, err) + require.Equal(t, legacyKeys, got) + } + got, err := executor.validatorSetAtHeight(101) + require.NoError(t, err) + require.Equal(t, staticKeys, got) + require.Equal(t, 2, legacyCalls, "H+1 must not call either historical contract") + + got[0][0] ^= 0xff + require.Equal(t, byte(0x22), executor.staticValidatorTmKeys[0][0], "callers must not mutate the configured set") +} + +func TestEnsureSyncerStartedIndependentOfValidatorMembership(t *testing.T) { + created := 0 + fakeSyncer := nodesync.NewFakeSyncer(nil) + executor := &Executor{ + newSyncerFunc: func() (*nodesync.Syncer, error) { + created++ + return fakeSyncer, nil + }, + } + + require.NoError(t, executor.ensureSyncerStarted()) + require.NoError(t, executor.ensureSyncerStarted()) + require.Equal(t, 1, created) + require.Same(t, fakeSyncer, executor.l1MsgReader) +} + +func TestEnsureSyncerStartedKeepsFactoryError(t *testing.T) { + created := 0 + wantErr := errors.New("factory failed") + executor := &Executor{ + newSyncerFunc: func() (*nodesync.Syncer, error) { + created++ + return nil, wantErr + }, + } + + require.ErrorIs(t, executor.ensureSyncerStarted(), wantErr) + require.ErrorIs(t, executor.ensureSyncerStarted(), wantErr) + require.Equal(t, 1, created) + require.Nil(t, executor.l1MsgReader) +} + +func TestAllValidatorEntryPointsUseUnifiedResolver(t *testing.T) { + source, err := os.ReadFile("executor.go") + require.NoError(t, err) + require.Equal(t, 3, strings.Count(string(source), ".validatorSetAtHeight("), + "NewExecutor and both DeliverBlock branches must use the H/H+1 resolver") +} diff --git a/node/core/config.go b/node/core/config.go index c3276cdec..72e91d37d 100644 --- a/node/core/config.go +++ b/node/core/config.go @@ -1,16 +1,20 @@ package node import ( + "bytes" "crypto/rand" + "encoding/hex" "errors" "fmt" "io" "os" "path/filepath" + "slices" "strings" "github.com/morph-l2/go-ethereum/common" "github.com/morph-l2/go-ethereum/common/hexutil" + gethcrypto "github.com/morph-l2/go-ethereum/crypto" tmconfig "github.com/tendermint/tendermint/config" tmflags "github.com/tendermint/tendermint/libs/cli/flags" tmlog "github.com/tendermint/tendermint/libs/log" @@ -33,7 +37,9 @@ type Config struct { L2StakingAddress common.Address `json:"l2staking_address"` MaxL1MessageNumPerBlock uint64 `json:"max_l1_message_num_per_block"` BlsKeyCheckForkHeight uint64 `json:"bls_key_check_fork_height"` - DevSequencer bool `json:"dev_sequencer"` + StaticValidatorTmKeys [][]byte `json:"static_validator_tm_keys"` + StaticValidatorKeySetHash common.Hash `json:"static_validator_key_set_hash"` + StaticValidatorKeySource string `json:"static_validator_key_source"` Logger tmlog.Logger `json:"logger"` } @@ -142,9 +148,13 @@ func (c *Config) SetCliContext(ctx *cli.Context) error { } } - if ctx.GlobalIsSet(flags.DevSequencer.Name) { - c.DevSequencer = ctx.GlobalBool(flags.DevSequencer.Name) + keys, keySetHash, err := parseStaticValidatorTmKeys(ctx.GlobalStringSlice(flags.StaticValidatorTmKeys.Name)) + if err != nil { + return fmt.Errorf("invalid --%s: %w", flags.StaticValidatorTmKeys.Name, err) } + c.StaticValidatorTmKeys = keys + c.StaticValidatorKeySetHash = keySetHash + c.StaticValidatorKeySource = "cli/env" if ctx.GlobalIsSet(flags.MainnetFlag.Name) { c.BlsKeyCheckForkHeight = MainnetBlsKeyCheckForkHeight @@ -153,3 +163,47 @@ func (c *Config) SetCliContext(ctx *cli.Context) error { return nil } + +// parseStaticValidatorTmKeys validates and canonicalizes the network-wide +// Tendermint ed25519 validator key set. Keys are fixed-width, so hashing the +// sorted concatenation is unambiguous and gives operators a compact value to +// compare across every node before the upgrade boundary is activated. +func parseStaticValidatorTmKeys(values []string) ([][]byte, common.Hash, error) { + var encodedKeys []string + for _, value := range values { + for _, key := range strings.Split(value, ",") { + if key = strings.TrimSpace(key); key != "" { + encodedKeys = append(encodedKeys, key) + } + } + } + if len(encodedKeys) == 0 { + return nil, common.Hash{}, errors.New("at least one Tendermint ed25519 public key is required") + } + + keys := make([][]byte, 0, len(encodedKeys)) + seen := make(map[string]struct{}, len(encodedKeys)) + for i, encoded := range encodedKeys { + encoded = strings.TrimPrefix(encoded, "0x") + if len(encoded) != tmKeySize*2 { + return nil, common.Hash{}, fmt.Errorf("key %d must decode to exactly %d bytes", i, tmKeySize) + } + key, err := hex.DecodeString(encoded) + if err != nil { + return nil, common.Hash{}, fmt.Errorf("key %d is not valid hex: %w", i, err) + } + fingerprint := string(key) + if _, ok := seen[fingerprint]; ok { + return nil, common.Hash{}, fmt.Errorf("key %d duplicates another Tendermint ed25519 public key", i) + } + seen[fingerprint] = struct{}{} + keys = append(keys, key) + } + + slices.SortFunc(keys, bytes.Compare) + canonical := make([]byte, 0, len(keys)*tmKeySize) + for _, key := range keys { + canonical = append(canonical, key...) + } + return keys, gethcrypto.Keccak256Hash(canonical), nil +} diff --git a/node/core/executor.go b/node/core/executor.go index 0c581973e..304340bb6 100644 --- a/node/core/executor.go +++ b/node/core/executor.go @@ -17,6 +17,7 @@ import ( "github.com/tendermint/tendermint/crypto" "github.com/tendermint/tendermint/l2node" tmlog "github.com/tendermint/tendermint/libs/log" + "github.com/tendermint/tendermint/upgrade" "morph-l2/bindings/bindings" "morph-l2/node/sync" @@ -35,6 +36,8 @@ type Executor struct { newSyncerFunc NewSyncerFunc syncer *sync.Syncer + syncerOnce syncos.Once + syncerErr error sequencerCaller *bindings.SequencerCaller l2StakingCaller *bindings.L2StakingCaller @@ -42,10 +45,15 @@ type Executor struct { currentSeqHash *[32]byte seqTmKeySet map[[tmKeySize]byte]struct{} - nextValidators [][]byte - tmPubKey []byte - isSequencer bool - devSequencer bool + nextValidators [][]byte + staticValidatorTmKeys [][]byte + tmPubKey []byte + isSequencer bool + + // legacyValidatorSetAtHeightFunc is a test-only seam for exercising the + // persisted H/H+1 boundary without an L2 RPC. Production leaves it nil and + // uses the protected historical getter body below. + legacyValidatorSetAtHeightFunc func(int64) ([][]byte, error) blsKeyCheckForkHeight uint64 @@ -102,28 +110,30 @@ func NewExecutor(newSyncFunc NewSyncerFunc, config *Config, tmPubKey crypto.PubK nextL1MsgIndex: index, maxL1MsgNumPerBlock: config.MaxL1MessageNumPerBlock, newSyncerFunc: newSyncFunc, - devSequencer: config.DevSequencer, + staticValidatorTmKeys: cloneValidatorKeys(config.StaticValidatorTmKeys), blsKeyCheckForkHeight: config.BlsKeyCheckForkHeight, logger: logger, metrics: PrometheusMetrics("morphnode"), } - if config.DevSequencer { - executor.syncer, err = executor.newSyncerFunc() - if err != nil { - return nil, err - } - //executor.syncer.Start() - executor.l1MsgReader = executor.syncer - return executor, nil - } - - // Get current height for initial sequencer set update + // Resolve the validator set through the same persisted H/H+1 boundary used + // by both DeliverBlock paths. At H the historical contracts still apply; + // H+1 and later never call those contracts. currentHeight, err := l2Client.BlockNumber(context.Background()) if err != nil { return nil, err } - if _, err = executor.updateSequencerSet(currentHeight); err != nil { + executor.nextValidators, err = executor.validatorSetAtHeight(currentHeight) + if err != nil { + return nil, err + } + logger.Info("static validator configuration", + "keyCount", len(config.StaticValidatorTmKeys), + "keySetHash", config.StaticValidatorKeySetHash.Hex(), + "keySetHashAlgorithm", "keccak256(sorted-concat-32-byte-keys)", + "upgradeHeight", upgrade.UpgradeBlockHeight(), + "source", config.StaticValidatorKeySource) + if err := executor.ensureSyncerStarted(); err != nil { return nil, err } @@ -264,10 +274,7 @@ func (e *Executor) DeliverBlock(txs [][]byte, metaData []byte, consensusData l2n if wrappedBlock.Number <= height { e.logger.Info("block already delivered by geth (via P2P sync)", "block_number", wrappedBlock.Number) - if e.devSequencer { - return consensusData.ValidatorSet, nil - } - return e.getValidatorsAtHeight(int64(wrappedBlock.Number)) + return e.validatorSetAtHeight(wrappedBlock.Number) } // We only accept the continuous blocks for now. @@ -307,11 +314,9 @@ func (e *Executor) DeliverBlock(txs [][]byte, metaData []byte, consensusData l2n e.updateNextL1MessageIndex(l2Block) - newValidatorSet := consensusData.ValidatorSet - if !e.devSequencer { - if newValidatorSet, err = e.updateSequencerSet(l2Block.Number); err != nil { - return nil, err - } + newValidatorSet, err := e.validatorSetAtHeight(l2Block.Number) + if err != nil { + return nil, err } e.metrics.Height.Set(float64(l2Block.Number)) @@ -375,17 +380,14 @@ func (e *Executor) L2Client() *types.RetryableClient { } // Syncer returns the current L1 message syncer instance, or nil if not yet -// initialized. Callers can use this to detect whether the syncer has already -// been set up (e.g. by updateSequencerSet in the PBFT-validator path) and -// avoid creating a duplicate. +// initialized. func (e *Executor) Syncer() *sync.Syncer { return e.syncer } // SetSyncer installs a pre-built syncer on the executor and wires it in as the -// l1MsgReader. This is intended for the V2/HA separated-deployment case, where -// a node holds a sequencer signer but is not a PBFT validator, so the normal -// lazy-init path in updateSequencerSet never fires. +// l1MsgReader. ensureSyncerStarted will start the installed instance instead +// of invoking the factory. // // The call is idempotent: if a syncer is already set, it is left untouched. func (e *Executor) SetSyncer(s *sync.Syncer) { @@ -396,6 +398,32 @@ func (e *Executor) SetSyncer(s *sync.Syncer) { e.l1MsgReader = s } +// ensureSyncerStarted wires and starts the L1 message reader independently of +// validator membership. Both this guard and Syncer.Start are idempotent: the +// former prevents duplicate construction, while the latter tolerates the +// derivation service invoking Start on the shared instance as well. +func (e *Executor) ensureSyncerStarted() error { + e.syncerOnce.Do(func() { + if e.syncer == nil { + if e.newSyncerFunc == nil { + e.syncerErr = errors.New("syncer factory is nil") + return + } + e.syncer, e.syncerErr = e.newSyncerFunc() + } + if e.syncerErr != nil { + return + } + if e.syncer == nil { + e.syncerErr = errors.New("syncer factory returned nil") + return + } + e.l1MsgReader = e.syncer + go e.syncer.Start() + }) + return e.syncerErr +} + // ============================================================================ // L2NodeV2 interface implementation for sequencer mode // ============================================================================ diff --git a/node/core/sequencers.go b/node/core/sequencers.go index 1323c090f..c19f9335e 100644 --- a/node/core/sequencers.go +++ b/node/core/sequencers.go @@ -7,10 +7,32 @@ import ( "github.com/morph-l2/go-ethereum/common" "github.com/morph-l2/go-ethereum/crypto/bls12381" "github.com/tendermint/tendermint/crypto/ed25519" + "github.com/tendermint/tendermint/upgrade" ) const tmKeySize = ed25519.PubKeySize +func cloneValidatorKeys(keys [][]byte) [][]byte { + cloned := make([][]byte, len(keys)) + for i := range keys { + cloned[i] = bytes.Clone(keys[i]) + } + return cloned +} + +// validatorSetAtHeight is the single validator data-source boundary. The +// upgrade package intentionally switches strictly after the persisted height: +// H is historical, H+1 is static. +func (e *Executor) validatorSetAtHeight(height uint64) ([][]byte, error) { + if upgrade.IsUpgraded(int64(height)) { + return cloneValidatorKeys(e.staticValidatorTmKeys), nil + } + if e.legacyValidatorSetAtHeightFunc != nil { + return e.legacyValidatorSetAtHeightFunc(int64(height)) + } + return e.getValidatorsAtHeight(int64(height)) +} + func (e *Executor) sequencerSetUpdates(height uint64) ([][]byte, error) { seqHash, err := e.sequencerCaller.SequencerSetVerifyHash(nil) if err != nil { @@ -99,19 +121,7 @@ func (e *Executor) updateSequencerSet(height uint64) ([][]byte, error) { _, isSequencer := e.seqTmKeySet[tmPKBz] if !e.isSequencer && isSequencer { - e.logger.Info("I am a sequencer, start to launch syncer") - if e.syncer == nil { - syncer, err := e.newSyncerFunc() - if err != nil { - e.logger.Error("failed to create syncer", "error", err) - return nil, err - } - e.syncer = syncer - e.l1MsgReader = syncer // syncer works as l1MsgReader - e.syncer.Start() - } else { - go e.syncer.Start() - } + e.logger.Info("I am a sequencer") } else if e.isSequencer && !isSequencer { // as the derivation always need the syncer running, it should not be stopped diff --git a/node/derivation/config.go b/node/derivation/config.go index a3f194ba8..441a1e697 100644 --- a/node/derivation/config.go +++ b/node/derivation/config.go @@ -179,11 +179,6 @@ func (c *Config) SetCliContext(ctx *cli.Context) error { } } - legacyValidatorMode := ctx.GlobalBool(flags.LegacyValidatorMode.Name) - if legacyValidatorMode { - log.Warn("--validator is deprecated; use --derivation.verify-mode=layer1") - c.VerifyMode = VerifyModeLayer1 - } if ctx.GlobalIsSet(flags.DerivationVerifyMode.Name) { c.VerifyMode = ctx.GlobalString(flags.DerivationVerifyMode.Name) } diff --git a/node/derivation/config_test.go b/node/derivation/config_test.go index 4148c0c96..b72819bc7 100644 --- a/node/derivation/config_test.go +++ b/node/derivation/config_test.go @@ -42,31 +42,6 @@ func TestVerifyMode_AcceptsExplicitModes(t *testing.T) { } } -func TestVerifyMode_LegacyValidatorAlias(t *testing.T) { - cfg := DefaultConfig() - if err := cfg.SetCliContext(newVerifyModeTestContext(t, map[string]string{ - flags.LegacyValidatorMode.Name: "true", - })); err != nil { - t.Fatalf("legacy validator alias rejected: %v", err) - } - if cfg.VerifyMode != VerifyModeLayer1 { - t.Fatalf("legacy validator alias resolved to %q, want %q", cfg.VerifyMode, VerifyModeLayer1) - } -} - -func TestVerifyMode_ExplicitModeOverridesLegacyValidatorAlias(t *testing.T) { - cfg := DefaultConfig() - if err := cfg.SetCliContext(newVerifyModeTestContext(t, map[string]string{ - flags.LegacyValidatorMode.Name: "true", - flags.DerivationVerifyMode.Name: VerifyModeLocal, - })); err != nil { - t.Fatalf("explicit verify-mode rejected: %v", err) - } - if cfg.VerifyMode != VerifyModeLocal { - t.Fatalf("explicit verify-mode resolved to %q, want %q", cfg.VerifyMode, VerifyModeLocal) - } -} - func TestVerifyMode_RejectsUnknown(t *testing.T) { // "hybrid" was the old default; ensure post-removal it's rejected so // stale operator configs fail loud rather than silently falling back to @@ -150,7 +125,6 @@ func newVerifyModeTestContext(t *testing.T, values map[string]string) *cli.Conte flagSet := flag.NewFlagSet(t.Name(), flag.ContinueOnError) for _, f := range []cli.Flag{ - flags.LegacyValidatorMode, flags.DerivationVerifyMode, flags.MetricsPort, flags.L1BeaconAddr, diff --git a/node/flags/flags.go b/node/flags/flags.go index e6ed45f78..6d7277ab1 100644 --- a/node/flags/flags.go +++ b/node/flags/flags.go @@ -144,10 +144,10 @@ var ( EnvVar: prefixEnvVar("TDM_CONFIG"), } - DevSequencer = &cli.BoolFlag{ - Name: "dev-sequencer", - Usage: "explicitly specify that running as a sequencer. If it enables, the tendermint validator/batch params won't be changed no matter what happens to staking/gov contacts. Only use in dev/test mode", - EnvVar: prefixEnvVar("DEV_SEQUENCER"), + StaticValidatorTmKeys = cli.StringSliceFlag{ + Name: "static-validator-tm-key", + Usage: "Tendermint ed25519 validator public key (32-byte hex); repeat for every network validator", + EnvVar: prefixEnvVar("STATIC_VALIDATOR_TM_KEYS"), } MockEnabled = &cli.BoolFlag{ @@ -156,12 +156,6 @@ var ( EnvVar: prefixEnvVar("MOCK_SEQUENCER"), } - LegacyValidatorMode = cli.BoolFlag{ - Name: "validator", - Usage: "Deprecated compatibility alias for --derivation.verify-mode=layer1", - EnvVar: prefixEnvVar("VALIDATOR"), - } - // derivation RollupContractAddress = cli.StringFlag{ Name: "derivation.rollupAddress", @@ -401,10 +395,9 @@ var Flags = []cli.Flag{ DBCache, DBFreezer, - DevSequencer, + StaticValidatorTmKeys, TendermintConfigPath, MockEnabled, - LegacyValidatorMode, // derivation RollupContractAddress, From dea8882d03f6429dfa41da0f5f922b23e8166fb2 Mon Sep 17 00:00:00 2001 From: Segue Date: Tue, 11 Aug 2026 20:40:49 +0800 Subject: [PATCH 05/16] feat(batch): bootstrap from canonical static batches --- common/batch/batch_cache.go | 351 +++++-- .../batch/batch_cache_genesis_header_test.go | 10 +- common/batch/batch_cache_test.go | 9 +- common/batch/batch_header.go | 20 +- common/batch/batch_header_zero_test.go | 36 + common/batch/batch_query.go | 989 +++++++++++++----- common/batch/batch_query_test.go | 129 +++ common/batch/batch_restart_test.go | 492 +-------- common/batch/batch_storage_test.go | 2 +- common/batch/canonical_bootstrap_test.go | 386 +++++++ common/batch/commit_test.go | 19 +- common/batch/config.go | 39 + common/batch/config_test.go | 36 + common/batch/helpers_test.go | 9 + common/batch/interfaces.go | 21 +- common/batch/l2_gov.go | 51 +- common/batch/snapshot.go | 101 ++ 17 files changed, 1793 insertions(+), 907 deletions(-) create mode 100644 common/batch/batch_header_zero_test.go create mode 100644 common/batch/batch_query_test.go create mode 100644 common/batch/canonical_bootstrap_test.go create mode 100644 common/batch/config.go create mode 100644 common/batch/config_test.go create mode 100644 common/batch/snapshot.go diff --git a/common/batch/batch_cache.go b/common/batch/batch_cache.go index 74b987a70..3ead3194c 100644 --- a/common/batch/batch_cache.go +++ b/common/batch/batch_cache.go @@ -18,6 +18,7 @@ import ( "github.com/morph-l2/go-ethereum/crypto" "github.com/morph-l2/go-ethereum/eth" "github.com/morph-l2/go-ethereum/log" + "github.com/morph-l2/go-ethereum/rpc" "morph-l2/common/blob" ) @@ -69,11 +70,11 @@ type BatchCache struct { l2Clients L2MultiClient rollupContract RollupBatchReader l2Gov L2GovCaller + l1Snapshot *confirmedL1Snapshot // config - batchTimeOut uint64 - blockInterval uint64 - maxBlobCount int + batchConfig BatchConfig + maxBlobCount int // replayL1CommittedBatches is true while InitAndSyncFromRollup is rebuilding committed batches from L2. replayL1CommittedBatches atomic.Bool @@ -94,6 +95,7 @@ func NewBatchCache( isBatchUpgraded func(uint64) bool, isBatchV2Upgraded func(uint64) bool, maxBlobCount int, + batchConfig BatchConfig, l1Client L1HeaderClient, l2Clients L2MultiClient, rollupContract RollupBatchReader, @@ -143,22 +145,32 @@ func NewBatchCache( rollupContract: rollupContract, l2Gov: l2Gov, batchStorage: NewBatchStorage(ldb), + batchConfig: batchConfig, maxBlobCount: maxBlobCount, } } func (bc *BatchCache) Init() error { - err := bc.updateBatchConfigFromGov() - if err != nil { + bc.initMu.Lock() + defer bc.initMu.Unlock() + return bc.withConfirmedL1Snapshot(bc.initLocked) +} + +func (bc *BatchCache) initLocked() error { + if err := bc.batchConfig.Validate(); err != nil { return err } + log.Info("Using static batch config", + "block_interval_blocks", bc.batchConfig.BlockInterval, + "timeout_seconds", bc.batchConfig.Timeout, + "config_hash", bc.batchConfig.Hash()) ci, fi, err := bc.getBatchStatusFromContract() if err != nil { return fmt.Errorf("get batch status from rollup failed err: %w", err) } - headerBytes, err := bc.getLastFinalizeBatchHeaderFromRollupByIndex(fi.Uint64()) + headerBytes, err := bc.getCanonicalCommittedBatchHeaderByIndex(fi.Uint64()) if err != nil { - return fmt.Errorf("get last finalize batch header err: %w", err) + return fmt.Errorf("get canonical finalized batch header err: %w", err) } // Initialize BatchCache parent batch information @@ -169,12 +181,59 @@ func (bc *BatchCache) Init() error { } lastPackedBlockHeight, err := headerBytes.LastBlockNumber() if err != nil { - store, err := bc.rollupContract.BatchDataStore(nil, fi) + store, err := bc.rollupContract.BatchDataStore(bc.snapshotCallOpts(), fi) if err != nil { return err } lastPackedBlockHeight = store.BlockNumber.Uint64() } + store, err := bc.rollupContract.BatchDataStore(bc.snapshotCallOpts(), fi) + if err != nil { + return fmt.Errorf("read finalized batch %d metadata: %w", fi.Uint64(), err) + } + if store.BlockNumber == nil || store.BlockNumber.Uint64() != lastPackedBlockHeight { + return fmt.Errorf("finalized batch %d L2 block mismatch: header=%d store=%v", fi.Uint64(), lastPackedBlockHeight, store.BlockNumber) + } + finalizedRoot, err := bc.rollupContract.FinalizedStateRoots(bc.snapshotCallOpts(), fi) + if err != nil { + return fmt.Errorf("read finalized state root for batch %d: %w", fi.Uint64(), err) + } + if common.Hash(finalizedRoot) != prevStateRoot { + return fmt.Errorf("finalized batch %d state root mismatch: header=%s rollup=%s", fi.Uint64(), prevStateRoot, common.Hash(finalizedRoot)) + } + l2Anchor, err := bc.l2Clients.BlockByNumber(context.Background(), new(big.Int).SetUint64(lastPackedBlockHeight)) + if err != nil { + return fmt.Errorf("read finalized L2 anchor block %d: %w", lastPackedBlockHeight, err) + } + if l2Anchor == nil { + return fmt.Errorf("read finalized L2 anchor block %d: nil block", lastPackedBlockHeight) + } + if l2Anchor.Root() != prevStateRoot { + return fmt.Errorf("finalized L2 anchor state root mismatch: block=%d header=%s l2=%s", lastPackedBlockHeight, prevStateRoot, l2Anchor.Root()) + } + l2AnchorHash := l2Anchor.Hash() + withdrawalRoot, err := headerBytes.WithdrawalRoot() + if err != nil { + return fmt.Errorf("read finalized withdrawal root: %w", err) + } + anchorWithdrawalRoot, err := bc.l2Gov.GetTreeRoot(&bind.CallOpts{Context: bc.ctx, BlockNumber: new(big.Int).SetUint64(lastPackedBlockHeight)}) + if err != nil { + return fmt.Errorf("read L2 withdrawal root at finalized anchor %d: %w", lastPackedBlockHeight, err) + } + if common.Hash(anchorWithdrawalRoot) != withdrawalRoot { + return fmt.Errorf("finalized withdrawal root mismatch: block=%d header=%s l2=%s", lastPackedBlockHeight, withdrawalRoot, common.Hash(anchorWithdrawalRoot)) + } + recheckedL2Anchor, err := bc.l2Clients.BlockByNumber(context.Background(), new(big.Int).SetUint64(lastPackedBlockHeight)) + if err != nil { + return fmt.Errorf("re-read finalized L2 anchor block %d: %w", lastPackedBlockHeight, err) + } + if recheckedL2Anchor == nil || recheckedL2Anchor.Hash() != l2AnchorHash { + actual := common.Hash{} + if recheckedL2Anchor != nil { + actual = recheckedL2Anchor.Hash() + } + return fmt.Errorf("finalized L2 anchor changed during bootstrap: block=%d expected=%s actual=%s", lastPackedBlockHeight, l2AnchorHash, actual) + } totalL1MessagePopped, err := headerBytes.TotalL1MessagePopped() if err != nil { return fmt.Errorf("get total l1 message popped err: %w", err) @@ -184,6 +243,7 @@ func (bc *BatchCache) Init() error { bc.prevStateRoot = prevStateRoot bc.lastPackedBlockHeight = lastPackedBlockHeight bc.currentBlockNumber = lastPackedBlockHeight + bc.currentBlockHash = l2AnchorHash bc.totalL1MessagePopped = totalL1MessagePopped bc.mu.Unlock() log.Info("Start assemble batch", "start batch", fi.Uint64(), "end batch", ci.Uint64()) @@ -205,30 +265,33 @@ func (bc *BatchCache) setInitialized() { func (bc *BatchCache) InitFromRollupByRange() error { bc.initMu.Lock() defer bc.initMu.Unlock() - if bc.isInitialized() { + return bc.withConfirmedL1Snapshot(func() error { + if bc.isInitialized() { + return nil + } + if err := bc.initLocked(); err != nil { + return err + } + if err := bc.assembleUnFinalizeBatchHeaderFromL2Blocks(); err != nil { + return err + } + bc.setInitialized() + log.Info("Initialized batch cache success") return nil - } - err := bc.Init() - if err != nil { - return err - } - err = bc.assembleUnFinalizeBatchHeaderFromL2Blocks() - if err != nil { - return err - } - bc.setInitialized() - log.Info("Initialized batch cache success") - return nil + }) } func (bc *BatchCache) InitAndSyncFromDatabase() error { bc.initMu.Lock() defer bc.initMu.Unlock() + return bc.withConfirmedL1Snapshot(bc.initAndSyncFromDatabaseLocked) +} + +func (bc *BatchCache) initAndSyncFromDatabaseLocked() error { if bc.isInitialized() { return nil } - err := bc.updateBatchConfigFromGov() - if err != nil { + if err := bc.batchConfig.Validate(); err != nil { return err } ci, fi, err := bc.getBatchStatusFromContract() @@ -251,9 +314,11 @@ func (bc *BatchCache) InitAndSyncFromDatabase() error { maxIndex = idx } } - // check batch hash with the batch that already rollup by submitter - for i := fi.Uint64(); i <= ci.Uint64(); i++ { - batchHash, err := bc.rollupContract.CommittedBatches(nil, new(big.Int).SetUint64(i)) + // Sealed storage contains batches after the finalized anchor; the anchor + // itself is reconstructed from canonical L1 and is never a local sealed + // checkpoint. Validate only the committed, unfinalized suffix. + for i := fi.Uint64() + 1; i > fi.Uint64() && i <= ci.Uint64(); i++ { + batchHash, err := bc.rollupContract.CommittedBatches(bc.snapshotCallOpts(), new(big.Int).SetUint64(i)) if err != nil { return err } @@ -290,7 +355,7 @@ func (bc *BatchCache) InitAndSyncFromDatabase() error { "commitIndex", ci.Uint64(), "finalizeIndex", fi.Uint64()) return bc.deleteBatchStorageAndInitFromRollupLocked() } - store, err := bc.rollupContract.BatchDataStore(nil, new(big.Int).SetUint64(latestBatchIndex)) + store, err := bc.rollupContract.BatchDataStore(bc.snapshotCallOpts(), new(big.Int).SetUint64(latestBatchIndex)) if err != nil { log.Error("Failed to load latest batch index from rollup", "error", err, @@ -317,7 +382,7 @@ func (bc *BatchCache) InitAndSyncFromDatabase() error { func (bc *BatchCache) InitAndSyncFromRollup() error { bc.initMu.Lock() defer bc.initMu.Unlock() - return bc.initAndSyncFromRollupLocked() + return bc.withConfirmedL1Snapshot(bc.initAndSyncFromRollupLocked) } func (bc *BatchCache) initAndSyncFromRollupLocked() error { @@ -327,7 +392,7 @@ func (bc *BatchCache) initAndSyncFromRollupLocked() error { bc.replayL1CommittedBatches.Store(true) defer bc.replayL1CommittedBatches.Store(false) - err := bc.Init() + err := bc.initLocked() if err != nil { return err } @@ -375,23 +440,8 @@ func (bc *BatchCache) LatestBatchIndex() (uint64, error) { return bc.parentBatchHeader.BatchIndex() } -func (bc *BatchCache) updateBatchConfigFromGov() error { - interval, err := bc.l2Gov.BatchBlockInterval(nil) - if err != nil { - return err - } - timeout, err := bc.l2Gov.BatchTimeout(nil) - if err != nil { - return err - } - bc.batchTimeOut = timeout.Uint64() - bc.blockInterval = interval.Uint64() - log.Info("Update batch config success", "interval", interval.Uint64(), "timeout", timeout.Uint64()) - return nil -} - func (bc *BatchCache) checkBatchHashCorrect(batchIndex *big.Int, batchHash common.Hash) (bool, error) { - commitBatchHash, err := bc.rollupContract.CommittedBatches(nil, batchIndex) + commitBatchHash, err := bc.rollupContract.CommittedBatches(bc.snapshotCallOpts(), batchIndex) if err != nil { return false, err } @@ -406,11 +456,11 @@ func (bc *BatchCache) checkBatchHashCorrect(batchIndex *big.Int, batchHash commo } func (bc *BatchCache) getBatchStatusFromContract() (*big.Int, *big.Int, error) { - latestCommitBatchIndex, err := bc.rollupContract.LastCommittedBatchIndex(nil) + latestCommitBatchIndex, err := bc.rollupContract.LastCommittedBatchIndex(bc.snapshotCallOpts()) if err != nil { return nil, nil, err } - lastFinalizedBatchIndex, err := bc.rollupContract.LastFinalizedBatchIndex(nil) + lastFinalizedBatchIndex, err := bc.rollupContract.LastFinalizedBatchIndex(bc.snapshotCallOpts()) if err != nil { return nil, nil, err } @@ -419,11 +469,11 @@ func (bc *BatchCache) getBatchStatusFromContract() (*big.Int, *big.Int, error) { func (bc *BatchCache) getBatchBlockRange(batchIndex *big.Int) (uint64, uint64, error) { preIndex := new(big.Int).Sub(batchIndex, big.NewInt(1)) - preBatchStorage, err := bc.rollupContract.BatchDataStore(nil, preIndex) + preBatchStorage, err := bc.rollupContract.BatchDataStore(bc.snapshotCallOpts(), preIndex) if err != nil { return 0, 0, err } - batchStorage, err := bc.rollupContract.BatchDataStore(nil, batchIndex) + batchStorage, err := bc.rollupContract.BatchDataStore(bc.snapshotCallOpts(), batchIndex) if err != nil { return 0, 0, err } @@ -435,7 +485,7 @@ func (bc *BatchCache) getUnFinalizeBlockRange() (uint64, uint64, *big.Int, error if err != nil { return 0, 0, nil, err } - finalizeBatchStorage, err := bc.rollupContract.BatchDataStore(nil, fi) + finalizeBatchStorage, err := bc.rollupContract.BatchDataStore(bc.snapshotCallOpts(), fi) if err != nil { return 0, 0, nil, err } @@ -666,7 +716,6 @@ func (bc *BatchCache) FetchAndCacheHeader(blockNumber uint64, withdrawRoot commo // SealBatch seals the currently accumulated batch, generates batch header and stores to sealedBatches // Parameters: -// - sequencerSetVerifyHash: sequencer set verification hash (obtained from L1 contract) // - blockTimestamp: current block timestamp (used to determine batch version) // // Returns: @@ -680,7 +729,7 @@ func (bc *BatchCache) FetchAndCacheHeader(blockNumber uint64, withdrawRoot commo // replayCommittedBatchIndex, when non-nil, is the rollup batch index being re-sealed while syncing // from L1 (InitAndSyncFromRollup). After V2 multi-blob, blob capacity is capped at replayProtocolMaxBlobs // (6), not max_blob_count, without querying L1 CommitBatch logs. -func (bc *BatchCache) SealBatch(sequencerSets []byte, blockTimestamp uint64, replayCommittedBatchIndex *uint64) (uint64, BatchHeaderBytes, bool, error) { +func (bc *BatchCache) SealBatch(blockTimestamp uint64, replayCommittedBatchIndex *uint64) (uint64, BatchHeaderBytes, bool, error) { bc.mu.Lock() defer bc.mu.Unlock() @@ -722,7 +771,7 @@ func (bc *BatchCache) SealBatch(sequencerSets []byte, blockTimestamp uint64, rep ) // Create batch header - batchHeader := bc.createBatchHeader(batchDataHash, sidecar, crypto.Keccak256Hash(sequencerSets), blockTimestamp) + batchHeader := bc.createBatchHeader(batchDataHash, sidecar, blockTimestamp) // Calculate batch hash batchHash, err := batchHeader.Hash() @@ -755,26 +804,21 @@ func (bc *BatchCache) SealBatch(sequencerSets []byte, blockTimestamp uint64, rep } blockContexts := hexutil.Bytes(blockContextsData) - // Convert sequencerSetVerifyHash to bytes - currentSequencerSetBytes := hexutil.Bytes(sequencerSets) - // Get L1 message count from batch data numL1Messages := bc.batchData.l1TxNum // Store sealed batch information as RPCRollupBatch sealedBatch := ð.RPCRollupBatch{ - Version: uint(version), - Hash: batchHash, - ParentBatchHeader: parentBatchHeaderBytes, - BlockContexts: blockContexts, - CurrentSequencerSetBytes: currentSequencerSetBytes, - PrevStateRoot: bc.prevStateRoot, - PostStateRoot: bc.postStateRoot, - WithdrawRoot: bc.withdrawRoot, - LastBlockNumber: bc.lastPackedBlockHeight, - NumL1Messages: numL1Messages, - Sidecar: *sidecar, - Signatures: []eth.RPCBatchSignature{}, + Version: uint(version), + Hash: batchHash, + ParentBatchHeader: parentBatchHeaderBytes, + BlockContexts: blockContexts, + PrevStateRoot: bc.prevStateRoot, + PostStateRoot: bc.postStateRoot, + WithdrawRoot: bc.withdrawRoot, + LastBlockNumber: bc.lastPackedBlockHeight, + NumL1Messages: numL1Messages, + Sidecar: *sidecar, } bc.sealedBatches[batchIndex] = sealedBatch // Store batch header copy @@ -915,7 +959,7 @@ func (bc *BatchCache) handleBatchSealing(blockTimestamp uint64, sealBlobCap int, } // createBatchHeader creates BatchHeader -func (bc *BatchCache) createBatchHeader(dataHash common.Hash, sidecar *ethtypes.BlobTxSidecar, sequencerSetVerifyHash common.Hash, blockTimestamp uint64) BatchHeaderBytes { +func (bc *BatchCache) createBatchHeader(dataHash common.Hash, sidecar *ethtypes.BlobTxSidecar, blockTimestamp uint64) BatchHeaderBytes { blobHashes := []common.Hash{blob.EmptyVersionedHash} if sidecar != nil && len(sidecar.Blobs) > 0 { blobHashes = sidecar.BlobHashes() @@ -942,7 +986,7 @@ func (bc *BatchCache) createBatchHeader(dataHash common.Hash, sidecar *ethtypes. PrevStateRoot: bc.prevStateRoot, PostStateRoot: bc.postStateRoot, WithdrawalRoot: bc.withdrawRoot, - SequencerSetVerifyHash: sequencerSetVerifyHash, + SequencerSetVerifyHash: common.Hash{}, ParentBatchHash: parentBatchHash, } @@ -1114,10 +1158,6 @@ func (bc *BatchCache) assembleBatchHeaderFromL2Blocks( } } - sequencerSet, _, err := bc.l2Gov.GetSequencerSetBytes(callOpts) - if err != nil { - return nil, fmt.Errorf("failed to get sequencer set verify hash at block %d: %w", callOpts.BlockNumber.Uint64(), err) - } // Get the last block's timestamp for packing lastBlock, err := bc.l2Clients.BlockByNumber(ctx, big.NewInt(int64(endBlockNum))) if err != nil { @@ -1126,7 +1166,7 @@ func (bc *BatchCache) assembleBatchHeaderFromL2Blocks( blockTimestamp := lastBlock.Time() // Seal batch and generate batchHeader - batchIndex, batchHeader, reachedExpectedSize, err := bc.SealBatch(sequencerSet, blockTimestamp, replayCommittedBatchIndex) + batchIndex, batchHeader, reachedExpectedSize, err := bc.SealBatch(blockTimestamp, replayCommittedBatchIndex) if err != nil { return nil, fmt.Errorf("failed to seal batch: %w", err) } @@ -1180,22 +1220,22 @@ func (bc *BatchCache) assembleUnFinalizeBatchHeaderFromL2Blocks() error { bc.logBatchPackingProgress(startBlockNum, blockNum, startBlockTime, nowBlockTime, &progressState) // Check timeout: if elapsed time >= batchTimeOut, must seal batch immediately - // This ensures batch is sealed before exceeding the maximum timeout configured in gov contract + // This ensures batch is sealed before exceeding the configured static timeout. timeout := false - if bc.batchTimeOut > 0 { + if bc.batchConfig.Timeout > 0 { elapsedTime := nowBlockTime - startBlockTime - if elapsedTime >= bc.batchTimeOut { + if elapsedTime >= bc.batchConfig.Timeout { timeout = true log.Info("Batch timeout reached, must seal batch", "startBlock", startBlockNum, "currentBlock", blockNum, - "elapsedTime", elapsedTime, "batchTimeOut", bc.batchTimeOut) + "elapsedTime", elapsedTime, "batchTimeOut", bc.batchConfig.Timeout) } } // Check if we need to seal batch due to capacity, block interval, or timeout // check ensures batch is sealed before exceeding the maximum timeout - if exceeded || (bc.blockInterval > 0 && (blockNum-startBlockNum+1) == bc.blockInterval) || timeout { + if exceeded || (bc.batchConfig.BlockInterval > 0 && (blockNum-startBlockNum+1) == bc.batchConfig.BlockInterval) || timeout { log.Info("block exceeds limit", "start", startBlockNum, "to", blockNum-1, "exceeded", exceeded, "timeout", timeout) - batchHash, reachedExpectedSize, batchIndex, err := bc.SealBatchAndCheck(callOpts, ci) + batchHash, reachedExpectedSize, batchIndex, err := bc.SealBatchAndCheck(ci) if err != nil { return err } @@ -1225,18 +1265,14 @@ func (bc *BatchCache) assembleUnFinalizeBatchHeaderFromL2Blocks() error { return nil } -func (bc *BatchCache) SealBatchAndCheck(callOpts *bind.CallOpts, ci *big.Int) (common.Hash, bool, uint64, error) { - sequencerSetBytes, _, err := bc.l2Gov.GetSequencerSetBytes(callOpts) - if err != nil { - return common.Hash{}, false, 0, err - } +func (bc *BatchCache) SealBatchAndCheck(ci *big.Int) (common.Hash, bool, uint64, error) { lastBlock, err := bc.l2Clients.BlockByNumber(context.Background(), big.NewInt(int64(bc.lastPackedBlockHeight))) if err != nil { return common.Hash{}, false, 0, fmt.Errorf("failed to get last block %d: %w", bc.lastPackedBlockHeight, err) } blockTimestamp := lastBlock.Time() // Seal batch and generate batchHeader - batchIndex, batchHeaderBytes, reachedExpectedSize, err := bc.SealBatch(sequencerSetBytes, blockTimestamp, nil) + batchIndex, batchHeaderBytes, reachedExpectedSize, err := bc.SealBatch(blockTimestamp, nil) if err != nil { return common.Hash{}, false, 0, fmt.Errorf("failed to seal batch: %w", err) } @@ -1374,10 +1410,15 @@ func (bc *BatchCache) AssembleCurrentBatchHeader() error { callOpts := &bind.CallOpts{ Context: bc.ctx, } - endBlockNum, err := bc.l2Clients.BlockNumber(bc.ctx) + safeHead, err := bc.l2Clients.BlockByNumber(bc.ctx, big.NewInt(int64(rpc.SafeBlockNumber))) if err != nil { - return err + return fmt.Errorf("get fixed L2 safe head: %w", err) + } + if safeHead == nil { + return errors.New("get fixed L2 safe head: nil block") } + endBlockNum := safeHead.NumberU64() + safeHeadHash := safeHead.Hash() if endBlockNum < bc.currentBlockNumber { return fmt.Errorf("has reorg, should check block status current %v, now %v", bc.currentBlockNumber, endBlockNum) } @@ -1387,17 +1428,10 @@ func (bc *BatchCache) AssembleCurrentBatchHeader() error { } version, _ := bc.parentBatchHeader.Version() if version < 1 { - parentIndex, err := bc.parentBatchHeader.BatchIndex() - if err != nil { - log.Error("failed to get block index", "err", err) - return err - } - store, err := bc.rollupContract.BatchDataStore(nil, new(big.Int).SetUint64(parentIndex)) - if err != nil { - log.Error("failed to get batch store", "err", err) - return err - } - startBlockNum = store.BlockNumber.Uint64() + // V0 headers do not encode lastBlockNumber. Bootstrap already resolved + // and verified it from BatchDataStore at the fixed L1 snapshot, so retain + // that pinned value instead of issuing a new latest-state getter here. + startBlockNum = bc.lastPackedBlockHeight } else { startBlockNum, err = bc.parentBatchHeader.LastBlockNumber() if err != nil { @@ -1411,11 +1445,25 @@ func (bc *BatchCache) AssembleCurrentBatchHeader() error { return fmt.Errorf("invalid block number") } startBlockNum++ + if currentBlockNum == endBlockNum { + return bc.verifyFixedL2Head(endBlockNum, safeHeadHash) + } + priorBlock, err := bc.l2Clients.BlockByNumber(bc.ctx, new(big.Int).SetUint64(currentBlockNum)) + if err != nil { + return fmt.Errorf("read L2 bootstrap parent block %d: %w", currentBlockNum, err) + } + if priorBlock == nil || priorBlock.NumberU64() != currentBlockNum { + return fmt.Errorf("L2 gap at bootstrap parent: expected=%d got=%v", currentBlockNum, blockNumberOrNil(priorBlock)) + } + previousBlockHash := priorBlock.Hash() // Get start block once to avoid repeated queries startBlock, err := bc.l2Clients.BlockByNumber(bc.ctx, big.NewInt(int64(startBlockNum))) if err != nil { return fmt.Errorf("failed to get start block %d: %w", startBlockNum, err) } + if startBlock == nil || startBlock.NumberU64() != startBlockNum { + return fmt.Errorf("L2 gap at first bootstrap block: expected=%d got=%v", startBlockNum, blockNumberOrNil(startBlock)) + } startBlockTime := startBlock.Time() progressState := batchPackProgressState{} @@ -1438,35 +1486,44 @@ func (bc *BatchCache) AssembleCurrentBatchHeader() error { if err != nil { return fmt.Errorf("failed to get block %d: %w", blockNum, err) } + if nowBlock == nil || nowBlock.NumberU64() != blockNum { + return fmt.Errorf("L2 gap while packing bootstrap range: expected=%d got=%v", blockNum, blockNumberOrNil(nowBlock)) + } nowBlockTime := nowBlock.Time() + if nowBlock.Hash() != bc.currentProposalHash() { + return fmt.Errorf("L2 block changed while packing bootstrap range: block=%d", blockNum) + } + if nowBlock.ParentHash() != previousBlockHash { + return fmt.Errorf("L2 parent hash discontinuity during bootstrap: block=%d expectedParent=%s actualParent=%s", blockNum, previousBlockHash, nowBlock.ParentHash()) + } + previousBlockHash = nowBlock.Hash() bc.logBatchPackingProgress(startBlockNum, blockNum, startBlockTime, nowBlockTime, &progressState) // Check timeout: if elapsed time >= batchTimeOut, must seal batch immediately - // This ensures batch is sealed before exceeding the maximum timeout configured in gov contract + // This ensures batch is sealed before exceeding the configured static timeout. timeout := false - if bc.batchTimeOut > 0 { + if bc.batchConfig.Timeout > 0 { elapsedTime := nowBlockTime - startBlockTime - if elapsedTime >= bc.batchTimeOut { + if elapsedTime >= bc.batchConfig.Timeout { timeout = true log.Info("Batch timeout reached, must seal batch", "startBlock", startBlockNum, "currentBlock", blockNum, - "elapsedTime", elapsedTime, "batchTimeOut", bc.batchTimeOut) + "elapsedTime", elapsedTime, "batchTimeOut", bc.batchConfig.Timeout) } } // Check if we need to seal batch due to capacity, block interval, or timeout // check ensures batch is sealed before exceeding the maximum timeout - if exceeded || (bc.blockInterval > 0 && (blockNum-startBlockNum+1) == bc.blockInterval) || timeout { + if exceeded || (bc.batchConfig.BlockInterval > 0 && (blockNum-startBlockNum+1) == bc.batchConfig.BlockInterval) || timeout { log.Info("block exceeds limit", "start", startBlockNum, "to", blockNum, "exceeded", exceeded, "timeout", timeout) - sequencerSetBytes, _, err := bc.l2Gov.GetSequencerSetBytes(callOpts) - if err != nil { - return fmt.Errorf("failed to get sequencer set verify hash at block %d: %w", callOpts.BlockNumber.Uint64(), err) - } lastBlock, err := bc.l2Clients.BlockByNumber(context.Background(), big.NewInt(int64(bc.lastPackedBlockHeight))) if err != nil { return fmt.Errorf("failed to get last block %d: %w", bc.lastPackedBlockHeight, err) } + if lastBlock == nil || lastBlock.NumberU64() != bc.lastPackedBlockHeight { + return fmt.Errorf("L2 gap at last packed block: expected=%d got=%v", bc.lastPackedBlockHeight, blockNumberOrNil(lastBlock)) + } blockTimestamp := lastBlock.Time() - batchIndex, _, _, err := bc.SealBatch(sequencerSetBytes, blockTimestamp, nil) + batchIndex, _, _, err := bc.SealBatch(blockTimestamp, nil) if err != nil { return fmt.Errorf("failed to seal batch: %w", err) } @@ -1479,6 +1536,9 @@ func (bc *BatchCache) AssembleCurrentBatchHeader() error { if err != nil { return fmt.Errorf("failed to get start block %d: %w", startBlockNum, err) } + if startBlock == nil || startBlock.NumberU64() != startBlockNum { + return fmt.Errorf("L2 gap after sealed bootstrap batch: expected=%d got=%v", startBlockNum, blockNumberOrNil(startBlock)) + } startBlockTime = startBlock.Time() progressState = batchPackProgressState{} } @@ -1488,6 +1548,34 @@ func (bc *BatchCache) AssembleCurrentBatchHeader() error { return fmt.Errorf("failed to pack block %d: %w", blockNum, err) } } + return bc.verifyFixedL2Head(endBlockNum, safeHeadHash) +} + +func blockNumberOrNil(block *ethtypes.Block) interface{} { + if block == nil { + return nil + } + return block.NumberU64() +} + +func (bc *BatchCache) currentProposalHash() common.Hash { + bc.mu.RLock() + defer bc.mu.RUnlock() + return bc.currentBlockHash +} + +func (bc *BatchCache) verifyFixedL2Head(number uint64, expected common.Hash) error { + head, err := bc.l2Clients.BlockByNumber(bc.ctx, new(big.Int).SetUint64(number)) + if err != nil { + return fmt.Errorf("re-read fixed L2 safe head %d: %w", number, err) + } + if head == nil || head.Hash() != expected { + actual := common.Hash{} + if head != nil { + actual = head.Hash() + } + return fmt.Errorf("L2 safe head changed during bootstrap: number=%d expected=%s actual=%s", number, expected, actual) + } return nil } @@ -1510,13 +1598,13 @@ func (bc *BatchCache) logBatchPackingProgress(startBlockNum, currentBlockNum, st } timePercent := uint64(0) - if bc.batchTimeOut > 0 { - timePercent = progressPercent(elapsedTime, bc.batchTimeOut) + if bc.batchConfig.Timeout > 0 { + timePercent = progressPercent(elapsedTime, bc.batchConfig.Timeout) } blockPercent := uint64(0) - if bc.blockInterval > 0 { - blockPercent = progressPercent(packedBlocks, bc.blockInterval) + if bc.batchConfig.BlockInterval > 0 { + blockPercent = progressPercent(packedBlocks, bc.batchConfig.BlockInterval) } blobPercent := uint64(0) @@ -1585,7 +1673,40 @@ func maxUint64(values ...uint64) uint64 { func (bc *BatchCache) DeleteBatchStorageAndInitFromRollup() error { bc.initMu.Lock() defer bc.initMu.Unlock() - return bc.deleteBatchStorageAndInitFromRollupLocked() + return bc.withConfirmedL1Snapshot(bc.deleteBatchStorageAndInitFromRollupLocked) +} + +// ResetCanonicalBootstrap discards every locally reconstructed/sealed batch and +// clears all in-memory progress. It is used only when cold-start validation +// fails after writes may already have occurred, so the next attempt cannot +// continue from a partial or reorged checkpoint. +func (bc *BatchCache) ResetCanonicalBootstrap() error { + bc.initMu.Lock() + defer bc.initMu.Unlock() + + if err := bc.batchStorage.DeleteAllSealedBatches(); err != nil { + if forceErr := bc.batchStorage.ForceDeleteAllSealedBatches(); forceErr != nil { + return fmt.Errorf("reset canonical bootstrap storage (normal error: %v): %w", err, forceErr) + } + } + + bc.mu.Lock() + defer bc.mu.Unlock() + bc.initDone = false + bc.sealedBatches = make(map[uint64]*eth.RPCRollupBatch) + bc.sealedBatchHeaders = make(map[uint64]*BatchHeaderBytes) + bc.parentBatchHeader = nil + bc.prevStateRoot = common.Hash{} + bc.batchData = nil + bc.totalL1MessagePopped = 0 + bc.postStateRoot = common.Hash{} + bc.withdrawRoot = common.Hash{} + bc.lastPackedBlockHeight = 0 + bc.currentBlockNumber = 0 + bc.currentBlockHash = common.Hash{} + bc.ClearCurrent() + bc.replayL1CommittedBatches.Store(false) + return nil } func (bc *BatchCache) deleteBatchStorageAndInitFromRollupLocked() error { diff --git a/common/batch/batch_cache_genesis_header_test.go b/common/batch/batch_cache_genesis_header_test.go index 2b984e8f3..6a8db254f 100644 --- a/common/batch/batch_cache_genesis_header_test.go +++ b/common/batch/batch_cache_genesis_header_test.go @@ -19,10 +19,6 @@ var ( globalGenesisBatchHeader *BatchHeaderBytes globalGenesisBatchHeaderErr error globalGenesisBatchHeaderOnce sync.Once - - // Global overrides for cache batch config in tests (instead of updateBatchConfigFromGov). - globalBatchTimeoutForTest uint64 = 10000000 - globalBlockIntervalForTest uint64 = 10000 ) func ensureGlobalGenesisBatchHeader() error { @@ -55,9 +51,6 @@ func initCacheWithGlobalGenesisHeader(cache *BatchCache) error { if globalGenesisBatchHeader == nil { return ErrKeyNotFound } - // Use global test knobs instead of querying gov config from chain. - cache.batchTimeOut = globalBatchTimeoutForTest - cache.blockInterval = globalBlockIntervalForTest headerCopy := make(BatchHeaderBytes, len(*globalGenesisBatchHeader)) copy(headerCopy, *globalGenesisBatchHeader) cache.parentBatchHeader = &headerCopy @@ -86,9 +79,10 @@ func initCacheWithGlobalGenesisHeader(cache *BatchCache) error { } func TestBatchCacheInitWithGlobalGenesisHeader(t *testing.T) { + requireBatchIntegration(t) testDB := openTestKV(t) a := func(uint64) bool { return true } - cache := NewBatchCache(nil, a, 3, l1Client, &SingleL2Client{C: l2Client}, rollupContract, l2Gov, testDB) + cache := NewBatchCache(nil, a, 3, testBatchConfig, l1Client, &SingleL2Client{C: l2Client}, rollupContract, l2Gov, testDB) var batchCacheSyncMu sync.Mutex done := make(chan error, 1) diff --git a/common/batch/batch_cache_test.go b/common/batch/batch_cache_test.go index 3965fa4aa..72456b382 100644 --- a/common/batch/batch_cache_test.go +++ b/common/batch/batch_cache_test.go @@ -26,8 +26,9 @@ func init() { } func TestBatchCacheInitServer(t *testing.T) { + requireBatchIntegration(t) testDB := openTestKV(t) - cache := NewBatchCache(nil, nil, 2, l1Client, &SingleL2Client{C: l2Client}, rollupContract, l2Gov, testDB) + cache := NewBatchCache(nil, nil, 2, testBatchConfig, l1Client, &SingleL2Client{C: l2Client}, rollupContract, l2Gov, testDB) var batchCacheSyncMu sync.Mutex @@ -59,15 +60,17 @@ func TestBatchCacheInitServer(t *testing.T) { } func TestBatchCacheInit(t *testing.T) { + requireBatchIntegration(t) testDB := openTestKV(t) - cache := NewBatchCache(nil, nil, 2, l1Client, &SingleL2Client{C: l2Client}, rollupContract, l2Gov, testDB) + cache := NewBatchCache(nil, nil, 2, testBatchConfig, l1Client, &SingleL2Client{C: l2Client}, rollupContract, l2Gov, testDB) err := cache.InitAndSyncFromRollup() require.NoError(t, err) } func TestBatchCacheInitByBlockRange(t *testing.T) { + requireBatchIntegration(t) testDB := openTestKV(t) - cache := NewBatchCache(nil, nil, 2, l1Client, &SingleL2Client{C: l2Client}, rollupContract, l2Gov, testDB) + cache := NewBatchCache(nil, nil, 2, testBatchConfig, l1Client, &SingleL2Client{C: l2Client}, rollupContract, l2Gov, testDB) err := cache.InitFromRollupByRange() require.NoError(t, err) } diff --git a/common/batch/batch_header.go b/common/batch/batch_header.go index 71faaf9fd..e5f1fbc4d 100644 --- a/common/batch/batch_header.go +++ b/common/batch/batch_header.go @@ -42,7 +42,25 @@ func (b BatchHeaderBytes) validate() error { switch version { case BatchHeaderVersion0: if len(b) != expectedLengthV0 { - return ErrInvalidBatchHeaderLength + // Frozen legacy V0 headers append one 32-byte skipped-message + // bitmap word per 256 popped messages. Current V0 headers are the + // fixed 249-byte form, so accept both exact historical encodings. + if len(b) < expectedLengthV0 { + return ErrInvalidBatchHeaderLength + } + l1MessagePopped := binary.BigEndian.Uint64(b[9:17]) + bitmapWords := l1MessagePopped / 256 + if l1MessagePopped%256 != 0 { + bitmapWords++ + } + maxInt := uint64(^uint(0) >> 1) + if bitmapWords > (maxInt-expectedLengthV0)/32 { + return ErrInvalidBatchHeaderLength + } + legacyLength := expectedLengthV0 + int(bitmapWords)*32 + if len(b) != legacyLength { + return ErrInvalidBatchHeaderLength + } } case BatchHeaderVersion1: if len(b) != expectedLengthV1 { diff --git a/common/batch/batch_header_zero_test.go b/common/batch/batch_header_zero_test.go new file mode 100644 index 000000000..b14f69950 --- /dev/null +++ b/common/batch/batch_header_zero_test.go @@ -0,0 +1,36 @@ +package batch + +import ( + "testing" + + "github.com/morph-l2/go-ethereum/common" + "github.com/stretchr/testify/require" +) + +func TestNewBatchHeaderSequencerSetVerifyHashIsZeroForAllVersions(t *testing.T) { + tests := []struct { + name string + upgraded bool + v2 bool + wantLen int + }{ + {name: "v0", wantLen: expectedLengthV0}, + {name: "v1", upgraded: true, wantLen: expectedLengthV1}, + {name: "v2", upgraded: true, v2: true, wantLen: expectedLengthV2}, + } + + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + cache := &BatchCache{ + isBatchUpgraded: func(uint64) bool { return test.upgraded }, + isBatchV2Upgraded: func(uint64) bool { return test.v2 }, + } + header := cache.createBatchHeader(common.Hash{1}, nil, 1) + verifyHash, err := header.SequencerSetVerifyHash() + require.NoError(t, err) + require.Equal(t, common.Hash{}, verifyHash) + require.Equal(t, BatchHeaderBytes(make([]byte, 32)), header[185:217]) + require.Len(t, header, test.wantLen) + }) + } +} diff --git a/common/batch/batch_query.go b/common/batch/batch_query.go index 21d463294..ffad39280 100644 --- a/common/batch/batch_query.go +++ b/common/batch/batch_query.go @@ -2,349 +2,834 @@ package batch import ( "bytes" - "context" + "encoding/binary" "errors" "fmt" "math/big" + "sort" + "strings" "morph-l2/bindings/bindings" + "morph-l2/common/blob" "github.com/morph-l2/go-ethereum/accounts/abi" - "github.com/morph-l2/go-ethereum/accounts/abi/bind" + "github.com/morph-l2/go-ethereum/common" + ethtypes "github.com/morph-l2/go-ethereum/core/types" + "github.com/morph-l2/go-ethereum/crypto" ) -// getLastFinalizeBatchHeaderFromRollupByIndex gets the batch header with the specified index from the rollup contract's FinalizeBatch event -// The finalizeBatch function only receives one parameter: batchHeader bytes, so it can be parsed directly from the transaction -// Query is limited to 10000 block heights, starting from the latest height and querying backwards until data is found -func (bc *BatchCache) getLastFinalizeBatchHeaderFromRollupByIndex(index uint64) (*BatchHeaderBytes, error) { - // Get the current latest block height - latestBlock, err := bc.l1Client.BlockNumber(context.Background()) - if err != nil { - return nil, fmt.Errorf("failed to get latest block number: %w", err) +type canonicalCommitEvent struct { + batchIndex uint64 + batchHash common.Hash + raw ethtypes.Log +} + +type canonicalRevertEvent struct { + batchIndex uint64 + batchHash common.Hash + raw ethtypes.Log +} + +func logIdentityLess(a, b ethtypes.Log) bool { + if a.BlockNumber != b.BlockNumber { + return a.BlockNumber < b.BlockNumber + } + if a.TxIndex != b.TxIndex { + return a.TxIndex < b.TxIndex } + return a.Index < b.Index +} - const blockRange = uint64(10000) // Query 10000 blocks each time - var endBlock uint64 = latestBlock - var startBlock uint64 +// selectCanonicalCommit follows the shared resolver contract from spec 9.3: +// discard commits at/before the last revert and select the last remaining log +// whose hash equals committedBatches[index] at the fixed snapshot. +func selectCanonicalCommit( + index uint64, + expectedHash common.Hash, + commits []canonicalCommitEvent, + reverts []canonicalRevertEvent, +) (*canonicalCommitEvent, error) { + if expectedHash == (common.Hash{}) { + return nil, fmt.Errorf("batch %d has no canonical committed hash at snapshot", index) + } + sort.Slice(commits, func(i, j int) bool { return logIdentityLess(commits[i].raw, commits[j].raw) }) + sort.Slice(reverts, func(i, j int) bool { return logIdentityLess(reverts[i].raw, reverts[j].raw) }) - // Start from the latest height, query backwards 10000 blocks each time until data is found - for endBlock > 0 { - // Calculate the start block for this query - if endBlock >= blockRange { - startBlock = endBlock - blockRange + 1 - } else { - startBlock = 0 + var lastRevert *canonicalRevertEvent + for i := range reverts { + if reverts[i].batchIndex == index { + lastRevert = &reverts[i] } - - // Set query options - filterOpts := &bind.FilterOpts{ - Start: startBlock, - End: &endBlock, + } + var selected *canonicalCommitEvent + for i := range commits { + candidate := &commits[i] + if candidate.batchIndex != index || candidate.batchHash != expectedHash { + continue } - - // Query the FinalizeBatch event with the corresponding index from the rollup contract - finalizeEventIter, err := bc.rollupContract.FilterFinalizeBatch(filterOpts, []*big.Int{new(big.Int).SetUint64(index)}, nil) - if err != nil { - // If query fails, continue querying backwards - if startBlock == 0 { - break // Already queried to block 0, exit loop - } - endBlock = startBlock - 1 + if lastRevert != nil && !logIdentityLess(lastRevert.raw, candidate.raw) { continue } - defer func() { _ = finalizeEventIter.Close() }() - // Iterate through query results - for finalizeEventIter.Next() { - event := finalizeEventIter.Event - // Get transaction hash from event - txHash := event.Raw.TxHash + selected = candidate + } + if selected == nil { + return nil, fmt.Errorf("no canonical CommitBatch log for batch %d and hash %s", index, expectedHash) + } + return selected, nil +} - // Get transaction details - tx, _, err := bc.l1Client.TransactionByHash(context.Background(), txHash) - if err != nil { - continue // If getting transaction fails, try next event - } +// getCanonicalCommittedBatchHeaderByIndex recovers a complete header from the +// last canonical CommitBatch transaction at the already-fixed L1 snapshot. A +// FinalizeBatch transaction is deliberately never used as a header substitute. +func (bc *BatchCache) getCanonicalCommittedBatchHeaderByIndex(index uint64) (*BatchHeaderBytes, error) { + if bc.l1Snapshot == nil { + return nil, errors.New("canonical batch header lookup requires a confirmed L1 snapshot") + } + expectedRaw, err := bc.rollupContract.CommittedBatches(bc.snapshotCallOpts(), new(big.Int).SetUint64(index)) + if err != nil { + return nil, fmt.Errorf("read committed batch %d at snapshot: %w", index, err) + } + expectedHash := common.Hash(expectedRaw) - // Parse finalizeBatch transaction data to get batchHeader - batchHeader, err := parseFinalizeBatchTxData(tx.Data()) - if err != nil { - continue // If parsing fails, try next event + commits, reverts, err := bc.collectCanonicalBatchEvents(index) + if err != nil { + return nil, err + } + selected, err := selectCanonicalCommit(index, expectedHash, commits, reverts) + if err != nil { + return nil, err + } + tx, pending, err := bc.l1Client.TransactionByHash(bc.ctx, selected.raw.TxHash) + if err != nil { + return nil, fmt.Errorf("load canonical batch %d transaction %s: %w", index, selected.raw.TxHash, err) + } + if tx == nil || pending { + return nil, fmt.Errorf("canonical batch %d transaction %s is missing or pending", index, selected.raw.TxHash) + } + header, err := bc.reconstructCommittedBatchHeader(index, expectedHash, tx) + if err != nil { + return nil, fmt.Errorf("reconstruct canonical batch %d from transaction %s: %w", index, selected.raw.TxHash, err) + } + return &header, nil +} + +func (bc *BatchCache) collectCanonicalBatchEvents(index uint64) ([]canonicalCommitEvent, []canonicalRevertEvent, error) { + const blockRange uint64 = 10_000 + var commits []canonicalCommitEvent + var reverts []canonicalRevertEvent + for start := uint64(0); ; { + end := start + blockRange - 1 + if end < start || end > bc.l1Snapshot.number { + end = bc.l1Snapshot.number + } + opts, err := bc.snapshotFilterOpts(start, end) + if err != nil { + return nil, nil, err + } + commitIter, err := bc.rollupContract.FilterCommitBatch(opts, []*big.Int{new(big.Int).SetUint64(index)}, nil) + if err != nil { + return nil, nil, fmt.Errorf("filter CommitBatch %d in L1 range [%d,%d]: %w", index, start, end, err) + } + for commitIter.Next() { + event := commitIter.Event + if event == nil || event.BatchIndex == nil || !event.BatchIndex.IsUint64() { + _ = commitIter.Close() + return nil, nil, fmt.Errorf("malformed CommitBatch event for batch %d", index) } + if event.BatchIndex.Uint64() != index || event.Raw.Removed || event.Raw.BlockNumber < start || event.Raw.BlockNumber > end { + _ = commitIter.Close() + return nil, nil, fmt.Errorf("non-canonical CommitBatch event returned for batch %d", index) + } + commits = append(commits, canonicalCommitEvent{ + batchIndex: event.BatchIndex.Uint64(), + batchHash: common.Hash(event.BatchHash), + raw: event.Raw, + }) + } + iterErr := commitIter.Error() + closeErr := commitIter.Close() + if iterErr != nil { + return nil, nil, fmt.Errorf("iterate CommitBatch %d in L1 range [%d,%d]: %w", index, start, end, iterErr) + } + if closeErr != nil { + return nil, nil, fmt.Errorf("close CommitBatch iterator for batch %d: %w", index, closeErr) + } - // Verify if batch index matches - batchIndex, err := batchHeader.BatchIndex() - if err != nil { - continue + opts, err = bc.snapshotFilterOpts(start, end) + if err != nil { + return nil, nil, err + } + revertIter, err := bc.rollupContract.FilterRevertBatch(opts, []*big.Int{new(big.Int).SetUint64(index)}, nil) + if err != nil { + return nil, nil, fmt.Errorf("filter RevertBatch %d in L1 range [%d,%d]: %w", index, start, end, err) + } + for revertIter.Next() { + event := revertIter.Event + if event == nil || event.BatchIndex == nil || !event.BatchIndex.IsUint64() { + _ = revertIter.Close() + return nil, nil, fmt.Errorf("malformed RevertBatch event for batch %d", index) } - if batchIndex == index { - return &batchHeader, nil + if event.BatchIndex.Uint64() != index || event.Raw.Removed || event.Raw.BlockNumber < start || event.Raw.BlockNumber > end { + _ = revertIter.Close() + return nil, nil, fmt.Errorf("non-canonical RevertBatch event returned for batch %d", index) } + reverts = append(reverts, canonicalRevertEvent{ + batchIndex: event.BatchIndex.Uint64(), + batchHash: common.Hash(event.BatchHash), + raw: event.Raw, + }) + } + iterErr = revertIter.Error() + closeErr = revertIter.Close() + if iterErr != nil { + return nil, nil, fmt.Errorf("iterate RevertBatch %d in L1 range [%d,%d]: %w", index, start, end, iterErr) + } + if closeErr != nil { + return nil, nil, fmt.Errorf("close RevertBatch iterator for batch %d: %w", index, closeErr) } - // Continue querying backwards - if endBlock < blockRange { - break // Already queried to block 0, exit loop + if end == bc.l1Snapshot.number { + break } - endBlock = startBlock - 1 + start = end + 1 } - - return nil, fmt.Errorf("failed to find last finalized batch header for batchIndex %d", index) + return commits, reverts, nil } -// parseFinalizeBatchTxData parses the finalizeBatch or importGenesisBatch transaction's input data to get BatchHeaderBytes -// Both finalizeBatch(bytes calldata _batchHeader) and importGenesisBatch(bytes calldata _batchHeader) receive one parameter: batchHeader bytes -// Both methods emit FinalizeBatch event, so we need to support parsing both -func parseFinalizeBatchTxData(txData []byte) (BatchHeaderBytes, error) { - // Get rollup ABI - rollupAbi, err := bindings.RollupMetaData.GetAbi() +func (bc *BatchCache) reconstructCommittedBatchHeader( + index uint64, + expectedHash common.Hash, + tx *ethtypes.Transaction, +) (BatchHeaderBytes, error) { + if tx == nil { + return nil, errors.New("nil canonical commit transaction") + } + genesis, matched, err := decodeImportGenesisHeader(tx.Data()) if err != nil { return nil, err } - - // Check if the first 4 bytes of transaction data match the method ID - if len(txData) < 4 { - return nil, errors.New("transaction data too short") + if matched { + if err := verifyHeaderIdentity(genesis, index, expectedHash); err != nil { + return nil, err + } + if err := bc.verifyFinalizedMessageCursor(genesis); err != nil { + return nil, err + } + return genesis, nil } - methodID := txData[:4] + abis, err := DefaultRollupABIs() + if err != nil { + return nil, err + } + decoded, err := decodeBatchCall(tx.Data(), abis) + if err != nil { + return nil, err + } + if decoded.input.Version > BatchHeaderVersion2 { + return nil, fmt.Errorf("canonical commit has unsupported batch version %d", decoded.input.Version) + } + if len(decoded.explicitHeader) > 0 { + if err := verifyHeaderIdentity(decoded.explicitHeader, index, expectedHash); err != nil { + return nil, fmt.Errorf("invalid explicit header in %s.%s: %w", decoded.era, decoded.method, err) + } + if err := bc.verifyFinalizedMessageCursor(decoded.explicitHeader); err != nil { + return nil, err + } + return decoded.explicitHeader, nil + } - // Try to get finalizeBatch method - finalizeBatchMethod, ok := rollupAbi.Methods["finalizeBatch"] - if !ok { - return nil, errors.New("finalizeBatch method not found in ABI") + parent := BatchHeaderBytes(decoded.input.ParentBatchHeader) + parentIndex, err := parent.BatchIndex() + if err != nil { + return nil, fmt.Errorf("decode parent batch header index: %w", err) + } + if parentIndex == ^uint64(0) || parentIndex+1 != index { + return nil, fmt.Errorf("canonical commit parent index mismatch: parent=%d current=%d", parentIndex, index) + } + parentHash, err := parent.Hash() + if err != nil { + return nil, fmt.Errorf("hash parent batch header: %w", err) + } + parentCursor, err := parent.TotalL1MessagePopped() + if err != nil { + return nil, fmt.Errorf("decode parent L1 message cursor: %w", err) } - var method abi.Method - var methodName string + queue, err := bc.l1MessageQueue() + if err != nil { + return nil, err + } + var ( + dataHash common.Hash + l1MessageCount uint64 + ) + switch decoded.era { + case "pre-submitter", "current": + l1MessageCount = uint64(decoded.input.NumL1Messages) + if l1MessageCount > ^uint64(0)-parentCursor { + return nil, fmt.Errorf("L1 message cursor overflow: parent=%d batch=%d", parentCursor, l1MessageCount) + } + data := make([]byte, 10, 10+32*int(l1MessageCount)) + binary.BigEndian.PutUint64(data[:8], decoded.input.LastBlockNumber) + binary.BigEndian.PutUint16(data[8:10], decoded.input.NumL1Messages) + for i := uint64(0); i < l1MessageCount; i++ { + messageHash, err := queue.GetCrossDomainMessage( + bc.snapshotCallOpts(), + new(big.Int).SetUint64(parentCursor+i), + ) + if err != nil { + return nil, fmt.Errorf("read L1 message %d for canonical batch %d: %w", parentCursor+i, index, err) + } + data = append(data, messageHash[:]...) + } + dataHash = crypto.Keccak256Hash(data) + case "before-move-block-context", "legacy": + data, count, err := bc.reconstructBlockContextData(decoded, parentCursor, queue) + if err != nil { + return nil, err + } + l1MessageCount = count + dataHash = crypto.Keccak256Hash(data) + default: + return nil, fmt.Errorf("unsupported canonical commit ABI era %q", decoded.era) + } + if l1MessageCount > ^uint64(0)-parentCursor { + return nil, fmt.Errorf("L1 message cursor overflow: parent=%d batch=%d", parentCursor, l1MessageCount) + } + totalCursor := parentCursor + l1MessageCount - // Check if method ID matches finalizeBatch - if bytes.Equal(methodID, finalizeBatchMethod.ID) { - method = finalizeBatchMethod - methodName = "finalizeBatch" + blobHashRaw, err := bc.rollupContract.BatchBlobVersionedHashes( + bc.snapshotCallOpts(), + new(big.Int).SetUint64(index), + ) + if err != nil { + return nil, fmt.Errorf("read blob hash for canonical batch %d: %w", index, err) + } + blobCommitHash, err := canonicalBlobCommitHash(tx, decoded, common.Hash(blobHashRaw)) + if err != nil { + return nil, err + } + base := BatchHeaderV0{ + BatchIndex: index, + L1MessagePopped: l1MessageCount, + TotalL1MessagePopped: totalCursor, + DataHash: dataHash, + BlobVersionedHash: blobCommitHash, + PrevStateRoot: common.Hash(decoded.input.PrevStateRoot), + PostStateRoot: common.Hash(decoded.input.PostStateRoot), + WithdrawalRoot: common.Hash(decoded.input.WithdrawalRoot), + SequencerSetVerifyHash: decoded.sequencerSetHash, + ParentBatchHash: parentHash, + } + var header BatchHeaderBytes + if decoded.input.Version >= BatchHeaderVersion1 { + header = BatchHeaderV1{ + BatchHeaderV0: base, + LastBlockNumber: decoded.input.LastBlockNumber, + }.Bytes() + if decoded.input.Version == BatchHeaderVersion2 { + header[0] = BatchHeaderVersion2 + } } else { - // Try importGenesisBatch method - importGenesisBatchMethod, ok := rollupAbi.Methods["importGenesisBatch"] - if !ok { - return nil, errors.New("importGenesisBatch method not found in ABI") - } - if bytes.Equal(methodID, importGenesisBatchMethod.ID) { - method = importGenesisBatchMethod - methodName = "importGenesisBatch" - } else { - return nil, fmt.Errorf("transaction is not a finalizeBatch or importGenesisBatch call, methodID: %x", methodID) + header = base.Bytes() + if decoded.era == "legacy" { + header = append(header, decoded.skippedBitmap...) } } + if err := verifyHeaderIdentity(header, index, expectedHash); err != nil { + return nil, fmt.Errorf("reconstructed %s.%s header: %w", decoded.era, decoded.method, err) + } + if err := bc.verifyFinalizedMessageCursor(header); err != nil { + return nil, err + } + return header, nil +} - // Parse parameters (only one parameter: batchHeader bytes) - args, err := method.Inputs.Unpack(txData[4:]) - if err != nil { - return nil, fmt.Errorf("failed to unpack %s transaction parameters: %w", methodName, err) +func canonicalBlobCommitHash( + tx *ethtypes.Transaction, + decoded *decodedBatchCall, + stored common.Hash, +) (common.Hash, error) { + hashes := tx.BlobHashes() + if len(hashes) == 0 { + if decoded.era == "current" && decoded.method == "commitBatch" { + return common.Hash{}, errors.New("current canonical commitBatch has no fresh blob hash") + } + if stored != (common.Hash{}) { + return stored, nil + } + if decoded.method == "commitState" || decoded.input.Version >= BatchHeaderVersion2 { + return common.Hash{}, fmt.Errorf("%s.%s has no recoverable stored blob hash", decoded.era, decoded.method) + } + // Frozen V0/V1 commitBatch transactions mined before blobs were + // available used the protocol's non-zero empty versioned hash. + return blob.EmptyVersionedHash, nil } - if len(args) == 0 { - return nil, fmt.Errorf("no arguments found in %s transaction", methodName) + var fromTransaction common.Hash + if decoded.input.Version >= BatchHeaderVersion2 { + fromTransaction = aggregateBlobHashes(hashes) + } else { + fromTransaction = hashes[0] + } + if stored != (common.Hash{}) && stored != fromTransaction { + return common.Hash{}, fmt.Errorf("canonical batch blob hash mismatch: stored=%s transaction=%s", stored, fromTransaction) } + return fromTransaction, nil +} - // The first parameter is batchHeader bytes - batchHeaderBytes, ok := args[0].([]byte) +func decodeImportGenesisHeader(data []byte) (BatchHeaderBytes, bool, error) { + rollupABI, err := bindings.RollupMetaData.GetAbi() + if err != nil { + return nil, false, err + } + method, ok := rollupABI.Methods["importGenesisBatch"] + if !ok || len(data) < 4 || !bytes.Equal(data[:4], method.ID) { + return nil, false, nil + } + args, err := method.Inputs.Unpack(data[4:]) + if err != nil { + return nil, true, fmt.Errorf("decode importGenesisBatch: %w", err) + } + if len(args) != 1 { + return nil, true, fmt.Errorf("decode importGenesisBatch: expected one argument, got %d", len(args)) + } + header, ok := args[0].([]byte) if !ok { - return nil, fmt.Errorf("failed to cast batchHeader to []byte in %s transaction", methodName) + return nil, true, errors.New("decode importGenesisBatch: invalid header argument") } - - return BatchHeaderBytes(batchHeaderBytes), nil + return BatchHeaderBytes(bytes.Clone(header)), true, nil } -// batchDataInputStruct represents the parsed batch data input structure from ABI -type batchDataInputStruct struct { - Version uint8 `json:"version"` - ParentBatchHeader []uint8 `json:"parentBatchHeader"` - LastBlockNumber uint64 `json:"lastBlockNumber"` - NumL1Messages uint16 `json:"numL1Messages"` - PrevStateRoot [32]uint8 `json:"prevStateRoot"` - PostStateRoot [32]uint8 `json:"postStateRoot"` - WithdrawalRoot [32]uint8 `json:"withdrawalRoot"` +func verifyHeaderIdentity(header BatchHeaderBytes, index uint64, expectedHash common.Hash) error { + gotIndex, err := header.BatchIndex() + if err != nil { + return fmt.Errorf("decode batch header index: %w", err) + } + if gotIndex != index { + return fmt.Errorf("batch header index mismatch: event=%d header=%d", index, gotIndex) + } + gotHash, err := header.Hash() + if err != nil { + return fmt.Errorf("hash batch header: %w", err) + } + if gotHash != expectedHash { + return fmt.Errorf("batch header hash mismatch: getter=%s reconstructed=%s", expectedHash, gotHash) + } + return nil } -// batchSignatureInputStruct represents the parsed batch signature input structure from ABI -type batchSignatureInputStruct struct { - SignedSequencersBitmap *big.Int `json:"signedSequencersBitmap"` - SequencerSets []uint8 `json:"sequencerSets"` - Signature []uint8 `json:"signature"` +func (bc *BatchCache) l1MessageQueue() (*bindings.L1MessageQueueWithGasPriceOracleCaller, error) { + address, err := bc.rollupContract.MessageQueue(bc.snapshotCallOpts()) + if err != nil { + return nil, fmt.Errorf("read Rollup messageQueue at snapshot: %w", err) + } + if address == (common.Address{}) { + return nil, errors.New("Rollup messageQueue is zero at snapshot") + } + queue, err := bindings.NewL1MessageQueueWithGasPriceOracleCaller(address, bc.l1Client) + if err != nil { + return nil, fmt.Errorf("bind L1 message queue %s: %w", address, err) + } + return queue, nil } -// convertBatchDataInput converts the parsed struct to bindings.IRollupBatchDataInput -func convertBatchDataInput(s batchDataInputStruct) *bindings.IRollupBatchDataInput { - // Convert []uint8 to []byte - parentBatchHeader := make([]byte, len(s.ParentBatchHeader)) - copy(parentBatchHeader, s.ParentBatchHeader) +func (bc *BatchCache) verifyFinalizedMessageCursor(header BatchHeaderBytes) error { + total, err := header.TotalL1MessagePopped() + if err != nil { + return fmt.Errorf("decode finalized L1 message cursor: %w", err) + } + queue, err := bc.l1MessageQueue() + if err != nil { + return err + } + pending, err := queue.PendingQueueIndex(bc.snapshotCallOpts()) + if err != nil { + return fmt.Errorf("read finalized L1 message queue cursor: %w", err) + } + if pending == nil || !pending.IsUint64() || pending.Uint64() != total { + return fmt.Errorf("finalized L1 message cursor mismatch: header=%d queue=%v", total, pending) + } + next, err := queue.NextCrossDomainMessageIndex(bc.snapshotCallOpts()) + if err != nil { + return fmt.Errorf("read next L1 message queue index: %w", err) + } + if next == nil || !next.IsUint64() || next.Uint64() < total { + return fmt.Errorf("L1 message queue shorter than finalized cursor: header=%d next=%v", total, next) + } + return nil +} - return &bindings.IRollupBatchDataInput{ - Version: s.Version, - ParentBatchHeader: parentBatchHeader, - LastBlockNumber: s.LastBlockNumber, - NumL1Messages: s.NumL1Messages, - PrevStateRoot: s.PrevStateRoot, - PostStateRoot: s.PostStateRoot, - WithdrawalRoot: s.WithdrawalRoot, +func (bc *BatchCache) reconstructBlockContextData( + decoded *decodedBatchCall, + parentCursor uint64, + queue *bindings.L1MessageQueueWithGasPriceOracleCaller, +) ([]byte, uint64, error) { + contexts := decoded.blockContexts + if len(contexts) < 2 { + return nil, 0, fmt.Errorf("%s block contexts are shorter than count prefix", decoded.era) + } + blockCount := int(binary.BigEndian.Uint16(contexts[:2])) + if blockCount == 0 || len(contexts) != 2+blockCount*60 { + return nil, 0, fmt.Errorf("%s block context length mismatch: blocks=%d bytes=%d", decoded.era, blockCount, len(contexts)) + } + data := make([]byte, 0, blockCount*58) + var messageCount uint64 + for i := 0; i < blockCount; i++ { + contextBytes := contexts[2+i*60 : 2+(i+1)*60] + data = append(data, contextBytes[:58]...) + messageCount += uint64(binary.BigEndian.Uint16(contextBytes[58:60])) + } + if decoded.era == "legacy" { + expectedBitmapBytes := int((messageCount + 255) / 256 * 32) + if len(decoded.skippedBitmap) != expectedBitmapBytes { + return nil, 0, fmt.Errorf("legacy skipped bitmap length mismatch: messages=%d expected=%d actual=%d", messageCount, expectedBitmapBytes, len(decoded.skippedBitmap)) + } + } + if messageCount > ^uint64(0)-parentCursor { + return nil, 0, fmt.Errorf("historical L1 message cursor overflow: parent=%d batch=%d", parentCursor, messageCount) } + for i := uint64(0); i < messageCount; i++ { + if decoded.era == "legacy" && legacyMessageSkipped(decoded.skippedBitmap, i) { + continue + } + messageHash, err := queue.GetCrossDomainMessage( + bc.snapshotCallOpts(), + new(big.Int).SetUint64(parentCursor+i), + ) + if err != nil { + return nil, 0, fmt.Errorf("read historical L1 message %d: %w", parentCursor+i, err) + } + data = append(data, messageHash[:]...) + } + return data, messageCount, nil } -// convertBatchSignatureInput converts the parsed struct to bindings.IRollupBatchSignatureInput -func convertBatchSignatureInput(s batchSignatureInputStruct) *bindings.IRollupBatchSignatureInput { - // Convert []uint8 to []byte - sequencerSets := make([]byte, len(s.SequencerSets)) - copy(sequencerSets, s.SequencerSets) - signature := make([]byte, len(s.Signature)) - copy(signature, s.Signature) +func legacyMessageSkipped(bitmap []byte, index uint64) bool { + wordStart := int(index/256) * 32 + bitInWord := index % 256 + byteIndex := wordStart + 31 - int(bitInWord/8) + return byteIndex >= 0 && byteIndex < len(bitmap) && bitmap[byteIndex]&(1< 0 { - parentBatchHeader, err := getLastFinalizeBatchHeaderByIndex(batchIndex - 1) - if err == nil { - parentBytes := parentBatchHeader.Bytes() - if !bytes.Equal(parentBytes, batchDataInput.ParentBatchHeader) { - t.Errorf("❌ ParentBatchHeader mismatch: assembled=%x, commitBatch=%x", parentBytes[:min(32, len(parentBytes))], batchDataInput.ParentBatchHeader[:min(32, len(batchDataInput.ParentBatchHeader))]) - } else { - t.Logf("✓ ParentBatchHeader: match") - } - } - } - - // Compare LastBlockNumber - lastBlock, err := assembledBatchHeader.LastBlockNumber() - if err == nil { - if lastBlock != batchDataInput.LastBlockNumber { - t.Errorf("❌ LastBlockNumber mismatch: assembled=%d, commitBatch=%d", lastBlock, batchDataInput.LastBlockNumber) - } else { - t.Logf("✓ LastBlockNumber: %d (match)", lastBlock) - } - } - - // Compare NumL1Messages - l1MsgPopped, err := assembledBatchHeader.L1MessagePopped() - require.NoError(t, err) - if l1MsgPopped != uint64(batchDataInput.NumL1Messages) { - t.Errorf("❌ NumL1Messages mismatch: assembled=%d, commitBatch=%d", l1MsgPopped, batchDataInput.NumL1Messages) - } else { - t.Logf("✓ NumL1Messages: %d (match)", l1MsgPopped) - } - - // Compare PrevStateRoot - prevStateRoot, err := assembledBatchHeader.PrevStateRoot() - require.NoError(t, err) - prevStateRootFromCommit := common.BytesToHash(batchDataInput.PrevStateRoot[:]) - if prevStateRoot != prevStateRootFromCommit { - t.Errorf("❌ PrevStateRoot mismatch: assembled=%x, commitBatch=%x", prevStateRoot, prevStateRootFromCommit) - } else { - t.Logf("✓ PrevStateRoot: %x (match)", prevStateRoot) - } - - // Compare PostStateRoot - postStateRoot, err := assembledBatchHeader.PostStateRoot() - require.NoError(t, err) - postStateRootFromCommit := common.BytesToHash(batchDataInput.PostStateRoot[:]) - if postStateRoot != postStateRootFromCommit { - t.Errorf("❌ PostStateRoot mismatch: assembled=%x, commitBatch=%x", postStateRoot, postStateRootFromCommit) - } else { - t.Logf("✓ PostStateRoot: %x (match)", postStateRoot) - } - - // Compare WithdrawalRoot - withdrawRoot, err := assembledBatchHeader.WithdrawalRoot() - require.NoError(t, err) - withdrawRootFromCommit := common.BytesToHash(batchDataInput.WithdrawalRoot[:]) - if withdrawRoot != withdrawRootFromCommit { - t.Errorf("❌ WithdrawalRoot mismatch: assembled=%x, commitBatch=%x", withdrawRoot, withdrawRootFromCommit) - } else { - t.Logf("✓ WithdrawalRoot: %x (match)", withdrawRoot) - } - - // Compare SequencerSetVerifyHash - sequencerSetsHash := crypto.Keccak256Hash(batchSignatureInput.SequencerSets) - seqHash, err := assembledBatchHeader.SequencerSetVerifyHash() - require.NoError(t, err) - if seqHash != sequencerSetsHash { - t.Errorf("❌ SequencerSetVerifyHash mismatch: assembled=%x, from SequencerSets=%x", seqHash, sequencerSetsHash) - } else { - t.Logf("✓ SequencerSetVerifyHash: %x (match)", seqHash) - } - - if seqHash != sequencerSetVerifyHash { - t.Errorf("❌ SequencerSetVerifyHash mismatch with provided hash: assembled=%x, provided=%x", seqHash, sequencerSetVerifyHash) - } else { - t.Logf("✓ SequencerSetVerifyHash matches provided hash: %x", sequencerSetVerifyHash) - } -} - -func min(a, b int) int { - if a < b { - return a - } - return b -} - -// getLastFinalizeBatchHeaderByIndex gets the batch header with the specified index from the rollup contract's FinalizeBatch event -// The finalizeBatch function only receives one parameter: batchHeader bytes, so it can be parsed directly from the transaction -// Query is limited to 10000 block heights, starting from the latest height and querying backwards until data is found -func getLastFinalizeBatchHeaderByIndex(index uint64) (*BatchHeaderBytes, error) { - // Get the current latest block height - latestBlock, err := l1Client.BlockNumber(context.Background()) - if err != nil { - return nil, fmt.Errorf("failed to get latest block number: %w", err) - } - - const blockRange = uint64(10000) // Query 10000 blocks each time - var endBlock uint64 = latestBlock - var startBlock uint64 - - // Start from the latest height, query backwards 10000 blocks each time until data is found - for endBlock > 0 { - // Calculate the start block for this query - if endBlock >= blockRange { - startBlock = endBlock - blockRange + 1 - } else { - startBlock = 0 - } - - // Set query options - filterOpts := &bind.FilterOpts{ - Start: startBlock, - End: &endBlock, - } - - // Query the FinalizeBatch event with the corresponding index from the rollup contract - finalizeEventIter, err := rollupContract.FilterFinalizeBatch(filterOpts, []*big.Int{new(big.Int).SetUint64(index)}, nil) - if err != nil { - // If query fails, continue querying backwards - if endBlock < blockRange { - break // Already queried to block 0, exit loop - } - endBlock = startBlock - 1 - continue - } - - // Iterate through query results - for finalizeEventIter.Next() { - event := finalizeEventIter.Event - // Get transaction hash from event - txHash := event.Raw.TxHash - - // Get transaction details - tx, _, err := l1Client.TransactionByHash(context.Background(), txHash) - if err != nil { - continue // If getting transaction fails, try next event - } - - // Parse finalizeBatch transaction data to get batchHeader - batchHeader, err := parseFinalizeBatchTxData(tx.Data()) - if err != nil { - continue // If parsing fails, try next event - } - - // Verify if batch index matches - batchIndex, err := batchHeader.BatchIndex() - if err != nil { - continue - } - if batchIndex == index { - finalizeEventIter.Close() - return &batchHeader, nil - } - } - finalizeEventIter.Close() - - // Continue querying backwards - if endBlock < blockRange { - break // Already queried to block 0, exit loop - } - endBlock = startBlock - 1 - } - - return nil, ErrBatchNotFound -} - -func getInfosFromContract() (*big.Int, *big.Int) { - latestCommitBatchIndex, _ := rollupContract.LastCommittedBatchIndex(nil) - lastFinalizedBatchIndex, _ := rollupContract.LastFinalizedBatchIndex(nil) - return latestCommitBatchIndex, lastFinalizedBatchIndex -} - -func getFirstUnFinalizeBatchBlockNumRange(lastFinalizedBatchIndex *big.Int) (*big.Int, *big.Int, error) { - fis, err := rollupContract.BatchDataStore(nil, lastFinalizedBatchIndex) - if err != nil { - return nil, nil, err - } - ufis, err := rollupContract.BatchDataStore(nil, new(big.Int).SetUint64(lastFinalizedBatchIndex.Uint64()+1)) - if err != nil { - return nil, nil, err - } - - return fis.BlockNumber, ufis.BlockNumber, nil -} - -// getCommitBatchDataByIndex gets batchDataInput and batchSignatureInput with the specified index from the rollup contract's CommitBatch event -// Reference the implementation of getLastFinalizeBatchHeaderByIndex -// Query is limited to 10000 block heights, starting from the latest height and querying backwards until data is found -func getCommitBatchDataByIndex(index uint64) (*bindings.IRollupBatchDataInput, *bindings.IRollupBatchSignatureInput, error) { - // Get the current latest block height - latestBlock, err := l1Client.BlockNumber(context.Background()) - if err != nil { - return nil, nil, fmt.Errorf("failed to get latest block number: %w", err) - } - - const blockRange = uint64(10000) // Query 10000 blocks each time - var endBlock uint64 = latestBlock - var startBlock uint64 - - // Start from the latest height, query backwards 10000 blocks each time until data is found - for endBlock > 0 { - // Calculate the start block for this query - if endBlock >= blockRange { - startBlock = endBlock - blockRange + 1 - } else { - startBlock = 0 - } - - // Set query options - filterOpts := &bind.FilterOpts{ - Start: startBlock, - End: &endBlock, - } - - // Query the CommitBatch event with the corresponding index from the rollup contract - commitEventIter, err := rollupContract.FilterCommitBatch(filterOpts, []*big.Int{new(big.Int).SetUint64(index)}, nil) - if err != nil { - // If query fails, continue querying backwards - if endBlock < blockRange { - break // Already queried to block 0, exit loop - } - endBlock = startBlock - 1 - continue - } - - // Iterate through query results - for commitEventIter.Next() { - event := commitEventIter.Event - // Get transaction hash from event - txHash := event.Raw.TxHash - - // Get transaction details - tx, _, err := l1Client.TransactionByHash(context.Background(), txHash) - if err != nil { - return nil, nil, fmt.Errorf("failed to get transaction by hash: %w", err) - } - - // Parse commitBatch transaction data to get batchDataInput and batchSignatureInput - batchDataInput, batchSignatureInput, err := parseCommitBatchTxData(tx.Data()) - if err != nil { - return nil, nil, fmt.Errorf("failed to parse commit batch data: %w", err) - } - - // Verify if batch index matches (by checking batchIndex in parentBatchHeader) - if len(batchDataInput.ParentBatchHeader) > 0 { - parentHeader := BatchHeaderBytes(batchDataInput.ParentBatchHeader) - parentBatchIndex, err := parentHeader.BatchIndex() - if err == nil && parentBatchIndex+1 == index { - commitEventIter.Close() - return batchDataInput, batchSignatureInput, nil - } - } - } - commitEventIter.Close() - - // Continue querying backwards - if endBlock < blockRange { - break // Already queried to block 0, exit loop - } - endBlock = startBlock - 1 - } - - return nil, nil, ErrBatchNotFound -} - -// assembleBatchHeaderFromL2Blocks fetches blocks from L2 client in the specified range and assembles batchHeader -// Parameters: -// - bc: BatchCache instance (parentBatchHeader and prevStateRoot already initialized) -// - startBlockNum: starting block number -// - endBlockNum: ending block number -// - sequencerSetVerifyHash: sequencer set verification hash -// - l2Client: L2 client -// -// Returns: -// - batchHeader: assembled batchHeader -// - error: returns error if assembly fails -func assembleBatchHeaderFromL2Blocks( - bc *BatchCache, - startBlockNum, endBlockNum uint64, - sequencerBytes []byte, - l2Client *ethclient.Client, - l2Gov L2GovCaller, -) (*BatchHeaderBytes, error) { - ctx := context.Background() - - // Fetch blocks from L2 client in the specified range and accumulate to batch - for blockNum := startBlockNum; blockNum <= endBlockNum; blockNum++ { - root, err := l2Gov.GetTreeRoot(&bind.CallOpts{ - Context: ctx, - BlockNumber: new(big.Int).SetUint64(blockNum), - }) - if err != nil { - return nil, fmt.Errorf("failed to get withdraw root at block %d: %w", blockNum, err) - } - // Check capacity and store to current - exceeded, err := bc.CalculateCapWithProposalBlock(blockNum, root) - if err != nil { - return nil, fmt.Errorf("failed to calculate cap with block %d: %w", blockNum, err) - } - - // Pack current block (confirm and append to batch) - if err = bc.PackCurrentBlock(blockNum); err != nil { - return nil, fmt.Errorf("failed to pack block %d: %w", blockNum, err) - } - - // If capacity exceeds limit, can stop early (optional) - _ = exceeded // Checked but not used in this test - } - - // Get the last block's timestamp for packing - lastBlock, err := l2Client.BlockByNumber(ctx, big.NewInt(int64(endBlockNum))) - if err != nil { - return nil, fmt.Errorf("failed to get last block %d: %w", endBlockNum, err) - } - blockTimestamp := lastBlock.Time() - - // Seal batch and generate batchHeader - batchIndex, batchHeaderBytes, _, err := bc.SealBatch(sequencerBytes, blockTimestamp, nil) - if err != nil { - return nil, fmt.Errorf("failed to seal batch: %w", err) - } - - // Get the sealed batch header - _, found := bc.GetSealedBatch(batchIndex) - if !found { - return nil, fmt.Errorf("sealed batch not found for index %d", batchIndex) - } - - return &batchHeaderBytes, nil + requireBatchIntegration(t) + cache := NewBatchCache( + nil, + nil, + 2, + testBatchConfig, + l1Client, + &SingleL2Client{C: l2Client}, + rollupContract, + l2Gov, + openTestKV(t), + ) + require.NoError(t, cache.InitAndSyncFromRollup()) + require.True(t, cache.isInitialized()) } diff --git a/common/batch/batch_storage_test.go b/common/batch/batch_storage_test.go index 9ceee7da2..fceea3dcc 100644 --- a/common/batch/batch_storage_test.go +++ b/common/batch/batch_storage_test.go @@ -233,7 +233,7 @@ func TestSealBatchPropagatesStoreErrorAndKeepsState(t *testing.T) { originalBatchData := bc.batchData originalParent := bc.parentBatchHeader - _, _, _, err := bc.SealBatch([]byte{1}, 1, nil) + _, _, _, err := bc.SealBatch(1, nil) require.ErrorIs(t, err, errWriteBatchUnavailable) require.Empty(t, bc.sealedBatches) require.Empty(t, bc.sealedBatchHeaders) diff --git a/common/batch/canonical_bootstrap_test.go b/common/batch/canonical_bootstrap_test.go new file mode 100644 index 000000000..fd9a58a02 --- /dev/null +++ b/common/batch/canonical_bootstrap_test.go @@ -0,0 +1,386 @@ +package batch + +import ( + "bytes" + "context" + "encoding/binary" + "errors" + "math/big" + "testing" + + "morph-l2/bindings/bindings" + + "github.com/holiman/uint256" + "github.com/morph-l2/go-ethereum" + "github.com/morph-l2/go-ethereum/accounts/abi/bind" + "github.com/morph-l2/go-ethereum/common" + ethtypes "github.com/morph-l2/go-ethereum/core/types" + "github.com/morph-l2/go-ethereum/crypto" + "github.com/morph-l2/go-ethereum/eth" + "github.com/morph-l2/go-ethereum/rpc" + "github.com/stretchr/testify/require" +) + +func TestSelectCanonicalCommitUsesRecommitAfterLastRevert(t *testing.T) { + hash := common.HexToHash("0x1234") + commits := []canonicalCommitEvent{ + {batchIndex: 9, batchHash: hash, raw: ethtypes.Log{BlockNumber: 10, TxIndex: 1, Index: 2}}, + {batchIndex: 9, batchHash: hash, raw: ethtypes.Log{BlockNumber: 12, TxIndex: 0, Index: 0}}, + } + reverts := []canonicalRevertEvent{ + {batchIndex: 9, batchHash: hash, raw: ethtypes.Log{BlockNumber: 11, TxIndex: 3, Index: 4}}, + } + + selected, err := selectCanonicalCommit(9, hash, commits, reverts) + require.NoError(t, err) + require.Equal(t, uint64(12), selected.raw.BlockNumber) + + // Ordering includes transaction and log index, not just block number. + commits = append(commits, canonicalCommitEvent{ + batchIndex: 9, + batchHash: hash, + raw: ethtypes.Log{BlockNumber: 12, TxIndex: 0, Index: 3}, + }) + selected, err = selectCanonicalCommit(9, hash, commits, reverts) + require.NoError(t, err) + require.Equal(t, uint(3), selected.raw.Index) +} + +func TestSelectCanonicalCommitRejectsEmptyCheckpoint(t *testing.T) { + _, err := selectCanonicalCommit(7, common.Hash{}, nil, nil) + require.ErrorContains(t, err, "no canonical committed hash") +} + +type queueBackend struct { + messages map[uint64]common.Hash + pending uint64 + next uint64 + callBlocks []uint64 + latest uint64 + headers []*ethtypes.Header + headerReads int + transactions map[common.Hash]*ethtypes.Transaction +} + +func (b *queueBackend) CodeAt(context.Context, common.Address, *big.Int) ([]byte, error) { + return []byte{1}, nil +} + +func (b *queueBackend) CallContract(_ context.Context, call ethereum.CallMsg, blockNumber *big.Int) ([]byte, error) { + if blockNumber == nil { + return nil, errors.New("queue getter was not pinned to snapshot") + } + b.callBlocks = append(b.callBlocks, blockNumber.Uint64()) + if len(call.Data) < 4 { + return nil, errors.New("short queue calldata") + } + selector := call.Data[:4] + switch { + case bytes.Equal(selector, crypto.Keccak256([]byte("getCrossDomainMessage(uint256)"))[:4]): + if len(call.Data) != 36 { + return nil, errors.New("invalid message getter calldata") + } + index := new(big.Int).SetBytes(call.Data[4:]).Uint64() + hash, ok := b.messages[index] + if !ok { + return nil, errors.New("message index unavailable") + } + return hash.Bytes(), nil + case bytes.Equal(selector, crypto.Keccak256([]byte("pendingQueueIndex()"))[:4]): + return common.LeftPadBytes(new(big.Int).SetUint64(b.pending).Bytes(), 32), nil + case bytes.Equal(selector, crypto.Keccak256([]byte("nextCrossDomainMessageIndex()"))[:4]): + return common.LeftPadBytes(new(big.Int).SetUint64(b.next).Bytes(), 32), nil + default: + return nil, errors.New("unexpected queue getter") + } +} + +func (b *queueBackend) BlockNumber(context.Context) (uint64, error) { return b.latest, nil } + +func (b *queueBackend) HeaderByNumber(context.Context, *big.Int) (*ethtypes.Header, error) { + if len(b.headers) == 0 { + return nil, errors.New("header unavailable") + } + index := b.headerReads + if index >= len(b.headers) { + index = len(b.headers) - 1 + } + b.headerReads++ + return b.headers[index], nil +} + +func (b *queueBackend) TransactionByHash(_ context.Context, hash common.Hash) (*ethtypes.Transaction, bool, error) { + tx, ok := b.transactions[hash] + if !ok { + return nil, false, errors.New("transaction unavailable") + } + return tx, false, nil +} + +type canonicalRollupStub struct { + blobHash common.Hash + queueAddress common.Address + callBlockReads []uint64 +} + +func (r *canonicalRollupStub) record(opts *bind.CallOpts) error { + if opts == nil || opts.BlockNumber == nil { + return errors.New("rollup getter was not pinned to snapshot") + } + r.callBlockReads = append(r.callBlockReads, opts.BlockNumber.Uint64()) + return nil +} + +func (r *canonicalRollupStub) BatchBlobVersionedHashes(opts *bind.CallOpts, _ *big.Int) ([32]byte, error) { + if err := r.record(opts); err != nil { + return [32]byte{}, err + } + return [32]byte(r.blobHash), nil +} + +func (r *canonicalRollupStub) MessageQueue(opts *bind.CallOpts) (common.Address, error) { + if err := r.record(opts); err != nil { + return common.Address{}, err + } + return r.queueAddress, nil +} + +func (*canonicalRollupStub) CommittedBatches(*bind.CallOpts, *big.Int) ([32]byte, error) { + return [32]byte{}, errors.New("not implemented") +} +func (*canonicalRollupStub) LastCommittedBatchIndex(*bind.CallOpts) (*big.Int, error) { + return nil, errors.New("not implemented") +} +func (*canonicalRollupStub) LastFinalizedBatchIndex(*bind.CallOpts) (*big.Int, error) { + return nil, errors.New("not implemented") +} +func (*canonicalRollupStub) BatchDataStore(*bind.CallOpts, *big.Int) (struct { + OriginTimestamp *big.Int + FinalizeTimestamp *big.Int + BlockNumber *big.Int + Submitter common.Address +}, error) { + return struct { + OriginTimestamp *big.Int + FinalizeTimestamp *big.Int + BlockNumber *big.Int + Submitter common.Address + }{}, errors.New("not implemented") +} +func (*canonicalRollupStub) FinalizedStateRoots(*bind.CallOpts, *big.Int) ([32]byte, error) { + return [32]byte{}, errors.New("not implemented") +} +func (*canonicalRollupStub) FilterCommitBatch(*bind.FilterOpts, []*big.Int, [][32]byte) (*bindings.RollupCommitBatchIterator, error) { + return nil, errors.New("not implemented") +} +func (*canonicalRollupStub) FilterRevertBatch(*bind.FilterOpts, []*big.Int, [][32]byte) (*bindings.RollupRevertBatchIterator, error) { + return nil, errors.New("not implemented") +} + +func TestReconstructCurrentCanonicalCommitAtPinnedSnapshot(t *testing.T) { + parent := BatchHeaderV1{ + BatchHeaderV0: BatchHeaderV0{ + BatchIndex: 7, + TotalL1MessagePopped: 3, + PostStateRoot: common.HexToHash("0x70"), + }, + LastBlockNumber: 90, + }.Bytes() + input := bindings.IRollupBatchDataInput{ + Version: BatchHeaderVersion1, + ParentBatchHeader: parent, + LastBlockNumber: 100, + NumL1Messages: 2, + PrevStateRoot: common.HexToHash("0x70"), + PostStateRoot: common.HexToHash("0x80"), + WithdrawalRoot: common.HexToHash("0x90"), + } + abis, err := DefaultRollupABIs() + require.NoError(t, err) + calldata, err := abis.Current.Pack("commitBatch", input) + require.NoError(t, err) + + messages := map[uint64]common.Hash{ + 3: common.HexToHash("0x300"), + 4: common.HexToHash("0x400"), + } + data := make([]byte, 10) + binary.BigEndian.PutUint64(data[:8], input.LastBlockNumber) + binary.BigEndian.PutUint16(data[8:], input.NumL1Messages) + data = append(data, messages[3].Bytes()...) + data = append(data, messages[4].Bytes()...) + parentHash, err := parent.Hash() + require.NoError(t, err) + blobHash := common.HexToHash("0xb10b") + expected := BatchHeaderV1{ + BatchHeaderV0: BatchHeaderV0{ + BatchIndex: 8, + L1MessagePopped: 2, + TotalL1MessagePopped: 5, + DataHash: crypto.Keccak256Hash(data), + BlobVersionedHash: blobHash, + PrevStateRoot: common.Hash(input.PrevStateRoot), + PostStateRoot: common.Hash(input.PostStateRoot), + WithdrawalRoot: common.Hash(input.WithdrawalRoot), + ParentBatchHash: parentHash, + }, + LastBlockNumber: input.LastBlockNumber, + }.Bytes() + expectedHash, err := expected.Hash() + require.NoError(t, err) + + backend := &queueBackend{messages: messages, pending: 5, next: 6} + rollup := &canonicalRollupStub{ + blobHash: blobHash, + queueAddress: common.HexToAddress("0x1234"), + } + cache := &BatchCache{ + ctx: context.Background(), + l1Client: backend, + rollupContract: rollup, + l1Snapshot: &confirmedL1Snapshot{number: 99, hash: common.HexToHash("0x99")}, + sealedBatches: make(map[uint64]*eth.RPCRollupBatch), + sealedBatchHeaders: make(map[uint64]*BatchHeaderBytes), + } + tx := ethtypes.NewTx(ðtypes.BlobTx{ + ChainID: uint256.NewInt(1), + GasTipCap: uint256.NewInt(1), + GasFeeCap: uint256.NewInt(1), + Value: uint256.NewInt(0), + Data: calldata, + BlobFeeCap: uint256.NewInt(1), + BlobHashes: []common.Hash{blobHash}, + }) + + got, err := cache.reconstructCommittedBatchHeader(8, expectedHash, tx) + require.NoError(t, err) + require.Equal(t, expected, got) + require.NotEmpty(t, rollup.callBlockReads) + for _, number := range rollup.callBlockReads { + require.Equal(t, uint64(99), number) + } + for _, number := range backend.callBlocks { + require.Equal(t, uint64(99), number) + } + + backend.pending = 4 + _, err = cache.reconstructCommittedBatchHeader(8, expectedHash, tx) + require.ErrorContains(t, err, "cursor mismatch") +} + +func TestConfirmedL1SnapshotHashChangeFailsClosed(t *testing.T) { + number := uint64(14) + backend := &queueBackend{ + latest: 20, + headers: []*ethtypes.Header{ + {Number: new(big.Int).SetUint64(number), Extra: []byte("before")}, + {Number: new(big.Int).SetUint64(number), Extra: []byte("after")}, + }, + } + cache := &BatchCache{ctx: context.Background(), l1Client: backend} + err := cache.withConfirmedL1Snapshot(func() error { + require.Equal(t, number, cache.snapshotCallOpts().BlockNumber.Uint64()) + return nil + }) + require.ErrorContains(t, err, "snapshot changed") + require.Nil(t, cache.l1Snapshot) +} + +type scriptedL2 struct { + safe *ethtypes.Block + numbered map[uint64][]*ethtypes.Block + reads map[uint64]int +} + +func (c *scriptedL2) BlockNumber(context.Context) (uint64, error) { return c.safe.NumberU64(), nil } +func (c *scriptedL2) Len() int { return 1 } +func (c *scriptedL2) BlockByNumber(_ context.Context, number *big.Int) (*ethtypes.Block, error) { + if number.Int64() == int64(rpc.SafeBlockNumber) { + return c.safe, nil + } + n := number.Uint64() + values := c.numbered[n] + if len(values) == 0 { + return nil, nil + } + read := c.reads[n] + if read >= len(values) { + read = len(values) - 1 + } + c.reads[n]++ + return values[read], nil +} + +func l2Block(number uint64, marker string) *ethtypes.Block { + return ethtypes.NewBlockWithHeader(ðtypes.Header{ + Number: new(big.Int).SetUint64(number), + Extra: []byte(marker), + }) +} + +func initializedSafeHeadCache(last uint64, l2 L2MultiClient) *BatchCache { + parent := BatchHeaderV1{ + BatchHeaderV0: BatchHeaderV0{BatchIndex: 3}, + LastBlockNumber: last, + }.Bytes() + return &BatchCache{ + ctx: context.Background(), + initDone: true, + parentBatchHeader: &parent, + lastPackedBlockHeight: last, + currentBlockNumber: last, + batchData: NewBatchData(), + batchConfig: BatchConfig{BlockInterval: 100}, + l2Clients: l2, + } +} + +func TestAssembleCurrentBatchRejectsChangedSafeHeadHash(t *testing.T) { + l2 := &scriptedL2{ + safe: l2Block(10, "safe-before"), + numbered: map[uint64][]*ethtypes.Block{10: {l2Block(10, "safe-after")}}, + reads: make(map[uint64]int), + } + cache := initializedSafeHeadCache(10, l2) + err := cache.AssembleCurrentBatchHeader() + require.ErrorContains(t, err, "safe head changed") +} + +func TestAssembleCurrentBatchRejectsL2Gap(t *testing.T) { + l2 := &scriptedL2{ + safe: l2Block(12, "safe"), + numbered: map[uint64][]*ethtypes.Block{ + 10: {l2Block(10, "parent")}, + 11: {l2Block(12, "wrong-height")}, + }, + reads: make(map[uint64]int), + } + cache := initializedSafeHeadCache(10, l2) + err := cache.AssembleCurrentBatchHeader() + require.ErrorContains(t, err, "L2 gap") +} + +func TestResetCanonicalBootstrapLeavesNoCheckpoint(t *testing.T) { + storage := NewBatchStorage(openTestKV(t)) + storeTestChain(t, storage, []uint64{1, 2}) + header := makeTestHeader(2) + cache := &BatchCache{ + initDone: true, + batchStorage: storage, + sealedBatches: map[uint64]*eth.RPCRollupBatch{2: {}}, + sealedBatchHeaders: map[uint64]*BatchHeaderBytes{2: &header}, + parentBatchHeader: &header, + batchData: NewBatchData(), + currentBlockHash: common.HexToHash("0x1234"), + } + + require.NoError(t, cache.ResetCanonicalBootstrap()) + _, err := storage.loadBatchIndices() + require.True(t, isKVNotFound(err), "checkpoint must be absent after rollback: %v", err) + require.False(t, cache.initDone) + require.Empty(t, cache.sealedBatches) + require.Empty(t, cache.sealedBatchHeaders) + require.Nil(t, cache.parentBatchHeader) + require.Nil(t, cache.batchData) + require.Zero(t, cache.currentBlockHash) +} diff --git a/common/batch/commit_test.go b/common/batch/commit_test.go index c704b885f..da2b84c07 100644 --- a/common/batch/commit_test.go +++ b/common/batch/commit_test.go @@ -13,7 +13,6 @@ import ( "morph-l2/common/blob" "github.com/holiman/uint256" - "github.com/morph-l2/go-ethereum/common" "github.com/morph-l2/go-ethereum/consensus/misc/eip4844" ethtypes "github.com/morph-l2/go-ethereum/core/types" "github.com/morph-l2/go-ethereum/crypto" @@ -34,7 +33,7 @@ func TestRollupWithProof(t *testing.T) { } testDB := openTestKV(t) - cache := NewBatchCache(nil, nil, 2, l1Client, &SingleL2Client{C: l2Client}, rollupContract, l2Gov, testDB) + cache := NewBatchCache(nil, nil, 2, testBatchConfig, l1Client, &SingleL2Client{C: l2Client}, rollupContract, l2Gov, testDB) err := cache.InitFromRollupByRange() require.NoError(t, err) @@ -53,11 +52,6 @@ func TestRollupWithProof(t *testing.T) { batch, err := cache.Get(latestCommitBatchIndex.Uint64() + 1) require.NoError(t, err) - h := crypto.Keccak256Hash(batch.CurrentSequencerSetBytes) - t.Log("sequencer verify hash:", h.String()) - - signature, err := buildSigInput(batch) - require.NoError(t, err) rollupBatch := bindings.IRollupBatchDataInput{ Version: uint8(batch.Version), ParentBatchHeader: batch.ParentBatchHeader, @@ -70,7 +64,7 @@ func TestRollupWithProof(t *testing.T) { tip, gasFeeCap, blobFee, head, err := getGasTipAndCap(l1Client) require.NoError(t, err) - calldata, err := abi.Pack("commitBatch", rollupBatch, *signature) + calldata, err := abi.Pack("commitBatch", rollupBatch) require.NoError(t, err) nonce, err := l1Client.NonceAt(context.Background(), address, nil) require.NoError(t, err) @@ -186,15 +180,6 @@ func getGasTipAndCap(l1client *ethclient.Client) (*big.Int, *big.Int, *big.Int, return tip, gasFeeCap, blobFee, head, nil } -func buildSigInput(batch *eth.RPCRollupBatch) (*bindings.IRollupBatchSignatureInput, error) { - sigData := &bindings.IRollupBatchSignatureInput{ - SignedSequencersBitmap: common.Big0, - SequencerSets: batch.CurrentSequencerSetBytes, - Signature: []byte("0x"), - } - return sigData, nil -} - func sendTx(client *ethclient.Client, txFeeLimit uint64, tx *ethtypes.Transaction) error { if txFeeLimit > 0 { var fee uint64 diff --git a/common/batch/config.go b/common/batch/config.go new file mode 100644 index 000000000..331a01d82 --- /dev/null +++ b/common/batch/config.go @@ -0,0 +1,39 @@ +package batch + +import ( + "encoding/binary" + "errors" + + "github.com/morph-l2/go-ethereum/common" + "github.com/morph-l2/go-ethereum/crypto" +) + +const batchConfigHashDomain = "morph.batch-config.v1" + +// BatchConfig contains the two static batching thresholds used by a submitter. +// BlockInterval is measured in L2 blocks and Timeout is measured in seconds. +// Either threshold may be disabled independently, but disabling both would make +// it impossible to guarantee that an in-progress batch is ever sealed. +type BatchConfig struct { + BlockInterval uint64 + Timeout uint64 +} + +// Validate checks the protocol-level zero-value rule for static batching +// configuration. +func (c BatchConfig) Validate() error { + if c.BlockInterval == 0 && c.Timeout == 0 { + return errors.New("batch block interval and timeout cannot both be zero") + } + return nil +} + +// Hash returns a stable, domain-separated hash suitable for comparing the +// effective configuration across preflight, startup and backup instances. +func (c BatchConfig) Hash() common.Hash { + encoded := make([]byte, len(batchConfigHashDomain)+16) + copy(encoded, batchConfigHashDomain) + binary.BigEndian.PutUint64(encoded[len(batchConfigHashDomain):], c.BlockInterval) + binary.BigEndian.PutUint64(encoded[len(batchConfigHashDomain)+8:], c.Timeout) + return crypto.Keccak256Hash(encoded) +} diff --git a/common/batch/config_test.go b/common/batch/config_test.go new file mode 100644 index 000000000..4c98c3db8 --- /dev/null +++ b/common/batch/config_test.go @@ -0,0 +1,36 @@ +package batch + +import ( + "testing" + + "github.com/morph-l2/go-ethereum/common" + "github.com/stretchr/testify/require" +) + +func TestBatchConfigValidate(t *testing.T) { + tests := []struct { + name string + config BatchConfig + wantErr bool + }{ + {name: "interval only", config: BatchConfig{BlockInterval: 10}}, + {name: "timeout only", config: BatchConfig{Timeout: 60}}, + {name: "both", config: BatchConfig{BlockInterval: 10, Timeout: 60}}, + {name: "both zero", config: BatchConfig{}, wantErr: true}, + } + + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + err := test.config.Validate() + require.Equal(t, test.wantErr, err != nil) + }) + } +} + +func TestBatchConfigHashIsStableAndFieldSensitive(t *testing.T) { + config := BatchConfig{BlockInterval: 10, Timeout: 60} + require.Equal(t, config.Hash(), config.Hash()) + require.NotEqual(t, common.Hash{}, config.Hash()) + require.NotEqual(t, config.Hash(), (BatchConfig{BlockInterval: 11, Timeout: 60}).Hash()) + require.NotEqual(t, config.Hash(), (BatchConfig{BlockInterval: 10, Timeout: 61}).Hash()) +} diff --git a/common/batch/helpers_test.go b/common/batch/helpers_test.go index f248a2228..081acbe11 100644 --- a/common/batch/helpers_test.go +++ b/common/batch/helpers_test.go @@ -14,6 +14,8 @@ import ( ldbutil "github.com/syndtr/goleveldb/leveldb/util" ) +var testBatchConfig = BatchConfig{BlockInterval: 10_000, Timeout: 10_000_000} + type testLevelDB struct { db *leveldb.DB } @@ -28,6 +30,13 @@ func openTestKV(t *testing.T) SealedBatchKV { return &testLevelDB{db: db} } +func requireBatchIntegration(t *testing.T) { + t.Helper() + if os.Getenv("MORPH_BATCH_INTEGRATION") != "1" { + t.Skip("set MORPH_BATCH_INTEGRATION=1 with local L1/L2 RPCs to run integration test") + } +} + func (d *testLevelDB) GetBytes(key []byte) ([]byte, error) { v, err := d.db.Get(key, nil) if err == ldberrors.ErrNotFound { diff --git a/common/batch/interfaces.go b/common/batch/interfaces.go index 739ef422c..46155df06 100644 --- a/common/batch/interfaces.go +++ b/common/batch/interfaces.go @@ -38,7 +38,9 @@ type SealedBatchKV interface { // L1HeaderClient is the L1 RPC surface required to recover batch headers from events. type L1HeaderClient interface { + bind.ContractCaller BlockNumber(ctx context.Context) (uint64, error) + HeaderByNumber(ctx context.Context, number *big.Int) (*ethtypes.Header, error) TransactionByHash(ctx context.Context, hash common.Hash) (*ethtypes.Transaction, bool, error) } @@ -69,22 +71,23 @@ func (s *SingleL2Client) Len() int { return 1 } // RollupBatchReader is the rollup contract view BatchCache needs (subset of generated Rollup bindings). type RollupBatchReader interface { + BatchBlobVersionedHashes(opts *bind.CallOpts, batchIndex *big.Int) ([32]byte, error) CommittedBatches(opts *bind.CallOpts, batchIndex *big.Int) ([32]byte, error) LastCommittedBatchIndex(opts *bind.CallOpts) (*big.Int, error) LastFinalizedBatchIndex(opts *bind.CallOpts) (*big.Int, error) BatchDataStore(opts *bind.CallOpts, batchIndex *big.Int) (struct { - OriginTimestamp *big.Int - FinalizeTimestamp *big.Int - BlockNumber *big.Int - SignedSequencersBitmap *big.Int + OriginTimestamp *big.Int + FinalizeTimestamp *big.Int + BlockNumber *big.Int + Submitter common.Address }, error) - FilterFinalizeBatch(opts *bind.FilterOpts, batchIndex []*big.Int, batchHash [][32]byte) (*bindings.RollupFinalizeBatchIterator, error) + FinalizedStateRoots(opts *bind.CallOpts, batchIndex *big.Int) ([32]byte, error) + MessageQueue(opts *bind.CallOpts) (common.Address, error) + FilterCommitBatch(opts *bind.FilterOpts, batchIndex []*big.Int, batchHash [][32]byte) (*bindings.RollupCommitBatchIterator, error) + FilterRevertBatch(opts *bind.FilterOpts, batchIndex []*big.Int, batchHash [][32]byte) (*bindings.RollupRevertBatchIterator, error) } -// L2GovCaller reads batch-related Gov / bridge / sequencer data on L2. +// L2GovCaller is the sole L2 contract view needed by batch assembly. type L2GovCaller interface { - BatchBlockInterval(opts *bind.CallOpts) (*big.Int, error) - BatchTimeout(opts *bind.CallOpts) (*big.Int, error) GetTreeRoot(opts *bind.CallOpts) ([32]byte, error) - GetSequencerSetBytes(opts *bind.CallOpts) ([]byte, common.Hash, error) } diff --git a/common/batch/l2_gov.go b/common/batch/l2_gov.go index acfe0c0d0..afcab9e07 100644 --- a/common/batch/l2_gov.go +++ b/common/batch/l2_gov.go @@ -1,24 +1,20 @@ package batch import ( - "bytes" "fmt" - "math/big" "morph-l2/bindings/bindings" "morph-l2/bindings/predeploys" "github.com/morph-l2/go-ethereum/accounts/abi/bind" - "github.com/morph-l2/go-ethereum/common" - "github.com/morph-l2/go-ethereum/common/hexutil" - "github.com/morph-l2/go-ethereum/crypto" ) // L2Gov bundles read-only L2 contracts used when assembling rollup batches. +// The name is retained for API compatibility, but batching no longer reads the +// L2 Sequencer or Gov contracts. Only the withdrawal tree root remains an L2 +// contract dependency. type L2Gov struct { - sequencerContract *bindings.SequencerCaller l2MessagePasserContract *bindings.L2ToL1MessagePasserCaller - govContract *bindings.GovCaller } // NewL2Gov builds an L2Gov using any ContractCaller (e.g. *ethclient.Client or a multi-client backend). @@ -26,57 +22,16 @@ func NewL2Gov(backend bind.ContractCaller) (*L2Gov, error) { if backend == nil { return nil, fmt.Errorf("nil contract backend") } - sequencerContract, err := bindings.NewSequencerCaller(predeploys.SequencerAddr, backend) - if err != nil { - return nil, err - } l2MessagePasserContract, err := bindings.NewL2ToL1MessagePasserCaller(predeploys.L2ToL1MessagePasserAddr, backend) if err != nil { return nil, err } - govContract, err := bindings.NewGovCaller(predeploys.GovAddr, backend) - if err != nil { - return nil, err - } return &L2Gov{ - sequencerContract: sequencerContract, l2MessagePasserContract: l2MessagePasserContract, - govContract: govContract, }, nil } -// SequencerSetVerifyHash gets the sequencer set verify hash from the Sequencer contract. -func (c *L2Gov) SequencerSetVerifyHash(opts *bind.CallOpts) ([32]byte, error) { - return c.sequencerContract.SequencerSetVerifyHash(opts) -} - // GetTreeRoot gets the tree root from the L2ToL1MessagePasser contract. func (c *L2Gov) GetTreeRoot(opts *bind.CallOpts) ([32]byte, error) { return c.l2MessagePasserContract.GetTreeRoot(opts) } - -// BatchBlockInterval gets the batch block interval from the Gov contract. -func (c *L2Gov) BatchBlockInterval(opts *bind.CallOpts) (*big.Int, error) { - return c.govContract.BatchBlockInterval(opts) -} - -// BatchTimeout gets the batch timeout from the Gov contract. -func (c *L2Gov) BatchTimeout(opts *bind.CallOpts) (*big.Int, error) { - return c.govContract.BatchTimeout(opts) -} - -// GetSequencerSetBytes returns sequencer set bytes after hash consistency check. -func (c *L2Gov) GetSequencerSetBytes(opts *bind.CallOpts) ([]byte, common.Hash, error) { - hash, err := c.sequencerContract.SequencerSetVerifyHash(opts) - if err != nil { - return nil, common.Hash{}, err - } - setBytes, err := c.sequencerContract.GetSequencerSetBytes(opts) - if err != nil { - return nil, common.Hash{}, err - } - if bytes.Equal(hash[:], crypto.Keccak256Hash(setBytes).Bytes()) { - return setBytes, hash, nil - } - return nil, common.Hash{}, fmt.Errorf("sequencer set hash verify failed %v: %v", hexutil.Encode(setBytes), common.BytesToHash(hash[:]).String()) -} diff --git a/common/batch/snapshot.go b/common/batch/snapshot.go new file mode 100644 index 000000000..604342e30 --- /dev/null +++ b/common/batch/snapshot.go @@ -0,0 +1,101 @@ +package batch + +import ( + "fmt" + "math/big" + + "github.com/morph-l2/go-ethereum/accounts/abi/bind" + "github.com/morph-l2/go-ethereum/common" +) + +const canonicalBootstrapConfirmations uint64 = 6 + +type confirmedL1Snapshot struct { + number uint64 + hash common.Hash +} + +// withConfirmedL1Snapshot pins every contract getter and event query performed +// by fn to one confirmed L1 block number, then verifies the block hash again. +// The surrounding initMu serialization guarantees there is at most one active +// bootstrap snapshot per cache. +func (bc *BatchCache) withConfirmedL1Snapshot(fn func() error) error { + if bc.l1Snapshot != nil { + return fn() + } + + latest, err := bc.l1Client.BlockNumber(bc.ctx) + if err != nil { + return fmt.Errorf("read latest L1 block for canonical bootstrap: %w", err) + } + snapshotNumber := uint64(0) + if latest > canonicalBootstrapConfirmations { + snapshotNumber = latest - canonicalBootstrapConfirmations + } + header, err := bc.l1Client.HeaderByNumber(bc.ctx, new(big.Int).SetUint64(snapshotNumber)) + if err != nil { + return fmt.Errorf("read confirmed L1 snapshot %d: %w", snapshotNumber, err) + } + if header == nil { + return fmt.Errorf("read confirmed L1 snapshot %d: nil header", snapshotNumber) + } + if header.Number == nil || header.Number.Uint64() != snapshotNumber { + return fmt.Errorf("confirmed L1 snapshot number mismatch: requested=%d returned=%v", snapshotNumber, header.Number) + } + + bc.l1Snapshot = &confirmedL1Snapshot{number: snapshotNumber, hash: header.Hash()} + opErr := fn() + verifyErr := bc.verifyConfirmedL1Snapshot() + bc.l1Snapshot = nil + if opErr != nil { + if verifyErr != nil { + return fmt.Errorf("canonical bootstrap failed (%v) and snapshot verification failed: %w", opErr, verifyErr) + } + return opErr + } + return verifyErr +} + +func (bc *BatchCache) verifyConfirmedL1Snapshot() error { + if bc.l1Snapshot == nil { + return fmt.Errorf("canonical bootstrap has no confirmed L1 snapshot") + } + header, err := bc.l1Client.HeaderByNumber(bc.ctx, new(big.Int).SetUint64(bc.l1Snapshot.number)) + if err != nil { + return fmt.Errorf("re-read confirmed L1 snapshot %d: %w", bc.l1Snapshot.number, err) + } + if header == nil { + return fmt.Errorf("re-read confirmed L1 snapshot %d: nil header", bc.l1Snapshot.number) + } + if header.Number == nil || header.Number.Uint64() != bc.l1Snapshot.number { + return fmt.Errorf("re-read confirmed L1 snapshot number mismatch: requested=%d returned=%v", bc.l1Snapshot.number, header.Number) + } + if header.Hash() != bc.l1Snapshot.hash { + return fmt.Errorf( + "confirmed L1 snapshot changed during bootstrap: number=%d expected=%s actual=%s", + bc.l1Snapshot.number, + bc.l1Snapshot.hash, + header.Hash(), + ) + } + return nil +} + +func (bc *BatchCache) snapshotCallOpts() *bind.CallOpts { + opts := &bind.CallOpts{Context: bc.ctx} + if bc.l1Snapshot != nil { + opts.BlockNumber = new(big.Int).SetUint64(bc.l1Snapshot.number) + } + return opts +} + +func (bc *BatchCache) snapshotFilterOpts(start, end uint64) (*bind.FilterOpts, error) { + if bc.l1Snapshot == nil { + return nil, fmt.Errorf("canonical event query requires a confirmed L1 snapshot") + } + if end > bc.l1Snapshot.number { + return nil, fmt.Errorf("canonical event query end %d exceeds snapshot %d", end, bc.l1Snapshot.number) + } + endCopy := end + return &bind.FilterOpts{Start: start, End: &endCopy, Context: bc.ctx}, nil +} From c5033de37cebe9c1532e3eda6c9493a1431016ac Mon Sep 17 00:00:00 2001 From: Segue Date: Tue, 11 Aug 2026 20:40:52 +0800 Subject: [PATCH 06/16] feat(submitter): write fresh canonical batches only --- tx-submitter/README.md | 5 +- tx-submitter/constants/methods.go | 7 +- tx-submitter/entry.go | 55 +- tx-submitter/event/indexer.go | 145 ----- tx-submitter/event/storage.go | 109 ---- tx-submitter/event/storage_test.go | 40 -- tx-submitter/flags/flags.go | 95 ++-- tx-submitter/iface/client.go | 8 +- tx-submitter/iface/rollup.go | 35 +- tx-submitter/mock/event_storage.go | 26 - tx-submitter/mock/l1staking.go | 62 --- tx-submitter/mock/l2client.go | 42 -- tx-submitter/mock/rollup.go | 50 +- tx-submitter/mock/submitter.go | 63 +++ tx-submitter/params/leveldb_keys.go | 5 - tx-submitter/services/batch_fecher.go | 3 +- tx-submitter/services/pendingtx.go | 56 +- tx-submitter/services/pendingtx_test.go | 76 +++ tx-submitter/services/rollup.go | 586 ++++---------------- tx-submitter/services/rollup_handle_test.go | 91 +-- tx-submitter/services/rollup_test.go | 36 +- tx-submitter/services/rotator.go | 158 ------ tx-submitter/types/batch_cache.go | 19 +- tx-submitter/utils/config.go | 105 ++-- tx-submitter/utils/config_test.go | 40 ++ tx-submitter/utils/utils.go | 126 +++-- tx-submitter/utils/utils_test.go | 4 +- 27 files changed, 647 insertions(+), 1400 deletions(-) delete mode 100644 tx-submitter/event/indexer.go delete mode 100644 tx-submitter/event/storage.go delete mode 100644 tx-submitter/event/storage_test.go delete mode 100644 tx-submitter/mock/event_storage.go delete mode 100644 tx-submitter/mock/l1staking.go create mode 100644 tx-submitter/mock/submitter.go delete mode 100644 tx-submitter/params/leveldb_keys.go create mode 100644 tx-submitter/services/pendingtx_test.go delete mode 100644 tx-submitter/services/rotator.go create mode 100644 tx-submitter/utils/config_test.go diff --git a/tx-submitter/README.md b/tx-submitter/README.md index 428197da2..01c4d3fe3 100644 --- a/tx-submitter/README.md +++ b/tx-submitter/README.md @@ -11,6 +11,5 @@ ```bash cast call $ROLLUP_ADDR "lastFinalizedBatchIndex()" -r $L1RPC cast call $ROLLUP_ADDR "lastCommittedBatchIndex()" -r $L1RPC -cast call $L2_SEQUENCER_ADDR "getSequencerSet2()(address[])" -r $L2RPC -cast call $L1_STAKING_ADDR "getActiveStakers()(address[])" -r $L1RPC -``` \ No newline at end of file +cast call $SUBMITTER_ADDR "isActive(address)(bool)" $TX_SUBMITTER_ADDR -r $L1RPC +``` diff --git a/tx-submitter/constants/methods.go b/tx-submitter/constants/methods.go index 7de9eb498..629f87f44 100644 --- a/tx-submitter/constants/methods.go +++ b/tx-submitter/constants/methods.go @@ -3,13 +3,10 @@ package constants const ( // MethodCommitBatch is the method name for committing a batch (with blob when applicable) MethodCommitBatch = "commitBatch" - // MethodCommitState is the method name for recommitting batch state using stored blob hash (no blob in tx) - MethodCommitState = "commitState" // MethodFinalizeBatch is the method name for finalizing a batch MethodFinalizeBatch = "finalizeBatch" ) -// IsCommitLikeMethod returns true for commitBatch or commitState (same calldata shape for batch index parsing). -func IsCommitLikeMethod(method string) bool { - return method == MethodCommitBatch || method == MethodCommitState +func IsOwnedCommitMethod(method string) bool { + return method == MethodCommitBatch } diff --git a/tx-submitter/entry.go b/tx-submitter/entry.go index b67cf0da9..d223b3ce8 100644 --- a/tx-submitter/entry.go +++ b/tx-submitter/entry.go @@ -6,7 +6,6 @@ import ( "crypto/rsa" "fmt" "io" - "math/big" "os" "os/signal" "strings" @@ -14,7 +13,6 @@ import ( "morph-l2/bindings/bindings" "morph-l2/tx-submitter/db" - "morph-l2/tx-submitter/event" "morph-l2/tx-submitter/iface" "morph-l2/tx-submitter/l1checker" "morph-l2/tx-submitter/metrics" @@ -23,7 +21,6 @@ import ( "morph-l2/tx-submitter/utils" "github.com/morph-l2/externalsign" - "github.com/morph-l2/go-ethereum" "github.com/morph-l2/go-ethereum/common" "github.com/morph-l2/go-ethereum/crypto" "github.com/morph-l2/go-ethereum/ethclient" @@ -44,18 +41,28 @@ func Main() func(ctx *cli.Context) error { if err != nil { return err } + if cfg.BatchConfigPreflight { + report, err := cfg.BatchConfigReportJSON() + if err != nil { + return fmt.Errorf("encode batch config preflight: %w", err) + } + _, err = fmt.Fprintln(os.Stdout, string(report)) + return err + } // log start info log.Info("starting tx submitter", "l1_rpc", cfg.L1EthRpc, "l2_rpcs", cfg.L2EthRpcs, "rollup_addr", cfg.RollupAddress, - "l2_sequencer_addr", cfg.L2SequencerAddress, - "l2_gov_addr", cfg.L2GovAddress, - "l1_staking_addr", cfg.L1StakingAddress, + "submitter_addr", cfg.SubmitterAddress, + "batch_block_interval_blocks", cfg.BatchBlockInterval, + "batch_timeout_seconds", cfg.BatchTimeout, + "batch_config_hash", cfg.BatchConfig().Hash(), + "batch_config_source_block_number", cfg.BatchConfigSourceBlockNumber, + "batch_config_source_block_hash", cfg.BatchConfigSourceBlockHash, "fee_limit", cfg.TxFeeLimit, "finalize_enable", cfg.Finalize, - "priority_rollup_enable", cfg.PriorityRollup, "rollup_interval", cfg.RollupInterval.String(), "finalize_interval", cfg.FinalizeInterval.String(), "tx_process_interval", cfg.TxProcessInterval.String(), @@ -64,7 +71,6 @@ func Main() func(ctx *cli.Context) error { "journal_path", cfg.JournalFilePath, "gas_rough_estimate", cfg.RoughEstimateGas, "gas_limit_buffer", cfg.GasLimitBuffer, - "rotator_buffer", cfg.RotatorBuffer, "rough_estimate_gas", cfg.RoughEstimateGas, "rough_estimate_base_gas", cfg.RollupTxGasBase, "rough_estimate_per_l1_msg", cfg.RollupTxGasPerL1Msg, @@ -153,10 +159,10 @@ func Main() func(ctx *cli.Context) error { return fmt.Errorf("failed to get rollup abi: %w", err) } - // l1 staking - l1Staking, err := bindings.NewL1Staking(common.HexToAddress(cfg.L1StakingAddress), l1Client) + // L1 submitter qualification contract. + submitter, err := bindings.NewSubmitter(common.HexToAddress(cfg.SubmitterAddress), l1Client) if err != nil { - return fmt.Errorf("failed to connect to l1 staking contract") + return fmt.Errorf("failed to connect to submitter contract: %w", err) } var rsaPriv *rsa.PrivateKey @@ -178,33 +184,10 @@ func Main() func(ctx *cli.Context) error { } - l1StakingAbi, err := bindings.L1StakingMetaData.GetAbi() - if err != nil { - return fmt.Errorf("failed to get l1 staking abi: %w", err) - } - // new event listener - filter := ethereum.FilterQuery{ - Addresses: []common.Address{common.HexToAddress(cfg.L1StakingAddress)}, - Topics: [][]common.Hash{ - {l1StakingAbi.Events["StakersRemoved"].ID}, - }, - } - ldb, err := db.New(cfg.LeveldbPathName) if err != nil { return fmt.Errorf("failed to connect leveldb: %w", err) } - eventInfoStorage := event.NewEventInfoStorage(ldb) - err = eventInfoStorage.Load() - if err != nil { - return err - } - eventIndexer := event.NewEventIndexer(l1Client, new(big.Int).SetUint64(cfg.L1StakingDeployedBlockNumber), filter, cfg.EventIndexStep, eventInfoStorage) - // new rotator - rotator := services.NewRotator(common.HexToAddress(cfg.L2SequencerAddress), common.HexToAddress(cfg.L2GovAddress), eventIndexer) - // start rorator event indexer - rotator.StartEventIndexer() - // block monitor bm := l1checker.NewBlockMonitor(cfg.BlockNotIncreasedThreshold, l1Client) @@ -220,17 +203,15 @@ func Main() func(ctx *cli.Context) error { l1Client, l2Clients, l1Rollup, - l1Staking, + submitter, chainID, privKey, rollupAddr, rollupAbi, cfg, rsaPriv, - rotator, ldb, bm, - eventInfoStorage, l2Caller, ) diff --git a/tx-submitter/event/indexer.go b/tx-submitter/event/indexer.go deleted file mode 100644 index dc517ba72..000000000 --- a/tx-submitter/event/indexer.go +++ /dev/null @@ -1,145 +0,0 @@ -package event - -import ( - "context" - "math/big" - "time" - - "github.com/morph-l2/go-ethereum" - "github.com/morph-l2/go-ethereum/core/types" - "github.com/morph-l2/go-ethereum/ethclient" - "github.com/morph-l2/go-ethereum/log" -) - -type EventIndexer struct { - client *ethclient.Client - deployBlock *big.Int // Block number of contract deployment - filterQuery ethereum.FilterQuery - indexStep uint64 // index step - storage IEventStorage -} - -func NewEventIndexer(client *ethclient.Client, deployedBlock *big.Int, filter ethereum.FilterQuery, indexStep uint64, storage IEventStorage) *EventIndexer { - return &EventIndexer{ - client: client, - deployBlock: deployedBlock, - filterQuery: filter, - indexStep: indexStep, - storage: storage, - } -} - -func (l *EventIndexer) Index() { - log.Info("event indexer started") - - if l.storage.BlockProcessed() == 0 { - l.storage.SetBlockProcessed(l.deployBlock.Uint64()) - err := l.storage.Store() - if err != nil { - log.Error("failed to store initial block number", "error", err) - } - } - - // Create a ticker that triggers every minute - ticker := time.NewTicker(time.Second * 15) - defer ticker.Stop() - - for range ticker.C { - // Get the current block number - currentBlock, err := l.client.BlockNumber(context.Background()) - if err != nil { - log.Error("failed to get current block number", "error", err) - continue - } - - if currentBlock <= l.storage.BlockProcessed() { - log.Info("no new block to index", "current_block", currentBlock, "last_processed_block", l.storage.BlockProcessed()) - continue - } - - // Perform indexing operation - indexedEventInfo, err := l.index(l.client, big.NewInt(int64(l.storage.BlockProcessed())), big.NewInt(int64(currentBlock))) - if err != nil { - log.Error("indexing operation failed", "error", err) - continue - } - - if indexedEventInfo != nil { - l.storage.SetBlockProcessed(indexedEventInfo.BlockProcessed) - } else { - l.storage.SetBlockProcessed(currentBlock) - } - - // Update storage - err = l.storage.Store() - if err != nil { - log.Error("event index complete, failed to update storage", "error", err) - } else { - info := l.storage.EventInfo() - log.Info("event index complete, storage updated", "processed_block", info.BlockProcessed, "block_time", info.BlockTime) - } - } -} - -// filter logs from from_block to to_block -// if the range is too large, will divide the range -func (ei *EventIndexer) index(client *ethclient.Client, fromBlock, toBlock *big.Int) (*EventInfo, error) { - - endBlock := toBlock.Uint64() - startBlock := endBlock - ei.indexStep - lastProcessedBlock := fromBlock.Uint64() - if startBlock < lastProcessedBlock { - startBlock = lastProcessedBlock - } - logFilter := ei.GetFilter() - - // Find the last unprocessed log - for endBlock >= lastProcessedBlock && endBlock > startBlock { - logFilter.FromBlock = big.NewInt(int64(startBlock)) - logFilter.ToBlock = big.NewInt(int64(endBlock)) - historicalLogs, err := client.FilterLogs(context.Background(), logFilter) - if err != nil { - log.Error("Failed to get historical logs", - "error", err, - "start_block", startBlock, - "end_block", endBlock, - ) - continue - } else { - if len(historicalLogs) > 0 { - latestLog := historicalLogs[len(historicalLogs)-1] - var block *types.Block - for { - block, err = client.BlockByNumber(context.Background(), new(big.Int).SetUint64(latestLog.BlockNumber)) - if err != nil { - log.Error("Failed to get block", "error", err) - time.Sleep(time.Second * 5) - continue - } else { - return &EventInfo{ - BlockProcessed: toBlock.Uint64(), - BlockTime: block.Time(), - }, nil - } - } - } - log.Info("backward indexing", "block_num", startBlock, "target_num", lastProcessedBlock) - // update query range - endBlock = startBlock - if endBlock <= lastProcessedBlock+ei.indexStep { - startBlock = lastProcessedBlock + 1 - } else { - startBlock = endBlock - ei.indexStep - } - - } - } - return nil, nil -} - -func (l *EventIndexer) GetFilter() ethereum.FilterQuery { - return l.filterQuery -} -func (l *EventIndexer) GetStorage() IEventStorage { - return l.storage -} diff --git a/tx-submitter/event/storage.go b/tx-submitter/event/storage.go deleted file mode 100644 index dcec97af0..000000000 --- a/tx-submitter/event/storage.go +++ /dev/null @@ -1,109 +0,0 @@ -package event - -import ( - "encoding/json" - "errors" - "fmt" - "sync" - - "morph-l2/tx-submitter/db" - "morph-l2/tx-submitter/params" -) - -type IEventStorage interface { - Store() error - Load() error - BlockProcessed() uint64 - SetBlockProcessed(blockNum uint64) - BlockTime() uint64 - SetBlockTime(blockTime uint64) - EventInfo() EventInfo -} - -type EventInfo struct { - BlockTime uint64 `json:"block_time"` // event emit time - BlockProcessed uint64 `json:"block_processed"` // block processed -} - -type EventInfoStorage struct { - eventInfo EventInfo - db db.Database - mu sync.RWMutex -} - -func NewEventInfoStorage(db db.Database) *EventInfoStorage { - return &EventInfoStorage{ - db: db, - } -} - -func (e *EventInfoStorage) Store() error { - // Convert struct to JSON string - jsonData, err := json.Marshal(e.eventInfo) - if err != nil { - return fmt.Errorf("failed to convert struct to JSON: %w", err) - } - - e.mu.Lock() - defer e.mu.Unlock() - // Write JSON data to file - err = e.db.PutString(params.EventInfoKey, string(jsonData)) - if err != nil { - return fmt.Errorf("failed to write to db: err=%w, data=%v", err, jsonData) - } - return nil -} - -func (e *EventInfoStorage) Load() error { - e.mu.Lock() - defer e.mu.Unlock() - - jsonStr, err := e.db.GetString(params.EventInfoKey) - if err != nil { - if errors.Is(err, db.ErrKeyNotFound) { - // Initialize with default values if not found - e.eventInfo = EventInfo{} - return nil - } - return fmt.Errorf("failed to read from db: %w", err) - } - - err = json.Unmarshal([]byte(jsonStr), &e.eventInfo) - if err != nil { - return fmt.Errorf("failed to parse JSON: %w", err) - } - - return nil -} - -func (e *EventInfoStorage) BlockProcessed() uint64 { - e.mu.RLock() - defer e.mu.RUnlock() - return e.eventInfo.BlockProcessed -} - -func (e *EventInfoStorage) SetBlockProcessed(blockNum uint64) { - e.mu.Lock() - defer e.mu.Unlock() - e.eventInfo.BlockProcessed = blockNum -} - -func (e *EventInfoStorage) BlockTime() uint64 { - e.mu.RLock() - defer e.mu.RUnlock() - return e.eventInfo.BlockTime -} - -func (e *EventInfoStorage) SetBlockTime(blockTime uint64) { - e.mu.Lock() - defer e.mu.Unlock() - e.eventInfo.BlockTime = blockTime -} - -// EventInfo returns a copy of the current event info. -// This ensures thread safety without exposing the internal state. -func (e *EventInfoStorage) EventInfo() EventInfo { - e.mu.RLock() - defer e.mu.RUnlock() - return e.eventInfo -} diff --git a/tx-submitter/event/storage_test.go b/tx-submitter/event/storage_test.go deleted file mode 100644 index 2299bab52..000000000 --- a/tx-submitter/event/storage_test.go +++ /dev/null @@ -1,40 +0,0 @@ -package event - -import ( - "morph-l2/tx-submitter/db" - "os" - "path/filepath" - "testing" - - "github.com/stretchr/testify/require" -) - -func TestEventInfoStorage(t *testing.T) { - // Create a unique test directory - testDir := filepath.Join(t.TempDir(), "testleveldb") - - // Cleanup before test (in case it exists) - os.RemoveAll(testDir) - - // Cleanup after test - t.Cleanup(func() { - os.RemoveAll(testDir) - }) - - db, err := db.New(testDir) - require.NoError(t, err) - storage := NewEventInfoStorage(db) - err = storage.Load() - require.NoError(t, err) - - storage.SetBlockTime(100) - storage.SetBlockProcessed(100) - err = storage.Store() - require.NoError(t, err) - - storage2 := NewEventInfoStorage(db) - err = storage2.Load() - require.NoError(t, err) - require.Equal(t, storage.BlockTime(), storage2.BlockTime()) - require.Equal(t, storage.BlockProcessed(), storage2.BlockProcessed()) -} diff --git a/tx-submitter/flags/flags.go b/tx-submitter/flags/flags.go index cdbf1405d..568fe1637 100644 --- a/tx-submitter/flags/flags.go +++ b/tx-submitter/flags/flags.go @@ -3,8 +3,6 @@ package flags import ( "time" - "morph-l2/bindings/predeploys" - "github.com/urfave/cli" ) @@ -59,11 +57,11 @@ var ( Required: true, EnvVar: prefixEnvVar("ROLLUP_ADDRESS"), } - L1StakingAddressFlag = cli.StringFlag{ - Name: "l1_staking_address", - Usage: "Address of the staking contract", + SubmitterAddressFlag = cli.StringFlag{ + Name: "submitter_address", + Usage: "Address of the Submitter qualification and stake contract", Required: true, - EnvVar: prefixEnvVar("L1_STAKING_ADDRESS"), + EnvVar: prefixEnvVar("SUBMITTER_ADDRESS"), } // finalize flags @@ -74,28 +72,17 @@ var ( Required: true, } - // decentralize config - PriorityRollupFlag = cli.BoolFlag{ - Name: "priority_rollup", - Usage: "Enable priority rollup", - EnvVar: prefixEnvVar("PRIORITY_ROLLUP"), + BatchBlockIntervalFlag = cli.Uint64Flag{ + Name: "batch_block_interval", + Usage: "Static batch sealing interval in L2 blocks (may be zero if timeout is non-zero)", + EnvVar: prefixEnvVar("BATCH_BLOCK_INTERVAL"), Required: true, } - - // L2 contract - L2SequencerAddressFlag = cli.StringFlag{ - Name: "l2_sequencer_address", - Usage: "Address of the sequencer contract", - Required: false, - EnvVar: prefixEnvVar("L2_SEQUENCER_ADDRESS"), - Value: predeploys.Sequencer, - } - L2GovAddressFlag = cli.StringFlag{ - Name: "l2_gov_address", - Usage: "Address of the gov contract", - Required: false, - EnvVar: prefixEnvVar("L2_GOV_ADDRESS"), - Value: predeploys.Gov, + BatchTimeoutFlag = cli.Uint64Flag{ + Name: "batch_timeout", + Usage: "Static batch sealing timeout in seconds (may be zero if interval is non-zero)", + EnvVar: prefixEnvVar("BATCH_TIMEOUT"), + Required: true, } /* Optional Flags */ @@ -206,12 +193,20 @@ var ( EnvVar: prefixEnvVar("JOURNAL_FILE_PATH"), Value: "journal.rlp", } - // listener processed block record path - StakingEventStoreFileFlag = cli.StringFlag{ - Name: "staking_event_store_filename", - Usage: "The file name of the storage", - EnvVar: prefixEnvVar("STAKING_EVENT_STORE_FILENAME"), - Value: "StakingEventStore.json", + BatchConfigPreflightFlag = cli.BoolFlag{ + Name: "batch_config_preflight", + Usage: "Print the effective static batch configuration and hash, then exit", + EnvVar: prefixEnvVar("BATCH_CONFIG_PREFLIGHT"), + } + BatchConfigSourceBlockNumberFlag = cli.Uint64Flag{ + Name: "batch_config_source_block_number", + Usage: "L2 block number at which the static batch values were snapshotted", + EnvVar: prefixEnvVar("BATCH_CONFIG_SOURCE_BLOCK_NUMBER"), + } + BatchConfigSourceBlockHashFlag = cli.StringFlag{ + Name: "batch_config_source_block_hash", + Usage: "L2 block hash at which the static batch values were snapshotted", + EnvVar: prefixEnvVar("BATCH_CONFIG_SOURCE_BLOCK_HASH"), } TipFeeBumpFlag = cli.Uint64Flag{ @@ -288,28 +283,6 @@ var ( EnvVar: prefixEnvVar("ROUGH_ESTIMATE_GAS"), } - RotatorBufferFlag = cli.Int64Flag{ - Name: "rotator_buffer", - Usage: "rotation interval buffer", - Value: 15, - EnvVar: prefixEnvVar("ROTATOR_BUFFER"), - } - - // l1 staking deployed blocknum - L1StakingDeployedBlocknumFlag = cli.Uint64Flag{ - Name: "l1_staking_deployed_blocknum", - Usage: "The deployed block number of L1Staking", - EnvVar: prefixEnvVar("L1_STAKING_DEPLOYED_BLOCKNUM"), - Required: true, - } - - // event indexer - EventIndexStepFlag = cli.Uint64Flag{ - Name: "event_index_step", - Usage: "The step size for event indexing", - Value: 100, - EnvVar: prefixEnvVar("EVENT_INDEX_STEP"), - } LeveldbPathNameFlag = cli.StringFlag{ Name: "leveldb_path_name", Usage: "The path name of the leveldb", @@ -356,10 +329,10 @@ var requiredFlags = []cli.Flag{ RollupAddressFlag, TxTimeoutFlag, FinalizeFlag, - PriorityRollupFlag, TxFeeLimitFlag, - L1StakingAddressFlag, - L1StakingDeployedBlocknumFlag, + SubmitterAddressFlag, + BatchBlockIntervalFlag, + BatchTimeoutFlag, } var optionalFlags = []cli.Flag{ @@ -382,10 +355,11 @@ var optionalFlags = []cli.Flag{ GasLimitBuffer, JournalFlag, + BatchConfigPreflightFlag, + BatchConfigSourceBlockNumberFlag, + BatchConfigSourceBlockHashFlag, PrivateKeyFlag, - L2SequencerAddressFlag, - L2GovAddressFlag, TipFeeBumpFlag, MaxTipFlag, MinTipFlag, @@ -400,9 +374,6 @@ var optionalFlags = []cli.Flag{ ExternalSignUrl, ExternalSignRsaPriv, RoughEstimateGasFlag, - RotatorBufferFlag, - StakingEventStoreFileFlag, - EventIndexStepFlag, LeveldbPathNameFlag, BlockNotIncreasedThreshold, diff --git a/tx-submitter/iface/client.go b/tx-submitter/iface/client.go index 2081355db..228dd1e4e 100644 --- a/tx-submitter/iface/client.go +++ b/tx-submitter/iface/client.go @@ -254,14 +254,14 @@ func (c *L2Clients) GetBlockTraceByNumber(ctx context.Context, number *big.Int) func (c *L2Clients) GetRollupBatchByIndex(ctx context.Context, batchIndex uint64) (*eth.RPCRollupBatch, error) { var result *eth.RPCRollupBatch err := c.tryAllClients(func(client L2Client) error { - var err error - result, err = client.GetRollupBatchByIndex(ctx, batchIndex) + candidate, err := client.GetRollupBatchByIndex(ctx, batchIndex) if err != nil { return err } - if result != nil && len(result.Signatures) > 0 { - return nil + if candidate == nil { + return errors.New("rollup batch not found") } + result = candidate return nil }) diff --git a/tx-submitter/iface/rollup.go b/tx-submitter/iface/rollup.go index 9a60a64b5..6ee543c6b 100644 --- a/tx-submitter/iface/rollup.go +++ b/tx-submitter/iface/rollup.go @@ -11,41 +11,32 @@ import ( ) type IRollup interface { + BatchBlobVersionedHashes(opts *bind.CallOpts, batchIndex *big.Int) ([32]byte, error) LastCommittedBatchIndex(opts *bind.CallOpts) (*big.Int, error) - CommitBatch(opts *bind.TransactOpts, batchDataInput bindings.IRollupBatchDataInput, batchSignatureInput bindings.IRollupBatchSignatureInput) (*types.Transaction, error) + CommitBatch(opts *bind.TransactOpts, batchDataInput bindings.IRollupBatchDataInput) (*types.Transaction, error) LastFinalizedBatchIndex(opts *bind.CallOpts) (*big.Int, error) + FinalizedStateRoots(opts *bind.CallOpts, batchIndex *big.Int) ([32]byte, error) + MessageQueue(opts *bind.CallOpts) (common.Address, error) FinalizeBatch(*bind.TransactOpts, []byte) (*types.Transaction, error) BatchInsideChallengeWindow(opts *bind.CallOpts, batchIndex *big.Int) (bool, error) BatchExist(opts *bind.CallOpts, batchIndex *big.Int) (bool, error) CommittedBatches(opts *bind.CallOpts, batchIndex *big.Int) ([32]byte, error) - BatchBlobVersionedHashes(opts *bind.CallOpts, batchIndex *big.Int) ([32]byte, error) BatchDataStore(opts *bind.CallOpts, batchIndex *big.Int) (struct { - OriginTimestamp *big.Int - FinalizeTimestamp *big.Int - BlockNumber *big.Int - SignedSequencersBitmap *big.Int + OriginTimestamp *big.Int + FinalizeTimestamp *big.Int + BlockNumber *big.Int + Submitter common.Address }, error) FilterCommitBatch(opts *bind.FilterOpts, batchIndex []*big.Int, batchHash [][32]byte) (*bindings.RollupCommitBatchIterator, error) FilterFinalizeBatch(opts *bind.FilterOpts, batchIndex []*big.Int, batchHash [][32]byte) (*bindings.RollupFinalizeBatchIterator, error) + FilterRevertBatch(opts *bind.FilterOpts, batchIndex []*big.Int, batchHash [][32]byte) (*bindings.RollupRevertBatchIterator, error) } -// IL2Sequencer is the interface for the sequencer on L2 -type IL2Sequencer interface { - SequencerSetVerifyHash(opts *bind.CallOpts) ([32]byte, error) -} - -type IL2Gov interface { - RollupEpoch(opts *bind.CallOpts) (*big.Int, error) - BatchBlockInterval(opts *bind.CallOpts) (*big.Int, error) - BatchTimeout(opts *bind.CallOpts) (*big.Int, error) -} - -type IL1Staking interface { - IsStaker(opts *bind.CallOpts, addr common.Address) (bool, error) - GetStakersBitmap(opts *bind.CallOpts, _stakers []common.Address) (*big.Int, error) - GetActiveStakers(opts *bind.CallOpts) ([]common.Address, error) - GetStakers(opts *bind.CallOpts) ([255]common.Address, error) +// ISubmitter is the minimum qualification surface required by the official +// submitter. Selection/rotation is deliberately not part of this interface. +type ISubmitter interface { + IsActive(opts *bind.CallOpts, addr common.Address) (bool, error) } type IL2MessagePasser interface { diff --git a/tx-submitter/mock/event_storage.go b/tx-submitter/mock/event_storage.go deleted file mode 100644 index e26087720..000000000 --- a/tx-submitter/mock/event_storage.go +++ /dev/null @@ -1,26 +0,0 @@ -package mock - -import ( - "morph-l2/tx-submitter/event" -) - -// MockEventInfoStorage is a mock implementation of the EventInfoStorage -type MockEventInfoStorage struct { - event.EventInfoStorage -} - -// NewMockEventInfoStorage creates a new mock EventInfoStorage -func NewMockEventInfoStorage() *event.EventInfoStorage { - mockDB := NewMockDB() - return event.NewEventInfoStorage(mockDB) -} - -// Store implements the Store method -func (m *MockEventInfoStorage) Store() error { - return nil -} - -// Load implements the Load method -func (m *MockEventInfoStorage) Load() error { - return nil -} diff --git a/tx-submitter/mock/l1staking.go b/tx-submitter/mock/l1staking.go deleted file mode 100644 index 08939b834..000000000 --- a/tx-submitter/mock/l1staking.go +++ /dev/null @@ -1,62 +0,0 @@ -package mock - -import ( - "math/big" - - "github.com/morph-l2/go-ethereum/accounts/abi/bind" - "github.com/morph-l2/go-ethereum/common" -) - -// MockL1Staking is a mock implementation of IL1Staking -type MockL1Staking struct { - activeStakers []common.Address -} - -// NewMockL1Staking creates a new mock L1Staking instance -func NewMockL1Staking() *MockL1Staking { - return &MockL1Staking{ - activeStakers: []common.Address{}, - } -} - -// IsStaker implements IL1Staking -func (m *MockL1Staking) IsStaker(opts *bind.CallOpts, addr common.Address) (bool, error) { - for _, staker := range m.activeStakers { - if staker == addr { - return true, nil - } - } - return false, nil -} - -// GetStakersBitmap implements IL1Staking -func (m *MockL1Staking) GetStakersBitmap(opts *bind.CallOpts, _stakers []common.Address) (*big.Int, error) { - bitmap := big.NewInt(0) - for i, staker := range _stakers { - for _, activeStaker := range m.activeStakers { - if staker == activeStaker { - bitmap.SetBit(bitmap, i, 1) - } - } - } - return bitmap, nil -} - -// GetActiveStakers implements IL1Staking -func (m *MockL1Staking) GetActiveStakers(opts *bind.CallOpts) ([]common.Address, error) { - return m.activeStakers, nil -} - -// GetStakers implements IL1Staking -func (m *MockL1Staking) GetStakers(opts *bind.CallOpts) ([255]common.Address, error) { - var result [255]common.Address - for i := 0; i < len(m.activeStakers) && i < 255; i++ { - result[i] = m.activeStakers[i] - } - return result, nil -} - -// SetActiveStakers sets the active stakers for testing -func (m *MockL1Staking) SetActiveStakers(stakers []common.Address) { - m.activeStakers = stakers -} diff --git a/tx-submitter/mock/l2client.go b/tx-submitter/mock/l2client.go index d1acf1b8a..c73e1c816 100644 --- a/tx-submitter/mock/l2client.go +++ b/tx-submitter/mock/l2client.go @@ -3,7 +3,6 @@ package mock import ( "context" "math/big" - "time" "github.com/morph-l2/go-ethereum" "github.com/morph-l2/go-ethereum/common" @@ -21,27 +20,16 @@ type L2ClientWrapper struct { TipCap *big.Int reorgDepth int64 reorgCount int64 - sequencerSet []common.Address BaseFee *big.Int } func NewL2ClientWrapper() *L2ClientWrapper { - // Create mock CallContractResult for epoch update time - // Return a timestamp from 1 hour ago - timestamp := time.Now().Add(-1 * time.Hour).Unix() result := make([]byte, 32) - big.NewInt(timestamp).FillBytes(result) - - // Create a mock sequencer set with two addresses - mockAddr1 := common.HexToAddress("0x1111111111111111111111111111111111111111") - mockAddr2 := common.HexToAddress("0x2222222222222222222222222222222222222222") - sequencerSet := []common.Address{mockAddr1, mockAddr2} return &L2ClientWrapper{ TipCap: big.NewInt(1e9), Balance: big.NewInt(1e18), CallContractResult: result, - sequencerSet: sequencerSet, BaseFee: big.NewInt(1e9), } } @@ -52,31 +40,6 @@ func (l *L2ClientWrapper) CallContract(ctx context.Context, msg ethereum.CallMsg return nil, l.CallContractErr } - // If calling the sequencer set method, return the mock sequencer set - if len(msg.Data) >= 4 { - methodID := msg.Data[:4] - // Method ID for GetSequencerSet2() - if string(methodID) == "\x77\xd7\xdf\xfb" { - // Encode the sequencer set into bytes - result := make([]byte, 0) - // First 32 bytes for offset - offset := make([]byte, 32) - big.NewInt(32).FillBytes(offset) - result = append(result, offset...) - // Next 32 bytes for length - length := make([]byte, 32) - big.NewInt(int64(len(l.sequencerSet))).FillBytes(length) - result = append(result, length...) - // Then the addresses - for _, addr := range l.sequencerSet { - addrBytes := make([]byte, 32) - copy(addrBytes[12:], addr.Bytes()) - result = append(result, addrBytes...) - } - return result, nil - } - } - return l.CallContractResult, nil } @@ -207,8 +170,3 @@ func (l *L2ClientWrapper) SuggestGasTipCap(ctx context.Context) (*big.Int, error } return big.NewInt(1e9), nil } - -// GetSequencerSet2 implements IL2Sequencer -func (m *L2ClientWrapper) GetSequencerSet2() ([]common.Address, error) { - return m.sequencerSet, nil -} diff --git a/tx-submitter/mock/rollup.go b/tx-submitter/mock/rollup.go index cf889c6a4..6542fc38e 100644 --- a/tx-submitter/mock/rollup.go +++ b/tx-submitter/mock/rollup.go @@ -7,6 +7,7 @@ import ( "github.com/morph-l2/go-ethereum/core/types" "github.com/morph-l2/go-ethereum/accounts/abi/bind" + "github.com/morph-l2/go-ethereum/common" "morph-l2/bindings/bindings" ) @@ -21,6 +22,16 @@ type MockRollup struct { finalizeTx *types.Transaction } +// BatchBlobVersionedHashes implements IRollup. +func (m *MockRollup) BatchBlobVersionedHashes(opts *bind.CallOpts, batchIndex *big.Int) ([32]byte, error) { + return [32]byte{}, nil +} + +// MessageQueue implements IRollup. +func (m *MockRollup) MessageQueue(opts *bind.CallOpts) (common.Address, error) { + return common.Address{}, nil +} + // NewMockRollup creates a new instance of MockRollup func NewMockRollup() *MockRollup { return &MockRollup{ @@ -37,10 +48,14 @@ func (m *MockRollup) LastCommittedBatchIndex(opts *bind.CallOpts) (*big.Int, err } // CommitBatch implements IRollup -func (m *MockRollup) CommitBatch(opts *bind.TransactOpts, batchDataInput bindings.IRollupBatchDataInput, batchSignatureInput bindings.IRollupBatchSignatureInput) (*types.Transaction, error) { +func (m *MockRollup) CommitBatch(opts *bind.TransactOpts, batchDataInput bindings.IRollupBatchDataInput) (*types.Transaction, error) { return m.batchTx, nil } +func (m *MockRollup) FinalizedStateRoots(opts *bind.CallOpts, batchIndex *big.Int) ([32]byte, error) { + return [32]byte{}, nil +} + // LastFinalizedBatchIndex implements IRollup func (m *MockRollup) LastFinalizedBatchIndex(opts *bind.CallOpts) (*big.Int, error) { return m.lastFinalizedBatchIndex, nil @@ -66,28 +81,22 @@ func (m *MockRollup) CommittedBatches(opts *bind.CallOpts, batchIndex *big.Int) return [32]byte{}, nil } -// BatchBlobVersionedHashes implements IRollup (no stored hash by default) -func (m *MockRollup) BatchBlobVersionedHashes(opts *bind.CallOpts, batchIndex *big.Int) ([32]byte, error) { - return [32]byte{}, nil -} - // BatchDataStore implements IRollup func (m *MockRollup) BatchDataStore(opts *bind.CallOpts, batchIndex *big.Int) (struct { - OriginTimestamp *big.Int - FinalizeTimestamp *big.Int - BlockNumber *big.Int - SignedSequencersBitmap *big.Int + OriginTimestamp *big.Int + FinalizeTimestamp *big.Int + BlockNumber *big.Int + Submitter common.Address }, error) { return struct { - OriginTimestamp *big.Int - FinalizeTimestamp *big.Int - BlockNumber *big.Int - SignedSequencersBitmap *big.Int + OriginTimestamp *big.Int + FinalizeTimestamp *big.Int + BlockNumber *big.Int + Submitter common.Address }{ - OriginTimestamp: big.NewInt(0), - FinalizeTimestamp: big.NewInt(0), - BlockNumber: big.NewInt(0), - SignedSequencersBitmap: big.NewInt(0), + OriginTimestamp: big.NewInt(0), + FinalizeTimestamp: big.NewInt(0), + BlockNumber: big.NewInt(0), }, nil } @@ -101,6 +110,11 @@ func (m *MockRollup) FilterFinalizeBatch(opts *bind.FilterOpts, batchIndex []*bi return nil, nil } +// FilterRevertBatch implements IRollup. +func (m *MockRollup) FilterRevertBatch(opts *bind.FilterOpts, batchIndex []*big.Int, batchHash [][32]byte) (*bindings.RollupRevertBatchIterator, error) { + return nil, errors.New("FilterRevertBatch not implemented in mock") +} + // SetLastCommittedBatchIndex sets the mock value for LastCommittedBatchIndex func (m *MockRollup) SetLastCommittedBatchIndex(index *big.Int) { m.lastCommittedBatchIndex = index diff --git a/tx-submitter/mock/submitter.go b/tx-submitter/mock/submitter.go new file mode 100644 index 000000000..03a760318 --- /dev/null +++ b/tx-submitter/mock/submitter.go @@ -0,0 +1,63 @@ +package mock + +import ( + "sync" + + "github.com/morph-l2/go-ethereum/accounts/abi/bind" + "github.com/morph-l2/go-ethereum/common" +) + +// MockSubmitter implements the minimal submitter qualification view used by +// the transaction submitter. +type MockSubmitter struct { + mu sync.RWMutex + active map[common.Address]bool + defaultOn bool + lastQuery common.Address + err error +} + +func NewMockSubmitter() *MockSubmitter { + return &MockSubmitter{ + active: make(map[common.Address]bool), + defaultOn: true, + } +} + +func (m *MockSubmitter) IsActive(_ *bind.CallOpts, addr common.Address) (bool, error) { + m.mu.Lock() + defer m.mu.Unlock() + m.lastQuery = addr + if m.err != nil { + return false, m.err + } + active, configured := m.active[addr] + if !configured { + return m.defaultOn, nil + } + return active, nil +} + +func (m *MockSubmitter) SetActive(addr common.Address, active bool) { + m.mu.Lock() + defer m.mu.Unlock() + m.active[addr] = active +} + +func (m *MockSubmitter) SetDefaultActive(active bool) { + m.mu.Lock() + defer m.mu.Unlock() + m.defaultOn = active +} + +func (m *MockSubmitter) SetError(err error) { + m.mu.Lock() + defer m.mu.Unlock() + m.err = err +} + +func (m *MockSubmitter) LastQuery() common.Address { + m.mu.RLock() + defer m.mu.RUnlock() + return m.lastQuery +} diff --git a/tx-submitter/params/leveldb_keys.go b/tx-submitter/params/leveldb_keys.go deleted file mode 100644 index b998e6de9..000000000 --- a/tx-submitter/params/leveldb_keys.go +++ /dev/null @@ -1,5 +0,0 @@ -package params - -const ( - EventInfoKey = "event_info" -) diff --git a/tx-submitter/services/batch_fecher.go b/tx-submitter/services/batch_fecher.go index 7c658ab81..f87b11fdc 100644 --- a/tx-submitter/services/batch_fecher.go +++ b/tx-submitter/services/batch_fecher.go @@ -27,8 +27,7 @@ func (bf *BatchFetcher) GetRollupBatchByIndex(index uint64) (*eth.RPCRollupBatch lastErr = err continue } - // Validate that batch exists and has signatures before returning - if batch != nil && len(batch.Signatures) > 0 { + if batch != nil { return batch, nil } } diff --git a/tx-submitter/services/pendingtx.go b/tx-submitter/services/pendingtx.go index b94735b94..02632003a 100644 --- a/tx-submitter/services/pendingtx.go +++ b/tx-submitter/services/pendingtx.go @@ -1,6 +1,7 @@ package services import ( + "bytes" "fmt" "sort" "sync" @@ -40,7 +41,7 @@ type PendingTxs struct { } // NewPendingTxs creates a new PendingTxs instance. -// Commit-like txs (commitBatch / commitState) are detected via utils.ParseMethod + constants.IsCommitLikeMethod, not stored method IDs. +// Only current commitBatch transactions are owned by this process. func NewPendingTxs(journal Journal) *PendingTxs { return &PendingTxs{ txinfos: make(map[common.Hash]*types.TxRecord), @@ -221,8 +222,11 @@ func (pt *PendingTxs) ExistedIndex(index uint64) bool { for i := len(txs) - 1; i >= 0; i-- { tx := txs[i].Tx - if constants.IsCommitLikeMethod(utils.ParseMethod(tx, abi)) { - pindex := utils.ParseParentBatchIndex(tx.Data()) + 1 + if constants.IsOwnedCommitMethod(utils.ParseMethod(tx, abi)) { + pindex, err := utils.ParseOwnedCommitBatchIndex(tx.Data(), abi) + if err != nil { + continue + } if index == pindex { return true } @@ -242,20 +246,40 @@ func (pt *PendingTxs) Recover(txs []*ethtypes.Transaction, abi *abi.ABI) error { var maxCommitBatchIndex, maxFinalizeBatchIndex uint64 for _, tx := range txs { - // Get method name - method := utils.ParseMethod(tx, abi) - - // Get batch index based on method + method := "cancel" var batchIndex uint64 - if constants.IsCommitLikeMethod(method) { - batchIndex = utils.ParseParentBatchIndex(tx.Data()) + 1 - if batchIndex > maxCommitBatchIndex { - maxCommitBatchIndex = batchIndex - } - } else if method == constants.MethodFinalizeBatch { - batchIndex = utils.ParseFBatchIndex(tx.Data()) - if batchIndex > maxFinalizeBatchIndex { - maxFinalizeBatchIndex = batchIndex + var err error + finalizeMethod, hasFinalizeMethod := abi.Methods[constants.MethodFinalizeBatch] + if len(tx.Data()) != 0 { + ownedMethod, _, ownedErr := utils.ParseOwnedCommit(tx.Data(), abi) + switch { + case ownedErr == nil: + method = ownedMethod + if tx.Type() != ethtypes.BlobTxType || tx.BlobTxSidecar() == nil || len(tx.BlobTxSidecar().Blobs) == 0 || len(tx.BlobHashes()) == 0 { + return fmt.Errorf("owned commit journal entry %s is missing its blob sidecar; use a fresh data directory", tx.Hash()) + } + batchIndex, err = utils.ParseOwnedCommitBatchIndex(tx.Data(), abi) + if err != nil { + return fmt.Errorf("invalid owned commit journal entry %s: %w", tx.Hash(), err) + } + if batchIndex > maxCommitBatchIndex { + maxCommitBatchIndex = batchIndex + } + case hasFinalizeMethod && len(tx.Data()) >= 4 && bytes.Equal(tx.Data()[:4], finalizeMethod.ID): + method = constants.MethodFinalizeBatch + if _, err := finalizeMethod.Inputs.Unpack(tx.Data()[4:]); err != nil { + return fmt.Errorf("invalid finalize journal entry %s: %w", tx.Hash(), err) + } + batchIndex = utils.ParseFBatchIndex(tx.Data()) + if batchIndex > maxFinalizeBatchIndex { + maxFinalizeBatchIndex = batchIndex + } + default: + selector := tx.Data() + if len(selector) > 4 { + selector = selector[:4] + } + return fmt.Errorf("unsupported legacy or unknown journal calldata in tx %s (selector 0x%x); use a fresh data directory", tx.Hash(), selector) } } diff --git a/tx-submitter/services/pendingtx_test.go b/tx-submitter/services/pendingtx_test.go new file mode 100644 index 000000000..370f1a0ad --- /dev/null +++ b/tx-submitter/services/pendingtx_test.go @@ -0,0 +1,76 @@ +package services + +import ( + "encoding/binary" + "encoding/hex" + "math/big" + "testing" + + "github.com/holiman/uint256" + "github.com/morph-l2/go-ethereum/common" + ethtypes "github.com/morph-l2/go-ethereum/core/types" + "github.com/morph-l2/go-ethereum/crypto/kzg4844" + "github.com/stretchr/testify/require" + + "morph-l2/bindings/bindings" + "morph-l2/tx-submitter/mock" +) + +func TestPendingTxsRecoverRejectsNonOwnedCalldata(t *testing.T) { + current, err := bindings.RollupMetaData.GetAbi() + require.NoError(t, err) + + selectors := map[string]string{ + "pre submitter commitBatch": "428868b5", + "pre submitter commitState": "1e8825be", + "pre submitter commitBatchWithProof": "4e8f1d67", + "unknown": "deadbeef", + } + selectors["current commitState"] = hex.EncodeToString(current.Methods["commitState"].ID) + selectors["current commitBatchWithProof"] = hex.EncodeToString(current.Methods["commitBatchWithProof"].ID) + + for name, selector := range selectors { + t.Run(name, func(t *testing.T) { + data, err := hex.DecodeString(selector) + require.NoError(t, err) + tx := ethtypes.NewTx(ðtypes.DynamicFeeTx{ + ChainID: big.NewInt(1), + To: &common.Address{}, + Data: data, + }) + pending := NewPendingTxs(mock.NewMockJournal()) + err = pending.Recover([]*ethtypes.Transaction{tx}, current) + require.ErrorContains(t, err, "legacy or unknown journal calldata") + require.Zero(t, pending.Len()) + }) + } +} + +func TestPendingTxsRecoverAcceptsCurrentFreshBlobCommit(t *testing.T) { + current, err := bindings.RollupMetaData.GetAbi() + require.NoError(t, err) + require.Equal(t, "41f756da", hex.EncodeToString(current.Methods["commitBatch"].ID)) + + parentHeader := make([]byte, 9) + binary.BigEndian.PutUint64(parentHeader[1:], 7) + calldata, err := current.Pack("commitBatch", bindings.IRollupBatchDataInput{ + ParentBatchHeader: parentHeader, + }) + require.NoError(t, err) + + tx := ethtypes.NewTx(ðtypes.BlobTx{ + ChainID: uint256.NewInt(1), + To: common.Address{}, + Data: calldata, + BlobFeeCap: uint256.NewInt(1), + BlobHashes: []common.Hash{{1}}, + Sidecar: ðtypes.BlobTxSidecar{ + Blobs: []kzg4844.Blob{{1}}, + Commitments: []kzg4844.Commitment{{1}}, + }, + }) + pending := NewPendingTxs(mock.NewMockJournal()) + require.NoError(t, pending.Recover([]*ethtypes.Transaction{tx}, current)) + require.Equal(t, 1, pending.Len()) + require.Equal(t, uint64(8), pending.GetPindex()) +} diff --git a/tx-submitter/services/rollup.go b/tx-submitter/services/rollup.go index 2df98cffd..34a9a9c66 100644 --- a/tx-submitter/services/rollup.go +++ b/tx-submitter/services/rollup.go @@ -30,7 +30,6 @@ import ( "morph-l2/common/blob" "morph-l2/tx-submitter/constants" "morph-l2/tx-submitter/db" - "morph-l2/tx-submitter/event" "morph-l2/tx-submitter/iface" "morph-l2/tx-submitter/l1checker" "morph-l2/tx-submitter/localpool" @@ -42,26 +41,23 @@ import ( const ( txSlotSize = 32 * 1024 txMaxSize = 4 * txSlotSize // 128KB - rotatorWait = 3 * time.Second rollupSumKey = "rollup_sum" finalizeSumKey = "finalize_sum" collectedL1FeeSumKey = "collected_l1_fee_sum" ) type Rollup struct { - ctx context.Context - metrics *metrics.Metrics - l1RpcClient *rpc.Client - L1Client iface.Client - L2Clients []iface.L2Client - Rollup iface.IRollup - Staking iface.IL1Staking - chainId *big.Int - privKey *ecdsa.PrivateKey - rollupAddr common.Address - abi *abi.ABI - // rotator - rotator *Rotator + ctx context.Context + metrics *metrics.Metrics + l1RpcClient *rpc.Client + L1Client iface.Client + L2Clients []iface.L2Client + Rollup iface.IRollup + Submitter iface.ISubmitter + chainId *big.Int + privKey *ecdsa.PrivateKey + rollupAddr common.Address + abi *abi.ABI pendingTxs *PendingTxs rollupFinalizeMu sync.Mutex externalRsaPriv *rsa.PrivateKey @@ -81,7 +77,6 @@ type Rollup struct { batchCache *batch.BatchCache batchCacheLegacy *types.BatchCacheLegacy bm *l1checker.BlockMonitor - eventInfoStorage *event.EventInfoStorage reorgDetector iface.IReorgDetector ChainConfigMap blob.ChainBlobConfigs @@ -94,17 +89,15 @@ func NewRollup( l1 iface.Client, l2Clients []iface.L2Client, rollup iface.IRollup, - staking iface.IL1Staking, + submitter iface.ISubmitter, chainId *big.Int, priKey *ecdsa.PrivateKey, rollupAddr common.Address, abi *abi.ABI, cfg utils.Config, rsaPriv *rsa.PrivateKey, - rotator *Rotator, ldb *db.Db, bm *l1checker.BlockMonitor, - eventInfoStorage *event.EventInfoStorage, l2Caller *types.L2Caller, ) *Rollup { reorgDetector := NewReorgDetector(l1) @@ -114,13 +107,12 @@ func NewRollup( l1RpcClient: l1RpcClient, L1Client: l1, Rollup: rollup, - Staking: staking, + Submitter: submitter, L2Clients: l2Clients, privKey: priKey, chainId: chainId, rollupAddr: rollupAddr, abi: abi, - rotator: rotator, cfg: cfg, signer: ethtypes.LatestSignerForChainID(chainId), externalRsaPriv: rsaPriv, @@ -130,17 +122,17 @@ func NewRollup( return cfg.BatchV2UpgradeTime > 0 && blockTimestamp >= cfg.BatchV2UpgradeTime }, cfg.MaxBlobCount, + cfg.BatchConfig(), l1, &iface.L2Clients{Clients: l2Clients}, rollup, l2Caller, ldb, ), - ldb: ldb, - bm: bm, - eventInfoStorage: eventInfoStorage, - reorgDetector: reorgDetector, - ChainConfigMap: blob.ChainConfigMap, + ldb: ldb, + bm: bm, + reorgDetector: reorgDetector, + ChainConfigMap: blob.ChainConfigMap, } if !cfg.SealBatch { fetcher := NewBatchFetcher(l2Clients) @@ -156,26 +148,38 @@ func (r *Rollup) Start() error { return err } + // Cold start is synchronous and fail-closed. On an empty database this + // reconstructs the finalized anchor and committed range from canonical L1, + // then packs every L2 block through the observed head before any journal or + // submission loop is created. + if r.cfg.SealBatch { + if err := r.batchCache.InitAndSyncFromDatabase(); err != nil { + return r.rollbackCanonicalBootstrap("initialize batch cache from canonical state", err) + } + if err := r.batchCache.AssembleCurrentBatchHeader(); err != nil { + return r.rollbackCanonicalBootstrap("assemble downtime L2 blocks", err) + } + } + // journal jn := localpool.New(r.cfg.JournalFilePath) - err := jn.Init() - if err != nil { - log.Crit("journal file init failed", "err", err) + if err := jn.Init(); err != nil { + return r.failStartup("journal file init failed", err) } // pendingtxs r.pendingTxs = NewPendingTxs(jn) txs, err := jn.ParseAllTxs() if err != nil { - log.Crit("parse l1 mempool error", "error", err) + return r.failStartup("parse pending transaction journal", err) } if err := r.pendingTxs.Recover(txs, r.abi); err != nil { - log.Crit("failed to recover pending transactions", "error", err) + return r.failStartup("recover pending transactions", err) } // init fee metrics sum err = r.InitFeeMetricsSum() if err != nil { - return fmt.Errorf("init fee metrics sum failed: %w", err) + return r.failStartup("init fee metrics sum failed", err) } /// start services @@ -274,21 +278,6 @@ func (r *Rollup) Start() error { if r.cfg.SealBatch { var batchCacheSyncMu sync.Mutex - - go func() { - batchCacheSyncMu.Lock() - defer batchCacheSyncMu.Unlock() - for { - - if err = r.batchCache.InitAndSyncFromDatabase(); err != nil { - log.Error("init and sync from database failed, wait for the next try", "error", err) - time.Sleep(5 * time.Second) - continue - } - break - } - }() - go utils.Loop(r.ctx, r.cfg.TxProcessInterval, func() { batchCacheSyncMu.Lock() defer batchCacheSyncMu.Unlock() @@ -308,6 +297,20 @@ func (r *Rollup) Start() error { return nil } +func (r *Rollup) rollbackCanonicalBootstrap(stage string, cause error) error { + if resetErr := r.batchCache.ResetCanonicalBootstrap(); resetErr != nil { + return fmt.Errorf("%s: %v; rollback canonical bootstrap: %w", stage, cause, resetErr) + } + return fmt.Errorf("%s: %w", stage, cause) +} + +func (r *Rollup) failStartup(stage string, cause error) error { + if r.cfg.SealBatch { + return r.rollbackCanonicalBootstrap(stage, cause) + } + return fmt.Errorf("%s: %w", stage, cause) +} + func (r *Rollup) ProcessTx() error { // Check for reorgs first with exponential backoff retry hasReorg, depth, err := r.detectReorgWithRetry() @@ -326,45 +329,6 @@ func (r *Rollup) ProcessTx() error { return nil } - // Check if this submitter should process transactions - if err = r.checkSubmitterTurn(); err != nil { - if errors.Is(err, errNotMyTurn) { - // If rotator is not configured or not yet initialized, just skip this round safely. - if r.rotator == nil || r.rotator.startTime == nil || r.rotator.epoch == nil { - log.Info("Awaiting turn for transaction processing, but rotator state is not initialized", - "has_rotator", r.rotator != nil) - return nil - } - - // Get current submitter index for logging - activeSubmitter, activeIndex, rotErr := r.rotator.CurrentSubmitter(r.L2Clients, r.Staking) - if rotErr != nil || activeSubmitter == nil { - log.Warn("Failed to get current submitter while awaiting turn", - "error", rotErr) - return nil - } - - // Calculate rotation timing information - past := (time.Now().Unix() - r.rotator.startTime.Int64()) % r.rotator.epoch.Int64() - start := time.Now().Unix() - past - end := start + r.rotator.epoch.Int64() - timeLeft := end - time.Now().Unix() - - // Format timestamps for human readability - endTimeFormatted := utils.FormatTime(big.NewInt(end)) - timeLeftFormatted := fmt.Sprintf("%dm%ds", timeLeft/60, timeLeft%60) - - log.Info("Awaiting turn for transaction processing", - "current_submitter", activeSubmitter.Hex(), - "submitter_index", activeIndex, - "total_submitters", len(r.rotator.GetSubmitterSet()), - "next_rotation", endTimeFormatted, - "time_remaining", timeLeftFormatted) - return nil - } - return err - } - // Process each transaction for _, txRecord := range txRecords { if err := r.processSingleTx(txRecord); err != nil { @@ -392,59 +356,6 @@ func (r *Rollup) detectReorgWithRetry() (bool, uint64, error) { return false, 0, lastErr } -var errNotMyTurn = errors.New("not my turn") - -func (r *Rollup) checkSubmitterTurn() error { - // If we are in priority rollup mode or rotator is not configured, always allow processing. - if r.cfg.PriorityRollup || r.rotator == nil { - return nil - } - - activeSubmitter, submitterIndex, err := r.rotator.CurrentSubmitter(r.L2Clients, r.Staking) - if err != nil { - return fmt.Errorf("rollup: get current submitter err, %w", err) - } - - myAddress := r.WalletAddr().Hex() - activeAddress := activeSubmitter.Hex() - isMyTurn := activeAddress == myAddress - - // Calculate rotation timing information - if r.rotator.startTime == nil || r.rotator.epoch == nil { - log.Warn("rotator state not initialized, skipping submitter turn check", - "has_rotator", r.rotator != nil) - return errNotMyTurn - } - past := (time.Now().Unix() - r.rotator.startTime.Int64()) % r.rotator.epoch.Int64() - start := time.Now().Unix() - past - end := start + r.rotator.epoch.Int64() - timeLeft := end - time.Now().Unix() - - // Format timestamps for human readability - startTimeFormatted := utils.FormatTime(big.NewInt(start)) - endTimeFormatted := utils.FormatTime(big.NewInt(end)) - timeLeftFormatted := fmt.Sprintf("%dm%ds", timeLeft/60, timeLeft%60) - - if !isMyTurn { - log.Debug("Not active submitter", - "active_submitter", activeAddress, - "index", submitterIndex, - "my_address", myAddress, - "total_submitters", len(r.rotator.GetSubmitterSet()), - "rotation_end", endTimeFormatted, - "time_remaining", timeLeftFormatted) - return errNotMyTurn - } - - log.Info("Active submitter status", - "index", submitterIndex, - "total_submitters", len(r.rotator.GetSubmitterSet()), - "rotation_start", startTimeFormatted, - "rotation_end", endTimeFormatted, - "time_remaining", timeLeftFormatted) - return nil -} - // Handle chain reorganization (metrics + pending pool; DetectReorg only detects and updates block history). func (r *Rollup) handleReorg(depth uint64) error { r.metrics.SetReorgDepth(float64(depth)) @@ -519,7 +430,7 @@ func (r *Rollup) updateFeeMetrics(tx *ethtypes.Transaction, receipt *ethtypes.Re txFeeFloat, _ := txFeeEth.Float64() // Update metrics based on transaction type - if constants.IsCommitLikeMethod(method) { + if constants.IsOwnedCommitMethod(method) { r.rollupFeeSum += txFeeFloat r.metrics.RollupCostSum.Add(txFeeFloat) r.metrics.RollupCost.Set(txFeeFloat) @@ -581,8 +492,11 @@ func (r *Rollup) getTxStatus(tx *ethtypes.Transaction) (*txStatus, error) { func (r *Rollup) handlePendingTx(txRecord *types.TxRecord, tx *ethtypes.Transaction, method string) error { // Obsolete / doomed txs: cancel before timeout handling so we do not resubmit a tx that will revert. - if constants.IsCommitLikeMethod(method) { - batchIndex := utils.ParseParentBatchIndex(tx.Data()) + 1 + if constants.IsOwnedCommitMethod(method) { + batchIndex, err := utils.ParseOwnedCommitBatchIndex(tx.Data(), r.abi) + if err != nil { + return fmt.Errorf("parse owned commit batch index: %w", err) + } lastCommitted, err := r.Rollup.LastCommittedBatchIndex(nil) if err != nil { return fmt.Errorf("get last committed batch index error: %w", err) @@ -772,8 +686,11 @@ func (r *Rollup) handleConfirmedTx(txRecord *types.TxRecord, tx *ethtypes.Transa method := utils.ParseMethod(tx, r.abi) if status.receipt.Status == ethtypes.ReceiptStatusFailed { - if constants.IsCommitLikeMethod(method) { - batchIndex := utils.ParseParentBatchIndex(tx.Data()) + 1 + if constants.IsOwnedCommitMethod(method) { + batchIndex, err := utils.ParseOwnedCommitBatchIndex(tx.Data(), r.abi) + if err != nil { + return fmt.Errorf("parse owned commit batch index: %w", err) + } lastCommitted, err := r.Rollup.LastCommittedBatchIndex(nil) if err != nil { return fmt.Errorf("get last committed batch index error: %w", err) @@ -802,8 +719,11 @@ func (r *Rollup) handleConfirmedTx(txRecord *types.TxRecord, tx *ethtypes.Transa } } } else { // Transaction succeeded - if constants.IsCommitLikeMethod(method) { - batchIndex := utils.ParseParentBatchIndex(tx.Data()) + 1 + if constants.IsOwnedCommitMethod(method) { + batchIndex, err := utils.ParseOwnedCommitBatchIndex(tx.Data(), r.abi) + if err != nil { + return fmt.Errorf("parse owned commit batch index: %w", err) + } log.Info("Successfully committed batch", "method", method, "batch_index", batchIndex, "tx_hash", tx.Hash().String(), "block_number", status.receipt.BlockNumber.Uint64(), "gas_used", status.receipt.GasUsed, "confirm", confirmations) } else if method == constants.MethodFinalizeBatch { batchIndex := utils.ParseFBatchIndex(tx.Data()) @@ -993,84 +913,6 @@ func (r *Rollup) finalize() error { } func (r *Rollup) rollup() error { - // Get current block height - if !r.cfg.PriorityRollup { - activeSubmitter, activeIndex, err := r.rotator.CurrentSubmitter(r.L2Clients, r.Staking) - if err != nil { - return fmt.Errorf("rollup: get current submitter err, %w", err) - } - - err = r.eventInfoStorage.Load() - if err != nil { - return fmt.Errorf("failed to load storage in rollup: %w", err) - } - log.Debug("Indexer status", - "blocks_processed", r.eventInfoStorage.BlockProcessed(), - "last_event_time", r.eventInfoStorage.BlockTime()) - - // get current block number - blockNumber, err := r.L1Client.BlockNumber(context.Background()) - if err != nil { - return fmt.Errorf("failed to get block number in rollup: %w", err) - } - // set metrics - r.metrics.SetIndexerBlockProcessed(r.eventInfoStorage.BlockProcessed()) - // check if indexed block number is too old - if blockNumber > r.eventInfoStorage.BlockProcessed()+100 { - log.Info("Indexer sync required", - "module", r.GetModuleName(), - "current_block", blockNumber, - "processed_block", r.eventInfoStorage.BlockProcessed(), - "blocks_behind", blockNumber-r.eventInfoStorage.BlockProcessed()) - return nil - } - - past := (time.Now().Unix() - r.rotator.startTime.Int64()) % r.rotator.epoch.Int64() - start := time.Now().Unix() - past - end := start + r.rotator.epoch.Int64() - - // Calculate time remaining in current turn - timeLeft := end - time.Now().Unix() - myAddress := r.WalletAddr().Hex() - activeAddress := activeSubmitter.Hex() - isMyTurn := activeAddress == myAddress - totalSubmitters := len(r.rotator.GetSubmitterSet()) - - // Format timestamps for human readability - startTimeFormatted := utils.FormatTime(big.NewInt(start)) - endTimeFormatted := utils.FormatTime(big.NewInt(end)) - timeLeftFormatted := fmt.Sprintf("%dm%ds", timeLeft/60, timeLeft%60) - - log.Info("Rotation status", - "submitter_index", activeIndex, - "active_submitter", activeAddress, - "my_address", myAddress, - "total_submitters", totalSubmitters, - "is_my_turn", isMyTurn, - "rotation_start", startTimeFormatted, - "rotation_end", endTimeFormatted, - "time_remaining", timeLeftFormatted) - - if isMyTurn { - if timeLeft < r.cfg.RotatorBuffer { - bufferFormatted := fmt.Sprintf("%dm%ds", r.cfg.RotatorBuffer/60, r.cfg.RotatorBuffer%60) - log.Info("Insufficient time for rollup", - "time_remaining", timeLeftFormatted, - "buffer_required", bufferFormatted) - return nil - } - - log.Info("Starting rollup", - "submitter_index", activeIndex, - "total_submitters", totalSubmitters) - } else { - log.Info("Skipping rollup - not active submitter", - "active_index", activeIndex, - "active_submitter", activeAddress) - return nil - } - } - if pendingN := r.pendingTxs.Len(); pendingN > int(r.cfg.MaxTxsInPendingPool) { log.Info("Pending pool full", "current_size", pendingN, @@ -1115,10 +957,6 @@ func (r *Rollup) rollup() error { return nil } - signature, err := r.buildSignatureInput(rpcRollupBatch) - if err != nil { - return err - } rollupBatch := bindings.IRollupBatchDataInput{ Version: uint8(rpcRollupBatch.Version), ParentBatchHeader: rpcRollupBatch.ParentBatchHeader, @@ -1129,33 +967,15 @@ func (r *Rollup) rollup() error { WithdrawalRoot: rpcRollupBatch.WithdrawRoot, } - storedBlobHash, err := r.Rollup.BatchBlobVersionedHashes(nil, big.NewInt(int64(batchIndex))) - if err != nil { - return fmt.Errorf("get batch blob versioned hash: %w", err) - } - useCommitState := storedBlobHash != [32]byte{} - if useCommitState { - if err := r.validateStoredBlobAgainstSealedHeader(storedBlobHash, batchIndex); err != nil { - return err - } - log.Info("Using commitState (L1 stored blob hash matches sealed header)", - "batch_index", batchIndex, - "stored_blob_hash", common.Hash(storedBlobHash).Hex()) - } - // tip and cap tip, gasFeeCap, blobFee, head, err := r.GetGasTipAndCap() if err != nil { return fmt.Errorf("get gas tip and cap error:%v", err) } - // calldata encode — commitState reuses stored blob hash and must not carry blob data in tx - var calldata []byte - if useCommitState { - calldata, err = r.abi.Pack("commitState", rollupBatch, *signature) - } else { - calldata, err = r.abi.Pack("commitBatch", rollupBatch, *signature) - } + // The official writer has one post-cutover path: the current commitBatch + // selector with a freshly constructed blob sidecar. + calldata, err := r.abi.Pack("commitBatch", rollupBatch) if err != nil { return fmt.Errorf("pack calldata error:%v", err) } @@ -1164,10 +984,11 @@ func (r *Rollup) rollup() error { // is available during eth_estimateGas simulation. var estimateBlobHashes []common.Hash var estimateBlobFeeCap *big.Int - if !useCommitState && len(rpcRollupBatch.Sidecar.Blobs) > 0 { - estimateBlobHashes = blob.BlobHashes(rpcRollupBatch.Sidecar.Blobs, rpcRollupBatch.Sidecar.Commitments) - estimateBlobFeeCap = blobFee + if len(rpcRollupBatch.Sidecar.Blobs) == 0 { + return fmt.Errorf("batch %d has no fresh blob sidecar", batchIndex) } + estimateBlobHashes = blob.BlobHashes(rpcRollupBatch.Sidecar.Blobs, rpcRollupBatch.Sidecar.Commitments) + estimateBlobFeeCap = blobFee gas, err := r.EstimateGas(r.WalletAddr(), r.rollupAddr, calldata, gasFeeCap, tip, estimateBlobHashes, estimateBlobFeeCap) if err != nil { log.Warn("Estimate gas failed", "batch_index", batchIndex, "error", err) @@ -1193,13 +1014,7 @@ func (r *Rollup) rollup() error { return fmt.Errorf("failed to get next nonce") } - // Create and sign transaction (commitState is always type-2, never blob) - var tx *ethtypes.Transaction - if useCommitState { - tx, err = r.createDynamicFeeTx(nonce, gas, tip, gasFeeCap, calldata) - } else { - tx, err = r.createRollupTx(rpcRollupBatch, nonce, gas, tip, gasFeeCap, blobFee, calldata, head) - } + tx, err := r.createRollupTx(rpcRollupBatch, nonce, gas, tip, gasFeeCap, blobFee, calldata, head) if err != nil { return fmt.Errorf("failed to create rollup tx: %w", err) } @@ -1242,10 +1057,10 @@ func (r *Rollup) getNextNonce() uint64 { } func (r *Rollup) createRollupTx(batch *eth.RPCRollupBatch, nonce, gas uint64, tip, gasFeeCap, blobFee *big.Int, calldata []byte, head *ethtypes.Header) (*ethtypes.Transaction, error) { - if len(batch.Sidecar.Blobs) > 0 { - return r.createBlobTx(batch, nonce, gas, tip, gasFeeCap, blobFee, calldata, head) + if batch == nil || len(batch.Sidecar.Blobs) == 0 { + return nil, errors.New("official commitBatch requires a fresh blob sidecar") } - return r.createDynamicFeeTx(nonce, gas, tip, gasFeeCap, calldata) + return r.createBlobTx(batch, nonce, gas, tip, gasFeeCap, blobFee, calldata, head) } func (r *Rollup) createBlobTx(rpcBatch *eth.RPCRollupBatch, nonce, gas uint64, tip, gasFeeCap, blobFee *big.Int, calldata []byte, head *ethtypes.Header) (*ethtypes.Transaction, error) { @@ -1287,72 +1102,20 @@ func (r *Rollup) createBlobTx(rpcBatch *eth.RPCRollupBatch, nonce, gas uint64, t }), nil } -func (r *Rollup) createDynamicFeeTx(nonce, gas uint64, tip, gasFeeCap *big.Int, calldata []byte) (*ethtypes.Transaction, error) { - return ethtypes.NewTx(ðtypes.DynamicFeeTx{ - ChainID: r.chainId, - Nonce: nonce, - GasTipCap: tip, - GasFeeCap: gasFeeCap, - Gas: gas, - To: &r.rollupAddr, - Data: calldata, - }), nil -} - -// validateStoredBlobAgainstSealedHeader requires L1 stored blob hash to match the sealed -// header blob field (offset 57). Sealed headers always carry a non-zero semantic value -// (e.g. EmptyVersionedHash); callers must not skip validation when the field decodes to zero. -func (r *Rollup) validateStoredBlobAgainstSealedHeader(stored [32]byte, batchIndex uint64) error { - if stored == ([32]byte{}) { - return nil - } - if !r.cfg.SealBatch || r.batchCache == nil { - return nil - } - header, ok := r.batchCache.GetSealedBatchHeader(batchIndex) - if !ok || header == nil { - return fmt.Errorf("commitState: sealed batch header %d not in cache", batchIndex) - } - headerBlob, err := header.BlobCommitHash() - if err != nil { - return fmt.Errorf("commitState: batch %d header blob field: %w", batchIndex, err) - } - if common.Hash(stored) != headerBlob { - return fmt.Errorf( - "commitState: L1 stored blob hash %s != sealed header %s (batch %d)", - common.Hash(stored).Hex(), headerBlob.Hex(), batchIndex, - ) - } - return nil -} - -// tryRebuildRollupCommitTx aligns an in-flight commit-like tx with L1 blob-hash state: -// - stored blob versioned hash set -> must use commitState (no blob), upgrading commitBatch / blob txs -// - stored hash cleared -> must use commitBatch (+ blob sidecar when present), downgrading stale commitState -// -// Returns handled=true when newTx must be used and the default ReSubmitTx copy path must be skipped. +// tryRebuildRollupCommitTx rebuilds an owned current commitBatch from the local +// batch cache. It deliberately ignores the old transaction's blob hash/sidecar +// and L1 stored-hash state, and always constructs a fresh blob sidecar. func (r *Rollup) tryRebuildRollupCommitTx(tx *ethtypes.Transaction, tip, gasFeeCap, blobFeeCap *big.Int, head *ethtypes.Header) (newTx *ethtypes.Transaction, handled bool, err error) { - method := utils.ParseMethod(tx, r.abi) - if !constants.IsCommitLikeMethod(method) { + if tx.To() == nil || *tx.To() != r.rollupAddr { return nil, false, nil } - batchIndex := utils.ParseParentBatchIndex(tx.Data()) + 1 - stored, err := r.Rollup.BatchBlobVersionedHashes(nil, big.NewInt(int64(batchIndex))) - if err != nil { - return nil, false, err + batchIndex, err := utils.ParseOwnedCommitBatchIndex(tx.Data(), r.abi) + if errors.Is(err, utils.ErrNotOwnedCommit) { + return nil, false, nil } - if err := r.validateStoredBlobAgainstSealedHeader(stored, batchIndex); err != nil { + if err != nil { return nil, true, err } - if stored != ([32]byte{}) { - if method == constants.MethodCommitState && tx.Type() == ethtypes.DynamicFeeTxType { - return nil, false, nil - } - } else { - if method == constants.MethodCommitBatch { - return nil, false, nil - } - } var rpcRollupBatch *eth.RPCRollupBatch exist := true @@ -1364,10 +1127,6 @@ func (r *Rollup) tryRebuildRollupCommitTx(tx *ethtypes.Transaction, tip, gasFeeC if err != nil || !exist || rpcRollupBatch == nil { return nil, true, fmt.Errorf("cannot rebuild rollup commit tx: batch %d not in cache (err=%v)", batchIndex, err) } - signature, err := r.buildSignatureInput(rpcRollupBatch) - if err != nil { - return nil, true, err - } rollupBatch := bindings.IRollupBatchDataInput{ Version: uint8(rpcRollupBatch.Version), ParentBatchHeader: rpcRollupBatch.ParentBatchHeader, @@ -1378,23 +1137,7 @@ func (r *Rollup) tryRebuildRollupCommitTx(tx *ethtypes.Transaction, tip, gasFeeC WithdrawalRoot: rpcRollupBatch.WithdrawRoot, } - if stored != ([32]byte{}) { - calldata, err := r.abi.Pack("commitState", rollupBatch, *signature) - if err != nil { - return nil, true, err - } - newTx, err = r.createDynamicFeeTx(tx.Nonce(), tx.Gas(), tip, gasFeeCap, calldata) - if err != nil { - return nil, true, err - } - log.Info("Rebuilt pending rollup tx as commitState (stored blob hash on L1)", - "batch_index", batchIndex, - "old_tx_type", tx.Type(), - "old_method", method) - return newTx, true, nil - } - - calldata, err := r.abi.Pack("commitBatch", rollupBatch, *signature) + calldata, err := r.abi.Pack("commitBatch", rollupBatch) if err != nil { return nil, true, err } @@ -1405,10 +1148,9 @@ func (r *Rollup) tryRebuildRollupCommitTx(tx *ethtypes.Transaction, tip, gasFeeC if err != nil { return nil, true, err } - log.Info("Rebuilt pending rollup tx as commitBatch (no stored blob hash on L1)", + log.Info("Rebuilt pending rollup tx as fresh-blob commitBatch", "batch_index", batchIndex, - "old_tx_type", tx.Type(), - "old_method", method) + "old_tx_type", tx.Type()) return newTx, true, nil } @@ -1428,15 +1170,6 @@ func (r *Rollup) logTxInfo(tx *ethtypes.Transaction, batchIndex uint64) { ) } -func (r *Rollup) buildSignatureInput(batch *eth.RPCRollupBatch) (*bindings.IRollupBatchSignatureInput, error) { - sigData := bindings.IRollupBatchSignatureInput{ - SignedSequencersBitmap: common.Big0, - SequencerSets: batch.CurrentSequencerSetBytes, - Signature: []byte("0x"), - } - return &sigData, nil -} - func (r *Rollup) GetGasTipAndCap() (*big.Int, *big.Int, *big.Int, *ethtypes.Header, error) { head, err := r.L1Client.HeaderByNumber(context.Background(), nil) if err != nil { @@ -1498,35 +1231,12 @@ func (r *Rollup) GetGasTipAndCap() (*big.Int, *big.Int, *big.Int, *ethtypes.Head // PreCheck is run before the submitter to check whether the submitter can be started func (r *Rollup) PreCheck() error { - - // debug stakers - stakers, err := r.Staking.GetStakers(nil) - if err != nil { - log.Debug("get stakers error", "err", err) - } else { - log.Debug("stakers", "len", len(stakers)) - for _, s := range stakers { - log.Debug("staker", "addr", s.Hex()) - } - } - // debug active stakers - activeStakers, err := r.Staking.GetActiveStakers(nil) - if err != nil { - log.Debug("get active stakers error", "err", err) - } else { - log.Debug("active stakers", "len", len(activeStakers)) - for _, s := range activeStakers { - log.Debug("active staker", "addr", s.Hex()) - } - } - - isStaker, err := r.IsStaker() + isActive, err := r.IsActive() if err != nil { - return fmt.Errorf("check if this account is sequencer error:%v", err) + return fmt.Errorf("check submitter qualification: %w", err) } - - if !isStaker { - return fmt.Errorf("this account is not staker, can not rollup") + if !isActive { + return fmt.Errorf("submitter %s is not active", r.WalletAddr().Hex()) } return nil @@ -1552,7 +1262,7 @@ func GetRollupBatchByIndex(index uint64, clients []iface.L2Client) (*eth.RPCRoll log.Warn("failed to get batch", "error", err) continue } - if batch != nil && len(batch.Signatures) > 0 { + if batch != nil { return batch, nil } } @@ -1560,100 +1270,6 @@ func GetRollupBatchByIndex(index uint64, clients []iface.L2Client) (*eth.RPCRoll return nil, nil } -// query sequencer set from sequencer contract on l2 -func QuerySequencerSet(addr common.Address, clients []iface.L2Client) ([]common.Address, error) { - if len(clients) < 1 { - return nil, fmt.Errorf("no client to query sequencer set") - } - for _, client := range clients { - // l2 sequencer - l2Seqencer, err := bindings.NewSequencer(addr, client) - if err != nil { - log.Warn("failed to connect to sequencer", "error", err) - continue - } - // get sequencer set - seqSet, err := l2Seqencer.GetSequencerSet2(nil) - if err != nil { - log.Warn("failed to get sequencer set", "error", err) - continue - } - return seqSet, nil - } - return nil, fmt.Errorf("no sequencer set found after querying all clients") -} - -// query epoch from gov contract on l2 -func GetEpoch(addr common.Address, clients []iface.L2Client) (*big.Int, error) { - if len(clients) < 1 { - return nil, fmt.Errorf("no client to query epoch") - } - for _, client := range clients { - // l2 gov - l2Gov, err := bindings.NewGov(addr, client) - if err != nil { - log.Warn("failed to connect to gov", "error", err) - continue - } - // get epoch - epoch, err := l2Gov.RollupEpoch(nil) - if err != nil { - log.Warn("failed to get epoch", "error", err) - continue - } - return epoch, nil - } - return nil, fmt.Errorf("no epoch found after querying all clients") -} - -// query sequencer set update time from sequencer contract on l2 -func GetSequencerSetUpdateTime(addr common.Address, clients []iface.L2Client) (*big.Int, error) { - if len(clients) < 1 { - return nil, fmt.Errorf("no client to query sequencer set update time") - } - for _, client := range clients { - // l2 sequencer - l2Seqencer, err := bindings.NewSequencer(addr, client) - if err != nil { - log.Warn("failed to connect to sequencer", "error", err) - continue - } - // get sequencer set update time - updateTime, err := l2Seqencer.UpdateTime(nil) - if err != nil { - log.Warn("failed to get sequencer set update time", "error", err) - continue - } - return updateTime, nil - } - return nil, fmt.Errorf("no sequencer set update time found after querying all clients") -} - -// query epoch update time from gov contract on l2 -func GetEpochUpdateTime(addr common.Address, clients []iface.L2Client) (*big.Int, error) { - if len(clients) < 1 { - return nil, fmt.Errorf("no client to query epoch update time") - } - for _, client := range clients { - // l2 gov - l2Gov, err := bindings.NewGov(addr, client) - if err != nil { - log.Warn("failed to connect to gov", "error", err) - continue - } - // get epoch update time - updateTime, err := l2Gov.RollupEpochUpdateTime(nil) - if err != nil { - log.Warn("failed to get epoch update time", "error", err) - continue - } - return updateTime, nil - - } - return nil, fmt.Errorf("no epoch update time found after querying all clients") - -} - func UpdateGasLimit(tx *ethtypes.Transaction) (*ethtypes.Transaction, error) { // add buffer to gas limit (*1.2) newGasLimit := tx.Gas() * 12 / 10 @@ -1826,6 +1442,9 @@ func (r *Rollup) ReSubmitTx(resend bool, tx *ethtypes.Transaction) (*ethtypes.Tr } else if err != nil { return nil, err } else { + if len(tx.Data()) != 0 && tx.To() != nil && *tx.To() == r.rollupAddr && utils.ParseMethod(tx, r.abi) != constants.MethodFinalizeBatch { + return nil, fmt.Errorf("refuse to resubmit non-owned rollup calldata (selector 0x%x)", tx.Data()[:min(4, len(tx.Data()))]) + } switch tx.Type() { case ethtypes.DynamicFeeTxType: newTx = ethtypes.NewTx(ðtypes.DynamicFeeTx{ @@ -1870,8 +1489,8 @@ func (r *Rollup) ReSubmitTx(resend bool, tx *ethtypes.Transaction) (*ethtypes.Tr } } - // Original tx was not a blob tx, but rebuild (e.g. commitState -> commitBatch) may produce a blob tx. - // In that case blobFeeCap was never bumped above (bump only ran for BlobTxType). Apply the same bump as for blob replacements. + // A malformed/non-blob owned record cannot pass strict journal recovery, but + // keep replacement robust if one is injected into memory by a test/tool. if !resend && newTx != nil && newTx.Type() == ethtypes.BlobTxType && tx.Type() != ethtypes.BlobTxType { embeddedCap := newTx.BlobGasFeeCap() if embeddedCap != nil { @@ -1928,13 +1547,12 @@ func (r *Rollup) ReSubmitTx(resend bool, tx *ethtypes.Transaction) (*ethtypes.Tr return newTx, nil } -func (r *Rollup) IsStaker() (bool, error) { - - isStaker, err := r.Staking.IsStaker(nil, r.WalletAddr()) +func (r *Rollup) IsActive() (bool, error) { + isActive, err := r.Submitter.IsActive(nil, r.WalletAddr()) if err != nil { - return false, fmt.Errorf("call IsStaker err :%v", err) + return false, fmt.Errorf("call Submitter.isActive: %w", err) } - return isStaker, nil + return isActive, nil } func (r *Rollup) EstimateGas( diff --git a/tx-submitter/services/rollup_handle_test.go b/tx-submitter/services/rollup_handle_test.go index 2c3bb3ed3..644a5d631 100644 --- a/tx-submitter/services/rollup_handle_test.go +++ b/tx-submitter/services/rollup_handle_test.go @@ -2,11 +2,11 @@ package services import ( "context" + "errors" "math/big" "testing" "time" - "github.com/morph-l2/go-ethereum" "github.com/morph-l2/go-ethereum/common" "github.com/morph-l2/go-ethereum/core" ethtypes "github.com/morph-l2/go-ethereum/core/types" @@ -14,7 +14,8 @@ import ( "github.com/stretchr/testify/require" "morph-l2/bindings/bindings" - "morph-l2/tx-submitter/event" + commonbatch "morph-l2/common/batch" + "morph-l2/tx-submitter/db" "morph-l2/tx-submitter/iface" "morph-l2/tx-submitter/metrics" "morph-l2/tx-submitter/mock" @@ -42,47 +43,17 @@ func setupTestRollup(t *testing.T) (*Rollup, *mock.L1ClientWrapper, *mock.L2Clie metrics.UnregisterMetrics() }) - // Create mock event storage - eventStorage := mock.NewMockEventInfoStorage() - err = eventStorage.Load() - require.NoError(t, err) - - // Initialize event storage with test data - eventStorage.SetBlockProcessed(1000) - eventStorage.SetBlockTime(uint64(time.Now().Unix())) - err = eventStorage.Store() - require.NoError(t, err) - - // Create mock event indexer - indexer := event.NewEventIndexer( - nil, // We don't need a real ethclient.Client for testing - big.NewInt(0), - ethereum.FilterQuery{}, - 100, - eventStorage, - ) - - // Create mock rotator - rotator := NewRotator(common.Address{}, common.Address{}, indexer) - - // Create mock L1Staking - l1Staking := mock.NewMockL1Staking() - // Set some test stakers - testStakers := []common.Address{ - common.HexToAddress("0x1111111111111111111111111111111111111111"), - common.HexToAddress("0x2222222222222222222222222222222222222222"), - } - l1Staking.SetActiveStakers(testStakers) + submitter := mock.NewMockSubmitter() // Create rollup config defaultCfg := utils.Config{ - MaxTip: 10e9, - MaxBaseFee: 100e9, - MinTip: 1e9, - TipFeeBump: 100, - TxTimeout: 10 * time.Second, - PriorityRollup: true, - MaxBlobCount: 6, // required by batch.NewBatchCache (must be > 0) + MaxTip: 10e9, + MaxBaseFee: 100e9, + MinTip: 1e9, + TipFeeBump: 100, + TxTimeout: 10 * time.Second, + BatchBlockInterval: 1, + MaxBlobCount: 6, // required by batch.NewBatchCache (must be > 0) } // Create mock journal @@ -104,17 +75,15 @@ func setupTestRollup(t *testing.T) (*Rollup, *mock.L1ClientWrapper, *mock.L2Clie l1Mock, []iface.L2Client{l2Mock}, mockRollup, - l1Staking, + submitter, big.NewInt(1), privateKey, common.Address{}, rollupAbi, defaultCfg, nil, - rotator, nil, nil, - eventStorage, nil, ) @@ -129,6 +98,42 @@ func setupTestRollup(t *testing.T) (*Rollup, *mock.L1ClientWrapper, *mock.L2Clie return rollup, l1Mock, l2Mock, mockRollup } +func TestPreCheckUsesSubmitterIsActive(t *testing.T) { + r, _, _, _ := setupTestRollup(t) + submitter := r.Submitter.(*mock.MockSubmitter) + + require.NoError(t, r.PreCheck()) + require.Equal(t, r.WalletAddr(), submitter.LastQuery()) + + submitter.SetActive(r.WalletAddr(), false) + require.ErrorContains(t, r.PreCheck(), "is not active") +} + +func TestRollbackCanonicalBootstrapClearsCheckpoint(t *testing.T) { + ldb, err := db.New(t.TempDir()) + require.NoError(t, err) + t.Cleanup(func() { require.NoError(t, ldb.Close()) }) + require.NoError(t, ldb.PutBytes([]byte(commonbatch.SealedBatchIndicesKey), []byte("corrupt-checkpoint"))) + + cache := commonbatch.NewBatchCache( + nil, + nil, + 1, + commonbatch.BatchConfig{BlockInterval: 1}, + nil, + &iface.L2Clients{}, + nil, + nil, + ldb, + ) + r := &Rollup{batchCache: cache} + cause := errors.New("safe head changed") + err = r.rollbackCanonicalBootstrap("assemble downtime L2 blocks", cause) + require.ErrorIs(t, err, cause) + _, err = ldb.GetBytes([]byte(commonbatch.SealedBatchIndicesKey)) + require.Error(t, err, "failed startup must not leave a resumable checkpoint") +} + // TestHandleDiscardedTx tests the handling of discarded transactions func TestHandleDiscardedTx(t *testing.T) { r, l1Mock, _, _ := setupTestRollup(t) diff --git a/tx-submitter/services/rollup_test.go b/tx-submitter/services/rollup_test.go index 79fe4fcd3..664f2de5b 100644 --- a/tx-submitter/services/rollup_test.go +++ b/tx-submitter/services/rollup_test.go @@ -8,6 +8,7 @@ import ( "github.com/morph-l2/go-ethereum/common" "github.com/morph-l2/go-ethereum/core/types" "github.com/morph-l2/go-ethereum/crypto/kzg4844" + "github.com/morph-l2/go-ethereum/eth" "github.com/stretchr/testify/require" "morph-l2/tx-submitter/utils" @@ -104,6 +105,7 @@ func TestGetGasTipAndCap(t *testing.T) { } func TestReSubmitTx(t *testing.T) { + genericTo := common.HexToAddress("0x0000000000000000000000000000000000000001") marketTip := big.NewInt(3e9) // 3 Gwei market tip baseFee := big.NewInt(2e9) // 2 Gwei base fee block := types.NewBlockWithHeader( @@ -131,7 +133,7 @@ func TestReSubmitTx(t *testing.T) { GasTipCap: big.NewInt(2e9), // 2 Gwei GasFeeCap: big.NewInt(10e9), // 10 Gwei Gas: 100000, - To: &common.Address{}, + To: &genericTo, Value: big.NewInt(0), Data: []byte{1, 2, 3, 4}, }) @@ -188,7 +190,7 @@ func TestReSubmitTx(t *testing.T) { GasTipCap: uint256.MustFromBig(big.NewInt(2e9)), GasFeeCap: uint256.MustFromBig(big.NewInt(10e9)), Gas: 200000, - To: common.Address{}, + To: genericTo, Value: uint256.NewInt(0), Data: []byte{1, 2, 3, 4}, BlobFeeCap: uint256.MustFromBig(big.NewInt(5e9)), @@ -376,7 +378,6 @@ func TestTxStateTransition(t *testing.T) { // Create rollup instance rollup, l1Mock, _, _ := setupTestRollup(t) - rollup.cfg.PriorityRollup = true // Test transaction state transitions t.Run("Transaction State Flow", func(t *testing.T) { @@ -416,3 +417,32 @@ func TestTxStateTransition(t *testing.T) { require.Nil(t, txRecord) }) } + +func TestCreateRollupTxRequiresFreshBlobSidecar(t *testing.T) { + r, _, _, _ := setupTestRollup(t) + _, err := r.createRollupTx( + ð.RPCRollupBatch{}, + 1, + 100_000, + big.NewInt(1), + big.NewInt(2), + big.NewInt(1), + []byte{0x41, 0xf7, 0x56, 0xda}, + &types.Header{}, + ) + require.ErrorContains(t, err, "fresh blob sidecar") +} + +func TestReSubmitTxRejectsNonOwnedRollupCalldata(t *testing.T) { + r, l1Mock, _, _ := setupTestRollup(t) + l1Mock.Block = types.NewBlockWithHeader(&types.Header{BaseFee: big.NewInt(1)}) + tx := types.NewTx(&types.DynamicFeeTx{ + ChainID: big.NewInt(1), + GasTipCap: big.NewInt(1), + GasFeeCap: big.NewInt(2), + To: &r.rollupAddr, + Data: []byte{0xde, 0xad, 0xbe, 0xef}, + }) + _, err := r.ReSubmitTx(false, tx) + require.ErrorContains(t, err, "refuse to resubmit non-owned rollup calldata") +} diff --git a/tx-submitter/services/rotator.go b/tx-submitter/services/rotator.go deleted file mode 100644 index bb5bb81af..000000000 --- a/tx-submitter/services/rotator.go +++ /dev/null @@ -1,158 +0,0 @@ -package services - -import ( - "errors" - "fmt" - "math/big" - "sync" - "time" - - "morph-l2/tx-submitter/event" - "morph-l2/tx-submitter/iface" - "morph-l2/tx-submitter/utils" - - "github.com/morph-l2/go-ethereum/common" - "github.com/morph-l2/go-ethereum/log" -) - -type Rotator struct { - // used to record the start time of rotation - // updated when the sequencerSet, stakingSet or epoch is updated - startTime *big.Int // timestamp - submitterSet []common.Address - sequencerSet []common.Address - epoch *big.Int // epoch for rotation - mu sync.Mutex // lock - l2SequencerAddr common.Address - l2GovAddr common.Address - indexer *event.EventIndexer -} - -func NewRotator(l2SeqencerAddr, l2GovAddr common.Address, indexer *event.EventIndexer) *Rotator { - return &Rotator{ - l2SequencerAddr: l2SeqencerAddr, - l2GovAddr: l2GovAddr, - indexer: indexer, - } -} - -func (r *Rotator) StartEventIndexer() { - go r.indexer.Index() -} - -// UpdateState updates the state of the rotator -// updated by event listener in the future -func (r *Rotator) UpdateState(clients []iface.L2Client, l1Staking iface.IL1Staking) error { - - epochUpdateTime, err := GetEpochUpdateTime(r.l2GovAddr, clients) - if err != nil { - log.Error("failed to get epoch update time", "err", err) - return fmt.Errorf("GetCurrentSubmitter: failed to get epoch update time: %w", err) - } - // sequencer set update time - sequcerUpdateTime, err := GetSequencerSetUpdateTime(r.l2SequencerAddr, clients) - if err != nil { - log.Error("failed to get sequencer set update time", "err", err) - return fmt.Errorf("GetCurrentSubmitter: failed to get sequencer set update time: %w", err) - } - - storage := r.indexer.GetStorage() - err = storage.Load() - if err != nil { - log.Error("failed to load storage", "err", err) - return fmt.Errorf("GetCurrentSubmitter: failed to load storage: %w", err) - } - // if index not complete - if storage.BlockProcessed() == 0 { - return errors.New("wait event index service to complete") - } - - r.startTime = utils.MaxOfThreeBig(epochUpdateTime, sequcerUpdateTime, big.NewInt(int64(storage.BlockTime()))) - - // get current sequencer set - seqSet, err := QuerySequencerSet(r.l2SequencerAddr, clients) - if err != nil { - log.Error("failed to get sequencer set", "err", err) - return fmt.Errorf("UpdateState: failed to get sequencer set: %w", err) - } - r.SetSequencerSet(seqSet) - // get current epoch - epoch, err := GetEpoch(r.l2GovAddr, clients) - if err != nil { - log.Error("failed to get epoch", "err", err) - return err - } - r.epoch = epoch - - // get l1staking active staker set - stakers, err := l1Staking.GetActiveStakers(nil) - if err != nil { - log.Error("failed to get active stakers", "err", err) - return fmt.Errorf("UpdateState: failed to get active stakers: %w", err) - } - submitterSet := utils.IntersectionOfAddresses(r.GetSequencerSet(), stakers) - r.SetSubmitterSet(submitterSet) - // rotator info - log.Info( - "rotator state updated", - "epoch", r.epoch, - "start_time", utils.FormatTime(r.startTime), - "start_timestamp", r.startTime, - "epoch_update_time", utils.FormatTime(epochUpdateTime), - "epoch_update_timestamp", epochUpdateTime, - "seq_update_time", utils.FormatTime(sequcerUpdateTime), - "seq_update_timestamp", sequcerUpdateTime, - "indexed_latest_block", storage.BlockProcessed(), - "indexed_event_time", utils.FormatTime(big.NewInt(int64(storage.BlockTime()))), - "indexed_event_timestamp", storage.BlockTime(), - ) - - return nil -} - -// GetCurrentSubmitter returns the current sequencer that should be submitting -func (r *Rotator) CurrentSubmitter(clients []iface.L2Client, l1Staking iface.IL1Staking) (*common.Address, int64, error) { - - err := r.UpdateState(clients, l1Staking) - if err != nil { - return nil, 0, fmt.Errorf("update state err: %w", err) - } - - if len(r.GetSubmitterSet()) == 0 { - return nil, 0, fmt.Errorf("GetCurrentSubmitter: sequencer set is empty") - } - - if r.epoch.Int64() == 0 { - return nil, 0, fmt.Errorf("GetCurrentSubmitter: epoch is 0") - } - - sec := time.Now().Unix() - r.startTime.Int64() - seqIdx := sec / r.epoch.Int64() % int64(len(r.GetSequencerSet())) - - return &r.GetSubmitterSet()[seqIdx], seqIdx, nil - -} - -func (r *Rotator) SetSubmitterSet(newSet []common.Address) { - r.mu.Lock() - defer r.mu.Unlock() - r.submitterSet = newSet -} -func (r *Rotator) GetSubmitterSet() []common.Address { - r.mu.Lock() - defer r.mu.Unlock() - return r.submitterSet -} - -func (r *Rotator) SetSequencerSet(newSet []common.Address) { - r.mu.Lock() - defer r.mu.Unlock() - r.sequencerSet = newSet -} - -func (r *Rotator) GetSequencerSet() []common.Address { - r.mu.Lock() - defer r.mu.Unlock() - return r.sequencerSet - -} diff --git a/tx-submitter/types/batch_cache.go b/tx-submitter/types/batch_cache.go index 7ebe49b81..fd1cbe5c0 100644 --- a/tx-submitter/types/batch_cache.go +++ b/tx-submitter/types/batch_cache.go @@ -45,21 +45,12 @@ func (b *BatchCacheLegacy) Get(batchIndex uint64) (*eth.RPCRollupBatch, bool) { return nil, false } - // Validate batch before caching - batch must exist and have signatures - if fetchedBatch != nil && len(fetchedBatch.Signatures) > 0 { - // Store valid batch in cache for future use + if fetchedBatch != nil { b.m.Lock() b.batchCache[batchIndex] = fetchedBatch b.m.Unlock() return fetchedBatch, true - } else if fetchedBatch != nil { - // Batch exists but doesn't have signatures, don't cache it - log.Debug("Batch validation failed - no signatures", - "batch_index", batchIndex, - "found", fetchedBatch != nil, - "has_signatures", len(fetchedBatch.Signatures) > 0) - return fetchedBatch, true } } @@ -67,12 +58,8 @@ func (b *BatchCacheLegacy) Get(batchIndex uint64) (*eth.RPCRollupBatch, bool) { } func (b *BatchCacheLegacy) Set(batchIndex uint64, batch *eth.RPCRollupBatch) { - // Validate batch before caching - batch must exist and have signatures - if batch == nil || len(batch.Signatures) == 0 { - log.Debug("Refusing to cache invalid batch", - "batch_index", batchIndex, - "exists", batch != nil, - "has_signatures", batch != nil && len(batch.Signatures) > 0) + if batch == nil { + log.Debug("Refusing to cache nil batch", "batch_index", batchIndex) return } diff --git a/tx-submitter/utils/config.go b/tx-submitter/utils/config.go index 4e9269f95..3a1520636 100644 --- a/tx-submitter/utils/config.go +++ b/tx-submitter/utils/config.go @@ -1,10 +1,13 @@ package utils import ( + "encoding/json" + "fmt" "time" "github.com/urfave/cli" + "morph-l2/common/batch" "morph-l2/tx-submitter/flags" ) @@ -28,8 +31,13 @@ type Config struct { // RollupAddress is the Rollup contract address. RollupAddress string - // StakingAddress - L1StakingAddress string + // SubmitterAddress is the Submitter qualification and stake contract. + SubmitterAddress string + + // Static batch sealing configuration. Values are snapshotted before cutover + // and must remain identical across the working and standby instances. + BatchBlockInterval uint64 + BatchTimeout uint64 // PollInterval is the delay between querying L2 for more transaction // and creating a new batch. @@ -46,18 +54,11 @@ type Config struct { // finalize // if start finalize Finalize bool - // L2 contract - L2SequencerAddress string - L2GovAddress string - // metrics MetricsServerEnable bool MetricsHostname string MetricsPort uint64 - // decentralized - PriorityRollup bool - // tx fee limit TxFeeLimit uint64 @@ -82,6 +83,10 @@ type Config struct { // journal file path JournalFilePath string + // BatchConfigPreflight prints the effective static batch configuration and exits. + BatchConfigPreflight bool + BatchConfigSourceBlockNumber uint64 + BatchConfigSourceBlockHash string // tip bump TipFeeBump uint64 MaxTip uint64 @@ -99,14 +104,6 @@ type Config struct { ExternalSignRsaPriv string // rough estimate gas switch RoughEstimateGas bool - // rotator interval buffer - RotatorBuffer int64 - // listener process path - StakingEventStoreFilename string - // l1 staking deployed block number - L1StakingDeployedBlockNumber uint64 - // event indexer index step - EventIndexStep uint64 // leveldb path name LeveldbPathName string BlockNotIncreasedThreshold int64 @@ -118,6 +115,39 @@ type Config struct { BatchV2UpgradeTime uint64 } +// BatchConfig returns the immutable configuration consumed by common/batch. +func (c Config) BatchConfig() batch.BatchConfig { + return batch.BatchConfig{ + BlockInterval: c.BatchBlockInterval, + Timeout: c.BatchTimeout, + } +} + +// BatchConfigReport is the stable, machine-readable preflight output used by +// operators to compare all working and standby instances before cutover. +type BatchConfigReport struct { + BlockInterval uint64 `json:"batch_block_interval"` + Timeout uint64 `json:"batch_timeout"` + SourceBlockNumber uint64 `json:"source_block_number"` + SourceBlockHash string `json:"source_block_hash"` + Hash string `json:"config_hash"` +} + +func (c Config) BatchConfigReport() BatchConfigReport { + bc := c.BatchConfig() + return BatchConfigReport{ + BlockInterval: bc.BlockInterval, + Timeout: bc.Timeout, + SourceBlockNumber: c.BatchConfigSourceBlockNumber, + SourceBlockHash: c.BatchConfigSourceBlockHash, + Hash: bc.Hash().Hex(), + } +} + +func (c Config) BatchConfigReportJSON() ([]byte, error) { + return json.Marshal(c.BatchConfigReport()) +} + // NewConfig parses the DriverConfig from the provided flags or environment variables. // This method fails if ValidateConfig deems the configuration to be malformed. func NewConfig(ctx *cli.Context) (Config, error) { @@ -131,18 +161,15 @@ func NewConfig(ctx *cli.Context) (Config, error) { TxTimeout: ctx.GlobalDuration(flags.TxTimeoutFlag.Name), // finalize Finalize: ctx.GlobalBool(flags.FinalizeFlag.Name), - // L1 contract - L1StakingAddress: ctx.GlobalString(flags.L1StakingAddressFlag.Name), - RollupAddress: ctx.GlobalString(flags.RollupAddressFlag.Name), - // L2 contract - L2SequencerAddress: ctx.GlobalString(flags.L2SequencerAddressFlag.Name), - L2GovAddress: ctx.GlobalString(flags.L2GovAddressFlag.Name), + // L1 contracts and static batch configuration + SubmitterAddress: ctx.GlobalString(flags.SubmitterAddressFlag.Name), + RollupAddress: ctx.GlobalString(flags.RollupAddressFlag.Name), + BatchBlockInterval: ctx.GlobalUint64(flags.BatchBlockIntervalFlag.Name), + BatchTimeout: ctx.GlobalUint64(flags.BatchTimeoutFlag.Name), // metrics MetricsServerEnable: ctx.GlobalBool(flags.MetricsServerEnable.Name), MetricsHostname: ctx.GlobalString(flags.MetricsHostname.Name), MetricsPort: ctx.GlobalUint64(flags.MetricsPort.Name), - // decentralized - PriorityRollup: ctx.GlobalBool(flags.PriorityRollupFlag.Name), // tx config TxFeeLimit: ctx.GlobalUint64(flags.TxFeeLimitFlag.Name), @@ -160,12 +187,15 @@ func NewConfig(ctx *cli.Context) (Config, error) { GasLimitBuffer: ctx.GlobalUint64(flags.GasLimitBuffer.Name), - JournalFilePath: ctx.GlobalString(flags.JournalFlag.Name), - TipFeeBump: ctx.GlobalUint64(flags.TipFeeBumpFlag.Name), - MaxTip: ctx.GlobalUint64(flags.MaxTipFlag.Name), - MinTip: ctx.GlobalUint64(flags.MinTipFlag.Name), - MaxBaseFee: ctx.GlobalUint64(flags.MaxBaseFeeFlag.Name), - MaxTxsInPendingPool: ctx.GlobalUint64(flags.MaxTxsInPendingPoolFlag.Name), + JournalFilePath: ctx.GlobalString(flags.JournalFlag.Name), + BatchConfigPreflight: ctx.GlobalBool(flags.BatchConfigPreflightFlag.Name), + BatchConfigSourceBlockNumber: ctx.GlobalUint64(flags.BatchConfigSourceBlockNumberFlag.Name), + BatchConfigSourceBlockHash: ctx.GlobalString(flags.BatchConfigSourceBlockHashFlag.Name), + TipFeeBump: ctx.GlobalUint64(flags.TipFeeBumpFlag.Name), + MaxTip: ctx.GlobalUint64(flags.MaxTipFlag.Name), + MinTip: ctx.GlobalUint64(flags.MinTipFlag.Name), + MaxBaseFee: ctx.GlobalUint64(flags.MaxBaseFeeFlag.Name), + MaxTxsInPendingPool: ctx.GlobalUint64(flags.MaxTxsInPendingPoolFlag.Name), // external sign ExternalSign: ctx.GlobalBool(flags.ExternalSign.Name), @@ -176,15 +206,6 @@ func NewConfig(ctx *cli.Context) (Config, error) { ExternalSignRsaPriv: ctx.GlobalString(flags.ExternalSignRsaPriv.Name), // rough estimate gas switch RoughEstimateGas: ctx.GlobalBool(flags.RoughEstimateGasFlag.Name), - // rotator interval buffer - RotatorBuffer: ctx.GlobalInt64(flags.RotatorBufferFlag.Name), - - // path - StakingEventStoreFilename: ctx.GlobalString(flags.StakingEventStoreFileFlag.Name), - // l1 staking deployed block number - L1StakingDeployedBlockNumber: ctx.GlobalUint64(flags.L1StakingDeployedBlocknumFlag.Name), - // index step - EventIndexStep: ctx.GlobalUint64(flags.EventIndexStepFlag.Name), // leveldb path name LeveldbPathName: ctx.GlobalString(flags.LeveldbPathNameFlag.Name), // BlockNotIncreasedThreshold @@ -197,5 +218,9 @@ func NewConfig(ctx *cli.Context) (Config, error) { BatchV2UpgradeTime: ctx.GlobalUint64(flags.BatchV2UpgradeTimeFlag.Name), } + if err := cfg.BatchConfig().Validate(); err != nil { + return Config{}, fmt.Errorf("invalid static batch configuration: %w", err) + } + return cfg, nil } diff --git a/tx-submitter/utils/config_test.go b/tx-submitter/utils/config_test.go new file mode 100644 index 000000000..08f53f76a --- /dev/null +++ b/tx-submitter/utils/config_test.go @@ -0,0 +1,40 @@ +package utils + +import ( + "testing" + + "github.com/stretchr/testify/require" +) + +func TestConfigBatchConfig(t *testing.T) { + tests := []struct { + name string + cfg Config + wantErr bool + }{ + {name: "interval only", cfg: Config{BatchBlockInterval: 10}}, + {name: "timeout only", cfg: Config{BatchTimeout: 60}}, + {name: "both", cfg: Config{BatchBlockInterval: 10, BatchTimeout: 60}}, + {name: "both zero", cfg: Config{}, wantErr: true}, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + require.Equal(t, tt.wantErr, tt.cfg.BatchConfig().Validate() != nil) + }) + } +} + +func TestBatchConfigReportIncludesSourceAndStableHash(t *testing.T) { + cfg := Config{ + BatchBlockInterval: 10, + BatchTimeout: 60, + BatchConfigSourceBlockNumber: 123, + BatchConfigSourceBlockHash: "0xabc", + } + report := cfg.BatchConfigReport() + require.Equal(t, uint64(10), report.BlockInterval) + require.Equal(t, uint64(60), report.Timeout) + require.Equal(t, uint64(123), report.SourceBlockNumber) + require.Equal(t, "0xabc", report.SourceBlockHash) + require.Equal(t, cfg.BatchConfig().Hash().Hex(), report.Hash) +} diff --git a/tx-submitter/utils/utils.go b/tx-submitter/utils/utils.go index 7001e5dae..2b82fe4b3 100644 --- a/tx-submitter/utils/utils.go +++ b/tx-submitter/utils/utils.go @@ -4,9 +4,9 @@ import ( "bytes" "context" "encoding/binary" + "errors" "fmt" "math/big" - "reflect" "regexp" "strconv" "time" @@ -20,6 +20,70 @@ import ( "github.com/morph-l2/go-ethereum/log" ) +var ErrNotOwnedCommit = errors.New("calldata is not an owned current commitBatch") + +// ParseOwnedCommit accepts only the current, post-cutover commitBatch selector. +// Historical commitBatch, commitState, commitBatchWithProof, and unknown +// calldata must never be imported into the official writer's pending state. +func ParseOwnedCommit(data []byte, current *abi.ABI) (methodName string, input *bindings.IRollupBatchDataInput, err error) { + if current == nil || len(data) < 4 { + return "", nil, ErrNotOwnedCommit + } + method, ok := current.Methods["commitBatch"] + if !ok || !bytes.Equal(data[:4], method.ID) { + return "", nil, ErrNotOwnedCommit + } + values, err := method.Inputs.Unpack(data[4:]) + if err != nil || len(values) != 1 { + if err == nil { + err = errors.New("unexpected commitBatch argument count") + } + return "", nil, fmt.Errorf("decode owned commitBatch: %w", err) + } + defer func() { + if recovered := recover(); recovered != nil { + methodName = "" + input = nil + err = fmt.Errorf("decode owned commitBatch tuple: %v", recovered) + } + }() + converted := abi.ConvertType(values[0], new(bindings.IRollupBatchDataInput)) + input, ok = converted.(*bindings.IRollupBatchDataInput) + if !ok || input == nil { + return "", nil, errors.New("decode owned commitBatch: invalid batch tuple") + } + return method.Name, input, nil +} + +func ParseOwnedCommitBatchIndex(data []byte, current *abi.ABI) (uint64, error) { + _, input, err := ParseOwnedCommit(data, current) + if err != nil { + return 0, err + } + if len(input.ParentBatchHeader) < 9 { + return 0, errors.New("owned commitBatch parent header is too short") + } + parentIndex := binary.BigEndian.Uint64(input.ParentBatchHeader[1:9]) + if parentIndex == ^uint64(0) { + return 0, errors.New("owned commitBatch parent index overflows") + } + return parentIndex + 1, nil +} + +// ParseParentBatchIndex is retained for non-authoritative diagnostics. It is +// strict: only the current commitBatch selector is accepted. +func ParseParentBatchIndex(data []byte) uint64 { + current, err := bindings.RollupMetaData.GetAbi() + if err != nil { + return 0 + } + batchIndex, err := ParseOwnedCommitBatchIndex(data, current) + if err != nil || batchIndex == 0 { + return 0 + } + return batchIndex - 1 +} + // Loop Run the f func periodically. func Loop(ctx context.Context, period time.Duration, f func()) { tick := time.NewTicker(period) @@ -63,50 +127,6 @@ func ParseFBatchIndex(calldata []byte) uint64 { return binary.BigEndian.Uint64(batchBytes[1:9]) } -func ParseParentBatchIndex(calldata []byte) uint64 { - /// * Field Bytes Type Index Comments - /// * version 1 uint8 0 The batch version - /// * batchIndex 8 uint64 1 The index of the batch - /// * l1MessagePopped 8 uint64 9 Number of L1 messages popped in the batch - - if len(calldata) < 4 { - return 0 - } - rollupAbi, err := bindings.RollupMetaData.GetAbi() - if err != nil { - return 0 - } - sel := calldata[:4] - var method abi.Method - var ok bool - if bytes.Equal(sel, rollupAbi.Methods["commitState"].ID) { - method, ok = rollupAbi.Methods["commitState"] - } else if bytes.Equal(sel, rollupAbi.Methods["commitBatch"].ID) { - method, ok = rollupAbi.Methods["commitBatch"] - } else { - // Unknown selector: keep legacy behavior (unpack as commitBatch). Matches older fixtures and - // any tx whose first tuple matches BatchDataInput layout even if the selector differs. - method, ok = rollupAbi.Methods["commitBatch"] - } - if !ok { - return 0 - } - parms, err := method.Inputs.UnpackValues(calldata[4:]) - if err != nil || len(parms) == 0 { - return 0 - } - v := reflect.ValueOf(parms[0]) - pbh := v.FieldByName("ParentBatchHeader") - if !pbh.IsValid() { - return 0 - } - b := pbh.Bytes() - if len(b) < 9 { - return 0 - } - return binary.BigEndian.Uint64(b[1:9]) -} - // SetFBatchIndex sets the batch index in the calldata while preserving all other data func SetFBatchIndex(calldata []byte, batchIndex uint64) error { if len(calldata) < 4 { @@ -152,18 +172,14 @@ func ParseBusinessInfo(tx *types.Transaction, a *abi.ABI) []interface{} { // var batchIndex uint64 // var finalizedIndex uint64 var res []interface{} - if len(tx.Data()) > 0 { + if len(tx.Data()) >= 4 { id := tx.Data()[:4] if bytes.Equal(id, a.Methods["commitBatch"].ID) { method := "commitBatch" - batchIndex := ParseParentBatchIndex(tx.Data()) + 1 - res = append(res, - "method", method, - "batchIndex", batchIndex, - ) - } else if bytes.Equal(id, a.Methods["commitState"].ID) { - method := "commitState" - batchIndex := ParseParentBatchIndex(tx.Data()) + 1 + batchIndex, err := ParseOwnedCommitBatchIndex(tx.Data(), a) + if err != nil { + return []interface{}{} + } res = append(res, "method", method, "batchIndex", batchIndex, @@ -195,8 +211,6 @@ func ParseMethod(tx *types.Transaction, a *abi.ABI) string { id := tx.Data()[:4] if bytes.Equal(id, a.Methods["commitBatch"].ID) { return "commitBatch" - } else if bytes.Equal(id, a.Methods["commitState"].ID) { - return "commitState" } else if bytes.Equal(id, a.Methods["finalizeBatch"].ID) { return "finalizeBatch" } else { diff --git a/tx-submitter/utils/utils_test.go b/tx-submitter/utils/utils_test.go index 75f2382f2..8b9eea4b5 100644 --- a/tx-submitter/utils/utils_test.go +++ b/tx-submitter/utils/utils_test.go @@ -8,11 +8,11 @@ import ( ) // test ParseBatchIndex -func TestParseBatchIndex(t *testing.T) { +func TestParseParentBatchIndexRejectsLegacyCalldata(t *testing.T) { calldata := "4a8d544f00000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000008f00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000001e02c48c62b7604ecf508766324cd85afdc5faeb86de53f12dee5c24c7e25957b02018f517672a7760905227d4f085cf88d82b4793497846bb366b6cedd69af06aae5857a041c5a53ed68439ff4c9b8a79b9b749eb93f1e07ed07f58d60ebcf737300000000000000000000000000000000000000000000000000000000000000f900000000000000377b00000000000000000000000000001d987eb22c17eea2e2e3f3756599686586cbc42883ceedded826bc8508c13539cf6e017301c53247c7de720dd18b7d668cbcc17cb711a7aae46fbc43224203e0bca62c74b38e4c32df8e0656c3bd6acedc0ec57684327c99d10a1247f00ab8d3dece2c48c62b7604ecf508766324cd85afdc5faeb86de53f12dee5c24c7e25957b02e5857a041c5a53ed68439ff4c9b8a79b9b749eb93f1e07ed07f58d60ebcf7373a4d7461c3b8d74650efdca322d5fe15e699d534ee47ec9321bcb82bc7a9ec6e97bf7c21d9929b9d5d906a163a1dd8e74341ef11f7b84681b67d76953ee6ee3fb000000000000000000000000000000000000000000000000000000000000000000000000008ca2025800000000006acc320000000067d1c5aa00000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006acc330000000067d1c5ab00000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800003000000000000006acc340000000067d1c5ac00000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006acc350000000067d1c5ad00000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006acc360000000067d1c5ae00000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006acc370000000067d1c5af00000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800002000000000000006acc380000000067d1c5b000000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800004000000000000006acc390000000067d1c5b100000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006acc3a0000000067d1c5b200000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800002000000000000006acc3b0000000067d1c5b300000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006acc3c0000000067d1c5b500000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006acc3d0000000067d1c5b600000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800002000000000000006acc3e0000000067d1c5b700000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006acc3f0000000067d1c5b900000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006acc400000000067d1c5ba00000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800002000000000000006acc410000000067d1c5bc00000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006acc420000000067d1c5bd00000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006acc430000000067d1c5be00000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006acc440000000067d1c5bf00000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006acc450000000067d1c5c000000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006acc460000000067d1c5c100000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006acc470000000067d1c5c200000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800002000000000000006acc480000000067d1c5c300000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006acc490000000067d1c5c400000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006acc4a0000000067d1c5c500000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006acc4b0000000067d1c5c600000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800003000000000000006acc4c0000000067d1c5c700000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006acc4d0000000067d1c5c800000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006acc4e0000000067d1c5c900000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800002000000000000006acc4f0000000067d1c5ca00000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006acc500000000067d1c5cc00000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800002000000000000006acc510000000067d1c5cd00000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006acc520000000067d1c5ce00000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006acc530000000067d1c5cf00000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006acc540000000067d1c5d000000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800005000000000000006acc550000000067d1c5d100000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006acc560000000067d1c5d200000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006acc570000000067d1c5d300000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800002000000000000006acc580000000067d1c5d400000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800002000000000000006acc590000000067d1c5d500000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800003000000000000006acc5a0000000067d1c5d600000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006acc5b0000000067d1c5d700000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006acc5c0000000067d1c5d800000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006acc5d0000000067d1c5d900000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800002000000000000006acc5e0000000067d1c5da00000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800003000000000000006acc5f0000000067d1c5db00000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006acc600000000067d1c5dc00000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006acc610000000067d1c5dd00000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006acc620000000067d1c5de00000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800002000000000000006acc630000000067d1c5df00000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006acc640000000067d1c5e000000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006acc650000000067d1c5e200000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006acc660000000067d1c5e300000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006acc670000000067d1c5e400000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006acc680000000067d1c5e500000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800002000000000000006acc690000000067d1c5e600000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006acc6a0000000067d1c5e700000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006acc6b0000000067d1c5e800000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006acc6c0000000067d1c5e900000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800003000000000000006acc6d0000000067d1c5ea00000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006acc6e0000000067d1c5eb00000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800002000000000000006acc6f0000000067d1c5ec00000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800003000000000000006acc700000000067d1c5ed00000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800002000000000000006acc710000000067d1c5ee00000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006acc720000000067d1c5ef00000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006acc730000000067d1c5f000000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006acc740000000067d1c5f300000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006acc750000000067d1c5f400000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006acc760000000067d1c5f500000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006acc770000000067d1c5f600000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006acc780000000067d1c5f700000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006acc790000000067d1c5f800000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006acc7a0000000067d1c5f900000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006acc7b0000000067d1c5fa00000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800002000000000000006acc7c0000000067d1c5fb00000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006acc7d0000000067d1c5fc00000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006acc7e0000000067d1c5fd00000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006acc7f0000000067d1c5ff00000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800002000000000000006acc800000000067d1c60000000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006acc810000000067d1c60200000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006acc820000000067d1c60300000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006acc830000000067d1c60400000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006acc840000000067d1c60600000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800002000000000000006acc850000000067d1c60700000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006acc860000000067d1c60800000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006acc870000000067d1c60a00000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800002000000000000006acc880000000067d1c60c00000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006acc890000000067d1c60e00000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006acc8a0000000067d1c60f00000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800003000000000000006acc8b0000000067d1c61000000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800003000000000000006acc8c0000000067d1c61200000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006acc8d0000000067d1c61300000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006acc8e0000000067d1c61400000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800002000000000000006acc8f0000000067d1c61500000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800003000000000000006acc900000000067d1c61600000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006acc910000000067d1c61700000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006acc920000000067d1c61800000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006acc930000000067d1c61900000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006acc940000000067d1c61a00000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006acc950000000067d1c61b00000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006acc960000000067d1c61d00000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006acc970000000067d1c61e00000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006acc980000000067d1c61f00000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006acc990000000067d1c62000000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006acc9a0000000067d1c62100000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006acc9b0000000067d1c62200000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800002000000000000006acc9c0000000067d1c62300000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006acc9d0000000067d1c62500000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006acc9e0000000067d1c62600000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800003000000000000006acc9f0000000067d1c62700000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006acca00000000067d1c62800000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006acca10000000067d1c62a00000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006acca20000000067d1c62b00000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006acca30000000067d1c62c00000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006acca40000000067d1c62d00000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006acca50000000067d1c62e00000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006acca60000000067d1c63000000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006acca70000000067d1c63300000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800002000000000000006acca80000000067d1c63400000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006acca90000000067d1c63600000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006accaa0000000067d1c63900000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006accab0000000067d1c63a00000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006accac0000000067d1c63d00000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006accad0000000067d1c63e00000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006accae0000000067d1c63f00000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006accaf0000000067d1c64100000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006accb00000000067d1c64300000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006accb10000000067d1c64500000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800002000000000000006accb20000000067d1c64700000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006accb30000000067d1c64900000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006accb40000000067d1c64b00000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006accb50000000067d1c64c00000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006accb60000000067d1c64e00000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006accb70000000067d1c64f00000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006accb80000000067d1c65000000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006accb90000000067d1c65200000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006accba0000000067d1c65300000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006accbb0000000067d1c65400000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006accbc0000000067d1c65500000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006accbd0000000067d1c65600000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006accbe0000000067d1c65700000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006accbf0000000067d1c65900000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006accc00000000067d1c65a00000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800002000000000000006accc10000000067d1c65b00000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006accc20000000067d1c65c00000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006accc30000000067d1c65d00000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006accc40000000067d1c65e00000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006accc50000000067d1c65f00000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006accc60000000067d1c66000000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800003000000000000006accc70000000067d1c66100000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006accc80000000067d1c66200000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006accc90000000067d1c66300000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006accca0000000067d1c66400000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006acccb0000000067d1c66500000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800002000000000000006acccc0000000067d1c66600000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006acccd0000000067d1c66700000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800003000000000000006accce0000000067d1c66800000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800002000000000000006acccf0000000067d1c66900000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006accd00000000067d1c66a00000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006accd10000000067d1c66b00000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800002000000000000006accd20000000067d1c66c00000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800002000000000000006accd30000000067d1c66d00000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006accd40000000067d1c66e00000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006accd50000000067d1c66f00000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006accd60000000067d1c67000000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800002000000000000006accd70000000067d1c67200000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006accd80000000067d1c67300000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006accd90000000067d1c67400000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006accda0000000067d1c67500000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006accdb0000000067d1c67600000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006accdc0000000067d1c67700000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006accdd0000000067d1c67800000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006accde0000000067d1c67900000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006accdf0000000067d1c67a00000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006acce00000000067d1c67b00000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006acce10000000067d1c67c00000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006acce20000000067d1c67d00000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006acce30000000067d1c67e00000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006acce40000000067d1c67f00000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800002000000000000006acce50000000067d1c68000000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006acce60000000067d1c68100000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800002000000000000006acce70000000067d1c68200000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006acce80000000067d1c68300000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800002000000000000006acce90000000067d1c68400000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006accea0000000067d1c68500000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006acceb0000000067d1c68600000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800002000000000000006accec0000000067d1c68700000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800002000000000000006acced0000000067d1c68800000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800002000000000000006accee0000000067d1c68900000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006accef0000000067d1c68b00000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006accf00000000067d1c68d00000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006accf10000000067d1c68e00000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006accf20000000067d1c68f00000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006accf30000000067d1c69000000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800003000000000000006accf40000000067d1c69100000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006accf50000000067d1c69200000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800002000000000000006accf60000000067d1c69300000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800002000000000000006accf70000000067d1c69400000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006accf80000000067d1c69500000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006accf90000000067d1c69600000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800002000000000000006accfa0000000067d1c69700000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006accfb0000000067d1c69800000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006accfc0000000067d1c69900000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006accfd0000000067d1c69a00000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006accfe0000000067d1c69b00000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800002000000000000006accff0000000067d1c69c00000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006acd000000000067d1c69d00000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006acd010000000067d1c69e00000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800002000000000000006acd020000000067d1c69f00000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006acd030000000067d1c6a000000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006acd040000000067d1c6a100000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800002000000000000006acd050000000067d1c6a300000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800002000000000000006acd060000000067d1c6a400000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006acd070000000067d1c6a500000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006acd080000000067d1c6a600000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006acd090000000067d1c6a700000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006acd0a0000000067d1c6a800000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800002000000000000006acd0b0000000067d1c6a900000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006acd0c0000000067d1c6aa00000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800002000000000000006acd0d0000000067d1c6ab00000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006acd0e0000000067d1c6ac00000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006acd0f0000000067d1c6af00000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006acd100000000067d1c6b100000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006acd110000000067d1c6b200000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006acd120000000067d1c6b300000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006acd130000000067d1c6b500000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800002000000000000006acd140000000067d1c6b800000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006acd150000000067d1c6bb00000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006acd160000000067d1c6bd00000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800002000000000000006acd170000000067d1c6bf00000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006acd180000000067d1c6c100000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800002000000000000006acd190000000067d1c6c300000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006acd1a0000000067d1c6c400000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006acd1b0000000067d1c6c500000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006acd1c0000000067d1c6c600000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006acd1d0000000067d1c6c700000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006acd1e0000000067d1c6c800000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006acd1f0000000067d1c6ca00000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006acd200000000067d1c6cb00000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006acd210000000067d1c6cd00000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006acd220000000067d1c6ce00000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006acd230000000067d1c6cf00000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006acd240000000067d1c6d100000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006acd250000000067d1c6d300000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006acd260000000067d1c6d400000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006acd270000000067d1c6d500000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006acd280000000067d1c6d600000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006acd290000000067d1c6d800000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006acd2a0000000067d1c6da00000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006acd2b0000000067d1c6dc00000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800002000000000000006acd2c0000000067d1c6de00000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006acd2d0000000067d1c6df00000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006acd2e0000000067d1c6e000000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006acd2f0000000067d1c6e200000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006acd300000000067d1c6e300000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006acd310000000067d1c6e400000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006acd320000000067d1c6e500000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006acd330000000067d1c6e600000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006acd340000000067d1c6e800000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006acd350000000067d1c6e900000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006acd360000000067d1c6eb00000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006acd370000000067d1c6ed00000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006acd380000000067d1c6ef00000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800002000000000000006acd390000000067d1c6f100000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006acd3a0000000067d1c6f200000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006acd3b0000000067d1c6f300000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006acd3c0000000067d1c6f500000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006acd3d0000000067d1c6f600000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006acd3e0000000067d1c6f700000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006acd3f0000000067d1c6f800000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006acd400000000067d1c6fa00000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006acd410000000067d1c6fb00000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006acd420000000067d1c6fc00000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006acd430000000067d1c6fd00000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006acd440000000067d1c6fe00000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006acd450000000067d1c6ff00000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006acd460000000067d1c70000000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006acd470000000067d1c70100000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006acd480000000067d1c70200000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006acd490000000067d1c70300000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006acd4a0000000067d1c70400000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006acd4b0000000067d1c70500000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006acd4c0000000067d1c70600000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006acd4d0000000067d1c70700000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006acd4e0000000067d1c70800000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006acd4f0000000067d1c70900000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800003000000000000006acd500000000067d1c70b00000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006acd510000000067d1c70c00000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006acd520000000067d1c70d00000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006acd530000000067d1c70e00000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006acd540000000067d1c70f00000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006acd550000000067d1c71000000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006acd560000000067d1c71100000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800002000000000000006acd570000000067d1c71300000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006acd580000000067d1c71500000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006acd590000000067d1c71600000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800003000000000000006acd5a0000000067d1c71700000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006acd5b0000000067d1c71800000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800002000000000000006acd5c0000000067d1c71900000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006acd5d0000000067d1c71a00000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800004000000000000006acd5e0000000067d1c71b00000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006acd5f0000000067d1c71c00000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006acd600000000067d1c71d00000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800003000000000000006acd610000000067d1c71e00000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006acd620000000067d1c71f00000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800002000000000000006acd630000000067d1c72000000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006acd640000000067d1c72100000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006acd650000000067d1c72200000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800003000000000000006acd660000000067d1c72300000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800002000000000000006acd670000000067d1c72400000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006acd680000000067d1c72500000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006acd690000000067d1c72600000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800002000000000000006acd6a0000000067d1c72700000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006acd6b0000000067d1c72800000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006acd6c0000000067d1c72900000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006acd6d0000000067d1c72a00000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800002000000000000006acd6e0000000067d1c72b00000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800004000000000000006acd6f0000000067d1c72c00000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800002000000000000006acd700000000067d1c72e00000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006acd710000000067d1c72f00000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006acd720000000067d1c73000000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800002000000000000006acd730000000067d1c73100000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800002000000000000006acd740000000067d1c73200000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006acd750000000067d1c73300000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800002000000000000006acd760000000067d1c73400000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006acd770000000067d1c73500000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006acd780000000067d1c73600000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800002000000000000006acd790000000067d1c73700000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800003000000000000006acd7a0000000067d1c73800000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800002000000000000006acd7b0000000067d1c73900000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006acd7c0000000067d1c73a00000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006acd7d0000000067d1c73b00000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800003000000000000006acd7e0000000067d1c73c00000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006acd7f0000000067d1c73e00000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800003000000000000006acd800000000067d1c73f00000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006acd810000000067d1c74000000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006acd820000000067d1c74100000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006acd830000000067d1c74200000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800003000000000000006acd840000000067d1c74300000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006acd850000000067d1c74400000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800002000000000000006acd860000000067d1c74500000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006acd870000000067d1c74600000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800002000000000000006acd880000000067d1c74700000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800002000000000000006acd890000000067d1c74800000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006acd8a0000000067d1c74900000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800003000000000000006acd8b0000000067d1c74a00000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006acd8c0000000067d1c74b00000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006acd8d0000000067d1c74c00000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006acd8e0000000067d1c74d00000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006acd8f0000000067d1c74f00000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006acd900000000067d1c75000000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006acd910000000067d1c75100000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006acd920000000067d1c75200000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006acd930000000067d1c75300000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006acd940000000067d1c75500000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006acd950000000067d1c75600000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006acd960000000067d1c75700000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006acd970000000067d1c75800000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006acd980000000067d1c75900000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006acd990000000067d1c75a00000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006acd9a0000000067d1c75b00000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006acd9b0000000067d1c75c00000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006acd9c0000000067d1c75d00000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006acd9d0000000067d1c75e00000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006acd9e0000000067d1c75f00000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006acd9f0000000067d1c76000000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800002000000000000006acda00000000067d1c76200000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006acda10000000067d1c76300000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800002000000000000006acda20000000067d1c76500000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006acda30000000067d1c76600000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006acda40000000067d1c76700000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006acda50000000067d1c76800000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006acda60000000067d1c76a00000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006acda70000000067d1c76b00000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006acda80000000067d1c76c00000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006acda90000000067d1c76d00000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006acdaa0000000067d1c76e00000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006acdab0000000067d1c76f00000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800002000000000000006acdac0000000067d1c77000000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006acdad0000000067d1c77100000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006acdae0000000067d1c77200000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006acdaf0000000067d1c77300000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006acdb00000000067d1c77400000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006acdb10000000067d1c77500000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006acdb20000000067d1c77600000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006acdb30000000067d1c77700000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006acdb40000000067d1c77800000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800002000000000000006acdb50000000067d1c77900000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800002000000000000006acdb60000000067d1c77a00000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800002000000000000006acdb70000000067d1c77b00000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006acdb80000000067d1c77c00000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006acdb90000000067d1c77e00000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006acdba0000000067d1c78000000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006acdbb0000000067d1c78200000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006acdbc0000000067d1c78300000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006acdbd0000000067d1c78400000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006acdbe0000000067d1c78600000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006acdbf0000000067d1c78700000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006acdc00000000067d1c78800000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006acdc10000000067d1c78b00000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006acdc20000000067d1c78c00000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006acdc30000000067d1c78d00000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006acdc40000000067d1c78f00000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006acdc50000000067d1c79100000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006acdc60000000067d1c79300000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006acdc70000000067d1c79400000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006acdc80000000067d1c79500000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006acdc90000000067d1c79700000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006acdca0000000067d1c79800000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800002000000000000006acdcb0000000067d1c79900000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800002000000000000006acdcc0000000067d1c79b00000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006acdcd0000000067d1c79c00000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006acdce0000000067d1c79e00000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006acdcf0000000067d1c79f00000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006acdd00000000067d1c7a000000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006acdd10000000067d1c7a200000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006acdd20000000067d1c7a400000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006acdd30000000067d1c7a600000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006acdd40000000067d1c7a700000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006acdd50000000067d1c7a800000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006acdd60000000067d1c7a900000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006acdd70000000067d1c7aa00000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006acdd80000000067d1c7ab00000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006acdd90000000067d1c7ac00000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006acdda0000000067d1c7ad00000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800002000000000000006acddb0000000067d1c7ae00000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800002000000000000006acddc0000000067d1c7af00000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006acddd0000000067d1c7b000000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006acdde0000000067d1c7b100000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006acddf0000000067d1c7b200000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006acde00000000067d1c7b300000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006acde10000000067d1c7b500000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006acde20000000067d1c7b700000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006acde30000000067d1c7b800000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006acde40000000067d1c7b900000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006acde50000000067d1c7bb00000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006acde60000000067d1c7bc00000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006acde70000000067d1c7bd00000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006acde80000000067d1c7bf00000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800002000000000000006acde90000000067d1c7c000000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006acdea0000000067d1c7c200000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006acdeb0000000067d1c7c300000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006acdec0000000067d1c7c400000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006acded0000000067d1c7c500000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800003000000000000006acdee0000000067d1c7c600000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006acdef0000000067d1c7c800000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006acdf00000000067d1c7c900000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800002000000000000006acdf10000000067d1c7ca00000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006acdf20000000067d1c7cb00000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006acdf30000000067d1c7cc00000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006acdf40000000067d1c7cd00000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006acdf50000000067d1c7ce00000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006acdf60000000067d1c7d000000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006acdf70000000067d1c7d100000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006acdf80000000067d1c7d200000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006acdf90000000067d1c7d300000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006acdfa0000000067d1c7d400000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800002000000000000006acdfb0000000067d1c7d500000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006acdfc0000000067d1c7d600000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006acdfd0000000067d1c7d700000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006acdfe0000000067d1c7d800000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006acdff0000000067d1c7d900000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006ace000000000067d1c7da00000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800002000000000000006ace010000000067d1c7db00000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800002000000000000006ace020000000067d1c7dd00000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006ace030000000067d1c7df00000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006ace040000000067d1c7e000000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006ace050000000067d1c7e100000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800005000000000000006ace060000000067d1c7e200000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006ace070000000067d1c7e300000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800002000000000000006ace080000000067d1c7e400000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006ace090000000067d1c7e500000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800002000000000000006ace0a0000000067d1c7e600000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006ace0b0000000067d1c7e700000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006ace0c0000000067d1c7e800000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006ace0d0000000067d1c7e900000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800003000000000000006ace0e0000000067d1c7ea00000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006ace0f0000000067d1c7eb00000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800003000000000000006ace100000000067d1c7ec00000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800003000000000000006ace110000000067d1c7ed00000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800002000000000000006ace120000000067d1c7ee00000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800002000000000000006ace130000000067d1c7ef00000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006ace140000000067d1c7f000000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006ace150000000067d1c7f100000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006ace160000000067d1c7f200000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006ace170000000067d1c7f300000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800002000000000000006ace180000000067d1c7f400000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800003000000000000006ace190000000067d1c7f500000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006ace1a0000000067d1c7f700000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006ace1b0000000067d1c7f800000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006ace1c0000000067d1c7f900000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006ace1d0000000067d1c7fa00000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006ace1e0000000067d1c7fb00000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800003000000000000006ace1f0000000067d1c7fc00000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006ace200000000067d1c7fd00000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800002000000000000006ace210000000067d1c7fe00000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006ace220000000067d1c7ff00000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006ace230000000067d1c80000000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006ace240000000067d1c80100000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800002000000000000006ace250000000067d1c80200000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006ace260000000067d1c80300000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006ace270000000067d1c80400000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800002000000000000006ace280000000067d1c80500000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006ace290000000067d1c80600000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006ace2a0000000067d1c80800000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006ace2b0000000067d1c80900000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006ace2c0000000067d1c80a00000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006ace2d0000000067d1c80c00000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006ace2e0000000067d1c80d00000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006ace2f0000000067d1c80e00000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006ace300000000067d1c81000000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006ace310000000067d1c81100000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800002000000000000006ace320000000067d1c81200000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006ace330000000067d1c81300000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006ace340000000067d1c81400000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006ace350000000067d1c81500000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006ace360000000067d1c81600000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006ace370000000067d1c81700000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006ace380000000067d1c81800000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006ace390000000067d1c81900000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800004000000000000006ace3a0000000067d1c81b00000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006ace3b0000000067d1c81c00000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006ace3c0000000067d1c81d00000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006ace3d0000000067d1c81f00000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006ace3e0000000067d1c82100000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800003000000000000006ace3f0000000067d1c82300000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006ace400000000067d1c82400000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006ace410000000067d1c82500000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006ace420000000067d1c82600000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006ace430000000067d1c82700000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006ace440000000067d1c82800000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006ace450000000067d1c82900000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006ace460000000067d1c82a00000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800002000000000000006ace470000000067d1c82b00000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006ace480000000067d1c82c00000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006ace490000000067d1c82d00000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800002000000000000006ace4a0000000067d1c82e00000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800003000000000000006ace4b0000000067d1c83000000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006ace4c0000000067d1c83100000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006ace4d0000000067d1c83200000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006ace4e0000000067d1c83400000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800002000000000000006ace4f0000000067d1c83500000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006ace500000000067d1c83600000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006ace510000000067d1c83700000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006ace520000000067d1c83800000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006ace530000000067d1c83900000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006ace540000000067d1c83a00000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006ace550000000067d1c83b00000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006ace560000000067d1c83c00000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006ace570000000067d1c83d00000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800002000000000000006ace580000000067d1c83e00000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800003000000000000006ace590000000067d1c83f00000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800002000000000000006ace5a0000000067d1c84100000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006ace5b0000000067d1c84200000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006ace5c0000000067d1c84300000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800002000000000000006ace5d0000000067d1c84400000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800002000000000000006ace5e0000000067d1c84500000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006ace5f0000000067d1c84700000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006ace600000000067d1c84900000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006ace610000000067d1c84a00000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006ace620000000067d1c84b00000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006ace630000000067d1c84c00000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006ace640000000067d1c84d00000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006ace650000000067d1c84e00000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006ace660000000067d1c84f00000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006ace670000000067d1c85000000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006ace680000000067d1c85100000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006ace690000000067d1c85200000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006ace6a0000000067d1c85300000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800003000000000000006ace6b0000000067d1c85400000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800002000000000000006ace6c0000000067d1c85600000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006ace6d0000000067d1c85800000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800002000000000000006ace6e0000000067d1c85900000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006ace6f0000000067d1c85a00000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006ace700000000067d1c85b00000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006ace710000000067d1c85c00000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006ace720000000067d1c85d00000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006ace730000000067d1c85e00000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006ace740000000067d1c85f00000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800002000000000000006ace750000000067d1c86000000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006ace760000000067d1c86100000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800002000000000000006ace770000000067d1c86300000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006ace780000000067d1c86400000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006ace790000000067d1c86500000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800002000000000000006ace7a0000000067d1c86700000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006ace7b0000000067d1c86800000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006ace7c0000000067d1c86900000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006ace7d0000000067d1c86a00000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800002000000000000006ace7e0000000067d1c86c00000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800002000000000000006ace7f0000000067d1c86d00000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800002000000000000006ace800000000067d1c86e00000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006ace810000000067d1c87000000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006ace820000000067d1c87100000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006ace830000000067d1c87200000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800003000000000000006ace840000000067d1c87300000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006ace850000000067d1c87400000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006ace860000000067d1c87500000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006ace870000000067d1c87600000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800002000000000000006ace880000000067d1c87700000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000006ace890000000067d1c87800000000000000000000000000000000000000000000000000000000000f42400000000001c9c3800001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000fe0000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000044000000000000000000000000000000000000000000000000000000000000003c0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001c0000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000000000000000000000000000000000002c000000000000000000000000000000000000000000000000000000000000000070000000000000000000000006ab0e960911b50f6d14f249782ac12ec3e7584a0000000000000000000000000bba36cdf020788f0d08d5688c0bee3fb30ce1c8000000000000000000000000034e387b37d3adeaa6d5b92ce30de3af3dca39796000000000000000000000000a59b26db10c5ca26a97aa2fd2e74cb8da9d1eb65000000000000000000000000f834ffbeb6bb3f4841afc6b5fb40b94cd580fa2300000000000000000000000061f2945d4bc9e40b66a6376d1094a50438f613e2000000000000000000000000b6cf39ee72e0127e6ea6059e38b8c197227a6ac700000000000000000000000000000000000000000000000000000000000000070000000000000000000000006ab0e960911b50f6d14f249782ac12ec3e7584a0000000000000000000000000bba36cdf020788f0d08d5688c0bee3fb30ce1c8000000000000000000000000034e387b37d3adeaa6d5b92ce30de3af3dca39796000000000000000000000000a59b26db10c5ca26a97aa2fd2e74cb8da9d1eb65000000000000000000000000f834ffbeb6bb3f4841afc6b5fb40b94cd580fa2300000000000000000000000061f2945d4bc9e40b66a6376d1094a50438f613e2000000000000000000000000b6cf39ee72e0127e6ea6059e38b8c197227a6ac700000000000000000000000000000000000000000000000000000000000000070000000000000000000000006ab0e960911b50f6d14f249782ac12ec3e7584a0000000000000000000000000bba36cdf020788f0d08d5688c0bee3fb30ce1c8000000000000000000000000034e387b37d3adeaa6d5b92ce30de3af3dca39796000000000000000000000000a59b26db10c5ca26a97aa2fd2e74cb8da9d1eb65000000000000000000000000f834ffbeb6bb3f4841afc6b5fb40b94cd580fa2300000000000000000000000061f2945d4bc9e40b66a6376d1094a50438f613e2000000000000000000000000b6cf39ee72e0127e6ea6059e38b8c197227a6ac700000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000015f903b2fe6b812c9c5b4882525df682e2a26238bdd18badf5a4d3bf2dc2ec8acd600d92182181e25359a631da62585b00000000000000000000000000000000022a08b55fac8d2669fb3a47977ac43b2b9918d953532cab1625c9e94f49c333b7160442a38e8399a285646df4081c60" bs, err := hex.DecodeString(calldata) require.NoError(t, err) - require.EqualValues(t, ParseParentBatchIndex(bs), 14203) + require.Zero(t, ParseParentBatchIndex(bs)) } func TestParseFBatchIndex(t *testing.T) { From f311846a9a182c2fe93974629ce0811e57611954 Mon Sep 17 00:00:00 2001 From: Segue Date: Tue, 11 Aug 2026 20:41:00 +0800 Subject: [PATCH 07/16] feat(derivation): resolve canonical rollup submissions --- node/derivation/calldata_fixture_test.go | 199 +++++++++ node/derivation/canonical_commit.go | 376 ++++++++++++++++++ node/derivation/canonical_commit_test.go | 225 +++++++++++ node/derivation/derivation.go | 257 ++++++++---- node/derivation/derivation_test.go | 3 + .../testdata/rollup_calldata_fixtures.json | 123 ++++++ node/types/batch.go | 10 + 7 files changed, 1125 insertions(+), 68 deletions(-) create mode 100644 node/derivation/calldata_fixture_test.go create mode 100644 node/derivation/canonical_commit.go create mode 100644 node/derivation/canonical_commit_test.go create mode 100644 node/derivation/testdata/rollup_calldata_fixtures.json diff --git a/node/derivation/calldata_fixture_test.go b/node/derivation/calldata_fixture_test.go new file mode 100644 index 000000000..4653f515d --- /dev/null +++ b/node/derivation/calldata_fixture_test.go @@ -0,0 +1,199 @@ +package derivation + +import ( + "encoding/json" + "errors" + "os" + "strings" + "testing" + + "github.com/morph-l2/go-ethereum/accounts/abi" + "github.com/morph-l2/go-ethereum/common" + "github.com/morph-l2/go-ethereum/common/hexutil" + "github.com/stretchr/testify/require" + + "morph-l2/bindings/bindings" + "morph-l2/node/types" +) + +const targetCurrentRollupABI = `[ + {"type":"function","name":"commitBatch","stateMutability":"payable","inputs":[{"name":"batchDataInput","type":"tuple","components":[{"name":"version","type":"uint8"},{"name":"parentBatchHeader","type":"bytes"},{"name":"lastBlockNumber","type":"uint64"},{"name":"numL1Messages","type":"uint16"},{"name":"prevStateRoot","type":"bytes32"},{"name":"postStateRoot","type":"bytes32"},{"name":"withdrawalRoot","type":"bytes32"}]}],"outputs":[]}, + {"type":"function","name":"commitState","stateMutability":"nonpayable","inputs":[{"name":"batchDataInput","type":"tuple","components":[{"name":"version","type":"uint8"},{"name":"parentBatchHeader","type":"bytes"},{"name":"lastBlockNumber","type":"uint64"},{"name":"numL1Messages","type":"uint16"},{"name":"prevStateRoot","type":"bytes32"},{"name":"postStateRoot","type":"bytes32"},{"name":"withdrawalRoot","type":"bytes32"}]}],"outputs":[]}, + {"type":"function","name":"commitBatchWithProof","stateMutability":"nonpayable","inputs":[{"name":"batchDataInput","type":"tuple","components":[{"name":"version","type":"uint8"},{"name":"parentBatchHeader","type":"bytes"},{"name":"lastBlockNumber","type":"uint64"},{"name":"numL1Messages","type":"uint16"},{"name":"prevStateRoot","type":"bytes32"},{"name":"postStateRoot","type":"bytes32"},{"name":"withdrawalRoot","type":"bytes32"}]},{"name":"batchHeader","type":"bytes"},{"name":"batchProof","type":"bytes"}],"outputs":[]} +]` + +type calldataFixtureCorpus struct { + SchemaVersion int `json:"schemaVersion"` + Fixtures []calldataFixture `json:"fixtures"` +} + +type calldataFixture struct { + Name string `json:"name"` + Epoch string `json:"epoch"` + Selector string `json:"selector"` + Data string `json:"data"` + Expected calldataFixtureExpected `json:"expected"` +} + +type calldataFixtureExpected struct { + Version uint64 `json:"version"` + ParentBatchHeader string `json:"parentBatchHeader"` + BlockContexts string `json:"blockContexts"` + LastBlockNumber uint64 `json:"lastBlockNumber"` + NumL1Messages uint16 `json:"numL1Messages"` + PrevStateRoot string `json:"prevStateRoot"` + PostStateRoot string `json:"postStateRoot"` + WithdrawRoot string `json:"withdrawRoot"` +} + +func loadCalldataFixtureCorpus(t *testing.T) calldataFixtureCorpus { + t.Helper() + raw, err := os.ReadFile("testdata/rollup_calldata_fixtures.json") + require.NoError(t, err) + var corpus calldataFixtureCorpus + require.NoError(t, json.Unmarshal(raw, &corpus)) + require.Equal(t, 1, corpus.SchemaVersion) + require.Len(t, corpus.Fixtures, 8) + return corpus +} + +func newFixtureDerivation(t *testing.T) *Derivation { + t.Helper() + currentABI, err := abi.JSON(strings.NewReader(targetCurrentRollupABI)) + require.NoError(t, err) + preSubmitterABI, err := types.PreSubmitterRollupMetaData.GetAbi() + require.NoError(t, err) + legacyABI, err := types.LegacyRollupMetaData.GetAbi() + require.NoError(t, err) + beforeMoveABI, err := types.BeforeMoveBlockCtxABI.GetAbi() + require.NoError(t, err) + return &Derivation{ + rollupABI: ¤tABI, + preSubmitterRollupABI: preSubmitterABI, + legacyRollupABI: legacyABI, + beforeMoveBlockCtxABI: beforeMoveABI, + } +} + +func TestUnPackDataAllHistoricalAndCurrentSelectors(t *testing.T) { + d := newFixtureDerivation(t) + for _, fixture := range loadCalldataFixtureCorpus(t).Fixtures { + t.Run(fixture.Name, func(t *testing.T) { + data, err := hexutil.Decode(fixture.Data) + require.NoError(t, err) + require.Equal(t, fixture.Selector, hexutil.Encode(data[:4])) + + got, err := d.UnPackData(data) + require.NoError(t, err) + require.Equal(t, uint(fixture.Expected.Version), got.Version) + require.Equal(t, common.FromHex(fixture.Expected.ParentBatchHeader), []byte(got.ParentBatchHeader)) + expectedBlockContexts := fixture.Expected.BlockContexts + if expectedBlockContexts == "" { + expectedBlockContexts = "0x" + } + require.Equal(t, expectedBlockContexts, hexutil.Encode(got.BlockContexts)) + require.Equal(t, fixture.Expected.LastBlockNumber, got.LastBlockNumber) + require.Equal(t, fixture.Expected.NumL1Messages, got.NumL1Messages) + require.Equal(t, common.HexToHash(fixture.Expected.PrevStateRoot), got.PrevStateRoot) + require.Equal(t, common.HexToHash(fixture.Expected.PostStateRoot), got.PostStateRoot) + require.Equal(t, common.HexToHash(fixture.Expected.WithdrawRoot), got.WithdrawRoot) + }) + } +} + +func TestUnPackDataRejectsUnknownShortAndMalformedCalldata(t *testing.T) { + d := newFixtureDerivation(t) + for length := 0; length < 4; length++ { + _, err := d.UnPackData(make([]byte, length)) + require.ErrorIs(t, err, types.ErrNotCommitBatchTx) + } + _, err := d.UnPackData([]byte{0xde, 0xad, 0xbe, 0xef}) + require.ErrorIs(t, err, types.ErrNotCommitBatchTx) + + currentABI, err := abi.JSON(strings.NewReader(targetCurrentRollupABI)) + require.NoError(t, err) + _, err = d.UnPackData(currentABI.Methods["commitBatch"].ID) + require.Error(t, err) + require.False(t, errors.Is(err, types.ErrNotCommitBatchTx), "known selector with malformed arguments must not be reported as unknown") +} + +func TestUnPackDataMixedCutoverWindow(t *testing.T) { + d := newFixtureDerivation(t) + fixtures := make(map[string]string) + for _, fixture := range loadCalldataFixtureCorpus(t).Fixtures { + fixtures[fixture.Name] = fixture.Data + } + for _, name := range []string{ + "pre_submitter_commitBatch", + "current_commitBatch", + "pre_submitter_commitState", + "current_commitState", + "pre_submitter_commitBatchWithProof", + "current_commitBatchWithProof", + } { + data, err := hexutil.Decode(fixtures[name]) + require.NoError(t, err) + _, err = d.UnPackData(data) + require.NoError(t, err, name) + } +} + +func TestSubmissionSelectorClassification(t *testing.T) { + d := newFixtureDerivation(t) + for _, fixture := range loadCalldataFixtureCorpus(t).Fixtures { + data, err := hexutil.Decode(fixture.Data) + require.NoError(t, err) + switch fixture.Name { + case "pre_submitter_commitState", "pre_submitter_commitBatchWithProof", "current_commitState", "current_commitBatchWithProof": + require.True(t, d.isStoredSourceSubmission(data), fixture.Name) + case "current_commitBatch": + require.True(t, d.isCurrentFreshCommitBatch(data), fixture.Name) + default: + require.False(t, d.isStoredSourceSubmission(data), fixture.Name) + require.False(t, d.isCurrentFreshCommitBatch(data), fixture.Name) + } + } +} + +func TestRollupSelectorFreeze(t *testing.T) { + beforeMoveABI, err := types.BeforeMoveBlockCtxABI.GetAbi() + require.NoError(t, err) + legacyABI, err := types.LegacyRollupMetaData.GetAbi() + require.NoError(t, err) + require.Equal(t, "0x4a8d544f", hexutil.Encode(beforeMoveABI.Methods["commitBatch"].ID), "before-move selector") + require.Equal(t, "0xd63b3549", hexutil.Encode(legacyABI.Methods["commitBatch"].ID), "legacy selector") + + preSubmitterABI, err := types.PreSubmitterRollupMetaData.GetAbi() + require.NoError(t, err) + currentABI, err := abi.JSON(strings.NewReader(targetCurrentRollupABI)) + require.NoError(t, err) + + wantOld := map[string]string{ + "commitBatch": "0x428868b5", + "commitState": "0x1e8825be", + "commitBatchWithProof": "0x4e8f1d67", + } + wantNew := map[string]string{ + "commitBatch": "0x41f756da", + "commitState": "0x67caa37a", + "commitBatchWithProof": "0x1544ba3a", + } + for name := range wantOld { + require.Equal(t, wantOld[name], hexutil.Encode(preSubmitterABI.Methods[name].ID)) + require.Equal(t, wantNew[name], hexutil.Encode(currentABI.Methods[name].ID)) + require.NotEqual(t, preSubmitterABI.Methods[name].ID, currentABI.Methods[name].ID) + } +} + +func TestGeneratedRollupBindingUsesCurrentSelectors(t *testing.T) { + generatedABI, err := bindings.RollupMetaData.GetAbi() + require.NoError(t, err) + want := map[string]string{ + "commitBatch": "0x41f756da", + "commitState": "0x67caa37a", + "commitBatchWithProof": "0x1544ba3a", + } + for name, selector := range want { + require.Equal(t, selector, hexutil.Encode(generatedABI.Methods[name].ID), name) + } +} diff --git a/node/derivation/canonical_commit.go b/node/derivation/canonical_commit.go new file mode 100644 index 000000000..9d3b6b454 --- /dev/null +++ b/node/derivation/canonical_commit.go @@ -0,0 +1,376 @@ +package derivation + +import ( + "context" + "errors" + "fmt" + "math/big" + "slices" + "strings" + + "github.com/morph-l2/go-ethereum" + "github.com/morph-l2/go-ethereum/accounts/abi" + "github.com/morph-l2/go-ethereum/common" + eth "github.com/morph-l2/go-ethereum/core/types" + "github.com/morph-l2/go-ethereum/crypto" + geth "github.com/morph-l2/go-ethereum/eth" +) + +var ( + ErrSnapshotChanged = errors.New("fixed L1 snapshot changed") + ErrCanonicalCommitNotFound = errors.New("canonical CommitBatch log not found") + ErrStoredBlobSourceNotFound = errors.New("stored blob source not found") + ErrZeroBlobVersionedHash = errors.New("pre-cutover zero blob versioned hash") + zeroBlobVersionedHash = common.HexToHash("0x010657f37554c781402a22917dee2f75def7ab966d7b770905398eba3c444014") + canonicalResolverRollupABI = mustCanonicalResolverRollupABI() + canonicalCommitEventTopic = canonicalResolverRollupABI.Events["CommitBatch"].ID + canonicalRevertEventTopic = canonicalResolverRollupABI.Events["RevertBatch"].ID +) + +const canonicalResolverRollupABIJSON = `[ + {"type":"function","name":"committedBatches","stateMutability":"view","inputs":[{"name":"batchIndex","type":"uint256"}],"outputs":[{"name":"batchHash","type":"bytes32"}]}, + {"type":"function","name":"batchBlobVersionedHashes","stateMutability":"view","inputs":[{"name":"batchIndex","type":"uint256"}],"outputs":[{"name":"blobVersionedHash","type":"bytes32"}]}, + {"type":"event","name":"CommitBatch","anonymous":false,"inputs":[{"name":"batchIndex","type":"uint256","indexed":true},{"name":"batchHash","type":"bytes32","indexed":true}]}, + {"type":"event","name":"RevertBatch","anonymous":false,"inputs":[{"name":"batchIndex","type":"uint256","indexed":true},{"name":"batchHash","type":"bytes32","indexed":true}]} +]` + +func mustCanonicalResolverRollupABI() abi.ABI { + parsed, err := abi.JSON(strings.NewReader(canonicalResolverRollupABIJSON)) + if err != nil { + panic(err) + } + return parsed +} + +// FixedL1Snapshot pins both the number used by legacy JSON-RPC block-number +// calls and its hash. Every resolver verifies the hash before and after its +// reads, providing EIP-1898-equivalent fail-closed behavior on providers that +// do not support blockHash call options. +type FixedL1Snapshot struct { + Number uint64 + Hash common.Hash +} + +type CanonicalCommitClient interface { + HeaderByNumber(context.Context, *big.Int) (*eth.Header, error) + CallContract(context.Context, ethereum.CallMsg, *big.Int) ([]byte, error) + FilterLogs(context.Context, ethereum.FilterQuery) ([]eth.Log, error) +} + +type StoredBlobSourceClient interface { + CanonicalCommitClient + TransactionByHash(context.Context, common.Hash) (*eth.Transaction, bool, error) +} + +type CanonicalCommit struct { + BatchIndex uint64 + BatchHash common.Hash + Log eth.Log +} + +type StoredBlobSource struct { + BatchIndex uint64 + StoredHash common.Hash + CommitLog eth.Log + Tx *eth.Transaction + BlobHashes []common.Hash +} + +type RollupBatchDecoder func([]byte) (geth.RPCRollupBatch, error) + +func PinL1Snapshot(ctx context.Context, client CanonicalCommitClient, number uint64) (FixedL1Snapshot, error) { + header, err := client.HeaderByNumber(ctx, uint64Big(number)) + if err != nil { + return FixedL1Snapshot{}, err + } + if header == nil || header.Number == nil || header.Number.Uint64() != number { + return FixedL1Snapshot{}, fmt.Errorf("pin L1 snapshot %d: invalid header", number) + } + return FixedL1Snapshot{Number: number, Hash: header.Hash()}, nil +} + +func ResolveCanonicalCommit( + ctx context.Context, + client CanonicalCommitClient, + rollupAddress common.Address, + batchIndex uint64, + snapshotNumber uint64, +) (*CanonicalCommit, error) { + snapshot, err := PinL1Snapshot(ctx, client, snapshotNumber) + if err != nil { + return nil, err + } + return ResolveCanonicalCommitAtSnapshot(ctx, client, rollupAddress, batchIndex, snapshot) +} + +func ResolveCanonicalCommitAtSnapshot( + ctx context.Context, + client CanonicalCommitClient, + rollupAddress common.Address, + batchIndex uint64, + snapshot FixedL1Snapshot, +) (*CanonicalCommit, error) { + if err := verifyL1Snapshot(ctx, client, snapshot); err != nil { + return nil, err + } + committedHash, err := readRollupHashAtSnapshot(ctx, client, rollupAddress, "committedBatches", batchIndex, snapshot.Number) + if err != nil { + return nil, err + } + if committedHash == (common.Hash{}) { + if err := verifyL1Snapshot(ctx, client, snapshot); err != nil { + return nil, err + } + return nil, nil + } + + logs, err := client.FilterLogs(ctx, ethereum.FilterQuery{ + FromBlock: uint64Big(0), + ToBlock: uint64Big(snapshot.Number), + Addresses: []common.Address{rollupAddress}, + Topics: [][]common.Hash{ + {canonicalCommitEventTopic, canonicalRevertEventTopic}, + {common.BigToHash(uint64Big(batchIndex))}, + }, + }) + if err != nil { + return nil, err + } + ordered, err := validateAndSortLifecycleLogs(logs, rollupAddress, batchIndex, snapshot.Number) + if err != nil { + return nil, err + } + + lastRevert := -1 + for i := range ordered { + if ordered[i].Topics[0] == canonicalRevertEventTopic { + lastRevert = i + } + } + var selected *eth.Log + for i := lastRevert + 1; i < len(ordered); i++ { + log := ordered[i] + if log.Topics[0] == canonicalCommitEventTopic && log.Topics[2] == committedHash { + candidate := log + selected = &candidate + } + } + if err := verifyL1Snapshot(ctx, client, snapshot); err != nil { + return nil, err + } + if selected == nil { + return nil, fmt.Errorf("%w: batch %d, getter hash %s", ErrCanonicalCommitNotFound, batchIndex, committedHash.Hex()) + } + return &CanonicalCommit{ + BatchIndex: batchIndex, + BatchHash: committedHash, + Log: *selected, + }, nil +} + +// ResolveStoredBlobSource deliberately does not apply the canonical resolver's +// "after the last Revert" filter. Rollup retains batchBlobVersionedHashes so a +// later blobless commitState/commitBatchWithProof can reuse a blob originally +// published by a pre-Revert Commit. The source remains identified by its full +// canonical L1 log and transaction identity. +func ResolveStoredBlobSource( + ctx context.Context, + client StoredBlobSourceClient, + rollupAddress common.Address, + batchIndex uint64, + snapshotNumber uint64, + decode RollupBatchDecoder, +) (*StoredBlobSource, error) { + snapshot, err := PinL1Snapshot(ctx, client, snapshotNumber) + if err != nil { + return nil, err + } + return ResolveStoredBlobSourceAtSnapshot(ctx, client, rollupAddress, batchIndex, snapshot, decode) +} + +func ResolveStoredBlobSourceAtSnapshot( + ctx context.Context, + client StoredBlobSourceClient, + rollupAddress common.Address, + batchIndex uint64, + snapshot FixedL1Snapshot, + decode RollupBatchDecoder, +) (*StoredBlobSource, error) { + if decode == nil { + return nil, errors.New("rollup batch decoder is nil") + } + if err := verifyL1Snapshot(ctx, client, snapshot); err != nil { + return nil, err + } + storedHash, err := readRollupHashAtSnapshot(ctx, client, rollupAddress, "batchBlobVersionedHashes", batchIndex, snapshot.Number) + if err != nil { + return nil, err + } + if storedHash == (common.Hash{}) || storedHash == zeroBlobVersionedHash { + if err := verifyL1Snapshot(ctx, client, snapshot); err != nil { + return nil, err + } + if storedHash == zeroBlobVersionedHash { + return nil, ErrZeroBlobVersionedHash + } + return nil, fmt.Errorf("%w: batch %d has no stored hash", ErrStoredBlobSourceNotFound, batchIndex) + } + + logs, err := client.FilterLogs(ctx, ethereum.FilterQuery{ + FromBlock: uint64Big(0), + ToBlock: uint64Big(snapshot.Number), + Addresses: []common.Address{rollupAddress}, + Topics: [][]common.Hash{ + {canonicalCommitEventTopic}, + {common.BigToHash(uint64Big(batchIndex))}, + }, + }) + if err != nil { + return nil, err + } + ordered, err := validateAndSortLifecycleLogs(logs, rollupAddress, batchIndex, snapshot.Number) + if err != nil { + return nil, err + } + + var source *StoredBlobSource + for i := len(ordered) - 1; i >= 0; i-- { + log := ordered[i] + if log.Topics[0] != canonicalCommitEventTopic { + continue + } + tx, pending, err := client.TransactionByHash(ctx, log.TxHash) + if err != nil { + return nil, err + } + if pending || tx == nil { + return nil, fmt.Errorf("stored blob source transaction %s is pending or missing", log.TxHash.Hex()) + } + if tx.Hash() != log.TxHash { + return nil, fmt.Errorf("stored blob source transaction hash mismatch: requested %s, got %s", log.TxHash.Hex(), tx.Hash().Hex()) + } + blobHashes := tx.BlobHashes() + if len(blobHashes) == 0 { + continue + } + batch, err := decode(tx.Data()) + if err != nil { + return nil, fmt.Errorf("decode stored blob source transaction %s: %w", log.TxHash.Hex(), err) + } + if !storedBlobHashMatches(uint(batch.Version), blobHashes, storedHash) { + continue + } + source = &StoredBlobSource{ + BatchIndex: batchIndex, + StoredHash: storedHash, + CommitLog: log, + Tx: tx, + BlobHashes: slices.Clone(blobHashes), + } + break + } + if err := verifyL1Snapshot(ctx, client, snapshot); err != nil { + return nil, err + } + if source == nil { + return nil, fmt.Errorf("%w: batch %d, stored hash %s", ErrStoredBlobSourceNotFound, batchIndex, storedHash.Hex()) + } + return source, nil +} + +func storedBlobHashMatches(version uint, blobHashes []common.Hash, storedHash common.Hash) bool { + if len(blobHashes) == 0 { + return false + } + if version != 2 { + return len(blobHashes) == 1 && blobHashes[0] == storedHash + } + encoded := make([]byte, 0, len(blobHashes)*common.HashLength) + for _, hash := range blobHashes { + encoded = append(encoded, hash[:]...) + } + return crypto.Keccak256Hash(encoded) == storedHash +} + +func readRollupHashAtSnapshot( + ctx context.Context, + client CanonicalCommitClient, + rollupAddress common.Address, + method string, + batchIndex uint64, + snapshotNumber uint64, +) (common.Hash, error) { + input, err := canonicalResolverRollupABI.Pack(method, uint64Big(batchIndex)) + if err != nil { + return common.Hash{}, err + } + output, err := client.CallContract(ctx, ethereum.CallMsg{To: &rollupAddress, Data: input}, uint64Big(snapshotNumber)) + if err != nil { + return common.Hash{}, err + } + if len(output) != common.HashLength { + return common.Hash{}, fmt.Errorf("%s returned %d bytes, want %d", method, len(output), common.HashLength) + } + values, err := canonicalResolverRollupABI.Methods[method].Outputs.Unpack(output) + if err != nil { + return common.Hash{}, err + } + if len(values) != 1 { + return common.Hash{}, fmt.Errorf("%s returned %d values, want 1", method, len(values)) + } + decoded := *abi.ConvertType(values[0], new([32]byte)).(*[32]byte) + return common.Hash(decoded), nil +} + +func validateAndSortLifecycleLogs(logs []eth.Log, rollupAddress common.Address, batchIndex, snapshotNumber uint64) ([]eth.Log, error) { + indexTopic := common.BigToHash(uint64Big(batchIndex)) + ordered := slices.Clone(logs) + for i := range ordered { + log := ordered[i] + if log.Removed { + return nil, fmt.Errorf("removed lifecycle log at %d/%d/%d", log.BlockNumber, log.TxIndex, log.Index) + } + if log.Address != rollupAddress || log.BlockNumber > snapshotNumber || len(log.Topics) != 3 || log.Topics[1] != indexTopic { + return nil, fmt.Errorf("malformed lifecycle log at %d/%d/%d", log.BlockNumber, log.TxIndex, log.Index) + } + if log.Topics[0] != canonicalCommitEventTopic && log.Topics[0] != canonicalRevertEventTopic { + return nil, fmt.Errorf("unexpected lifecycle topic %s", log.Topics[0].Hex()) + } + } + slices.SortFunc(ordered, func(a, b eth.Log) int { + if a.BlockNumber != b.BlockNumber { + return compareUint64(a.BlockNumber, b.BlockNumber) + } + if a.TxIndex != b.TxIndex { + return compareUint64(uint64(a.TxIndex), uint64(b.TxIndex)) + } + return compareUint64(uint64(a.Index), uint64(b.Index)) + }) + return ordered, nil +} + +func verifyL1Snapshot(ctx context.Context, client CanonicalCommitClient, snapshot FixedL1Snapshot) error { + header, err := client.HeaderByNumber(ctx, uint64Big(snapshot.Number)) + if err != nil { + return err + } + if header == nil || header.Number == nil || header.Number.Uint64() != snapshot.Number || header.Hash() != snapshot.Hash { + return fmt.Errorf("%w: block %d expected %s", ErrSnapshotChanged, snapshot.Number, snapshot.Hash.Hex()) + } + return nil +} + +func uint64Big(value uint64) *big.Int { + return new(big.Int).SetUint64(value) +} + +func compareUint64(a, b uint64) int { + switch { + case a < b: + return -1 + case a > b: + return 1 + default: + return 0 + } +} diff --git a/node/derivation/canonical_commit_test.go b/node/derivation/canonical_commit_test.go new file mode 100644 index 000000000..d62565481 --- /dev/null +++ b/node/derivation/canonical_commit_test.go @@ -0,0 +1,225 @@ +package derivation + +import ( + "context" + "math/big" + "testing" + + "github.com/holiman/uint256" + "github.com/morph-l2/go-ethereum" + "github.com/morph-l2/go-ethereum/common" + "github.com/morph-l2/go-ethereum/common/hexutil" + eth "github.com/morph-l2/go-ethereum/core/types" + "github.com/morph-l2/go-ethereum/crypto" + "github.com/stretchr/testify/require" +) + +type resolverFakeClient struct { + header *eth.Header + changedHeader *eth.Header + changeAfterCall int + headerCalls int + callHash common.Hash + logs []eth.Log + filterCalls int + lastQuery ethereum.FilterQuery + transactions map[common.Hash]*eth.Transaction +} + +func (f *resolverFakeClient) HeaderByNumber(context.Context, *big.Int) (*eth.Header, error) { + f.headerCalls++ + if f.changedHeader != nil && f.changeAfterCall > 0 && f.headerCalls >= f.changeAfterCall { + return f.changedHeader, nil + } + return f.header, nil +} + +func (f *resolverFakeClient) CallContract(context.Context, ethereum.CallMsg, *big.Int) ([]byte, error) { + return canonicalResolverRollupABI.Methods["committedBatches"].Outputs.Pack(f.callHash) +} + +func (f *resolverFakeClient) FilterLogs(_ context.Context, query ethereum.FilterQuery) ([]eth.Log, error) { + f.filterCalls++ + f.lastQuery = query + filtered := make([]eth.Log, 0, len(f.logs)) + for _, log := range f.logs { + if len(query.Addresses) > 0 && log.Address != query.Addresses[0] { + continue + } + matched := true + for position, alternatives := range query.Topics { + if len(alternatives) == 0 { + continue + } + if len(log.Topics) <= position { + matched = false + break + } + positionMatched := false + for _, topic := range alternatives { + if log.Topics[position] == topic { + positionMatched = true + break + } + } + if !positionMatched { + matched = false + break + } + } + if matched { + filtered = append(filtered, log) + } + } + return filtered, nil +} + +func (f *resolverFakeClient) TransactionByHash(_ context.Context, hash common.Hash) (*eth.Transaction, bool, error) { + return f.transactions[hash], false, nil +} + +func resolverTestHeader(number uint64, marker byte) *eth.Header { + return ð.Header{Number: new(big.Int).SetUint64(number), Time: number, Extra: []byte{marker}} +} + +func resolverTestLog(address common.Address, event, hash common.Hash, index, block uint64, txIndex, logIndex uint) eth.Log { + return eth.Log{ + Address: address, + Topics: []common.Hash{event, common.BigToHash(uint64Big(index)), hash}, + BlockNumber: block, + TxIndex: txIndex, + Index: logIndex, + } +} + +func TestResolveCanonicalCommitAfterLastRevertWithSameHashRecommit(t *testing.T) { + const snapshotNumber = uint64(100) + const batchIndex = uint64(7) + rollupAddress := common.HexToAddress("0x1234") + batchHash := common.HexToHash("0xaaaa") + wrongHash := common.HexToHash("0xbbbb") + wantTx := common.HexToHash("0x3333") + + oldCommit := resolverTestLog(rollupAddress, canonicalCommitEventTopic, batchHash, batchIndex, 10, 0, 0) + revert := resolverTestLog(rollupAddress, canonicalRevertEventTopic, batchHash, batchIndex, 11, 0, 0) + wrongCommit := resolverTestLog(rollupAddress, canonicalCommitEventTopic, wrongHash, batchIndex, 12, 0, 0) + recommit := resolverTestLog(rollupAddress, canonicalCommitEventTopic, batchHash, batchIndex, 12, 1, 3) + recommit.TxHash = wantTx + client := &resolverFakeClient{ + header: resolverTestHeader(snapshotNumber, 1), + callHash: batchHash, + logs: []eth.Log{recommit, oldCommit, wrongCommit, revert}, // deliberately unsorted + } + + got, err := ResolveCanonicalCommit(context.Background(), client, rollupAddress, batchIndex, snapshotNumber) + require.NoError(t, err) + require.NotNil(t, got) + require.Equal(t, batchHash, got.BatchHash) + require.Equal(t, wantTx, got.Log.TxHash, "same-hash recommit must be identified by its post-Revert log") + require.Equal(t, snapshotNumber, client.lastQuery.ToBlock.Uint64()) + require.Equal(t, 3, client.headerCalls, "snapshot must be pinned and verified before/after reads") +} + +func TestResolveCanonicalCommitReturnsNilForZeroGetter(t *testing.T) { + client := &resolverFakeClient{header: resolverTestHeader(100, 1)} + got, err := ResolveCanonicalCommit(context.Background(), client, common.HexToAddress("0x1234"), 1, 100) + require.NoError(t, err) + require.Nil(t, got) + require.Zero(t, client.filterCalls, "zero committedBatches must not scan logs") +} + +func TestResolveCanonicalCommitRejectsChangedSnapshot(t *testing.T) { + client := &resolverFakeClient{ + header: resolverTestHeader(100, 1), + changedHeader: resolverTestHeader(100, 2), + changeAfterCall: 3, + callHash: common.HexToHash("0xaaaa"), + logs: []eth.Log{ + resolverTestLog(common.HexToAddress("0x1234"), canonicalCommitEventTopic, common.HexToHash("0xaaaa"), 1, 10, 0, 0), + }, + } + _, err := ResolveCanonicalCommit(context.Background(), client, common.HexToAddress("0x1234"), 1, 100) + require.ErrorIs(t, err, ErrSnapshotChanged) +} + +func TestResolveStoredBlobSourceSearchesBeforeRevert(t *testing.T) { + const snapshotNumber = uint64(100) + const batchIndex = uint64(7) + rollupAddress := common.HexToAddress("0x1234") + corpus := loadCalldataFixtureCorpus(t) + fixtureData := make(map[string][]byte, len(corpus.Fixtures)) + for _, fixture := range corpus.Fixtures { + data, err := hexutil.Decode(fixture.Data) + require.NoError(t, err) + fixtureData[fixture.Name] = data + } + + blobHashes := []common.Hash{common.HexToHash("0x1111"), common.HexToHash("0x2222")} + storedHashBytes := make([]byte, 0, len(blobHashes)*common.HashLength) + for _, hash := range blobHashes { + storedHashBytes = append(storedHashBytes, hash[:]...) + } + storedHash := crypto.Keccak256Hash(storedHashBytes) + originalTx := newResolverBlobTx(1, fixtureData["pre_submitter_commitBatch"], blobHashes) + bloblessTx := eth.NewTx(ð.LegacyTx{Nonce: 2, To: &rollupAddress, Data: fixtureData["current_commitState"]}) + + originalCommit := resolverTestLog(rollupAddress, canonicalCommitEventTopic, common.HexToHash("0xaaaa"), batchIndex, 10, 0, 0) + originalCommit.TxHash = originalTx.Hash() + revert := resolverTestLog(rollupAddress, canonicalRevertEventTopic, common.HexToHash("0xaaaa"), batchIndex, 11, 0, 0) + bloblessRecommit := resolverTestLog(rollupAddress, canonicalCommitEventTopic, common.HexToHash("0xbbbb"), batchIndex, 12, 0, 0) + bloblessRecommit.TxHash = bloblessTx.Hash() + client := &resolverFakeClient{ + header: resolverTestHeader(snapshotNumber, 1), + callHash: storedHash, + logs: []eth.Log{bloblessRecommit, revert, originalCommit}, + transactions: map[common.Hash]*eth.Transaction{ + originalTx.Hash(): originalTx, + bloblessTx.Hash(): bloblessTx, + }, + } + d := newFixtureDerivation(t) + + source, err := ResolveStoredBlobSource(context.Background(), client, rollupAddress, batchIndex, snapshotNumber, d.UnPackData) + require.NoError(t, err) + require.Equal(t, originalTx.Hash(), source.CommitLog.TxHash) + require.Equal(t, blobHashes, source.BlobHashes) + require.Equal(t, []common.Hash{canonicalCommitEventTopic}, client.lastQuery.Topics[0], + "stored-source lookup must scan every historical Commit and must not reuse the post-Revert canonical filter") +} + +func TestResolveStoredBlobSourcePreservesZeroHashSpecialCase(t *testing.T) { + client := &resolverFakeClient{ + header: resolverTestHeader(100, 1), + callHash: zeroBlobVersionedHash, + } + _, err := ResolveStoredBlobSource(context.Background(), client, common.HexToAddress("0x1234"), 1, 100, newFixtureDerivation(t).UnPackData) + require.ErrorIs(t, err, ErrZeroBlobVersionedHash) + require.Zero(t, client.filterCalls) +} + +func TestStoredBlobHashMatchesLegacyAndV2(t *testing.T) { + hashes := []common.Hash{common.HexToHash("0x1111"), common.HexToHash("0x2222")} + require.True(t, storedBlobHashMatches(1, hashes[:1], hashes[0])) + require.False(t, storedBlobHashMatches(1, hashes[:1], hashes[1])) + encoded := append(append([]byte{}, hashes[0][:]...), hashes[1][:]...) + require.True(t, storedBlobHashMatches(2, hashes, crypto.Keccak256Hash(encoded))) + require.False(t, storedBlobHashMatches(2, hashes, hashes[0])) +} + +func newResolverBlobTx(nonce uint64, data []byte, blobHashes []common.Hash) *eth.Transaction { + return eth.NewTx(ð.BlobTx{ + ChainID: uint256.NewInt(1), + Nonce: nonce, + GasTipCap: uint256.NewInt(1), + GasFeeCap: uint256.NewInt(2), + Gas: 100_000, + To: common.HexToAddress("0x1234"), + Value: uint256.NewInt(0), + Data: data, + BlobFeeCap: uint256.NewInt(1), + BlobHashes: blobHashes, + V: uint256.NewInt(0), + R: uint256.NewInt(0), + S: uint256.NewInt(0), + }) +} diff --git a/node/derivation/derivation.go b/node/derivation/derivation.go index b050f3483..c2dab2f08 100644 --- a/node/derivation/derivation.go +++ b/node/derivation/derivation.go @@ -24,6 +24,7 @@ import ( "morph-l2/bindings/bindings" "morph-l2/bindings/predeploys" + commonbatch "morph-l2/common/batch" nodecommon "morph-l2/node/common" "morph-l2/node/sync" "morph-l2/node/types" @@ -49,6 +50,7 @@ type Derivation struct { L2ToL1MessagePasser *bindings.L2ToL1MessagePasser rollupABI *abi.ABI + preSubmitterRollupABI *abi.ABI legacyRollupABI *abi.ABI // before remove skipMap beforeMoveBlockCtxABI *abi.ABI @@ -112,6 +114,10 @@ func NewDerivationClient(ctx context.Context, cfg *Config, syncer *sync.Syncer, if err != nil { return nil, err } + preSubmitterRollupAbi, err := types.PreSubmitterRollupMetaData.GetAbi() + if err != nil { + return nil, err + } beforeMoveBlockCtxAbi, err := types.BeforeMoveBlockCtxABI.GetAbi() if err != nil { return nil, err @@ -135,6 +141,7 @@ func NewDerivationClient(ctx context.Context, cfg *Config, syncer *sync.Syncer, syncer: syncer, rollup: rollup, rollupABI: rollupAbi, + preSubmitterRollupABI: preSubmitterRollupAbi, legacyRollupABI: legacyRollupAbi, beforeMoveBlockCtxABI: beforeMoveBlockCtxAbi, logger: logger, @@ -309,6 +316,10 @@ func (d *Derivation) derivationBlock(ctx context.Context) { d.logger.Error("fetch batch info outline failed", "err", err) return } + if err := d.hydrateStoredBlobHashes(ctx, batchInfo, lg.TxHash, lg.BlockNumber); err != nil { + d.logger.Error("resolve stored blob source failed", "batchIndex", batchInfo.batchIndex, "err", err) + return + } d.logger.Info("local verify fetched batch metadata", "batchIndex", batchInfo.batchIndex, "version", batchInfo.version, @@ -552,30 +563,63 @@ func (d *Derivation) fetchRollupDataByTxHash(txHash common.Hash, blockNumber uin return nil, err } - // Get block header to retrieve timestamp - header, err := d.l1Client.HeaderByNumber(d.ctx, big.NewInt(int64(blockNumber))) - if err != nil { - return nil, err - } - // Get transaction blob hashes blobHashes := tx.BlobHashes() + blobSourceTx := tx + blobSourceBlockNumber := blockNumber + if len(blobHashes) == 0 && d.isCurrentFreshCommitBatch(tx.Data()) { + return nil, fmt.Errorf("current commitBatch %s has no fresh blob", txHash.Hex()) + } + if len(blobHashes) == 0 && d.isStoredSourceSubmission(tx.Data()) { + parentBatchIndex, indexErr := commonbatch.BatchHeaderBytes(batch.ParentBatchHeader).BatchIndex() + if indexErr != nil { + return nil, fmt.Errorf("resolve stored blob source batch index: %w", indexErr) + } + source, sourceErr := ResolveStoredBlobSource( + d.ctx, + d.l1Client, + d.RollupContractAddress, + parentBatchIndex+1, + blockNumber, + d.UnPackData, + ) + if sourceErr != nil && !errors.Is(sourceErr, ErrZeroBlobVersionedHash) { + return nil, sourceErr + } + if source != nil { + blobSourceTx = source.Tx + blobSourceBlockNumber = source.CommitLog.BlockNumber + blobHashes = source.BlobHashes + d.logger.Info("recovered stored blob source", + "batchIndex", parentBatchIndex+1, + "commitTxHash", txHash, + "sourceTxHash", source.CommitLog.TxHash, + "sourceBlockNumber", source.CommitLog.BlockNumber) + } + } if len(blobHashes) > 0 { - d.logger.Info("Transaction contains blobs", "txHash", txHash, "blobCount", len(blobHashes)) + d.logger.Info("Transaction contains blobs", "txHash", blobSourceTx.Hash(), "blobCount", len(blobHashes)) + + // Retrieve the timestamp and blob index positions from the transaction + // that actually published the blobs, not from a later blobless commit. + header, err := d.l1Client.HeaderByNumber(d.ctx, big.NewInt(int64(blobSourceBlockNumber))) + if err != nil { + return nil, err + } // Initialize indexedBlobHashes as nil var indexedBlobHashes []IndexedBlobHash // Only try to build IndexedBlobHash array if not forcing get all blobs // Try to get the block to build IndexedBlobHash array - block, err := d.l1Client.BlockByNumber(d.ctx, big.NewInt(int64(blockNumber))) + block, err := d.l1Client.BlockByNumber(d.ctx, big.NewInt(int64(blobSourceBlockNumber))) if err == nil { // Successfully got the block, now build IndexedBlobHash array - d.logger.Info("Building IndexedBlobHash array from block", "blockNumber", blockNumber) - indexedBlobHashes = dataAndHashesFromTxs(block.Transactions(), tx) + d.logger.Info("Building IndexedBlobHash array from block", "blockNumber", blobSourceBlockNumber) + indexedBlobHashes = dataAndHashesFromTxs(block.Transactions(), blobSourceTx) d.logger.Info("Built IndexedBlobHash array", "count", len(indexedBlobHashes)) } else { - d.logger.Info("Failed to get block, will try fetching all blobs", "blockNumber", blockNumber, "error", err) + d.logger.Info("Failed to get block, will try fetching all blobs", "blockNumber", blobSourceBlockNumber, "error", err) } // Get all blobs corresponding to this timestamp, content-verified @@ -599,25 +643,86 @@ func (d *Derivation) fetchRollupDataByTxHash(txHash common.Hash, blockNumber uin rollupData.l1BlockNumber = blockNumber rollupData.txHash = txHash rollupData.nonce = tx.Nonce() - rollupData.blobHashes = tx.BlobHashes() + rollupData.blobHashes = blobHashes return rollupData, nil } +func (d *Derivation) hydrateStoredBlobHashes(ctx context.Context, batchInfo *BatchInfo, txHash common.Hash, blockNumber uint64) error { + if batchInfo == nil || len(batchInfo.blobHashes) > 0 { + return nil + } + tx, pending, err := d.l1Client.TransactionByHash(ctx, txHash) + if err != nil { + return err + } + if pending || tx == nil { + return errors.New("pending or missing rollup transaction") + } + if d.isCurrentFreshCommitBatch(tx.Data()) { + return fmt.Errorf("current commitBatch %s has no fresh blob", txHash.Hex()) + } + if !d.isStoredSourceSubmission(tx.Data()) { + return nil + } + source, err := ResolveStoredBlobSource( + ctx, + d.l1Client, + d.RollupContractAddress, + batchInfo.batchIndex, + blockNumber, + d.UnPackData, + ) + if errors.Is(err, ErrZeroBlobVersionedHash) { + return nil // frozen pre-cutover ZERO_VERSIONED_HASH branch + } + if err != nil { + return err + } + batchInfo.blobHashes = source.BlobHashes + return nil +} + +func (d *Derivation) isStoredSourceSubmission(data []byte) bool { + return matchesRollupMethodSelector(d.preSubmitterRollupABI, data, "commitState", "commitBatchWithProof") || + matchesRollupMethodSelector(d.rollupABI, data, "commitState", "commitBatchWithProof") +} + +func (d *Derivation) isCurrentFreshCommitBatch(data []byte) bool { + if !matchesRollupMethodSelector(d.rollupABI, data, "commitBatch") { + return false + } + // Until bindings are regenerated in a mixed development checkout, the + // current and frozen ABI may temporarily have the same old selector. Never + // reinterpret that frozen historical selector as the fresh-blob-only call. + return !matchesRollupMethodSelector(d.preSubmitterRollupABI, data, "commitBatch") +} + +func matchesRollupMethodSelector(contractABI *abi.ABI, data []byte, methodNames ...string) bool { + if contractABI == nil || len(data) < 4 { + return false + } + for _, name := range methodNames { + if method, ok := contractABI.Methods[name]; ok && bytes.Equal(method.ID, data[:4]) { + return true + } + } + return false +} + func (d *Derivation) UnPackData(data []byte) (geth.RPCRollupBatch, error) { var batch geth.RPCRollupBatch + if len(data) < 4 { + return batch, types.ErrNotCommitBatchTx + } if bytes.Equal(d.beforeMoveBlockCtxABI.Methods["commitBatch"].ID, data[:4]) { args, err := d.beforeMoveBlockCtxABI.Methods["commitBatch"].Inputs.Unpack(data[4:]) if err != nil { return batch, fmt.Errorf("submitBatches Unpack error:%v", err) } - rollupBatchData := args[0].(struct { - Version uint8 "json:\"version\"" - ParentBatchHeader []uint8 "json:\"parentBatchHeader\"" - BlockContexts []uint8 "json:\"blockContexts\"" - PrevStateRoot [32]uint8 "json:\"prevStateRoot\"" - PostStateRoot [32]uint8 "json:\"postStateRoot\"" - WithdrawalRoot [32]uint8 "json:\"withdrawalRoot\"" - }) + if len(args) == 0 { + return batch, errors.New("commitBatch has no batchDataInput") + } + rollupBatchData := *abi.ConvertType(args[0], new(beforeMoveBatchDataInput)).(*beforeMoveBatchDataInput) batch = geth.RPCRollupBatch{ Version: uint(rollupBatchData.Version), ParentBatchHeader: rollupBatchData.ParentBatchHeader, @@ -626,20 +731,16 @@ func (d *Derivation) UnPackData(data []byte) (geth.RPCRollupBatch, error) { PostStateRoot: common.BytesToHash(rollupBatchData.PostStateRoot[:]), WithdrawRoot: common.BytesToHash(rollupBatchData.WithdrawalRoot[:]), } + return batch, nil } else if bytes.Equal(d.legacyRollupABI.Methods["commitBatch"].ID, data[:4]) { args, err := d.legacyRollupABI.Methods["commitBatch"].Inputs.Unpack(data[4:]) if err != nil { return batch, fmt.Errorf("submitBatches Unpack error:%v", err) } - rollupBatchData := args[0].(struct { - Version uint8 "json:\"version\"" - ParentBatchHeader []uint8 "json:\"parentBatchHeader\"" - BlockContexts []uint8 "json:\"blockContexts\"" - SkippedL1MessageBitmap []uint8 "json:\"skippedL1MessageBitmap\"" - PrevStateRoot [32]uint8 "json:\"prevStateRoot\"" - PostStateRoot [32]uint8 "json:\"postStateRoot\"" - WithdrawalRoot [32]uint8 "json:\"withdrawalRoot\"" - }) + if len(args) == 0 { + return batch, errors.New("commitBatch has no batchDataInput") + } + rollupBatchData := *abi.ConvertType(args[0], new(legacyBatchDataInput)).(*legacyBatchDataInput) batch = geth.RPCRollupBatch{ Version: uint(rollupBatchData.Version), ParentBatchHeader: rollupBatchData.ParentBatchHeader, @@ -648,44 +749,66 @@ func (d *Derivation) UnPackData(data []byte) (geth.RPCRollupBatch, error) { PostStateRoot: common.BytesToHash(rollupBatchData.PostStateRoot[:]), WithdrawRoot: common.BytesToHash(rollupBatchData.WithdrawalRoot[:]), } - } else if bytes.Equal(d.rollupABI.Methods["commitBatch"].ID, data[:4]) { - args, err := d.rollupABI.Methods["commitBatch"].Inputs.Unpack(data[4:]) + return batch, nil + } else if decoded, matched, err := unpackModernRollupCall(d.preSubmitterRollupABI, data); matched { + return decoded, err + } else if decoded, matched, err := unpackModernRollupCall(d.rollupABI, data); matched { + return decoded, err + } + return batch, types.ErrNotCommitBatchTx +} + +type beforeMoveBatchDataInput struct { + Version uint8 + ParentBatchHeader []byte + BlockContexts []byte + PrevStateRoot [32]byte + PostStateRoot [32]byte + WithdrawalRoot [32]byte +} + +type legacyBatchDataInput struct { + Version uint8 + ParentBatchHeader []byte + BlockContexts []byte + SkippedL1MessageBitmap []byte + PrevStateRoot [32]byte + PostStateRoot [32]byte + WithdrawalRoot [32]byte +} + +type modernBatchDataInput struct { + Version uint8 + ParentBatchHeader []byte + LastBlockNumber uint64 + NumL1Messages uint16 + PrevStateRoot [32]byte + PostStateRoot [32]byte + WithdrawalRoot [32]byte +} + +// unpackModernRollupCall recognizes all three modern submission entrypoints. +// Pre-submitter and current ABIs are passed separately so their selectors stay +// independently frozen; only args[0] (BatchDataInput) is consumed. +func unpackModernRollupCall(contractABI *abi.ABI, data []byte) (geth.RPCRollupBatch, bool, error) { + var batch geth.RPCRollupBatch + if contractABI == nil || len(data) < 4 { + return batch, false, nil + } + for _, methodName := range []string{"commitBatch", "commitState", "commitBatchWithProof"} { + method, ok := contractABI.Methods[methodName] + if !ok || !bytes.Equal(method.ID, data[:4]) { + continue + } + args, err := method.Inputs.Unpack(data[4:]) if err != nil { - return batch, fmt.Errorf("submitBatches Unpack error:%v", err) + return batch, true, fmt.Errorf("%s Unpack error:%v", methodName, err) } - rollupBatchData := args[0].(struct { - Version uint8 "json:\"version\"" - ParentBatchHeader []uint8 "json:\"parentBatchHeader\"" - LastBlockNumber uint64 "json:\"lastBlockNumber\"" - NumL1Messages uint16 "json:\"numL1Messages\"" - PrevStateRoot [32]uint8 "json:\"prevStateRoot\"" - PostStateRoot [32]uint8 "json:\"postStateRoot\"" - WithdrawalRoot [32]uint8 "json:\"withdrawalRoot\"" - }) - batch = geth.RPCRollupBatch{ - Version: uint(rollupBatchData.Version), - ParentBatchHeader: rollupBatchData.ParentBatchHeader, - LastBlockNumber: rollupBatchData.LastBlockNumber, - NumL1Messages: rollupBatchData.NumL1Messages, - PrevStateRoot: common.BytesToHash(rollupBatchData.PrevStateRoot[:]), - PostStateRoot: common.BytesToHash(rollupBatchData.PostStateRoot[:]), - WithdrawRoot: common.BytesToHash(rollupBatchData.WithdrawalRoot[:]), + if len(args) == 0 { + return batch, true, fmt.Errorf("%s has no batchDataInput", methodName) } - } else if bytes.Equal(d.rollupABI.Methods["commitBatchWithProof"].ID, data[:4]) { - args, err := d.rollupABI.Methods["commitBatchWithProof"].Inputs.Unpack(data[4:]) - if err != nil { - return batch, fmt.Errorf("commitBatchWithProof Unpack error:%v", err) - } - rollupBatchData := args[0].(struct { - Version uint8 "json:\"version\"" - ParentBatchHeader []uint8 "json:\"parentBatchHeader\"" - LastBlockNumber uint64 "json:\"lastBlockNumber\"" - NumL1Messages uint16 "json:\"numL1Messages\"" - PrevStateRoot [32]uint8 "json:\"prevStateRoot\"" - PostStateRoot [32]uint8 "json:\"postStateRoot\"" - WithdrawalRoot [32]uint8 "json:\"withdrawalRoot\"" - }) - batch = geth.RPCRollupBatch{ + rollupBatchData := *abi.ConvertType(args[0], new(modernBatchDataInput)).(*modernBatchDataInput) + return geth.RPCRollupBatch{ Version: uint(rollupBatchData.Version), ParentBatchHeader: rollupBatchData.ParentBatchHeader, LastBlockNumber: rollupBatchData.LastBlockNumber, @@ -693,11 +816,9 @@ func (d *Derivation) UnPackData(data []byte) (geth.RPCRollupBatch, error) { PrevStateRoot: common.BytesToHash(rollupBatchData.PrevStateRoot[:]), PostStateRoot: common.BytesToHash(rollupBatchData.PostStateRoot[:]), WithdrawRoot: common.BytesToHash(rollupBatchData.WithdrawalRoot[:]), - } - } else { - return batch, types.ErrNotCommitBatchTx + }, true, nil } - return batch, nil + return batch, false, nil } func (d *Derivation) parseBatch(batch geth.RPCRollupBatch) (*BatchInfo, error) { diff --git a/node/derivation/derivation_test.go b/node/derivation/derivation_test.go index 69eb750d6..d70b88d99 100644 --- a/node/derivation/derivation_test.go +++ b/node/derivation/derivation_test.go @@ -21,10 +21,13 @@ func TestUnPackData(t *testing.T) { require.NoError(t, err) legacyRollupAbi, err := types.LegacyRollupMetaData.GetAbi() require.NoError(t, err) + preSubmitterRollupABI, err := types.PreSubmitterRollupMetaData.GetAbi() + require.NoError(t, err) beforeMoveBlockCtxABI, err := types.BeforeMoveBlockCtxABI.GetAbi() require.NoError(t, err) d := Derivation{ rollupABI: rollupAbi, + preSubmitterRollupABI: preSubmitterRollupABI, legacyRollupABI: legacyRollupAbi, beforeMoveBlockCtxABI: beforeMoveBlockCtxABI, } diff --git a/node/derivation/testdata/rollup_calldata_fixtures.json b/node/derivation/testdata/rollup_calldata_fixtures.json new file mode 100644 index 000000000..b728a2023 --- /dev/null +++ b/node/derivation/testdata/rollup_calldata_fixtures.json @@ -0,0 +1,123 @@ +{ + "schemaVersion": 1, + "fixtures": [ + { + "name": "before_move_commitBatch", + "epoch": "before-move", + "selector": "0x4a8d544f", + "data": "0x4a8d544f00000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000180000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c00000000000000000000000000000000000000000000000000000000000000100111111111111111111111111111111111111111111111111111111111111111122222222222222222222222222222222222222222222222222222222222222223333333333333333333333333333333333333333333333333333333333333333000000000000000000000000000000000000000000000000000000000000000301020300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002aabb0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000009000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000002aabb0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002ccdd000000000000000000000000000000000000000000000000000000000000", + "expected": { + "version": 0, + "parentBatchHeader": "0x010203", + "blockContexts": "0xaabb", + "prevStateRoot": "0x1111111111111111111111111111111111111111111111111111111111111111", + "postStateRoot": "0x2222222222222222222222222222222222222222222222222222222222222222", + "withdrawRoot": "0x3333333333333333333333333333333333333333333333333333333333333333" + } + }, + { + "name": "legacy_commitBatch", + "epoch": "legacy", + "selector": "0xd63b3549", + "data": "0xd63b3549000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000001e0000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000e000000000000000000000000000000000000000000000000000000000000001200000000000000000000000000000000000000000000000000000000000000160111111111111111111111111111111111111111111111111111111111111111122222222222222222222222222222222222222222222222222222222222222223333333333333333333333333333333333333333333333333333333333333333000000000000000000000000000000000000000000000000000000000000000301020300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002aabb0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001cc000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000009000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000002aabb0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002ccdd000000000000000000000000000000000000000000000000000000000000", + "expected": { + "version": 1, + "parentBatchHeader": "0x010203", + "blockContexts": "0xaabb", + "prevStateRoot": "0x1111111111111111111111111111111111111111111111111111111111111111", + "postStateRoot": "0x2222222222222222222222222222222222222222222222222222222222222222", + "withdrawRoot": "0x3333333333333333333333333333333333333333333333333333333333333333" + } + }, + { + "name": "pre_submitter_commitBatch", + "epoch": "pre-submitter", + "selector": "0x428868b5", + "data": "0x428868b500000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000160000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000e000000000000000000000000000000000000000000000000000000000000030390000000000000000000000000000000000000000000000000000000000000007111111111111111111111111111111111111111111111111111111111111111122222222222222222222222222222222222222222222222222222222222222223333333333333333333333333333333333333333333333333333333333333333000000000000000000000000000000000000000000000000000000000000000301020300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000009000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000002aabb0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002ccdd000000000000000000000000000000000000000000000000000000000000", + "expected": { + "version": 2, + "parentBatchHeader": "0x010203", + "lastBlockNumber": 12345, + "numL1Messages": 7, + "prevStateRoot": "0x1111111111111111111111111111111111111111111111111111111111111111", + "postStateRoot": "0x2222222222222222222222222222222222222222222222222222222222222222", + "withdrawRoot": "0x3333333333333333333333333333333333333333333333333333333333333333" + } + }, + { + "name": "pre_submitter_commitState", + "epoch": "pre-submitter", + "selector": "0x1e8825be", + "data": "0x1e8825be00000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000160000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000e000000000000000000000000000000000000000000000000000000000000030390000000000000000000000000000000000000000000000000000000000000007111111111111111111111111111111111111111111111111111111111111111122222222222222222222222222222222222222222222222222222222222222223333333333333333333333333333333333333333333333333333333333333333000000000000000000000000000000000000000000000000000000000000000301020300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000009000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000002aabb0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002ccdd000000000000000000000000000000000000000000000000000000000000", + "expected": { + "version": 2, + "parentBatchHeader": "0x010203", + "lastBlockNumber": 12345, + "numL1Messages": 7, + "prevStateRoot": "0x1111111111111111111111111111111111111111111111111111111111111111", + "postStateRoot": "0x2222222222222222222222222222222222222222222222222222222222222222", + "withdrawRoot": "0x3333333333333333333333333333333333333333333333333333333333333333" + } + }, + { + "name": "pre_submitter_commitBatchWithProof", + "epoch": "pre-submitter", + "selector": "0x4e8f1d67", + "data": "0x4e8f1d67000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000001a0000000000000000000000000000000000000000000000000000000000000028000000000000000000000000000000000000000000000000000000000000002c0000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000e000000000000000000000000000000000000000000000000000000000000030390000000000000000000000000000000000000000000000000000000000000007111111111111111111111111111111111111111111111111111111111111111122222222222222222222222222222222222222222222222222222222222222223333333333333333333333333333333333333333333333333333333333333333000000000000000000000000000000000000000000000000000000000000000301020300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000009000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000002aabb0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002ccdd0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002123400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000025678000000000000000000000000000000000000000000000000000000000000", + "expected": { + "version": 2, + "parentBatchHeader": "0x010203", + "lastBlockNumber": 12345, + "numL1Messages": 7, + "prevStateRoot": "0x1111111111111111111111111111111111111111111111111111111111111111", + "postStateRoot": "0x2222222222222222222222222222222222222222222222222222222222222222", + "withdrawRoot": "0x3333333333333333333333333333333333333333333333333333333333333333" + } + }, + { + "name": "current_commitBatch", + "epoch": "current", + "selector": "0x41f756da", + "data": "0x41f756da0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000e00000000000000000000000000000000000000000000000000000000000003039000000000000000000000000000000000000000000000000000000000000000711111111111111111111111111111111111111111111111111111111111111112222222222222222222222222222222222222222222222222222222222222222333333333333333333333333333333333333333333333333333333333333333300000000000000000000000000000000000000000000000000000000000000030102030000000000000000000000000000000000000000000000000000000000", + "expected": { + "version": 2, + "parentBatchHeader": "0x010203", + "lastBlockNumber": 12345, + "numL1Messages": 7, + "prevStateRoot": "0x1111111111111111111111111111111111111111111111111111111111111111", + "postStateRoot": "0x2222222222222222222222222222222222222222222222222222222222222222", + "withdrawRoot": "0x3333333333333333333333333333333333333333333333333333333333333333" + } + }, + { + "name": "current_commitState", + "epoch": "current", + "selector": "0x67caa37a", + "data": "0x67caa37a0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000e00000000000000000000000000000000000000000000000000000000000003039000000000000000000000000000000000000000000000000000000000000000711111111111111111111111111111111111111111111111111111111111111112222222222222222222222222222222222222222222222222222222222222222333333333333333333333333333333333333333333333333333333333333333300000000000000000000000000000000000000000000000000000000000000030102030000000000000000000000000000000000000000000000000000000000", + "expected": { + "version": 2, + "parentBatchHeader": "0x010203", + "lastBlockNumber": 12345, + "numL1Messages": 7, + "prevStateRoot": "0x1111111111111111111111111111111111111111111111111111111111111111", + "postStateRoot": "0x2222222222222222222222222222222222222222222222222222222222222222", + "withdrawRoot": "0x3333333333333333333333333333333333333333333333333333333333333333" + } + }, + { + "name": "current_commitBatchWithProof", + "epoch": "current", + "selector": "0x1544ba3a", + "data": "0x1544ba3a0000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000018000000000000000000000000000000000000000000000000000000000000001c0000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000e000000000000000000000000000000000000000000000000000000000000030390000000000000000000000000000000000000000000000000000000000000007111111111111111111111111111111111111111111111111111111111111111122222222222222222222222222222222222222222222222222222222222222223333333333333333333333333333333333333333333333333333333333333333000000000000000000000000000000000000000000000000000000000000000301020300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002123400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000025678000000000000000000000000000000000000000000000000000000000000", + "expected": { + "version": 2, + "parentBatchHeader": "0x010203", + "lastBlockNumber": 12345, + "numL1Messages": 7, + "prevStateRoot": "0x1111111111111111111111111111111111111111111111111111111111111111", + "postStateRoot": "0x2222222222222222222222222222222222222222222222222222222222222222", + "withdrawRoot": "0x3333333333333333333333333333333333333333333333333333333333333333" + } + } + ] +} diff --git a/node/types/batch.go b/node/types/batch.go index 3038dbb74..a7785a19f 100644 --- a/node/types/batch.go +++ b/node/types/batch.go @@ -1,10 +1,20 @@ package types import ( + _ "embed" + "github.com/morph-l2/go-ethereum/accounts/abi/bind" ) +//go:embed abi/pre_submitter_rollup.json +var preSubmitterRollupABI string + var ( + // PreSubmitterRollupMetaData is the immutable production ABI immediately + // before BatchSignatureInput was removed. Historical calldata decoding must + // never be coupled to the regenerated current Rollup binding. + PreSubmitterRollupMetaData = &bind.MetaData{ABI: preSubmitterRollupABI} + LegacyRollupMetaData = &bind.MetaData{ ABI: "[{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"_chainID\",\"type\":\"uint64\"}],\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"},{\"inputs\":[],\"name\":\"ErrZeroAddress\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"ErrorIncorrectBatchLength\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"ErrorNoBlockInBatch\",\"type\":\"error\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"batchIndex\",\"type\":\"uint256\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"winner\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"string\",\"name\":\"res\",\"type\":\"string\"}],\"name\":\"ChallengeRes\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"receiver\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"ChallengeRewardClaim\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint64\",\"name\":\"batchIndex\",\"type\":\"uint64\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"challenger\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"challengeDeposit\",\"type\":\"uint256\"}],\"name\":\"ChallengeState\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"batchIndex\",\"type\":\"uint256\"},{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"batchHash\",\"type\":\"bytes32\"}],\"name\":\"CommitBatch\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"batchIndex\",\"type\":\"uint256\"},{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"batchHash\",\"type\":\"bytes32\"},{\"indexed\":false,\"internalType\":\"bytes32\",\"name\":\"stateRoot\",\"type\":\"bytes32\"},{\"indexed\":false,\"internalType\":\"bytes32\",\"name\":\"withdrawRoot\",\"type\":\"bytes32\"}],\"name\":\"FinalizeBatch\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint8\",\"name\":\"version\",\"type\":\"uint8\"}],\"name\":\"Initialized\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"previousOwner\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"newOwner\",\"type\":\"address\"}],\"name\":\"OwnershipTransferred\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address\",\"name\":\"account\",\"type\":\"address\"}],\"name\":\"Paused\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address\",\"name\":\"receiver\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"ProveRemainingClaimed\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"batchIndex\",\"type\":\"uint256\"},{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"batchHash\",\"type\":\"bytes32\"}],\"name\":\"RevertBatch\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address\",\"name\":\"account\",\"type\":\"address\"}],\"name\":\"Unpaused\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"account\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"bool\",\"name\":\"status\",\"type\":\"bool\"}],\"name\":\"UpdateChallenger\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"oldPeriod\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"newPeriod\",\"type\":\"uint256\"}],\"name\":\"UpdateFinalizationPeriodSeconds\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"oldPercent\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"newPercent\",\"type\":\"uint256\"}],\"name\":\"UpdateProofRewardPercent\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"oldWindow\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"newWindow\",\"type\":\"uint256\"}],\"name\":\"UpdateProofWindow\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"oldVerifier\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"newVerifier\",\"type\":\"address\"}],\"name\":\"UpdateVerifier\",\"type\":\"event\"},{\"inputs\":[],\"name\":\"LAYER_2_CHAIN_ID\",\"outputs\":[{\"internalType\":\"uint64\",\"name\":\"\",\"type\":\"uint64\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"__maxNumTxInChunk\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"_account\",\"type\":\"address\"}],\"name\":\"addChallenger\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"owner\",\"type\":\"address\"}],\"name\":\"batchChallengeReward\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"batchChallenged\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"batchIndex\",\"type\":\"uint256\"}],\"name\":\"batchChallengedSuccess\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"batchIndex\",\"type\":\"uint256\"}],\"name\":\"batchDataStore\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"originTimestamp\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"finalizeTimestamp\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"blockNumber\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"signedSequencersBitmap\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"batchIndex\",\"type\":\"uint256\"}],\"name\":\"batchExist\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"batchIndex\",\"type\":\"uint256\"}],\"name\":\"batchInChallenge\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"batchIndex\",\"type\":\"uint256\"}],\"name\":\"batchInsideChallengeWindow\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"batchIndex\",\"type\":\"uint64\"},{\"internalType\":\"bytes32\",\"name\":\"_batchHash\",\"type\":\"bytes32\"}],\"name\":\"challengeState\",\"outputs\":[],\"stateMutability\":\"payable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"batchIndex\",\"type\":\"uint256\"}],\"name\":\"challenges\",\"outputs\":[{\"internalType\":\"uint64\",\"name\":\"batchIndex\",\"type\":\"uint64\"},{\"internalType\":\"address\",\"name\":\"challenger\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"challengeDeposit\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"startTime\",\"type\":\"uint256\"},{\"internalType\":\"bool\",\"name\":\"challengeSuccess\",\"type\":\"bool\"},{\"internalType\":\"bool\",\"name\":\"finished\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"receiver\",\"type\":\"address\"}],\"name\":\"claimProveRemaining\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"receiver\",\"type\":\"address\"}],\"name\":\"claimReward\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"internalType\":\"uint8\",\"name\":\"version\",\"type\":\"uint8\"},{\"internalType\":\"bytes\",\"name\":\"parentBatchHeader\",\"type\":\"bytes\"},{\"internalType\":\"bytes\",\"name\":\"blockContexts\",\"type\":\"bytes\"},{\"internalType\":\"bytes\",\"name\":\"skippedL1MessageBitmap\",\"type\":\"bytes\"},{\"internalType\":\"bytes32\",\"name\":\"prevStateRoot\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32\",\"name\":\"postStateRoot\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32\",\"name\":\"withdrawalRoot\",\"type\":\"bytes32\"}],\"internalType\":\"structIRollup.BatchDataInput\",\"name\":\"batchDataInput\",\"type\":\"tuple\"},{\"components\":[{\"internalType\":\"uint256\",\"name\":\"signedSequencersBitmap\",\"type\":\"uint256\"},{\"internalType\":\"bytes\",\"name\":\"sequencerSets\",\"type\":\"bytes\"},{\"internalType\":\"bytes\",\"name\":\"signature\",\"type\":\"bytes\"}],\"internalType\":\"structIRollup.BatchSignatureInput\",\"name\":\"batchSignatureInput\",\"type\":\"tuple\"}],\"name\":\"commitBatch\",\"outputs\":[],\"stateMutability\":\"payable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"batchIndex\",\"type\":\"uint256\"}],\"name\":\"committedBatches\",\"outputs\":[{\"internalType\":\"bytes32\",\"name\":\"batchHash\",\"type\":\"bytes32\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"finalizationPeriodSeconds\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes\",\"name\":\"_batchHeader\",\"type\":\"bytes\"}],\"name\":\"finalizeBatch\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"batchIndex\",\"type\":\"uint256\"}],\"name\":\"finalizedStateRoots\",\"outputs\":[{\"internalType\":\"bytes32\",\"name\":\"stateRoot\",\"type\":\"bytes32\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes\",\"name\":\"_batchHeader\",\"type\":\"bytes\"}],\"name\":\"importGenesisBatch\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"inChallenge\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"_l1StakingContract\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"_messageQueue\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"_verifier\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"_finalizationPeriodSeconds\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"_proofWindow\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"_proofRewardPercent\",\"type\":\"uint256\"}],\"name\":\"initialize\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"_batchIndex\",\"type\":\"uint256\"}],\"name\":\"isBatchFinalized\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"challengerAddress\",\"type\":\"address\"}],\"name\":\"isChallenger\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"isChallenger\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"l1StakingContract\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"lastCommittedBatchIndex\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"lastFinalizedBatchIndex\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"messageQueue\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"owner\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"paused\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"proofRewardPercent\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"proofWindow\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"proveRemaining\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes\",\"name\":\"_batchHeader\",\"type\":\"bytes\"},{\"internalType\":\"bytes\",\"name\":\"_batchProof\",\"type\":\"bytes\"}],\"name\":\"proveState\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"_account\",\"type\":\"address\"}],\"name\":\"removeChallenger\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"renounceOwnership\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes\",\"name\":\"_batchHeader\",\"type\":\"bytes\"},{\"internalType\":\"uint256\",\"name\":\"_count\",\"type\":\"uint256\"}],\"name\":\"revertBatch\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"revertReqIndex\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bool\",\"name\":\"_status\",\"type\":\"bool\"}],\"name\":\"setPause\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"newOwner\",\"type\":\"address\"}],\"name\":\"transferOwnership\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"_newPeriod\",\"type\":\"uint256\"}],\"name\":\"updateFinalizePeriodSeconds\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"_newWindow\",\"type\":\"uint256\"}],\"name\":\"updateProofWindow\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"_newProofRewardPercent\",\"type\":\"uint256\"}],\"name\":\"updateRewardPercentage\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"_newVerifier\",\"type\":\"address\"}],\"name\":\"updateVerifier\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"verifier\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"withdrawalRoot\",\"type\":\"bytes32\"}],\"name\":\"withdrawalRoots\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"exist\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"stateMutability\":\"payable\",\"type\":\"receive\"}]", } From 0fd2039766215028ea36138af3ebe8de25298adb Mon Sep 17 00:00:00 2001 From: Segue Date: Tue, 11 Aug 2026 20:48:19 +0800 Subject: [PATCH 08/16] feat(prover): resolve canonical submitter cutover batches --- prover/Makefile | 6 +- prover/bin/challenge/.env.dev | 4 + prover/bin/challenge/src/abi/Rollup.json | 288 +++++++- prover/bin/challenge/src/abi/Submitter.json | 660 ++++++++++++++++++ prover/bin/challenge/src/abi/mod.rs | 10 + .../bin/challenge/src/bin/auto_challenge.rs | 103 +-- prover/bin/challenge/src/external_sign.rs | 3 +- prover/bin/challenge/src/handler.rs | 249 ++++--- prover/bin/challenge/src/lib.rs | 4 +- prover/bin/challenge/src/rollup_compat.rs | 253 +++++++ prover/bin/challenge/src/util.rs | 1 + prover/bin/shadow-prove/.env.dev | 5 +- prover/bin/shadow-prove/abi/Rollup.json | 196 ++++-- prover/bin/shadow-prove/src/abi.rs | 10 + prover/bin/shadow-prove/src/execute.rs | 10 +- prover/bin/shadow-prove/src/lib.rs | 1 + prover/bin/shadow-prove/src/main.rs | 12 +- prover/bin/shadow-prove/src/rollup_compat.rs | 334 +++++++++ prover/bin/shadow-prove/src/shadow_prove.rs | 21 +- prover/bin/shadow-prove/src/shadow_rollup.rs | 525 +++++++------- prover/bin/shadow-prove/src/util.rs | 1 + 21 files changed, 2223 insertions(+), 473 deletions(-) create mode 100644 prover/bin/challenge/src/abi/Submitter.json create mode 100644 prover/bin/challenge/src/rollup_compat.rs create mode 100644 prover/bin/shadow-prove/src/rollup_compat.rs diff --git a/prover/Makefile b/prover/Makefile index d55f0bb46..6328e1661 100644 --- a/prover/Makefile +++ b/prover/Makefile @@ -1,3 +1,5 @@ +RUST_TOOLCHAIN ?= 1.91.0 + start-server: docker compose -f docker-compose-app.yml up -d @@ -6,7 +8,7 @@ stop-server: build-shadow-prove: - cd bin/shadow-prove && cargo build --release + cd bin/shadow-prove && cargo +$(RUST_TOOLCHAIN) build --release build-challenge: - cd bin/challenge && cargo build --release + cd bin/challenge && cargo +$(RUST_TOOLCHAIN) build --release diff --git a/prover/bin/challenge/.env.dev b/prover/bin/challenge/.env.dev index b5224a629..847cbb13e 100644 --- a/prover/bin/challenge/.env.dev +++ b/prover/bin/challenge/.env.dev @@ -3,12 +3,16 @@ HANDLER_L1_RPC = "http://localhost:8545" HANDLER_L2_RPC = "http://localhost:8545" HANDLER_PROVER_RPC ="http://localhost:3030" HANDLER_L1_ROLLUP = "0x5FbDB2315678afecb367f032d93F642f64180aa3" +# Development fixture only. Production must use the archived Rollup proxy +# deployment block from the deployment manifest. +HANDLER_L1_ROLLUP_DEPLOY_BLOCK = 1 CHALLENGE_HANDLER_PRIVATE_KEY = "0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80" SHADOW_PROVING = true # challenger CHALLENGER_L1_RPC = "http://localhost:8545" CHALLENGER_L1_ROLLUP = "0x5FbDB2315678afecb367f032d93F642f64180aa3" +CHALLENGER_L1_ROLLUP_DEPLOY_BLOCK = 1 CHALLENGER_PRIVATEKEY = "0x47e179ec197488593b187f80a00eb0da91f1b9d0b13f8733639f19c30a34926a" diff --git a/prover/bin/challenge/src/abi/Rollup.json b/prover/bin/challenge/src/abi/Rollup.json index 28f7a5c6d..e9da0e250 100644 --- a/prover/bin/challenge/src/abi/Rollup.json +++ b/prover/bin/challenge/src/abi/Rollup.json @@ -217,6 +217,25 @@ "name": "RevertBatch", "type": "event" }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "oldAddr", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "newAddr", + "type": "address" + } + ], + "name": "SubmitterContractUpdated", + "type": "event" + }, { "anonymous": false, "inputs": [ @@ -306,6 +325,25 @@ "name": "UpdateProofWindow", "type": "event" }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint256", + "name": "oldPeriod", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "newPeriod", + "type": "uint256" + } + ], + "name": "UpdateRollupDelayPeriod", + "type": "event" + }, { "anonymous": false, "inputs": [ @@ -364,6 +402,25 @@ "stateMutability": "nonpayable", "type": "function" }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "batchIndex", + "type": "uint256" + } + ], + "name": "batchBlobVersionedHashes", + "outputs": [ + { + "internalType": "bytes32", + "name": "blobVersionedHash", + "type": "bytes32" + } + ], + "stateMutability": "view", + "type": "function" + }, { "inputs": [ { @@ -441,9 +498,9 @@ "type": "uint256" }, { - "internalType": "uint256", - "name": "signedSequencersBitmap", - "type": "uint256" + "internalType": "address", + "name": "submitter", + "type": "address" } ], "stateMutability": "view", @@ -637,33 +694,121 @@ "internalType": "struct IRollup.BatchDataInput", "name": "batchDataInput", "type": "tuple" - }, + } + ], + "name": "commitBatch", + "outputs": [], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [ { "components": [ { - "internalType": "uint256", - "name": "signedSequencersBitmap", - "type": "uint256" + "internalType": "uint8", + "name": "version", + "type": "uint8" }, { "internalType": "bytes", - "name": "sequencerSets", + "name": "parentBatchHeader", "type": "bytes" }, + { + "internalType": "uint64", + "name": "lastBlockNumber", + "type": "uint64" + }, + { + "internalType": "uint16", + "name": "numL1Messages", + "type": "uint16" + }, + { + "internalType": "bytes32", + "name": "prevStateRoot", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "postStateRoot", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "withdrawalRoot", + "type": "bytes32" + } + ], + "internalType": "struct IRollup.BatchDataInput", + "name": "batchDataInput", + "type": "tuple" + }, + { + "internalType": "bytes", + "name": "_batchHeader", + "type": "bytes" + }, + { + "internalType": "bytes", + "name": "_batchProof", + "type": "bytes" + } + ], + "name": "commitBatchWithProof", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "components": [ + { + "internalType": "uint8", + "name": "version", + "type": "uint8" + }, { "internalType": "bytes", - "name": "signature", + "name": "parentBatchHeader", "type": "bytes" + }, + { + "internalType": "uint64", + "name": "lastBlockNumber", + "type": "uint64" + }, + { + "internalType": "uint16", + "name": "numL1Messages", + "type": "uint16" + }, + { + "internalType": "bytes32", + "name": "prevStateRoot", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "postStateRoot", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "withdrawalRoot", + "type": "bytes32" } ], - "internalType": "struct IRollup.BatchSignatureInput", - "name": "batchSignatureInput", + "internalType": "struct IRollup.BatchDataInput", + "name": "batchDataInput", "type": "tuple" } ], - "name": "commitBatch", + "name": "commitState", "outputs": [], - "stateMutability": "payable", + "stateMutability": "nonpayable", "type": "function" }, { @@ -779,7 +924,7 @@ "inputs": [ { "internalType": "address", - "name": "_l1StakingContract", + "name": "_submitterContract", "type": "address" }, { @@ -826,6 +971,32 @@ "stateMutability": "nonpayable", "type": "function" }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "_rollupDelayPeriod", + "type": "uint256" + } + ], + "name": "initialize3", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_newSubmitter", + "type": "address" + } + ], + "name": "initialize4", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, { "inputs": [ { @@ -864,19 +1035,6 @@ "stateMutability": "view", "type": "function" }, - { - "inputs": [], - "name": "l1StakingContract", - "outputs": [ - { - "internalType": "address", - "name": "", - "type": "address" - } - ], - "stateMutability": "view", - "type": "function" - }, { "inputs": [], "name": "lastCommittedBatchIndex", @@ -942,6 +1100,25 @@ "stateMutability": "view", "type": "function" }, + { + "inputs": [ + { + "internalType": "address", + "name": "submitter", + "type": "address" + } + ], + "name": "pendingBatchCount", + "outputs": [ + { + "internalType": "uint256", + "name": "count", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, { "inputs": [], "name": "proofRewardPercent", @@ -968,6 +1145,24 @@ "stateMutability": "view", "type": "function" }, + { + "inputs": [ + { + "internalType": "bytes", + "name": "_batchHeader", + "type": "bytes" + }, + { + "internalType": "bytes", + "name": "_batchProof", + "type": "bytes" + } + ], + "name": "proveCommittedBatchState", + "outputs": [], + "stateMutability": "view", + "type": "function" + }, { "inputs": [], "name": "proveRemaining", @@ -1050,6 +1245,19 @@ "stateMutability": "view", "type": "function" }, + { + "inputs": [], + "name": "rollupDelayPeriod", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, { "inputs": [ { @@ -1063,6 +1271,19 @@ "stateMutability": "nonpayable", "type": "function" }, + { + "inputs": [], + "name": "submitterContract", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, { "inputs": [ { @@ -1115,6 +1336,19 @@ "stateMutability": "nonpayable", "type": "function" }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "_newPeriod", + "type": "uint256" + } + ], + "name": "updateRollupDelayPeriod", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, { "inputs": [ { diff --git a/prover/bin/challenge/src/abi/Submitter.json b/prover/bin/challenge/src/abi/Submitter.json new file mode 100644 index 000000000..85eeb4d97 --- /dev/null +++ b/prover/bin/challenge/src/abi/Submitter.json @@ -0,0 +1,660 @@ +[ + { + "inputs": [], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint256", + "name": "oldValue", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "newValue", + "type": "uint256" + } + ], + "name": "ChallengeDepositUpdated", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "submitter", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "receiver", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "CreditClaimed", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint8", + "name": "version", + "type": "uint8" + } + ], + "name": "Initialized", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint256", + "name": "oldValue", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "newValue", + "type": "uint256" + } + ], + "name": "MinimumStakeUpdated", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "previousOwner", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "newOwner", + "type": "address" + } + ], + "name": "OwnershipTransferred", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint256", + "name": "oldValue", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "newValue", + "type": "uint256" + } + ], + "name": "RewardPercentageUpdated", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "receiver", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "SlashRemainingClaimed", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "submitter", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amount", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "reward", + "type": "uint256" + } + ], + "name": "Slashed", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "submitter", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amount", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "total", + "type": "uint256" + } + ], + "name": "Staked", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "submitter", + "type": "address" + } + ], + "name": "SubmitterAdded", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "submitter", + "type": "address" + } + ], + "name": "SubmitterRemoved", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "submitter", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amount", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "totalCredit", + "type": "uint256" + } + ], + "name": "WithdrawalCredited", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "submitter", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "receiver", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "WithdrawalPaid", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "submitter", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "receiver", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "WithdrawalRequested", + "type": "event" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "submitter", + "type": "address" + } + ], + "name": "addSubmitter", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "challengeDeposit", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "receiver", + "type": "address" + } + ], + "name": "claimCredit", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "receiver", + "type": "address" + } + ], + "name": "claimSlashRemaining", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "receiver", + "type": "address" + } + ], + "name": "claimWithdrawal", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "submitter", + "type": "address" + } + ], + "name": "claimable", + "outputs": [ + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "submitter", + "type": "address" + } + ], + "name": "exiting", + "outputs": [ + { + "internalType": "bool", + "name": "isExiting", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "owner_", + "type": "address" + }, + { + "internalType": "address", + "name": "rollupContract_", + "type": "address" + }, + { + "internalType": "uint256", + "name": "minimumStake_", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "challengeDeposit_", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "rewardPercentage_", + "type": "uint256" + } + ], + "name": "initialize", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "submitter", + "type": "address" + } + ], + "name": "isActive", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "minimumStake", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "owner", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "submitter", + "type": "address" + } + ], + "name": "registered", + "outputs": [ + { + "internalType": "bool", + "name": "isRegistered", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "submitter", + "type": "address" + } + ], + "name": "removeSubmitter", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "renounceOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "rewardPercentage", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "rollupContract", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "value", + "type": "uint256" + } + ], + "name": "setMinimumStake", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "submitter", + "type": "address" + } + ], + "name": "slash", + "outputs": [ + { + "internalType": "uint256", + "name": "reward", + "type": "uint256" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "slashRemaining", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "stake", + "outputs": [], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "submitter", + "type": "address" + } + ], + "name": "stakeOf", + "outputs": [ + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "newOwner", + "type": "address" + } + ], + "name": "transferOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "value", + "type": "uint256" + } + ], + "name": "updateChallengeDeposit", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "value", + "type": "uint256" + } + ], + "name": "updateRewardPercentage", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "receiver", + "type": "address" + } + ], + "name": "withdraw", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + } +] diff --git a/prover/bin/challenge/src/abi/mod.rs b/prover/bin/challenge/src/abi/mod.rs index 5e88bf9fa..b62b8e5cc 100644 --- a/prover/bin/challenge/src/abi/mod.rs +++ b/prover/bin/challenge/src/abi/mod.rs @@ -7,3 +7,13 @@ pub mod rollup_abi { use ethers::prelude::abigen; abigen!(Rollup, "src/abi/Rollup.json"); } + +pub mod pre_submitter_rollup_abi { + use ethers::prelude::abigen; + abigen!(PreSubmitterRollup, "src/abi/pre_submitter/Rollup.json"); +} + +pub mod submitter_abi { + use ethers::prelude::abigen; + abigen!(Submitter, "src/abi/Submitter.json"); +} diff --git a/prover/bin/challenge/src/bin/auto_challenge.rs b/prover/bin/challenge/src/bin/auto_challenge.rs index 5c100b9f4..e7f06fe62 100644 --- a/prover/bin/challenge/src/bin/auto_challenge.rs +++ b/prover/bin/challenge/src/bin/auto_challenge.rs @@ -1,4 +1,6 @@ -use challenge_handler::abi::rollup_abi::Rollup; +use challenge_handler::abi::{rollup_abi::Rollup, submitter_abi::Submitter}; +use challenge_handler::rollup_compat::resolve_canonical_commit; +use challenge_handler::util::read_parse_env; use dotenv::dotenv; use env_logger::Env; use ethers::prelude::*; @@ -39,6 +41,7 @@ pub async fn challenge() -> Result<(), Box> { let l1_rpc = var("CHALLENGER_L1_RPC").expect("Cannot detect L1_RPC env var"); let l1_rollup_address = var("CHALLENGER_L1_ROLLUP").expect("Cannot detect L1_ROLLUP env var"); let private_key = var("CHALLENGER_PRIVATEKEY").expect("Cannot detect CHALLENGER_PRIVATEKEY env var"); + let rollup_deployed_block: u64 = read_parse_env("CHALLENGER_L1_ROLLUP_DEPLOY_BLOCK"); let l1_provider: Provider = Provider::::try_from(l1_rpc)?; let l1_signer = Arc::new(SignerMiddleware::new( l1_provider.clone(), @@ -66,21 +69,28 @@ pub async fn challenge() -> Result<(), Box> { let proof_window = l1_rollup.proof_window().await?; log::info!("finalization_period: {:#?} proof_window: {:#?}", finalization_period, proof_window); - let min_deposit: U256 = U256::from(4 * 10u128.pow(18)); - log::info!("min_deposit: {:#?}", min_deposit); - loop { - std::thread::sleep(Duration::from_secs(12)); - let _ = auto_challenge(&l1_provider, &l1_rollup, min_deposit).await; + tokio::time::sleep(Duration::from_secs(12)).await; + let _ = auto_challenge(&l1_provider, &l1_rollup, rollup_deployed_block).await; } } -async fn auto_challenge(l1_provider: &Provider, l1_rollup: &RollupType, min_deposit: U256) -> Result<(), Box> { +async fn auto_challenge(l1_provider: &Provider, l1_rollup: &RollupType, rollup_deployed_block: u64) -> Result<(), Box> { // Search for the latest batch. - let latest = match l1_provider.get_block_number().await { - Ok(bn) => bn, + let (latest, snapshot_hash) = match l1_provider.get_block(BlockNumber::Finalized).await { + Ok(Some(block)) => match (block.number, block.hash) { + (Some(number), Some(hash)) => (number, hash), + _ => { + log::error!("finalized L1 block has no stable identity"); + return Ok(()); + } + }, + Ok(None) => { + log::error!("finalized L1 block is unavailable"); + return Ok(()); + } Err(e) => { - log::error!("L1 provider.get_block_number error: {:#?}", e); + log::error!("L1 provider.get finalized block error: {:#?}", e); return Ok(()); } }; @@ -101,44 +111,28 @@ async fn auto_challenge(l1_provider: &Provider, l1_rollup: &RollupType, mi } log::info!("latest blocknum = {:#?}", latest); - let start = if latest > U64::from(600) { - latest - U64::from(600) - } else { - U64::from(1) - }; - - let filter = l1_rollup.commit_batch_filter().filter.from_block(start).address(l1_rollup.address()); - let mut logs: Vec = match l1_provider.get_logs(&filter).await { - Ok(logs) => logs, - Err(e) => { - log::error!("l1_rollup.commit_batch.get_logs error: {:#?}", e); - return Ok(()); - } - }; - - if logs.is_empty() { - log::error!("There have been no commit_batch logs for the last 600 blocks."); + let block_id = BlockId::Number(BlockNumber::Number(latest)); + let submitter_address = l1_rollup.submitter_contract().block(block_id).call().await?; + let submitter = Submitter::new(submitter_address, l1_rollup.client()); + let min_deposit = submitter.challenge_deposit().block(block_id).call().await?; + let batch_index = l1_rollup.last_committed_batch_index().block(block_id).call().await?.as_u64(); + if resolve_canonical_commit(l1_rollup, l1_provider, batch_index, rollup_deployed_block, latest) + .await? + .is_none() + { + log::warn!("latest batch {batch_index} has no canonical Commit at fixed snapshot"); return Ok(()); } - - logs.sort_by(|a, b| a.block_number.unwrap().cmp(&b.block_number.unwrap())); - let batch_index = match logs.get(logs.len() - 2) { - Some(log) => log.topics[1].to_low_u64_be(), - None => { - log::error!("find commit_batch log error"); - return Ok(()); - } - }; log::info!("latest batch index = {:#?}", batch_index); // Challenge state. - let is_batch_finalized = l1_rollup.is_batch_finalized(U256::from(batch_index)).await?; + let is_batch_finalized = l1_rollup.is_batch_finalized(U256::from(batch_index)).block(block_id).call().await?; if is_batch_finalized { log::info!("is_batch_finalized = true, No need for challenge, batch index = {:#?}", batch_index); return Ok(()); } - let challenges = match l1_rollup.challenges(U256::from(batch_index)).await { + let challenges = match l1_rollup.challenges(U256::from(batch_index)).block(block_id).call().await { Ok(x) => x, Err(e) => { log::info!("query l1_rollup.challenges error, batch index = {:#?}, {:#?}", batch_index, e); @@ -151,7 +145,11 @@ async fn auto_challenge(l1_provider: &Provider, l1_rollup: &RollupType, mi return Ok(()); } - let batch_hash = l1_rollup.committed_batches(U256::from(batch_index)).await?; + let batch_hash = l1_rollup.committed_batches(U256::from(batch_index)).block(block_id).call().await?; + if !snapshot_unchanged(l1_provider, latest, snapshot_hash).await { + log::error!("finalized L1 snapshot changed while preparing auto-challenge"); + return Ok(()); + } // l1_rollup.connect() let tx: FunctionCall<_, _, _> = l1_rollup.challenge_state(batch_index, batch_hash).value(min_deposit); @@ -210,7 +208,13 @@ async fn detecte_challenge(latest: U64, l1_rollup: &RollupType, l1_provider: &Pr } else { U64::from(1) }; - let filter = l1_rollup.challenge_state_filter().filter.from_block(start).address(l1_rollup.address()); + let block_id = BlockId::Number(BlockNumber::Number(latest)); + let filter = l1_rollup + .challenge_state_filter() + .filter + .from_block(start) + .to_block(latest) + .address(l1_rollup.address()); let mut logs: Vec = match l1_provider.get_logs(&filter).await { Ok(logs) => logs, Err(e) => { @@ -227,16 +231,22 @@ async fn detecte_challenge(latest: U64, l1_rollup: &RollupType, l1_provider: &Pr for log in logs { let batch_index: u64 = log.topics[1].to_low_u64_be(); - let batch_in_challenge: bool = match l1_rollup.batch_in_challenge(U256::from(batch_index)).await { + let batch_in_challenge: bool = match l1_rollup.batch_in_challenge(U256::from(batch_index)).block(block_id).call().await { Ok(x) => x, Err(e) => { log::info!("query l1_rollup.batch_in_challenge error, batch index = {:#?}, {:#?}", batch_index, e); return None; } }; - let _is_batch_finalized: bool = l1_rollup.is_batch_finalized(U256::from(batch_index)).await.unwrap(); + let is_batch_finalized: bool = match l1_rollup.is_batch_finalized(U256::from(batch_index)).block(block_id).call().await { + Ok(value) => value, + Err(err) => { + log::error!("query l1_rollup.is_batch_finalized failed: {err:#}"); + return None; + } + }; - if batch_in_challenge { + if batch_in_challenge && !is_batch_finalized { log::info!("prev challenge not finalized, batch index = {:#?}", batch_index); return Some(true); } @@ -246,6 +256,13 @@ async fn detecte_challenge(latest: U64, l1_rollup: &RollupType, l1_provider: &Pr Some(false) } +async fn snapshot_unchanged(provider: &Provider, number: U64, expected_hash: H256) -> bool { + matches!( + provider.get_block(number).await, + Ok(Some(block)) if block.hash == Some(expected_hash) + ) +} + // Check layer2 state. async fn verify_state_transition() { // Do nothing diff --git a/prover/bin/challenge/src/external_sign.rs b/prover/bin/challenge/src/external_sign.rs index fb4a28fa2..627de3b08 100644 --- a/prover/bin/challenge/src/external_sign.rs +++ b/prover/bin/challenge/src/external_sign.rs @@ -97,7 +97,6 @@ impl ExternalSign { return Err("ext_sign response sign data invalid".into()); } - let sig = hex::decode(&response.result.sign_datas[0].sign[2..])?; let signed_tx: Bytes = tx.rlp_signed(&Signature::try_from(sig.as_slice())?); Ok(signed_tx) @@ -141,7 +140,7 @@ impl ExternalSign { log::debug!("===payload: {:?}", serde_json::to_string(payload).unwrap()); let response: reqwest::Response = self.client.post(url).json(&payload).send().await?; log::info!("===do_request response: {:?}", response); - + let status = response.status(); if !status.is_success() { let text = response.text().await?; diff --git a/prover/bin/challenge/src/handler.rs b/prover/bin/challenge/src/handler.rs index 36eac035f..c949799ed 100644 --- a/prover/bin/challenge/src/handler.rs +++ b/prover/bin/challenge/src/handler.rs @@ -1,13 +1,14 @@ -use crate::abi::rollup_abi::{CommitBatchCall, Rollup}; +use crate::abi::{rollup_abi::Rollup, submitter_abi::Submitter}; use crate::external_sign::ExternalSign; use crate::metrics::METRICS; +use crate::rollup_compat::{decode_batch_data_input, resolve_canonical_commit, CanonicalCommit}; use crate::util::read_env_var; use crate::util::{self, read_parse_env}; +use ethers::prelude::*; use ethers::providers::{Http, Provider}; use ethers::signers::Wallet; use ethers::types::Address; use ethers::types::Bytes; -use ethers::{abi::AbiDecode, prelude::*}; use eyre::anyhow; use serde::{Deserialize, Serialize}; use std::env::var; @@ -51,6 +52,7 @@ pub struct ChallengeHandler { l1_provider: Provider, l2_rpc: String, ext_signer: Option, + rollup_deployed_block: u64, } impl ChallengeHandler { @@ -60,6 +62,7 @@ impl ChallengeHandler { let l2_rpc = var("HANDLER_L2_RPC").expect("Cannot detect L2_RPC env var"); let l1_rollup_address = var("HANDLER_L1_ROLLUP").expect("Cannot detect L1_ROLLUP env var"); let _ = var("HANDLER_PROVER_RPC").expect("Cannot detect PROVER_RPC env var"); + let rollup_deployed_block: u64 = read_parse_env("HANDLER_L1_ROLLUP_DEPLOY_BLOCK"); let private_key = read_env_var( "CHALLENGE_HANDLER_PRIVATE_KEY", @@ -98,6 +101,7 @@ impl ChallengeHandler { l1_provider, l2_rpc, ext_signer, + rollup_deployed_block, } } @@ -109,11 +113,36 @@ impl ChallengeHandler { loop { sleep(Duration::from_secs(12)).await; + // A challenge win/slash does not terminate the batch. Keep the + // service in an explicit pending state until the owner executes + // the canonical RevertBatch and clears revertReqIndex. + match l1_rollup.revert_req_index().await { + Ok(index) if !index.is_zero() => { + log::warn!("challenge result is awaiting owner RevertBatch, revert_req_index={}", index); + continue; + } + Ok(_) => {} + Err(err) => { + log::error!("query revertReqIndex failed: {err:#}"); + continue; + } + } + // Step1. fetch latest blocknum. - let latest = match l1_provider.get_block_number().await { - Ok(bn) => bn, + let (latest, snapshot_hash) = match l1_provider.get_block(BlockNumber::Finalized).await { + Ok(Some(block)) => match (block.number, block.hash) { + (Some(number), Some(hash)) => (number, hash), + _ => { + log::error!("finalized L1 block has no stable identity"); + continue; + } + }, + Ok(None) => { + log::error!("finalized L1 block is unavailable"); + continue; + } Err(e) => { - log::error!("L1 provider.get_block_number error: {:#?}", e); + log::error!("L1 provider.get finalized block error: {:#?}", e); continue; } }; @@ -146,12 +175,13 @@ impl ChallengeHandler { METRICS.detected_batch_index.set(batch_index as i64); // Step3. query challenged batch info. - let (challenged_rollup_hash, batch_hash) = match query_batch_tx(latest, l1_rollup, batch_index, l1_provider).await { + let (challenged_commit, batch_hash) = match query_batch_tx(latest, l1_rollup, batch_index, self.rollup_deployed_block, l1_provider).await + { Some(value) => value, None => continue, }; - let mut batch_info = match batch_inspect(l1_rollup, l1_provider, batch_index, challenged_rollup_hash).await { + let mut batch_info = match batch_inspect(l1_rollup, l1_provider, batch_index, &challenged_commit, latest).await { Some(mut b) => { b.batch_index = batch_index; b.parent_batch_hash = batch_hash.as_bytes().try_into().unwrap_or_default(); @@ -159,6 +189,10 @@ impl ChallengeHandler { } None => continue, }; + if !snapshot_unchanged(l1_provider, latest, snapshot_hash).await { + log::error!("finalized L1 snapshot changed while building challenge proof input"); + continue; + } let blocks_len = batch_info.end_block - batch_info.start_block + 1; log::info!( @@ -241,8 +275,18 @@ impl ChallengeHandler { } async fn prove_state(&self, batch_index: u64, batch_header: Bytes, batch_proof: ProveResult, l1_rollup: &RollupType) -> bool { + let signer = if let Some(external) = &self.ext_signer { + Address::from_str(&external.address).unwrap_or_default() + } else { + l1_rollup.client().address() + }; for _ in 0..MAX_RETRY_TIMES { sleep(Duration::from_secs(12)).await; + if !self.proof_signer_is_active(l1_rollup, signer).await { + // Re-run the gate before every retry: a threshold increase, + // exit, or slash between attempts must stop transmission. + return false; + } log::info!("starting prove state onchain, batch index = {:#?}", batch_index); let proof = Bytes::from(batch_proof.proof_data.clone()); @@ -263,6 +307,50 @@ impl ChallengeHandler { } false } + + async fn proof_signer_is_active(&self, l1_rollup: &RollupType, signer: Address) -> bool { + let (number, hash) = match self.l1_provider.get_block(BlockNumber::Finalized).await { + Ok(Some(block)) => match (block.number, block.hash) { + (Some(number), Some(hash)) => (number, hash), + _ => { + log::error!("finalized L1 block has no stable identity"); + return false; + } + }, + Ok(None) => { + log::error!("finalized L1 block is unavailable"); + return false; + } + Err(err) => { + log::error!("query finalized L1 snapshot failed: {err:#}"); + return false; + } + }; + let snapshot = BlockId::Number(BlockNumber::Number(number)); + let submitter_address = match l1_rollup.submitter_contract().block(snapshot).call().await { + Ok(address) => address, + Err(err) => { + log::error!("query Rollup.submitterContract failed: {err:#}"); + return false; + } + }; + let submitter = Submitter::new(submitter_address, l1_rollup.client()); + let active = match submitter.is_active(signer).block(snapshot).call().await { + Ok(active) => active, + Err(err) => { + log::error!("Submitter.isActive preflight failed: {err:#}"); + return false; + } + }; + if !snapshot_unchanged(&self.l1_provider, number, hash).await { + log::error!("finalized snapshot changed during Submitter.isActive preflight"); + return false; + } + if !active { + log::error!("refusing proveState from inactive submitter signer {signer:?}"); + } + active + } } /** @@ -292,65 +380,35 @@ async fn query_proof(batch_index: u64) -> Option { Some(prove_result) } -async fn query_batch_tx(latest: U64, l1_rollup: &RollupType, batch_index: u64, l1_provider: &Provider) -> Option<(H256, H256)> { - let start = if latest > U64::from(7200 * 3) { - // Depends on challenge period - latest - U64::from(7200 * 3) - } else { - U64::from(1) +async fn query_batch_tx( + latest: U64, + l1_rollup: &RollupType, + batch_index: u64, + rollup_deployed_block: u64, + l1_provider: &Provider, +) -> Option<(CanonicalCommit, H256)> { + let commit = match resolve_canonical_commit(l1_rollup, l1_provider, batch_index, rollup_deployed_block, latest).await { + Ok(Some(commit)) => commit, + Ok(None) => { + log::warn!("batch {batch_index} has no canonical Commit at snapshot {latest}"); + return None; + } + Err(err) => { + log::error!("canonical Commit resolution failed: {err:#}"); + return None; + } }; - let challenged_hash = query_tx_hash(l1_rollup, start, batch_index, l1_provider).await.or_else(|| { - log::warn!("challenged_hash is none"); - None - })?; - - let batch_hash: [u8; 32] = l1_rollup.committed_batches(U256::from(batch_index - 1)).await.unwrap_or_default(); - Some((challenged_hash, H256::from_slice(&batch_hash))) -} - -async fn query_tx_hash(l1_rollup: &RollupType, start: U64, batch_index: u64, l1_provider: &Provider) -> Option { - let filter = l1_rollup - .commit_batch_filter() - .filter - .from_block(start) - .topic1(U256::from(batch_index)) - .address(l1_rollup.address()); - let logs: Vec = match l1_provider.get_logs(&filter).await { - Ok(logs) => logs, - Err(e) => { - log::error!("l1_rollup.commit_batch.get_logs error: {:#?}", e); + let block_id = BlockId::Number(BlockNumber::Number(latest)); + let parent_index = batch_index.checked_sub(1)?; + let parent_hash: [u8; 32] = match l1_rollup.committed_batches(U256::from(parent_index)).block(block_id).call().await { + Ok(hash) => hash, + Err(err) => { + log::error!("query canonical parent batch hash failed: {err:#}"); return None; } }; - if logs.is_empty() { - log::error!("no commit_batch log of {:?}, commit_batch logs is empty", batch_index); - return None; - } - for log in logs { - if log.topics[1].to_low_u64_be() != batch_index { - continue; - } - let tx_hash = log.transaction_hash.unwrap(); - let receipt = l1_provider.get_transaction_receipt(tx_hash).await.unwrap(); - match receipt { - Some(tr) => { - match tr.status.unwrap_or_default().as_u64() { - 1 => return Some(tx_hash), - _ => { - log::warn!("commit_batch receipt is fail: {:#?}", tr); - continue; - } - }; - } - None => { - log::warn!("no commit_batch receipt, batch index = {:?}, tx_hash = {:?}", batch_index, tx_hash); - } - } - } - log::error!("unable to find valid commit_batch log, batch index = {:?}", batch_index); - - None + Some((commit, H256::from_slice(&parent_hash))) } async fn detecte_challenge_event(latest: U64, l1_rollup: &RollupType, l1_provider: &Provider) -> Option { @@ -360,7 +418,13 @@ async fn detecte_challenge_event(latest: U64, l1_rollup: &RollupType, l1_provide } else { U64::from(1) }; - let filter = l1_rollup.challenge_state_filter().filter.from_block(start).address(l1_rollup.address()); + let block_id = BlockId::Number(BlockNumber::Number(latest)); + let filter = l1_rollup + .challenge_state_filter() + .filter + .from_block(start) + .to_block(latest) + .address(l1_rollup.address()); let mut logs: Vec = match l1_provider.get_logs(&filter).await { Ok(logs) => logs, Err(e) => { @@ -381,14 +445,14 @@ async fn detecte_challenge_event(latest: U64, l1_rollup: &RollupType, l1_provide for log in logs { let batch_index: u64 = log.topics[1].to_low_u64_be(); - let batch_in_challenge: bool = match l1_rollup.batch_in_challenge(U256::from(batch_index)).await { + let batch_in_challenge: bool = match l1_rollup.batch_in_challenge(U256::from(batch_index)).block(block_id).call().await { Ok(x) => x, Err(e) => { log::info!("query l1_rollup.batch_in_challenge error, batch index = {:#?}, {:#?}", batch_index, e); return None; } }; - let is_batch_finalized: bool = match l1_rollup.is_batch_finalized(U256::from(batch_index)).await { + let is_batch_finalized: bool = match l1_rollup.is_batch_finalized(U256::from(batch_index)).block(block_id).call().await { Ok(x) => x, Err(e) => { log::info!("query l1_rollup.is_batch_finalized error, batch index = {:#?}, {:#?}", batch_index, e); @@ -421,8 +485,15 @@ struct BatchInfo { parent_batch_hash: [u8; 32], } -async fn batch_inspect(l1_rollup: &RollupType, l1_provider: &Provider, batch_index: u64, hash: TxHash) -> Option { - let prev_batch_last_bn: U256 = match l1_rollup.batch_data_store(U256::from(batch_index - 1)).await { +async fn batch_inspect( + l1_rollup: &RollupType, + l1_provider: &Provider, + batch_index: u64, + commit: &CanonicalCommit, + snapshot: U64, +) -> Option { + let block_id = BlockId::Number(BlockNumber::Number(snapshot)); + let prev_batch_last_bn: U256 = match l1_rollup.batch_data_store(U256::from(batch_index - 1)).block(block_id).call().await { Ok(s) => s.2, Err(e) => { log::error!("l1_rollup.batch_data_store err: {:#?}", e); @@ -430,7 +501,7 @@ async fn batch_inspect(l1_rollup: &RollupType, l1_provider: &Provider, bat } }; //Step1. Get transaction - let result = l1_provider.get_transaction(hash).await; + let result = l1_provider.get_transaction(commit.transaction_hash).await; let tx = match result { Ok(Some(tx)) => tx, Ok(None) => { @@ -442,26 +513,35 @@ async fn batch_inspect(l1_rollup: &RollupType, l1_provider: &Provider, bat return None; } }; + if tx.to != Some(l1_rollup.address()) + || tx.block_number != Some(commit.identity.block_number.into()) + || tx.block_hash != Some(commit.block_hash) + || tx.transaction_index != Some(commit.identity.transaction_index.into()) + { + log::error!("canonical Commit transaction identity changed"); + return None; + } //Step2. Parse transaction data let data = tx.input; if data.is_empty() { - log::warn!("batch inspect: tx.input is empty, tx_hash = {:#?}", hash); + log::warn!("batch inspect: tx.input is empty, tx_hash = {:#?}", commit.transaction_hash); return None; } - let param = if let Ok(_param) = CommitBatchCall::decode(&data) { - _param - } else { - log::error!("batch inspect: decode tx.input error, tx_hash = {:#?}", hash); - return None; + let param = match decode_batch_data_input(&data) { + Ok(param) => param, + Err(err) => { + log::error!("batch inspect: decode tx.input error, tx_hash = {:?}: {err:#}", commit.transaction_hash); + return None; + } }; - let version: u8 = param.batch_data_input.version; - let prev_state_root: [u8; 32] = param.batch_data_input.prev_state_root; - let post_state_root: [u8; 32] = param.batch_data_input.post_state_root; - let withdrawal_root: [u8; 32] = param.batch_data_input.withdrawal_root; - let last_block_number: u64 = param.batch_data_input.last_block_number; - let num_l1_messages = param.batch_data_input.num_l1_messages; + let version = param.version; + let prev_state_root = param.prev_state_root; + let post_state_root = param.post_state_root; + let withdrawal_root = param.withdrawal_root; + let last_block_number = param.last_block_number; + let num_l1_messages = param.num_l1_messages; log::info!("======> batch inspect: decode tx.input, version = {:#?}", version); log::info!("======> batch inspect: decode tx.input, param = {:#?}", param); @@ -477,7 +557,7 @@ async fn batch_inspect(l1_rollup: &RollupType, l1_provider: &Provider, bat }; // prev_batch_header - let prev_batch_header: Bytes = param.batch_data_input.parent_batch_header; + let prev_batch_header = param.parent_batch_header; let prev_total_l1_message = prev_batch_header.get(17..25).unwrap_or_default(); let post_total_l1_message = u64::from_be_bytes(prev_total_l1_message.try_into().unwrap()) + batch_info.l1_message_popped; batch_info.total_l1_message_popped = post_total_l1_message; @@ -485,6 +565,13 @@ async fn batch_inspect(l1_rollup: &RollupType, l1_provider: &Provider, bat Some(batch_info) } +async fn snapshot_unchanged(provider: &Provider, number: U64, expected_hash: H256) -> bool { + matches!( + provider.get_block(number).await, + Ok(Some(block)) if block.hash == Some(expected_hash) + ) +} + impl BatchInfo { fn fill_ext(&mut self, batch_header_ex: Vec) -> &Self { log::debug!("batch_header_ex len: {:#?}", batch_header_ex.len()); @@ -580,4 +667,4 @@ pub fn contract_error(e: ContractError) -> String { format!("error: {:?}", e) }; error_msg -} \ No newline at end of file +} diff --git a/prover/bin/challenge/src/lib.rs b/prover/bin/challenge/src/lib.rs index 92477d8b2..d5001864c 100644 --- a/prover/bin/challenge/src/lib.rs +++ b/prover/bin/challenge/src/lib.rs @@ -1,6 +1,6 @@ pub mod abi; +mod external_sign; pub mod handler; pub mod metrics; +pub mod rollup_compat; pub mod util; -mod external_sign; - diff --git a/prover/bin/challenge/src/rollup_compat.rs b/prover/bin/challenge/src/rollup_compat.rs new file mode 100644 index 000000000..a55432706 --- /dev/null +++ b/prover/bin/challenge/src/rollup_compat.rs @@ -0,0 +1,253 @@ +use ethers::{ + abi::AbiDecode, + providers::Middleware, + types::{BlockId, BlockNumber, Bytes, Filter, Log, TxHash, H256, U256, U64}, + utils::keccak256, +}; +use eyre::{eyre, Result}; + +use crate::abi::{ + pre_submitter_rollup_abi::{ + CommitBatchCall as PreSubmitterCommitBatchCall, CommitBatchWithProofCall as PreSubmitterCommitBatchWithProofCall, + CommitStateCall as PreSubmitterCommitStateCall, + }, + rollup_abi::{CommitBatchCall, CommitBatchWithProofCall, CommitStateCall, Rollup}, +}; + +#[derive(Clone, Copy, Debug, Eq, PartialEq)] +pub enum SubmissionKind { + CommitBatch, + CommitState, + CommitBatchWithProof, +} + +#[derive(Clone, Debug, Eq, PartialEq)] +pub struct BatchDataInput { + pub version: u8, + pub parent_batch_header: Bytes, + pub last_block_number: u64, + pub num_l1_messages: u16, + pub prev_state_root: [u8; 32], + pub post_state_root: [u8; 32], + pub withdrawal_root: [u8; 32], + pub kind: SubmissionKind, + pub pre_submitter: bool, +} + +macro_rules! decoded { + ($input:expr, $kind:expr, $pre:expr) => {{ + let input = $input; + BatchDataInput { + version: input.version, + parent_batch_header: input.parent_batch_header, + last_block_number: input.last_block_number, + num_l1_messages: input.num_l1_messages, + prev_state_root: input.prev_state_root, + post_state_root: input.post_state_root, + withdrawal_root: input.withdrawal_root, + kind: $kind, + pre_submitter: $pre, + } + }}; +} + +pub fn decode_batch_data_input(data: &[u8]) -> Result { + if data.len() < 4 { + return Err(eyre!("rollup calldata is shorter than a selector")); + } + + if let Ok(call) = CommitBatchCall::decode(data) { + return Ok(decoded!(call.batch_data_input, SubmissionKind::CommitBatch, false)); + } + if let Ok(call) = CommitStateCall::decode(data) { + return Ok(decoded!(call.batch_data_input, SubmissionKind::CommitState, false)); + } + if let Ok(call) = CommitBatchWithProofCall::decode(data) { + return Ok(decoded!(call.batch_data_input, SubmissionKind::CommitBatchWithProof, false)); + } + + if let Ok(call) = PreSubmitterCommitBatchCall::decode(data) { + return Ok(decoded!(call.batch_data_input, SubmissionKind::CommitBatch, true)); + } + if let Ok(call) = PreSubmitterCommitStateCall::decode(data) { + return Ok(decoded!(call.batch_data_input, SubmissionKind::CommitState, true)); + } + if let Ok(call) = PreSubmitterCommitBatchWithProofCall::decode(data) { + return Ok(decoded!(call.batch_data_input, SubmissionKind::CommitBatchWithProof, true)); + } + + Err(eyre!("unsupported rollup submission selector")) +} + +#[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd)] +pub struct LogIdentity { + pub block_number: u64, + pub transaction_index: u64, + pub log_index: u64, +} + +#[derive(Clone, Copy, Debug, Eq, PartialEq)] +pub struct CanonicalCommit { + pub identity: LogIdentity, + pub transaction_hash: TxHash, + pub block_hash: H256, + pub batch_hash: H256, +} + +fn identity(log: &Log) -> Option { + Some(LogIdentity { + block_number: log.block_number?.as_u64(), + transaction_index: log.transaction_index?.as_u64(), + log_index: log.log_index?.as_u64(), + }) +} + +pub fn select_canonical_commit(commits: &[Log], reverts: &[Log], canonical_hash: H256) -> Option { + if canonical_hash.is_zero() { + return None; + } + let last_revert = reverts.iter().filter(|log| !log.removed.unwrap_or_default()).filter_map(identity).max(); + + commits + .iter() + .filter(|log| !log.removed.unwrap_or_default()) + .filter_map(|log| { + let identity = identity(log)?; + if last_revert.is_some_and(|revert| identity <= revert) { + return None; + } + if log.topics.len() < 3 || log.topics[2] != canonical_hash { + return None; + } + Some(CanonicalCommit { + identity, + transaction_hash: log.transaction_hash?, + block_hash: log.block_hash?, + batch_hash: log.topics[2], + }) + }) + .max_by_key(|commit| commit.identity) +} + +const QUERY_RANGE: u64 = 10_000; + +pub async fn resolve_canonical_commit( + rollup: &Rollup, + provider: &P, + batch_index: u64, + rollup_deployed_block: u64, + snapshot_number: U64, +) -> Result> +where + M: Middleware + 'static, + P: Middleware + 'static, +{ + if rollup_deployed_block == 0 || rollup_deployed_block > snapshot_number.as_u64() { + return Err(eyre!( + "invalid Rollup deployment block {rollup_deployed_block} for snapshot {snapshot_number}" + )); + } + let before = provider + .get_block(snapshot_number) + .await? + .ok_or_else(|| eyre!("snapshot block {} not found", snapshot_number))?; + let before_hash = before.hash.ok_or_else(|| eyre!("snapshot block has no hash"))?; + let block_id = BlockId::Number(BlockNumber::Number(snapshot_number)); + let canonical_hash: H256 = rollup.committed_batches(U256::from(batch_index)).block(block_id).call().await?.into(); + if canonical_hash.is_zero() { + return Ok(None); + } + + let commit_topic = H256::from(keccak256("CommitBatch(uint256,bytes32)")); + let revert_topic = H256::from(keccak256("RevertBatch(uint256,bytes32)")); + let mut commits = Vec::new(); + let mut reverts = Vec::new(); + let mut from = rollup_deployed_block; + while from <= snapshot_number.as_u64() { + let to = from.saturating_add(QUERY_RANGE - 1).min(snapshot_number.as_u64()); + let base = Filter::new() + .address(rollup.address()) + .from_block(from) + .to_block(to) + .topic1(U256::from(batch_index)); + commits.extend(provider.get_logs(&base.clone().topic0(commit_topic)).await?); + reverts.extend(provider.get_logs(&base.topic0(revert_topic)).await?); + if to == snapshot_number.as_u64() { + break; + } + from = to + 1; + } + + let after = provider + .get_block(snapshot_number) + .await? + .ok_or_else(|| eyre!("snapshot block {} disappeared", snapshot_number))?; + if after.hash != Some(before_hash) { + return Err(eyre!("snapshot block hash changed during canonical resolution")); + } + + Ok(select_canonical_commit(&commits, &reverts, canonical_hash)) +} + +#[cfg(test)] +mod tests { + use super::*; + + fn log(block: u64, tx: u64, index: u64, hash: H256, marker: u64) -> Log { + Log { + block_number: Some(block.into()), + transaction_index: Some(tx.into()), + log_index: Some(index.into()), + transaction_hash: Some(H256::from_low_u64_be(marker)), + block_hash: Some(H256::from_low_u64_be(block)), + topics: vec![H256::zero(), H256::from_low_u64_be(7), hash], + ..Default::default() + } + } + + #[test] + fn rejects_short_and_unknown_calldata() { + assert!(decode_batch_data_input(&[]).is_err()); + assert!(decode_batch_data_input(&[0xde, 0xad, 0xbe, 0xef]).is_err()); + } + + #[test] + fn decodes_shared_pre_and_post_submitter_fixtures() { + let corpus: serde_json::Value = + serde_json::from_str(include_str!("../../../../node/derivation/testdata/rollup_calldata_fixtures.json")).unwrap(); + let mut decoded_count = 0; + for fixture in corpus["fixtures"].as_array().unwrap() { + let epoch = fixture["epoch"].as_str().unwrap(); + if epoch != "pre-submitter" && epoch != "current" { + continue; + } + let data = ethers::utils::hex::decode(fixture["data"].as_str().unwrap().trim_start_matches("0x")).unwrap(); + let decoded = decode_batch_data_input(&data).unwrap_or_else(|err| panic!("{}: {err:#}", fixture["name"])); + assert_eq!(decoded.version as u64, fixture["expected"]["version"].as_u64().unwrap()); + assert_eq!(decoded.last_block_number, fixture["expected"]["lastBlockNumber"].as_u64().unwrap()); + decoded_count += 1; + } + assert_eq!(decoded_count, 6); + } + + #[test] + fn same_hash_recommit_after_revert_is_selected_by_identity() { + let hash = H256::from_low_u64_be(99); + let resolved = select_canonical_commit(&[log(10, 0, 0, hash, 1), log(12, 0, 0, hash, 3)], &[log(11, 0, 0, hash, 2)], hash).unwrap(); + assert_eq!(resolved.transaction_hash, H256::from_low_u64_be(3)); + } + + #[test] + fn zero_hash_and_same_block_revert_order_fail_closed() { + let hash = H256::from_low_u64_be(99); + assert!(select_canonical_commit(&[log(10, 0, 0, hash, 1)], &[], H256::zero()).is_none()); + + let before_revert = log(10, 0, 1, hash, 1); + let revert = log(10, 0, 2, hash, 2); + assert!(select_canonical_commit(&[before_revert], std::slice::from_ref(&revert), hash).is_none()); + + let after_revert = log(10, 0, 3, hash, 3); + let resolved = select_canonical_commit(&[after_revert], &[revert], hash).unwrap(); + assert_eq!(resolved.transaction_hash, H256::from_low_u64_be(3)); + } +} diff --git a/prover/bin/challenge/src/util.rs b/prover/bin/challenge/src/util.rs index 3cfb7432c..d48aa397f 100644 --- a/prover/bin/challenge/src/util.rs +++ b/prover/bin/challenge/src/util.rs @@ -47,6 +47,7 @@ pub fn read_parse_env(var_name: &'static str) -> T { } #[tokio::test] +#[ignore = "requires HANDLER_PROVER_RPC and an external prover service"] async fn test_call_prover() { env_logger::Builder::from_env(env_logger::Env::default().default_filter_or("info")).init(); dotenv::dotenv().ok(); diff --git a/prover/bin/shadow-prove/.env.dev b/prover/bin/shadow-prove/.env.dev index 25b52d157..4900af649 100644 --- a/prover/bin/shadow-prove/.env.dev +++ b/prover/bin/shadow-prove/.env.dev @@ -4,7 +4,10 @@ SHADOW_PROVING_L2_RPC = "http://localhost:8545" SHADOW_PROVING_PROVER_RPC ="http://localhost:3030" SHADOW_PROVING_L1_ROLLUP = "0x5FbDB2315678afecb367f032d93F642f64180aa3" SHADOW_PROVING_L1_SHADOW_ROLLUP = "0x5FbDB2315678afecb367f032d93F642f64180aa3" +# Development fixture only. Production must use the archived Rollup proxy +# deployment block from the deployment manifest. +SHADOW_PROVING_L1_ROLLUP_DEPLOY_BLOCK = 1 SHADOW_PROVING_PRIVATE_KEY = "0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80" SHADOW_PROVING_MAX_TXN = 600 SHADOW_PROVING_MAX_BLOCK = 300 -SHADOW_PROVING_BATCH_INTERVAL = 7200 \ No newline at end of file +SHADOW_PROVING_BATCH_INTERVAL = 7200 diff --git a/prover/bin/shadow-prove/abi/Rollup.json b/prover/bin/shadow-prove/abi/Rollup.json index e32ca8619..e9da0e250 100644 --- a/prover/bin/shadow-prove/abi/Rollup.json +++ b/prover/bin/shadow-prove/abi/Rollup.json @@ -217,6 +217,25 @@ "name": "RevertBatch", "type": "event" }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "oldAddr", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "newAddr", + "type": "address" + } + ], + "name": "SubmitterContractUpdated", + "type": "event" + }, { "anonymous": false, "inputs": [ @@ -383,6 +402,25 @@ "stateMutability": "nonpayable", "type": "function" }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "batchIndex", + "type": "uint256" + } + ], + "name": "batchBlobVersionedHashes", + "outputs": [ + { + "internalType": "bytes32", + "name": "blobVersionedHash", + "type": "bytes32" + } + ], + "stateMutability": "view", + "type": "function" + }, { "inputs": [ { @@ -460,9 +498,9 @@ "type": "uint256" }, { - "internalType": "uint256", - "name": "signedSequencersBitmap", - "type": "uint256" + "internalType": "address", + "name": "submitter", + "type": "address" } ], "stateMutability": "view", @@ -656,28 +694,6 @@ "internalType": "struct IRollup.BatchDataInput", "name": "batchDataInput", "type": "tuple" - }, - { - "components": [ - { - "internalType": "uint256", - "name": "signedSequencersBitmap", - "type": "uint256" - }, - { - "internalType": "bytes", - "name": "sequencerSets", - "type": "bytes" - }, - { - "internalType": "bytes", - "name": "signature", - "type": "bytes" - } - ], - "internalType": "struct IRollup.BatchSignatureInput", - "name": "batchSignatureInput", - "type": "tuple" } ], "name": "commitBatch", @@ -729,40 +745,68 @@ "name": "batchDataInput", "type": "tuple" }, + { + "internalType": "bytes", + "name": "_batchHeader", + "type": "bytes" + }, + { + "internalType": "bytes", + "name": "_batchProof", + "type": "bytes" + } + ], + "name": "commitBatchWithProof", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ { "components": [ { - "internalType": "uint256", - "name": "signedSequencersBitmap", - "type": "uint256" + "internalType": "uint8", + "name": "version", + "type": "uint8" }, { "internalType": "bytes", - "name": "sequencerSets", + "name": "parentBatchHeader", "type": "bytes" }, { - "internalType": "bytes", - "name": "signature", - "type": "bytes" + "internalType": "uint64", + "name": "lastBlockNumber", + "type": "uint64" + }, + { + "internalType": "uint16", + "name": "numL1Messages", + "type": "uint16" + }, + { + "internalType": "bytes32", + "name": "prevStateRoot", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "postStateRoot", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "withdrawalRoot", + "type": "bytes32" } ], - "internalType": "struct IRollup.BatchSignatureInput", - "name": "batchSignatureInput", + "internalType": "struct IRollup.BatchDataInput", + "name": "batchDataInput", "type": "tuple" - }, - { - "internalType": "bytes", - "name": "_batchHeader", - "type": "bytes" - }, - { - "internalType": "bytes", - "name": "_batchProof", - "type": "bytes" } ], - "name": "commitBatchWithProof", + "name": "commitState", "outputs": [], "stateMutability": "nonpayable", "type": "function" @@ -880,7 +924,7 @@ "inputs": [ { "internalType": "address", - "name": "_l1StakingContract", + "name": "_submitterContract", "type": "address" }, { @@ -940,6 +984,19 @@ "stateMutability": "nonpayable", "type": "function" }, + { + "inputs": [ + { + "internalType": "address", + "name": "_newSubmitter", + "type": "address" + } + ], + "name": "initialize4", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, { "inputs": [ { @@ -978,19 +1035,6 @@ "stateMutability": "view", "type": "function" }, - { - "inputs": [], - "name": "l1StakingContract", - "outputs": [ - { - "internalType": "address", - "name": "", - "type": "address" - } - ], - "stateMutability": "view", - "type": "function" - }, { "inputs": [], "name": "lastCommittedBatchIndex", @@ -1056,6 +1100,25 @@ "stateMutability": "view", "type": "function" }, + { + "inputs": [ + { + "internalType": "address", + "name": "submitter", + "type": "address" + } + ], + "name": "pendingBatchCount", + "outputs": [ + { + "internalType": "uint256", + "name": "count", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, { "inputs": [], "name": "proofRewardPercent", @@ -1208,6 +1271,19 @@ "stateMutability": "nonpayable", "type": "function" }, + { + "inputs": [], + "name": "submitterContract", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, { "inputs": [ { @@ -1322,4 +1398,4 @@ "stateMutability": "payable", "type": "receive" } -] \ No newline at end of file +] diff --git a/prover/bin/shadow-prove/src/abi.rs b/prover/bin/shadow-prove/src/abi.rs index d98d7b99d..17a583a48 100644 --- a/prover/bin/shadow-prove/src/abi.rs +++ b/prover/bin/shadow-prove/src/abi.rs @@ -7,6 +7,16 @@ sol!( "abi/Rollup.json" ); +// Immutable production ABI from immediately before the Submitter cutover. +// Keep it in a namespace of its own because both ABI snapshots contain an +// `IRollup.BatchDataInput` internal type. +pub mod pre_submitter { + use alloy_sol_types::sol; + + sol!(PreSubmitterRollup, "abi/pre_submitter/Rollup.json"); +} +pub use pre_submitter::PreSubmitterRollup; + sol!( #[sol(rpc)] ShadowRollup, diff --git a/prover/bin/shadow-prove/src/execute.rs b/prover/bin/shadow-prove/src/execute.rs index 1a5142e6e..b063b747f 100644 --- a/prover/bin/shadow-prove/src/execute.rs +++ b/prover/bin/shadow-prove/src/execute.rs @@ -124,6 +124,7 @@ mod tests { // cargo test -p shadow-proving --lib -- execute::tests::test_execute_range --exact --nocapture -- --start-block 0x35 --end-block 0x36 --rpc http://127.0.0.1:9545 #[test] + #[ignore = "requires an external L2 RPC and explicit CLI fixture arguments"] fn test_execute_range() { env_logger::Builder::new().filter_level(log::LevelFilter::Info).format_target(false).init(); @@ -138,6 +139,7 @@ mod tests { // cargo test -p shadow-proving --lib -- execute::tests::test_execute_block --exact --nocapture -- --block-number 19997 --rpc http://127.0.0.1:9545 #[tokio::test(flavor = "multi_thread")] + #[ignore = "requires an external L2 RPC and explicit CLI fixture arguments"] async fn test_execute_block() { env_logger::Builder::new().filter_level(log::LevelFilter::Info).format_target(false).init(); @@ -152,6 +154,7 @@ mod tests { } #[tokio::test] + #[ignore = "requires a local L2 RPC fixture"] async fn test_execute_batch() { env_logger::Builder::new().filter_level(log::LevelFilter::Info).format_target(false).init(); let handle = tokio::spawn(async move { @@ -171,6 +174,7 @@ mod tests { handle.await.unwrap(); } #[tokio::test] + #[ignore = "requires a local L2 RPC fixture"] async fn test_execute_remote() { env_logger::Builder::new().filter_level(log::LevelFilter::Info).format_target(false).init(); let provider = @@ -184,8 +188,10 @@ mod tests { println!("batch_info.post_state_root: {:?}", batch_info.post_state_root); } - // cargo test --package shadow-proving --lib -- execute::tests::test_execute_local --exact --nocapture + // cargo test --package shadow-proving --lib -- execute::tests::test_execute_local --exact + // --nocapture #[tokio::test] + #[ignore = "requires pre-generated local executor fixtures"] async fn test_execute_local() { env_logger::Builder::new().filter_level(log::LevelFilter::Info).format_target(false).init(); let dir = "../../testdata/state"; @@ -209,6 +215,7 @@ mod tests { } #[tokio::test] + #[ignore = "requires a production L2 RPC and writes a local fixture"] async fn execute_batch_input() { env_logger::Builder::new().filter_level(log::LevelFilter::Info).format_target(false).init(); let provider = ProviderBuilder::new() @@ -259,6 +266,7 @@ mod tests { } #[tokio::test] + #[ignore = "requires a pre-generated local executor fixture"] async fn execute_batch_input_local() { env_logger::Builder::new().filter_level(log::LevelFilter::Info).format_target(false).init(); let file = File::open("proof/shadow_input.json").unwrap(); diff --git a/prover/bin/shadow-prove/src/lib.rs b/prover/bin/shadow-prove/src/lib.rs index 62e485968..3766143bf 100644 --- a/prover/bin/shadow-prove/src/lib.rs +++ b/prover/bin/shadow-prove/src/lib.rs @@ -8,6 +8,7 @@ use crate::util::read_env_var; pub mod abi; pub mod execute; pub mod metrics; +pub mod rollup_compat; pub mod shadow_prove; pub mod shadow_rollup; pub mod util; diff --git a/prover/bin/shadow-prove/src/main.rs b/prover/bin/shadow-prove/src/main.rs index a6cf82946..e31d1139b 100644 --- a/prover/bin/shadow-prove/src/main.rs +++ b/prover/bin/shadow-prove/src/main.rs @@ -22,8 +22,10 @@ use shadow_proving::{ SHADOW_PROVING_MAX_TXN, SHADOW_PROVING_PROVER_RPC, }; -use tokio::time::{interval, sleep}; -use tokio::{sync::broadcast, time::MissedTickBehavior}; +use tokio::{ + sync::broadcast, + time::{interval, sleep, MissedTickBehavior}, +}; #[tokio::main] async fn main() { @@ -157,6 +159,7 @@ fn init_shadow_proving( let private_key: String = read_parse_env("SHADOW_PROVING_PRIVATE_KEY"); let rollup: String = read_parse_env("SHADOW_PROVING_L1_ROLLUP"); let shadow_rollup: String = read_parse_env("SHADOW_PROVING_L1_SHADOW_ROLLUP"); + let rollup_deployed_block: u64 = read_parse_env("SHADOW_PROVING_L1_ROLLUP_DEPLOY_BLOCK"); let signer: PrivateKeySigner = private_key.parse().expect("parse PrivateKeySigner"); let wallet: EthereumWallet = EthereumWallet::from(signer.clone()); @@ -179,6 +182,7 @@ fn init_shadow_proving( l1_provider.clone(), l2_provider.clone(), l1_signer.clone(), + rollup_deployed_block, ); let shadow_prover = ShadowProver::new( @@ -305,12 +309,14 @@ fn log_format( } #[tokio::test] +#[ignore = "requires the full configured shadow-proving environment"] async fn test_shadow() { dotenv().ok(); env_logger::Builder::new().filter_level(log::LevelFilter::Info).format_target(false).init(); log::info!("Starting shadow proving..."); - // cargo test -p shadow-proving --bin shadow-proving -- test_shadow --exact --nocapture -- --batch-num 100 --no-prove + // cargo test -p shadow-proving --bin shadow-proving -- test_shadow --exact --nocapture -- + // --batch-num 100 --no-prove let (batch_num, prove) = test_args::read_shadow_test_args_from_argv(); let (batch_syncer, shadow_prover, l2_provider) = init_shadow_proving(); diff --git a/prover/bin/shadow-prove/src/rollup_compat.rs b/prover/bin/shadow-prove/src/rollup_compat.rs new file mode 100644 index 000000000..5a2fd5a3f --- /dev/null +++ b/prover/bin/shadow-prove/src/rollup_compat.rs @@ -0,0 +1,334 @@ +use alloy_primitives::{Bytes, TxHash, B256, U256}; +use alloy_provider::{DynProvider, Provider}; +use alloy_rpc_types::{BlockId, BlockNumberOrTag, Log}; +use alloy_sol_types::SolCall; +use anyhow::{anyhow, Result}; + +use crate::abi::{ + pre_submitter::IRollup as PreSubmitterIRollup, IRollup, PreSubmitterRollup, Rollup, + Rollup::RollupInstance, +}; + +#[derive(Clone, Copy, Debug, Eq, PartialEq)] +pub enum SubmissionKind { + CommitBatch, + CommitState, + CommitBatchWithProof, +} + +#[derive(Clone, Debug, Eq, PartialEq)] +pub struct BatchDataInput { + pub version: u8, + pub parent_batch_header: Bytes, + pub last_block_number: u64, + pub num_l1_messages: u16, + pub prev_state_root: [u8; 32], + pub post_state_root: [u8; 32], + pub withdrawal_root: [u8; 32], + pub kind: SubmissionKind, + pub pre_submitter: bool, +} + +#[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd)] +pub struct LogIdentity { + pub block_number: u64, + pub transaction_index: u64, + pub log_index: u64, +} + +#[derive(Clone, Copy, Debug, Eq, PartialEq)] +pub struct CanonicalCommit { + pub identity: LogIdentity, + pub transaction_hash: TxHash, + pub block_hash: B256, + pub batch_hash: B256, +} + +fn log_identity(log: &Log) -> Option { + Some(LogIdentity { + block_number: log.block_number?, + transaction_index: log.transaction_index?, + log_index: log.log_index?, + }) +} + +/// Select the last canonical Commit after the last Revert at one fixed L1 +/// snapshot. Full log identity (rather than batch hash alone) deliberately +/// distinguishes a same-hash recommit. +pub fn select_canonical_commit( + commits: &[Log], + reverts: &[Log], + canonical_hash: B256, +) -> Option { + if canonical_hash == B256::ZERO { + return None; + } + + let last_revert = reverts.iter().filter(|log| !log.removed).filter_map(log_identity).max(); + + commits + .iter() + .filter(|log| !log.removed) + .filter_map(|log| { + let identity = log_identity(log)?; + if last_revert.is_some_and(|revert| identity <= revert) { + return None; + } + let topics = log.topics(); + if topics.len() < 3 || topics[2] != canonical_hash { + return None; + } + Some(CanonicalCommit { + identity, + transaction_hash: log.transaction_hash?, + block_hash: log.block_hash?, + batch_hash: topics[2], + }) + }) + .max_by_key(|commit| commit.identity) +} + +const LOG_QUERY_BLOCK_RANGE: u64 = 10_000; + +/// Resolve a batch's canonical Commit using one numbered snapshot. The block +/// hash is checked before and after all getter/log reads so RPCs without +/// EIP-1898 support cannot silently combine different forks. +pub async fn resolve_canonical_commit( + provider: &DynProvider, + rollup: &RollupInstance, + batch_index: u64, + rollup_deployed_block: u64, + snapshot_number: u64, +) -> Result> { + if rollup_deployed_block == 0 || rollup_deployed_block > snapshot_number { + return Err(anyhow!( + "invalid Rollup deployment block {rollup_deployed_block} for snapshot {snapshot_number}" + )); + } + let snapshot_tag = BlockNumberOrTag::Number(snapshot_number); + let before = provider + .get_block_by_number(snapshot_tag) + .await? + .ok_or_else(|| anyhow!("snapshot block {snapshot_number} not found"))?; + let before_hash = before.header.hash; + + let canonical_hash = rollup + .committedBatches(U256::from(batch_index)) + .block(BlockId::Number(snapshot_tag)) + .call() + .await?; + if canonical_hash == B256::ZERO { + return Ok(None); + } + + let mut commits = Vec::new(); + let mut reverts = Vec::new(); + let mut from = rollup_deployed_block; + while from <= snapshot_number { + let to = from.saturating_add(LOG_QUERY_BLOCK_RANGE - 1).min(snapshot_number); + let commit_filter = rollup + .CommitBatch_filter() + .filter + .from_block(from) + .to_block(to) + .topic1(U256::from(batch_index)) + .address(*rollup.address()); + commits.extend(provider.get_logs(&commit_filter).await?); + + let revert_filter = rollup + .RevertBatch_filter() + .filter + .from_block(from) + .to_block(to) + .topic1(U256::from(batch_index)) + .address(*rollup.address()); + reverts.extend(provider.get_logs(&revert_filter).await?); + + if to == snapshot_number { + break; + } + from = to + 1; + } + + let after = provider + .get_block_by_number(snapshot_tag) + .await? + .ok_or_else(|| anyhow!("snapshot block {snapshot_number} disappeared"))?; + if after.header.hash != before_hash { + return Err(anyhow!("snapshot block hash changed during canonical resolution")); + } + + Ok(select_canonical_commit(&commits, &reverts, canonical_hash)) +} + +fn current_input(input: IRollup::BatchDataInput, kind: SubmissionKind) -> BatchDataInput { + BatchDataInput { + version: input.version, + parent_batch_header: input.parentBatchHeader, + last_block_number: input.lastBlockNumber, + num_l1_messages: input.numL1Messages, + prev_state_root: *input.prevStateRoot, + post_state_root: *input.postStateRoot, + withdrawal_root: *input.withdrawalRoot, + kind, + pre_submitter: false, + } +} + +fn historical_input( + input: PreSubmitterIRollup::BatchDataInput, + kind: SubmissionKind, +) -> BatchDataInput { + BatchDataInput { + version: input.version, + parent_batch_header: input.parentBatchHeader, + last_block_number: input.lastBlockNumber, + num_l1_messages: input.numL1Messages, + prev_state_root: *input.prevStateRoot, + post_state_root: *input.postStateRoot, + withdrawal_root: *input.withdrawalRoot, + kind, + pre_submitter: true, + } +} + +/// Decode only the first `BatchDataInput` argument from every submission ABI +/// supported across the Submitter cutover. Unknown and short selectors fail +/// closed; historical signature data is intentionally not exposed. +pub fn decode_batch_data_input(data: &[u8]) -> Result { + if data.len() < 4 { + return Err(anyhow!("rollup calldata is shorter than a selector")); + } + + if let Ok(call) = Rollup::commitBatchCall::abi_decode(data) { + return Ok(current_input(call.batchDataInput, SubmissionKind::CommitBatch)); + } + if let Ok(call) = Rollup::commitStateCall::abi_decode(data) { + return Ok(current_input(call.batchDataInput, SubmissionKind::CommitState)); + } + if let Ok(call) = Rollup::commitBatchWithProofCall::abi_decode(data) { + return Ok(current_input(call.batchDataInput, SubmissionKind::CommitBatchWithProof)); + } + + if let Ok(call) = PreSubmitterRollup::commitBatchCall::abi_decode(data) { + return Ok(historical_input(call.batchDataInput, SubmissionKind::CommitBatch)); + } + if let Ok(call) = PreSubmitterRollup::commitStateCall::abi_decode(data) { + return Ok(historical_input(call.batchDataInput, SubmissionKind::CommitState)); + } + if let Ok(call) = PreSubmitterRollup::commitBatchWithProofCall::abi_decode(data) { + return Ok(historical_input(call.batchDataInput, SubmissionKind::CommitBatchWithProof)); + } + + Err(anyhow!("unsupported rollup submission selector")) +} + +#[cfg(test)] +mod tests { + use super::*; + use alloy_primitives::{Address, Log as PrimitiveLog}; + + fn rpc_log( + batch_index: u64, + batch_hash: B256, + block_number: u64, + transaction_index: u64, + log_index: u64, + tx_marker: u8, + ) -> Log { + Log { + inner: PrimitiveLog::new_unchecked( + Address::ZERO, + vec![ + B256::ZERO, + B256::from(U256::from(batch_index).to_be_bytes::<32>()), + batch_hash, + ], + Bytes::new(), + ), + block_number: Some(block_number), + transaction_index: Some(transaction_index), + log_index: Some(log_index), + transaction_hash: Some(B256::repeat_byte(tx_marker)), + block_hash: Some(B256::repeat_byte(block_number as u8)), + ..Default::default() + } + } + + #[test] + fn rejects_short_and_unknown_calldata() { + assert!(decode_batch_data_input(&[]).is_err()); + assert!(decode_batch_data_input(&[0xde, 0xad, 0xbe, 0xef]).is_err()); + } + + #[test] + fn decodes_shared_pre_and_post_submitter_fixtures() { + let corpus: serde_json::Value = serde_json::from_str(include_str!( + "../../../../node/derivation/testdata/rollup_calldata_fixtures.json" + )) + .unwrap(); + let mut decoded_count = 0; + for fixture in corpus["fixtures"].as_array().unwrap() { + let epoch = fixture["epoch"].as_str().unwrap(); + if epoch != "pre-submitter" && epoch != "current" { + continue; + } + let data = alloy_primitives::hex::decode( + fixture["data"].as_str().unwrap().trim_start_matches("0x"), + ) + .unwrap(); + let decoded = decode_batch_data_input(&data) + .unwrap_or_else(|err| panic!("{}: {err:#}", fixture["name"])); + assert_eq!(decoded.version as u64, fixture["expected"]["version"].as_u64().unwrap()); + assert_eq!( + decoded.last_block_number, + fixture["expected"]["lastBlockNumber"].as_u64().unwrap() + ); + decoded_count += 1; + } + assert_eq!(decoded_count, 6); + } + + #[test] + fn selector_sets_are_distinct() { + assert_eq!(Rollup::commitBatchCall::SELECTOR, [0x41, 0xf7, 0x56, 0xda]); + assert_eq!(Rollup::commitStateCall::SELECTOR, [0x67, 0xca, 0xa3, 0x7a]); + assert_eq!(Rollup::commitBatchWithProofCall::SELECTOR, [0x15, 0x44, 0xba, 0x3a]); + + assert_eq!(PreSubmitterRollup::commitBatchCall::SELECTOR, [0x42, 0x88, 0x68, 0xb5]); + assert_eq!(PreSubmitterRollup::commitStateCall::SELECTOR, [0x1e, 0x88, 0x25, 0xbe]); + assert_eq!( + PreSubmitterRollup::commitBatchWithProofCall::SELECTOR, + [0x4e, 0x8f, 0x1d, 0x67] + ); + } + + #[test] + fn recommit_after_revert_wins_even_when_hash_is_identical() { + let hash = B256::repeat_byte(0x44); + let first = rpc_log(7, hash, 10, 0, 0, 1); + let revert = rpc_log(7, hash, 11, 0, 0, 2); + let second = rpc_log(7, hash, 12, 0, 0, 3); + + let resolved = select_canonical_commit(&[first, second], &[revert], hash).unwrap(); + assert_eq!(resolved.transaction_hash, B256::repeat_byte(3)); + assert_eq!(resolved.identity.block_number, 12); + } + + #[test] + fn zero_hash_and_same_block_revert_order_fail_closed() { + let hash = B256::repeat_byte(0x44); + assert!( + select_canonical_commit(&[rpc_log(7, hash, 10, 0, 0, 1)], &[], B256::ZERO).is_none() + ); + + let before_revert = rpc_log(7, hash, 10, 0, 1, 1); + let revert = rpc_log(7, hash, 10, 0, 2, 2); + assert!(select_canonical_commit(&[before_revert], std::slice::from_ref(&revert), hash) + .is_none()); + + let after_revert = rpc_log(7, hash, 10, 0, 3, 3); + let resolved = select_canonical_commit(&[after_revert], &[revert], hash).unwrap(); + assert_eq!(resolved.transaction_hash, B256::repeat_byte(3)); + } +} diff --git a/prover/bin/shadow-prove/src/shadow_prove.rs b/prover/bin/shadow-prove/src/shadow_prove.rs index ad53a2e76..cd0dee8cd 100644 --- a/prover/bin/shadow-prove/src/shadow_prove.rs +++ b/prover/bin/shadow-prove/src/shadow_prove.rs @@ -39,7 +39,8 @@ mod task_status { pub const PROVED: &str = "Proved"; } -// ShadowProver is responsible for proving the batch state onchain through the shadow rollup contract. +// ShadowProver is responsible for proving the batch state onchain through the shadow rollup +// contract. #[derive(Clone, Debug)] pub struct ShadowProver { shadow_provider: DynProvider, @@ -284,14 +285,24 @@ async fn query_proof(batch_index: u64) -> Option { fn test() { use crate::abi::SP1Verifier; use alloy_json_rpc::ErrorPayload; - // Sample JSON error payload from an Ethereum JSON RPC response. - let json = r#"{"code":3,"message":"execution reverted: ","data":"0x810f00230000000000000000000000000000000000000000000000000000000000000001"}"#; + use alloy_primitives::hex; + use alloy_sol_types::SolError; + + let expected_error = SP1Verifier::WrongVerifierSelector { + received: [0, 0, 0, 1].into(), + expected: [0, 0, 0, 2].into(), + }; + let json = format!( + r#"{{"code":3,"message":"execution reverted: ","data":"{}"}}"#, + hex::encode_prefixed(expected_error.abi_encode()) + ); // Parse the JSON into an `ErrorPayload` struct. - let payload: ErrorPayload = serde_json::from_str(json).unwrap(); + let payload: ErrorPayload = serde_json::from_str(&json).unwrap(); if let Some(s) = payload.as_decoded_error::() { - println!("WrongVerifierSelector - expected: {:?}, received: {:?}", s.expected, s.received); + assert_eq!(s.expected, expected_error.expected); + assert_eq!(s.received, expected_error.received); } else if payload.as_decoded_error::().is_some() { println!("WrongVerifierSelector"); } else { diff --git a/prover/bin/shadow-prove/src/shadow_rollup.rs b/prover/bin/shadow-prove/src/shadow_rollup.rs index df1f7292c..3aa4f4c55 100644 --- a/prover/bin/shadow-prove/src/shadow_rollup.rs +++ b/prover/bin/shadow-prove/src/shadow_rollup.rs @@ -1,15 +1,17 @@ -use crate::{metrics::METRICS, BatchInfo, SHADOW_PROVING_BLOCKS_RANGE}; +use crate::{metrics::METRICS, BatchInfo}; use alloy_consensus::Transaction; use alloy_network::{Network, ReceiptResponse}; -use alloy_primitives::{hex, Address, Bytes, Keccak256, TxHash, B256, U256, U64}; +use alloy_primitives::{hex, Address, Bytes, Keccak256, TxHash, B256, U256}; use alloy_provider::{DynProvider, Provider}; -use alloy_rpc_types::Log; -use alloy_sol_types::SolCall; +use alloy_rpc_types::{BlockId, BlockNumberOrTag}; use anyhow::{anyhow, Context, Result}; use futures::future::join_all; use crate::{ + rollup_compat::{ + decode_batch_data_input, resolve_canonical_commit, BatchDataInput, CanonicalCommit, + }, Rollup::{self, RollupInstance}, ShadowRollup::{self, ShadowRollupInstance}, }; @@ -20,6 +22,7 @@ pub struct BatchSyncer { l2_provider: DynProvider, l1_rollup: RollupInstance, l1_shadow_rollup: ShadowRollupInstance, + rollup_deployed_block: u64, } impl BatchSyncer @@ -27,264 +30,140 @@ where P: Provider + Clone, N: Network, { + async fn finalized_snapshot(&self) -> Result<(u64, B256)> { + let block = self + .l1_provider + .get_block_by_number(BlockNumberOrTag::Finalized) + .await? + .ok_or_else(|| anyhow!("finalized L1 block is unavailable"))?; + Ok((block.header.number, block.header.hash)) + } + + async fn assert_snapshot(&self, number: u64, expected_hash: B256) -> Result<()> { + let block = self + .l1_provider + .get_block_by_number(BlockNumberOrTag::Number(number)) + .await? + .ok_or_else(|| anyhow!("snapshot L1 block {number} is unavailable"))?; + if block.header.hash != expected_hash { + return Err(anyhow!( + "finalized L1 snapshot changed: number={number}, expected={expected_hash:?}, actual={:?}", + block.header.hash + )); + } + Ok(()) + } + pub fn new( rollup_address: Address, shadow_rollup_address: Address, l1_provider: DynProvider, l2_provider: DynProvider, wallet: P, + rollup_deployed_block: u64, ) -> Self { let l1_rollup = Rollup::RollupInstance::new(rollup_address, l1_provider.clone()); let l1_shadow_rollup = ShadowRollup::new(shadow_rollup_address, wallet); - Self { l1_provider, l2_provider, l1_rollup, l1_shadow_rollup } + Self { l1_provider, l2_provider, l1_rollup, l1_shadow_rollup, rollup_deployed_block } } // Fetch the latest committed batch from l1-rollup if the batch_index has increased. pub async fn get_latest_batch(&self, batch_index: u64) -> Result> { - let last_committed_batch_index = self.l1_rollup.lastCommittedBatchIndex().call().await?; - if last_committed_batch_index <= batch_index + 1 { + let (snapshot, snapshot_hash) = self.finalized_snapshot().await?; + let last_committed_batch_index = self + .l1_rollup + .lastCommittedBatchIndex() + .block(BlockId::Number(BlockNumberOrTag::Number(snapshot))) + .call() + .await? + .to::(); + self.assert_snapshot(snapshot, snapshot_hash).await?; + if last_committed_batch_index <= batch_index.saturating_add(1) { log::info!( "The current batch_index has not increased, latest_batch_index: {last_committed_batch_index:?}" ); return Ok(None); } - self.get_committed_batch().await + if last_committed_batch_index <= 2 { + return Ok(None); + } + self.get_specified_batch_at(last_committed_batch_index - 1, snapshot, snapshot_hash).await } // Fetch the latest committed batch from l1-rollup. pub async fn get_committed_batch(&self) -> Result> { - let latest = match self.l1_provider.get_block_number().await { - Ok(v) => U64::from(v), - Err(e) => { - log::error!("l1_provider.get_block_number error: {:?}", e); - return Err(anyhow!("l1_provider.get_block_number error")); - } - }; - - log::info!("latest l1 blocknum = {:#?}", latest); - let start = if latest > U64::from(*SHADOW_PROVING_BLOCKS_RANGE) { - latest - U64::from(*SHADOW_PROVING_BLOCKS_RANGE) - } else { - U64::from(1) - }; - let filter = self + let (snapshot, snapshot_hash) = self.finalized_snapshot().await?; + let last_committed = self .l1_rollup - .CommitBatch_filter() - .filter - .from_block(start) - .address(*self.l1_rollup.address()); - let mut logs: Vec = match self.l1_provider.get_logs(&filter).await { - Ok(logs) => logs, - Err(e) => { - log::error!("l1_rollup.commit_batch.get_logs error: {:#?}", e); - return Err(anyhow!("l1_rollup.commit_batch.get_logs provider error")); - } - }; - if logs.is_empty() { - log::warn!("There have been no commit_batch logs for the last 600 blocks"); - return Ok(None); - } - if logs.len() < 3 { - log::warn!("No enough commit_batch logs for the last 600 blocks"); + .lastCommittedBatchIndex() + .block(BlockId::Number(BlockNumberOrTag::Number(snapshot))) + .call() + .await?; + self.assert_snapshot(snapshot, snapshot_hash).await?; + let last_committed = last_committed.to::(); + if last_committed <= 2 { return Ok(None); } - logs.sort_by(|a, b| a.block_number.unwrap().cmp(&b.block_number.unwrap())); - - let batch_index_hash = match logs.get(logs.len() - 2) { - Some(log) => { - let _index = U256::from_be_slice(log.topics()[1].as_slice()); - (_index.to::(), log.transaction_hash.unwrap_or_default()) - } - None => { - return Err(anyhow!("find commit_batch log error")); - } - }; - - if batch_index_hash.0 == 0 { - return Err(anyhow!("batch_index is 0")); - } - let prev_tx_hash = match logs.get(logs.len() - 3) { - Some(log) => log.transaction_hash.unwrap_or_default(), - None => { - return Err(anyhow!("find commit_batch log error")); - } - }; - - let (blocks, total_txn_count) = - match self.batch_blocks_inspect(prev_tx_hash, batch_index_hash.1).await { - Some(block_txn) => block_txn, - None => return Err(anyhow!("batch_blocks_inspect none")), - }; - - if blocks.0 >= blocks.1 { - return Err(anyhow!("blocks is empty")); - } - - let batch_info: BatchInfo = BatchInfo { - batch_index: batch_index_hash.0, - start_block: blocks.0, - end_block: blocks.1, - total_txn: total_txn_count, - }; - - // A rollup commit_batch_input contains prev batch_header. - let next_tx_hash = match logs.last() { - Some(log) => log.transaction_hash.unwrap_or_default(), - - None => { - return Err(anyhow!("find commit_batch log error")); - } - }; - let batch_input = batch_input_inspect(&self.l1_provider, next_tx_hash) - .await - .ok_or_else(|| anyhow!("Failed to inspect batch header"))?; - - log::info!("Found the committed batch, batch index = {:#?}", batch_index_hash.0); - Ok(Some((batch_info, batch_input.0))) + // The next canonical commit carries the target batch header as its + // parentBatchHeader, so prove the newest batch that has a canonical + // successor rather than relying on three adjacent logs. + self.get_specified_batch_at(last_committed - 1, snapshot, snapshot_hash).await } /// Fetch a specified batch from l1-rollup by batch_num. pub async fn get_specified_batch(&self, batch_num: u64) -> Result> { - use std::collections::{HashMap, HashSet}; + let (snapshot, snapshot_hash) = self.finalized_snapshot().await?; + self.get_specified_batch_at(batch_num, snapshot, snapshot_hash).await + } - if batch_num == 0 { - return Err(anyhow!("batch_num is 0")); - } - if batch_num == 1 { + async fn get_specified_batch_at( + &self, + batch_num: u64, + snapshot: u64, + snapshot_hash: B256, + ) -> Result> { + if batch_num <= 1 { // We need prev(batch_num-1) to infer start_block, and next(batch_num+1) to retrieve the // specified batch header (via parentBatchHeader). - return Err(anyhow!("batch_num is 1, cannot infer prev batch")); + return Err(anyhow!("batch_num must be greater than 1")); } - let last_committed_batch_index = self.l1_rollup.lastCommittedBatchIndex().call().await?; - if last_committed_batch_index <= batch_num { - log::info!( - "last committed batch index {:#?} is less than or equal to the specified batch_num {:#?}", - last_committed_batch_index, - batch_num - ); + self.assert_snapshot(snapshot, snapshot_hash).await?; + let prev = resolve_canonical_commit( + &self.l1_provider, + &self.l1_rollup, + batch_num - 1, + self.rollup_deployed_block, + snapshot, + ) + .await?; + self.assert_snapshot(snapshot, snapshot_hash).await?; + let current = resolve_canonical_commit( + &self.l1_provider, + &self.l1_rollup, + batch_num, + self.rollup_deployed_block, + snapshot, + ) + .await?; + self.assert_snapshot(snapshot, snapshot_hash).await?; + let next = resolve_canonical_commit( + &self.l1_provider, + &self.l1_rollup, + batch_num + 1, + self.rollup_deployed_block, + snapshot, + ) + .await?; + self.assert_snapshot(snapshot, snapshot_hash).await?; + let (Some(prev), Some(current), Some(next)) = (prev, current, next) else { return Ok(None); - } - - let latest = match self.l1_provider.get_block_number().await { - Ok(v) => U64::from(v), - Err(e) => { - log::error!("l1_provider.get_block_number error: {:?}", e); - return Err(anyhow!("l1_provider.get_block_number error")); - } }; - log::info!("latest l1 blocknum = {:#?}", latest); - - // Goal: collect (batch_index, tx_hash) for the specified batch and its immediate neighbors. - // - prev: batch_num - 1 (used to infer start_block) - // - curr: batch_num (used to infer end_block) - // - next: batch_num + 1 (commitBatch calldata contains parentBatchHeader == curr header) - let targets: HashSet = [batch_num - 1, batch_num, batch_num + 1].into_iter().collect(); - let mut found: HashMap = HashMap::with_capacity(3); - - let blocks_window = 600u64; - let mut distance_count = 1u64; - loop { - if distance_count > 100 { - return Err(anyhow!( - "Exceeded max distance count when searching commit_batch logs" - )); - } - // Scan backward from `latest` in fixed-size windows until all 3 targets are found. - // Note: alloy filter block numbers are U64; avoid underflow. - let to_block = latest.saturating_sub(U64::from(blocks_window * (distance_count - 1))); - let mut from_block = latest.saturating_sub(U64::from(blocks_window * distance_count)); - if from_block < U64::from(1) { - from_block = U64::from(1); - } - - let filter = self - .l1_rollup - .CommitBatch_filter() - .filter - .from_block(from_block) - .to_block(to_block) - .address(*self.l1_rollup.address()); - - let mut logs: Vec = match self.l1_provider.get_logs(&filter).await { - Ok(logs) => logs, - Err(e) => { - log::error!("l1_rollup.commit_batch.get_logs error: {:#?}", e); - return Err(anyhow!("l1_rollup.commit_batch.get_logs provider error")); - } - }; - if logs.is_empty() { - log::warn!( - "There have been no commit_batch logs for the this {} blocks window (from={:?}, to={:?})", - blocks_window, - from_block, - to_block - ); - } - - logs.sort_by(|a, b| { - a.block_number.unwrap_or_default().cmp(&b.block_number.unwrap_or_default()) - }); - - for log in logs.iter() { - if found.len() == targets.len() { - break; - } - if log.topics().len() < 2 { - continue; - } - let index_u256 = U256::from_be_slice(log.topics()[1].as_slice()); - let idx = index_u256.to::(); - if !targets.contains(&idx) { - continue; - } - // A given batch_index should appear only once; if duplicated events exist, keep the - // first-seen tx_hash. - found.entry(idx).or_insert(log.transaction_hash.unwrap_or_default()); - } - - if found.len() == targets.len() { - break; - } - - // Reached the chain head (block 1) and still not enough logs found. - if from_block == U64::from(1) { - log::warn!( - "Cannot find enough commit_batch logs for batch_num={} (found={:?})", - batch_num, - found.keys().collect::>() - ); - return Ok(None); - } - - distance_count += 1; - } - - // Assemble the triplet in order: prev, curr, next. - let mut batch_index_hash: Vec<(u64, TxHash)> = Vec::with_capacity(3); - for idx in [batch_num - 1, batch_num, batch_num + 1] { - let tx = *found.get(&idx).unwrap_or(&TxHash::ZERO); - if tx == TxHash::ZERO { - return Ok(None); - } - batch_index_hash.push((idx, tx)); - } - - let prev_tx_hash = batch_index_hash[0].1; - let curr_tx_hash = batch_index_hash[1].1; - let next_tx_hash = batch_index_hash[2].1; - - let (blocks, total_txn_count) = - match self.batch_blocks_inspect(prev_tx_hash, curr_tx_hash).await { - Some(block_txn) => block_txn, - None => return Err(anyhow!("batch_blocks_inspect none")), - }; - if blocks.0 >= blocks.1 { - return Err(anyhow!("blocks is empty")); - } + let ((blocks, total_txn_count), current_input) = + self.batch_blocks_inspect(&prev, ¤t).await?; let batch_info: BatchInfo = BatchInfo { batch_index: batch_num, @@ -294,12 +173,34 @@ where }; // next(batch_num+1) commitBatch calldata contains curr(batch_num) parentBatchHeader. - let batch_input = batch_input_inspect(&self.l1_provider, next_tx_hash) - .await - .ok_or_else(|| anyhow!("Failed to inspect batch header"))?; + let next_input = + canonical_batch_input_inspect(&self.l1_provider, &next, *self.l1_rollup.address()) + .await?; + let batch_header = next_input.parent_batch_header; + validate_canonical_header( + batch_header.as_ref(), + batch_num, + prev.batch_hash, + ¤t_input, + )?; + let mut hasher = Keccak256::new(); + hasher.update(batch_header.as_ref()); + let header_hash = hasher.finalize(); + if header_hash != current.batch_hash { + return Err(anyhow!( + "canonical batch header hash mismatch: event={:?}, calldata={:?}", + current.batch_hash, + header_hash + )); + } + self.assert_snapshot(snapshot, snapshot_hash).await?; - log::info!("Found the specified batch (prev/curr/next): {:?}", batch_index_hash); - Ok(Some((batch_info, batch_input.0))) + log::info!( + "Found canonical specified batch: batch_index={}, commit_identity={:?}", + batch_num, + current.identity + ); + Ok(Some((batch_info, batch_header))) } /** @@ -390,18 +291,30 @@ where // Inspect blocks range and total txn count in a batch. async fn batch_blocks_inspect( &self, - prev_batch_hash: TxHash, - current_batch_hash: TxHash, - ) -> Option<((u64, u64), u64)> { - let prev_batch_input = batch_input_inspect(&self.l1_provider, prev_batch_hash).await?; + prev: &CanonicalCommit, + current: &CanonicalCommit, + ) -> Result<(((u64, u64), u64), BatchDataInput)> { + let rollup_address = *self.l1_rollup.address(); + let prev_batch_input = + canonical_batch_input_inspect(&self.l1_provider, prev, rollup_address).await?; let current_batch_input = - batch_input_inspect(&self.l1_provider, current_batch_hash).await?; - let start_block = prev_batch_input.1 + 1; - let end_block = current_batch_input.1; + canonical_batch_input_inspect(&self.l1_provider, current, rollup_address).await?; + let start_block = prev_batch_input + .last_block_number + .checked_add(1) + .ok_or_else(|| anyhow!("previous batch last block overflows"))?; + let end_block = current_batch_input.last_block_number; if start_block == 0 { - log::error!("batch_blocks_inspect: start_block = 0, tx_hash = {:#?}", prev_batch_hash); - return None; + return Err(anyhow!( + "batch_blocks_inspect: start_block = 0, tx_hash = {:?}", + prev.transaction_hash + )); + } + if start_block > end_block { + return Err(anyhow!( + "batch_blocks_inspect: invalid block range {start_block}..={end_block}" + )); } let mut total_tx_count: u64 = 0; @@ -416,7 +329,9 @@ where } let results = join_all(tasks).await; for res in results { - total_tx_count += res.unwrap_or_default().unwrap_or_default(); + total_tx_count += res + .context("query L2 block transaction count")? + .ok_or_else(|| anyhow!("L2 block transaction count is unavailable"))?; } } @@ -429,7 +344,7 @@ where METRICS.shadow_txn_len.set(total_tx_count as i64); - Some(((start_block, end_block), total_tx_count)) + Ok((((start_block, end_block), total_tx_count), current_batch_input)) } // Check whether a batch has been proved successfully. @@ -509,17 +424,129 @@ pub async fn batch_input_inspect(l1_provider: &DynProvider, hash: TxHash) -> Opt log::warn!("batch inspect: tx.input is empty, tx_hash = {:#?}", hash); return None; } - let param = if let Ok(_param) = Rollup::commitBatchCall::abi_decode(data) { - _param - } else { - log::error!("batch inspect: decode tx.input error, tx_hash = {:#?}", hash); - return None; + let param = match decode_batch_data_input(data) { + Ok(param) => param, + Err(err) => { + log::error!("batch inspect: decode tx.input error, tx_hash = {:#?}", hash); + log::debug!("batch inspect decode error: {err:#}"); + return None; + } }; - let parent_batch_header: Bytes = param.batchDataInput.parentBatchHeader; - let last_block_number: u64 = param.batchDataInput.lastBlockNumber; + let parent_batch_header = param.parent_batch_header; + let last_block_number = param.last_block_number; Some((parent_batch_header, last_block_number)) } + +async fn canonical_batch_input_inspect( + l1_provider: &DynProvider, + commit: &CanonicalCommit, + rollup_address: Address, +) -> Result { + let tx = l1_provider + .get_transaction_by_hash(commit.transaction_hash) + .await? + .ok_or_else(|| anyhow!("canonical commit transaction is unavailable"))?; + if tx.to() != Some(rollup_address) || + tx.block_number != Some(commit.identity.block_number) || + tx.transaction_index != Some(commit.identity.transaction_index) || + tx.block_hash != Some(commit.block_hash) + { + return Err(anyhow!( + "canonical commit transaction identity mismatch: expected={:?}, tx_block={:?}, tx_index={:?}, tx_block_hash={:?}", + commit.identity, + tx.block_number, + tx.transaction_index, + tx.block_hash + )); + } + decode_batch_data_input(tx.input()) +} + +fn validate_canonical_header( + header: &[u8], + expected_batch_index: u64, + expected_parent_hash: B256, + input: &BatchDataInput, +) -> Result<()> { + let minimum_len = if input.version == 0 { 249 } else { 257 }; + if header.len() < minimum_len { + return Err(anyhow!( + "canonical batch header is too short: version={}, len={}", + input.version, + header.len() + )); + } + if header[0] != input.version { + return Err(anyhow!("canonical batch header version mismatch")); + } + if u64::from_be_bytes(header[1..9].try_into().unwrap()) != expected_batch_index { + return Err(anyhow!("canonical batch header index mismatch")); + } + if header[89..121] != input.prev_state_root || + header[121..153] != input.post_state_root || + header[153..185] != input.withdrawal_root + { + return Err(anyhow!("canonical batch header root mismatch")); + } + if header[217..249] != expected_parent_hash[..] { + return Err(anyhow!("canonical batch header parent hash mismatch")); + } + if input.version > 0 && + u64::from_be_bytes(header[249..257].try_into().unwrap()) != input.last_block_number + { + return Err(anyhow!("canonical batch header last block mismatch")); + } + Ok(()) +} + +#[cfg(test)] +mod canonical_header_tests { + use super::*; + use crate::rollup_compat::SubmissionKind; + + fn fixture() -> (Vec, BatchDataInput, B256) { + let parent_hash = B256::repeat_byte(0x44); + let input = BatchDataInput { + version: 2, + parent_batch_header: Bytes::new(), + last_block_number: 123, + num_l1_messages: 0, + prev_state_root: [0x11; 32], + post_state_root: [0x22; 32], + withdrawal_root: [0x33; 32], + kind: SubmissionKind::CommitBatch, + pre_submitter: false, + }; + let mut header = vec![0u8; 257]; + header[0] = input.version; + header[1..9].copy_from_slice(&7u64.to_be_bytes()); + header[89..121].copy_from_slice(&input.prev_state_root); + header[121..153].copy_from_slice(&input.post_state_root); + header[153..185].copy_from_slice(&input.withdrawal_root); + header[217..249].copy_from_slice(parent_hash.as_slice()); + header[249..257].copy_from_slice(&input.last_block_number.to_be_bytes()); + (header, input, parent_hash) + } + + #[test] + fn canonical_header_matches_calldata_roots_parent_and_range() { + let (header, input, parent_hash) = fixture(); + validate_canonical_header(&header, 7, parent_hash, &input).unwrap(); + } + + #[test] + fn canonical_header_mismatch_fails_closed() { + let (mut header, input, parent_hash) = fixture(); + header[121] ^= 1; + assert!(validate_canonical_header(&header, 7, parent_hash, &input).is_err()); + + let (mut header, input, parent_hash) = fixture(); + header[256] ^= 1; + assert!(validate_canonical_header(&header, 7, parent_hash, &input).is_err()); + } +} #[tokio::test] +#[ignore = "requires configured L1/L2 RPCs and a funded signer"] async fn test_sync_batch() { use alloy_network::EthereumWallet; use alloy_primitives::Address; @@ -546,6 +573,10 @@ async fn test_sync_batch() { let rollup = var("SHADOW_PROVING_L1_ROLLUP").expect("Cannot detect L1_ROLLUP env var"); let shadow_rollup = var("SHADOW_PROVING_L1_SHADOW_ROLLUP").expect("Cannot detect L1_SHADOW_ROLLUP env var"); + let rollup_deployed_block = var("SHADOW_PROVING_L1_ROLLUP_DEPLOY_BLOCK") + .unwrap_or_else(|_| "1".to_string()) + .parse() + .unwrap(); let l1_signer = ProviderBuilder::new().wallet(wallet).connect_http(l1_rpc.parse().unwrap()); @@ -555,12 +586,14 @@ async fn test_sync_batch() { l1_provider, l2_provider, l1_signer, + rollup_deployed_block, ); let batch = bs.get_committed_batch().await.unwrap().unwrap(); bs.sync_batch(batch.0, batch.1).await.unwrap(); } #[tokio::test] +#[ignore = "requires an authenticated historical L1 RPC fixture"] async fn test_inspect_batch_header() { use alloy_primitives::B256; use alloy_provider::ProviderBuilder; diff --git a/prover/bin/shadow-prove/src/util.rs b/prover/bin/shadow-prove/src/util.rs index 4858f7e3b..9aa8aaf64 100644 --- a/prover/bin/shadow-prove/src/util.rs +++ b/prover/bin/shadow-prove/src/util.rs @@ -50,6 +50,7 @@ pub fn read_parse_env(var_name: &'static str) -> T { } #[tokio::test] +#[ignore = "requires SHADOW_PROVING_PROVER_RPC and an external prover service"] async fn test_call_prover() { env_logger::Builder::from_env(env_logger::Env::default().default_filter_or("info")).init(); dotenv::dotenv().ok(); From b9bf687e0573c2aadbcc5e2330e6a46fd75b4fee Mon Sep 17 00:00:00 2001 From: Segue Date: Tue, 11 Aug 2026 20:52:59 +0800 Subject: [PATCH 09/16] fix(challenge): preserve pre-cutover authority reads --- prover/bin/challenge/src/abi/mod.rs | 5 + .../abi/pre_submitter/LegacyL1Staking.json | 34 ++++++ .../bin/challenge/src/bin/auto_challenge.rs | 8 +- prover/bin/challenge/src/handler.rs | 35 +++--- prover/bin/challenge/src/rollup_compat.rs | 111 +++++++++++++++++- 5 files changed, 170 insertions(+), 23 deletions(-) create mode 100644 prover/bin/challenge/src/abi/pre_submitter/LegacyL1Staking.json diff --git a/prover/bin/challenge/src/abi/mod.rs b/prover/bin/challenge/src/abi/mod.rs index b62b8e5cc..f6a612f04 100644 --- a/prover/bin/challenge/src/abi/mod.rs +++ b/prover/bin/challenge/src/abi/mod.rs @@ -13,6 +13,11 @@ pub mod pre_submitter_rollup_abi { abigen!(PreSubmitterRollup, "src/abi/pre_submitter/Rollup.json"); } +pub mod legacy_l1_staking_abi { + use ethers::prelude::abigen; + abigen!(LegacyL1Staking, "src/abi/pre_submitter/LegacyL1Staking.json"); +} + pub mod submitter_abi { use ethers::prelude::abigen; abigen!(Submitter, "src/abi/Submitter.json"); diff --git a/prover/bin/challenge/src/abi/pre_submitter/LegacyL1Staking.json b/prover/bin/challenge/src/abi/pre_submitter/LegacyL1Staking.json new file mode 100644 index 000000000..9d0abd2e7 --- /dev/null +++ b/prover/bin/challenge/src/abi/pre_submitter/LegacyL1Staking.json @@ -0,0 +1,34 @@ +[ + { + "inputs": [], + "name": "challengeDeposit", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "addr", + "type": "address" + } + ], + "name": "isActiveStaker", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + } +] diff --git a/prover/bin/challenge/src/bin/auto_challenge.rs b/prover/bin/challenge/src/bin/auto_challenge.rs index e7f06fe62..2d41cda37 100644 --- a/prover/bin/challenge/src/bin/auto_challenge.rs +++ b/prover/bin/challenge/src/bin/auto_challenge.rs @@ -1,5 +1,5 @@ -use challenge_handler::abi::{rollup_abi::Rollup, submitter_abi::Submitter}; -use challenge_handler::rollup_compat::resolve_canonical_commit; +use challenge_handler::abi::rollup_abi::Rollup; +use challenge_handler::rollup_compat::{challenge_deposit_at, resolve_canonical_commit}; use challenge_handler::util::read_parse_env; use dotenv::dotenv; use env_logger::Env; @@ -112,9 +112,7 @@ async fn auto_challenge(l1_provider: &Provider, l1_rollup: &RollupType, ro log::info!("latest blocknum = {:#?}", latest); let block_id = BlockId::Number(BlockNumber::Number(latest)); - let submitter_address = l1_rollup.submitter_contract().block(block_id).call().await?; - let submitter = Submitter::new(submitter_address, l1_rollup.client()); - let min_deposit = submitter.challenge_deposit().block(block_id).call().await?; + let min_deposit = challenge_deposit_at(l1_rollup, block_id).await?; let batch_index = l1_rollup.last_committed_batch_index().block(block_id).call().await?.as_u64(); if resolve_canonical_commit(l1_rollup, l1_provider, batch_index, rollup_deployed_block, latest) .await? diff --git a/prover/bin/challenge/src/handler.rs b/prover/bin/challenge/src/handler.rs index c949799ed..572d17a7a 100644 --- a/prover/bin/challenge/src/handler.rs +++ b/prover/bin/challenge/src/handler.rs @@ -1,7 +1,9 @@ -use crate::abi::{rollup_abi::Rollup, submitter_abi::Submitter}; +use crate::abi::rollup_abi::Rollup; use crate::external_sign::ExternalSign; use crate::metrics::METRICS; -use crate::rollup_compat::{decode_batch_data_input, resolve_canonical_commit, CanonicalCommit}; +use crate::rollup_compat::{ + authority_is_active, decode_batch_data_input, decode_batch_data_store_block_number, resolve_canonical_commit, CanonicalCommit, +}; use crate::util::read_env_var; use crate::util::{self, read_parse_env}; use ethers::prelude::*; @@ -327,23 +329,15 @@ impl ChallengeHandler { } }; let snapshot = BlockId::Number(BlockNumber::Number(number)); - let submitter_address = match l1_rollup.submitter_contract().block(snapshot).call().await { - Ok(address) => address, - Err(err) => { - log::error!("query Rollup.submitterContract failed: {err:#}"); - return false; - } - }; - let submitter = Submitter::new(submitter_address, l1_rollup.client()); - let active = match submitter.is_active(signer).block(snapshot).call().await { + let active = match authority_is_active(l1_rollup, signer, snapshot).await { Ok(active) => active, Err(err) => { - log::error!("Submitter.isActive preflight failed: {err:#}"); + log::error!("Rollup authority active preflight failed: {err:#}"); return false; } }; if !snapshot_unchanged(&self.l1_provider, number, hash).await { - log::error!("finalized snapshot changed during Submitter.isActive preflight"); + log::error!("finalized snapshot changed during authority active preflight"); return false; } if !active { @@ -493,10 +487,19 @@ async fn batch_inspect( snapshot: U64, ) -> Option { let block_id = BlockId::Number(BlockNumber::Number(snapshot)); - let prev_batch_last_bn: U256 = match l1_rollup.batch_data_store(U256::from(batch_index - 1)).block(block_id).call().await { - Ok(s) => s.2, + let parent_index = batch_index.checked_sub(1)?; + let calldata = l1_rollup.batch_data_store(U256::from(parent_index)).calldata()?; + let request: TypedTransaction = TransactionRequest::new().to(l1_rollup.address()).data(calldata).into(); + let prev_batch_last_bn: U256 = match l1_provider.call(&request, Some(block_id)).await { + Ok(data) => match decode_batch_data_store_block_number(data.as_ref()) { + Ok(block_number) => block_number, + Err(error) => { + log::error!("decode compatible batchDataStore output failed: {error:#}"); + return None; + } + }, Err(e) => { - log::error!("l1_rollup.batch_data_store err: {:#?}", e); + log::error!("l1_rollup compatible batchDataStore call failed: {:#?}", e); return None; } }; diff --git a/prover/bin/challenge/src/rollup_compat.rs b/prover/bin/challenge/src/rollup_compat.rs index a55432706..fc0875cbe 100644 --- a/prover/bin/challenge/src/rollup_compat.rs +++ b/prover/bin/challenge/src/rollup_compat.rs @@ -1,19 +1,109 @@ use ethers::{ abi::AbiDecode, providers::Middleware, - types::{BlockId, BlockNumber, Bytes, Filter, Log, TxHash, H256, U256, U64}, - utils::keccak256, + types::{transaction::eip2718::TypedTransaction, Address, BlockId, BlockNumber, Bytes, Filter, Log, TransactionRequest, TxHash, H256, U256, U64}, + utils::{id, keccak256}, }; use eyre::{eyre, Result}; use crate::abi::{ + legacy_l1_staking_abi::LegacyL1Staking, pre_submitter_rollup_abi::{ CommitBatchCall as PreSubmitterCommitBatchCall, CommitBatchWithProofCall as PreSubmitterCommitBatchWithProofCall, CommitStateCall as PreSubmitterCommitStateCall, }, rollup_abi::{CommitBatchCall, CommitBatchWithProofCall, CommitStateCall, Rollup}, + submitter_abi::Submitter, }; +#[derive(Clone, Copy, Debug, Eq, PartialEq)] +pub enum AuthorityKind { + LegacyL1Staking, + Submitter, +} + +#[derive(Clone, Copy, Debug, Eq, PartialEq)] +pub struct RollupAuthority { + pub address: Address, + pub kind: AuthorityKind, +} + +fn decode_address_word(data: &[u8]) -> Result
{ + if data.len() != 32 || data[..12].iter().any(|byte| *byte != 0) { + return Err(eyre!("authority getter returned malformed address data")); + } + let address = Address::from_slice(&data[12..]); + if address.is_zero() { + return Err(eyre!("authority getter returned zero address")); + } + Ok(address) +} + +pub async fn resolve_rollup_authority(rollup: &Rollup, snapshot: BlockId) -> Result { + match rollup.submitter_contract().block(snapshot).call().await { + Ok(address) if !address.is_zero() => Ok(RollupAuthority { + address, + kind: AuthorityKind::Submitter, + }), + Ok(_) => Err(eyre!("Rollup.submitterContract returned zero address")), + Err(current_error) => { + // Before initialize4 the proxy still runs the frozen Rollup ABI and + // rejects submitterContract(). Read the old slot through its + // historical getter at the exact same numbered snapshot. + let request: TypedTransaction = TransactionRequest::new() + .to(rollup.address()) + .data(Bytes::from(id("l1StakingContract()")[..4].to_vec())) + .into(); + let output = rollup.client().call(&request, Some(snapshot)).await.map_err(|legacy_error| { + eyre!("neither current nor pre-submit authority getter succeeded: current={current_error}; legacy={legacy_error}") + })?; + Ok(RollupAuthority { + address: decode_address_word(output.as_ref())?, + kind: AuthorityKind::LegacyL1Staking, + }) + } + } +} + +pub async fn authority_is_active(rollup: &Rollup, signer: Address, snapshot: BlockId) -> Result { + let authority = resolve_rollup_authority(rollup, snapshot).await?; + match authority.kind { + AuthorityKind::Submitter => Ok(Submitter::new(authority.address, rollup.client()) + .is_active(signer) + .block(snapshot) + .call() + .await?), + AuthorityKind::LegacyL1Staking => Ok(LegacyL1Staking::new(authority.address, rollup.client()) + .is_active_staker(signer) + .block(snapshot) + .call() + .await?), + } +} + +pub async fn challenge_deposit_at(rollup: &Rollup, snapshot: BlockId) -> Result { + let authority = resolve_rollup_authority(rollup, snapshot).await?; + match authority.kind { + AuthorityKind::Submitter => Ok(Submitter::new(authority.address, rollup.client()) + .challenge_deposit() + .block(snapshot) + .call() + .await?), + AuthorityKind::LegacyL1Staking => Ok(LegacyL1Staking::new(authority.address, rollup.client()) + .challenge_deposit() + .block(snapshot) + .call() + .await?), + } +} + +pub fn decode_batch_data_store_block_number(data: &[u8]) -> Result { + if data.len() != 128 { + return Err(eyre!("batchDataStore returned {}, expected exactly 128 bytes", data.len())); + } + Ok(U256::from_big_endian(&data[64..96])) +} + #[derive(Clone, Copy, Debug, Eq, PartialEq)] pub enum SubmissionKind { CommitBatch, @@ -250,4 +340,21 @@ mod tests { let resolved = select_canonical_commit(&[after_revert], &[revert], hash).unwrap(); assert_eq!(resolved.transaction_hash, H256::from_low_u64_be(3)); } + + #[test] + fn compatibility_words_reject_malformed_authority_and_ignore_legacy_bitmap_type() { + let address = Address::from_low_u64_be(7); + let mut address_word = [0u8; 32]; + address_word[12..].copy_from_slice(address.as_bytes()); + assert_eq!(decode_address_word(&address_word).unwrap(), address); + assert!(decode_address_word(&address_word[..31]).is_err()); + address_word[0] = 1; + assert!(decode_address_word(&address_word).is_err()); + + let mut old_tuple = [0u8; 128]; + U256::from(123).to_big_endian(&mut old_tuple[64..96]); + old_tuple[96..128].fill(0xff); // legacy uint256 bitmap, not an ABI address + assert_eq!(decode_batch_data_store_block_number(&old_tuple).unwrap(), U256::from(123)); + assert!(decode_batch_data_store_block_number(&old_tuple[..127]).is_err()); + } } From a7dea9846cae0f0e237d780ac1a12e63f4029d06 Mon Sep 17 00:00:00 2001 From: Segue Date: Tue, 11 Aug 2026 20:54:51 +0800 Subject: [PATCH 10/16] feat(ops): configure static validators and submitter runtime --- node/ops-morph/docker-compose.yml | 4 +- node/ops-morph/testnet/docker-compose.yml | 8 +- node/ops-morph/testnet/launch.sh | 9 +- ops/devnet-morph/devnet/__init__.py | 100 +++++++++++++++---- ops/devnet-morph/tests/test_devnet_config.py | 36 +++++++ ops/docker/.env | 8 +- ops/docker/docker-compose-cluster.yml | 3 + ops/docker/docker-compose-devnet.yml | 11 +- 8 files changed, 146 insertions(+), 33 deletions(-) diff --git a/node/ops-morph/docker-compose.yml b/node/ops-morph/docker-compose.yml index 4f9106aad..0ca681209 100644 --- a/node/ops-morph/docker-compose.yml +++ b/node/ops-morph/docker-compose.yml @@ -49,7 +49,7 @@ services: - "${PWD}/jwt-secret.txt:${JWT_SECRET_PATH}" command: > morphnode - --dev-sequencer + --static-validator-tm-key ${MORPH_NODE_STATIC_VALIDATOR_TM_KEYS:?required} --home $NODE_DATA_DIR tx-submitter: @@ -70,4 +70,4 @@ services: - TX_SUBMITTER_BATCH_BLOCK_NUM=1 - TX_SUBMITTER_BATCH_NUM=1 - TX_SUBMITTER_FIRST_TIME=true - - TX_SUBMITTER_CHAIN_ID=900 \ No newline at end of file + - TX_SUBMITTER_CHAIN_ID=900 diff --git a/node/ops-morph/testnet/docker-compose.yml b/node/ops-morph/testnet/docker-compose.yml index d0322fb09..b58418c1e 100644 --- a/node/ops-morph/testnet/docker-compose.yml +++ b/node/ops-morph/testnet/docker-compose.yml @@ -129,7 +129,7 @@ services: - "${PWD}/../jwt-secret.txt:${JWT_SECRET_PATH}" command: > morphnode - --dev-sequencer + --static-validator-tm-key ${MORPH_NODE_STATIC_VALIDATOR_TM_KEYS:?required} --home $NODE_DATA_DIR node-1: @@ -153,7 +153,7 @@ services: - "${PWD}/../jwt-secret.txt:${JWT_SECRET_PATH}" command: > morphnode - --dev-sequencer + --static-validator-tm-key ${MORPH_NODE_STATIC_VALIDATOR_TM_KEYS:?required} --home $NODE_DATA_DIR node-2: @@ -177,7 +177,7 @@ services: - "${PWD}/../jwt-secret.txt:${JWT_SECRET_PATH}" command: > morphnode - --dev-sequencer + --static-validator-tm-key ${MORPH_NODE_STATIC_VALIDATOR_TM_KEYS:?required} --home $NODE_DATA_DIR node-3: @@ -201,5 +201,5 @@ services: - "${PWD}/../jwt-secret.txt:${JWT_SECRET_PATH}" command: > morphnode - --dev-sequencer + --static-validator-tm-key ${MORPH_NODE_STATIC_VALIDATOR_TM_KEYS:?required} --home $NODE_DATA_DIR diff --git a/node/ops-morph/testnet/launch.sh b/node/ops-morph/testnet/launch.sh index 3394279ec..dff9c4f85 100644 --- a/node/ops-morph/testnet/launch.sh +++ b/node/ops-morph/testnet/launch.sh @@ -3,8 +3,9 @@ export MORPH_NODE_L2_ENGINE_AUTH=ops-morph/jwt-secret.txt export MORPH_NODE_L1_ETH_RPC=https://eth-goerli.g.alchemy.com/v2/hmGN_z77oy7LlFgPivFuKF893Iog5AOV export MORPH_NODE_SYNC_DEPOSIT_CONTRACT_ADDRESS=0xFd086bC7CD5C481DCC9C85ebE478A1C0b69FCbb9 +: "${MORPH_NODE_STATIC_VALIDATOR_TM_KEYS:?set the comma-separated network Tendermint ed25519 public keys}" -nohup ./build/bin/morphnode --dev-sequencer --home ./mytestnet/node0 --l2.eth=http://127.0.0.1:8545 --l2.engine=http://127.0.0.1:8551 > ./mytestnet/node0.log 2>&1 & -nohup ./build/bin/morphnode --dev-sequencer --home ./mytestnet/node1 --l2.eth=http://127.0.0.1:8645 --l2.engine=http://127.0.0.1:8651 > ./mytestnet/node1.log 2>&1 & -nohup ./build/bin/morphnode --dev-sequencer --home ./mytestnet/node2 --l2.eth=http://127.0.0.1:8745 --l2.engine=http://127.0.0.1:8751 > ./mytestnet/node2.log 2>&1 & -nohup ./build/bin/morphnode --dev-sequencer --home ./mytestnet/node3 --l2.eth=http://127.0.0.1:8845 --l2.engine=http://127.0.0.1:8851 > ./mytestnet/node3.log 2>&1 & \ No newline at end of file +nohup ./build/bin/morphnode --static-validator-tm-key "$MORPH_NODE_STATIC_VALIDATOR_TM_KEYS" --home ./mytestnet/node0 --l2.eth=http://127.0.0.1:8545 --l2.engine=http://127.0.0.1:8551 > ./mytestnet/node0.log 2>&1 & +nohup ./build/bin/morphnode --static-validator-tm-key "$MORPH_NODE_STATIC_VALIDATOR_TM_KEYS" --home ./mytestnet/node1 --l2.eth=http://127.0.0.1:8645 --l2.engine=http://127.0.0.1:8651 > ./mytestnet/node1.log 2>&1 & +nohup ./build/bin/morphnode --static-validator-tm-key "$MORPH_NODE_STATIC_VALIDATOR_TM_KEYS" --home ./mytestnet/node2 --l2.eth=http://127.0.0.1:8745 --l2.engine=http://127.0.0.1:8751 > ./mytestnet/node2.log 2>&1 & +nohup ./build/bin/morphnode --static-validator-tm-key "$MORPH_NODE_STATIC_VALIDATOR_TM_KEYS" --home ./mytestnet/node3 --l2.eth=http://127.0.0.1:8845 --l2.engine=http://127.0.0.1:8851 > ./mytestnet/node3.log 2>&1 & diff --git a/ops/devnet-morph/devnet/__init__.py b/ops/devnet-morph/devnet/__init__.py index 020efb254..a226e20c5 100644 --- a/ops/devnet-morph/devnet/__init__.py +++ b/ops/devnet-morph/devnet/__init__.py @@ -1,4 +1,6 @@ import argparse +import base64 +import binascii import logging import os import subprocess @@ -44,6 +46,24 @@ '0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80', ), help='Private key for the L1 contract deployer/owner') +parser.add_argument('--batch-submitter-private-key', + default=os.environ.get( + 'BATCH_SUBMITTER_PRIVATE_KEY', + '0x59c6995e998f97a5a0044966f0945389dc9e86dae88c7a8412f4603b6b78690d', + ), + help='Independent private key used by the devnet batch submitter') +parser.add_argument('--batch-submitter-address', + default=os.environ.get( + 'BATCH_SUBMITTER_ADDRESS', + '0x70997970C51812dc3A010C7d01b50e0d17dc79C8', + ), + help='Address expected to match --batch-submitter-private-key') +parser.add_argument('--legacy-genesis-l1-staking-proxy', + default=os.environ.get( + 'LEGACY_GENESIS_L1_STAKING_PROXY', + '0x000000000000000000000000000000000000dEaD', + ), + help='Immutable-only legacy OTHER_STAKING fixture; never used as Submitter') parser.add_argument('--cluster', action="store_true", default=os.environ.get('DEVNET_CLUSTER', '').lower() in ('1', 'true', 'yes'), help='Start an HA sequencer cluster instead of making node-0 the sequencer') @@ -196,6 +216,10 @@ def devnet_deploy(paths, args): deploy_config = read_json(devnet_cfg_orig) deploy_config['l1GenesisBlockTimestamp'] = "0x{:x}".format(int(time.time())) deploy_config['l1StartingBlockTag'] = 'earliest' + # The current L2 genesis format still has the historical OTHER_STAKING + # immutable. Keep that fixture explicit and separate from the live + # Submitter address; it must never be renamed or repointed to Submitter. + deploy_config['l1StakingProxy'] = args.legacy_genesis_l1_staking_proxy temp_deploy_config = pjoin(paths.devnet_dir, 'deploy-config.json') write_json(temp_deploy_config, deploy_config) @@ -252,29 +276,46 @@ def devnet_deploy(paths, args): log.info('Parser L1 contracts...') addresses = {} + deployment_blocks = {} deployment = read_json(paths.deployment_dir) for d in deployment: addresses[d['name']] = d['address'] + deployment_blocks[d['name']] = int(d['number']) log.info('Passing L1 contracts address:', addresses) - log.info('Do Staking Sequencer...') - deploy_config['l2StakingAddresses'] - deploy_config['l2StakingPks'] - deploy_config['l2StakingTmKeys'] - deploy_config['l2StakingBlsKeys'] - for i in range(4): - run_command(['cast', 'send', addresses['Proxy__L1Staking'], - 'register(bytes32,bytes memory)', - deploy_config['l2StakingTmKeys'][i], - deploy_config['l2StakingBlsKeys'][i], - '--rpc-url', 'http://127.0.0.1:9545', - '--value', '1ether', - '--private-key', deploy_config['l2StakingPks'][i] - ]) + rollup_deployed_block = deployment_blocks.get('Proxy__Rollup', 0) + if rollup_deployed_block <= 0: + raise RuntimeError('Proxy__Rollup deployment block missing from deployment output') + + log.info('Registering and funding the independent batch submitter...') + submitter_proxy = addresses['Proxy__Submitter'] + if submitter_proxy.lower() == args.legacy_genesis_l1_staking_proxy.lower(): + raise RuntimeError( + 'live Proxy__Submitter must differ from immutable legacy L1 staking fixture' + ) + submitter_address = run_command_capture_output([ + 'cast', 'wallet', 'address', '--private-key', args.batch_submitter_private_key, + ], cwd=paths.contracts_dir).stdout.strip() + if submitter_address.lower() != args.batch_submitter_address.lower(): + raise RuntimeError( + f'batch submitter key resolves to {submitter_address}, expected {args.batch_submitter_address}' + ) + run_command([ + 'cast', 'send', submitter_proxy, 'addSubmitter(address)', submitter_address, + '--rpc-url', 'http://127.0.0.1:9545', + '--private-key', args.deployer_private_key, + ], cwd=paths.contracts_dir) + run_command([ + 'cast', 'send', submitter_proxy, 'stake()', + '--rpc-url', 'http://127.0.0.1:9545', + '--value', '1ether', + '--private-key', args.batch_submitter_private_key, + ], cwd=paths.contracts_dir) configure_l1_sequencer(paths, args, addresses, deploy_config) sequencer_upgrade_time = int((time.time() + args.sequencer_upgrade_offset_seconds) * 1000) active_sequencer_private_key = '' if args.cluster else args.sequencer_private_key + static_validator_tm_keys = read_static_validator_tm_keys(paths.ops_dir) log.info( f'Single sequencer mode enabled: sequencer={args.sequencer_address}, cluster={args.cluster}, ' f'upgrade_time_ms={sequencer_upgrade_time}, ' @@ -293,17 +334,25 @@ def devnet_deploy(paths, args): if line and not line.startswith('#'): key, value = line.split('=') env_data[key.strip()] = value.strip() + # Never carry the retired runtime authority forward from an older + # tracked/local .env. The only allowed legacy staking address is the + # explicit immutable genesis fixture above. + env_data.pop('Proxy__L1Staking', None) + env_data.pop('MORPH_L1STAKING', None) env_data['L1_CROSS_DOMAIN_MESSENGER'] = addresses['Proxy__L1CrossDomainMessenger'] env_data['MORPH_PORTAL'] = addresses['Proxy__L1MessageQueueWithGasPriceOracle'] env_data['MORPH_ROLLUP'] = addresses['Proxy__Rollup'] + env_data['MORPH_ROLLUP_DEPLOY_BLOCK'] = str(rollup_deployed_block) env_data['RUST_LOG'] = rust_log_level - env_data['Proxy__L1Staking'] = addresses['Proxy__L1Staking'] - env_data['MORPH_L1STAKING'] = addresses['Proxy__L1Staking'] + env_data['Proxy__Submitter'] = submitter_proxy + env_data['MORPH_SUBMITTER'] = submitter_proxy env_data['L1_SEQUENCER_CONTRACT'] = addresses.get('Proxy__L1Sequencer', '') env_data['SEQUENCER_PRIVATE_KEY'] = args.sequencer_private_key env_data['ACTIVE_SEQUENCER_PRIVATE_KEY'] = active_sequencer_private_key env_data['HA_SEQUENCER_ADDR'] = args.sequencer_address env_data['SEQUENCER_UPGRADE_TIME'] = str(sequencer_upgrade_time) + env_data['MORPH_NODE_STATIC_VALIDATOR_TM_KEYS'] = static_validator_tm_keys + env_data['BATCH_SUBMITTER_PRIVATE_KEY'] = args.batch_submitter_private_key envfile.seek(0) for key, value in env_data.items(): envfile.write(f'{key}={value}\n') @@ -318,7 +367,9 @@ def devnet_deploy(paths, args): env={ 'MORPH_PORTAL': addresses['Proxy__L1MessageQueueWithGasPriceOracle'], 'MORPH_ROLLUP': addresses['Proxy__Rollup'], - 'MORPH_L1STAKING': addresses['Proxy__L1Staking'], + 'MORPH_ROLLUP_DEPLOY_BLOCK': str(rollup_deployed_block), + 'MORPH_SUBMITTER': submitter_proxy, + 'BATCH_SUBMITTER_PRIVATE_KEY': args.batch_submitter_private_key, 'PWD': paths.ops_dir, 'NODE_DATA_DIR': '/data', 'GETH_DATA_DIR': '/db', @@ -329,11 +380,26 @@ def devnet_deploy(paths, args): 'SEQUENCER_PRIVATE_KEY': args.sequencer_private_key, 'ACTIVE_SEQUENCER_PRIVATE_KEY': active_sequencer_private_key, 'SEQUENCER_UPGRADE_TIME': str(sequencer_upgrade_time), + 'MORPH_NODE_STATIC_VALIDATOR_TM_KEYS': static_validator_tm_keys, }) wait_up(8545) wait_for_rpc_server('127.0.0.1:8545') +def read_static_validator_tm_keys(ops_dir): + """Load and validate the devnet's canonical Tendermint ed25519 public key.""" + key_path = pjoin(ops_dir, 'node0', 'priv_validator_key.json') + with open(key_path, 'r', encoding='utf-8') as key_file: + encoded = json.load(key_file)['pub_key']['value'] + try: + key = base64.b64decode(encoded, validate=True) + except (ValueError, binascii.Error) as error: + raise RuntimeError(f'invalid Tendermint validator public key in {key_path}') from error + if len(key) != 32: + raise RuntimeError(f'Tendermint validator public key in {key_path} must be 32 bytes') + return key.hex() + + def configure_l1_sequencer(paths, args, addresses, deploy_config): """Register the devnet's single sequencer in L1Sequencer before starting L2.""" l1_sequencer_addr = addresses.get('Proxy__L1Sequencer', '') diff --git a/ops/devnet-morph/tests/test_devnet_config.py b/ops/devnet-morph/tests/test_devnet_config.py index f64532279..fa616d047 100644 --- a/ops/devnet-morph/tests/test_devnet_config.py +++ b/ops/devnet-morph/tests/test_devnet_config.py @@ -92,6 +92,42 @@ def test_compose_file_args_can_enable_cluster_mode(self): ], ) + def test_centralized_runtime_uses_submitter_and_static_validator_inputs(self): + compose = (DOCKER_DIR / "docker-compose-devnet.yml").read_text() + env_file = (DOCKER_DIR / ".env").read_text() + devnet_source = (DEVNET_PACKAGE / "devnet" / "__init__.py").read_text() + + self.assertNotIn("Proxy__L1Staking=", env_file) + self.assertNotIn("MORPH_L1STAKING=", env_file) + self.assertIn("MORPH_SUBMITTER=", env_file) + self.assertIn("BATCH_SUBMITTER_PRIVATE_KEY=", env_file) + self.assertIn("MORPH_NODE_STATIC_VALIDATOR_TM_KEYS=", env_file) + self.assertIn("MORPH_ROLLUP_DEPLOY_BLOCK=", env_file) + + env_values = dict( + line.split("=", 1) + for line in env_file.splitlines() + if line and not line.startswith("#") + ) + self.assertNotEqual( + env_values["LEGACY_GENESIS_L1_STAKING_PROXY"].lower(), + env_values["MORPH_SUBMITTER"].lower(), + ) + + self.assertIn("TX_SUBMITTER_SUBMITTER_ADDRESS=${MORPH_SUBMITTER:?required}", compose) + self.assertIn( + "GAS_ORACLE_L1_ROLLUP_DEPLOY_BLOCK=${MORPH_ROLLUP_DEPLOY_BLOCK:?required}", + compose, + ) + self.assertNotIn("l2StakingPks", devnet_source) + self.assertIn("--batch-submitter-private-key", devnet_source) + self.assertIn("env_data.pop('Proxy__L1Staking', None)", devnet_source) + self.assertIn("env_data.pop('MORPH_L1STAKING', None)", devnet_source) + self.assertIn( + "live Proxy__Submitter must differ from immutable legacy L1 staking fixture", + devnet_source, + ) + if __name__ == "__main__": unittest.main() diff --git a/ops/docker/.env b/ops/docker/.env index 670440268..3d1c7c4c1 100644 --- a/ops/docker/.env +++ b/ops/docker/.env @@ -7,9 +7,13 @@ L1_BEACON_CHAIN_RPC=http://layer1-cl:4000 L1_CROSS_DOMAIN_MESSENGER=0xcf7ed3acca5a467e9e704c703e8d87f634fb0fc9 MORPH_PORTAL=0xdc64a140aa3e981100a9beca4e685f962f0cf6c9 MORPH_ROLLUP=0xa513e6e4b8f2a923d98304ec87f64353c4d5c853 +MORPH_ROLLUP_DEPLOY_BLOCK=1 +LEGACY_GENESIS_L1_STAKING_PROXY=0x000000000000000000000000000000000000dEaD BUILD_GETH=l2-geth RUST_LOG=info -Proxy__L1Staking=0x5fc8d32690cc91d4c39d9d3abcbd16989f875707 BATCH_UPGRADE_TIME=0 -MORPH_L1STAKING=0x5fc8d32690cc91d4c39d9d3abcbd16989f875707 L1_SEQUENCER_CONTRACT=0x0165878a594ca255338adfa4d48449f69242eb8f +MORPH_NODE_STATIC_VALIDATOR_TM_KEYS=5280d0eee2a64d3ad29480d15ffd1b048ce5908f180b5ccd65cc3dcf00941abb +BATCH_SUBMITTER_PRIVATE_KEY=0x59c6995e998f97a5a0044966f0945389dc9e86dae88c7a8412f4603b6b78690d +Proxy__Submitter=0x5fc8d32690cc91d4c39d9d3abcbd16989f875707 +MORPH_SUBMITTER=0x5fc8d32690cc91d4c39d9d3abcbd16989f875707 diff --git a/ops/docker/docker-compose-cluster.yml b/ops/docker/docker-compose-cluster.yml index f5c75e6fb..900f10ee7 100644 --- a/ops/docker/docker-compose-cluster.yml +++ b/ops/docker/docker-compose-cluster.yml @@ -103,6 +103,7 @@ services: - MORPH_NODE_SYNC_START_HEIGHT=${MORPH_NODE_SYNC_START_HEIGHT:-1} - MORPH_NODE_UPGRADE_BATCH_TIME=${BATCH_UPGRADE_TIME} - MORPH_NODE_L1_SEQUENCER_CONTRACT=${L1_SEQUENCER_CONTRACT} + - MORPH_NODE_STATIC_VALIDATOR_TM_KEYS=${MORPH_NODE_STATIC_VALIDATOR_TM_KEYS:?required} - MORPH_NODE_HA_ENABLED=true - MORPH_NODE_HA_BOOTSTRAP=true - MORPH_NODE_HA_SERVER_ID=ha-node-0 @@ -143,6 +144,7 @@ services: - MORPH_NODE_SYNC_START_HEIGHT=${MORPH_NODE_SYNC_START_HEIGHT:-1} - MORPH_NODE_UPGRADE_BATCH_TIME=${BATCH_UPGRADE_TIME} - MORPH_NODE_L1_SEQUENCER_CONTRACT=${L1_SEQUENCER_CONTRACT} + - MORPH_NODE_STATIC_VALIDATOR_TM_KEYS=${MORPH_NODE_STATIC_VALIDATOR_TM_KEYS:?required} - MORPH_NODE_HA_ENABLED=true - MORPH_NODE_HA_JOIN=ha-node-0:9401 - MORPH_NODE_HA_SERVER_ID=ha-node-1 @@ -183,6 +185,7 @@ services: - MORPH_NODE_SYNC_START_HEIGHT=${MORPH_NODE_SYNC_START_HEIGHT:-1} - MORPH_NODE_UPGRADE_BATCH_TIME=${BATCH_UPGRADE_TIME} - MORPH_NODE_L1_SEQUENCER_CONTRACT=${L1_SEQUENCER_CONTRACT} + - MORPH_NODE_STATIC_VALIDATOR_TM_KEYS=${MORPH_NODE_STATIC_VALIDATOR_TM_KEYS:?required} - MORPH_NODE_HA_ENABLED=true - MORPH_NODE_HA_JOIN=ha-node-0:9401 - MORPH_NODE_HA_SERVER_ID=ha-node-2 diff --git a/ops/docker/docker-compose-devnet.yml b/ops/docker/docker-compose-devnet.yml index 451cd5e16..6809d24a8 100644 --- a/ops/docker/docker-compose-devnet.yml +++ b/ops/docker/docker-compose-devnet.yml @@ -185,6 +185,7 @@ services: - MORPH_NODE_L1_SEQUENCER_CONTRACT=${L1_SEQUENCER_CONTRACT} - MORPH_NODE_SEQUENCER_PRIVATE_KEY=${ACTIVE_SEQUENCER_PRIVATE_KEY} - MORPH_NODE_SEQUENCER_UPGRADE_TIME=${SEQUENCER_UPGRADE_TIME:-0} + - MORPH_NODE_STATIC_VALIDATOR_TM_KEYS=${MORPH_NODE_STATIC_VALIDATOR_TM_KEYS:?required} volumes: - ".devnet/node0:${NODE_DATA_DIR}" - "${PWD}/jwt-secret.txt:${JWT_SECRET_PATH}" @@ -221,6 +222,7 @@ services: - MORPH_NODE_L1_SEQUENCER_CONTRACT=${L1_SEQUENCER_CONTRACT} - MORPH_NODE_SEQUENCER_UPGRADE_TIME=${SEQUENCER_UPGRADE_TIME:-0} - MORPH_NODE_DERIVATION_VERIFY_MODE=layer1 + - MORPH_NODE_STATIC_VALIDATOR_TM_KEYS=${MORPH_NODE_STATIC_VALIDATOR_TM_KEYS:?required} volumes: - ".devnet/node1:${NODE_DATA_DIR}" - "${PWD}/jwt-secret.txt:${JWT_SECRET_PATH}" @@ -247,16 +249,16 @@ services: environment: - TX_SUBMITTER_BUILD_ENV=dev - TX_SUBMITTER_L1_ETH_RPC=${L1_ETH_RPC} - - TX_SUBMITTER_L1_PRIVATE_KEY=0xd99870855d97327d20c666abc78588f1449b1fac76ed0c86c1afb9ce2db85f32 + - TX_SUBMITTER_L1_PRIVATE_KEY=${BATCH_SUBMITTER_PRIVATE_KEY:?required} - TX_SUBMITTER_L2_ETH_RPCS=http://morph-el-0:8545,http://morph-el-1:8545 - TX_SUBMITTER_TX_TIMEOUT=60s - TX_SUBMITTER_ROLLUP_ADDRESS=${MORPH_ROLLUP:-0x6900000000000000000000000000000000000010} + - TX_SUBMITTER_SUBMITTER_ADDRESS=${MORPH_SUBMITTER:?required} - TX_SUBMITTER_FINALIZE=true - - TX_SUBMITTER_PRIORITY_ROLLUP=true - TX_SUBMITTER_SEAL_BATCH=true + - TX_SUBMITTER_BATCH_BLOCK_INTERVAL=200 + - TX_SUBMITTER_BATCH_TIMEOUT=600 - TX_SUBMITTER_TX_FEE_LIMIT=500000000000000000 - - TX_SUBMITTER_L1_STAKING_ADDRESS=${MORPH_L1STAKING:-0x5fc8d32690cc91d4c39d9d3abcbd16989f875707} - - TX_SUBMITTER_L1_STAKING_DEPLOYED_BLOCKNUM=0 - TX_SUBMITTER_BATCH_V2_UPGRADE_TIME=1777533291 gas-price-oracle: @@ -276,6 +278,7 @@ services: environment: - GAS_ORACLE_L1_RPC=${L1_ETH_RPC} - GAS_ORACLE_L1_BEACON_RPC=${L1_BEACON_CHAIN_RPC} + - GAS_ORACLE_L1_ROLLUP_DEPLOY_BLOCK=${MORPH_ROLLUP_DEPLOY_BLOCK:?required} - GAS_ORACLE_L2_RPC=http://morph-el-0:8545 - GAS_THRESHOLD=5 - INTERVAL=28000 From ab92ddd5baa3dc510d7dfbc15773de39e665b08d Mon Sep 17 00:00:00 2001 From: Segue Date: Tue, 11 Aug 2026 21:14:28 +0800 Subject: [PATCH 11/16] feat(gas-oracle): sample canonical blob sources --- gas-oracle/.env.dev | 5 +- gas-oracle/Makefile | 8 +- gas-oracle/app/Cargo.lock | 74 +- gas-oracle/app/Cargo.toml | 1 + gas-oracle/app/rust-toolchain | 2 +- gas-oracle/app/rustfmt.toml | 8 - gas-oracle/app/src/abi/Rollup.json | 288 +++++- gas-oracle/app/src/abi/mod.rs | 5 + gas-oracle/app/src/da_scalar/blob.rs | 8 +- gas-oracle/app/src/da_scalar/blob_client.rs | 8 +- gas-oracle/app/src/da_scalar/calculate.rs | 239 +++-- gas-oracle/app/src/da_scalar/error.rs | 1 - gas-oracle/app/src/da_scalar/l1_scalar.rs | 975 +++++++++++++++++--- gas-oracle/app/src/da_scalar/mod.rs | 2 +- gas-oracle/app/src/gas_price_oracle.rs | 12 +- gas-oracle/app/src/l1_base_fee.rs | 9 +- gas-oracle/app/src/lib.rs | 3 +- gas-oracle/app/src/metrics.rs | 8 +- gas-oracle/app/src/rollup_compat.rs | 299 ++++++ gas-oracle/app/src/signer.rs | 5 +- 20 files changed, 1696 insertions(+), 264 deletions(-) create mode 100644 gas-oracle/app/src/rollup_compat.rs diff --git a/gas-oracle/.env.dev b/gas-oracle/.env.dev index 3091dcb34..f59892b9c 100644 --- a/gas-oracle/.env.dev +++ b/gas-oracle/.env.dev @@ -13,6 +13,9 @@ TXN_PER_BLOCK = 1 TXN_PER_BATCH = 50 L1_ROLLUP = "0xC5386CBDa4864aE1F86Ed5840D370855F64Bcd3c" +# Sepolia Proxy creation tx 0xe0565450edc796d2ccf37f3c70c119a2db22d44021930bab3375852e36deec90. +# Production deployments must inject the block recorded in their deployment manifest. +GAS_ORACLE_L1_ROLLUP_DEPLOY_BLOCK = 4239849 L2_GAS_PRICE_ORACLE = "0x5FbDB2315678afecb367f032d93F642f64180aa3" L2_GAS_ORACLE_PRIVATE_KEY = "0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80" @@ -27,4 +30,4 @@ GAS_ORACLE_EXTERNAL_SIGN_URL = "http://localhost:8080/v1/sign/tx_sign" GAS_ORACLE_L1_BASE_FEE_BUFFER = 0 GAS_ORACLE_L1_BLOB_BASE_FEE_BUFFER = 0 GAS_ORACLE_COMMIT_SCALAR_BUFFER = 0 -GAS_ORACLE_BLOB_SCALAR_BUFFER = 0 \ No newline at end of file +GAS_ORACLE_BLOB_SCALAR_BUFFER = 0 diff --git a/gas-oracle/Makefile b/gas-oracle/Makefile index c6ee078a8..05001fb6f 100644 --- a/gas-oracle/Makefile +++ b/gas-oracle/Makefile @@ -8,15 +8,15 @@ run:build # Run the formatter but don't change any files. fmt: - cd app && cargo +nightly fmt --all -- --check + cd app && cargo fmt --all -- --check # Run the formatter and update files with style changes lint: - cd app && cargo +nightly fmt --all + cd app && cargo fmt --all # Run clippy clippy: - cd app && cargo +nightly clippy --all-targets -- -D warnings + cd app && cargo clippy --all-targets -- -D warnings # Run all tests test: @@ -37,4 +37,4 @@ help: @echo "test: Run all tests" @echo "clean: Clean the builds" @echo "help: Show this help message" - @echo "" \ No newline at end of file + @echo "" diff --git a/gas-oracle/app/Cargo.lock b/gas-oracle/app/Cargo.lock index 2f6cccb68..c0f49cdb3 100644 --- a/gas-oracle/app/Cargo.lock +++ b/gas-oracle/app/Cargo.lock @@ -137,6 +137,7 @@ name = "app" version = "0.1.0" dependencies = [ "axum", + "c-kzg", "dotenv", "env_logger", "ethers", @@ -369,6 +370,18 @@ dependencies = [ "generic-array", ] +[[package]] +name = "blst" +version = "0.3.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c20659f9bbee16cbbd2f7393e40ab6309f5a98f76a2eb57a995ec508b72387fe" +dependencies = [ + "cc", + "glob", + "threadpool", + "zeroize", +] + [[package]] name = "brotli" version = "6.0.0" @@ -448,6 +461,21 @@ dependencies = [ "pkg-config", ] +[[package]] +name = "c-kzg" +version = "2.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e00bf4b112b07b505472dbefd19e37e53307e2bfed5a79e0cc161d58ccd0e687" +dependencies = [ + "blst", + "cc", + "glob", + "hex", + "libc", + "once_cell", + "serde", +] + [[package]] name = "camino" version = "1.1.7" @@ -482,13 +510,14 @@ dependencies = [ [[package]] name = "cc" -version = "1.0.98" +version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41c270e7540d725e65ac7f1b212ac8ce349719624d7bcff99f8e2e488e8cf03f" +checksum = "5add81bb678e6cb321aff7fa0dc7689ad82b112dbc032cea19f91d6b8e3582b9" dependencies = [ + "find-msvc-tools", "jobserver", "libc", - "once_cell", + "shlex", ] [[package]] @@ -1265,6 +1294,12 @@ dependencies = [ "subtle", ] +[[package]] +name = "find-msvc-tools" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26b73573e6edcd2af0cdf47bd6cb58f0b3839491263c314eaad1ccf24430e1de" + [[package]] name = "fixed-hash" version = "0.8.0" @@ -2260,9 +2295,9 @@ dependencies = [ [[package]] name = "once_cell" -version = "1.19.0" +version = "1.21.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" +checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50" [[package]] name = "open-fastrlp" @@ -3344,6 +3379,12 @@ dependencies = [ "keccak", ] +[[package]] +name = "shlex" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8fadd59c855ef2080decdef8ff161eb6661b86933c9d82e5ba29dc602a55aba" + [[package]] name = "signal-hook-registry" version = "1.4.2" @@ -3613,6 +3654,15 @@ dependencies = [ "syn 2.0.66", ] +[[package]] +name = "threadpool" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d050e60b33d41c19108b32cea32164033a9013fe3b46cbd4457559bfbf77afaa" +dependencies = [ + "num_cpus", +] + [[package]] name = "time" version = "0.3.36" @@ -4442,6 +4492,20 @@ name = "zeroize" version = "1.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde" +dependencies = [ + "zeroize_derive", +] + +[[package]] +name = "zeroize_derive" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.66", +] [[package]] name = "zip" diff --git a/gas-oracle/app/Cargo.toml b/gas-oracle/app/Cargo.toml index 7fbb3b4e1..29be3eefc 100644 --- a/gas-oracle/app/Cargo.toml +++ b/gas-oracle/app/Cargo.toml @@ -6,6 +6,7 @@ edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] +c-kzg = "=2.1.5" ethers = { version = "2.0", features = ["optimism"] } tokio = { version = "1", features = ["full"] } dotenv = "0.15.0" diff --git a/gas-oracle/app/rust-toolchain b/gas-oracle/app/rust-toolchain index f1d81c421..6979a6c06 100644 --- a/gas-oracle/app/rust-toolchain +++ b/gas-oracle/app/rust-toolchain @@ -1 +1 @@ -nightly-2023-12-03 \ No newline at end of file +1.91.0 diff --git a/gas-oracle/app/rustfmt.toml b/gas-oracle/app/rustfmt.toml index 68c3c9303..057dc57df 100644 --- a/gas-oracle/app/rustfmt.toml +++ b/gas-oracle/app/rustfmt.toml @@ -1,11 +1,3 @@ reorder_imports = true -imports_granularity = "Crate" use_small_heuristics = "Max" -comment_width = 100 -wrap_comments = true -binop_separator = "Back" -trailing_comma = "Vertical" -trailing_semicolon = false use_field_init_shorthand = true -format_code_in_doc_comments = true -doc_comment_code_block_width = 100 diff --git a/gas-oracle/app/src/abi/Rollup.json b/gas-oracle/app/src/abi/Rollup.json index 28f7a5c6d..e9da0e250 100644 --- a/gas-oracle/app/src/abi/Rollup.json +++ b/gas-oracle/app/src/abi/Rollup.json @@ -217,6 +217,25 @@ "name": "RevertBatch", "type": "event" }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "oldAddr", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "newAddr", + "type": "address" + } + ], + "name": "SubmitterContractUpdated", + "type": "event" + }, { "anonymous": false, "inputs": [ @@ -306,6 +325,25 @@ "name": "UpdateProofWindow", "type": "event" }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint256", + "name": "oldPeriod", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "newPeriod", + "type": "uint256" + } + ], + "name": "UpdateRollupDelayPeriod", + "type": "event" + }, { "anonymous": false, "inputs": [ @@ -364,6 +402,25 @@ "stateMutability": "nonpayable", "type": "function" }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "batchIndex", + "type": "uint256" + } + ], + "name": "batchBlobVersionedHashes", + "outputs": [ + { + "internalType": "bytes32", + "name": "blobVersionedHash", + "type": "bytes32" + } + ], + "stateMutability": "view", + "type": "function" + }, { "inputs": [ { @@ -441,9 +498,9 @@ "type": "uint256" }, { - "internalType": "uint256", - "name": "signedSequencersBitmap", - "type": "uint256" + "internalType": "address", + "name": "submitter", + "type": "address" } ], "stateMutability": "view", @@ -637,33 +694,121 @@ "internalType": "struct IRollup.BatchDataInput", "name": "batchDataInput", "type": "tuple" - }, + } + ], + "name": "commitBatch", + "outputs": [], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [ { "components": [ { - "internalType": "uint256", - "name": "signedSequencersBitmap", - "type": "uint256" + "internalType": "uint8", + "name": "version", + "type": "uint8" }, { "internalType": "bytes", - "name": "sequencerSets", + "name": "parentBatchHeader", "type": "bytes" }, + { + "internalType": "uint64", + "name": "lastBlockNumber", + "type": "uint64" + }, + { + "internalType": "uint16", + "name": "numL1Messages", + "type": "uint16" + }, + { + "internalType": "bytes32", + "name": "prevStateRoot", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "postStateRoot", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "withdrawalRoot", + "type": "bytes32" + } + ], + "internalType": "struct IRollup.BatchDataInput", + "name": "batchDataInput", + "type": "tuple" + }, + { + "internalType": "bytes", + "name": "_batchHeader", + "type": "bytes" + }, + { + "internalType": "bytes", + "name": "_batchProof", + "type": "bytes" + } + ], + "name": "commitBatchWithProof", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "components": [ + { + "internalType": "uint8", + "name": "version", + "type": "uint8" + }, { "internalType": "bytes", - "name": "signature", + "name": "parentBatchHeader", "type": "bytes" + }, + { + "internalType": "uint64", + "name": "lastBlockNumber", + "type": "uint64" + }, + { + "internalType": "uint16", + "name": "numL1Messages", + "type": "uint16" + }, + { + "internalType": "bytes32", + "name": "prevStateRoot", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "postStateRoot", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "withdrawalRoot", + "type": "bytes32" } ], - "internalType": "struct IRollup.BatchSignatureInput", - "name": "batchSignatureInput", + "internalType": "struct IRollup.BatchDataInput", + "name": "batchDataInput", "type": "tuple" } ], - "name": "commitBatch", + "name": "commitState", "outputs": [], - "stateMutability": "payable", + "stateMutability": "nonpayable", "type": "function" }, { @@ -779,7 +924,7 @@ "inputs": [ { "internalType": "address", - "name": "_l1StakingContract", + "name": "_submitterContract", "type": "address" }, { @@ -826,6 +971,32 @@ "stateMutability": "nonpayable", "type": "function" }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "_rollupDelayPeriod", + "type": "uint256" + } + ], + "name": "initialize3", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_newSubmitter", + "type": "address" + } + ], + "name": "initialize4", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, { "inputs": [ { @@ -864,19 +1035,6 @@ "stateMutability": "view", "type": "function" }, - { - "inputs": [], - "name": "l1StakingContract", - "outputs": [ - { - "internalType": "address", - "name": "", - "type": "address" - } - ], - "stateMutability": "view", - "type": "function" - }, { "inputs": [], "name": "lastCommittedBatchIndex", @@ -942,6 +1100,25 @@ "stateMutability": "view", "type": "function" }, + { + "inputs": [ + { + "internalType": "address", + "name": "submitter", + "type": "address" + } + ], + "name": "pendingBatchCount", + "outputs": [ + { + "internalType": "uint256", + "name": "count", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, { "inputs": [], "name": "proofRewardPercent", @@ -968,6 +1145,24 @@ "stateMutability": "view", "type": "function" }, + { + "inputs": [ + { + "internalType": "bytes", + "name": "_batchHeader", + "type": "bytes" + }, + { + "internalType": "bytes", + "name": "_batchProof", + "type": "bytes" + } + ], + "name": "proveCommittedBatchState", + "outputs": [], + "stateMutability": "view", + "type": "function" + }, { "inputs": [], "name": "proveRemaining", @@ -1050,6 +1245,19 @@ "stateMutability": "view", "type": "function" }, + { + "inputs": [], + "name": "rollupDelayPeriod", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, { "inputs": [ { @@ -1063,6 +1271,19 @@ "stateMutability": "nonpayable", "type": "function" }, + { + "inputs": [], + "name": "submitterContract", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, { "inputs": [ { @@ -1115,6 +1336,19 @@ "stateMutability": "nonpayable", "type": "function" }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "_newPeriod", + "type": "uint256" + } + ], + "name": "updateRollupDelayPeriod", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, { "inputs": [ { diff --git a/gas-oracle/app/src/abi/mod.rs b/gas-oracle/app/src/abi/mod.rs index fdcc9a2c2..2db3287a1 100644 --- a/gas-oracle/app/src/abi/mod.rs +++ b/gas-oracle/app/src/abi/mod.rs @@ -13,3 +13,8 @@ pub mod rollup_abi { use ethers::prelude::abigen; abigen!(Rollup, "src/abi/Rollup.json"); } + +pub mod pre_submitter_rollup_abi { + use ethers::prelude::abigen; + abigen!(PreSubmitterRollup, "src/abi/pre_submitter/Rollup.json"); +} diff --git a/gas-oracle/app/src/da_scalar/blob.rs b/gas-oracle/app/src/da_scalar/blob.rs index dea157f3c..4320cbbef 100644 --- a/gas-oracle/app/src/da_scalar/blob.rs +++ b/gas-oracle/app/src/da_scalar/blob.rs @@ -77,7 +77,7 @@ impl Blob { let max_payload_size = num_blobs * (MAX_BLOB_TX_PAYLOAD_SIZE - 4096); if compressed_len as usize > max_payload_size { - return Err(BlobError::Error(anyhow!("oversized batch payload"))) + return Err(BlobError::Error(anyhow!("oversized batch payload"))); } let compressed_batch = decoded_blob[..compressed_len].to_vec(); @@ -103,7 +103,7 @@ impl Blob { if origin_batch.len() != orgin_content_size as usize { return Err(BlobError::Error(anyhow!( "decompress batch error: origin_batch_len is not equal to zstd_orgin_content_size" - ))) + ))); } let total_blob_payload = num_blobs as f32 * (MAX_BLOB_TX_PAYLOAD_SIZE - 4096) as f32; @@ -163,8 +163,8 @@ fn parse_block_header( // resolve Block_Size (21 bits) let block_size = - ((header[0] as u32 >> 3) | ((header[1] as u32) << 5) | ((header[2] as u32) << 13)) & - 0x1FFFFF; + ((header[0] as u32 >> 3) | ((header[1] as u32) << 5) | ((header[2] as u32) << 13)) + & 0x1FFFFF; Ok((last_block, block_type, block_size)) } diff --git a/gas-oracle/app/src/da_scalar/blob_client.rs b/gas-oracle/app/src/da_scalar/blob_client.rs index 8a2f2871b..266a08659 100644 --- a/gas-oracle/app/src/da_scalar/blob_client.rs +++ b/gas-oracle/app/src/da_scalar/blob_client.rs @@ -26,10 +26,10 @@ impl BeaconNode { indexes: Vec, ) -> Result { let client = reqwest::blocking::Client::new(); - let mut url = l1_beacon_rpc.to_owned() + - "/eth/v1/beacon/blob_sidecars/" + - slot.to_string().as_str() + - "?"; + let mut url = l1_beacon_rpc.to_owned() + + "/eth/v1/beacon/blob_sidecars/" + + slot.to_string().as_str() + + "?"; for index in indexes { url = url + "indices=" + index.to_string().as_str() + "&"; } diff --git a/gas-oracle/app/src/da_scalar/calculate.rs b/gas-oracle/app/src/da_scalar/calculate.rs index 7e7df970a..b67251129 100644 --- a/gas-oracle/app/src/da_scalar/calculate.rs +++ b/gas-oracle/app/src/da_scalar/calculate.rs @@ -1,18 +1,108 @@ +use std::collections::BTreeMap; + +use c_kzg::{ethereum_kzg_settings, Blob as KzgBlob, Bytes48}; use ethers::{ prelude::*, utils::{hex, rlp}, }; use eyre::anyhow; -use serde::{Deserialize, Serialize}; use serde_json::Value; use super::{ - blob::{decompress_batch, kzg_to_versioned_hash, Blob}, + blob::{decompress_batch, kzg_to_versioned_hash, Blob as MorphBlob}, error::ScalarError, typed_tx::TypedTransaction, MAX_BLOB_TX_PAYLOAD_SIZE, }; +fn sidecar_index(sidecar: &Value) -> Result { + let raw = sidecar.get("index").and_then(Value::as_str).ok_or_else(|| { + ScalarError::CalculateError(anyhow!("beacon blob sidecar index is missing or not a string")) + })?; + if raw.is_empty() || !raw.bytes().all(|byte| byte.is_ascii_digit()) { + return Err(ScalarError::CalculateError(anyhow!( + "beacon blob sidecar index is not an unsigned decimal integer" + ))); + } + raw.parse::().map_err(|e| { + ScalarError::CalculateError(anyhow!(format!( + "beacon blob sidecar index overflows u64: {e}" + ))) + }) +} + +fn decode_beacon_hex( + sidecar: &Value, + field: &'static str, + expected_len: usize, +) -> Result, ScalarError> { + let encoded = sidecar.get(field).and_then(Value::as_str).ok_or_else(|| { + ScalarError::CalculateError(anyhow!(format!( + "beacon blob sidecar {field} is missing or not a string" + ))) + })?; + let payload = encoded.strip_prefix("0x").ok_or_else(|| { + ScalarError::CalculateError(anyhow!(format!( + "beacon blob sidecar {field} must be 0x-prefixed" + ))) + })?; + if payload.len() != expected_len.saturating_mul(2) { + return Err(ScalarError::CalculateError(anyhow!(format!( + "beacon blob sidecar {field} has invalid encoded length" + )))); + } + let decoded = hex::decode(payload).map_err(|e| { + ScalarError::CalculateError(anyhow!(format!( + "beacon blob sidecar {field} is not valid hex: {e}" + ))) + })?; + if decoded.len() != expected_len { + return Err(ScalarError::CalculateError(anyhow!(format!( + "beacon blob sidecar {field} has invalid decoded length" + )))); + } + Ok(decoded) +} + +fn verify_blob_sidecar( + sidecar: &Value, + expected_versioned_hash: H256, +) -> Result<[u8; MAX_BLOB_TX_PAYLOAD_SIZE], ScalarError> { + let commitment_bytes = decode_beacon_hex(sidecar, "kzg_commitment", 48)?; + let actual_versioned_hash = kzg_to_versioned_hash(&commitment_bytes); + if expected_versioned_hash != actual_versioned_hash { + return Err(ScalarError::CalculateError(anyhow!(format!( + "invalid blob versioned hash: expected {expected_versioned_hash:?}, got {actual_versioned_hash:?}" + )))); + } + + let blob_bytes = decode_beacon_hex(sidecar, "blob", MAX_BLOB_TX_PAYLOAD_SIZE)?; + let proof_bytes = decode_beacon_hex(sidecar, "kzg_proof", 48)?; + let blob = KzgBlob::from_bytes(&blob_bytes).map_err(|e| { + ScalarError::CalculateError(anyhow!(format!("invalid KZG blob encoding: {e}"))) + })?; + let commitment = Bytes48::from_bytes(&commitment_bytes).map_err(|e| { + ScalarError::CalculateError(anyhow!(format!("invalid KZG commitment encoding: {e}"))) + })?; + let proof = Bytes48::from_bytes(&proof_bytes).map_err(|e| { + ScalarError::CalculateError(anyhow!(format!("invalid KZG proof encoding: {e}"))) + })?; + let verified = ethereum_kzg_settings(0) + .verify_blob_kzg_proof(&blob, &commitment, &proof) + .map_err(|e| { + ScalarError::CalculateError(anyhow!(format!("KZG sidecar verification failed: {e}"))) + })?; + if !verified { + return Err(ScalarError::CalculateError(anyhow!( + "KZG sidecar proof does not match blob and commitment" + ))); + } + + blob_bytes.try_into().map_err(|_| { + ScalarError::CalculateError(anyhow!("verified blob has an unexpected byte length")) + }) +} + /// Extract the full batch data from multiple blobs. /// All blobs belong to the same batch: the batch is compressed as a whole, split into /// multiple segments (each at most 4096 * 31 bytes), and then encoded into BLS12-381 @@ -23,76 +113,46 @@ pub(super) fn extract_tx_payload( sidecars: &[Value], ) -> Result, ScalarError> { let num_blobs = indexed_hashes.len(); + let mut sidecars_by_index = BTreeMap::new(); + for sidecar in sidecars { + let index = sidecar_index(sidecar)?; + if sidecars_by_index.insert(index, sidecar).is_some() { + return Err(ScalarError::CalculateError(anyhow!(format!( + "beacon response contains duplicate blob sidecar index {index}" + )))); + } + } + let mut combined_payload = Vec::::new(); for i_h in indexed_hashes { - if let Some(sidecar) = sidecars.iter().find(|sidecar| { - sidecar["index"].as_str().unwrap_or("1000").parse::().unwrap_or(1000) == i_h.index - }) { - let kzg_commitment = sidecar["kzg_commitment"].as_str().ok_or_else(|| { - ScalarError::CalculateError(anyhow!("Failed to fetch kzg commitment from blob")) - })?; - let decoded_commitment: Vec = - hex::decode(kzg_commitment).map_err(|e| ScalarError::CalculateError(e.into()))?; - let actual_versioned_hash = kzg_to_versioned_hash(&decoded_commitment); - - if i_h.hash != actual_versioned_hash { - log::error!( - "expected hash {:?} for blob at index {:?} but got {:?}", - i_h.hash, - i_h.index, - actual_versioned_hash - ); - - return Err(ScalarError::CalculateError(anyhow!(format!( - "Invalid versionedHash for Blob, expected hash {:?} for blob at index {:?} but got {:?}", - i_h.hash, i_h.index, actual_versioned_hash - )))); - } - - let encoded_blob = sidecar["blob"].as_str().ok_or_else(|| { - ScalarError::CalculateError(anyhow!(format!( - "Missing blob value in blob_hash: {:?}", - i_h.hash - ))) - })?; - let decoded_blob = hex::decode(encoded_blob).map_err(|e| { - ScalarError::CalculateError(anyhow!(format!( - "Failed to decode blob, blob_hash: {:?}, err: {}", - i_h.hash, e - ))) - })?; - - if decoded_blob.len() != MAX_BLOB_TX_PAYLOAD_SIZE { - return Err(ScalarError::CalculateError(anyhow!("Invalid length for Blob"))); - } - - let blob_array: [u8; MAX_BLOB_TX_PAYLOAD_SIZE] = decoded_blob.try_into().unwrap(); - let blob_struct = Blob(blob_array); - // Extract the raw payload segment by removing only the BLS12-381 encoding, - // without zstd decompression. - let payload_bytes = blob_struct.get_payload_bytes().map_err(|e| { - ScalarError::CalculateError(anyhow!(format!( - "Failed to get payload bytes from blob, blob_hash: {:?}, err: {}", - i_h.hash, e - ))) - })?; - combined_payload.extend_from_slice(&payload_bytes); - } else { - return Err(ScalarError::CalculateError(anyhow!(format!( + let sidecar = sidecars_by_index.get(&i_h.index).ok_or_else(|| { + ScalarError::CalculateError(anyhow!(format!( "no blob in response matches desired index: {}", i_h.index - )))); - } + ))) + })?; + let blob_array = verify_blob_sidecar(sidecar, i_h.hash)?; + let blob_struct = MorphBlob(blob_array); + // Extract the raw payload segment by removing only the BLS12-381 encoding, + // without zstd decompression. + let payload_bytes = blob_struct.get_payload_bytes().map_err(|e| { + ScalarError::CalculateError(anyhow!(format!( + "Failed to get payload bytes from blob, blob_hash: {:?}, err: {}", + i_h.hash, e + ))) + })?; + combined_payload.extend_from_slice(&payload_bytes); } // After concatenation, use detect_zstd_compressed to trim the valid compressed payload // (excluding trailing zero padding), then decompress the batch as a whole. - let compressed_data = Blob::detect_zstd_compressed(combined_payload, num_blobs).map_err(|e| { - ScalarError::CalculateError(anyhow!(format!( - "Failed to detect zstd compressed data from combined blob payload: {}", - e - ))) - })?; + let compressed_data = + MorphBlob::detect_zstd_compressed(combined_payload, num_blobs).map_err(|e| { + ScalarError::CalculateError(anyhow!(format!( + "Failed to detect zstd compressed data from combined blob payload: {}", + e + ))) + })?; decompress_batch(&compressed_data).map_err(|e| { ScalarError::CalculateError(anyhow!(format!( "Failed to decompress combined blob payload: {}", @@ -101,18 +161,20 @@ pub(super) fn extract_tx_payload( }) } -pub fn extract_txn_count(origin_batch: &Vec, last_block_num: u64) -> Option { +pub fn extract_txn_count(origin_batch: &[u8], last_block_num: u64) -> Option { if origin_batch.is_empty() || origin_batch.len() < 8 { return None; } let first_block_num = u64::from_be_bytes(origin_batch[0..8].try_into().unwrap_or_default()); - let block_count = last_block_num - first_block_num + 1; - if origin_batch.len() < 60 * block_count as usize { + let block_count = last_block_num.checked_sub(first_block_num)?.checked_add(1)?; + let block_count = usize::try_from(block_count).ok()?; + let required_len = 60usize.checked_mul(block_count)?; + if origin_batch.len() < required_len { log::error!("invalid blob batch len"); return None; } let mut txn_count_in_batch = 0u64; - for i in 0..block_count as usize { + for i in 0..block_count { let bys = &origin_batch[60 * i + 56..60 * i + 58]; let num_txn = u16::from_be_bytes(bys.try_into().unwrap_or_default()); @@ -129,13 +191,40 @@ pub fn extract_txn_count(origin_batch: &Vec, last_block_num: u64) -> Option< Some(txn_count_in_batch) } -#[derive(Debug, Serialize, Deserialize)] -struct BlockInfo { - block_number: U256, - timestamp: u64, - base_fee: U256, - gas_limit: u64, - num_txs: u64, +#[test] +fn extract_txn_count_rejects_reversed_or_truncated_ranges() { + let mut block = vec![0u8; 60]; + block[..8].copy_from_slice(&10u64.to_be_bytes()); + block[56..58].copy_from_slice(&2u16.to_be_bytes()); + assert_eq!(extract_txn_count(&block, 10), Some(2)); + assert_eq!(extract_txn_count(&block, 9), None); + assert_eq!(extract_txn_count(&block[..59], 10), None); +} + +#[test] +fn forged_sidecar_fails_kzg_verification() { + let settings = ethereum_kzg_settings(0); + let blob = KzgBlob::new([0u8; MAX_BLOB_TX_PAYLOAD_SIZE]); + let commitment = settings.blob_to_kzg_commitment(&blob).unwrap().to_bytes(); + + // The proof is valid, but for a different canonical blob/commitment pair. + // This ensures the negative test reaches the KZG relationship check rather + // than failing only on malformed point encodings. + let mut other_blob_bytes = [0u8; MAX_BLOB_TX_PAYLOAD_SIZE]; + other_blob_bytes[31] = 1; + let other_blob = KzgBlob::new(other_blob_bytes); + let other_commitment = settings.blob_to_kzg_commitment(&other_blob).unwrap().to_bytes(); + let forged_proof = settings.compute_blob_kzg_proof(&other_blob, &other_commitment).unwrap(); + + let sidecar = serde_json::json!({ + "index": "0", + "blob": format!("0x{}", hex::encode(blob.as_ref())), + "kzg_commitment": format!("0x{}", hex::encode(commitment.as_ref())), + "kzg_proof": format!("0x{}", hex::encode(forged_proof.to_bytes().as_ref())), + }); + let expected_hash = kzg_to_versioned_hash(commitment.as_ref()); + let error = verify_blob_sidecar(&sidecar, expected_hash).unwrap_err(); + assert!(error.to_string().contains("does not match")); } #[derive(Debug, Clone)] diff --git a/gas-oracle/app/src/da_scalar/error.rs b/gas-oracle/app/src/da_scalar/error.rs index 5a7b3a05f..ea5946678 100644 --- a/gas-oracle/app/src/da_scalar/error.rs +++ b/gas-oracle/app/src/da_scalar/error.rs @@ -8,4 +8,3 @@ pub enum ScalarError { #[error("{0}")] BeaconNodeError(eyre::Error), } - diff --git a/gas-oracle/app/src/da_scalar/l1_scalar.rs b/gas-oracle/app/src/da_scalar/l1_scalar.rs index bbd6d8e7a..d5ef3aeaf 100644 --- a/gas-oracle/app/src/da_scalar/l1_scalar.rs +++ b/gas-oracle/app/src/da_scalar/l1_scalar.rs @@ -1,7 +1,7 @@ -use std::sync::Arc; - -use eyre::anyhow; -use tokio::time::{sleep, Duration}; +use std::{ + collections::{BTreeSet, HashMap, HashSet}, + sync::Arc, +}; use super::{ blob_client::BeaconNode, @@ -10,20 +10,171 @@ use super::{ MAX_BLOB_TX_PAYLOAD_SIZE, }; use crate::{ - abi::{ - gas_price_oracle_abi::GasPriceOracle, - rollup_abi::{CommitBatchCall, Rollup}, - }, + abi::{gas_price_oracle_abi::GasPriceOracle, rollup_abi::Rollup}, metrics::ORACLE_SERVICE_METRICS, + rollup_compat::{ + decode_batch_data_input, resolve_canonical_commit, CanonicalCommit, SubmissionKind, + LOG_QUERY_RANGE, + }, signer::send_transaction, }; -use ethers::{abi::AbiDecode, prelude::*, utils::hex}; +use ethers::{ + prelude::*, + utils::{hex, keccak256}, +}; +use eyre::anyhow; use remote_signer_client::SignerClient; use serde_json::Value; const PRECISION: u64 = 10u64.pow(9); const MAX_COMMIT_SCALAR: u64 = 10u64.pow(9 + 6); const MAX_BLOB_SCALAR: u64 = 10u64.pow(9 + 2); +const SAMPLE_LOOKBACK_BLOCKS: u64 = 100; +const ZERO_VERSIONED_HASH: H256 = H256([ + 0x01, 0x06, 0x57, 0xf3, 0x75, 0x54, 0xc7, 0x81, 0x40, 0x2a, 0x22, 0x91, 0x7d, 0xee, 0x2f, 0x75, + 0xde, 0xf7, 0xab, 0x96, 0x6d, 0x7b, 0x77, 0x09, 0x05, 0x39, 0x8e, 0xba, 0x3c, 0x44, 0x40, 0x14, +]); + +#[derive(Clone, Debug, Eq, Hash, PartialEq)] +struct BlobSource { + transaction_hash: TxHash, + block_number: U64, + block_hash: H256, + transaction_index: U64, + blob_hashes: Vec, +} + +type HistoricalCommitIndex = HashMap>; + +fn index_commit_logs(logs: Vec) -> HistoricalCommitIndex { + let mut index = HistoricalCommitIndex::new(); + for log in logs { + if log.removed.unwrap_or_default() { + continue; + } + let Some(topic) = log.topics.get(1) else { + continue; + }; + let batch_index = U256::from_big_endian(topic.as_bytes()); + if batch_index > U256::from(u64::MAX) { + continue; + } + index.entry(batch_index.as_u64()).or_default().push(log); + } + index +} + +fn batch_indices_from_commit_logs(logs: &[Log], max_batch_index: u64) -> BTreeSet { + logs.iter() + .filter(|log| !log.removed.unwrap_or_default()) + .filter_map(|log| log.topics.get(1)) + .filter_map(|topic| { + let value = U256::from_big_endian(topic.as_bytes()); + (value <= U256::from(max_batch_index)).then(|| value.as_u64()) + }) + .filter(|index| *index > 0) + .collect() +} + +fn ensure_snapshot_hash(expected: H256, actual: Option) -> Result<(), ScalarError> { + if actual != Some(expected) { + return Err(ScalarError::Error(anyhow!( + "finalized snapshot hash changed; discard the complete DA sample round" + ))); + } + Ok(()) +} + +fn transaction_blob_hashes(transaction: &Transaction) -> Vec { + transaction + .other + .get_with("blobVersionedHashes", serde_json::from_value::>) + .unwrap_or(Ok(Vec::new())) + .unwrap_or_default() +} + +fn receipt_matches_source(receipt: &TransactionReceipt, source: &BlobSource) -> bool { + receipt.transaction_hash == source.transaction_hash + && receipt.block_number == Some(source.block_number) + && receipt.block_hash == Some(source.block_hash) + && receipt.transaction_index == source.transaction_index +} + +fn blob_commitment(version: u8, hashes: &[H256]) -> Result { + match version { + 0 | 1 if hashes.len() == 1 => Ok(hashes[0]), + 0 | 1 => Err(ScalarError::Error(anyhow!(format!( + "legacy batch must have exactly one blob, got {}", + hashes.len() + )))), + 2 if !hashes.is_empty() => { + let bytes: Vec = + hashes.iter().flat_map(|hash| hash.as_bytes().iter().copied()).collect(); + Ok(H256::from(keccak256(bytes))) + } + 2 => Err(ScalarError::Error(anyhow!("V2 batch has no blob"))), + _ => Err(ScalarError::Error(anyhow!(format!("unsupported batch version {version}")))), + } +} + +fn validate_fresh_blob_commitment( + stored_commitment: H256, + transaction_commitment: H256, +) -> Result<(), ScalarError> { + // Rollup garbage-collects the mapping after successor finalization. A + // direct canonical blob transaction remains a stable source even when the + // snapshot getter has already returned zero. + if !stored_commitment.is_zero() && stored_commitment != transaction_commitment { + return Err(ScalarError::Error(anyhow!(format!( + "canonical blob commitment mismatch: stored={stored_commitment:?}, tx={transaction_commitment:?}" + )))); + } + Ok(()) +} + +fn recover_gc_commitment(snapshot: H256, canonical_block: H256) -> H256 { + if snapshot.is_zero() { + canonical_block + } else { + snapshot + } +} + +#[derive(Clone, Copy, Debug, Eq, PartialEq)] +enum BloblessDisposition { + ExcludeMissing, + ExcludeZero, + ResolveStored(H256), +} + +fn classify_blobless_submission( + version: u8, + kind: SubmissionKind, + pre_submitter: bool, + stored_commitment: H256, +) -> Result { + if kind == SubmissionKind::CommitBatch && !pre_submitter { + return Err(ScalarError::Error(anyhow!( + "current canonical commitBatch has no blob; refusing zero-data scalar sample" + ))); + } + + if version <= 1 && stored_commitment == ZERO_VERSIONED_HASH { + // Before the Submitter cutover, commitBatch legitimately accepted a + // V0/V1 transaction without a blob and stored ZERO_VERSIONED_HASH. + // Permissionless proof/state submissions can also reuse that value. + return Ok(BloblessDisposition::ExcludeZero); + } + if stored_commitment.is_zero() { + return Ok(BloblessDisposition::ExcludeMissing); + } + if kind == SubmissionKind::CommitBatch { + return Err(ScalarError::Error(anyhow!( + "legacy canonical commitBatch is blobless with a non-zero stored commitment" + ))); + } + Ok(BloblessDisposition::ResolveStored(stored_commitment)) +} // Main struct to manage overhead information pub struct ScalarUpdater { @@ -33,6 +184,7 @@ pub struct ScalarUpdater { ext_signer: Option, l1_rollup: Rollup>, // Rollup object for L1 beacon_node: BeaconNode, // Beacon node for blockchain + l1_rollup_deploy_block: u64, gas_threshold: u64, commit_scalar_buffer: u64, blob_scalar_buffer: u64, @@ -50,6 +202,7 @@ impl ScalarUpdater { ext_signer: Option, l1_rollup: Rollup>, l1_beacon_rpc: String, + l1_rollup_deploy_block: u64, gas_threshold: u64, commit_scalar_buffer: u64, blob_scalar_buffer: u64, @@ -67,6 +220,7 @@ impl ScalarUpdater { ext_signer, l1_rollup, beacon_node, + l1_rollup_deploy_block, gas_threshold, commit_scalar_buffer, blob_scalar_buffer, @@ -79,27 +233,47 @@ impl ScalarUpdater { /// Calculate the user's average cost of the latest rollup and set it to the GasPriceOrale /// contract on the L2 network. pub async fn update(&mut self) -> Result<(), ScalarError> { - // Step1. fetch latest batches and calculate scalar. - let latest = self.l1_provider.get_block_number().await.map_err(|e| { - ScalarError::Error(anyhow!(format!( - "overhead.l1_provider.get_block_number error: {:#?}", - e - ))) - })?; - let start = if latest > U64::from(100) { - latest - U64::from(100) //100 + // Step1. Pin all reads to one finalized L1 block. A latest-head sample + // can be reorged after the oracle has already written its L2 scalar. + let snapshot = self + .l1_provider + .get_block(BlockNumber::Finalized) + .await + .map_err(|e| { + ScalarError::Error(anyhow!(format!( + "overhead.l1_provider.get finalized block error: {:#?}", + e + ))) + })? + .ok_or_else(|| ScalarError::Error(anyhow!("finalized L1 block is unavailable")))?; + let snapshot_number = snapshot + .number + .ok_or_else(|| ScalarError::Error(anyhow!("finalized L1 block has no number")))?; + let snapshot_hash = snapshot + .hash + .ok_or_else(|| ScalarError::Error(anyhow!("finalized L1 block has no hash")))?; + if self.l1_rollup_deploy_block == 0 + || self.l1_rollup_deploy_block > snapshot_number.as_u64() + { + return Err(ScalarError::Error(anyhow!(format!( + "configured Rollup deployment block {} is outside finalized snapshot 1..={}", + self.l1_rollup_deploy_block, snapshot_number + )))); + } + let start = if snapshot_number > U64::from(SAMPLE_LOOKBACK_BLOCKS) { + snapshot_number - U64::from(SAMPLE_LOOKBACK_BLOCKS) } else { U64::from(1) }; - let (mut commit_scalar, mut blob_scalar) = match self.calculate_scalar(start.as_u64()).await - { - Ok(Some(scalar)) => scalar, - Ok(None) => { - return Ok(()); - } - Err(e) => return Err(e), - }; + let (mut commit_scalar, mut blob_scalar) = + match self.calculate_scalar(start.as_u64(), snapshot_number, snapshot_hash).await { + Ok(Some(scalar)) => scalar, + Ok(None) => { + return Ok(()); + } + Err(e) => return Err(e), + }; // Step2. fetch current scalar on l2. let current_commit_scalar: U256 = self.l2_oracle.commit_scalar().await.map_err(|e| { @@ -170,84 +344,491 @@ impl ScalarUpdater { need_update } - async fn calculate_scalar(&mut self, start: u64) -> Result, ScalarError> { - let filter = self + async fn calculate_scalar( + &mut self, + start: u64, + snapshot_number: U64, + snapshot_hash: H256, + ) -> Result, ScalarError> { + let block_id = BlockId::Number(BlockNumber::Number(snapshot_number)); + let last_batch_index = self .l1_rollup - .commit_batch_filter() - .filter - .from_block(start) - .address(self.l1_rollup.address()); + .last_committed_batch_index() + .block(block_id) + .call() + .await + .map_err(|e| ScalarError::Error(anyhow!(format!("query last committed batch: {e:?}"))))? + .as_u64(); + if last_batch_index == 0 { + log::warn!("no non-genesis batch exists at finalized L1 snapshot, skip update"); + return Ok(None); + } - let mut logs = self.l1_provider.get_logs(&filter).await.map_err(|e| { - ScalarError::Error(anyhow!(format!("overhead.l1_provider.get_logs error: {:#?}", e))) - })?; + let commit_topic = H256::from(keccak256("CommitBatch(uint256,bytes32)")); + let candidate_logs = self + .l1_provider + .get_logs( + &Filter::new() + .address(self.l1_rollup.address()) + .from_block(start) + .to_block(snapshot_number) + .topic0(commit_topic), + ) + .await + .map_err(|e| { + ScalarError::Error(anyhow!(format!("query finalized Commit window: {e:?}"))) + })?; + let batch_indices = batch_indices_from_commit_logs(&candidate_logs, last_batch_index); + if batch_indices.is_empty() { + log::warn!("no Commit event exists in the finalized DA sample window"); + return Ok(None); + } + + let mut seen_sources = HashSet::new(); + let mut commit_scalar_sum = 0u128; + let mut blob_scalar_sum = 0u128; + let mut sample_count = 0u128; + let mut historical_commits = None; + + for batch_index in batch_indices { + let canonical = resolve_canonical_commit( + &self.l1_rollup, + &self.l1_provider, + batch_index, + start, + snapshot_number, + ) + .await + .map_err(|e| { + ScalarError::Error(anyhow!(format!( + "resolve canonical commit for batch {batch_index}: {e:#}" + ))) + })?; + let Some(canonical) = canonical else { + // The window may contain a Commit that was canonically reverted + // before the pinned snapshot. It contributes no DA sample. + continue; + }; - log::debug!("overhead.l1_provider.submit_batches.get_logs.len ={:#?}", logs.len()); + let canonical_tx = self.load_commit_transaction(canonical).await?; + let input = decode_batch_data_input(canonical_tx.input.as_ref()).map_err(|e| { + ScalarError::Error(anyhow!(format!( + "decode canonical commit {:?}: {e:#}", + canonical.transaction_hash + ))) + })?; + let blob_source = self + .resolve_blob_source( + batch_index, + canonical, + &canonical_tx, + input.version, + input.kind, + input.pre_submitter, + snapshot_number, + snapshot_hash, + &mut historical_commits, + ) + .await?; + let Some(blob_source) = blob_source else { + ORACLE_SERVICE_METRICS.da_sample_exclusions.inc(); + log::warn!( + "canonical batch {} has no verifiable real DA blob source; excluded", + batch_index + ); + continue; + }; + if !seen_sources.insert(blob_source.clone()) { + ORACLE_SERVICE_METRICS.da_sample_exclusions.inc(); + log::warn!( + "duplicate DA blob source {:?} in finalized window; counted once", + blob_source.transaction_hash + ); + continue; + } + + let (commit_scalar, blob_scalar) = self + .calculate_from_rollup(blob_source, input.last_block_number) + .await + .inspect_err(|_| { + log::info!( + "scalar is unavailable, canonical_tx_hash ={:#?}", + canonical.transaction_hash + ); + })?; + commit_scalar_sum += u128::from(commit_scalar); + blob_scalar_sum += u128::from(blob_scalar); + sample_count += 1; + } - logs.retain(|x| x.transaction_hash.is_some() && x.block_number.is_some()); - if logs.is_empty() { - log::warn!("rollup logs for the last 100 blocks of l1 is empty, skip update"); + let after = self + .l1_provider + .get_block(snapshot_number) + .await + .map_err(|e| ScalarError::Error(anyhow!(format!("recheck finalized snapshot: {e:?}"))))? + .ok_or_else(|| ScalarError::Error(anyhow!("finalized snapshot disappeared")))?; + ensure_snapshot_hash(snapshot_hash, after.hash)?; + if sample_count == 0 { return Ok(None); } - let log = logs.iter().max_by_key(|log| log.block_number.unwrap()).ok_or_else(|| { - ScalarError::Error(anyhow!(format!( - "no submit batches logs, start blocknum ={:#?}", - start - ))) - })?; + let commit_scalar = u64::try_from(commit_scalar_sum / sample_count) + .map_err(|_| ScalarError::Error(anyhow!("averaged commit scalar overflow")))?; + let blob_scalar = u64::try_from(blob_scalar_sum / sample_count) + .map_err(|_| ScalarError::Error(anyhow!("averaged blob scalar overflow")))?; + Ok(Some((commit_scalar, blob_scalar))) + } + + async fn load_commit_transaction( + &self, + commit: CanonicalCommit, + ) -> Result { + let transaction = self + .l1_provider + .get_transaction(commit.transaction_hash) + .await + .map_err(|e| ScalarError::Error(anyhow!(format!("get commit transaction: {e:?}"))))? + .ok_or_else(|| { + ScalarError::Error(anyhow!(format!( + "commit transaction {:?} is unavailable", + commit.transaction_hash + ))) + })?; + if transaction.to != Some(self.l1_rollup.address()) + || transaction.block_number != Some(commit.identity.block_number.into()) + || transaction.block_hash != Some(commit.block_hash) + || transaction.transaction_index != Some(commit.identity.transaction_index.into()) + { + return Err(ScalarError::Error(anyhow!(format!( + "commit transaction {:?} moved to a different block", + commit.transaction_hash + )))); + } + Ok(transaction) + } + + async fn load_canonical_block_commitment( + &self, + batch_index: u64, + canonical: CanonicalCommit, + ) -> Result { + if canonical.identity.block_number < self.l1_rollup_deploy_block { + return Err(ScalarError::Error(anyhow!(format!( + "canonical commit block {} predates configured Rollup deployment block {}", + canonical.identity.block_number, self.l1_rollup_deploy_block + )))); + } + + let canonical_number = U64::from(canonical.identity.block_number); + let before = self + .l1_provider + .get_block(BlockNumber::Number(canonical_number)) + .await + .map_err(|e| { + ScalarError::Error(anyhow!(format!( + "load canonical commit block before historical getter: {e:?}" + ))) + })? + .ok_or_else(|| ScalarError::Error(anyhow!("canonical commit block disappeared")))?; + if before.hash != Some(canonical.block_hash) { + return Err(ScalarError::Error(anyhow!( + "canonical commit block hash changed before historical getter" + ))); + } + + // Use the block hash, rather than an unpinned number, for the historical + // eth_call. This recovers the mapping value before later finalization GC. + let commitment: H256 = self + .l1_rollup + .batch_blob_versioned_hashes(U256::from(batch_index)) + .block(BlockId::Hash(canonical.block_hash)) + .call() + .await + .map_err(|e| { + ScalarError::Error(anyhow!(format!( + "query stored blob commitment at canonical commit block: {e:?}" + ))) + })? + .into(); + + let after = self + .l1_provider + .get_block(BlockNumber::Number(canonical_number)) + .await + .map_err(|e| { + ScalarError::Error(anyhow!(format!( + "recheck canonical commit block after historical getter: {e:?}" + ))) + })? + .ok_or_else(|| ScalarError::Error(anyhow!("canonical commit block disappeared")))?; + if after.hash != Some(canonical.block_hash) { + return Err(ScalarError::Error(anyhow!( + "canonical commit block hash changed during historical getter" + ))); + } + Ok(commitment) + } + + async fn load_historical_commit_index( + &self, + snapshot_number: U64, + ) -> Result { + if self.l1_rollup_deploy_block > snapshot_number.as_u64() { + return Err(ScalarError::Error(anyhow!( + "Rollup deployment block is newer than the finalized snapshot" + ))); + } - #[allow(clippy::manual_inspect)] - let (commit_scalar, blob_scalar) = self - .calculate_from_rollup(log.transaction_hash.unwrap(), log.block_number.unwrap()) + // Replay CommitBatch once per scalar round and share the resulting + // index between every stored-source batch. Replaying the entire chain + // separately for each batch multiplies RPC load by the sample count. + let commit_topic = H256::from(keccak256("CommitBatch(uint256,bytes32)")); + let mut logs = Vec::new(); + let mut from = self.l1_rollup_deploy_block; + let snapshot = snapshot_number.as_u64(); + loop { + let to = from.saturating_add(LOG_QUERY_RANGE - 1).min(snapshot); + logs.extend( + self.l1_provider + .get_logs( + &Filter::new() + .address(self.l1_rollup.address()) + .from_block(from) + .to_block(to) + .topic0(commit_topic), + ) + .await + .map_err(|e| { + ScalarError::Error(anyhow!(format!( + "query historical blob-source commits in {from}..={to}: {e:?}" + ))) + })?, + ); + if to == snapshot { + break; + } + from = to + .checked_add(1) + .ok_or_else(|| ScalarError::Error(anyhow!("commit replay block overflow")))?; + } + Ok(index_commit_logs(logs)) + } + + #[allow(clippy::too_many_arguments)] + async fn resolve_blob_source( + &self, + batch_index: u64, + canonical: CanonicalCommit, + canonical_tx: &Transaction, + version: u8, + kind: SubmissionKind, + pre_submitter: bool, + snapshot_number: U64, + snapshot_hash: H256, + historical_commits: &mut Option, + ) -> Result, ScalarError> { + let block_id = BlockId::Number(BlockNumber::Number(snapshot_number)); + let snapshot_commitment: H256 = self + .l1_rollup + .batch_blob_versioned_hashes(U256::from(batch_index)) + .block(block_id) + .call() .await .map_err(|e| { - log::info!( - "scalar is none, skip update, tx_hash ={:#?}", - log.transaction_hash.unwrap() + ScalarError::Error(anyhow!(format!("query stored blob commitment: {e:?}"))) + })? + .into(); + + let current_hashes = transaction_blob_hashes(canonical_tx); + if !current_hashes.is_empty() { + let actual_commitment = blob_commitment(version, ¤t_hashes)?; + validate_fresh_blob_commitment(snapshot_commitment, actual_commitment)?; + return Ok(Some(BlobSource { + transaction_hash: canonical.transaction_hash, + block_number: canonical.identity.block_number.into(), + block_hash: canonical.block_hash, + transaction_index: canonical.identity.transaction_index.into(), + blob_hashes: current_hashes, + })); + } + + // A post-cutover commitBatch is required by the Rollup contract to + // carry a blob. Keep that invariant fail-closed even if storage was GC'd. + if kind == SubmissionKind::CommitBatch && !pre_submitter { + return classify_blobless_submission(version, kind, pre_submitter, snapshot_commitment) + .map(|_| None); + } + + let canonical_block_commitment = if snapshot_commitment.is_zero() { + self.load_canonical_block_commitment(batch_index, canonical).await? + } else { + snapshot_commitment + }; + let expected_commitment = + recover_gc_commitment(snapshot_commitment, canonical_block_commitment); + match classify_blobless_submission(version, kind, pre_submitter, expected_commitment)? { + BloblessDisposition::ExcludeZero => { + log::warn!( + "canonical batch {} is a legal V0/V1 blobless ZERO_VERSIONED_HASH submission; excluded from DA scalar sampling", + batch_index ); - e - })?; + return Ok(None); + } + BloblessDisposition::ExcludeMissing => { + log::warn!( + "batch {} stored blob commitment is zero at both finalized snapshot and canonical commit block; excluded and alert", + batch_index + ); + return Ok(None); + } + BloblessDisposition::ResolveStored(commitment) => { + debug_assert_eq!(commitment, expected_commitment); + } + } - Ok(Some((commit_scalar, blob_scalar))) + // Stored-source recommits intentionally look *before* the last Revert. + // Canonical filtering here would discard the original blob transaction. + if historical_commits.is_none() { + *historical_commits = Some(self.load_historical_commit_index(snapshot_number).await?); + } + let mut candidates = historical_commits + .as_ref() + .and_then(|index| index.get(&batch_index)) + .cloned() + .unwrap_or_default(); + candidates.retain(|log| { + log.block_number + .is_some_and(|number| number.as_u64() <= canonical.identity.block_number) + }); + candidates.sort_by_key(|log| { + ( + log.block_number.unwrap_or_default(), + log.transaction_index.unwrap_or_default(), + log.log_index.unwrap_or_default(), + ) + }); + + for candidate in candidates.into_iter().rev() { + let Some(transaction_hash) = candidate.transaction_hash else { + continue; + }; + if transaction_hash == canonical.transaction_hash { + continue; + } + let Some(block_number) = candidate.block_number else { + continue; + }; + let Some(block_hash) = candidate.block_hash else { + continue; + }; + let Some(transaction_index) = candidate.transaction_index else { + continue; + }; + let Some(transaction) = + self.l1_provider.get_transaction(transaction_hash).await.map_err(|e| { + ScalarError::Error(anyhow!(format!("get blob-source transaction: {e:?}"))) + })? + else { + continue; + }; + if transaction.to != Some(self.l1_rollup.address()) + || transaction.block_number != Some(block_number) + || transaction.block_hash != Some(block_hash) + || transaction.transaction_index != Some(transaction_index) + { + return Err(ScalarError::Error(anyhow!(format!( + "blob-source transaction {transaction_hash:?} changed block identity" + )))); + } + let Ok(candidate_input) = decode_batch_data_input(transaction.input.as_ref()) else { + continue; + }; + if candidate_input.version != version { + continue; + } + let hashes = transaction_blob_hashes(&transaction); + if hashes.is_empty() { + continue; + } + if blob_commitment(candidate_input.version, &hashes)? != expected_commitment { + continue; + } + + let after = self + .l1_provider + .get_block(snapshot_number) + .await + .map_err(|e| { + ScalarError::Error(anyhow!(format!("recheck finalized snapshot: {e:?}"))) + })? + .ok_or_else(|| ScalarError::Error(anyhow!("finalized snapshot disappeared")))?; + ensure_snapshot_hash(snapshot_hash, after.hash)?; + log::info!( + "resolved stored blob source: batch={}, canonical_tx={:?}, source_tx={:?}, source_block={}, blobs={}", + batch_index, + canonical.transaction_hash, + transaction_hash, + block_number, + hashes.len() + ); + return Ok(Some(BlobSource { + transaction_hash, + block_number, + block_hash, + transaction_index, + blob_hashes: hashes, + })); + } + + log::warn!( + "no historical blob source matches batch {} commitment {:?}; exclude and alert", + batch_index, + expected_commitment + ); + Ok(None) } async fn calculate_from_rollup( &mut self, - tx_hash: TxHash, - block_num: U64, + blob_source: BlobSource, + last_block_number: u64, ) -> Result<(u64, u64), ScalarError> { //Step1. get_data_from_blob let (l2_data_len, num_blobs, l2_txn) = - self.get_data_from_blob(tx_hash, block_num).await.map_err(|e| { + self.get_data_from_blob(&blob_source, last_block_number).await.map_err(|e| { log::error!("get_data_from_blob error: {:#?}", e); e })?; - let blob_tx_receipt = self + let source_tx_receipt = self .l1_provider - .get_transaction_receipt(tx_hash) + .get_transaction_receipt(blob_source.transaction_hash) .await .map_err(|e| ScalarError::Error(anyhow!(format!("{:#?}", e))))? .ok_or_else(|| { ScalarError::Error(anyhow!(format!( - "l1 get transaction receipt return none, tx_hash= {:#?}", - tx_hash + "l1 get source transaction receipt return none, tx_hash= {:#?}", + blob_source.transaction_hash ))) })?; + if !receipt_matches_source(&source_tx_receipt, &blob_source) { + return Err(ScalarError::Error(anyhow!( + "original blob source receipt identity mismatch" + ))); + } // rollup_gas_used - let rollup_gas_used = blob_tx_receipt.gas_used.unwrap_or_default(); + let rollup_gas_used = source_tx_receipt.gas_used.unwrap_or_default(); if rollup_gas_used.is_zero() { return Err(ScalarError::Error(anyhow!(format!( "blob tx calldata gas_used is none or 0, tx_hash = {:#?}", - tx_hash + blob_source.transaction_hash )))); } //Step2. Calculate scalar - let commit_scalar = (rollup_gas_used.as_u64() + self.finalize_batch_gas_used) * PRECISION / - l2_txn.max(self.txn_per_batch); + let commit_scalar = (rollup_gas_used.as_u64() + self.finalize_batch_gas_used) * PRECISION + / l2_txn.max(self.txn_per_batch); let blob_scalar = if l2_data_len > 0 { num_blobs.max(1) * MAX_BLOB_TX_PAYLOAD_SIZE as u64 * PRECISION / l2_data_len } else { @@ -270,84 +851,85 @@ impl ScalarUpdater { async fn get_data_from_blob( &self, - tx_hash: TxHash, - block_num: U64, + source: &BlobSource, + last_block_num: u64, ) -> Result<(u64, u64, u64), ScalarError> { let blob_tx = self .l1_provider - .get_transaction(tx_hash) + .get_transaction(source.transaction_hash) .await .map_err(|e| ScalarError::Error(anyhow!(format!("{:#?}", e))))? .ok_or_else(|| { ScalarError::Error(anyhow!(format!( "l1 get transaction return none, tx_hash: {:#?}", - tx_hash + source.transaction_hash ))) })?; + if blob_tx.to != Some(self.l1_rollup.address()) + || blob_tx.block_number != Some(source.block_number) + || blob_tx.block_hash != Some(source.block_hash) + || blob_tx.transaction_index != Some(source.transaction_index) + { + return Err(ScalarError::Error(anyhow!( + "blob source transaction changed block identity" + ))); + } let blob_block = self .l1_provider - .get_block_with_txs(BlockNumber::Number(block_num)) + .get_block_with_txs(BlockNumber::Number(source.block_number)) .await .map_err(|e| ScalarError::Error(anyhow!(format!("{:#?}", e))))? .ok_or_else(|| { ScalarError::Error(anyhow!(format!( "l1 get block info return none, block_num: {:#?}", - block_num + source.block_number ))) })?; + if blob_block.hash != Some(source.block_hash) { + return Err(ScalarError::Error(anyhow!( + "blob source block hash changed before sidecar lookup" + ))); + } let indexed_hashes = data_and_hashes_from_txs(&blob_block.transactions, &blob_tx); if indexed_hashes.is_empty() { - log::info!("no blob in this batch, batch_tx_hash: {:#?}", tx_hash); - return Ok((0, 0, 0)); + return Err(ScalarError::Error(anyhow!(format!( + "resolved blob source {:?} has no blob positions", + source.transaction_hash + )))); + } + let actual_hashes: Vec = indexed_hashes.iter().map(|item| item.hash).collect(); + if actual_hashes != source.blob_hashes { + return Err(ScalarError::Error(anyhow!( + "blob positions changed between source resolution and sidecar lookup" + ))); } - // Waiting for the next L1 block to be produced. - let next_block_num = block_num + 1; - // Max delay 5 * 3 = 15 secs - let mut retry_times = 5; - let prev_beacon_root = loop { - let blk_info = self.l1_provider.get_block(BlockNumber::Number(next_block_num)).await; - if let Ok(Some(info)) = blk_info { - if let Some(beacon_blk_root) = info.parent_beacon_block_root { - break beacon_blk_root; - } else { - return Err(ScalarError::Error(anyhow!(format!( - "next block info's pre_beacon_root is none, block number: {:?}", - next_block_num - )))); - } - } else if retry_times > 0 { - retry_times -= 1; - sleep(Duration::from_secs(3)).await; - - log::info!( - "request next block info, retry times= {:?}, block number: {:?}", - retry_times, - next_block_num - ); - continue; - } else { - return Err(ScalarError::Error(anyhow!(format!( - "maximum number of requests next block info reached: {:?}, block number:{:?}", - retry_times, next_block_num - )))); - } - }; - - // Parse last_block_num - if blob_tx.input.is_empty() { - log::warn!("batch inspect: tx.input is empty, tx_hash = {:#?}", tx_hash); - return Err(ScalarError::Error(anyhow!(format!("commitBatch tx.input empty")))); + // The sample is finalized, so the following block (whose parent beacon + // root addresses this execution block's sidecars) must already exist. + let next_block_num = + U64::from( + source.block_number.as_u64().checked_add(1).ok_or_else(|| { + ScalarError::Error(anyhow!("blob source block number overflow")) + })?, + ); + let next_block = self + .l1_provider + .get_block(BlockNumber::Number(next_block_num)) + .await + .map_err(|e| ScalarError::Error(anyhow!(format!("get next L1 block: {e:?}"))))? + .ok_or_else(|| ScalarError::Error(anyhow!("next finalized L1 block is unavailable")))?; + if next_block.parent_hash != source.block_hash { + return Err(ScalarError::Error(anyhow!( + "next L1 block is not the canonical child of the blob source block" + ))); } - let param = if let Ok(_param) = CommitBatchCall::decode(&blob_tx.input) { - _param - } else { - log::error!("batch inspect: decode tx.input error, tx_hash = {:#?}", tx_hash); - return Err(ScalarError::Error(anyhow!(format!("decode commitBatch tx.input error",)))); - }; - let last_block_num: u64 = param.batch_data_input.last_block_number; + let prev_beacon_root = next_block.parent_beacon_block_root.ok_or_else(|| { + ScalarError::Error(anyhow!(format!( + "next block has no parent beacon root, block number: {next_block_num:?}" + ))) + })?; let indexes: Vec = indexed_hashes.iter().map(|item| item.index).collect(); let sidecars_rt = self @@ -358,14 +940,14 @@ impl ScalarUpdater { let sidecars: &Vec = sidecars_rt["data"].as_array().ok_or_else(|| { ScalarError::Error(anyhow!(format!( "blob_sidecars is none, blk_num: {:?}, blk_root: {:?}", - block_num, prev_beacon_root + source.block_number, prev_beacon_root ))) })?; if sidecars.is_empty() { return Err(ScalarError::Error(anyhow!(format!( "blob_sidecars is empty, blk_num: {:?}, blk_root: {:?}", - block_num, prev_beacon_root + source.block_number, prev_beacon_root )))); } @@ -377,7 +959,11 @@ impl ScalarUpdater { let origin_batch = extract_tx_payload(indexed_hashes, sidecars)?; let batch_size = origin_batch.len() as u64; - let txn_count = extract_txn_count(&origin_batch, last_block_num).unwrap_or_default(); + let txn_count = extract_txn_count(&origin_batch, last_block_num).ok_or_else(|| { + ScalarError::CalculateError(anyhow!( + "blob payload block range does not match canonical lastBlockNumber" + )) + })?; Ok((batch_size, num_blobs, txn_count)) } @@ -389,6 +975,142 @@ mod tests { use super::*; use std::{env::var, str::FromStr, sync::Arc}; + #[test] + fn v2_blob_commitment_preserves_order() { + let a = H256::from_low_u64_be(1); + let b = H256::from_low_u64_be(2); + assert_ne!(blob_commitment(2, &[a, b]).unwrap(), blob_commitment(2, &[b, a]).unwrap()); + assert!(blob_commitment(2, &[]).is_err()); + assert!(blob_commitment(1, &[a, b]).is_err()); + } + + #[test] + fn fresh_blob_survives_mapping_gc_but_not_a_nonzero_mismatch() { + let actual = H256::from_low_u64_be(7); + assert!(validate_fresh_blob_commitment(H256::zero(), actual).is_ok()); + assert!(validate_fresh_blob_commitment(actual, actual).is_ok()); + assert!(validate_fresh_blob_commitment(H256::from_low_u64_be(8), actual).is_err()); + } + + #[test] + fn mapping_gc_recovery_prefers_snapshot_then_canonical_block() { + let snapshot = H256::from_low_u64_be(1); + let historical = H256::from_low_u64_be(2); + assert_eq!(recover_gc_commitment(snapshot, historical), snapshot); + assert_eq!(recover_gc_commitment(H256::zero(), historical), historical); + assert_eq!(recover_gc_commitment(H256::zero(), H256::zero()), H256::zero()); + } + + #[test] + fn zero_blobless_classification_preserves_legacy_but_rejects_current_commit_batch() { + assert_eq!( + classify_blobless_submission( + 0, + SubmissionKind::CommitBatch, + true, + ZERO_VERSIONED_HASH, + ) + .unwrap(), + BloblessDisposition::ExcludeZero + ); + assert_eq!( + classify_blobless_submission( + 1, + SubmissionKind::CommitBatchWithProof, + false, + ZERO_VERSIONED_HASH, + ) + .unwrap(), + BloblessDisposition::ExcludeZero + ); + assert_eq!( + classify_blobless_submission(1, SubmissionKind::CommitState, false, H256::zero(),) + .unwrap(), + BloblessDisposition::ExcludeMissing + ); + assert!(classify_blobless_submission( + 1, + SubmissionKind::CommitBatch, + false, + ZERO_VERSIONED_HASH, + ) + .is_err()); + } + + #[test] + fn blob_source_dedup_uses_full_stable_identity() { + let source = BlobSource { + transaction_hash: H256::from_low_u64_be(1), + block_number: 10.into(), + block_hash: H256::from_low_u64_be(10), + transaction_index: 0.into(), + blob_hashes: vec![H256::from_low_u64_be(11)], + }; + let mut sources = HashSet::new(); + assert!(sources.insert(source.clone())); + assert!(!sources.insert(source.clone())); + + let mut orphan_replacement = source; + orphan_replacement.block_hash = H256::from_low_u64_be(12); + assert!(sources.insert(orphan_replacement)); + } + + #[test] + fn source_receipt_must_match_full_stable_identity() { + let source = BlobSource { + transaction_hash: H256::from_low_u64_be(1), + block_number: 10.into(), + block_hash: H256::from_low_u64_be(10), + transaction_index: 2.into(), + blob_hashes: vec![H256::from_low_u64_be(11)], + }; + let mut receipt = TransactionReceipt { + transaction_hash: source.transaction_hash, + block_number: Some(source.block_number), + block_hash: Some(source.block_hash), + transaction_index: source.transaction_index, + ..Default::default() + }; + assert!(receipt_matches_source(&receipt, &source)); + receipt.block_hash = Some(H256::from_low_u64_be(12)); + assert!(!receipt_matches_source(&receipt, &source)); + } + + #[test] + fn commit_window_indices_are_unique_and_bounded() { + let topic0 = H256::from_low_u64_be(1); + let make_log = |index: u64| Log { + topics: vec![topic0, H256::from_low_u64_be(index)], + ..Default::default() + }; + let removed = Log { removed: Some(true), ..make_log(2) }; + let indices = batch_indices_from_commit_logs( + &[make_log(1), make_log(1), removed, make_log(3), make_log(9)], + 3, + ); + assert_eq!(indices.into_iter().collect::>(), vec![1, 3]); + } + + #[test] + fn historical_commit_replay_is_indexed_once_by_batch() { + let make_log = |index: u64| Log { + topics: vec![H256::zero(), H256::from_low_u64_be(index)], + ..Default::default() + }; + let removed = Log { removed: Some(true), ..make_log(2) }; + let index = index_commit_logs(vec![make_log(1), make_log(1), make_log(2), removed]); + assert_eq!(index.get(&1).unwrap().len(), 2); + assert_eq!(index.get(&2).unwrap().len(), 1); + } + + #[test] + fn changed_snapshot_hash_fails_closed() { + let expected = H256::from_low_u64_be(1); + assert!(ensure_snapshot_hash(expected, Some(expected)).is_ok()); + assert!(ensure_snapshot_hash(expected, Some(H256::from_low_u64_be(2))).is_err()); + assert!(ensure_snapshot_hash(expected, None).is_err()); + } + #[tokio::test] #[ignore] async fn test_calculate_from_current_rollup() { @@ -436,6 +1158,7 @@ mod tests { .expect("Cannot detect GAS_ORACLE_L1_BEACON_RPC env empty") .parse() .expect("Cannot parse GAS_ORACLE_L1_BEACON_RPC env var empty"), + 1u64, gas_threshold, 0u64, 0u64, @@ -443,8 +1166,16 @@ mod tests { 50u64, ); - let latest_overhead = - overhead.calculate_from_rollup(rollup_tx_hash, rollup_tx_block_num).await; + let source_tx = + overhead.l1_provider.get_transaction(rollup_tx_hash).await.unwrap().unwrap(); + let source = BlobSource { + transaction_hash: rollup_tx_hash, + block_number: rollup_tx_block_num, + block_hash: source_tx.block_hash.unwrap(), + transaction_index: source_tx.transaction_index.unwrap(), + blob_hashes: transaction_blob_hashes(&source_tx), + }; + let latest_overhead = overhead.calculate_from_rollup(source, 0).await; log::info!("latest_overhead: {:#?}", latest_overhead); } diff --git a/gas-oracle/app/src/da_scalar/mod.rs b/gas-oracle/app/src/da_scalar/mod.rs index ae0ed3313..ddc6862df 100644 --- a/gas-oracle/app/src/da_scalar/mod.rs +++ b/gas-oracle/app/src/da_scalar/mod.rs @@ -4,7 +4,7 @@ mod calculate; mod error; #[allow(clippy::module_inception)] pub mod l1_scalar; -mod zstd_util; mod typed_tx; +mod zstd_util; const MAX_BLOB_TX_PAYLOAD_SIZE: usize = 131072; // 131072 = 4096 * 32 = 1024 * 4 * 32 = 128kb diff --git a/gas-oracle/app/src/gas_price_oracle.rs b/gas-oracle/app/src/gas_price_oracle.rs index 74655abb3..ebff1bfed 100644 --- a/gas-oracle/app/src/gas_price_oracle.rs +++ b/gas-oracle/app/src/gas_price_oracle.rs @@ -4,7 +4,6 @@ use crate::{ l1_base_fee::BaseFeeUpdater, read_parse_env, }; -use remote_signer_client::SignerClient; use ethers::{ prelude::*, providers::{Http, Provider}, @@ -12,6 +11,7 @@ use ethers::{ types::Address, }; use eyre::anyhow; +use remote_signer_client::SignerClient; use std::{env::var, error::Error, str::FromStr, sync::Arc, time::Duration}; use tokio::time::sleep; @@ -31,6 +31,7 @@ struct Config { interval: u64, overhead_interval: u64, l1_rollup_address: Address, + l1_rollup_deploy_block: u64, l2_oracle_address: Address, private_key: String, l1_beacon_rpc: String, @@ -58,6 +59,7 @@ impl Config { interval: read_parse_env("INTERVAL"), overhead_interval: read_parse_env("OVERHEAD_INTERVAL"), l1_rollup_address: Address::from_str(&var("L1_ROLLUP").expect("L1_ROLLUP env"))?, + l1_rollup_deploy_block: read_parse_env("GAS_ORACLE_L1_ROLLUP_DEPLOY_BLOCK"), l2_oracle_address: Address::from_str( &var("L2_GAS_PRICE_ORACLE").expect("L2_GAS_PRICE_ORACLE env"), )?, @@ -125,6 +127,12 @@ fn check_config(config: &Config) -> Result<(), Box> { if config.txn_per_batch < 10u64 { return Err(anyhow!("Check env config error, TXN_PER_BATCH should be more than 10").into()); } + if config.l1_rollup_deploy_block == 0 { + return Err(anyhow!( + "Check env config error, GAS_ORACLE_L1_ROLLUP_DEPLOY_BLOCK must be non-zero" + ) + .into()); + } Ok(()) } @@ -219,6 +227,7 @@ async fn prepare_updater( ext_signer, l1_rollup, config.l1_beacon_rpc.clone(), + config.l1_rollup_deploy_block, config.gas_threshold, config.commit_scalar_buffer, config.blob_scalar_buffer, @@ -252,6 +261,7 @@ fn register_metrics() { REGISTRY.register(Box::new(ORACLE_SERVICE_METRICS.commit_scalar.clone())).unwrap(); REGISTRY.register(Box::new(ORACLE_SERVICE_METRICS.blob_scalar.clone())).unwrap(); REGISTRY.register(Box::new(ORACLE_SERVICE_METRICS.txn_per_batch.clone())).unwrap(); + REGISTRY.register(Box::new(ORACLE_SERVICE_METRICS.da_sample_exclusions.clone())).unwrap(); REGISTRY.register(Box::new(ORACLE_SERVICE_METRICS.gas_oracle_owner_balance.clone())).unwrap(); REGISTRY.register(Box::new(ORACLE_SERVICE_METRICS.l1_rpc_status.clone())).unwrap(); } diff --git a/gas-oracle/app/src/l1_base_fee.rs b/gas-oracle/app/src/l1_base_fee.rs index 833e018a6..699bd6891 100644 --- a/gas-oracle/app/src/l1_base_fee.rs +++ b/gas-oracle/app/src/l1_base_fee.rs @@ -145,9 +145,8 @@ impl BaseFeeUpdater { let client: Arc, LocalWallet>> = self.l2_oracle.client(); if need_update { // Update calldata basefee and blob baseFee - let call = self - .l2_oracle - .set_l1_base_fee_and_blob_base_fee(l1_base_fee, l1_blob_base_fee); + let call = + self.l2_oracle.set_l1_base_fee_and_blob_base_fee(l1_base_fee, l1_blob_base_fee); let tx_hash = send_transaction(call, &client, &self.ext_signer, &self.l2_provider) .await .map_err(|e| { @@ -215,11 +214,11 @@ async fn query_l1_base_fee( return Err(OracleError::L1BaseFeeError(anyhow!(format!( "Failed to query l1_gas_price: {:#?}", e - )))) + )))); } }; - Ok((l1_base_fee, U256::from(latest_blob_fee), gas_price)) + Ok((l1_base_fee, latest_blob_fee, gas_price)) } #[tokio::test] diff --git a/gas-oracle/app/src/lib.rs b/gas-oracle/app/src/lib.rs index 60e6fa0de..385121cbd 100644 --- a/gas-oracle/app/src/lib.rs +++ b/gas-oracle/app/src/lib.rs @@ -5,6 +5,7 @@ mod error; pub mod gas_price_oracle; mod l1_base_fee; mod metrics; +mod rollup_compat; mod da_scalar; mod signer; @@ -38,4 +39,4 @@ pub fn contract_error(e: ContractError) -> String { format!("error: {:#?}", e) }; error_msg -} \ No newline at end of file +} diff --git a/gas-oracle/app/src/metrics.rs b/gas-oracle/app/src/metrics.rs index f6201e4e2..de9d520fa 100644 --- a/gas-oracle/app/src/metrics.rs +++ b/gas-oracle/app/src/metrics.rs @@ -1,5 +1,5 @@ use lazy_static::lazy_static; -use prometheus::{Gauge, IntGauge, Registry}; +use prometheus::{Gauge, IntCounter, IntGauge, Registry}; pub struct OracleServiceMetrics { pub l1_base_fee: Gauge, @@ -10,6 +10,7 @@ pub struct OracleServiceMetrics { pub commit_scalar: IntGauge, pub blob_scalar: Gauge, pub txn_per_batch: Gauge, + pub da_sample_exclusions: IntCounter, pub l1_rpc_status: IntGauge, } @@ -27,6 +28,11 @@ lazy_static! { .expect("metric can be created"), blob_scalar: Gauge::new("blobScalar", "blob scalar").expect("metric can be created"), txn_per_batch: Gauge::new("txnPerBatch", "txn per batch").expect("metric can be created"), + da_sample_exclusions: IntCounter::new( + "daSampleExclusions", + "canonical DA samples excluded because their source was absent, duplicate, or unverifiable", + ) + .expect("metric can be created"), gas_oracle_owner_balance: Gauge::new("gasOracleOwnerBalance", "gas oracle owner balance") .expect("metric can be created"), l1_rpc_status: IntGauge::new("l1RpcStatus", "l1 prc").expect("metric can be created"), diff --git a/gas-oracle/app/src/rollup_compat.rs b/gas-oracle/app/src/rollup_compat.rs new file mode 100644 index 000000000..b31a5f89c --- /dev/null +++ b/gas-oracle/app/src/rollup_compat.rs @@ -0,0 +1,299 @@ +use ethers::{ + abi::AbiDecode, + contract::EthCall, + providers::Middleware, + types::{Address, BlockId, BlockNumber, Filter, Log, TxHash, H256, U256, U64}, + utils::keccak256, +}; +use eyre::{eyre, Result}; + +use crate::abi::{ + pre_submitter_rollup_abi::{ + CommitBatchCall as PreSubmitterCommitBatchCall, + CommitBatchWithProofCall as PreSubmitterCommitBatchWithProofCall, + CommitStateCall as PreSubmitterCommitStateCall, + }, + rollup_abi::{CommitBatchCall, CommitBatchWithProofCall, CommitStateCall, Rollup}, +}; + +#[derive(Clone, Copy, Debug, Eq, PartialEq)] +#[allow(clippy::enum_variant_names)] +pub enum SubmissionKind { + CommitBatch, + CommitState, + CommitBatchWithProof, +} + +#[derive(Clone, Debug, Eq, PartialEq)] +pub struct BatchDataInput { + pub version: u8, + pub last_block_number: u64, + pub kind: SubmissionKind, + pub pre_submitter: bool, +} + +macro_rules! decoded { + ($input:expr, $kind:expr, $pre:expr) => {{ + let input = $input; + BatchDataInput { + version: input.version, + last_block_number: input.last_block_number, + kind: $kind, + pre_submitter: $pre, + } + }}; +} + +/// Decode only the batch fields consumed by the oracle. The selector is +/// matched before ABI decoding so malformed or unknown calldata fails closed. +pub fn decode_batch_data_input(data: &[u8]) -> Result { + let selector = + data.get(..4).ok_or_else(|| eyre!("rollup calldata is shorter than a selector"))?; + + if selector == CommitBatchCall::selector() { + let call = CommitBatchCall::decode(data)?; + return Ok(decoded!(call.batch_data_input, SubmissionKind::CommitBatch, false)); + } + if selector == CommitStateCall::selector() { + let call = CommitStateCall::decode(data)?; + return Ok(decoded!(call.batch_data_input, SubmissionKind::CommitState, false)); + } + if selector == CommitBatchWithProofCall::selector() { + let call = CommitBatchWithProofCall::decode(data)?; + return Ok(decoded!(call.batch_data_input, SubmissionKind::CommitBatchWithProof, false)); + } + if selector == PreSubmitterCommitBatchCall::selector() { + let call = PreSubmitterCommitBatchCall::decode(data)?; + return Ok(decoded!(call.batch_data_input, SubmissionKind::CommitBatch, true)); + } + if selector == PreSubmitterCommitStateCall::selector() { + let call = PreSubmitterCommitStateCall::decode(data)?; + return Ok(decoded!(call.batch_data_input, SubmissionKind::CommitState, true)); + } + if selector == PreSubmitterCommitBatchWithProofCall::selector() { + let call = PreSubmitterCommitBatchWithProofCall::decode(data)?; + return Ok(decoded!(call.batch_data_input, SubmissionKind::CommitBatchWithProof, true)); + } + + Err(eyre!("unsupported rollup submission selector")) +} + +#[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd)] +pub struct LogIdentity { + pub block_number: u64, + pub transaction_index: u64, + pub log_index: u64, +} + +#[derive(Clone, Copy, Debug, Eq, PartialEq)] +pub struct CanonicalCommit { + pub identity: LogIdentity, + pub transaction_hash: TxHash, + pub block_hash: H256, + pub batch_hash: H256, +} + +fn identity(log: &Log) -> Option { + Some(LogIdentity { + block_number: log.block_number?.as_u64(), + transaction_index: log.transaction_index?.as_u64(), + log_index: log.log_index?.as_u64(), + }) +} + +/// Select the last same-batch Commit after the last Revert. Full log identity +/// deliberately distinguishes a same-hash recommit from the reverted event. +pub fn select_canonical_commit( + commits: &[Log], + reverts: &[Log], + canonical_hash: H256, +) -> Option { + if canonical_hash.is_zero() { + return None; + } + let last_revert = + reverts.iter().filter(|log| !log.removed.unwrap_or_default()).filter_map(identity).max(); + + commits + .iter() + .filter(|log| !log.removed.unwrap_or_default()) + .filter_map(|log| { + let identity = identity(log)?; + if last_revert.is_some_and(|revert| identity <= revert) { + return None; + } + if log.topics.len() < 3 || log.topics[2] != canonical_hash { + return None; + } + Some(CanonicalCommit { + identity, + transaction_hash: log.transaction_hash?, + block_hash: log.block_hash?, + batch_hash: log.topics[2], + }) + }) + .max_by_key(|commit| commit.identity) +} + +pub const LOG_QUERY_RANGE: u64 = 10_000; + +pub async fn query_batch_logs( + provider: &M, + rollup: Address, + batch_index: u64, + event_topic: H256, + from_block: u64, + to_block: u64, +) -> Result> { + if from_block > to_block { + return Ok(Vec::new()); + } + let mut logs = Vec::new(); + let mut from = from_block; + loop { + let to = from.saturating_add(LOG_QUERY_RANGE - 1).min(to_block); + let filter = Filter::new() + .address(rollup) + .from_block(from) + .to_block(to) + .topic0(event_topic) + .topic1(U256::from(batch_index)); + logs.extend(provider.get_logs(&filter).await.map_err(|err| eyre!(err.to_string()))?); + if to == to_block { + break; + } + from = to + 1; + } + Ok(logs) +} + +pub async fn resolve_canonical_commit( + rollup: &Rollup, + provider: &M, + batch_index: u64, + from_block: u64, + snapshot_number: U64, +) -> Result> { + let before = provider + .get_block(snapshot_number) + .await + .map_err(|err| eyre!(err.to_string()))? + .ok_or_else(|| eyre!("snapshot block {} not found", snapshot_number))?; + let before_hash = before.hash.ok_or_else(|| eyre!("snapshot block has no hash"))?; + let block_id = BlockId::Number(BlockNumber::Number(snapshot_number)); + let canonical_hash: H256 = rollup + .committed_batches(U256::from(batch_index)) + .block(block_id) + .call() + .await + .map_err(|err| eyre!(err.to_string()))? + .into(); + if canonical_hash.is_zero() { + return Ok(None); + } + + let commit_topic = H256::from(keccak256("CommitBatch(uint256,bytes32)")); + let revert_topic = H256::from(keccak256("RevertBatch(uint256,bytes32)")); + let commits = query_batch_logs( + provider, + rollup.address(), + batch_index, + commit_topic, + from_block, + snapshot_number.as_u64(), + ) + .await?; + let reverts = query_batch_logs( + provider, + rollup.address(), + batch_index, + revert_topic, + from_block, + snapshot_number.as_u64(), + ) + .await?; + + let after = provider + .get_block(snapshot_number) + .await + .map_err(|err| eyre!(err.to_string()))? + .ok_or_else(|| eyre!("snapshot block {} disappeared", snapshot_number))?; + if after.hash != Some(before_hash) { + return Err(eyre!("snapshot block hash changed during canonical resolution")); + } + + Ok(select_canonical_commit(&commits, &reverts, canonical_hash)) +} + +#[cfg(test)] +mod tests { + use super::*; + + fn log(block: u64, marker: u64, hash: H256) -> Log { + Log { + block_number: Some(block.into()), + transaction_index: Some(0.into()), + log_index: Some(0.into()), + transaction_hash: Some(H256::from_low_u64_be(marker)), + block_hash: Some(H256::from_low_u64_be(block)), + topics: vec![H256::zero(), H256::from_low_u64_be(7), hash], + ..Default::default() + } + } + + #[test] + fn rejects_short_and_unknown_calldata() { + assert!(decode_batch_data_input(&[]).is_err()); + assert!(decode_batch_data_input(&[0xde, 0xad, 0xbe, 0xef]).is_err()); + } + + #[test] + fn decodes_shared_pre_and_post_submitter_fixtures() { + let corpus: serde_json::Value = serde_json::from_str(include_str!( + "../../../node/derivation/testdata/rollup_calldata_fixtures.json" + )) + .unwrap(); + let mut decoded_count = 0; + for fixture in corpus["fixtures"].as_array().unwrap() { + let epoch = fixture["epoch"].as_str().unwrap(); + if epoch != "pre-submitter" && epoch != "current" { + continue; + } + let data = ethers::utils::hex::decode( + fixture["data"].as_str().unwrap().trim_start_matches("0x"), + ) + .unwrap(); + let decoded = decode_batch_data_input(&data) + .unwrap_or_else(|err| panic!("{}: {err:#}", fixture["name"])); + assert_eq!(decoded.version as u64, fixture["expected"]["version"].as_u64().unwrap()); + assert_eq!( + decoded.last_block_number, + fixture["expected"]["lastBlockNumber"].as_u64().unwrap() + ); + decoded_count += 1; + } + assert_eq!(decoded_count, 6); + } + + #[test] + fn selector_sets_are_frozen() { + assert_eq!(CommitBatchCall::selector(), [0x41, 0xf7, 0x56, 0xda]); + assert_eq!(CommitStateCall::selector(), [0x67, 0xca, 0xa3, 0x7a]); + assert_eq!(CommitBatchWithProofCall::selector(), [0x15, 0x44, 0xba, 0x3a]); + assert_eq!(PreSubmitterCommitBatchCall::selector(), [0x42, 0x88, 0x68, 0xb5]); + assert_eq!(PreSubmitterCommitStateCall::selector(), [0x1e, 0x88, 0x25, 0xbe]); + assert_eq!(PreSubmitterCommitBatchWithProofCall::selector(), [0x4e, 0x8f, 0x1d, 0x67]); + } + + #[test] + fn same_hash_recommit_after_revert_wins() { + let hash = H256::from_low_u64_be(99); + let resolved = select_canonical_commit( + &[log(10, 1, hash), log(12, 3, hash)], + &[log(11, 2, hash)], + hash, + ) + .unwrap(); + assert_eq!(resolved.transaction_hash, H256::from_low_u64_be(3)); + } +} diff --git a/gas-oracle/app/src/signer.rs b/gas-oracle/app/src/signer.rs index 7eb865a34..34db74586 100644 --- a/gas-oracle/app/src/signer.rs +++ b/gas-oracle/app/src/signer.rs @@ -122,8 +122,6 @@ fn eip1559_estimator(base_fee_per_gas: U256, _rewards: Vec>) -> (U256, (max_fee_per_gas, max_priority_fee_per_gas) } - - #[tokio::test] #[ignore] async fn test_estimate_eip1559_fees() { @@ -151,6 +149,7 @@ async fn test_estimate_eip1559_fees() { let tx_typed = tx.as_eip1559_mut().unwrap(); log::info!( "====tx_typed gas data: {:?}, {:?}", - tx_typed.max_fee_per_gas, tx_typed.max_priority_fee_per_gas + tx_typed.max_fee_per_gas, + tx_typed.max_priority_fee_per_gas ); } From 6936e97640be30c504282e3e884d892b5c65cfcf Mon Sep 17 00:00:00 2001 From: Segue Date: Tue, 11 Aug 2026 21:14:41 +0800 Subject: [PATCH 12/16] build(bindings): verify reproducible generated artifacts --- .../bindings/centralization_cleanup_test.go | 237 ++++++++++++++++++ bindings/compile.sh | 35 ++- bindings/gen_bindings.sh | 5 +- contracts/package.json | 3 +- contracts/scripts/clean-generated-abi.js | 16 ++ contracts/scripts/verify-abi-archive.js | 36 +++ 6 files changed, 318 insertions(+), 14 deletions(-) create mode 100644 bindings/bindings/centralization_cleanup_test.go create mode 100644 contracts/scripts/clean-generated-abi.js create mode 100644 contracts/scripts/verify-abi-archive.js diff --git a/bindings/bindings/centralization_cleanup_test.go b/bindings/bindings/centralization_cleanup_test.go new file mode 100644 index 000000000..1650aef50 --- /dev/null +++ b/bindings/bindings/centralization_cleanup_test.go @@ -0,0 +1,237 @@ +package bindings + +import ( + "bytes" + "encoding/hex" + "encoding/json" + "os" + "path/filepath" + "runtime" + "strings" + "testing" + + "github.com/morph-l2/go-ethereum/accounts/abi" +) + +const ( + oldCommitBatchSelector = "428868b5" + oldCommitStateSelector = "1e8825be" + oldCommitBatchWithProofSelector = "4e8f1d67" + newCommitBatchSelector = "41f756da" + newCommitStateSelector = "67caa37a" + newCommitBatchWithProofSelector = "1544ba3a" +) + +type calldataCorpus struct { + SchemaVersion int `json:"schemaVersion"` + Fixtures []struct { + Name string `json:"name"` + Epoch string `json:"epoch"` + Selector string `json:"selector"` + Data string `json:"data"` + } `json:"fixtures"` +} + +func repositoryRoot(t *testing.T) string { + t.Helper() + _, filename, _, ok := runtime.Caller(0) + if !ok { + t.Fatal("resolve test source path") + } + return filepath.Clean(filepath.Join(filepath.Dir(filename), "..", "..")) +} + +func readABI(t *testing.T, path string) abi.ABI { + t.Helper() + raw, err := os.Open(path) + if err != nil { + t.Fatalf("open ABI %s: %v", path, err) + } + defer raw.Close() + parsed, err := abi.JSON(raw) + if err != nil { + t.Fatalf("parse ABI %s: %v", path, err) + } + return parsed +} + +func methodSelector(t *testing.T, parsed abi.ABI, name string) string { + t.Helper() + method, ok := parsed.Methods[name] + if !ok { + t.Fatalf("ABI is missing method %s", name) + } + return hex.EncodeToString(method.ID) +} + +func TestGeneratedRollupBindingShape(t *testing.T) { + current, err := RollupMetaData.GetAbi() + if err != nil { + t.Fatalf("parse generated Rollup ABI: %v", err) + } + if current == nil { + t.Fatal("generated Rollup ABI is nil") + } + if strings.Contains(RollupMetaData.ABI, "BatchSignatureInput") { + t.Fatal("generated Rollup binding still contains BatchSignatureInput") + } + + expected := map[string]struct { + selector string + inputs int + }{ + "commitBatch": {newCommitBatchSelector, 1}, + "commitState": {newCommitStateSelector, 1}, + "commitBatchWithProof": {newCommitBatchWithProofSelector, 3}, + } + for name, want := range expected { + method, ok := current.Methods[name] + if !ok { + t.Fatalf("generated Rollup ABI is missing %s", name) + } + if got := hex.EncodeToString(method.ID); got != want.selector { + t.Fatalf("%s selector = %s, want %s", name, got, want.selector) + } + if got := len(method.Inputs); got != want.inputs { + t.Fatalf("%s input count = %d, want %d", name, got, want.inputs) + } + } + + batchDataStore, ok := current.Methods["batchDataStore"] + if !ok { + t.Fatal("generated Rollup ABI is missing batchDataStore") + } + if len(batchDataStore.Outputs) != 4 || batchDataStore.Outputs[3].Type.String() != "address" { + t.Fatalf("batchDataStore output shape = %v, want fourth output address", batchDataStore.Outputs) + } + if _, ok := current.Methods["pendingBatchCount"]; !ok { + t.Fatal("generated Rollup ABI is missing pendingBatchCount") + } +} + +func TestGeneratedSubmitterBindingShape(t *testing.T) { + current, err := SubmitterMetaData.GetAbi() + if err != nil { + t.Fatalf("parse generated Submitter ABI: %v", err) + } + if current == nil { + t.Fatal("generated Submitter ABI is nil") + } + for _, name := range []string{ + "addSubmitter", + "claimCredit", + "claimWithdrawal", + "initialize", + "isActive", + "removeSubmitter", + "slash", + "stake", + "withdraw", + } { + if _, ok := current.Methods[name]; !ok { + t.Fatalf("generated Submitter ABI is missing %s", name) + } + } + active := current.Methods["isActive"] + if len(active.Inputs) != 1 || len(active.Outputs) != 1 || active.Outputs[0].Type.String() != "bool" { + t.Fatalf("isActive ABI shape changed: inputs=%v outputs=%v", active.Inputs, active.Outputs) + } +} + +func TestPreSubmitterAndCurrentSelectorsRemainDistinct(t *testing.T) { + root := repositoryRoot(t) + preSubmitter := readABI(t, filepath.Join( + root, + "contracts", + "abi", + "archive", + "pre-submitter", + "Rollup.json", + )) + current, err := RollupMetaData.GetAbi() + if err != nil || current == nil { + t.Fatalf("parse generated Rollup ABI: %v", err) + } + + tests := []struct { + name string + oldSelector string + newSelector string + oldInputCount int + newInputCount int + }{ + {"commitBatch", oldCommitBatchSelector, newCommitBatchSelector, 2, 1}, + {"commitState", oldCommitStateSelector, newCommitStateSelector, 2, 1}, + {"commitBatchWithProof", oldCommitBatchWithProofSelector, newCommitBatchWithProofSelector, 4, 3}, + } + for _, test := range tests { + oldMethod := preSubmitter.Methods[test.name] + newMethod := current.Methods[test.name] + if got := methodSelector(t, preSubmitter, test.name); got != test.oldSelector { + t.Fatalf("pre-submitter %s selector = %s, want %s", test.name, got, test.oldSelector) + } + if got := methodSelector(t, *current, test.name); got != test.newSelector { + t.Fatalf("current %s selector = %s, want %s", test.name, got, test.newSelector) + } + if bytes.Equal(oldMethod.ID, newMethod.ID) { + t.Fatalf("pre-submitter and current %s selectors collide", test.name) + } + if len(oldMethod.Inputs) != test.oldInputCount || len(newMethod.Inputs) != test.newInputCount { + t.Fatalf( + "%s input counts = old %d/current %d, want old %d/current %d", + test.name, + len(oldMethod.Inputs), + len(newMethod.Inputs), + test.oldInputCount, + test.newInputCount, + ) + } + } +} + +func TestSupportedRollupSelectorsHaveNoCollisions(t *testing.T) { + root := repositoryRoot(t) + raw, err := os.ReadFile(filepath.Join( + root, + "node", + "derivation", + "testdata", + "rollup_calldata_fixtures.json", + )) + if err != nil { + t.Fatalf("read shared calldata corpus: %v", err) + } + var corpus calldataCorpus + if err := json.Unmarshal(raw, &corpus); err != nil { + t.Fatalf("parse shared calldata corpus: %v", err) + } + if corpus.SchemaVersion != 1 || len(corpus.Fixtures) != 8 { + t.Fatalf("shared calldata corpus shape = version %d/%d fixtures, want version 1/8 fixtures", corpus.SchemaVersion, len(corpus.Fixtures)) + } + + seen := make(map[string]string, len(corpus.Fixtures)) + for _, fixture := range corpus.Fixtures { + selector := strings.TrimPrefix(strings.ToLower(fixture.Selector), "0x") + data := strings.TrimPrefix(strings.ToLower(fixture.Data), "0x") + if len(selector) != 8 || len(data) < 8 || data[:8] != selector { + t.Fatalf("fixture %s has inconsistent selector/data", fixture.Name) + } + if previous, ok := seen[selector]; ok { + t.Fatalf("selector collision %s between %s and %s", selector, previous, fixture.Name) + } + seen[selector] = fixture.Name + } + + for _, selector := range []string{ + oldCommitBatchSelector, + oldCommitStateSelector, + oldCommitBatchWithProofSelector, + newCommitBatchSelector, + newCommitStateSelector, + newCommitBatchWithProofSelector, + } { + if _, ok := seen[selector]; !ok { + t.Fatalf("shared calldata corpus is missing protected selector %s", selector) + } + } +} diff --git a/bindings/compile.sh b/bindings/compile.sh index 936cec851..920a29368 100755 --- a/bindings/compile.sh +++ b/bindings/compile.sh @@ -1,4 +1,6 @@ -#/bin/bash +#!/usr/bin/env bash + +set -euo pipefail CONTRACTS_PATH="../contracts/" cd $CONTRACTS_PATH || exit @@ -13,33 +15,44 @@ forge_compile() { CONTRACTS_PATH="../contracts/" cd $CONTRACTS_PATH || exit yarn clean - sed -i '.bak' '10a\ -import "@foundry-rs/hardhat-forge";' hardhat.config.ts + + config_backup=$(mktemp) + foundry_backup=$(mktemp) + cp hardhat.config.ts "$config_backup" + cp foundry.toml "$foundry_backup" + restore_sources() { + cp "$config_backup" hardhat.config.ts + cp "$foundry_backup" foundry.toml + rm -f "$config_backup" "$foundry_backup" + } + trap restore_sources EXIT + + { + echo 'import "@foundry-rs/hardhat-forge";' + cat "$config_backup" + } > hardhat.config.ts yarn directory=$(pwd)"/contracts" # shellcheck disable=SC2034 main_dirname=$(basename "$directory") - backup_extension=".bak" - find "$directory" -mindepth 1 -maxdepth 1 -type d | while read dir; do dirname=$(basename "$dir") # shellcheck disable=SC2116 subdirs=$(echo "'contracts/$dirname'") echo "$subdirs" - sed -i "$backup_extension" "2s|.*|src = $subdirs|" foundry.toml + awk -v source="$subdirs" 'NR == 2 { $0 = "src = " source } { print }' \ + "$foundry_backup" > foundry.toml yarn hardhat compile done - sed -i "$backup_extension" "2s|.*|src = 'contracts'|" foundry.toml - sed -i "$backup_extension" '11s/.*//' hardhat.config.ts - rm -rf foundry.toml.bak - rm -rf hardhat.config.ts.bak + restore_sources + trap - EXIT } #!/bin/bash -if [ ! -n "$1" ]; then +if [ "$#" -eq 0 ]; then echo "hardhat compile" hardhat_compile else diff --git a/bindings/gen_bindings.sh b/bindings/gen_bindings.sh index 619f944f8..886d19d80 100755 --- a/bindings/gen_bindings.sh +++ b/bindings/gen_bindings.sh @@ -1,4 +1,4 @@ -#/bin/bash +#!/usr/bin/env bash set -eu CONTRACTS_PATH="../contracts/" @@ -54,4 +54,5 @@ abigen \ # Place import "github.com/ethereum/go-ethereum" to "github.com/morph-l2/go-ethereum" TO_REPLACED_STRING="github.com/ethereum/go-ethereum" NEW_STRING="github.com/morph-l2/go-ethereum" -sed -i "" -e "s#${TO_REPLACED_STRING}#${NEW_STRING}#g" ./${PACKAGE}/${TYPE_LOWER}.go +sed -i.bak -e "s#${TO_REPLACED_STRING}#${NEW_STRING}#g" ./${PACKAGE}/${TYPE_LOWER}.go +rm -f ./${PACKAGE}/${TYPE_LOWER}.go.bak diff --git a/contracts/package.json b/contracts/package.json index 810c56de1..7bf7d938c 100644 --- a/contracts/package.json +++ b/contracts/package.json @@ -23,9 +23,10 @@ "test": "yarn test:hardhat && yarn test:forge", "clean": "yarn clean:build && yarn clean:compile && yarn clean:tsc", "clean:build": "rm -rf ./dist ./cache ./cache-hardhat ./node_modules ./logs", - "clean:compile": "rm -rf ./idea ./forge_artifacts ./abi ./artifacts ./typechain-types ./forge-artifacts", + "clean:compile": "rm -rf ./idea ./forge_artifacts ./artifacts ./typechain-types ./forge-artifacts && node scripts/clean-generated-abi.js", "clean:tsc": "rm -rf ./tsconfig.build.tsbuildinfo ./src/contract-artifacts.ts", "typechain": "typechain --target ethers-v5 --out-dir dist/types --glob 'artifacts/!(build-info)/**/+([a-zA-Z0-9_]).json'", + "verify:abi-archive": "node scripts/verify-abi-archive.js", "deploy:devnetL1": "rm -f devnetL1.json && hardhat deploy --network l1 --storagepath devnetL1.json", "initialize:devnetL1": "hardhat initialize --network l1 --storagepath devnetL1.json", "register:devnetL1": "hardhat register --network l1 --storagepath devnetL1.json", diff --git a/contracts/scripts/clean-generated-abi.js b/contracts/scripts/clean-generated-abi.js new file mode 100644 index 000000000..c43402d14 --- /dev/null +++ b/contracts/scripts/clean-generated-abi.js @@ -0,0 +1,16 @@ +const fs = require('fs') +const path = require('path') + +const { archiveDir, verifyAbiArchive } = require('./verify-abi-archive') + +verifyAbiArchive() + +const abiDir = path.dirname(archiveDir) +for (const entry of fs.readdirSync(abiDir, { withFileTypes: true })) { + if (entry.name === path.basename(archiveDir)) { + continue + } + fs.rmSync(path.join(abiDir, entry.name), { recursive: true, force: true }) +} + +verifyAbiArchive() diff --git a/contracts/scripts/verify-abi-archive.js b/contracts/scripts/verify-abi-archive.js new file mode 100644 index 000000000..db6527658 --- /dev/null +++ b/contracts/scripts/verify-abi-archive.js @@ -0,0 +1,36 @@ +const crypto = require('crypto') +const fs = require('fs') +const path = require('path') + +const archiveDir = path.resolve(__dirname, '..', 'abi', 'archive') +const manifestPath = path.join(archiveDir, 'manifest.json') + +function verifyAbiArchive() { + if (!fs.existsSync(manifestPath)) { + throw new Error(`frozen ABI manifest is missing: ${manifestPath}`) + } + + const manifest = JSON.parse(fs.readFileSync(manifestPath, 'utf8')) + for (const [relativePath, expectedHash] of Object.entries(manifest.artifacts)) { + const artifactPath = path.resolve(archiveDir, relativePath) + if (!fs.existsSync(artifactPath)) { + throw new Error(`frozen ABI artifact is missing: ${artifactPath}`) + } + const actualHash = crypto + .createHash('sha256') + .update(fs.readFileSync(artifactPath)) + .digest('hex') + if (actualHash !== expectedHash) { + throw new Error( + `frozen ABI hash mismatch for ${relativePath}: expected ${expectedHash}, got ${actualHash}` + ) + } + } +} + +if (require.main === module) { + verifyAbiArchive() + console.log('Frozen ABI archive hashes verified') +} + +module.exports = { archiveDir, verifyAbiArchive } From e3106ce87ed5a9c0a3c33677c9ed8a9f9c504c1b Mon Sep 17 00:00:00 2001 From: Segue Date: Tue, 11 Aug 2026 21:16:51 +0800 Subject: [PATCH 13/16] build(gas-oracle): pin native KZG toolchain --- ops/docker/Dockerfile.oracle | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/ops/docker/Dockerfile.oracle b/ops/docker/Dockerfile.oracle index b25c1fdac..c0e2a5a3d 100644 --- a/ops/docker/Dockerfile.oracle +++ b/ops/docker/Dockerfile.oracle @@ -1,7 +1,7 @@ -FROM rust:1.88-slim as builder +FROM rust:1.91.0-slim as builder RUN apt-get -qq update && apt-get -qq install -y --no-install-recommends \ - pkg-config libssl-dev && rm -rf /var/lib/apt/lists/* + build-essential pkg-config libssl-dev && rm -rf /var/lib/apt/lists/* COPY ./gas-oracle /gas-oracle @@ -9,6 +9,8 @@ WORKDIR /gas-oracle RUN cd app && cargo build --release FROM ubuntu:22.04 as app +RUN apt-get -qq update && apt-get -qq install -y --no-install-recommends \ + ca-certificates && rm -rf /var/lib/apt/lists/* COPY --from=builder /gas-oracle/app/target/release/app / -CMD ["./app"] \ No newline at end of file +CMD ["./app"] From 93e512b856cec95acd0da23c4743bbd1f2c3bf80 Mon Sep 17 00:00:00 2001 From: Segue Date: Tue, 11 Aug 2026 21:24:12 +0800 Subject: [PATCH 14/16] fix(node): retain audited legacy validator path --- node/core/sequencers.go | 6 ++++++ node/derivation/canonical_commit.go | 2 +- node/go.mod | 2 +- 3 files changed, 8 insertions(+), 2 deletions(-) diff --git a/node/core/sequencers.go b/node/core/sequencers.go index c19f9335e..0ed154285 100644 --- a/node/core/sequencers.go +++ b/node/core/sequencers.go @@ -12,6 +12,12 @@ import ( const tmKeySize = ed25519.PubKeySize +// Keep the protected pre-cutover update path addressable for symbol-level +// audits even though all production entry points now use validatorSetAtHeight. +// The method bodies below intentionally remain unchanged apart from removing +// the old syncer-start side effect. +var _ = (*Executor).updateSequencerSet + func cloneValidatorKeys(keys [][]byte) [][]byte { cloned := make([][]byte, len(keys)) for i := range keys { diff --git a/node/derivation/canonical_commit.go b/node/derivation/canonical_commit.go index 9d3b6b454..09241d010 100644 --- a/node/derivation/canonical_commit.go +++ b/node/derivation/canonical_commit.go @@ -257,7 +257,7 @@ func ResolveStoredBlobSourceAtSnapshot( if err != nil { return nil, fmt.Errorf("decode stored blob source transaction %s: %w", log.TxHash.Hex(), err) } - if !storedBlobHashMatches(uint(batch.Version), blobHashes, storedHash) { + if !storedBlobHashMatches(batch.Version, blobHashes, storedHash) { continue } source = &StoredBlobSource{ diff --git a/node/go.mod b/node/go.mod index fd46c9d89..2b127d3aa 100644 --- a/node/go.mod +++ b/node/go.mod @@ -12,6 +12,7 @@ require ( github.com/hashicorp/golang-lru v1.0.2 github.com/hashicorp/raft v1.7.3 github.com/hashicorp/raft-boltdb/v2 v2.3.1 + github.com/holiman/uint256 v1.2.4 github.com/mdlayher/vsock v1.2.1 github.com/morph-l2/go-ethereum v1.10.14-0.20260810084402-5a13ad4d76fe github.com/pkg/errors v0.9.1 @@ -73,7 +74,6 @@ require ( github.com/hashicorp/go-uuid v1.0.1 // indirect github.com/hashicorp/hcl v1.0.0 // indirect github.com/holiman/bloomfilter/v2 v2.0.3 // indirect - github.com/holiman/uint256 v1.2.4 // indirect github.com/huin/goupnp v1.3.0 // indirect github.com/iden3/go-iden3-crypto v0.0.16 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect From 434bb7ed123651f8cd7f5f9b246610393253bd6e Mon Sep 17 00:00:00 2001 From: Segue Date: Tue, 11 Aug 2026 21:26:03 +0800 Subject: [PATCH 15/16] ci: gate centralization cleanup consumers --- .github/workflows/bindings.yml | 60 ++++++++++++++++++++++++++++++ .github/workflows/cherry-pick.yml | 4 +- .github/workflows/common.yml | 37 ++++++++++++++++++ .github/workflows/contracts.yml | 21 +++++++---- .github/workflows/deployer.yml | 9 +++-- .github/workflows/gas-oracle.yml | 22 +++++++++-- .github/workflows/node.yml | 9 +++-- .github/workflows/ops.yml | 52 ++++++++++++++++++++++++++ .github/workflows/prover.yml | 35 +++++++++++++---- .github/workflows/tx-submitter.yml | 12 ++++-- build/lint.go | 2 +- 11 files changed, 230 insertions(+), 33 deletions(-) create mode 100644 .github/workflows/bindings.yml create mode 100644 .github/workflows/common.yml create mode 100644 .github/workflows/ops.yml diff --git a/.github/workflows/bindings.yml b/.github/workflows/bindings.yml new file mode 100644 index 000000000..eb0b09fd2 --- /dev/null +++ b/.github/workflows/bindings.yml @@ -0,0 +1,60 @@ +name: Bindings + +on: + push: + branches: + - main + paths: + - "build/lint.go" + - "bindings/**" + - "contracts/**" + - ".github/workflows/bindings.yml" + pull_request: + paths: + - "build/lint.go" + - "bindings/**" + - "contracts/**" + - ".github/workflows/bindings.yml" + +permissions: + contents: read + +jobs: + generate-and-test: + name: generate and test + runs-on: ubuntu-latest + timeout-minutes: 60 + steps: + - uses: actions/checkout@v4 + - name: Install Go + uses: actions/setup-go@v5 + with: + go-version: 1.24.x + - name: Install Node + uses: actions/setup-node@v4 + with: + node-version: 18.x + cache: yarn + cache-dependency-path: contracts/yarn.lock + - name: Install pinned Foundry + uses: foundry-rs/foundry-toolchain@v1 + with: + version: nightly-2024-02-23 + - name: Install pinned abigen + run: | + mkdir -p "$RUNNER_TEMP/bin" + GOBIN="$RUNNER_TEMP/bin" go install github.com/ethereum/go-ethereum/cmd/abigen@v1.14.6 + echo "$RUNNER_TEMP/bin" >> "$GITHUB_PATH" + - name: Regenerate bindings + run: | + yarn --cwd contracts verify:abi-archive + make -C bindings all + yarn --cwd contracts verify:abi-archive + - name: Verify generated files are committed + run: >- + git diff --exit-code -- + bindings/bindings bindings/bin contracts/abi/archive + - name: Run bindings tests + run: make -C bindings test + - name: Lint bindings + run: make -C bindings lint diff --git a/.github/workflows/cherry-pick.yml b/.github/workflows/cherry-pick.yml index 698d82818..e43bc1161 100644 --- a/.github/workflows/cherry-pick.yml +++ b/.github/workflows/cherry-pick.yml @@ -15,7 +15,7 @@ jobs: if: ${{ contains(github.event.pull_request.labels.*.name, 'release/0.1.x') && github.event.pull_request.merged == true }} steps: - name: Checkout - uses: actions/checkout@v2 + uses: actions/checkout@v4 with: fetch-depth: 0 - name: Cherry pick into release/0.1.x @@ -31,7 +31,7 @@ jobs: if: ${{ contains(github.event.pull_request.labels.*.name, 'release/0.2.x') && github.event.pull_request.merged == true }} steps: - name: Checkout - uses: actions/checkout@v2 + uses: actions/checkout@v4 with: fetch-depth: 0 - name: Cherry pick into release/0.2.x diff --git a/.github/workflows/common.yml b/.github/workflows/common.yml new file mode 100644 index 000000000..194bf565f --- /dev/null +++ b/.github/workflows/common.yml @@ -0,0 +1,37 @@ +name: Common + +on: + push: + branches: + - main + paths: + - "bindings/**" + - "common/**" + - "node/derivation/testdata/rollup_calldata_fixtures.json" + - ".github/workflows/common.yml" + pull_request: + paths: + - "bindings/**" + - "common/**" + - "node/derivation/testdata/rollup_calldata_fixtures.json" + - ".github/workflows/common.yml" + +permissions: + contents: read + +defaults: + run: + working-directory: "common" + +jobs: + test: + name: race tests + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - name: Install Go + uses: actions/setup-go@v5 + with: + go-version: 1.24.x + - name: Run tests with race detector + run: go test -race ./... diff --git a/.github/workflows/contracts.yml b/.github/workflows/contracts.yml index 8d3a270d4..40c6b0418 100644 --- a/.github/workflows/contracts.yml +++ b/.github/workflows/contracts.yml @@ -5,14 +5,15 @@ on: branches: - main paths: + - "build/**" - "contracts/**" - - ".github/workflows/contracts.yaml" + - ".github/workflows/contracts.yml" pull_request: paths: - "build/**" - "contracts/**" - "ops/l2-genesis/**" - - ".github/workflows/contracts.yaml" + - ".github/workflows/contracts.yml" permissions: contents: read @@ -26,15 +27,21 @@ jobs: if: github.event.pull_request.draft == false runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Install Node - uses: actions/setup-node@v3 + uses: actions/setup-node@v4 with: node-version: 18.x + - name: Install Go + uses: actions/setup-go@v5 + with: + go-version: 1.24.x - name: Install Solhint run: npm install -g solhint - - name: Lint + - name: Lint Solidity run: make lint-sol + - name: Lint Go + run: make lint-go test: runs-on: ubuntu-latest @@ -42,9 +49,9 @@ jobs: FORCE_COLOR: 1 GAS: true steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Install Node - uses: actions/setup-node@v3 + uses: actions/setup-node@v4 with: node-version: 18.x - name: Install Go diff --git a/.github/workflows/deployer.yml b/.github/workflows/deployer.yml index 184680c6b..50e976db4 100644 --- a/.github/workflows/deployer.yml +++ b/.github/workflows/deployer.yml @@ -5,15 +5,16 @@ on: branches: - main paths: + - "build/**" - "ops/l2-genesis/**" - - ".github/workflows/deployer.yaml" + - ".github/workflows/deployer.yml" pull_request: paths: - "build/**" - "bindings/**" - "node/**" - "ops/l2-genesis/**" - - ".github/workflows/deployer.yaml" + - ".github/workflows/deployer.yml" permissions: contents: read @@ -27,7 +28,7 @@ jobs: if: github.event.pull_request.draft == false runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Install Go uses: actions/setup-go@v5 with: @@ -39,7 +40,7 @@ jobs: test: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Install Go uses: actions/setup-go@v5 with: diff --git a/.github/workflows/gas-oracle.yml b/.github/workflows/gas-oracle.yml index ab1e3ea80..51149f7aa 100644 --- a/.github/workflows/gas-oracle.yml +++ b/.github/workflows/gas-oracle.yml @@ -6,11 +6,15 @@ on: - main paths: - "gas-oracle/**" - - ".github/workflows/gas-oracle.yaml" + - "node/derivation/testdata/rollup_calldata_fixtures.json" + - "ops/docker/Dockerfile.oracle" + - ".github/workflows/gas-oracle.yml" pull_request: paths: - "gas-oracle/**" - - ".github/workflows/gas-oracle.yaml" + - "node/derivation/testdata/rollup_calldata_fixtures.json" + - "ops/docker/Dockerfile.oracle" + - ".github/workflows/gas-oracle.yml" permissions: contents: read @@ -23,8 +27,20 @@ jobs: check: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 + - name: Install pinned Rust toolchain + uses: dtolnay/rust-toolchain@master + with: + toolchain: 1.91.0 + components: rustfmt, clippy - name: Run build run: make build - name: Run tests run: make test + - name: Check formatting + run: make fmt + - name: Run clippy + run: make clippy + - name: Build oracle container + working-directory: ${{ github.workspace }} + run: docker build -f ops/docker/Dockerfile.oracle . diff --git a/.github/workflows/node.yml b/.github/workflows/node.yml index 8b9773d3e..559606c94 100644 --- a/.github/workflows/node.yml +++ b/.github/workflows/node.yml @@ -5,15 +5,16 @@ on: branches: - main paths: + - "build/**" - "node/**" - - ".github/workflows/node.yaml" + - ".github/workflows/node.yml" pull_request: paths: - "build/**" - "bindings/**" - "node/**" - "ops/l2-genesis/**" - - ".github/workflows/node.yaml" + - ".github/workflows/node.yml" permissions: contents: read @@ -27,7 +28,7 @@ jobs: if: github.event.pull_request.draft == false runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Install Go uses: actions/setup-go@v5 with: @@ -39,7 +40,7 @@ jobs: test: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Install Go uses: actions/setup-go@v5 with: diff --git a/.github/workflows/ops.yml b/.github/workflows/ops.yml new file mode 100644 index 000000000..00037a109 --- /dev/null +++ b/.github/workflows/ops.yml @@ -0,0 +1,52 @@ +name: Ops + +on: + push: + branches: + - main + paths: + - "build/**" + - "node/ops-morph/**" + - "ops/devnet-morph/**" + - "ops/docker/**" + - "ops/tools/**" + - ".github/workflows/ops.yml" + pull_request: + paths: + - "build/**" + - "node/ops-morph/**" + - "ops/devnet-morph/**" + - "ops/docker/**" + - "ops/tools/**" + - ".github/workflows/ops.yml" + +permissions: + contents: read + +jobs: + go-lint: + name: ops tools lint + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - name: Install Go + uses: actions/setup-go@v5 + with: + go-version: 1.24.x + - name: Lint ops tools + run: make -C ops/tools lint + + devnet-config: + name: devnet config tests + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - name: Run deterministic devnet tests + run: python3 -m unittest discover -s ops/devnet-morph/tests -v + - name: Validate Compose configuration + working-directory: ops/docker + run: >- + docker compose + -f docker-compose-devnet.yml + -f docker-compose-cluster.yml + config --quiet diff --git a/.github/workflows/prover.yml b/.github/workflows/prover.yml index 8e611539a..bdc222c9f 100644 --- a/.github/workflows/prover.yml +++ b/.github/workflows/prover.yml @@ -5,12 +5,14 @@ on: branches: - main paths: + - "node/derivation/testdata/rollup_calldata_fixtures.json" - "prover/**" - - ".github/workflows/prover.yaml" + - ".github/workflows/prover.yml" pull_request: paths: + - "node/derivation/testdata/rollup_calldata_fixtures.json" - "prover/**" - - ".github/workflows/prover.yaml" + - ".github/workflows/prover.yml" permissions: contents: read @@ -26,15 +28,32 @@ jobs: timeout-minutes: 30 steps: - uses: actions/checkout@v4 - - uses: dtolnay/rust-toolchain@stable - - run: rustup component add rustfmt --toolchain nightly-2024-07-07 - - run: cargo fmt --all --check + - uses: dtolnay/rust-toolchain@master + with: + toolchain: nightly-2024-07-07 + components: rustfmt + - name: Check shadow-prove formatting + run: cargo +nightly-2024-07-07 fmt --package shadow-proving --check + - name: Check challenge formatting + run: >- + cargo +nightly-2024-07-07 fmt + --manifest-path bin/challenge/Cargo.toml + --check build: name: build runs-on: ubuntu-latest - timeout-minutes: 30 + timeout-minutes: 60 steps: - uses: actions/checkout@v4 - - uses: dtolnay/rust-toolchain@stable - - run: make build-shadow-prove && make build-shadow-prove + - uses: dtolnay/rust-toolchain@master + with: + toolchain: 1.91.0 + - name: Build shadow-prove + run: make build-shadow-prove + - name: Test shadow-prove + run: cargo test --manifest-path bin/shadow-prove/Cargo.toml + - name: Build challenge + run: make build-challenge + - name: Test challenge + run: cargo test --manifest-path bin/challenge/Cargo.toml diff --git a/.github/workflows/tx-submitter.yml b/.github/workflows/tx-submitter.yml index ae8fec5b3..171e9b56b 100644 --- a/.github/workflows/tx-submitter.yml +++ b/.github/workflows/tx-submitter.yml @@ -5,16 +5,20 @@ on: branches: - main paths: + - "build/**" + - "bindings/**" + - "common/**" - "tx-submitter/**" - - ".github/workflows/tx-submitter.yaml" + - ".github/workflows/tx-submitter.yml" pull_request: paths: - "build/**" - "bindings/**" + - "common/**" - "node/**" - "ops/l2-genesis/**" - "tx-submitter/**" - - ".github/workflows/tx-submitter.yaml" + - ".github/workflows/tx-submitter.yml" permissions: contents: read @@ -28,7 +32,7 @@ jobs: if: github.event.pull_request.draft == false runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Install Go uses: actions/setup-go@v5 with: @@ -40,7 +44,7 @@ jobs: test: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Install Go uses: actions/setup-go@v5 with: diff --git a/build/lint.go b/build/lint.go index e12ac1cc0..002a7d1f9 100644 --- a/build/lint.go +++ b/build/lint.go @@ -15,7 +15,7 @@ import ( const ( // GolangCIVersion to be used for linting. - GolangCIVersion = "github.com/golangci/golangci-lint/cmd/golangci-lint@latest" + GolangCIVersion = "github.com/golangci/golangci-lint/cmd/golangci-lint@v1.64.8" ) // GOBIN environment variable. From 2550618f1267f2c4577eeb176d463fa739f6d9fe Mon Sep 17 00:00:00 2001 From: Segue Date: Tue, 11 Aug 2026 21:29:37 +0800 Subject: [PATCH 16/16] fix(prover): cache canonical logs and fail over signers --- prover/bin/challenge/.env.dev | 4 + .../bin/challenge/src/bin/auto_challenge.rs | 19 +- prover/bin/challenge/src/handler.rs | 168 ++++----- prover/bin/challenge/src/lib.rs | 1 + prover/bin/challenge/src/main.rs | 2 + prover/bin/challenge/src/metrics.rs | 3 + prover/bin/challenge/src/proof_signer.rs | 155 ++++++++ prover/bin/challenge/src/rollup_compat.rs | 290 ++++++++++++--- prover/bin/shadow-prove/src/rollup_compat.rs | 340 ++++++++++++++---- prover/bin/shadow-prove/src/shadow_rollup.rs | 54 ++- 10 files changed, 795 insertions(+), 241 deletions(-) create mode 100644 prover/bin/challenge/src/proof_signer.rs diff --git a/prover/bin/challenge/.env.dev b/prover/bin/challenge/.env.dev index 847cbb13e..e0b5e4a3e 100644 --- a/prover/bin/challenge/.env.dev +++ b/prover/bin/challenge/.env.dev @@ -7,6 +7,10 @@ HANDLER_L1_ROLLUP = "0x5FbDB2315678afecb367f032d93F642f64180aa3" # deployment block from the deployment manifest. HANDLER_L1_ROLLUP_DEPLOY_BLOCK = 1 CHALLENGE_HANDLER_PRIVATE_KEY = "0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80" +# Comma-separated, explicitly approved local failover keys. The primary key is +# always evaluated first and no backup is used unless its address is active at +# the same finalized L1 snapshot. +CHALLENGE_HANDLER_BACKUP_PRIVATE_KEYS = "" SHADOW_PROVING = true # challenger diff --git a/prover/bin/challenge/src/bin/auto_challenge.rs b/prover/bin/challenge/src/bin/auto_challenge.rs index 2d41cda37..29aa69c96 100644 --- a/prover/bin/challenge/src/bin/auto_challenge.rs +++ b/prover/bin/challenge/src/bin/auto_challenge.rs @@ -1,5 +1,5 @@ use challenge_handler::abi::rollup_abi::Rollup; -use challenge_handler::rollup_compat::{challenge_deposit_at, resolve_canonical_commit}; +use challenge_handler::rollup_compat::{challenge_deposit_at, CanonicalLogIndex}; use challenge_handler::util::read_parse_env; use dotenv::dotenv; use env_logger::Env; @@ -69,13 +69,20 @@ pub async fn challenge() -> Result<(), Box> { let proof_window = l1_rollup.proof_window().await?; log::info!("finalization_period: {:#?} proof_window: {:#?}", finalization_period, proof_window); + let mut canonical_log_index = CanonicalLogIndex::new(rollup_deployed_block)?; loop { tokio::time::sleep(Duration::from_secs(12)).await; - let _ = auto_challenge(&l1_provider, &l1_rollup, rollup_deployed_block).await; + if let Err(error) = auto_challenge(&l1_provider, &l1_rollup, &mut canonical_log_index).await { + log::error!("ALERT: auto-challenge iteration failed closed: {error:#}"); + } } } -async fn auto_challenge(l1_provider: &Provider, l1_rollup: &RollupType, rollup_deployed_block: u64) -> Result<(), Box> { +async fn auto_challenge( + l1_provider: &Provider, + l1_rollup: &RollupType, + canonical_log_index: &mut CanonicalLogIndex, +) -> Result<(), Box> { // Search for the latest batch. let (latest, snapshot_hash) = match l1_provider.get_block(BlockNumber::Finalized).await { Ok(Some(block)) => match (block.number, block.hash) { @@ -114,7 +121,11 @@ async fn auto_challenge(l1_provider: &Provider, l1_rollup: &RollupType, ro let block_id = BlockId::Number(BlockNumber::Number(latest)); let min_deposit = challenge_deposit_at(l1_rollup, block_id).await?; let batch_index = l1_rollup.last_committed_batch_index().block(block_id).call().await?.as_u64(); - if resolve_canonical_commit(l1_rollup, l1_provider, batch_index, rollup_deployed_block, latest) + canonical_log_index + .refresh(l1_provider, l1_rollup.address(), latest, snapshot_hash) + .await?; + if canonical_log_index + .resolve(l1_rollup, batch_index, latest, snapshot_hash) .await? .is_none() { diff --git a/prover/bin/challenge/src/handler.rs b/prover/bin/challenge/src/handler.rs index 572d17a7a..68e199dd9 100644 --- a/prover/bin/challenge/src/handler.rs +++ b/prover/bin/challenge/src/handler.rs @@ -1,9 +1,8 @@ use crate::abi::rollup_abi::Rollup; use crate::external_sign::ExternalSign; use crate::metrics::METRICS; -use crate::rollup_compat::{ - authority_is_active, decode_batch_data_input, decode_batch_data_store_block_number, resolve_canonical_commit, CanonicalCommit, -}; +use crate::proof_signer::{select_active_proof_signer, ApprovedProofSigner}; +use crate::rollup_compat::{decode_batch_data_input, decode_batch_data_store_block_number, CanonicalCommit, CanonicalLogIndex}; use crate::util::read_env_var; use crate::util::{self, read_parse_env}; use ethers::prelude::*; @@ -18,6 +17,7 @@ use std::error::Error; use std::str::FromStr; use std::sync::Arc; use std::time::Duration; +use tokio::sync::Mutex; use tokio::time::sleep; use transaction::eip2718::TypedTransaction; @@ -53,8 +53,8 @@ pub struct ChallengeHandler { l1_rollup: RollupType, l1_provider: Provider, l2_rpc: String, - ext_signer: Option, - rollup_deployed_block: u64, + proof_signers: Vec, + canonical_log_index: Arc>, } impl ChallengeHandler { @@ -65,6 +65,9 @@ impl ChallengeHandler { let l1_rollup_address = var("HANDLER_L1_ROLLUP").expect("Cannot detect L1_ROLLUP env var"); let _ = var("HANDLER_PROVER_RPC").expect("Cannot detect PROVER_RPC env var"); let rollup_deployed_block: u64 = read_parse_env("HANDLER_L1_ROLLUP_DEPLOY_BLOCK"); + let canonical_log_index = Arc::new(Mutex::new( + CanonicalLogIndex::new(rollup_deployed_block).expect("HANDLER_L1_ROLLUP_DEPLOY_BLOCK must be non-zero"), + )); let private_key = read_env_var( "CHALLENGE_HANDLER_PRIVATE_KEY", @@ -72,17 +75,14 @@ impl ChallengeHandler { ); let l1_provider: Provider = Provider::::try_from(l1_rpc).unwrap(); - let l1_signer = Arc::new(SignerMiddleware::new( - l1_provider.clone(), - Wallet::from_str(private_key.as_str()) - .unwrap() - .with_chain_id(l1_provider.get_chainid().await.unwrap().as_u64()), - )); + let chain_id = l1_provider.get_chainid().await.unwrap().as_u64(); + let local_wallet = Wallet::from_str(private_key.as_str()).unwrap().with_chain_id(chain_id); + let l1_signer = Arc::new(SignerMiddleware::new(l1_provider.clone(), local_wallet.clone())); let l1_rollup: RollupType = Rollup::new(Address::from_str(l1_rollup_address.as_str()).unwrap(), l1_signer); let use_ext_sign: bool = read_env_var("HANDLER_EXTERNAL_SIGN", false); - let ext_signer = if use_ext_sign { + let primary_signer = if use_ext_sign { log::info!("Challenge handler will use external signer"); let handler_appid: String = read_parse_env("HANDLER_EXTERNAL_SIGN_APPID"); let privkey_pem: String = read_parse_env("HANDLER_EXTERNAL_SIGN_RSA_PRIV"); @@ -92,18 +92,32 @@ impl ChallengeHandler { let signer: ExternalSign = ExternalSign::new(&handler_appid, &privkey_pem, &sign_address, &sign_chain, &sign_url) .map_err(|e| anyhow!(format!("Prepare ExternalSign err: {:?}", e))) .unwrap(); - Some(signer) + let address = Address::from_str(&signer.address).expect("HANDLER_EXTERNAL_SIGN_ADDRESS must be a valid address"); + ApprovedProofSigner::external(address, signer) } else { log::info!("Challenge handler will use local signer"); - None + ApprovedProofSigner::local(local_wallet) }; + let mut proof_signers = vec![primary_signer]; + let backup_private_keys: String = read_env_var("CHALLENGE_HANDLER_BACKUP_PRIVATE_KEYS", String::new()); + for backup_private_key in backup_private_keys.split(',').map(str::trim).filter(|key| !key.is_empty()) { + let wallet = Wallet::from_str(backup_private_key) + .expect("CHALLENGE_HANDLER_BACKUP_PRIVATE_KEYS contains an invalid private key") + .with_chain_id(chain_id); + if proof_signers.iter().any(|approved| approved.address() == wallet.address()) { + panic!("CHALLENGE_HANDLER_BACKUP_PRIVATE_KEYS contains a duplicate approved signer"); + } + proof_signers.push(ApprovedProofSigner::local(wallet)); + } + log::info!("Configured {} ordered approved proof signer(s)", proof_signers.len()); + Self { l1_rollup, l1_provider, l2_rpc, - ext_signer, - rollup_deployed_block, + proof_signers, + canonical_log_index, } } @@ -150,11 +164,7 @@ impl ChallengeHandler { }; log::info!("Current L1 block number: {:#?}", latest); - let wallet = if let Some(signer) = &self.ext_signer { - Address::from_str(&signer.address).unwrap_or_default() - } else { - self.l1_rollup.client().address() - }; + let wallet = self.proof_signers[0].address(); // Record wallet balance. let balance = match l1_provider.get_balance(wallet, None).await { Ok(b) => b, @@ -177,11 +187,11 @@ impl ChallengeHandler { METRICS.detected_batch_index.set(batch_index as i64); // Step3. query challenged batch info. - let (challenged_commit, batch_hash) = match query_batch_tx(latest, l1_rollup, batch_index, self.rollup_deployed_block, l1_provider).await - { - Some(value) => value, - None => continue, - }; + let (challenged_commit, batch_hash) = + match query_batch_tx(latest, snapshot_hash, l1_rollup, batch_index, &self.canonical_log_index, l1_provider).await { + Some(value) => value, + None => continue, + }; let mut batch_info = match batch_inspect(l1_rollup, l1_provider, batch_index, &challenged_commit, latest).await { Some(mut b) => { @@ -277,24 +287,39 @@ impl ChallengeHandler { } async fn prove_state(&self, batch_index: u64, batch_header: Bytes, batch_proof: ProveResult, l1_rollup: &RollupType) -> bool { - let signer = if let Some(external) = &self.ext_signer { - Address::from_str(&external.address).unwrap_or_default() - } else { - l1_rollup.client().address() - }; for _ in 0..MAX_RETRY_TIMES { sleep(Duration::from_secs(12)).await; - if !self.proof_signer_is_active(l1_rollup, signer).await { - // Re-run the gate before every retry: a threshold increase, - // exit, or slash between attempts must stop transmission. - return false; + METRICS.proof_signer_available.set(0); + let selected = match select_active_proof_signer(l1_rollup, &self.proof_signers).await { + Ok(Some(selected)) => selected, + Ok(None) => { + log::error!( + "ALERT: refusing proveState because all {} approved proof signers are inactive; no transaction was sent", + self.proof_signers.len() + ); + return false; + } + Err(err) => { + log::error!("ALERT: refusing proveState because active signer selection failed: {err:#}; no transaction was sent"); + return false; + } + }; + METRICS.proof_signer_available.set(1); + if selected.approved_index > 0 { + log::warn!( + "ALERT: primary proof signer is inactive; switching to approved backup #{} {:?} at finalized snapshot {} ({:?})", + selected.approved_index, + selected.signer.address(), + selected.snapshot.number, + selected.snapshot.hash + ); } log::info!("starting prove state onchain, batch index = {:#?}", batch_index); let proof = Bytes::from(batch_proof.proof_data.clone()); let client: Arc, LocalWallet>> = self.l1_rollup.client(); let calldata = l1_rollup.prove_state(batch_header.clone(), proof).calldata(); - let result = send_transaction(self.l1_rollup.address(), calldata, &client, &self.ext_signer, &self.l1_provider).await; + let result = send_transaction(self.l1_rollup.address(), calldata, &client, &selected.signer, &self.l1_provider).await; if let Ok(tx_hash) = result { METRICS.verify_result.set(1); log::info!("prove_state success, batch_index: {:?}, tx_hash: {:#?}", batch_index, tx_hash); @@ -309,42 +334,6 @@ impl ChallengeHandler { } false } - - async fn proof_signer_is_active(&self, l1_rollup: &RollupType, signer: Address) -> bool { - let (number, hash) = match self.l1_provider.get_block(BlockNumber::Finalized).await { - Ok(Some(block)) => match (block.number, block.hash) { - (Some(number), Some(hash)) => (number, hash), - _ => { - log::error!("finalized L1 block has no stable identity"); - return false; - } - }, - Ok(None) => { - log::error!("finalized L1 block is unavailable"); - return false; - } - Err(err) => { - log::error!("query finalized L1 snapshot failed: {err:#}"); - return false; - } - }; - let snapshot = BlockId::Number(BlockNumber::Number(number)); - let active = match authority_is_active(l1_rollup, signer, snapshot).await { - Ok(active) => active, - Err(err) => { - log::error!("Rollup authority active preflight failed: {err:#}"); - return false; - } - }; - if !snapshot_unchanged(&self.l1_provider, number, hash).await { - log::error!("finalized snapshot changed during authority active preflight"); - return false; - } - if !active { - log::error!("refusing proveState from inactive submitter signer {signer:?}"); - } - active - } } /** @@ -376,12 +365,18 @@ async fn query_proof(batch_index: u64) -> Option { async fn query_batch_tx( latest: U64, + snapshot_hash: H256, l1_rollup: &RollupType, batch_index: u64, - rollup_deployed_block: u64, + canonical_log_index: &Arc>, l1_provider: &Provider, ) -> Option<(CanonicalCommit, H256)> { - let commit = match resolve_canonical_commit(l1_rollup, l1_provider, batch_index, rollup_deployed_block, latest).await { + let mut canonical_log_index = canonical_log_index.lock().await; + if let Err(err) = canonical_log_index.refresh(l1_provider, l1_rollup.address(), latest, snapshot_hash).await { + log::error!("canonical Commit index refresh failed: {err:#}"); + return None; + } + let commit = match canonical_log_index.resolve(l1_rollup, batch_index, latest, snapshot_hash).await { Ok(Some(commit)) => commit, Ok(None) => { log::warn!("batch {batch_index} has no canonical Commit at snapshot {latest}"); @@ -392,6 +387,7 @@ async fn query_batch_tx( return None; } }; + drop(canonical_log_index); let block_id = BlockId::Number(BlockNumber::Number(latest)); let parent_index = batch_index.checked_sub(1)?; @@ -611,23 +607,20 @@ async fn send_transaction( contract: Address, calldata: Option, local_signer: &Arc, LocalWallet>>, - ext_signer: &Option, + proof_signer: &ApprovedProofSigner, l2_provider: &Provider, ) -> Result> { let req = Eip1559TransactionRequest::new().data(calldata.unwrap_or_default()); let mut tx = TypedTransaction::Eip1559(req); tx.set_to(contract); - if let Some(signer) = ext_signer { - tx.set_from(Address::from_str(&signer.address).unwrap_or_default()); - } else { - tx.set_from(local_signer.address()); - } + tx.set_from(proof_signer.address()); local_signer.fill_transaction(&mut tx, None).await.map_err(|e| { let msg = contract_error(ContractError::, LocalWallet>>::from_middleware_error(e)); anyhow!("prove_state fill_transaction error: {:#?}", msg) })?; - let signed_tx = sign_tx(tx, local_signer, ext_signer) + let signed_tx = proof_signer + .sign_transaction(&tx) .await .map_err(|e| anyhow!("prove_state sign_tx error: {}", e))?; @@ -650,19 +643,6 @@ async fn send_transaction( } } -async fn sign_tx( - tx: TypedTransaction, - local_signer: &Arc, LocalWallet>>, - ext_signer: &Option, -) -> Result> { - if let Some(signer) = ext_signer { - Ok(signer.request_sign(&tx).await?) - } else { - let signature = local_signer.signer().sign_transaction(&tx).await?; - Ok(tx.rlp_signed(&signature)) - } -} - pub fn contract_error(e: ContractError) -> String { let error_msg = if let Some(contract_err) = e.as_revert() { format!("contract error: {:?}", contract_err) diff --git a/prover/bin/challenge/src/lib.rs b/prover/bin/challenge/src/lib.rs index d5001864c..025c2c899 100644 --- a/prover/bin/challenge/src/lib.rs +++ b/prover/bin/challenge/src/lib.rs @@ -2,5 +2,6 @@ pub mod abi; mod external_sign; pub mod handler; pub mod metrics; +pub mod proof_signer; pub mod rollup_compat; pub mod util; diff --git a/prover/bin/challenge/src/main.rs b/prover/bin/challenge/src/main.rs index f134ac403..0d2919caa 100644 --- a/prover/bin/challenge/src/main.rs +++ b/prover/bin/challenge/src/main.rs @@ -54,6 +54,8 @@ fn register_metrics() { REGISTRY.register(Box::new(METRICS.verify_result.clone())).unwrap(); // wallet balance. REGISTRY.register(Box::new(METRICS.wallet_balance.clone())).unwrap(); + // approved active proof signer availability. + REGISTRY.register(Box::new(METRICS.proof_signer_available.clone())).unwrap(); } async fn handle_metrics() -> String { diff --git a/prover/bin/challenge/src/metrics.rs b/prover/bin/challenge/src/metrics.rs index 480d01f26..013918216 100644 --- a/prover/bin/challenge/src/metrics.rs +++ b/prover/bin/challenge/src/metrics.rs @@ -6,6 +6,7 @@ pub struct Metrics { pub blocks_len: IntGauge, pub verify_result: IntGauge, pub wallet_balance: Gauge, + pub proof_signer_available: IntGauge, } lazy_static! { @@ -15,5 +16,7 @@ lazy_static! { blocks_len: IntGauge::new("blocks_len", "blocks len").expect("blocks_len metric can be created"), verify_result: IntGauge::new("verify_result", "verify result").expect("verify metric can be created"), wallet_balance: Gauge::new("handler_wallet_balance", "handler wallet balance").expect("wallet metric can be created"), + proof_signer_available: IntGauge::new("proof_signer_available", "whether an approved proof signer is active") + .expect("proof signer metric can be created"), }; } diff --git a/prover/bin/challenge/src/proof_signer.rs b/prover/bin/challenge/src/proof_signer.rs new file mode 100644 index 000000000..9ead803a8 --- /dev/null +++ b/prover/bin/challenge/src/proof_signer.rs @@ -0,0 +1,155 @@ +use crate::abi::rollup_abi::Rollup; +use crate::external_sign::ExternalSign; +use crate::rollup_compat::authority_is_active; +use ethers::prelude::{BlockId, BlockNumber, LocalWallet, Middleware, Signer}; +use ethers::types::{transaction::eip2718::TypedTransaction, Address, Bytes, H256, U64}; +use eyre::{eyre, Result}; +use std::future::Future; + +#[derive(Clone)] +pub enum ApprovedProofSigner { + Local(LocalWallet), + External { address: Address, signer: Box }, +} + +impl ApprovedProofSigner { + pub fn local(wallet: LocalWallet) -> Self { + Self::Local(wallet) + } + + pub fn external(address: Address, signer: ExternalSign) -> Self { + Self::External { + address, + signer: Box::new(signer), + } + } + + pub fn address(&self) -> Address { + match self { + Self::Local(wallet) => wallet.address(), + Self::External { address, .. } => *address, + } + } + + pub async fn sign_transaction(&self, tx: &TypedTransaction) -> Result { + match self { + Self::Local(wallet) => { + let signature = wallet.sign_transaction(tx).await?; + Ok(tx.rlp_signed(&signature)) + } + Self::External { signer, .. } => signer.request_sign(tx).await.map_err(|err| eyre!(err.to_string())), + } + } +} + +#[derive(Clone, Copy, Debug, Eq, PartialEq)] +pub struct FixedSnapshot { + pub number: U64, + pub hash: H256, +} + +#[derive(Clone)] +pub struct SelectedProofSigner { + pub signer: ApprovedProofSigner, + pub approved_index: usize, + pub snapshot: FixedSnapshot, +} + +async fn first_active_index(approved: &[Address], mut is_active: F) -> std::result::Result, E> +where + F: FnMut(Address) -> Fut, + Fut: Future>, +{ + for (index, address) in approved.iter().copied().enumerate() { + if is_active(address).await? { + return Ok(Some(index)); + } + } + Ok(None) +} + +async fn fixed_finalized_snapshot(client: &M) -> Result { + let block = client + .get_block(BlockNumber::Finalized) + .await + .map_err(|err| eyre!("query finalized L1 snapshot failed: {err}"))? + .ok_or_else(|| eyre!("finalized L1 block is unavailable"))?; + let number = block.number.ok_or_else(|| eyre!("finalized L1 block has no number"))?; + let hash = block.hash.ok_or_else(|| eyre!("finalized L1 block has no hash"))?; + Ok(FixedSnapshot { number, hash }) +} + +async fn ensure_snapshot_unchanged(client: &M, snapshot: FixedSnapshot) -> Result<()> { + let current = client + .get_block(snapshot.number) + .await + .map_err(|err| eyre!("recheck finalized L1 snapshot failed: {err}"))? + .ok_or_else(|| eyre!("finalized L1 snapshot disappeared during signer selection"))?; + if current.hash != Some(snapshot.hash) { + return Err(eyre!("finalized L1 snapshot changed during signer selection")); + } + Ok(()) +} + +/// Selects the first approved signer that is active at one immutable finalized +/// block hash. The caller must treat `None` and every error as a hard no-send. +pub async fn select_active_proof_signer( + rollup: &Rollup, + approved: &[ApprovedProofSigner], +) -> Result> { + if approved.is_empty() { + return Err(eyre!("no approved proof signers are configured")); + } + + let snapshot = fixed_finalized_snapshot(rollup.client().as_ref()).await?; + let block_id = BlockId::Hash(snapshot.hash); + let addresses = approved.iter().map(ApprovedProofSigner::address).collect::>(); + let approved_index = first_active_index(&addresses, |address| async move { + authority_is_active(rollup, address, block_id) + .await + .map_err(|err| eyre!("active check failed for approved proof signer {address:?}: {err}")) + }) + .await?; + ensure_snapshot_unchanged(rollup.client().as_ref(), snapshot).await?; + Ok(approved_index.map(|approved_index| SelectedProofSigner { + signer: approved[approved_index].clone(), + approved_index, + snapshot, + })) +} + +#[cfg(test)] +mod tests { + use super::first_active_index; + use ethers::types::Address; + use std::convert::Infallible; + use std::future::ready; + + #[tokio::test] + async fn primary_inactivation_selects_first_active_backup() { + let primary = Address::from_low_u64_be(1); + let backup = Address::from_low_u64_be(2); + let approved = [primary, backup]; + + let initial = first_active_index(&approved, |_| ready(Ok::<_, Infallible>(true))).await.unwrap(); + let after_primary_inactivation = first_active_index(&approved, |address| ready(Ok::<_, Infallible>(address == backup))) + .await + .unwrap(); + + assert_eq!(initial, Some(0)); + assert_eq!(after_primary_inactivation, Some(1)); + } + + #[tokio::test] + async fn all_inactive_signers_fail_closed_without_dispatch() { + let approved = [Address::from_low_u64_be(1), Address::from_low_u64_be(2)]; + let selected = first_active_index(&approved, |_| ready(Ok::<_, Infallible>(false))).await.unwrap(); + let mut sent_transactions = 0; + if selected.is_some() { + sent_transactions += 1; + } + + assert_eq!(selected, None); + assert_eq!(sent_transactions, 0); + } +} diff --git a/prover/bin/challenge/src/rollup_compat.rs b/prover/bin/challenge/src/rollup_compat.rs index fc0875cbe..8ea74f73e 100644 --- a/prover/bin/challenge/src/rollup_compat.rs +++ b/prover/bin/challenge/src/rollup_compat.rs @@ -5,6 +5,7 @@ use ethers::{ utils::{id, keccak256}, }; use eyre::{eyre, Result}; +use std::collections::HashMap; use crate::abi::{ legacy_l1_staking_abi::LegacyL1Staking, @@ -221,80 +222,221 @@ pub fn select_canonical_commit(commits: &[Log], reverts: &[Log], canonical_hash: const QUERY_RANGE: u64 = 10_000; -pub async fn resolve_canonical_commit( - rollup: &Rollup, - provider: &P, - batch_index: u64, +#[derive(Clone, Copy, Debug, Eq, PartialEq)] +struct IndexedSnapshot { + number: u64, + hash: H256, +} + +/// Reusable finalized Commit/Revert index. The first poll replays from the +/// configured Rollup deployment block; subsequent polls validate the previous +/// block-hash anchor and query only the newly finalized suffix. +#[derive(Debug)] +pub struct CanonicalLogIndex { rollup_deployed_block: u64, - snapshot_number: U64, -) -> Result> -where - M: Middleware + 'static, - P: Middleware + 'static, -{ - if rollup_deployed_block == 0 || rollup_deployed_block > snapshot_number.as_u64() { - return Err(eyre!( - "invalid Rollup deployment block {rollup_deployed_block} for snapshot {snapshot_number}" - )); - } - let before = provider - .get_block(snapshot_number) - .await? - .ok_or_else(|| eyre!("snapshot block {} not found", snapshot_number))?; - let before_hash = before.hash.ok_or_else(|| eyre!("snapshot block has no hash"))?; - let block_id = BlockId::Number(BlockNumber::Number(snapshot_number)); - let canonical_hash: H256 = rollup.committed_batches(U256::from(batch_index)).block(block_id).call().await?.into(); - if canonical_hash.is_zero() { - return Ok(None); - } - - let commit_topic = H256::from(keccak256("CommitBatch(uint256,bytes32)")); - let revert_topic = H256::from(keccak256("RevertBatch(uint256,bytes32)")); - let mut commits = Vec::new(); - let mut reverts = Vec::new(); - let mut from = rollup_deployed_block; - while from <= snapshot_number.as_u64() { - let to = from.saturating_add(QUERY_RANGE - 1).min(snapshot_number.as_u64()); - let base = Filter::new() - .address(rollup.address()) - .from_block(from) - .to_block(to) - .topic1(U256::from(batch_index)); - commits.extend(provider.get_logs(&base.clone().topic0(commit_topic)).await?); - reverts.extend(provider.get_logs(&base.topic0(revert_topic)).await?); - if to == snapshot_number.as_u64() { - break; + indexed_snapshot: Option, + commits: HashMap>, + reverts: HashMap>, +} + +impl CanonicalLogIndex { + pub fn new(rollup_deployed_block: u64) -> Result { + if rollup_deployed_block == 0 { + return Err(eyre!("Rollup deployment block must be non-zero")); + } + Ok(Self { + rollup_deployed_block, + indexed_snapshot: None, + commits: HashMap::new(), + reverts: HashMap::new(), + }) + } + + fn clear(&mut self) { + self.indexed_snapshot = None; + self.commits.clear(); + self.reverts.clear(); + } + + fn query_start(&self, snapshot_number: u64) -> Result> { + if snapshot_number < self.rollup_deployed_block { + return Err(eyre!( + "Rollup deployment block {} is newer than snapshot {snapshot_number}", + self.rollup_deployed_block + )); + } + match self.indexed_snapshot { + None => Ok(Some(self.rollup_deployed_block)), + Some(indexed) if snapshot_number < indexed.number => Err(eyre!( + "finalized snapshot moved backwards: indexed={}, requested={snapshot_number}", + indexed.number + )), + Some(indexed) if snapshot_number == indexed.number => Ok(None), + Some(indexed) => Ok(Some( + indexed.number.checked_add(1).ok_or_else(|| eyre!("canonical log index block overflow"))?, + )), + } + } + + fn verify_cached_anchor(&mut self, observed_hash: H256) -> Result<()> { + let Some(indexed) = self.indexed_snapshot else { + return Ok(()); + }; + if indexed.hash != observed_hash { + self.clear(); + return Err(eyre!("canonical log index anchor changed at block {}; cache discarded", indexed.number)); + } + Ok(()) + } + + fn batch_index(log: &Log) -> Result { + if log.removed.unwrap_or_default() { + return Err(eyre!("removed log returned for finalized canonical range")); + } + identity(log).ok_or_else(|| eyre!("canonical Rollup log has incomplete identity"))?; + if log.transaction_hash.is_none() || log.block_hash.is_none() { + return Err(eyre!("canonical Rollup log has incomplete block/transaction identity")); + } + if log.topics.len() < 3 { + return Err(eyre!("canonical Rollup log is missing indexed topics")); + } + let batch_index = U256::from_big_endian(log.topics[1].as_bytes()); + if batch_index > U256::from(u64::MAX) { + return Err(eyre!("Rollup batch index exceeds u64")); + } + Ok(batch_index.as_u64()) + } + + fn append_logs(&mut self, snapshot_number: u64, snapshot_hash: H256, commits: Vec, reverts: Vec) -> Result<()> { + let mut indexed_commits = Vec::with_capacity(commits.len()); + for log in commits { + indexed_commits.push((Self::batch_index(&log)?, log)); + } + let mut indexed_reverts = Vec::with_capacity(reverts.len()); + for log in reverts { + indexed_reverts.push((Self::batch_index(&log)?, log)); + } + for (batch_index, log) in indexed_commits { + self.commits.entry(batch_index).or_default().push(log); } - from = to + 1; + for (batch_index, log) in indexed_reverts { + self.reverts.entry(batch_index).or_default().push(log); + } + self.indexed_snapshot = Some(IndexedSnapshot { + number: snapshot_number, + hash: snapshot_hash, + }); + Ok(()) + } + + fn resolve_cached(&self, batch_index: u64, canonical_hash: H256) -> Option { + select_canonical_commit( + self.commits.get(&batch_index).map(Vec::as_slice).unwrap_or_default(), + self.reverts.get(&batch_index).map(Vec::as_slice).unwrap_or_default(), + canonical_hash, + ) } - let after = provider - .get_block(snapshot_number) - .await? - .ok_or_else(|| eyre!("snapshot block {} disappeared", snapshot_number))?; - if after.hash != Some(before_hash) { - return Err(eyre!("snapshot block hash changed during canonical resolution")); + pub async fn refresh(&mut self, provider: &P, rollup: Address, snapshot_number: U64, snapshot_hash: H256) -> Result<()> { + let before = provider + .get_block(snapshot_number) + .await? + .ok_or_else(|| eyre!("snapshot block {} not found", snapshot_number))?; + if before.hash != Some(snapshot_hash) { + return Err(eyre!("finalized snapshot hash changed before canonical log refresh")); + } + + if let Some(indexed) = self.indexed_snapshot { + let anchor = provider + .get_block(U64::from(indexed.number)) + .await? + .ok_or_else(|| eyre!("canonical log index anchor block disappeared"))?; + let anchor_hash = anchor.hash.ok_or_else(|| eyre!("canonical log index anchor has no hash"))?; + self.verify_cached_anchor(anchor_hash)?; + } + + let query_start = match self.query_start(snapshot_number.as_u64()) { + Ok(query_start) => query_start, + Err(error) => { + self.clear(); + return Err(error); + } + }; + let Some(mut from) = query_start else { + self.verify_cached_anchor(snapshot_hash)?; + return Ok(()); + }; + + let commit_topic = H256::from(keccak256("CommitBatch(uint256,bytes32)")); + let revert_topic = H256::from(keccak256("RevertBatch(uint256,bytes32)")); + let mut commits = Vec::new(); + let mut reverts = Vec::new(); + while from <= snapshot_number.as_u64() { + let to = from.saturating_add(QUERY_RANGE - 1).min(snapshot_number.as_u64()); + let base = Filter::new().address(rollup).from_block(from).to_block(to); + commits.extend(provider.get_logs(&base.clone().topic0(commit_topic)).await?); + reverts.extend(provider.get_logs(&base.topic0(revert_topic)).await?); + if to == snapshot_number.as_u64() { + break; + } + from = to.checked_add(1).ok_or_else(|| eyre!("canonical log query block overflow"))?; + } + + let after = provider + .get_block(snapshot_number) + .await? + .ok_or_else(|| eyre!("snapshot block {} disappeared", snapshot_number))?; + if after.hash != Some(snapshot_hash) { + return Err(eyre!("snapshot block hash changed during canonical log refresh")); + } + + self.append_logs(snapshot_number.as_u64(), snapshot_hash, commits, reverts) } - Ok(select_canonical_commit(&commits, &reverts, canonical_hash)) + pub async fn resolve( + &self, + rollup: &Rollup, + batch_index: u64, + snapshot_number: U64, + snapshot_hash: H256, + ) -> Result> { + if self.indexed_snapshot + != Some(IndexedSnapshot { + number: snapshot_number.as_u64(), + hash: snapshot_hash, + }) + { + return Err(eyre!("canonical log index is not anchored to the requested snapshot")); + } + let block_id = BlockId::Number(BlockNumber::Number(snapshot_number)); + let canonical_hash: H256 = rollup.committed_batches(U256::from(batch_index)).block(block_id).call().await?.into(); + if canonical_hash.is_zero() { + return Ok(None); + } + Ok(self.resolve_cached(batch_index, canonical_hash)) + } } #[cfg(test)] mod tests { use super::*; - fn log(block: u64, tx: u64, index: u64, hash: H256, marker: u64) -> Log { + fn batch_log(batch: u64, block: u64, tx: u64, index: u64, hash: H256, marker: u64) -> Log { Log { block_number: Some(block.into()), transaction_index: Some(tx.into()), log_index: Some(index.into()), transaction_hash: Some(H256::from_low_u64_be(marker)), block_hash: Some(H256::from_low_u64_be(block)), - topics: vec![H256::zero(), H256::from_low_u64_be(7), hash], + topics: vec![H256::zero(), H256::from_low_u64_be(batch), hash], ..Default::default() } } + fn log(block: u64, tx: u64, index: u64, hash: H256, marker: u64) -> Log { + batch_log(7, block, tx, index, hash, marker) + } + #[test] fn rejects_short_and_unknown_calldata() { assert!(decode_batch_data_input(&[]).is_err()); @@ -357,4 +499,48 @@ mod tests { assert_eq!(decode_batch_data_store_block_number(&old_tuple).unwrap(), U256::from(123)); assert!(decode_batch_data_store_block_number(&old_tuple[..127]).is_err()); } + + #[test] + fn canonical_log_index_reuses_snapshot_and_only_plans_new_suffix() { + let snapshot_hash = H256::from_low_u64_be(100); + let mut index = CanonicalLogIndex::new(5).unwrap(); + assert_eq!(index.query_start(10).unwrap(), Some(5)); + + let hash7 = H256::from_low_u64_be(7); + let hash8 = H256::from_low_u64_be(8); + index + .append_logs( + 10, + snapshot_hash, + vec![batch_log(7, 7, 0, 0, hash7, 7), batch_log(8, 8, 0, 0, hash8, 8)], + vec![], + ) + .unwrap(); + + assert!(index.resolve_cached(7, hash7).is_some()); + assert!(index.resolve_cached(8, hash8).is_some()); + assert_eq!(index.query_start(10).unwrap(), None); + assert_eq!(index.query_start(12).unwrap(), Some(11)); + + let hash9 = H256::from_low_u64_be(9); + index + .append_logs(12, H256::from_low_u64_be(102), vec![batch_log(9, 11, 0, 0, hash9, 9)], vec![]) + .unwrap(); + assert!(index.resolve_cached(7, hash7).is_some()); + assert!(index.resolve_cached(9, hash9).is_some()); + } + + #[test] + fn canonical_log_index_discards_cache_and_fails_closed_on_anchor_reorg() { + let old_hash = H256::from_low_u64_be(100); + let mut index = CanonicalLogIndex::new(5).unwrap(); + index + .append_logs(10, old_hash, vec![batch_log(7, 7, 0, 0, H256::from_low_u64_be(7), 7)], vec![]) + .unwrap(); + + let error = index.verify_cached_anchor(H256::from_low_u64_be(101)).unwrap_err(); + assert!(error.to_string().contains("cache discarded")); + assert!(index.resolve_cached(7, H256::from_low_u64_be(7)).is_none()); + assert_eq!(index.query_start(12).unwrap(), Some(5)); + } } diff --git a/prover/bin/shadow-prove/src/rollup_compat.rs b/prover/bin/shadow-prove/src/rollup_compat.rs index 5a2fd5a3f..992eb2f09 100644 --- a/prover/bin/shadow-prove/src/rollup_compat.rs +++ b/prover/bin/shadow-prove/src/rollup_compat.rs @@ -1,3 +1,5 @@ +use std::collections::HashMap; + use alloy_primitives::{Bytes, TxHash, B256, U256}; use alloy_provider::{DynProvider, Provider}; use alloy_rpc_types::{BlockId, BlockNumberOrTag, Log}; @@ -90,75 +92,238 @@ pub fn select_canonical_commit( const LOG_QUERY_BLOCK_RANGE: u64 = 10_000; -/// Resolve a batch's canonical Commit using one numbered snapshot. The block -/// hash is checked before and after all getter/log reads so RPCs without -/// EIP-1898 support cannot silently combine different forks. -pub async fn resolve_canonical_commit( - provider: &DynProvider, - rollup: &RollupInstance, - batch_index: u64, +#[derive(Clone, Copy, Debug, Eq, PartialEq)] +struct IndexedSnapshot { + number: u64, + hash: B256, +} + +/// A reusable Commit/Revert index anchored to one canonical finalized block. +/// +/// The first refresh replays from the configured Rollup deployment block. A +/// later refresh first verifies the previous anchor and then scans only the +/// newly finalized suffix. If the anchor changed, the cache is discarded and +/// the current operation fails closed; a later poll may rebuild from genesis. +#[derive(Debug)] +pub struct CanonicalLogIndex { rollup_deployed_block: u64, - snapshot_number: u64, -) -> Result> { - if rollup_deployed_block == 0 || rollup_deployed_block > snapshot_number { - return Err(anyhow!( - "invalid Rollup deployment block {rollup_deployed_block} for snapshot {snapshot_number}" - )); + indexed_snapshot: Option, + commits: HashMap>, + reverts: HashMap>, +} + +impl CanonicalLogIndex { + pub fn new(rollup_deployed_block: u64) -> Result { + if rollup_deployed_block == 0 { + return Err(anyhow!("Rollup deployment block must be non-zero")); + } + Ok(Self { + rollup_deployed_block, + indexed_snapshot: None, + commits: HashMap::new(), + reverts: HashMap::new(), + }) } - let snapshot_tag = BlockNumberOrTag::Number(snapshot_number); - let before = provider - .get_block_by_number(snapshot_tag) - .await? - .ok_or_else(|| anyhow!("snapshot block {snapshot_number} not found"))?; - let before_hash = before.header.hash; - - let canonical_hash = rollup - .committedBatches(U256::from(batch_index)) - .block(BlockId::Number(snapshot_tag)) - .call() - .await?; - if canonical_hash == B256::ZERO { - return Ok(None); + + fn clear(&mut self) { + self.indexed_snapshot = None; + self.commits.clear(); + self.reverts.clear(); } - let mut commits = Vec::new(); - let mut reverts = Vec::new(); - let mut from = rollup_deployed_block; - while from <= snapshot_number { - let to = from.saturating_add(LOG_QUERY_BLOCK_RANGE - 1).min(snapshot_number); - let commit_filter = rollup - .CommitBatch_filter() - .filter - .from_block(from) - .to_block(to) - .topic1(U256::from(batch_index)) - .address(*rollup.address()); - commits.extend(provider.get_logs(&commit_filter).await?); - - let revert_filter = rollup - .RevertBatch_filter() - .filter - .from_block(from) - .to_block(to) - .topic1(U256::from(batch_index)) - .address(*rollup.address()); - reverts.extend(provider.get_logs(&revert_filter).await?); - - if to == snapshot_number { - break; + fn query_start(&self, snapshot_number: u64) -> Result> { + if snapshot_number < self.rollup_deployed_block { + return Err(anyhow!( + "Rollup deployment block {} is newer than snapshot {snapshot_number}", + self.rollup_deployed_block + )); + } + match self.indexed_snapshot { + None => Ok(Some(self.rollup_deployed_block)), + Some(indexed) if snapshot_number < indexed.number => Err(anyhow!( + "finalized snapshot moved backwards: indexed={}, requested={snapshot_number}", + indexed.number + )), + Some(indexed) if snapshot_number == indexed.number => Ok(None), + Some(indexed) => Ok(Some( + indexed + .number + .checked_add(1) + .ok_or_else(|| anyhow!("canonical log index block overflow"))?, + )), } - from = to + 1; } - let after = provider - .get_block_by_number(snapshot_tag) - .await? - .ok_or_else(|| anyhow!("snapshot block {snapshot_number} disappeared"))?; - if after.header.hash != before_hash { - return Err(anyhow!("snapshot block hash changed during canonical resolution")); + fn verify_cached_anchor(&mut self, observed_hash: B256) -> Result<()> { + let Some(indexed) = self.indexed_snapshot else { + return Ok(()); + }; + if indexed.hash != observed_hash { + self.clear(); + return Err(anyhow!( + "canonical log index anchor changed at block {}; cache discarded", + indexed.number + )); + } + Ok(()) } - Ok(select_canonical_commit(&commits, &reverts, canonical_hash)) + fn batch_index(log: &Log) -> Result { + if log.removed { + return Err(anyhow!("removed log returned for finalized canonical range")); + } + // Require every component used by canonical identity before admitting a + // log to the cache. Resolution must never silently skip malformed RPC + // results and fall back to an older Commit. + log_identity(log).ok_or_else(|| anyhow!("canonical Rollup log has incomplete identity"))?; + if log.transaction_hash.is_none() || log.block_hash.is_none() { + return Err(anyhow!("canonical Rollup log has incomplete block/transaction identity")); + } + let topics = log.topics(); + if topics.len() < 3 { + return Err(anyhow!("canonical Rollup log is missing indexed topics")); + } + let batch_index = U256::from_be_slice(topics[1].as_slice()); + if batch_index > U256::from(u64::MAX) { + return Err(anyhow!("Rollup batch index exceeds u64")); + } + Ok(batch_index.to::()) + } + + fn append_logs( + &mut self, + snapshot_number: u64, + snapshot_hash: B256, + commits: Vec, + reverts: Vec, + ) -> Result<()> { + let mut indexed_commits = Vec::with_capacity(commits.len()); + for log in commits { + indexed_commits.push((Self::batch_index(&log)?, log)); + } + let mut indexed_reverts = Vec::with_capacity(reverts.len()); + for log in reverts { + indexed_reverts.push((Self::batch_index(&log)?, log)); + } + for (batch_index, log) in indexed_commits { + self.commits.entry(batch_index).or_default().push(log); + } + for (batch_index, log) in indexed_reverts { + self.reverts.entry(batch_index).or_default().push(log); + } + self.indexed_snapshot = + Some(IndexedSnapshot { number: snapshot_number, hash: snapshot_hash }); + Ok(()) + } + + fn resolve_cached(&self, batch_index: u64, canonical_hash: B256) -> Option { + select_canonical_commit( + self.commits.get(&batch_index).map(Vec::as_slice).unwrap_or_default(), + self.reverts.get(&batch_index).map(Vec::as_slice).unwrap_or_default(), + canonical_hash, + ) + } + + pub async fn refresh( + &mut self, + provider: &DynProvider, + rollup: &RollupInstance, + snapshot_number: u64, + snapshot_hash: B256, + ) -> Result<()> { + let snapshot_tag = BlockNumberOrTag::Number(snapshot_number); + let before = provider + .get_block_by_number(snapshot_tag) + .await? + .ok_or_else(|| anyhow!("snapshot block {snapshot_number} not found"))?; + if before.header.hash != snapshot_hash { + return Err(anyhow!("finalized snapshot hash changed before canonical log refresh")); + } + + if let Some(indexed) = self.indexed_snapshot { + let anchor = provider + .get_block_by_number(BlockNumberOrTag::Number(indexed.number)) + .await? + .ok_or_else(|| anyhow!("canonical log index anchor block disappeared"))?; + self.verify_cached_anchor(anchor.header.hash)?; + } + + let query_start = match self.query_start(snapshot_number) { + Ok(query_start) => query_start, + Err(error) => { + self.clear(); + return Err(error); + } + }; + let Some(mut from) = query_start else { + // Equal-height reuse still verifies the caller supplied the exact + // cached block hash, rather than merely the block number. + self.verify_cached_anchor(snapshot_hash)?; + return Ok(()); + }; + + let mut commits = Vec::new(); + let mut reverts = Vec::new(); + while from <= snapshot_number { + let to = from.saturating_add(LOG_QUERY_BLOCK_RANGE - 1).min(snapshot_number); + let commit_filter = rollup + .CommitBatch_filter() + .filter + .from_block(from) + .to_block(to) + .address(*rollup.address()); + commits.extend(provider.get_logs(&commit_filter).await?); + + let revert_filter = rollup + .RevertBatch_filter() + .filter + .from_block(from) + .to_block(to) + .address(*rollup.address()); + reverts.extend(provider.get_logs(&revert_filter).await?); + + if to == snapshot_number { + break; + } + from = + to.checked_add(1).ok_or_else(|| anyhow!("canonical log query block overflow"))?; + } + + let after = provider + .get_block_by_number(snapshot_tag) + .await? + .ok_or_else(|| anyhow!("snapshot block {snapshot_number} disappeared"))?; + if after.header.hash != snapshot_hash { + return Err(anyhow!("snapshot block hash changed during canonical log refresh")); + } + + self.append_logs(snapshot_number, snapshot_hash, commits, reverts) + } + + /// Resolve from the already-refreshed fixed snapshot. Multiple batch + /// lookups (shadow prev/current/next) reuse the same log replay. + pub async fn resolve( + &self, + rollup: &RollupInstance, + batch_index: u64, + snapshot_number: u64, + snapshot_hash: B256, + ) -> Result> { + if self.indexed_snapshot != + Some(IndexedSnapshot { number: snapshot_number, hash: snapshot_hash }) + { + return Err(anyhow!("canonical log index is not anchored to the requested snapshot")); + } + let canonical_hash = rollup + .committedBatches(U256::from(batch_index)) + .block(BlockId::Number(BlockNumberOrTag::Number(snapshot_number))) + .call() + .await?; + if canonical_hash == B256::ZERO { + return Ok(None); + } + Ok(self.resolve_cached(batch_index, canonical_hash)) + } } fn current_input(input: IRollup::BatchDataInput, kind: SubmissionKind) -> BatchDataInput { @@ -331,4 +496,59 @@ mod tests { let resolved = select_canonical_commit(&[after_revert], &[revert], hash).unwrap(); assert_eq!(resolved.transaction_hash, B256::repeat_byte(3)); } + + #[test] + fn canonical_log_index_reuses_one_snapshot_for_adjacent_batches_and_then_increments() { + let snapshot_hash = B256::repeat_byte(0xaa); + let mut index = CanonicalLogIndex::new(5).unwrap(); + assert_eq!(index.query_start(10).unwrap(), Some(5)); + + let hash6 = B256::repeat_byte(6); + let hash7 = B256::repeat_byte(7); + let hash8 = B256::repeat_byte(8); + index + .append_logs( + 10, + snapshot_hash, + vec![ + rpc_log(6, hash6, 6, 0, 0, 6), + rpc_log(7, hash7, 7, 0, 0, 7), + rpc_log(8, hash8, 8, 0, 0, 8), + ], + vec![], + ) + .unwrap(); + + // prev/current/next are all served by the same indexed replay. + assert_eq!(index.resolve_cached(6, hash6).unwrap().identity.block_number, 6); + assert_eq!(index.resolve_cached(7, hash7).unwrap().identity.block_number, 7); + assert_eq!(index.resolve_cached(8, hash8).unwrap().identity.block_number, 8); + assert_eq!(index.query_start(10).unwrap(), None); + assert_eq!(index.query_start(12).unwrap(), Some(11)); + + index + .append_logs( + 12, + B256::repeat_byte(0xbb), + vec![rpc_log(9, B256::repeat_byte(9), 11, 0, 0, 9)], + vec![], + ) + .unwrap(); + assert!(index.resolve_cached(7, hash7).is_some()); + assert!(index.resolve_cached(9, B256::repeat_byte(9)).is_some()); + } + + #[test] + fn canonical_log_index_discards_cache_and_fails_closed_on_anchor_reorg() { + let old_hash = B256::repeat_byte(0xaa); + let mut index = CanonicalLogIndex::new(5).unwrap(); + index + .append_logs(10, old_hash, vec![rpc_log(7, B256::repeat_byte(7), 7, 0, 0, 7)], vec![]) + .unwrap(); + + let error = index.verify_cached_anchor(B256::repeat_byte(0xbb)).unwrap_err(); + assert!(error.to_string().contains("cache discarded")); + assert!(index.resolve_cached(7, B256::repeat_byte(7)).is_none()); + assert_eq!(index.query_start(12).unwrap(), Some(5)); + } } diff --git a/prover/bin/shadow-prove/src/shadow_rollup.rs b/prover/bin/shadow-prove/src/shadow_rollup.rs index 3aa4f4c55..5dc88264b 100644 --- a/prover/bin/shadow-prove/src/shadow_rollup.rs +++ b/prover/bin/shadow-prove/src/shadow_rollup.rs @@ -7,11 +7,11 @@ use alloy_rpc_types::{BlockId, BlockNumberOrTag}; use anyhow::{anyhow, Context, Result}; use futures::future::join_all; +use std::sync::Arc; +use tokio::sync::Mutex; use crate::{ - rollup_compat::{ - decode_batch_data_input, resolve_canonical_commit, BatchDataInput, CanonicalCommit, - }, + rollup_compat::{decode_batch_data_input, BatchDataInput, CanonicalCommit, CanonicalLogIndex}, Rollup::{self, RollupInstance}, ShadowRollup::{self, ShadowRollupInstance}, }; @@ -22,7 +22,7 @@ pub struct BatchSyncer { l2_provider: DynProvider, l1_rollup: RollupInstance, l1_shadow_rollup: ShadowRollupInstance, - rollup_deployed_block: u64, + canonical_log_index: Arc>, } impl BatchSyncer @@ -64,8 +64,12 @@ where ) -> Self { let l1_rollup = Rollup::RollupInstance::new(rollup_address, l1_provider.clone()); let l1_shadow_rollup = ShadowRollup::new(shadow_rollup_address, wallet); + let canonical_log_index = Arc::new(Mutex::new( + CanonicalLogIndex::new(rollup_deployed_block) + .expect("SHADOW_PROVING_L1_ROLLUP_DEPLOY_BLOCK must be non-zero"), + )); - Self { l1_provider, l2_provider, l1_rollup, l1_shadow_rollup, rollup_deployed_block } + Self { l1_provider, l2_provider, l1_rollup, l1_shadow_rollup, canonical_log_index } } // Fetch the latest committed batch from l1-rollup if the batch_index has increased. @@ -131,32 +135,20 @@ where } self.assert_snapshot(snapshot, snapshot_hash).await?; - let prev = resolve_canonical_commit( - &self.l1_provider, - &self.l1_rollup, - batch_num - 1, - self.rollup_deployed_block, - snapshot, - ) - .await?; - self.assert_snapshot(snapshot, snapshot_hash).await?; - let current = resolve_canonical_commit( - &self.l1_provider, - &self.l1_rollup, - batch_num, - self.rollup_deployed_block, - snapshot, - ) - .await?; - self.assert_snapshot(snapshot, snapshot_hash).await?; - let next = resolve_canonical_commit( - &self.l1_provider, - &self.l1_rollup, - batch_num + 1, - self.rollup_deployed_block, - snapshot, - ) - .await?; + let mut canonical_log_index = self.canonical_log_index.lock().await; + canonical_log_index + .refresh(&self.l1_provider, &self.l1_rollup, snapshot, snapshot_hash) + .await?; + let prev = canonical_log_index + .resolve(&self.l1_rollup, batch_num - 1, snapshot, snapshot_hash) + .await?; + let current = canonical_log_index + .resolve(&self.l1_rollup, batch_num, snapshot, snapshot_hash) + .await?; + let next = canonical_log_index + .resolve(&self.l1_rollup, batch_num + 1, snapshot, snapshot_hash) + .await?; + drop(canonical_log_index); self.assert_snapshot(snapshot, snapshot_hash).await?; let (Some(prev), Some(current), Some(next)) = (prev, current, next) else { return Ok(None);