Skip to content
Merged
Show file tree
Hide file tree
Changes from 15 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
"scripts": {
"prepublishOnly": "hardhat clean && hardhat compile",
"build": "hardhat compile",
"test": "hardhat --network hardhat test test/contract/*.spec.ts",
"lint:test": "eslint ./test",
"solhint": "solhint -f table src/**/*.sol",
"prettier:solidity": "prettier --write src/**/*.sol",
Expand All @@ -32,8 +33,7 @@
"deploy-factory": "hardhat run scripts/deployment.ts",
"deploy-rollup": "hardhat run scripts/rollupCreation.ts",
"deploy-eth-rollup": "hardhat run scripts/createEthRollup.ts",
"deploy-erc20-rollup": "hardhat run scripts/createERC20Rollup.ts",
"test": "hardhat --network hardhat test test/contract/*.spec.ts"
"deploy-erc20-rollup": "hardhat run scripts/createERC20Rollup.ts"
},
"dependencies": {
"@offchainlabs/upgrade-executor": "1.1.0-beta.0",
Expand Down
8 changes: 8 additions & 0 deletions src/bridge/ISequencerInbox.sol
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,8 @@ interface ISequencerInbox is IDelayedMessageProvider {

function maxDataSize() external view returns (uint256);

function batchPosterManager() external view returns (address);

struct DasKeySetInfo {
bool isValidKeyset;
uint64 creationBlock;
Expand Down Expand Up @@ -165,6 +167,12 @@ interface ISequencerInbox is IDelayedMessageProvider {
*/
function setIsSequencer(address addr, bool isSequencer_) external;

/**
* @notice Updates the batch poster manager, the address which has the ability to rotate batch poster keys
* @param newBatchPosterManager The new batch poster manager to be set
*/
function setBatchPosterManager(address newBatchPosterManager) external;

/// @notice Allows the rollup owner to sync the rollup address
function updateRollupAddress() external;
}
23 changes: 21 additions & 2 deletions src/bridge/SequencerInbox.sol
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import {
AlreadyValidDASKeyset,
NoSuchKeyset,
NotForked,
NotBatchPosterManager,
NotOwner,
RollupNotChanged
} from "../libraries/Error.sol";
Expand Down Expand Up @@ -55,6 +56,7 @@ contract SequencerInbox is GasRefundEnabled, ISequencerInbox {
bytes1 public constant DATA_AUTHENTICATED_FLAG = 0x40;

IOwnable public rollup;

mapping(address => bool) public isBatchPoster;
// see ISequencerInbox.MaxTimeVariation
uint256 internal immutable delayBlocks;
Expand All @@ -71,6 +73,10 @@ contract SequencerInbox is GasRefundEnabled, ISequencerInbox {

mapping(address => bool) public isSequencer;

/// @notice The batch poster manager has the ability to change the batch poster addresses
/// This enables the batch poster to do key rotation
address public batchPosterManager;

// On L1 this should be set to 117964: 90% of Geth's 128KB tx size limit, leaving ~13KB for proving
uint256 public immutable maxDataSize;
uint256 internal immutable deployTimeChainId = block.chainid;
Expand All @@ -80,7 +86,9 @@ contract SequencerInbox is GasRefundEnabled, ISequencerInbox {
constructor(
IBridge bridge_,
ISequencerInbox.MaxTimeVariation memory maxTimeVariation_,
uint256 _maxDataSize
uint256 _maxDataSize,
address[] memory batchPosters_,
address batchPosterManager_
) {
if (bridge_ == IBridge(address(0))) revert HadZeroInit();
bridge = bridge_;
Expand All @@ -91,6 +99,10 @@ contract SequencerInbox is GasRefundEnabled, ISequencerInbox {
delaySeconds = maxTimeVariation_.delaySeconds;
futureSeconds = maxTimeVariation_.futureSeconds;
maxDataSize = _maxDataSize;
for (uint256 i = 0; i < batchPosters_.length; i++) {
isBatchPoster[batchPosters_[i]] = true;
}
batchPosterManager = batchPosterManager_;
}

function _chainIdChanged() internal view returns (bool) {
Expand Down Expand Up @@ -469,11 +481,18 @@ contract SequencerInbox is GasRefundEnabled, ISequencerInbox {
}

/// @inheritdoc ISequencerInbox
function setIsSequencer(address addr, bool isSequencer_) external onlyRollupOwner {
function setIsSequencer(address addr, bool isSequencer_) external {
if (msg.sender != batchPosterManager) revert NotBatchPosterManager(msg.sender);
isSequencer[addr] = isSequencer_;
emit OwnerFunctionCalled(4);
}

/// @inheritdoc ISequencerInbox
function setBatchPosterManager(address newBatchPosterManager) external onlyRollupOwner {
batchPosterManager = newBatchPosterManager;
emit OwnerFunctionCalled(5);
}

function isValidKeysetHash(bytes32 ksHash) external view returns (bool) {
return dasKeySetInfo[ksHash].isValidKeyset;
}
Expand Down
3 changes: 3 additions & 0 deletions src/libraries/Error.sol
Original file line number Diff line number Diff line change
Expand Up @@ -176,5 +176,8 @@ error AlreadyValidDASKeyset(bytes32);
/// @dev Tried to use or invalidate an already invalid Data Availability Service keyset
error NoSuchKeyset(bytes32);

/// @dev Thrown when the provided address is not the designated batch poster manager
error NotBatchPosterManager(address);

/// @dev Thrown when rollup is not updated with updateRollupAddress
error RollupNotChanged();
6 changes: 4 additions & 2 deletions src/mocks/SequencerInboxStub.sol
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,10 @@ contract SequencerInboxStub is SequencerInbox {
IBridge bridge_,
address sequencer_,
ISequencerInbox.MaxTimeVariation memory maxTimeVariation_,
uint256 maxDataSize_
) SequencerInbox(bridge_, maxTimeVariation_, maxDataSize_) {
uint256 maxDataSize_,
address[] memory batchPosters_,
address batchPosterManager_
) SequencerInbox(bridge_, maxTimeVariation_, maxDataSize_, batchPosters_, batchPosterManager_) {
isBatchPoster[sequencer_] = true;
}

Expand Down
8 changes: 6 additions & 2 deletions src/rollup/BridgeCreator.sol
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,9 @@ contract BridgeCreator is Ownable {
address rollup,
address nativeToken,
ISequencerInbox.MaxTimeVariation calldata maxTimeVariation,
uint256 maxDataSize
uint256 maxDataSize,
address[] memory batchPosters,
address batchPosterManager
) external returns (BridgeContracts memory) {
// create ETH-based bridge if address zero is provided for native token, otherwise create ERC20-based bridge
BridgeContracts memory frame = _createBridge(
Expand All @@ -106,7 +108,9 @@ contract BridgeCreator is Ownable {
frame.sequencerInbox = new SequencerInbox(
IBridge(frame.bridge),
maxTimeVariation,
maxDataSize
maxDataSize,
batchPosters,
batchPosterManager
);
frame.inbox.initialize(frame.bridge, frame.sequencerInbox);
frame.rollupEventInbox.initialize(frame.bridge);
Expand Down
13 changes: 6 additions & 7 deletions src/rollup/RollupCreator.sol
Original file line number Diff line number Diff line change
Expand Up @@ -35,12 +35,14 @@ contract RollupCreator is Ownable {

struct RollupDeploymentParams {
Config config;
address batchPoster;
address[] validators;
uint256 maxDataSize;
address nativeToken;
bool deployFactoriesToL2;
uint256 maxFeePerGasForRetryables;
//// @dev The address of the batch poster, not used when set to zero address
address[] batchPosters;
address batchPosterManager;
}

BridgeCreator public bridgeCreator;
Expand Down Expand Up @@ -127,7 +129,9 @@ contract RollupCreator is Ownable {
address(rollup),
deployParams.nativeToken,
deployParams.config.sequencerInboxMaxTimeVariation,
deployParams.maxDataSize
deployParams.maxDataSize,
deployParams.batchPosters,
deployParams.batchPosterManager
);

IChallengeManager challengeManager = IChallengeManager(
Expand Down Expand Up @@ -171,11 +175,6 @@ contract RollupCreator is Ownable {
})
);

// setting batch poster, if the address provided is not zero address
if (deployParams.batchPoster != address(0)) {
bridgeContracts.sequencerInbox.setIsBatchPoster(deployParams.batchPoster, true);
}

// Call setValidator on the newly created rollup contract just if validator set is not empty
if (deployParams.validators.length != 0) {
bool[] memory _vals = new bool[](deployParams.validators.length);
Expand Down
81 changes: 80 additions & 1 deletion test/contract/arbRollup.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,7 @@ const wasmModuleRoot =

// let rollup: RollupContract
let rollup: RollupContract
let batchPosterManager: Signer
let rollupUser: RollupUserLogic
let rollupAdmin: RollupAdminLogic
let bridge: Bridge
Expand Down Expand Up @@ -131,6 +132,7 @@ const setup = async () => {
const val3 = accounts[4]
const val4 = accounts[5]
sequencer = accounts[6]
const batchPosterManager = accounts[7]

const oneStep0Fac = (await ethers.getContractFactory(
'OneStepProver0'
Expand Down Expand Up @@ -264,7 +266,7 @@ const setup = async () => {

const deployParams = {
config: await getDefaultConfig(),
batchPoster: await sequencer.getAddress(),
batchPosters: [await sequencer.getAddress()],
validators: [
await val1.getAddress(),
await val2.getAddress(),
Expand All @@ -275,6 +277,7 @@ const setup = async () => {
nativeToken: ethers.constants.AddressZero,
deployFactoriesToL2: true,
maxFeePerGasForRetryables: maxFeePerGas,
batchPosterManager: await batchPosterManager.getAddress(),
}

const response = await rollupCreator.createRollup(deployParams, {
Expand Down Expand Up @@ -325,6 +328,7 @@ const setup = async () => {
delayedBridge: rollupCreatedEvent.bridge,
delayedInbox: rollupCreatedEvent.inboxAddress,
bridge,
batchPosterManager,
upgradeExecutorAddress: rollupCreatedEvent.upgradeExecutor,
adminproxy: rollupCreatedEvent.adminProxy,
}
Expand Down Expand Up @@ -509,6 +513,7 @@ describe('ArbRollup', () => {
bridge: bridgeContract,
admin: adminI,
validators: validatorsI,
batchPosterManager: batchPosterManagerI,
upgradeExecutorAddress,
adminproxy: adminproxyAddress,
} = await setup()
Expand All @@ -520,6 +525,7 @@ describe('ArbRollup', () => {
upgradeExecutor = upgradeExecutorAddress
adminproxy = adminproxyAddress
rollup = new RollupContract(rollupUser.connect(validators[0]))
batchPosterManager = batchPosterManagerI
})

it('should only initialize once', async function () {
Expand Down Expand Up @@ -1110,6 +1116,7 @@ describe('ArbRollup', () => {
rollupUser: rollupUserContract,
admin: adminI,
validators: validatorsI,
batchPosterManager: batchPosterManagerI,
upgradeExecutorAddress,
} = await setup()
rollupAdmin = rollupAdminContract
Expand All @@ -1118,6 +1125,7 @@ describe('ArbRollup', () => {
validators = validatorsI
upgradeExecutor = upgradeExecutorAddress
rollup = new RollupContract(rollupUser.connect(validators[0]))
batchPosterManager = batchPosterManagerI
})

it('should stake on initial node again', async function () {
Expand Down Expand Up @@ -1368,6 +1376,77 @@ describe('ArbRollup', () => {
).to.eq('view')
})

it('can set is sequencer', async function () {
const testAddress = await accounts[9].getAddress()
expect(await sequencerInbox.isSequencer(testAddress)).to.be.false
await expect(
sequencerInbox.setIsSequencer(testAddress, true)
).to.revertedWith(
`NotBatchPosterManager("${await sequencerInbox.signer.getAddress()}")`
)
expect(await sequencerInbox.isSequencer(testAddress)).to.be.false

await (
await sequencerInbox
.connect(batchPosterManager)
.setIsSequencer(testAddress, true)
).wait()

expect(await sequencerInbox.isSequencer(testAddress)).to.be.true

await (
await sequencerInbox
.connect(batchPosterManager)
.setIsSequencer(testAddress, false)
).wait()

expect(await sequencerInbox.isSequencer(testAddress)).to.be.false
})

it('can set a batch poster', async function () {
const testAddress = await accounts[9].getAddress()
expect(await sequencerInbox.isBatchPoster(testAddress)).to.be.false
await expect(
sequencerInbox.setIsBatchPoster(testAddress, true)
).to.revertedWith(
`NotBatchPosterManager("${await sequencerInbox.signer.getAddress()}")`
)
expect(await sequencerInbox.isBatchPoster(testAddress)).to.be.false

await (
await sequencerInbox
.connect(batchPosterManager)
.setIsBatchPoster(testAddress, true)
).wait()

expect(await sequencerInbox.isBatchPoster(testAddress)).to.be.true

await (
await sequencerInbox
.connect(batchPosterManager)
.setIsBatchPoster(testAddress, false)
).wait()

expect(await sequencerInbox.isBatchPoster(testAddress)).to.be.false
})

it('can set batch poster manager', async function () {
const testManager = await accounts[8].getAddress()
expect(await sequencerInbox.batchPosterManager()).to.eq(
await batchPosterManager.getAddress()
)
await expect(
sequencerInbox.connect(accounts[8]).setBatchPosterManager(testManager)
).to.revertedWith(`NotOwner("${testManager}", "${rollupUser.address}")`)
expect(await sequencerInbox.batchPosterManager()).to.eq(
await batchPosterManager.getAddress()
)

await (await sequencerInbox.setBatchPosterManager(testManager)).wait()

expect(await sequencerInbox.batchPosterManager()).to.eq(testManager)
})

it('should fail the batch poster check', async function () {
await expect(
sequencerInbox.addSequencerL2Batch(
Expand Down
7 changes: 5 additions & 2 deletions test/contract/sequencerInboxForceInclude.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -221,8 +221,9 @@ describe('SequencerInboxForceInclude', async () => {
const admin = accounts[0]
const adminAddr = await admin.getAddress()
const user = accounts[1]
const rollupOwner = accounts[2]
const batchPoster = accounts[3]
const batchPosterManager = accounts[4]
const rollupOwner = accounts[5]

const rollupMockFac = (await ethers.getContractFactory(
'RollupMock'
Expand Down Expand Up @@ -268,7 +269,9 @@ describe('SequencerInboxForceInclude', async () => {
futureBlocks: 10,
futureSeconds: 3000,
},
117964
117964,
[await batchPoster.getAddress()],
await batchPosterManager.getAddress()
)

const inbox = await inboxFac.attach(inboxProxy.address).connect(user)
Expand Down
Loading