diff --git a/.github/workflows/contract-tests.yml b/.github/workflows/contract-tests.yml index 9e0ec1fe2f..46b2dbdd3f 100644 --- a/.github/workflows/contract-tests.yml +++ b/.github/workflows/contract-tests.yml @@ -85,6 +85,9 @@ jobs: - name: Forge build run: forge build + - name: Test size + run: yarn contract:size + - name: Test Storage Layouts run: yarn run test:storage diff --git a/hardhat.config.ts b/hardhat.config.ts index 401a29b304..34cb2483f3 100644 --- a/hardhat.config.ts +++ b/hardhat.config.ts @@ -5,6 +5,7 @@ import '@nomiclabs/hardhat-etherscan' import '@typechain/hardhat' import 'solidity-coverage' import 'hardhat-gas-reporter' +import 'hardhat-contract-sizer' import 'hardhat-ignore-warnings' // import '@tovarishfin/hardhat-yul'; import dotenv from 'dotenv' @@ -23,7 +24,17 @@ const solidity = { }, }, ], - overrides: {}, + overrides: { + 'src/rollup/RollupUserLogic.sol': { + version: '0.8.9', + settings: { + optimizer: { + enabled: true, + runs: 20, + }, + }, + }, + }, } if (process.env['INTERFACE_TESTER_SOLC_VERSION']) { diff --git a/package.json b/package.json index 17537128fa..2c36320162 100644 --- a/package.json +++ b/package.json @@ -26,6 +26,7 @@ "build:forge:sol": "forge build --skip *.yul", "build:forge:yul": "FOUNDRY_PROFILE=yul forge build --skip *.sol", "build:forge": "yarn build:forge:sol && yarn build:forge:yul", + "contract:size": "STRICT=true hardhat size-contracts", "lint:test": "eslint ./test", "solhint": "solhint -f table src/**/*.sol", "prettier:solidity": "prettier --write src/**/*.sol", @@ -81,6 +82,7 @@ "hardhat-deploy": "^0.11.37", "hardhat-gas-reporter": "^1.0.9", "hardhat-ignore-warnings": "^0.2.9", + "hardhat-contract-sizer": "^2.10.0", "postinstall-postinstall": "^2.1.0", "prettier": "^2.5.1", "prettier-plugin-solidity": "^1.0.0-beta.19", diff --git a/src/rollup/IRollupLogic.sol b/src/rollup/IRollupLogic.sol index bc1520be63..b3ec866009 100644 --- a/src/rollup/IRollupLogic.sol +++ b/src/rollup/IRollupLogic.sol @@ -24,6 +24,8 @@ interface IRollupUserAbs is IRollupCore, IOwnable { function confirmNextNode(bytes32 blockHash, bytes32 sendRoot) external; + function fastConfirmNextNode(bytes32 blockHash, bytes32 sendRoot) external; + function stakeOnExistingNode(uint64 nodeNum, bytes32 nodeHash) external; function stakeOnNewNode( diff --git a/src/rollup/RollupAdminLogic.sol b/src/rollup/RollupAdminLogic.sol index f7f07b1f55..0579eaaf22 100644 --- a/src/rollup/RollupAdminLogic.sol +++ b/src/rollup/RollupAdminLogic.sol @@ -379,4 +379,15 @@ contract RollupAdminLogic is RollupCore, IRollupAdmin, DoubleLogicUUPSUpgradeabl validatorWhitelistDisabled = _validatorWhitelistDisabled; emit OwnerFunctionCalled(30); } + + /** + * @notice set the anyTrustFastConfirmer address + * must also call `setValidator` to set the same address as a validator to work + * old fast confirmer need to be removed from the validator list manually + * @param _anyTrustFastConfirmer new value of anyTrustFastConfirmer + */ + function setAnyTrustFastConfirmer(address _anyTrustFastConfirmer) external { + anyTrustFastConfirmer = _anyTrustFastConfirmer; + emit OwnerFunctionCalled(31); + } } diff --git a/src/rollup/RollupCore.sol b/src/rollup/RollupCore.sol index a62655ff58..621783e841 100644 --- a/src/rollup/RollupCore.sol +++ b/src/rollup/RollupCore.sol @@ -77,6 +77,7 @@ abstract contract RollupCore is IRollupCore, PausableUpgradeable { uint64 internal constant GENESIS_NODE = 0; bool public validatorWhitelistDisabled; + address public anyTrustFastConfirmer; // If the chain this RollupCore is deployed on is an Arbitrum chain. bool internal immutable _hostChainIsArbitrum = ArbitrumChecker.runningOnArbitrum(); diff --git a/src/rollup/RollupUserLogic.sol b/src/rollup/RollupUserLogic.sol index 9807f20142..de28e14de8 100644 --- a/src/rollup/RollupUserLogic.sol +++ b/src/rollup/RollupUserLogic.sol @@ -116,29 +116,28 @@ abstract contract AbsRollupUserLogic is emit NodeRejected(firstUnresolvedNodeNum); } - /** - * @notice Confirm the next unresolved node - * @param blockHash The block hash at the end of the assertion - * @param sendRoot The send root at the end of the assertion - */ - function confirmNextNode(bytes32 blockHash, bytes32 sendRoot) - external - onlyValidator - whenNotPaused - { + function _confirmNextNode( + bytes32 blockHash, + bytes32 sendRoot, + bool isFastConfirm + ) internal { requireUnresolvedExists(); uint64 nodeNum = firstUnresolvedNode(); Node storage node = getNodeStorage(nodeNum); - // Verify the block's deadline has passed - node.requirePastDeadline(); + if (!isFastConfirm) { + // Verify the block's deadline has passed + node.requirePastDeadline(); + } // Check that prev is latest confirmed assert(node.prevNum == latestConfirmed()); Node storage prevNode = getNodeStorage(node.prevNum); - prevNode.requirePastChildConfirmDeadline(); + if (!isFastConfirm) { + prevNode.requirePastChildConfirmDeadline(); + } removeOldZombies(0); @@ -155,6 +154,29 @@ abstract contract AbsRollupUserLogic is confirmNode(nodeNum, blockHash, sendRoot); } + /** + * @notice Confirm the next unresolved node + * @param blockHash The block hash at the end of the assertion + * @param sendRoot The send root at the end of the assertion + */ + function confirmNextNode(bytes32 blockHash, bytes32 sendRoot) + external + onlyValidator + whenNotPaused + { + _confirmNextNode(blockHash, sendRoot, false); + } + + /** + * @notice This allow anyTrustFastConfirmer to confirm next node regardless of deadline + * the anyTrustFastConfirmer is supposed to be set only on an AnyTrust chain to + * a contract that can call this function when received sufficient signatures + */ + function fastConfirmNextNode(bytes32 blockHash, bytes32 sendRoot) external whenNotPaused { + require(msg.sender == anyTrustFastConfirmer, "NOT_FAST_CONFIRMER"); + _confirmNextNode(blockHash, sendRoot, true); + } + /** * @notice Create a new stake * @param depositAmount The amount of either eth or tokens staked diff --git a/test/contract/arbRollup.spec.ts b/test/contract/arbRollup.spec.ts index 8660f0ef68..998587eea1 100644 --- a/test/contract/arbRollup.spec.ts +++ b/test/contract/arbRollup.spec.ts @@ -1504,3 +1504,98 @@ describe('ArbRollup', () => { ) }) }) + +const fastConfirmerAddr = '0x000000000000000000000000000000000000fa51' +describe.only('ArbRollupFastConfirm', () => { + it('should initialize', async function () { + const { + rollupAdmin: rollupAdminContract, + rollupUser: rollupUserContract, + bridge: bridgeContract, + admin: adminI, + validators: validatorsI, + batchPosterManager: batchPosterManagerI, + upgradeExecutorAddress, + } = await setup() + rollupAdmin = rollupAdminContract + rollupUser = rollupUserContract + bridge = bridgeContract + admin = adminI + validators = validatorsI + upgradeExecutor = upgradeExecutorAddress + // adminproxy = adminproxyAddress + rollup = new RollupContract(rollupUser.connect(validators[0])) + batchPosterManager = batchPosterManagerI + }) + it('should set fast confirmer', async function () { + await ( + await rollupAdmin + .connect(await impersonateAccount(upgradeExecutor)) + .setAnyTrustFastConfirmer(fastConfirmerAddr) + ).wait() + await expect(await rollup.rollup.anyTrustFastConfirmer()).to.eq( + fastConfirmerAddr + ) + }) + it('should place stake on new node', async function () { + await tryAdvanceChain(minimumAssertionPeriod) + + const initNode: { + assertion: { afterState: ExecutionStateStruct } + nodeNum: number + nodeHash: BytesLike + inboxMaxCount: BigNumber + } = { + assertion: { + afterState: { + globalState: { + bytes32Vals: [zerobytes32, zerobytes32], + u64Vals: [0, 0], + }, + machineStatus: MachineStatus.FINISHED, + }, + }, + inboxMaxCount: BigNumber.from(1), + nodeHash: zerobytes32, + nodeNum: 0, + } + + const stake = await rollup.currentRequiredStake() + const { node } = await makeSimpleNode( + rollup, + sequencerInbox, + initNode, + undefined, + undefined, + stake + ) + updatePrevNode(node) + }) + it('should fail to confirm before deadline', async function () { + await expect(rollup.confirmNextNode(prevNodes[0])).to.be.revertedWith( + 'BEFORE_DEADLINE' + ) + }) + it('should fail to fast confirm if not fast confirmer', async function () { + await expect(rollup.fastConfirmNextNode(prevNodes[0])).to.be.revertedWith( + 'NOT_FAST_CONFIRMER' + ) + }) + it('should fail to fast confirm if not validator', async function () { + await expect( + rollup + .connect(await impersonateAccount(fastConfirmerAddr)) + .fastConfirmNextNode(prevNodes[0]) + ).to.be.revertedWith('NOT_VALIDATOR') + }) + it('should fast confirm', async function () { + await ( + await rollupAdmin + .connect(await impersonateAccount(upgradeExecutor)) + .setValidator([fastConfirmerAddr], [true]) + ).wait() + await rollup + .connect(await impersonateAccount(fastConfirmerAddr)) + .fastConfirmNextNode(prevNodes[0]) + }) +}) diff --git a/test/contract/common/rolluplib.ts b/test/contract/common/rolluplib.ts index 4ef9c10a7d..46758f52d5 100644 --- a/test/contract/common/rolluplib.ts +++ b/test/contract/common/rolluplib.ts @@ -224,6 +224,13 @@ export class RollupContract { ) } + fastConfirmNextNode(node: Node): Promise { + return this.rollup.fastConfirmNextNode( + node.assertion.afterState.globalState.bytes32Vals[0], + node.assertion.afterState.globalState.bytes32Vals[1] + ) + } + rejectNextNode(stakerAddress: string): Promise { return this.rollup.rejectNextNode(stakerAddress) } diff --git a/test/signatures/RollupAdminLogic b/test/signatures/RollupAdminLogic index 65a3167e1e..7db12d7576 100644 --- a/test/signatures/RollupAdminLogic +++ b/test/signatures/RollupAdminLogic @@ -1,6 +1,7 @@ { "_stakerMap(address)": "e8bd4922", "amountStaked(address)": "ef40a670", + "anyTrustFastConfirmer()": "55840a58", "baseStake()": "76e7e23b", "bridge()": "e78cea92", "chainId()": "9a8a0592", @@ -40,6 +41,7 @@ "rollupDeploymentBlock()": "1b1689e9", "rollupEventInbox()": "aa38a6e7", "sequencerInbox()": "ee35f327", + "setAnyTrustFastConfirmer(address)": "0d561b37", "setBaseStake(uint256)": "06ae5851", "setConfirmPeriodBlocks(uint64)": "ce66d05c", "setDelayedInbox(address,bool)": "47fb24c5", diff --git a/test/signatures/RollupCore b/test/signatures/RollupCore index 01125d8eae..f830b8cc0e 100644 --- a/test/signatures/RollupCore +++ b/test/signatures/RollupCore @@ -1,6 +1,7 @@ { "_stakerMap(address)": "e8bd4922", "amountStaked(address)": "ef40a670", + "anyTrustFastConfirmer()": "55840a58", "baseStake()": "76e7e23b", "bridge()": "e78cea92", "chainId()": "9a8a0592", diff --git a/test/signatures/RollupUserLogic b/test/signatures/RollupUserLogic index e0b3588941..241d535771 100644 --- a/test/signatures/RollupUserLogic +++ b/test/signatures/RollupUserLogic @@ -3,6 +3,7 @@ "_stakerMap(address)": "e8bd4922", "addToDeposit(address)": "45c5b2c7", "amountStaked(address)": "ef40a670", + "anyTrustFastConfirmer()": "55840a58", "baseStake()": "76e7e23b", "bridge()": "e78cea92", "chainId()": "9a8a0592", @@ -16,6 +17,7 @@ "currentChallenge(address)": "69fd251c", "currentRequiredStake()": "4d26732d", "extraChallengeTimeBlocks()": "771b2f97", + "fastConfirmNextNode(bytes32,bytes32)": "4a7a8010", "firstUnresolvedNode()": "d735e21d", "getNode(uint64)": "92c8134c", "getNodeCreationBlockForLogLookup(uint64)": "f63a434a", diff --git a/test/storage/RollupAdminLogic b/test/storage/RollupAdminLogic index 7832c870a4..8e3dc844e0 100644 --- a/test/storage/RollupAdminLogic +++ b/test/storage/RollupAdminLogic @@ -35,4 +35,5 @@ | totalWithdrawableFunds | uint256 | 124 | 0 | 32 | src/rollup/RollupAdminLogic.sol:RollupAdminLogic | | rollupDeploymentBlock | uint256 | 125 | 0 | 32 | src/rollup/RollupAdminLogic.sol:RollupAdminLogic | | validatorWhitelistDisabled | bool | 126 | 0 | 1 | src/rollup/RollupAdminLogic.sol:RollupAdminLogic | +| anyTrustFastConfirmer | address | 126 | 1 | 20 | src/rollup/RollupAdminLogic.sol:RollupAdminLogic | | _nodeCreatedAtArbSysBlock | mapping(uint64 => uint256) | 127 | 0 | 32 | src/rollup/RollupAdminLogic.sol:RollupAdminLogic | diff --git a/test/storage/RollupCore b/test/storage/RollupCore index 4480b0cc5b..1349cece22 100644 --- a/test/storage/RollupCore +++ b/test/storage/RollupCore @@ -35,4 +35,5 @@ | totalWithdrawableFunds | uint256 | 124 | 0 | 32 | src/rollup/RollupCore.sol:RollupCore | | rollupDeploymentBlock | uint256 | 125 | 0 | 32 | src/rollup/RollupCore.sol:RollupCore | | validatorWhitelistDisabled | bool | 126 | 0 | 1 | src/rollup/RollupCore.sol:RollupCore | +| anyTrustFastConfirmer | address | 126 | 1 | 20 | src/rollup/RollupCore.sol:RollupCore | | _nodeCreatedAtArbSysBlock | mapping(uint64 => uint256) | 127 | 0 | 32 | src/rollup/RollupCore.sol:RollupCore | diff --git a/test/storage/RollupUserLogic b/test/storage/RollupUserLogic index 27139d25ba..5fd459185d 100644 --- a/test/storage/RollupUserLogic +++ b/test/storage/RollupUserLogic @@ -35,4 +35,5 @@ | totalWithdrawableFunds | uint256 | 124 | 0 | 32 | src/rollup/RollupUserLogic.sol:RollupUserLogic | | rollupDeploymentBlock | uint256 | 125 | 0 | 32 | src/rollup/RollupUserLogic.sol:RollupUserLogic | | validatorWhitelistDisabled | bool | 126 | 0 | 1 | src/rollup/RollupUserLogic.sol:RollupUserLogic | +| anyTrustFastConfirmer | address | 126 | 1 | 20 | src/rollup/RollupUserLogic.sol:RollupUserLogic | | _nodeCreatedAtArbSysBlock | mapping(uint64 => uint256) | 127 | 0 | 32 | src/rollup/RollupUserLogic.sol:RollupUserLogic | diff --git a/yarn.lock b/yarn.lock index e8abb5fe1a..4eb93f42be 100644 --- a/yarn.lock +++ b/yarn.lock @@ -70,6 +70,11 @@ "@chainsafe/persistent-merkle-tree" "^0.4.2" case "^1.6.3" +"@colors/colors@1.5.0": + version "1.5.0" + resolved "https://registry.yarnpkg.com/@colors/colors/-/colors-1.5.0.tgz#bb504579c1cae923e6576a4f5da43d25f97bdbd9" + integrity sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ== + "@cspotcode/source-map-consumer@0.8.0": version "0.8.0" resolved "https://registry.yarnpkg.com/@cspotcode/source-map-consumer/-/source-map-consumer-0.8.0.tgz#33bf4b7b39c178821606f669bbc447a6a629786b" @@ -2621,6 +2626,15 @@ cli-table3@^0.5.0: optionalDependencies: colors "^1.1.2" +cli-table3@^0.6.0: + version "0.6.5" + resolved "https://registry.yarnpkg.com/cli-table3/-/cli-table3-0.6.5.tgz#013b91351762739c16a9567c21a04632e449bf2f" + integrity sha512-+W/5efTR7y5HRD7gACw9yQjqMVvEMLBHmboM/kPWam+H+Hmyrgjh6YncVKK122YZkXrLudzTuAukUw9FnMf7IQ== + dependencies: + string-width "^4.2.0" + optionalDependencies: + "@colors/colors" "1.5.0" + cli-width@^2.0.0: version "2.2.1" resolved "https://registry.yarnpkg.com/cli-width/-/cli-width-2.2.1.tgz#b0433d0b4e9c847ef18868a4ef16fd5fc8271c48" @@ -4381,6 +4395,15 @@ har-validator@~5.1.3: ajv "^6.12.3" har-schema "^2.0.0" +hardhat-contract-sizer@^2.10.0: + version "2.10.0" + resolved "https://registry.yarnpkg.com/hardhat-contract-sizer/-/hardhat-contract-sizer-2.10.0.tgz#72646f43bfe50e9a5702c9720c9bc3e77d93a2c9" + integrity sha512-QiinUgBD5MqJZJh1hl1jc9dNnpJg7eE/w4/4GEnrcmZJJTDbVFNe3+/3Ep24XqISSkYxRz36czcPHKHd/a0dwA== + dependencies: + chalk "^4.0.0" + cli-table3 "^0.6.0" + strip-ansi "^6.0.0" + hardhat-deploy@^0.11.37: version "0.11.37" resolved "https://registry.yarnpkg.com/hardhat-deploy/-/hardhat-deploy-0.11.37.tgz#6a771b859c82ae25292321a6d510d7c0eda09d2b"