Skip to content

Commit c755485

Browse files
authored
Merge pull request #74 from OffchainLabs/batch-poster-manager
Add a batch poster manager for the sequencer inbox
2 parents b4c739f + 14d2cd4 commit c755485

File tree

8 files changed

+172
-23
lines changed

8 files changed

+172
-23
lines changed

src/bridge/ISequencerInbox.sol

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,10 @@ interface ISequencerInbox is IDelayedMessageProvider {
9191

9292
function maxDataSize() external view returns (uint256);
9393

94+
/// @notice The batch poster manager has the ability to change the batch poster addresses
95+
/// This enables the batch poster to do key rotation
96+
function batchPosterManager() external view returns (address);
97+
9498
struct DasKeySetInfo {
9599
bool isValidKeyset;
96100
uint64 creationBlock;
@@ -210,9 +214,16 @@ interface ISequencerInbox is IDelayedMessageProvider {
210214
*/
211215
function setIsSequencer(address addr, bool isSequencer_) external;
212216

217+
/**
218+
* @notice Updates the batch poster manager, the address which has the ability to rotate batch poster keys
219+
* @param newBatchPosterManager The new batch poster manager to be set
220+
*/
221+
function setBatchPosterManager(address newBatchPosterManager) external;
222+
223+
/// @notice Allows the rollup owner to sync the rollup address
224+
function updateRollupAddress() external;
225+
213226
// ---------- initializer ----------
214227

215228
function initialize(IBridge bridge_, MaxTimeVariation calldata maxTimeVariation_) external;
216-
217-
function updateRollupAddress() external;
218229
}

src/bridge/SequencerInbox.sol

Lines changed: 27 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ import {
2222
AlreadyValidDASKeyset,
2323
NoSuchKeyset,
2424
NotForked,
25+
NotBatchPosterManager,
2526
RollupNotChanged,
2627
DataBlobsNotSupported,
2728
InitParamZero,
@@ -87,6 +88,7 @@ contract SequencerInbox is DelegateCallAware, GasRefundEnabled, ISequencerInbox
8788
uint256 internal constant GAS_PER_BLOB = 1 << 17;
8889

8990
IOwnable public rollup;
91+
9092
mapping(address => bool) public isBatchPoster;
9193

9294
// we previously stored the max time variation in a (uint,uint,uint,uint) struct here
@@ -99,6 +101,13 @@ contract SequencerInbox is DelegateCallAware, GasRefundEnabled, ISequencerInbox
99101
_;
100102
}
101103

104+
modifier onlyRollupOwnerOrBatchPosterManager() {
105+
if (msg.sender != rollup.owner() && msg.sender != batchPosterManager) {
106+
revert NotBatchPosterManager(msg.sender);
107+
}
108+
_;
109+
}
110+
102111
mapping(address => bool) public isSequencer;
103112
IReader4844 public immutable reader4844;
104113

@@ -108,6 +117,9 @@ contract SequencerInbox is DelegateCallAware, GasRefundEnabled, ISequencerInbox
108117
uint64 internal delaySeconds;
109118
uint64 internal futureSeconds;
110119

120+
/// @inheritdoc ISequencerInbox
121+
address public batchPosterManager;
122+
111123
// On L1 this should be set to 117964: 90% of Geth's 128KB tx size limit, leaving ~13KB for proving
112124
uint256 public immutable maxDataSize;
113125
uint256 internal immutable deployTimeChainId = block.chainid;
@@ -720,7 +732,10 @@ contract SequencerInbox is DelegateCallAware, GasRefundEnabled, ISequencerInbox
720732
}
721733

722734
/// @inheritdoc ISequencerInbox
723-
function setIsBatchPoster(address addr, bool isBatchPoster_) external onlyRollupOwner {
735+
function setIsBatchPoster(address addr, bool isBatchPoster_)
736+
external
737+
onlyRollupOwnerOrBatchPosterManager
738+
{
724739
isBatchPoster[addr] = isBatchPoster_;
725740
emit OwnerFunctionCalled(1);
726741
}
@@ -756,9 +771,18 @@ contract SequencerInbox is DelegateCallAware, GasRefundEnabled, ISequencerInbox
756771
}
757772

758773
/// @inheritdoc ISequencerInbox
759-
function setIsSequencer(address addr, bool isSequencer_) external onlyRollupOwner {
774+
function setIsSequencer(address addr, bool isSequencer_)
775+
external
776+
onlyRollupOwnerOrBatchPosterManager
777+
{
760778
isSequencer[addr] = isSequencer_;
761-
emit OwnerFunctionCalled(4);
779+
emit OwnerFunctionCalled(4); // Owner in this context can also be batch poster manager
780+
}
781+
782+
/// @inheritdoc ISequencerInbox
783+
function setBatchPosterManager(address newBatchPosterManager) external onlyRollupOwner {
784+
batchPosterManager = newBatchPosterManager;
785+
emit OwnerFunctionCalled(5);
762786
}
763787

764788
function isValidKeysetHash(bytes32 ksHash) external view returns (bool) {

src/libraries/Error.sol

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -176,6 +176,9 @@ error AlreadyValidDASKeyset(bytes32);
176176
/// @dev Tried to use or invalidate an already invalid Data Availability Service keyset
177177
error NoSuchKeyset(bytes32);
178178

179+
/// @dev Thrown when the provided address is not the designated batch poster manager
180+
error NotBatchPosterManager(address);
181+
179182
/// @dev Thrown when a data blob feature is attempted to be used on a chain that doesnt support it
180183
error DataBlobsNotSupported();
181184

src/rollup/RollupCreator.sol

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -35,12 +35,14 @@ contract RollupCreator is Ownable {
3535

3636
struct RollupDeploymentParams {
3737
Config config;
38-
address batchPoster;
3938
address[] validators;
4039
uint256 maxDataSize;
4140
address nativeToken;
4241
bool deployFactoriesToL2;
4342
uint256 maxFeePerGasForRetryables;
43+
//// @dev The address of the batch poster, not used when set to zero address
44+
address[] batchPosters;
45+
address batchPosterManager;
4446
}
4547

4648
BridgeCreator public bridgeCreator;
@@ -186,9 +188,12 @@ contract RollupCreator is Ownable {
186188
})
187189
);
188190

189-
// setting batch poster, if the address provided is not zero address
190-
if (deployParams.batchPoster != address(0)) {
191-
bridgeContracts.sequencerInbox.setIsBatchPoster(deployParams.batchPoster, true);
191+
// Setting batch posters and batch poster manager
192+
for (uint256 i = 0; i < deployParams.batchPosters.length; i++) {
193+
bridgeContracts.sequencerInbox.setIsBatchPoster(deployParams.batchPosters[i], true);
194+
}
195+
if (deployParams.batchPosterManager != address(0)) {
196+
bridgeContracts.sequencerInbox.setBatchPosterManager(deployParams.batchPosterManager);
192197
}
193198

194199
// Call setValidator on the newly created rollup contract just if validator set is not empty

test/contract/arbRollup.spec.ts

Lines changed: 88 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,7 @@ const dummy4844Reader = '0x0000000000000000000000000000000000000089'
8686

8787
// let rollup: RollupContract
8888
let rollup: RollupContract
89+
let batchPosterManager: Signer
8990
let rollupUser: RollupUserLogic
9091
let rollupAdmin: RollupAdminLogic
9192
let bridge: Bridge
@@ -132,6 +133,7 @@ const setup = async () => {
132133
const val3 = accounts[4]
133134
const val4 = accounts[5]
134135
sequencer = accounts[6]
136+
const batchPosterManager = accounts[7]
135137

136138
const oneStep0Fac = (await ethers.getContractFactory(
137139
'OneStepProver0'
@@ -285,7 +287,7 @@ const setup = async () => {
285287

286288
const deployParams = {
287289
config: await getDefaultConfig(),
288-
batchPoster: await sequencer.getAddress(),
290+
batchPosters: [await sequencer.getAddress()],
289291
validators: [
290292
await val1.getAddress(),
291293
await val2.getAddress(),
@@ -296,6 +298,7 @@ const setup = async () => {
296298
nativeToken: ethers.constants.AddressZero,
297299
deployFactoriesToL2: true,
298300
maxFeePerGasForRetryables: maxFeePerGas,
301+
batchPosterManager: await batchPosterManager.getAddress(),
299302
}
300303

301304
const response = await rollupCreator.createRollup(deployParams, {
@@ -322,6 +325,10 @@ const setup = async () => {
322325
)) as SequencerInbox__factory
323326
).attach(rollupCreatedEvent.sequencerInbox)
324327

328+
await sequencerInbox
329+
.connect(await impersonateAccount(rollupCreatedEvent.upgradeExecutor))
330+
.setBatchPosterManager(await batchPosterManager.getAddress())
331+
325332
challengeManager = (
326333
(await ethers.getContractFactory(
327334
'ChallengeManager'
@@ -346,6 +353,7 @@ const setup = async () => {
346353
delayedBridge: rollupCreatedEvent.bridge,
347354
delayedInbox: rollupCreatedEvent.inboxAddress,
348355
bridge,
356+
batchPosterManager,
349357
upgradeExecutorAddress: rollupCreatedEvent.upgradeExecutor,
350358
adminproxy: rollupCreatedEvent.adminProxy,
351359
}
@@ -524,6 +532,7 @@ describe('ArbRollup', () => {
524532
bridge: bridgeContract,
525533
admin: adminI,
526534
validators: validatorsI,
535+
batchPosterManager: batchPosterManagerI,
527536
upgradeExecutorAddress,
528537
adminproxy: adminproxyAddress,
529538
} = await setup()
@@ -535,6 +544,7 @@ describe('ArbRollup', () => {
535544
upgradeExecutor = upgradeExecutorAddress
536545
adminproxy = adminproxyAddress
537546
rollup = new RollupContract(rollupUser.connect(validators[0]))
547+
batchPosterManager = batchPosterManagerI
538548
})
539549

540550
it('should only initialize once', async function () {
@@ -1125,6 +1135,7 @@ describe('ArbRollup', () => {
11251135
rollupUser: rollupUserContract,
11261136
admin: adminI,
11271137
validators: validatorsI,
1138+
batchPosterManager: batchPosterManagerI,
11281139
upgradeExecutorAddress,
11291140
} = await setup()
11301141
rollupAdmin = rollupAdminContract
@@ -1133,6 +1144,7 @@ describe('ArbRollup', () => {
11331144
validators = validatorsI
11341145
upgradeExecutor = upgradeExecutorAddress
11351146
rollup = new RollupContract(rollupUser.connect(validators[0]))
1147+
batchPosterManager = batchPosterManagerI
11361148
})
11371149

11381150
it('should stake on initial node again', async function () {
@@ -1383,6 +1395,81 @@ describe('ArbRollup', () => {
13831395
).to.eq('view')
13841396
})
13851397

1398+
it('can set is sequencer', async function () {
1399+
const testAddress = await accounts[9].getAddress()
1400+
expect(await sequencerInbox.isSequencer(testAddress)).to.be.false
1401+
await expect(
1402+
sequencerInbox.setIsSequencer(testAddress, true)
1403+
).to.revertedWith(
1404+
`NotBatchPosterManager("${await sequencerInbox.signer.getAddress()}")`
1405+
)
1406+
expect(await sequencerInbox.isSequencer(testAddress)).to.be.false
1407+
1408+
await (
1409+
await sequencerInbox
1410+
.connect(batchPosterManager)
1411+
.setIsSequencer(testAddress, true)
1412+
).wait()
1413+
1414+
expect(await sequencerInbox.isSequencer(testAddress)).to.be.true
1415+
1416+
await (
1417+
await sequencerInbox
1418+
.connect(batchPosterManager)
1419+
.setIsSequencer(testAddress, false)
1420+
).wait()
1421+
1422+
expect(await sequencerInbox.isSequencer(testAddress)).to.be.false
1423+
})
1424+
1425+
it('can set a batch poster', async function () {
1426+
const testAddress = await accounts[9].getAddress()
1427+
expect(await sequencerInbox.isBatchPoster(testAddress)).to.be.false
1428+
await expect(
1429+
sequencerInbox.setIsBatchPoster(testAddress, true)
1430+
).to.revertedWith(
1431+
`NotBatchPosterManager("${await sequencerInbox.signer.getAddress()}")`
1432+
)
1433+
expect(await sequencerInbox.isBatchPoster(testAddress)).to.be.false
1434+
1435+
await (
1436+
await sequencerInbox
1437+
.connect(batchPosterManager)
1438+
.setIsBatchPoster(testAddress, true)
1439+
).wait()
1440+
1441+
expect(await sequencerInbox.isBatchPoster(testAddress)).to.be.true
1442+
1443+
await (
1444+
await sequencerInbox
1445+
.connect(batchPosterManager)
1446+
.setIsBatchPoster(testAddress, false)
1447+
).wait()
1448+
1449+
expect(await sequencerInbox.isBatchPoster(testAddress)).to.be.false
1450+
})
1451+
1452+
it('can set batch poster manager', async function () {
1453+
const testManager = await accounts[8].getAddress()
1454+
expect(await sequencerInbox.batchPosterManager()).to.eq(
1455+
await batchPosterManager.getAddress()
1456+
)
1457+
await expect(
1458+
sequencerInbox.connect(accounts[8]).setBatchPosterManager(testManager)
1459+
).to.revertedWith(`NotOwner("${testManager}", "${upgradeExecutor}")`)
1460+
expect(await sequencerInbox.batchPosterManager()).to.eq(
1461+
await batchPosterManager.getAddress()
1462+
)
1463+
1464+
await (
1465+
await sequencerInbox
1466+
.connect(await impersonateAccount(upgradeExecutor))
1467+
.setBatchPosterManager(testManager)
1468+
).wait()
1469+
1470+
expect(await sequencerInbox.batchPosterManager()).to.eq(testManager)
1471+
})
1472+
13861473
it('should fail the chainid fork check', async function () {
13871474
await expect(sequencerInbox.removeDelayAfterFork()).to.revertedWith(
13881475
'NotForked()'

test/contract/sequencerInboxForceInclude.spec.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -225,6 +225,7 @@ describe('SequencerInboxForceInclude', async () => {
225225
const dummyRollup = accounts[2]
226226
const rollupOwner = accounts[3]
227227
const batchPoster = accounts[4]
228+
const batchPosterManager = accounts[5]
228229

229230
const rollupMockFac = (await ethers.getContractFactory(
230231
'RollupMock'

0 commit comments

Comments
 (0)