diff --git a/arbnode/delayed.go b/arbnode/delayed.go index a966b3aea7e..f76b6dc5e3c 100644 --- a/arbnode/delayed.go +++ b/arbnode/delayed.go @@ -37,7 +37,7 @@ func init() { } messageDeliveredID = parsedIBridgeABI.Events["MessageDelivered"].ID - parsedIMessageProviderABI, err := bridgegen.IMessageProviderMetaData.GetAbi() + parsedIMessageProviderABI, err := bridgegen.IDelayedMessageProviderMetaData.GetAbi() if err != nil { panic(err) } @@ -56,7 +56,7 @@ type DelayedBridge struct { address common.Address fromBlock uint64 client arbutil.L1Interface - messageProviders map[common.Address]*bridgegen.IMessageProvider + messageProviders map[common.Address]*bridgegen.IDelayedMessageProvider } func NewDelayedBridge(client arbutil.L1Interface, addr common.Address, fromBlock uint64) (*DelayedBridge, error) { @@ -70,7 +70,7 @@ func NewDelayedBridge(client arbutil.L1Interface, addr common.Address, fromBlock address: addr, fromBlock: fromBlock, client: client, - messageProviders: make(map[common.Address]*bridgegen.IMessageProvider), + messageProviders: make(map[common.Address]*bridgegen.IDelayedMessageProvider), }, nil } @@ -86,7 +86,7 @@ func (b *DelayedBridge) GetMessageCount(ctx context.Context, blockNumber *big.In Context: ctx, BlockNumber: blockNumber, } - bigRes, err := b.con.MessageCount(opts) + bigRes, err := b.con.DelayedMessageCount(opts) if err != nil { return 0, errors.WithStack(err) } @@ -101,7 +101,7 @@ func (b *DelayedBridge) GetAccumulator(ctx context.Context, sequenceNumber uint6 Context: ctx, BlockNumber: blockNumber, } - return b.con.InboxAccs(opts, new(big.Int).SetUint64(sequenceNumber)) + return b.con.DelayedInboxAccs(opts, new(big.Int).SetUint64(sequenceNumber)) } type DelayedInboxMessage struct { @@ -255,7 +255,7 @@ func (b *DelayedBridge) parseMessage(ctx context.Context, ethLog types.Log) (*bi con, ok := b.messageProviders[ethLog.Address] if !ok { var err error - con, err = bridgegen.NewIMessageProvider(ethLog.Address, b.client) + con, err = bridgegen.NewIDelayedMessageProvider(ethLog.Address, b.client) if err != nil { return nil, nil, errors.WithStack(err) } diff --git a/arbnode/node.go b/arbnode/node.go index fd5bcc716b3..c12b3a71ce1 100644 --- a/arbnode/node.go +++ b/arbnode/node.go @@ -164,7 +164,7 @@ func deployBridgeCreator(ctx context.Context, l1Reader *headerreader.HeaderReade return common.Address{}, fmt.Errorf("inbox deploy error: %w", err) } - rollupEventBridgeTemplate, tx, _, err := rollupgen.DeployRollupEventBridge(auth, client) + rollupEventBridgeTemplate, tx, _, err := rollupgen.DeployRollupEventInbox(auth, client) err = andTxSucceeded(ctx, l1Reader, tx, err) if err != nil { return common.Address{}, fmt.Errorf("rollup event bridge deploy error: %w", err) @@ -232,33 +232,45 @@ func deployChallengeFactory(ctx context.Context, l1Reader *headerreader.HeaderRe return ospEntryAddr, challengeManagerAddr, nil } -func deployRollupCreator(ctx context.Context, l1Reader *headerreader.HeaderReader, auth *bind.TransactOpts) (*rollupgen.RollupCreator, common.Address, error) { +func deployRollupCreator(ctx context.Context, l1Reader *headerreader.HeaderReader, auth *bind.TransactOpts) (*rollupgen.RollupCreator, common.Address, common.Address, common.Address, error) { bridgeCreator, err := deployBridgeCreator(ctx, l1Reader, auth) if err != nil { - return nil, common.Address{}, err + return nil, common.Address{}, common.Address{}, common.Address{}, err } ospEntryAddr, challengeManagerAddr, err := deployChallengeFactory(ctx, l1Reader, auth) if err != nil { - return nil, common.Address{}, err + return nil, common.Address{}, common.Address{}, common.Address{}, err } rollupAdminLogic, tx, _, err := rollupgen.DeployRollupAdminLogic(auth, l1Reader.Client()) err = andTxSucceeded(ctx, l1Reader, tx, err) if err != nil { - return nil, common.Address{}, fmt.Errorf("rollup admin logic deploy error: %w", err) + return nil, common.Address{}, common.Address{}, common.Address{}, fmt.Errorf("rollup admin logic deploy error: %w", err) } rollupUserLogic, tx, _, err := rollupgen.DeployRollupUserLogic(auth, l1Reader.Client()) err = andTxSucceeded(ctx, l1Reader, tx, err) if err != nil { - return nil, common.Address{}, fmt.Errorf("rollup user logic deploy error: %w", err) + return nil, common.Address{}, common.Address{}, common.Address{}, fmt.Errorf("rollup user logic deploy error: %w", err) } rollupCreatorAddress, tx, rollupCreator, err := rollupgen.DeployRollupCreator(auth, l1Reader.Client()) err = andTxSucceeded(ctx, l1Reader, tx, err) if err != nil { - return nil, common.Address{}, fmt.Errorf("rollup creator deploy error: %w", err) + return nil, common.Address{}, common.Address{}, common.Address{}, fmt.Errorf("rollup creator deploy error: %w", err) + } + + validatorUtils, tx, _, err := rollupgen.DeployValidatorUtils(auth, l1Reader.Client()) + err = andTxSucceeded(ctx, l1Reader, tx, err) + if err != nil { + return nil, common.Address{}, common.Address{}, common.Address{}, fmt.Errorf("validator utils deploy error: %w", err) + } + + validatorWalletCreator, tx, _, err := rollupgen.DeployValidatorWalletCreator(auth, l1Reader.Client()) + err = andTxSucceeded(ctx, l1Reader, tx, err) + if err != nil { + return nil, common.Address{}, common.Address{}, common.Address{}, fmt.Errorf("validator wallet creator deploy error: %w", err) } tx, err = rollupCreator.SetTemplates( @@ -268,13 +280,15 @@ func deployRollupCreator(ctx context.Context, l1Reader *headerreader.HeaderReade challengeManagerAddr, rollupAdminLogic, rollupUserLogic, + validatorUtils, + validatorWalletCreator, ) err = andTxSucceeded(ctx, l1Reader, tx, err) if err != nil { - return nil, common.Address{}, fmt.Errorf("rollup set template error: %w", err) + return nil, common.Address{}, common.Address{}, common.Address{}, fmt.Errorf("rollup set template error: %w", err) } - return rollupCreator, rollupCreatorAddress, nil + return rollupCreator, rollupCreatorAddress, validatorUtils, validatorWalletCreator, nil } func DeployOnL1(ctx context.Context, l1client arbutil.L1Interface, deployAuth *bind.TransactOpts, sequencer common.Address, authorizeValidators uint64, wasmModuleRoot common.Hash, chainId *big.Int, readerConfig headerreader.Config, machineConfig validator.NitroMachineConfig) (*RollupAddresses, error) { @@ -290,7 +304,7 @@ func DeployOnL1(ctx context.Context, l1client arbutil.L1Interface, deployAuth *b } } - rollupCreator, rollupCreatorAddress, err := deployRollupCreator(ctx, l1Reader, deployAuth) + rollupCreator, rollupCreatorAddress, validatorUtils, validatorWalletCreator, err := deployRollupCreator(ctx, l1Reader, deployAuth) if err != nil { return nil, fmt.Errorf("error deploying rollup creator: %w", err) } @@ -345,18 +359,6 @@ func DeployOnL1(ctx context.Context, l1client arbutil.L1Interface, deployAuth *b return nil, fmt.Errorf("error setting is batch poster: %w", err) } - validatorUtils, tx, _, err := rollupgen.DeployValidatorUtils(deployAuth, l1client) - err = andTxSucceeded(ctx, l1Reader, tx, err) - if err != nil { - return nil, fmt.Errorf("validator utils deploy error: %w", err) - } - - validatorWalletCreator, tx, _, err := rollupgen.DeployValidatorWalletCreator(deployAuth, l1client) - err = andTxSucceeded(ctx, l1Reader, tx, err) - if err != nil { - return nil, fmt.Errorf("validator utils deploy error: %w", err) - } - var allowValidators []bool var validatorAddrs []common.Address for i := uint64(1); i <= authorizeValidators; i++ { @@ -376,7 +378,7 @@ func DeployOnL1(ctx context.Context, l1client arbutil.L1Interface, deployAuth *b } return &RollupAddresses{ - Bridge: info.DelayedBridge, + Bridge: info.Bridge, Inbox: info.InboxAddress, SequencerInbox: info.SequencerInbox, DeployedAt: receipt.BlockNumber.Uint64(), diff --git a/arbos/arbosState/arbosstate.go b/arbos/arbosState/arbosstate.go index d96970afbd6..34a4d54225f 100644 --- a/arbos/arbosState/arbosstate.go +++ b/arbos/arbosState/arbosstate.go @@ -206,11 +206,8 @@ func InitializeArbosState(stateDB vm.StateDB, burner burn.Burner, chainConfig *p merkleAccumulator.InitializeMerkleAccumulator(sto.OpenSubStorage(sendMerkleSubspace)) blockhash.InitializeBlockhashes(sto.OpenSubStorage(blockhashesSubspace)) - // by default, the remapped zero address is the initial chain owner - initialChainOwner := util.RemapL1Address(common.Address{}) - if chainConfig.ArbitrumChainParams.InitialChainOwner != (common.Address{}) { - initialChainOwner = chainConfig.ArbitrumChainParams.InitialChainOwner - } + // may be the zero address + initialChainOwner := chainConfig.ArbitrumChainParams.InitialChainOwner ownersStorage := sto.OpenSubStorage(chainOwnerSubspace) _ = addressSet.Initialize(ownersStorage) _ = addressSet.OpenAddressSet(ownersStorage).Add(initialChainOwner) diff --git a/arbos/incomingmessage.go b/arbos/incomingmessage.go index c0e42f6fe02..5d075d63db1 100644 --- a/arbos/incomingmessage.go +++ b/arbos/incomingmessage.go @@ -203,7 +203,7 @@ func (msg *L1IncomingMessage) ParseL2Transactions(chainId *big.Int, batchFetcher ChainId: chainId, L1RequestId: depositRequestId, // Matches the From of parseUnsignedTx - To: util.RemapL1Address(msg.Header.Poster), + To: msg.Header.Poster, Value: tx.Value(), }) return types.Transactions{deposit, tx}, nil @@ -381,7 +381,7 @@ func parseUnsignedTx(rd io.Reader, poster common.Address, requestId *common.Hash case L2MessageKind_UnsignedUserTx: inner = &types.ArbitrumUnsignedTx{ ChainId: chainId, - From: util.RemapL1Address(poster), + From: poster, Nonce: nonce, GasFeeCap: maxFeePerGas.Big(), Gas: gasLimit.Big().Uint64(), @@ -396,7 +396,7 @@ func parseUnsignedTx(rd io.Reader, poster common.Address, requestId *common.Hash inner = &types.ArbitrumContractTx{ ChainId: chainId, RequestId: *requestId, - From: util.RemapL1Address(poster), + From: poster, GasFeeCap: maxFeePerGas.Big(), Gas: gasLimit.Big().Uint64(), To: destination, @@ -411,6 +411,10 @@ func parseUnsignedTx(rd io.Reader, poster common.Address, requestId *common.Hash } func parseEthDepositMessage(rd io.Reader, header *L1IncomingMessageHeader, chainId *big.Int) (*types.Transaction, error) { + to, err := util.AddressFromReader(rd) + if err != nil { + return nil, err + } balance, err := util.HashFromReader(rd) if err != nil { return nil, err @@ -421,7 +425,8 @@ func parseEthDepositMessage(rd io.Reader, header *L1IncomingMessageHeader, chain tx := &types.ArbitrumDepositTx{ ChainId: chainId, L1RequestId: *header.RequestId, - To: util.RemapL1Address(header.Poster), + From: header.Poster, + To: to, Value: balance.Big(), } return types.NewTx(tx), nil @@ -492,7 +497,7 @@ func parseSubmitRetryableMessage(rd io.Reader, header *L1IncomingMessageHeader, tx := &types.ArbitrumSubmitRetryableTx{ ChainId: chainId, RequestId: *header.RequestId, - From: util.RemapL1Address(header.Poster), + From: header.Poster, L1BaseFee: header.L1BaseFee, DepositValue: depositValue.Big(), GasFeeCap: maxFeePerGas.Big(), diff --git a/arbos/tx_processor.go b/arbos/tx_processor.go index 6cb98fe8048..ace65c3c30c 100644 --- a/arbos/tx_processor.go +++ b/arbos/tx_processor.go @@ -107,9 +107,6 @@ func (p *TxProcessor) StartTxHook() (endTxNow bool, gasUsed uint64, err error, r switch tx := underlyingTx.GetInner().(type) { case *types.ArbitrumDepositTx: defer (startTracer())() - if p.msg.From() != arbosAddress { - return false, 0, errors.New("deposit not from arbAddress"), nil - } util.MintBalance(p.msg.To(), p.msg.Value(), evm, util.TracingDuringEVM, "deposit") return true, 0, nil, nil case *types.ArbitrumInternalTx: diff --git a/arbstate/geth_test.go b/arbstate/geth_test.go index 74b71b7e069..075516a8b0e 100644 --- a/arbstate/geth_test.go +++ b/arbstate/geth_test.go @@ -73,6 +73,9 @@ func TestEthDepositMessage(t *testing.T) { L1BaseFee: big.NewInt(10000000000000), } msgBuf := bytes.Buffer{} + if err := util.AddressToWriter(addr, &msgBuf); err != nil { + t.Error(err) + } if err := util.HashToWriter(balance, &msgBuf); err != nil { t.Error(err) } @@ -88,7 +91,11 @@ func TestEthDepositMessage(t *testing.T) { secondRequestId := common.BigToHash(big.NewInt(4)) header.RequestId = &secondRequestId + header.Poster = util.RemapL1Address(addr) msgBuf2 := bytes.Buffer{} + if err := util.AddressToWriter(addr, &msgBuf2); err != nil { + t.Error(err) + } if err := util.HashToWriter(balance2, &msgBuf2); err != nil { t.Error(err) } @@ -103,7 +110,7 @@ func TestEthDepositMessage(t *testing.T) { RunMessagesThroughAPI(t, [][]byte{serialized, serialized2}, statedb) - balanceAfter := statedb.GetBalance(util.RemapL1Address(addr)) + balanceAfter := statedb.GetBalance(addr) if balanceAfter.Cmp(new(big.Int).Add(balance.Big(), balance2.Big())) != 0 { Fail(t) } diff --git a/contracts/deploy/InboxStubCreator.js b/contracts/deploy/InboxStubCreator.js index 4694ac8da4d..69d23cf5306 100644 --- a/contracts/deploy/InboxStubCreator.js +++ b/contracts/deploy/InboxStubCreator.js @@ -9,7 +9,7 @@ module.exports = async (hre) => { const inbox = await ethers.getContract("InboxStub"); if (inboxDeployResult.newlyDeployed) { - await bridge.setInbox(inbox.address, true); + await bridge.setDelayedInbox(inbox.address, true); await inbox.initialize(bridge.address); } }; diff --git a/contracts/src/bridge/Bridge.sol b/contracts/src/bridge/Bridge.sol index db347e28fcb..73843a5ece5 100644 --- a/contracts/src/bridge/Bridge.sol +++ b/contracts/src/bridge/Bridge.sol @@ -11,10 +11,12 @@ import "./IBridge.sol"; import "./Messages.sol"; import "../libraries/DelegateCallAware.sol"; +import {L1MessageType_batchPostingReport} from "../libraries/MessageTypes.sol"; + /** * @title Staging ground for incoming and outgoing messages - * @notice Holds the inbox accumulator for delayed messages, and is the ETH escrow - * for value sent with these messages. + * @notice Holds the inbox accumulator for sequenced and delayed messages. + * It is also the ETH escrow for value sent with these messages. * Since the escrow is held here, this contract also contains a list of allowed * outboxes that can make calls from here and withdraw this escrow. */ @@ -26,16 +28,21 @@ contract Bridge is OwnableUpgradeable, DelegateCallAware, IBridge { bool allowed; } - mapping(address => InOutInfo) private allowedInboxesMap; + mapping(address => InOutInfo) private allowedDelayedInboxesMap; mapping(address => InOutInfo) private allowedOutboxesMap; - address[] public allowedInboxList; + address[] public allowedDelayedInboxList; address[] public allowedOutboxList; address private _activeOutbox; /// @dev Accumulator for delayed inbox messages; tail represents hash of the current state; each element represents the inclusion of a new message. - bytes32[] public override inboxAccs; + bytes32[] public override delayedInboxAccs; + + /// @dev Accumulator for sequencer inbox messages; tail represents hash of the current state; each element represents the inclusion of a new message. + bytes32[] public override sequencerInboxAccs; + + address public sequencerInbox; address private constant EMPTY_ACTIVEOUTBOX = address(type(uint160).max); @@ -55,14 +62,64 @@ contract Bridge is OwnableUpgradeable, DelegateCallAware, IBridge { return outbox; } - function allowedInboxes(address inbox) external view override returns (bool) { - return allowedInboxesMap[inbox].allowed; + function allowedDelayedInboxes(address inbox) external view override returns (bool) { + return allowedDelayedInboxesMap[inbox].allowed; } function allowedOutboxes(address outbox) external view override returns (bool) { return allowedOutboxesMap[outbox].allowed; } + modifier onlySequencerInbox() { + if (msg.sender != sequencerInbox) revert NotSequencerInbox(msg.sender); + _; + } + + function enqueueSequencerMessage(bytes32 dataHash, uint256 afterDelayedMessagesRead) + external + override + onlySequencerInbox + returns ( + uint256 seqMessageIndex, + bytes32 beforeAcc, + bytes32 delayedAcc, + bytes32 acc + ) + { + seqMessageIndex = sequencerInboxAccs.length; + if (sequencerInboxAccs.length > 0) { + beforeAcc = sequencerInboxAccs[sequencerInboxAccs.length - 1]; + } + if (afterDelayedMessagesRead > 0) { + delayedAcc = delayedInboxAccs[afterDelayedMessagesRead - 1]; + } + acc = keccak256(abi.encodePacked(beforeAcc, dataHash, delayedAcc)); + sequencerInboxAccs.push(acc); + } + + /** + * @dev allows the sequencer inbox to submit a delayed message of the batchPostingReport type + * This is done through a separate function entrypoint instead of allowing the sequencer inbox + * to call `enqueueDelayedMessage` to avoid the gas overhead of an extra SLOAD in either + * every delayed inbox or every sequencer inbox call. + */ + function submitBatchSpendingReport(address sender, bytes32 messageDataHash) + external + override + onlySequencerInbox + returns (uint256) + { + return + addMessageToDelayedAccumulator( + L1MessageType_batchPostingReport, + sender, + uint64(block.number), + uint64(block.timestamp), // solhint-disable-line not-rely-on-time, + block.basefee, + messageDataHash + ); + } + /** * @dev Enqueue a message in the delayed inbox accumulator. * These messages are later sequenced in the SequencerInbox, either by the sequencer as @@ -73,9 +130,9 @@ contract Bridge is OwnableUpgradeable, DelegateCallAware, IBridge { address sender, bytes32 messageDataHash ) external payable override returns (uint256) { - if (!allowedInboxesMap[msg.sender].allowed) revert NotInbox(msg.sender); + if (!allowedDelayedInboxesMap[msg.sender].allowed) revert NotDelayedInbox(msg.sender); return - addMessageToAccumulator( + addMessageToDelayedAccumulator( kind, sender, uint64(block.number), @@ -85,7 +142,7 @@ contract Bridge is OwnableUpgradeable, DelegateCallAware, IBridge { ); } - function addMessageToAccumulator( + function addMessageToDelayedAccumulator( uint8 kind, address sender, uint64 blockNumber, @@ -93,7 +150,7 @@ contract Bridge is OwnableUpgradeable, DelegateCallAware, IBridge { uint256 baseFeeL1, bytes32 messageDataHash ) internal returns (uint256) { - uint256 count = inboxAccs.length; + uint256 count = delayedInboxAccs.length; bytes32 messageHash = Messages.messageHash( kind, sender, @@ -105,9 +162,9 @@ contract Bridge is OwnableUpgradeable, DelegateCallAware, IBridge { ); bytes32 prevAcc = 0; if (count > 0) { - prevAcc = inboxAccs[count - 1]; + prevAcc = delayedInboxAccs[count - 1]; } - inboxAccs.push(Messages.accumulateInboxMessage(prevAcc, messageHash)); + delayedInboxAccs.push(Messages.accumulateInboxMessage(prevAcc, messageHash)); emit MessageDelivered( count, prevAcc, @@ -140,21 +197,28 @@ contract Bridge is OwnableUpgradeable, DelegateCallAware, IBridge { emit BridgeCallTriggered(msg.sender, to, value, data); } - function setInbox(address inbox, bool enabled) external override onlyOwner { - InOutInfo storage info = allowedInboxesMap[inbox]; + function setSequencerInbox(address _sequencerInbox) external override onlyOwner { + sequencerInbox = _sequencerInbox; + emit SequencerInboxUpdated(_sequencerInbox); + } + + function setDelayedInbox(address inbox, bool enabled) external override onlyOwner { + InOutInfo storage info = allowedDelayedInboxesMap[inbox]; bool alreadyEnabled = info.allowed; emit InboxToggle(inbox, enabled); if ((alreadyEnabled && enabled) || (!alreadyEnabled && !enabled)) { return; } if (enabled) { - allowedInboxesMap[inbox] = InOutInfo(allowedInboxList.length, true); - allowedInboxList.push(inbox); + allowedDelayedInboxesMap[inbox] = InOutInfo(allowedDelayedInboxList.length, true); + allowedDelayedInboxList.push(inbox); } else { - allowedInboxList[info.index] = allowedInboxList[allowedInboxList.length - 1]; - allowedInboxesMap[allowedInboxList[info.index]].index = info.index; - allowedInboxList.pop(); - delete allowedInboxesMap[inbox]; + allowedDelayedInboxList[info.index] = allowedDelayedInboxList[ + allowedDelayedInboxList.length - 1 + ]; + allowedDelayedInboxesMap[allowedDelayedInboxList[info.index]].index = info.index; + allowedDelayedInboxList.pop(); + delete allowedDelayedInboxesMap[inbox]; } } @@ -178,7 +242,11 @@ contract Bridge is OwnableUpgradeable, DelegateCallAware, IBridge { } } - function messageCount() external view override returns (uint256) { - return inboxAccs.length; + function delayedMessageCount() external view override returns (uint256) { + return delayedInboxAccs.length; + } + + function sequencerMessageCount() external view override returns (uint256) { + return sequencerInboxAccs.length; } } diff --git a/contracts/src/bridge/IBridge.sol b/contracts/src/bridge/IBridge.sol index e3e01019d3a..1b795bfed56 100644 --- a/contracts/src/bridge/IBridge.sol +++ b/contracts/src/bridge/IBridge.sol @@ -8,7 +8,11 @@ import {NotContract} from "../libraries/Error.sol"; /// @dev Thrown when an un-authorized address tries to access an only-inbox function /// @param sender The un-authorized sender -error NotInbox(address sender); +error NotDelayedInbox(address sender); + +/// @dev Thrown when an un-authorized address tries to access an only-sequencer-inbox function +/// @param sender The un-authorized sender +error NotSequencerInbox(address sender); /// @dev Thrown when an un-authorized address tries to access an only-outbox function /// @param sender The un-authorized sender @@ -41,12 +45,27 @@ interface IBridge { event OutboxToggle(address indexed outbox, bool enabled); + event SequencerInboxUpdated(address newSequencerInbox); + function enqueueDelayedMessage( uint8 kind, address sender, bytes32 messageDataHash ) external payable returns (uint256); + function enqueueSequencerMessage(bytes32 dataHash, uint256 afterDelayedMessagesRead) + external + returns ( + uint256 seqMessageIndex, + bytes32 beforeAcc, + bytes32 delayedAcc, + bytes32 acc + ); + + function submitBatchSpendingReport(address batchPoster, bytes32 dataHash) + external + returns (uint256 msgNum); + function executeCall( address to, uint256 value, @@ -54,19 +73,27 @@ interface IBridge { ) external returns (bool success, bytes memory returnData); // These are only callable by the admin - function setInbox(address inbox, bool enabled) external; + function setDelayedInbox(address inbox, bool enabled) external; function setOutbox(address inbox, bool enabled) external; + function setSequencerInbox(address _sequencerInbox) external; + // View functions + function sequencerInbox() external view returns (address); + function activeOutbox() external view returns (address); - function allowedInboxes(address inbox) external view returns (bool); + function allowedDelayedInboxes(address inbox) external view returns (bool); function allowedOutboxes(address outbox) external view returns (bool); - function inboxAccs(uint256 index) external view returns (bytes32); + function delayedInboxAccs(uint256 index) external view returns (bytes32); + + function sequencerInboxAccs(uint256 index) external view returns (bytes32); + + function delayedMessageCount() external view returns (uint256); - function messageCount() external view returns (uint256); + function sequencerMessageCount() external view returns (uint256); } diff --git a/contracts/src/bridge/IMessageProvider.sol b/contracts/src/bridge/IDelayedMessageProvider.sol similarity index 52% rename from contracts/src/bridge/IMessageProvider.sol rename to contracts/src/bridge/IDelayedMessageProvider.sol index afdfe8e3060..7c6ef8ebc56 100644 --- a/contracts/src/bridge/IMessageProvider.sol +++ b/contracts/src/bridge/IDelayedMessageProvider.sol @@ -4,8 +4,11 @@ pragma solidity ^0.8.0; -interface IMessageProvider { +interface IDelayedMessageProvider { + /// @dev event emitted when a inbox message is added to the Bridge's delayed accumulator event InboxMessageDelivered(uint256 indexed messageNum, bytes data); + /// @dev event emitted when a inbox message is added to the Bridge's delayed accumulator + /// same as InboxMessageDelivered but the batch data is available in tx.input event InboxMessageDeliveredFromOrigin(uint256 indexed messageNum); } diff --git a/contracts/src/bridge/IInbox.sol b/contracts/src/bridge/IInbox.sol index 1063ff3e3b8..103a84507f1 100644 --- a/contracts/src/bridge/IInbox.sol +++ b/contracts/src/bridge/IInbox.sol @@ -5,7 +5,7 @@ pragma solidity ^0.8.4; import "./IBridge.sol"; -import "./IMessageProvider.sol"; +import "./IDelayedMessageProvider.sol"; import {AlreadyInit, NotOrigin, DataTooLarge} from "../libraries/Error.sol"; /// @dev The contract is paused, so cannot be paused @@ -41,7 +41,7 @@ error RetryableData( bytes data ); -interface IInbox is IMessageProvider { +interface IInbox is IDelayedMessageProvider { function sendL2Message(bytes calldata messageData) external returns (uint256); function sendUnsignedTransaction( @@ -76,12 +76,6 @@ interface IInbox is IMessageProvider { bytes calldata data ) external payable returns (uint256); - function submitBatchSpendingReportTransaction( - address batchPosterAddr, - bytes32 dataHash, - uint256 batchNumber - ) external returns (uint256); - /// @dev Gas limit and maxFeePerGas should not be set to 1 as that is used to trigger the RetryableData error function createRetryableTicket( address to, diff --git a/contracts/src/bridge/ISequencerInbox.sol b/contracts/src/bridge/ISequencerInbox.sol index 206fa22ba1e..f0ba22ae370 100644 --- a/contracts/src/bridge/ISequencerInbox.sol +++ b/contracts/src/bridge/ISequencerInbox.sol @@ -6,8 +6,9 @@ pragma solidity ^0.8.0; import "../libraries/IGasRefunder.sol"; import {AlreadyInit, HadZeroInit, NotOrigin, DataTooLarge, NotRollup} from "../libraries/Error.sol"; +import "./IDelayedMessageProvider.sol"; -interface ISequencerInbox { +interface ISequencerInbox is IDelayedMessageProvider { struct MaxTimeVariation { uint256 delayBlocks; uint256 futureBlocks; @@ -55,9 +56,6 @@ interface ISequencerInbox { /// @dev Thrown when someone attempts to read more messages than exist error DelayedTooFar(); - /// @dev Thrown if the length of the header plus the length of the batch overflows - error DataLengthOverflow(); - /// @dev Force include can only read messages more blocks old than the delay period error ForceIncludeBlockTooSoon(); @@ -71,7 +69,7 @@ interface ISequencerInbox { error NotBatchPoster(); /// @dev The sequence number provided to this message was inconsistent with the number of batches already included - error BadSequencerNumber(); + error BadSequencerNumber(uint256 stored, uint256 received); /// @dev The batch data has the inbox authenticated bit set, but the batch data was not authenticated by the inbox error DataNotAuthenticated(); diff --git a/contracts/src/bridge/Inbox.sol b/contracts/src/bridge/Inbox.sol index 3afde1b7321..0f624a95583 100644 --- a/contracts/src/bridge/Inbox.sol +++ b/contracts/src/bridge/Inbox.sol @@ -16,7 +16,6 @@ import { L1MessageType_L2FundedByL1, L1MessageType_submitRetryableTx, L1MessageType_ethDeposit, - L1MessageType_batchPostingReport, L2MessageType_unsignedEOATx, L2MessageType_unsignedContractTx } from "../libraries/MessageTypes.sol"; @@ -236,26 +235,6 @@ contract Inbox is DelegateCallAware, PausableUpgradeable, IInbox { ); } - function submitBatchSpendingReportTransaction( - address batchPosterAddr, - bytes32 dataHash, - uint256 batchNumber - ) external virtual override whenNotPaused returns (uint256) { - require(ISequencerInbox(msg.sender) == sequencerInbox, "unauthorized"); - return - _deliverMessage( - L1MessageType_batchPostingReport, - batchPosterAddr, - abi.encodePacked( - block.timestamp, - batchPosterAddr, - dataHash, - batchNumber, - block.basefee - ) - ); - } - /** * @notice Get the L1 fee for submitting a retryable * @dev This fee can be paid by funds already in the L2 aliased address or by the current message value @@ -276,24 +255,23 @@ contract Inbox is DelegateCallAware, PausableUpgradeable, IInbox { /// Look into retryable tickets if you are interested in this functionality. /// @dev this function should not be called inside contract constructors function depositEth() public payable override whenNotPaused onlyAllowed returns (uint256) { - address sender = msg.sender; + address dest = msg.sender; // solhint-disable-next-line avoid-tx-origin - if (!AddressUpgradeable.isContract(sender) && tx.origin == msg.sender) { + if (AddressUpgradeable.isContract(msg.sender) || tx.origin != msg.sender) { // isContract check fails if this function is called during a contract's constructor. // We don't adjust the address for calls coming from L1 contracts since their addresses get remapped // If the caller is an EOA, we adjust the address. // This is needed because unsigned messages to the L2 (such as retryables) // have the L1 sender address mapped. - // Here we preemptively reverse the mapping for EOAs so deposits work as expected - sender = AddressAliasHelper.undoL1ToL2Alias(sender); + dest = AddressAliasHelper.applyL1ToL2Alias(msg.sender); } return _deliverMessage( L1MessageType_ethDeposit, - sender, // arb-os will add the alias to this value - abi.encodePacked(msg.value) + msg.sender, + abi.encodePacked(dest, msg.value) ); } @@ -472,7 +450,11 @@ contract Inbox is DelegateCallAware, PausableUpgradeable, IInbox { ) internal returns (uint256) { if (_messageData.length > MAX_DATA_SIZE) revert DataTooLarge(_messageData.length, MAX_DATA_SIZE); - uint256 msgNum = deliverToBridge(_kind, _sender, keccak256(_messageData)); + uint256 msgNum = deliverToBridge( + _kind, + AddressAliasHelper.applyL1ToL2Alias(_sender), + keccak256(_messageData) + ); emit InboxMessageDelivered(msgNum, _messageData); return msgNum; } diff --git a/contracts/src/bridge/SequencerInbox.sol b/contracts/src/bridge/SequencerInbox.sol index 7128a7c3792..2044e11eb41 100644 --- a/contracts/src/bridge/SequencerInbox.sol +++ b/contracts/src/bridge/SequencerInbox.sol @@ -23,11 +23,9 @@ import {MAX_DATA_SIZE} from "../libraries/Constants.sol"; * sequencer within a time limit they can be force included into the rollup inbox by anyone. */ contract SequencerInbox is DelegateCallAware, GasRefundEnabled, ISequencerInbox { - bytes32[] public override inboxAccs; uint256 public totalDelayedMessagesRead; - IBridge public delayedBridge; - IInbox public delayedInbox; + IBridge public bridge; /// @dev The size of the batch header uint256 public constant HEADER_LENGTH = 40; @@ -39,8 +37,11 @@ contract SequencerInbox is DelegateCallAware, GasRefundEnabled, ISequencerInbox mapping(address => bool) public isBatchPoster; ISequencerInbox.MaxTimeVariation public maxTimeVariation; - mapping(bytes32 => bool) public isValidKeysetHash; - mapping(bytes32 => uint256) public keysetHashCreationBlock; + struct DasKeySetInfo { + bool isValidKeyset; + uint64 creationBlock; + } + mapping(bytes32 => DasKeySetInfo) public dasKeySetInfo; modifier onlyRollupOwner() { if (msg.sender != IRollupUserAbs(rollup).owner()) revert NotOwner(msg.sender, rollup); @@ -48,15 +49,13 @@ contract SequencerInbox is DelegateCallAware, GasRefundEnabled, ISequencerInbox } function initialize( - IBridge delayedBridge_, - IInbox delayedInbox_, + IBridge bridge_, address rollup_, ISequencerInbox.MaxTimeVariation calldata maxTimeVariation_ ) external onlyDelegated { - if (delayedBridge != IBridge(address(0))) revert AlreadyInit(); - if (delayedBridge_ == IBridge(address(0))) revert HadZeroInit(); - delayedBridge = delayedBridge_; - delayedInbox = delayedInbox_; + if (bridge != IBridge(address(0))) revert AlreadyInit(); + if (bridge_ == IBridge(address(0))) revert HadZeroInit(); + bridge = bridge_; rollup = rollup_; maxTimeVariation = maxTimeVariation_; } @@ -112,22 +111,24 @@ contract SequencerInbox is DelegateCallAware, GasRefundEnabled, ISequencerInbox // Verify that message hash represents the last message sequence of delayed message to be included bytes32 prevDelayedAcc = 0; if (_totalDelayedMessagesRead > 1) { - prevDelayedAcc = delayedBridge.inboxAccs(_totalDelayedMessagesRead - 2); + prevDelayedAcc = bridge.delayedInboxAccs(_totalDelayedMessagesRead - 2); } if ( - delayedBridge.inboxAccs(_totalDelayedMessagesRead - 1) != + bridge.delayedInboxAccs(_totalDelayedMessagesRead - 1) != Messages.accumulateInboxMessage(prevDelayedAcc, messageHash) ) revert IncorrectMessagePreimage(); (bytes32 dataHash, TimeBounds memory timeBounds) = formEmptyDataHash( _totalDelayedMessagesRead ); - (bytes32 beforeAcc, bytes32 delayedAcc, bytes32 afterAcc) = addSequencerL2BatchImpl( - dataHash, - _totalDelayedMessagesRead - ); + ( + uint256 seqMessageIndex, + bytes32 beforeAcc, + bytes32 delayedAcc, + bytes32 afterAcc + ) = addSequencerL2BatchImpl(dataHash, _totalDelayedMessagesRead, 0); emit SequencerBatchDelivered( - inboxAccs.length - 1, + seqMessageIndex, beforeAcc, afterAcc, delayedAcc, @@ -146,20 +147,19 @@ contract SequencerInbox is DelegateCallAware, GasRefundEnabled, ISequencerInbox // solhint-disable-next-line avoid-tx-origin if (msg.sender != tx.origin) revert NotOrigin(); if (!isBatchPoster[msg.sender]) revert NotBatchPoster(); - if (inboxAccs.length != sequenceNumber) revert BadSequencerNumber(); (bytes32 dataHash, TimeBounds memory timeBounds) = formDataHash( data, afterDelayedMessagesRead ); - (bytes32 beforeAcc, bytes32 delayedAcc, bytes32 afterAcc) = addSequencerL2BatchImpl( - dataHash, - afterDelayedMessagesRead - ); - if (data.length > 0) { - _reportBatchSpending(dataHash, sequenceNumber); - } + ( + uint256 seqMessageIndex, + bytes32 beforeAcc, + bytes32 delayedAcc, + bytes32 afterAcc + ) = addSequencerL2BatchImpl(dataHash, afterDelayedMessagesRead, data.length); + if (seqMessageIndex != sequenceNumber) revert BadSequencerNumber(seqMessageIndex, sequenceNumber); emit SequencerBatchDelivered( - inboxAccs.length - 1, + sequenceNumber, beforeAcc, afterAcc, delayedAcc, @@ -176,16 +176,20 @@ contract SequencerInbox is DelegateCallAware, GasRefundEnabled, ISequencerInbox IGasRefunder gasRefunder ) external override refundsGas(gasRefunder) { if (!isBatchPoster[msg.sender] && msg.sender != rollup) revert NotBatchPoster(); - if (inboxAccs.length != sequenceNumber) revert BadSequencerNumber(); (bytes32 dataHash, TimeBounds memory timeBounds) = formDataHash( data, afterDelayedMessagesRead ); - (bytes32 beforeAcc, bytes32 delayedAcc, bytes32 afterAcc) = addSequencerL2BatchImpl( - dataHash, - afterDelayedMessagesRead - ); + // we set the calldata length posted to 0 here since the caller isn't the origin + // of the tx, so they might have not paid tx input cost for the calldata + ( + uint256 seqMessageIndex, + bytes32 beforeAcc, + bytes32 delayedAcc, + bytes32 afterAcc + ) = addSequencerL2BatchImpl(dataHash, afterDelayedMessagesRead, 0); + if (seqMessageIndex != sequenceNumber) revert BadSequencerNumber(seqMessageIndex, sequenceNumber); emit SequencerBatchDelivered( sequenceNumber, beforeAcc, @@ -198,19 +202,20 @@ contract SequencerInbox is DelegateCallAware, GasRefundEnabled, ISequencerInbox emit SequencerBatchData(sequenceNumber, data); } - function _reportBatchSpending(bytes32 dataHash, uint256 sequenceNumber) internal { - delayedInbox.submitBatchSpendingReportTransaction(msg.sender, dataHash, sequenceNumber); - } - - function dasKeysetHashFromBatchData(bytes memory data) internal pure returns (bytes32) { - if (data.length < 33 || data[0] & 0x80 == 0) { - return bytes32(0); + modifier validateBatchData(bytes calldata data) { + uint256 fullDataLen = HEADER_LENGTH + data.length; + if (fullDataLen > MAX_DATA_SIZE) revert DataTooLarge(fullDataLen, MAX_DATA_SIZE); + if (data.length > 0 && (data[0] & DATA_AUTHENTICATED_FLAG) == DATA_AUTHENTICATED_FLAG) { + revert DataNotAuthenticated(); } - bytes32 temp; - assembly { - temp := mload(add(data, 33)) + // the first byte is used to identify the type of batch data + // das batches expect to have the type byte set, followed by the keyset (so they should have at least 33 bytes) + if (data.length >= 33 && data[0] & 0x80 != 0) { + // we skip the first byte, then read the next 32 bytes for the keyset + bytes32 dasKeysetHash = bytes32(data[1:33]); + if (!dasKeySetInfo[dasKeysetHash].isValidKeyset) revert NoSuchKeyset(dasKeysetHash); } - return temp; + _; } function packHeader(uint256 afterDelayedMessagesRead) @@ -234,29 +239,12 @@ contract SequencerInbox is DelegateCallAware, GasRefundEnabled, ISequencerInbox function formDataHash(bytes calldata data, uint256 afterDelayedMessagesRead) internal view + validateBatchData(data) returns (bytes32, TimeBounds memory) { - bytes32 dasKeysetHash = dasKeysetHashFromBatchData(data); - if (dasKeysetHash != bytes32(0)) { - if (!isValidKeysetHash[dasKeysetHash]) revert NoSuchKeyset(dasKeysetHash); - } - uint256 fullDataLen = HEADER_LENGTH + data.length; - if (fullDataLen < HEADER_LENGTH) revert DataLengthOverflow(); - if (fullDataLen > MAX_DATA_SIZE) revert DataTooLarge(fullDataLen, MAX_DATA_SIZE); - bytes memory fullData = new bytes(fullDataLen); (bytes memory header, TimeBounds memory timeBounds) = packHeader(afterDelayedMessagesRead); - - for (uint256 i = 0; i < HEADER_LENGTH; i++) { - fullData[i] = header[i]; - } - if (data.length > 0 && (data[0] & DATA_AUTHENTICATED_FLAG) == DATA_AUTHENTICATED_FLAG) { - revert DataNotAuthenticated(); - } - // copy data into fullData at offset of HEADER_LENGTH (the extra 32 offset is because solidity puts the array len first) - assembly { - calldatacopy(add(fullData, add(HEADER_LENGTH, 32)), data.offset, data.length) - } - return (keccak256(fullData), timeBounds); + bytes32 dataHash = keccak256(bytes.concat(header, data)); + return (dataHash, timeBounds); } function formEmptyDataHash(uint256 afterDelayedMessagesRead) @@ -268,31 +256,55 @@ contract SequencerInbox is DelegateCallAware, GasRefundEnabled, ISequencerInbox return (keccak256(header), timeBounds); } - function addSequencerL2BatchImpl(bytes32 dataHash, uint256 afterDelayedMessagesRead) + function addSequencerL2BatchImpl( + bytes32 dataHash, + uint256 afterDelayedMessagesRead, + uint256 calldataLengthPosted + ) internal returns ( + uint256 seqMessageIndex, bytes32 beforeAcc, bytes32 delayedAcc, bytes32 acc ) { if (afterDelayedMessagesRead < totalDelayedMessagesRead) revert DelayedBackwards(); - if (afterDelayedMessagesRead > delayedBridge.messageCount()) revert DelayedTooFar(); + if (afterDelayedMessagesRead > bridge.delayedMessageCount()) revert DelayedTooFar(); - if (inboxAccs.length > 0) { - beforeAcc = inboxAccs[inboxAccs.length - 1]; - } - if (afterDelayedMessagesRead > 0) { - delayedAcc = delayedBridge.inboxAccs(afterDelayedMessagesRead - 1); - } + (seqMessageIndex, beforeAcc, delayedAcc, acc) = bridge.enqueueSequencerMessage( + dataHash, + afterDelayedMessagesRead + ); - acc = keccak256(abi.encodePacked(beforeAcc, dataHash, delayedAcc)); - inboxAccs.push(acc); totalDelayedMessagesRead = afterDelayedMessagesRead; + + if (calldataLengthPosted > 0) { + // this msg isn't included in the current sequencer batch, but instead added to + // the delayed messages queue that is yet to be included + address batchPoster = msg.sender; + bytes memory spendingReportMsg = abi.encodePacked( + block.timestamp, + batchPoster, + dataHash, + seqMessageIndex, + block.basefee + ); + uint256 msgNum = bridge.submitBatchSpendingReport( + batchPoster, + keccak256(spendingReportMsg) + ); + // this is the same event used by Inbox.sol after including a message to the delayed message accumulator + emit InboxMessageDelivered(msgNum, spendingReportMsg); + } + } + + function inboxAccs(uint256 index) external view override returns (bytes32) { + return bridge.sequencerInboxAccs(index); } function batchCount() external view override returns (uint256) { - return inboxAccs.length; + return bridge.sequencerMessageCount(); } /** @@ -301,6 +313,7 @@ contract SequencerInbox is DelegateCallAware, GasRefundEnabled, ISequencerInbox */ function setMaxTimeVariation(ISequencerInbox.MaxTimeVariation memory maxTimeVariation_) external + override onlyRollupOwner { maxTimeVariation = maxTimeVariation_; @@ -312,7 +325,7 @@ contract SequencerInbox is DelegateCallAware, GasRefundEnabled, ISequencerInbox * @param addr the address * @param isBatchPoster_ if the specified address should be authorized as a batch poster */ - function setIsBatchPoster(address addr, bool isBatchPoster_) external onlyRollupOwner { + function setIsBatchPoster(address addr, bool isBatchPoster_) external override onlyRollupOwner { isBatchPoster[addr] = isBatchPoster_; emit OwnerFunctionCalled(1); } @@ -321,11 +334,13 @@ contract SequencerInbox is DelegateCallAware, GasRefundEnabled, ISequencerInbox * @notice Makes Data Availability Service keyset valid * @param keysetBytes bytes of the serialized keyset */ - function setValidKeyset(bytes calldata keysetBytes) external onlyRollupOwner { + function setValidKeyset(bytes calldata keysetBytes) external override onlyRollupOwner { bytes32 ksHash = keccak256(keysetBytes); - if (isValidKeysetHash[ksHash]) revert AlreadyValidDASKeyset(ksHash); - isValidKeysetHash[ksHash] = true; - keysetHashCreationBlock[ksHash] = block.number; + if (dasKeySetInfo[ksHash].isValidKeyset) revert AlreadyValidDASKeyset(ksHash); + dasKeySetInfo[ksHash] = DasKeySetInfo({ + isValidKeyset: true, + creationBlock: uint64(block.number) + }); emit SetValidKeyset(ksHash, keysetBytes); emit OwnerFunctionCalled(2); } @@ -334,16 +349,24 @@ contract SequencerInbox is DelegateCallAware, GasRefundEnabled, ISequencerInbox * @notice Invalidates a Data Availability Service keyset * @param ksHash hash of the keyset */ - function invalidateKeysetHash(bytes32 ksHash) external onlyRollupOwner { - if (!isValidKeysetHash[ksHash]) revert NoSuchKeyset(ksHash); - isValidKeysetHash[ksHash] = false; + function invalidateKeysetHash(bytes32 ksHash) external override onlyRollupOwner { + if (!dasKeySetInfo[ksHash].isValidKeyset) revert NoSuchKeyset(ksHash); + // we don't delete the block creation value since its used to fetch the SetValidKeyset + // event efficiently. The event provides the hash preimage of the key. + // this is still needed when syncing the chain after a keyset is invalidated. + dasKeySetInfo[ksHash].isValidKeyset = false; emit InvalidateKeyset(ksHash); emit OwnerFunctionCalled(3); } + function isValidKeysetHash(bytes32 ksHash) external view returns (bool) { + return dasKeySetInfo[ksHash].isValidKeyset; + } + + /// @notice the creation block is intended to still be available after a keyset is deleted function getKeysetCreationBlock(bytes32 ksHash) external view returns (uint256) { - uint256 bnum = keysetHashCreationBlock[ksHash]; - if (bnum == 0) revert NoSuchKeyset(ksHash); - return bnum; + DasKeySetInfo memory ksInfo = dasKeySetInfo[ksHash]; + if (ksInfo.creationBlock == 0) revert NoSuchKeyset(ksHash); + return uint256(ksInfo.creationBlock); } } diff --git a/contracts/src/challenge/ChallengeManager.sol b/contracts/src/challenge/ChallengeManager.sol index cc2ef895084..e89214ad37b 100644 --- a/contracts/src/challenge/ChallengeManager.sol +++ b/contracts/src/challenge/ChallengeManager.sol @@ -33,7 +33,7 @@ contract ChallengeManager is DelegateCallAware, IChallengeManager { IChallengeResultReceiver public resultReceiver; ISequencerInbox public sequencerInbox; - IBridge public delayedBridge; + IBridge public bridge; IOneStepProofEntry public osp; function challengeInfo(uint64 challengeIndex) @@ -99,14 +99,14 @@ contract ChallengeManager is DelegateCallAware, IChallengeManager { function initialize( IChallengeResultReceiver resultReceiver_, ISequencerInbox sequencerInbox_, - IBridge delayedBridge_, + IBridge bridge_, IOneStepProofEntry osp_ ) external override onlyDelegated { require(address(resultReceiver) == address(0), "ALREADY_INIT"); require(address(resultReceiver_) != address(0), "NO_RESULT_RECEIVER"); resultReceiver = resultReceiver_; sequencerInbox = sequencerInbox_; - delayedBridge = delayedBridge_; + bridge = bridge_; osp = osp_; } @@ -254,11 +254,7 @@ contract ChallengeManager is DelegateCallAware, IChallengeManager { } bytes32 afterHash = osp.proveOneStep( - ExecutionContext({ - maxInboxMessagesRead: challenge.maxInboxMessages, - sequencerInbox: sequencerInbox, - delayedBridge: delayedBridge - }), + ExecutionContext({maxInboxMessagesRead: challenge.maxInboxMessages, bridge: bridge}), challengeStart, selection.oldSegments[selection.challengePosition], proof diff --git a/contracts/src/challenge/IChallengeManager.sol b/contracts/src/challenge/IChallengeManager.sol index efa62fcf453..0f033cb4b5d 100644 --- a/contracts/src/challenge/IChallengeManager.sol +++ b/contracts/src/challenge/IChallengeManager.sol @@ -43,7 +43,7 @@ interface IChallengeManager { function initialize( IChallengeResultReceiver resultReceiver_, ISequencerInbox sequencerInbox_, - IBridge delayedBridge_, + IBridge bridge_, IOneStepProofEntry osp_ ) external; diff --git a/contracts/src/mocks/BridgeStub.sol b/contracts/src/mocks/BridgeStub.sol index be3fd654352..6d9426bcb6b 100644 --- a/contracts/src/mocks/BridgeStub.sol +++ b/contracts/src/mocks/BridgeStub.sol @@ -14,19 +14,28 @@ contract BridgeStub is IBridge { bool allowed; } - mapping(address => InOutInfo) private allowedInboxesMap; + mapping(address => InOutInfo) private allowedDelayedInboxesMap; //mapping(address => InOutInfo) private allowedOutboxesMap; - address[] public allowedInboxList; + address[] public allowedDelayedInboxList; address[] public allowedOutboxList; address public override activeOutbox; // Accumulator for delayed inbox; tail represents hash of the current state; each element represents the inclusion of a new message. - bytes32[] public override inboxAccs; + bytes32[] public override delayedInboxAccs; - function allowedInboxes(address inbox) external view override returns (bool) { - return allowedInboxesMap[inbox].allowed; + bytes32[] public override sequencerInboxAccs; + + address public sequencerInbox; + + function setSequencerInbox(address _sequencerInbox) external override { + sequencerInbox = _sequencerInbox; + emit SequencerInboxUpdated(_sequencerInbox); + } + + function allowedDelayedInboxes(address inbox) external view override returns (bool) { + return allowedDelayedInboxesMap[inbox].allowed; } function allowedOutboxes(address) external pure override returns (bool) { @@ -38,9 +47,9 @@ contract BridgeStub is IBridge { address sender, bytes32 messageDataHash ) external payable override returns (uint256) { - require(allowedInboxesMap[msg.sender].allowed, "NOT_FROM_INBOX"); + require(allowedDelayedInboxesMap[msg.sender].allowed, "NOT_FROM_INBOX"); return - addMessageToAccumulator( + addMessageToDelayedAccumulator( kind, sender, block.number, @@ -50,7 +59,34 @@ contract BridgeStub is IBridge { ); } - function addMessageToAccumulator( + function enqueueSequencerMessage(bytes32 dataHash, uint256 afterDelayedMessagesRead) + external + returns ( + uint256 seqMessageIndex, + bytes32 beforeAcc, + bytes32 delayedAcc, + bytes32 acc + ) + { + seqMessageIndex = sequencerInboxAccs.length; + if (sequencerInboxAccs.length > 0) { + beforeAcc = sequencerInboxAccs[sequencerInboxAccs.length - 1]; + } + if (afterDelayedMessagesRead > 0) { + delayedAcc = delayedInboxAccs[afterDelayedMessagesRead - 1]; + } + acc = keccak256(abi.encodePacked(beforeAcc, dataHash, delayedAcc)); + sequencerInboxAccs.push(acc); + } + + function submitBatchSpendingReport(address batchPoster, bytes32 dataHash) + external + returns (uint256) + { + // TODO: implement stub + } + + function addMessageToDelayedAccumulator( uint8, address, uint256, @@ -58,7 +94,7 @@ contract BridgeStub is IBridge { uint256, bytes32 messageDataHash ) internal returns (uint256) { - uint256 count = inboxAccs.length; + uint256 count = delayedInboxAccs.length; bytes32 messageHash = Messages.messageHash( 0, address(uint160(0)), @@ -70,9 +106,9 @@ contract BridgeStub is IBridge { ); bytes32 prevAcc = 0; if (count > 0) { - prevAcc = inboxAccs[count - 1]; + prevAcc = delayedInboxAccs[count - 1]; } - inboxAccs.push(Messages.accumulateInboxMessage(prevAcc, messageHash)); + delayedInboxAccs.push(Messages.accumulateInboxMessage(prevAcc, messageHash)); return count; } @@ -84,21 +120,23 @@ contract BridgeStub is IBridge { revert("NOT_IMPLEMENTED"); } - function setInbox(address inbox, bool enabled) external override { - InOutInfo storage info = allowedInboxesMap[inbox]; + function setDelayedInbox(address inbox, bool enabled) external override { + InOutInfo storage info = allowedDelayedInboxesMap[inbox]; bool alreadyEnabled = info.allowed; emit InboxToggle(inbox, enabled); if ((alreadyEnabled && enabled) || (!alreadyEnabled && !enabled)) { return; } if (enabled) { - allowedInboxesMap[inbox] = InOutInfo(allowedInboxList.length, true); - allowedInboxList.push(inbox); + allowedDelayedInboxesMap[inbox] = InOutInfo(allowedDelayedInboxList.length, true); + allowedDelayedInboxList.push(inbox); } else { - allowedInboxList[info.index] = allowedInboxList[allowedInboxList.length - 1]; - allowedInboxesMap[allowedInboxList[info.index]].index = info.index; - allowedInboxList.pop(); - delete allowedInboxesMap[inbox]; + allowedDelayedInboxList[info.index] = allowedDelayedInboxList[ + allowedDelayedInboxList.length - 1 + ]; + allowedDelayedInboxesMap[allowedDelayedInboxList[info.index]].index = info.index; + allowedDelayedInboxList.pop(); + delete allowedDelayedInboxesMap[inbox]; } } @@ -109,7 +147,11 @@ contract BridgeStub is IBridge { revert("NOT_IMPLEMENTED"); } - function messageCount() external view override returns (uint256) { - return inboxAccs.length; + function delayedMessageCount() external view override returns (uint256) { + return delayedInboxAccs.length; + } + + function sequencerMessageCount() external view override returns (uint256) { + return sequencerInboxAccs.length; } } diff --git a/contracts/src/mocks/InboxStub.sol b/contracts/src/mocks/InboxStub.sol index de32569523f..c9677855c98 100644 --- a/contracts/src/mocks/InboxStub.sol +++ b/contracts/src/mocks/InboxStub.sol @@ -99,14 +99,6 @@ contract InboxStub is IInbox { revert("NOT_IMPLEMENTED"); } - function submitBatchSpendingReportTransaction( - address, - bytes32, - uint256 - ) external pure override returns (uint256) { - revert("NOT_IMPLEMENTED"); - } - function createRetryableTicket( address, uint256, diff --git a/contracts/src/mocks/SequencerInboxStub.sol b/contracts/src/mocks/SequencerInboxStub.sol index f11e3a1dd01..24d0003a4b0 100644 --- a/contracts/src/mocks/SequencerInboxStub.sol +++ b/contracts/src/mocks/SequencerInboxStub.sol @@ -8,11 +8,11 @@ import "../bridge/SequencerInbox.sol"; contract SequencerInboxStub is SequencerInbox { constructor( - IBridge delayedBridge_, + IBridge bridge_, address sequencer_, ISequencerInbox.MaxTimeVariation memory maxTimeVariation_ ) { - delayedBridge = delayedBridge_; + bridge = bridge_; rollup = msg.sender; maxTimeVariation = maxTimeVariation_; isBatchPoster[sequencer_] = true; @@ -20,12 +20,14 @@ contract SequencerInboxStub is SequencerInbox { function addInitMessage() external { (bytes32 dataHash, TimeBounds memory timeBounds) = formEmptyDataHash(0); - (bytes32 beforeAcc, bytes32 delayedAcc, bytes32 afterAcc) = addSequencerL2BatchImpl( - dataHash, - 0 - ); + ( + uint256 sequencerMessageCount, + bytes32 beforeAcc, + bytes32 delayedAcc, + bytes32 afterAcc + ) = addSequencerL2BatchImpl(dataHash, 0, 0); emit SequencerBatchDelivered( - inboxAccs.length - 1, + sequencerMessageCount, beforeAcc, afterAcc, delayedAcc, diff --git a/contracts/src/osp/IOneStepProver.sol b/contracts/src/osp/IOneStepProver.sol index 834af62c5d5..e26ff60429d 100644 --- a/contracts/src/osp/IOneStepProver.sol +++ b/contracts/src/osp/IOneStepProver.sol @@ -12,8 +12,7 @@ import "../bridge/IBridge.sol"; struct ExecutionContext { uint256 maxInboxMessagesRead; - ISequencerInbox sequencerInbox; - IBridge delayedBridge; + IBridge bridge; } abstract contract IOneStepProver { diff --git a/contracts/src/osp/OneStepProverHostIo.sol b/contracts/src/osp/OneStepProverHostIo.sol index 8d8b52f11bf..bbedf000e35 100644 --- a/contracts/src/osp/OneStepProverHostIo.sol +++ b/contracts/src/osp/OneStepProverHostIo.sol @@ -10,7 +10,6 @@ import "../state/Deserialize.sol"; import "./IOneStepProver.sol"; import "../bridge/Messages.sol"; import "../bridge/IBridge.sol"; -import "../bridge/ISequencerInbox.sol"; contract OneStepProverHostIo is IOneStepProver { using GlobalStateLib for GlobalState; @@ -165,13 +164,13 @@ contract OneStepProverHostIo is IOneStepProver { bytes32 delayedAcc; if (msgIndex > 0) { - beforeAcc = execCtx.sequencerInbox.inboxAccs(msgIndex - 1); + beforeAcc = execCtx.bridge.sequencerInboxAccs(msgIndex - 1); } if (afterDelayedMsg > 0) { - delayedAcc = execCtx.delayedBridge.inboxAccs(afterDelayedMsg - 1); + delayedAcc = execCtx.bridge.delayedInboxAccs(afterDelayedMsg - 1); } bytes32 acc = keccak256(abi.encodePacked(beforeAcc, messageHash, delayedAcc)); - require(acc == execCtx.sequencerInbox.inboxAccs(msgIndex), "BAD_SEQINBOX_MESSAGE"); + require(acc == execCtx.bridge.sequencerInboxAccs(msgIndex), "BAD_SEQINBOX_MESSAGE"); return true; } @@ -185,7 +184,7 @@ contract OneStepProverHostIo is IOneStepProver { bytes32 beforeAcc; if (msgIndex > 0) { - beforeAcc = execCtx.delayedBridge.inboxAccs(msgIndex - 1); + beforeAcc = execCtx.bridge.delayedInboxAccs(msgIndex - 1); } bytes32 messageDataHash = keccak256(message[DELAYED_HEADER_LEN:]); @@ -198,7 +197,7 @@ contract OneStepProverHostIo is IOneStepProver { ); bytes32 acc = Messages.accumulateInboxMessage(beforeAcc, messageHash); - require(acc == execCtx.delayedBridge.inboxAccs(msgIndex), "BAD_DELAYED_MESSAGE"); + require(acc == execCtx.bridge.delayedInboxAccs(msgIndex), "BAD_DELAYED_MESSAGE"); return true; } diff --git a/contracts/src/rollup/BridgeCreator.sol b/contracts/src/rollup/BridgeCreator.sol index 50c34d947e2..4c8f6f319c1 100644 --- a/contracts/src/rollup/BridgeCreator.sol +++ b/contracts/src/rollup/BridgeCreator.sol @@ -9,40 +9,40 @@ import "../bridge/SequencerInbox.sol"; import "../bridge/ISequencerInbox.sol"; import "../bridge/Inbox.sol"; import "../bridge/Outbox.sol"; -import "./RollupEventBridge.sol"; +import "./RollupEventInbox.sol"; import "../bridge/IBridge.sol"; import "@openzeppelin/contracts/access/Ownable.sol"; import "@openzeppelin/contracts/proxy/transparent/ProxyAdmin.sol"; contract BridgeCreator is Ownable { - Bridge public delayedBridgeTemplate; + Bridge public bridgeTemplate; SequencerInbox public sequencerInboxTemplate; Inbox public inboxTemplate; - RollupEventBridge public rollupEventBridgeTemplate; + RollupEventInbox public rollupEventInboxTemplate; Outbox public outboxTemplate; event TemplatesUpdated(); constructor() Ownable() { - delayedBridgeTemplate = new Bridge(); + bridgeTemplate = new Bridge(); sequencerInboxTemplate = new SequencerInbox(); inboxTemplate = new Inbox(); - rollupEventBridgeTemplate = new RollupEventBridge(); + rollupEventInboxTemplate = new RollupEventInbox(); outboxTemplate = new Outbox(); } function updateTemplates( - address _delayedBridgeTemplate, + address _bridgeTemplate, address _sequencerInboxTemplate, address _inboxTemplate, - address _rollupEventBridgeTemplate, + address _rollupEventInboxTemplate, address _outboxTemplate ) external onlyOwner { - delayedBridgeTemplate = Bridge(_delayedBridgeTemplate); + bridgeTemplate = Bridge(_bridgeTemplate); sequencerInboxTemplate = SequencerInbox(_sequencerInboxTemplate); inboxTemplate = Inbox(_inboxTemplate); - rollupEventBridgeTemplate = RollupEventBridge(_rollupEventBridgeTemplate); + rollupEventInboxTemplate = RollupEventInbox(_rollupEventInboxTemplate); outboxTemplate = Outbox(_outboxTemplate); emit TemplatesUpdated(); @@ -50,10 +50,10 @@ contract BridgeCreator is Ownable { struct CreateBridgeFrame { ProxyAdmin admin; - Bridge delayedBridge; + Bridge bridge; SequencerInbox sequencerInbox; Inbox inbox; - RollupEventBridge rollupEventBridge; + RollupEventInbox rollupEventInbox; Outbox outbox; } @@ -67,16 +67,14 @@ contract BridgeCreator is Ownable { Bridge, SequencerInbox, Inbox, - RollupEventBridge, + RollupEventInbox, Outbox ) { CreateBridgeFrame memory frame; { - frame.delayedBridge = Bridge( - address( - new TransparentUpgradeableProxy(address(delayedBridgeTemplate), adminProxy, "") - ) + frame.bridge = Bridge( + address(new TransparentUpgradeableProxy(address(bridgeTemplate), adminProxy, "")) ); frame.sequencerInbox = SequencerInbox( address( @@ -86,10 +84,10 @@ contract BridgeCreator is Ownable { frame.inbox = Inbox( address(new TransparentUpgradeableProxy(address(inboxTemplate), adminProxy, "")) ); - frame.rollupEventBridge = RollupEventBridge( + frame.rollupEventInbox = RollupEventInbox( address( new TransparentUpgradeableProxy( - address(rollupEventBridgeTemplate), + address(rollupEventInboxTemplate), adminProxy, "" ) @@ -100,25 +98,19 @@ contract BridgeCreator is Ownable { ); } - frame.delayedBridge.initialize(); - frame.sequencerInbox.initialize( - IBridge(frame.delayedBridge), - IInbox(frame.inbox), - rollup, - maxTimeVariation - ); - frame.inbox.initialize(IBridge(frame.delayedBridge), ISequencerInbox(frame.sequencerInbox)); - frame.rollupEventBridge.initialize(address(frame.delayedBridge), rollup); - frame.outbox.initialize(rollup, IBridge(frame.delayedBridge)); + frame.bridge.initialize(); + frame.sequencerInbox.initialize(IBridge(frame.bridge), rollup, maxTimeVariation); + frame.inbox.initialize(IBridge(frame.bridge), ISequencerInbox(frame.sequencerInbox)); + frame.rollupEventInbox.initialize(address(frame.bridge), rollup); + frame.outbox.initialize(rollup, IBridge(frame.bridge)); - frame.delayedBridge.setInbox(address(frame.inbox), true); - frame.delayedBridge.transferOwnership(rollup); + frame.bridge.transferOwnership(rollup); return ( - frame.delayedBridge, + frame.bridge, frame.sequencerInbox, frame.inbox, - frame.rollupEventBridge, + frame.rollupEventInbox, frame.outbox ); } diff --git a/contracts/src/rollup/IRollupCore.sol b/contracts/src/rollup/IRollupCore.sol index 8617aa4f778..62a3298f5d0 100644 --- a/contracts/src/rollup/IRollupCore.sol +++ b/contracts/src/rollup/IRollupCore.sol @@ -59,13 +59,13 @@ interface IRollupCore { function wasmModuleRoot() external view returns (bytes32); - function delayedBridge() external view returns (IBridge); + function bridge() external view returns (IBridge); - function sequencerBridge() external view returns (ISequencerInbox); + function sequencerInbox() external view returns (ISequencerInbox); function outbox() external view returns (IOutbox); - function rollupEventBridge() external view returns (IRollupEventBridge); + function rollupEventInbox() external view returns (IRollupEventInbox); function challengeManager() external view returns (IChallengeManager); diff --git a/contracts/src/rollup/IRollupEventBridge.sol b/contracts/src/rollup/IRollupEventInbox.sol similarity index 93% rename from contracts/src/rollup/IRollupEventBridge.sol rename to contracts/src/rollup/IRollupEventInbox.sol index 2af93e5b28a..e0018c902c4 100644 --- a/contracts/src/rollup/IRollupEventBridge.sol +++ b/contracts/src/rollup/IRollupEventInbox.sol @@ -6,7 +6,7 @@ pragma solidity ^0.8.0; import "../bridge/IBridge.sol"; -interface IRollupEventBridge { +interface IRollupEventInbox { function bridge() external view returns (IBridge); function initialize(address _bridge, address _rollup) external; diff --git a/contracts/src/rollup/IRollupLogic.sol b/contracts/src/rollup/IRollupLogic.sol index 83c8f3aef43..af6ed5e70fc 100644 --- a/contracts/src/rollup/IRollupLogic.sol +++ b/contracts/src/rollup/IRollupLogic.sol @@ -120,7 +120,7 @@ interface IRollupAdmin { * @param _inbox Inbox contract to add or remove * @param _enabled New status of inbox */ - function setInbox(address _inbox, bool _enabled) external; + function setDelayedInbox(address _inbox, bool _enabled) external; /** * @notice Pause interaction with the rollup contract @@ -210,4 +210,10 @@ interface IRollupAdmin { * @param newWasmModuleRoot new module root */ function setWasmModuleRoot(bytes32 newWasmModuleRoot) external; + + /** + * @notice set a new sequencer inbox contract + * @param _sequencerInbox new address of sequencer inbox + */ + function setSequencerInbox(address _sequencerInbox) external; } diff --git a/contracts/src/rollup/RollupAdminLogic.sol b/contracts/src/rollup/RollupAdminLogic.sol index e2b40c61ec7..d2667758027 100644 --- a/contracts/src/rollup/RollupAdminLogic.sol +++ b/contracts/src/rollup/RollupAdminLogic.sol @@ -21,16 +21,26 @@ contract RollupAdminLogic is RollupCore, IRollupAdmin, SecondaryLogicUUPSUpgrade onlyProxy initializer { - delayedBridge = connectedContracts.delayedBridge; - sequencerBridge = connectedContracts.sequencerInbox; + rollupDeploymentBlock = block.number; + bridge = connectedContracts.bridge; + sequencerInbox = connectedContracts.sequencerInbox; + connectedContracts.bridge.setDelayedInbox(address(connectedContracts.inbox), true); + connectedContracts.bridge.setSequencerInbox(address(connectedContracts.sequencerInbox)); + + inbox = connectedContracts.inbox; outbox = connectedContracts.outbox; - delayedBridge.setOutbox(address(connectedContracts.outbox), true); - rollupEventBridge = connectedContracts.rollupEventBridge; - delayedBridge.setInbox(address(connectedContracts.rollupEventBridge), true); + connectedContracts.bridge.setOutbox(address(connectedContracts.outbox), true); + rollupEventInbox = connectedContracts.rollupEventInbox; + connectedContracts.bridge.setDelayedInbox( + address(connectedContracts.rollupEventInbox), + true + ); - rollupEventBridge.rollupInitialized(config.chainId); - sequencerBridge.addSequencerL2Batch(0, "", 1, IGasRefunder(address(0))); + connectedContracts.rollupEventInbox.rollupInitialized(config.chainId); + connectedContracts.sequencerInbox.addSequencerL2Batch(0, "", 1, IGasRefunder(address(0))); + validatorUtils = connectedContracts.validatorUtils; + validatorWalletCreator = connectedContracts.validatorWalletCreator; challengeManager = connectedContracts.challengeManager; Node memory node = createInitialNode(); @@ -84,7 +94,7 @@ contract RollupAdminLogic is RollupCore, IRollupAdmin, SecondaryLogicUUPSUpgrade */ function setOutbox(IOutbox _outbox) external override { outbox = _outbox; - delayedBridge.setOutbox(address(_outbox), true); + bridge.setOutbox(address(_outbox), true); emit OwnerFunctionCalled(0); } @@ -94,7 +104,7 @@ contract RollupAdminLogic is RollupCore, IRollupAdmin, SecondaryLogicUUPSUpgrade */ function removeOldOutbox(address _outbox) external override { require(_outbox != address(outbox), "CUR_OUTBOX"); - delayedBridge.setOutbox(_outbox, false); + bridge.setOutbox(_outbox, false); emit OwnerFunctionCalled(1); } @@ -103,8 +113,8 @@ contract RollupAdminLogic is RollupCore, IRollupAdmin, SecondaryLogicUUPSUpgrade * @param _inbox Inbox contract to add or remove * @param _enabled New status of inbox */ - function setInbox(address _inbox, bool _enabled) external override { - delayedBridge.setInbox(address(_inbox), _enabled); + function setDelayedInbox(address _inbox, bool _enabled) external override { + bridge.setDelayedInbox(address(_inbox), _enabled); emit OwnerFunctionCalled(2); } @@ -296,4 +306,13 @@ contract RollupAdminLogic is RollupCore, IRollupAdmin, SecondaryLogicUUPSUpgrade wasmModuleRoot = newWasmModuleRoot; emit OwnerFunctionCalled(26); } + + /** + * @notice set a new sequencer inbox contract + * @param _sequencerInbox new address of sequencer inbox + */ + function setSequencerInbox(address _sequencerInbox) external override { + bridge.setSequencerInbox(_sequencerInbox); + emit OwnerFunctionCalled(27); + } } diff --git a/contracts/src/rollup/RollupCore.sol b/contracts/src/rollup/RollupCore.sol index 23ac66dd3ab..c8ea7080c77 100644 --- a/contracts/src/rollup/RollupCore.sol +++ b/contracts/src/rollup/RollupCore.sol @@ -9,7 +9,7 @@ import "@openzeppelin/contracts-upgradeable/security/PausableUpgradeable.sol"; import "./Node.sol"; import "./IRollupCore.sol"; import "./RollupLib.sol"; -import "./IRollupEventBridge.sol"; +import "./IRollupEventInbox.sol"; import "./IRollupCore.sol"; import "../challenge/IChallengeManager.sol"; @@ -31,11 +31,17 @@ abstract contract RollupCore is IRollupCore, PausableUpgradeable { uint256 public baseStake; bytes32 public wasmModuleRoot; - IBridge public delayedBridge; - ISequencerInbox public sequencerBridge; + IInbox public inbox; + IBridge public bridge; IOutbox public outbox; - IRollupEventBridge public rollupEventBridge; + ISequencerInbox public sequencerInbox; + IRollupEventInbox public rollupEventInbox; IChallengeManager public override challengeManager; + + // misc useful contracts when interacting with the rollup + address public validatorUtils; + address public validatorWalletCreator; + // when a staker loses a challenge, half of their funds get escrowed in this address address public loserStakeEscrow; address public stakeToken; @@ -63,6 +69,7 @@ abstract contract RollupCore is IRollupCore, PausableUpgradeable { mapping(address => uint256) private _withdrawableFunds; uint256 public totalWithdrawableFunds; + uint256 public rollupDeploymentBlock; // The node number of the initial node uint64 internal constant GENESIS_NODE = 0; @@ -529,7 +536,7 @@ abstract contract RollupCore is IRollupCore, PausableUpgradeable { { // validate data memoryFrame.prevNode = getNode(prevNodeNum); - memoryFrame.currentInboxSize = sequencerBridge.batchCount(); + memoryFrame.currentInboxSize = bridge.sequencerMessageCount(); // Make sure the previous state is correct against the node being built on require( @@ -560,7 +567,7 @@ abstract contract RollupCore is IRollupCore, PausableUpgradeable { require(afterInboxCount <= memoryFrame.currentInboxSize, "INBOX_PAST_END"); // This gives replay protection against the state of the inbox if (afterInboxCount > 0) { - memoryFrame.sequencerBatchAcc = sequencerBridge.inboxAccs(afterInboxCount - 1); + memoryFrame.sequencerBatchAcc = bridge.sequencerInboxAccs(afterInboxCount - 1); } } diff --git a/contracts/src/rollup/RollupCreator.sol b/contracts/src/rollup/RollupCreator.sol index 29b33436537..6b6026dcb73 100644 --- a/contracts/src/rollup/RollupCreator.sol +++ b/contracts/src/rollup/RollupCreator.sol @@ -18,7 +18,7 @@ contract RollupCreator is Ownable { address inboxAddress, address adminProxy, address sequencerInbox, - address delayedBridge + address bridge ); event TemplatesUpdated(); @@ -28,6 +28,9 @@ contract RollupCreator is Ownable { IRollupAdmin public rollupAdminLogic; IRollupUser public rollupUserLogic; + address public validatorUtils; + address public validatorWalletCreator; + constructor() Ownable() {} function setTemplates( @@ -35,22 +38,26 @@ contract RollupCreator is Ownable { IOneStepProofEntry _osp, IChallengeManager _challengeManagerLogic, IRollupAdmin _rollupAdminLogic, - IRollupUser _rollupUserLogic + IRollupUser _rollupUserLogic, + address _validatorUtils, + address _validatorWalletCreator ) external onlyOwner { bridgeCreator = _bridgeCreator; osp = _osp; challengeManagerTemplate = _challengeManagerLogic; rollupAdminLogic = _rollupAdminLogic; rollupUserLogic = _rollupUserLogic; + validatorUtils = _validatorUtils; + validatorWalletCreator = _validatorWalletCreator; emit TemplatesUpdated(); } struct CreateRollupFrame { ProxyAdmin admin; - IBridge delayedBridge; + IBridge bridge; ISequencerInbox sequencerInbox; IInbox inbox; - IRollupEventBridge rollupEventBridge; + IRollupEventInbox rollupEventInbox; IOutbox outbox; ArbitrumProxy rollup; } @@ -68,10 +75,10 @@ contract RollupCreator is Ownable { frame.admin = new ProxyAdmin(); ( - frame.delayedBridge, + frame.bridge, frame.sequencerInbox, frame.inbox, - frame.rollupEventBridge, + frame.rollupEventInbox, frame.outbox ) = bridgeCreator.createBridge( address(frame.admin), @@ -93,20 +100,23 @@ contract RollupCreator is Ownable { challengeManager.initialize( IChallengeResultReceiver(expectedRollupAddr), frame.sequencerInbox, - frame.delayedBridge, + frame.bridge, osp ); frame.rollup = new ArbitrumProxy( config, ContractDependencies({ - delayedBridge: frame.delayedBridge, + bridge: frame.bridge, sequencerInbox: frame.sequencerInbox, + inbox: frame.inbox, outbox: frame.outbox, - rollupEventBridge: frame.rollupEventBridge, + rollupEventInbox: frame.rollupEventInbox, challengeManager: challengeManager, rollupAdminLogic: rollupAdminLogic, - rollupUserLogic: rollupUserLogic + rollupUserLogic: rollupUserLogic, + validatorUtils: validatorUtils, + validatorWalletCreator: validatorWalletCreator }) ); require(address(frame.rollup) == expectedRollupAddr, "WRONG_ROLLUP_ADDR"); @@ -116,7 +126,7 @@ contract RollupCreator is Ownable { address(frame.inbox), address(frame.admin), address(frame.sequencerInbox), - address(frame.delayedBridge) + address(frame.bridge) ); return address(frame.rollup); } diff --git a/contracts/src/rollup/RollupEventBridge.sol b/contracts/src/rollup/RollupEventInbox.sol similarity index 88% rename from contracts/src/rollup/RollupEventBridge.sol rename to contracts/src/rollup/RollupEventInbox.sol index c541e8156e3..ed9378e258f 100644 --- a/contracts/src/rollup/RollupEventBridge.sol +++ b/contracts/src/rollup/RollupEventInbox.sol @@ -4,16 +4,16 @@ pragma solidity ^0.8.0; -import "./IRollupEventBridge.sol"; +import "./IRollupEventInbox.sol"; import "../bridge/IBridge.sol"; -import "../bridge/IMessageProvider.sol"; +import "../bridge/IDelayedMessageProvider.sol"; import "../libraries/DelegateCallAware.sol"; import {INITIALIZATION_MSG_TYPE} from "../libraries/MessageTypes.sol"; /** * @title The inbox for rollup protocol events */ -contract RollupEventBridge is IRollupEventBridge, IMessageProvider, DelegateCallAware { +contract RollupEventInbox is IRollupEventInbox, IDelayedMessageProvider, DelegateCallAware { uint8 internal constant CREATE_NODE_EVENT = 0; uint8 internal constant CONFIRM_NODE_EVENT = 1; uint8 internal constant REJECT_NODE_EVENT = 2; diff --git a/contracts/src/rollup/RollupLib.sol b/contracts/src/rollup/RollupLib.sol index c7fdb745f0e..93f51f91cfd 100644 --- a/contracts/src/rollup/RollupLib.sol +++ b/contracts/src/rollup/RollupLib.sol @@ -11,7 +11,8 @@ import "../bridge/ISequencerInbox.sol"; import "../bridge/IBridge.sol"; import "../bridge/IOutbox.sol"; -import "./IRollupEventBridge.sol"; +import "../bridge/IInbox.sol"; +import "./IRollupEventInbox.sol"; import "./IRollupLogic.sol"; struct Config { @@ -27,13 +28,17 @@ struct Config { } struct ContractDependencies { - IBridge delayedBridge; + IBridge bridge; ISequencerInbox sequencerInbox; + IInbox inbox; IOutbox outbox; - IRollupEventBridge rollupEventBridge; + IRollupEventInbox rollupEventInbox; IChallengeManager challengeManager; IRollupAdmin rollupAdminLogic; IRollupUser rollupUserLogic; + // misc contracts that are useful when interacting with the rollup + address validatorUtils; + address validatorWalletCreator; } library RollupLib { diff --git a/contracts/src/test-helpers/BridgeTester.sol b/contracts/src/test-helpers/BridgeTester.sol index 03802763c53..b2f31b0efeb 100644 --- a/contracts/src/test-helpers/BridgeTester.sol +++ b/contracts/src/test-helpers/BridgeTester.sol @@ -29,13 +29,22 @@ contract BridgeTester is OwnableUpgradeable, DelegateCallAware, IBridge { mapping(address => InOutInfo) private allowedInboxesMap; mapping(address => InOutInfo) private allowedOutboxesMap; - address[] public allowedInboxList; + address[] public allowedDelayedInboxList; address[] public allowedOutboxList; address private _activeOutbox; + address public sequencerInbox; + + function setSequencerInbox(address _sequencerInbox) external override onlyOwner { + sequencerInbox = _sequencerInbox; + emit SequencerInboxUpdated(_sequencerInbox); + } + /// @dev Accumulator for delayed inbox messages; tail represents hash of the current state; each element represents the inclusion of a new message. - bytes32[] public override inboxAccs; + bytes32[] public override delayedInboxAccs; + + bytes32[] public override sequencerInboxAccs; address private constant EMPTY_ACTIVEOUTBOX = address(type(uint160).max); @@ -49,7 +58,7 @@ contract BridgeTester is OwnableUpgradeable, DelegateCallAware, IBridge { return _activeOutbox; } - function allowedInboxes(address inbox) external view override returns (bool) { + function allowedDelayedInboxes(address inbox) external view override returns (bool) { return allowedInboxesMap[inbox].allowed; } @@ -57,6 +66,25 @@ contract BridgeTester is OwnableUpgradeable, DelegateCallAware, IBridge { return allowedOutboxesMap[outbox].allowed; } + function enqueueSequencerMessage(bytes32 dataHash, uint256 afterDelayedMessagesRead) + external + returns ( + uint256 seqMessageIndex, + bytes32 beforeAcc, + bytes32 delayedAcc, + bytes32 acc + ) + { + // TODO: implement stub logic + } + + function submitBatchSpendingReport(address batchPoster, bytes32 dataHash) + external + returns (uint256) + { + // TODO: implement stub + } + /** * @dev Enqueue a message in the delayed inbox accumulator. * These messages are later sequenced in the SequencerInbox, either by the sequencer as @@ -67,9 +95,9 @@ contract BridgeTester is OwnableUpgradeable, DelegateCallAware, IBridge { address sender, bytes32 messageDataHash ) external payable override returns (uint256) { - if (!allowedInboxesMap[msg.sender].allowed) revert NotInbox(msg.sender); + if (!allowedInboxesMap[msg.sender].allowed) revert NotDelayedInbox(msg.sender); return - addMessageToAccumulator( + addMessageToDelayedAccumulator( kind, sender, uint64(block.number), @@ -79,7 +107,7 @@ contract BridgeTester is OwnableUpgradeable, DelegateCallAware, IBridge { ); } - function addMessageToAccumulator( + function addMessageToDelayedAccumulator( uint8 kind, address sender, uint64 blockNumber, @@ -87,7 +115,7 @@ contract BridgeTester is OwnableUpgradeable, DelegateCallAware, IBridge { uint256 baseFeeL1, bytes32 messageDataHash ) internal returns (uint256) { - uint256 count = inboxAccs.length; + uint256 count = delayedInboxAccs.length; bytes32 messageHash = Messages.messageHash( kind, sender, @@ -99,9 +127,9 @@ contract BridgeTester is OwnableUpgradeable, DelegateCallAware, IBridge { ); bytes32 prevAcc = 0; if (count > 0) { - prevAcc = inboxAccs[count - 1]; + prevAcc = delayedInboxAccs[count - 1]; } - inboxAccs.push(Messages.accumulateInboxMessage(prevAcc, messageHash)); + delayedInboxAccs.push(Messages.accumulateInboxMessage(prevAcc, messageHash)); emit MessageDelivered( count, prevAcc, @@ -134,7 +162,7 @@ contract BridgeTester is OwnableUpgradeable, DelegateCallAware, IBridge { emit BridgeCallTriggered(msg.sender, to, value, data); } - function setInbox(address inbox, bool enabled) external override onlyOwner { + function setDelayedInbox(address inbox, bool enabled) external override onlyOwner { InOutInfo storage info = allowedInboxesMap[inbox]; bool alreadyEnabled = info.allowed; emit InboxToggle(inbox, enabled); @@ -142,12 +170,14 @@ contract BridgeTester is OwnableUpgradeable, DelegateCallAware, IBridge { return; } if (enabled) { - allowedInboxesMap[inbox] = InOutInfo(allowedInboxList.length, true); - allowedInboxList.push(inbox); + allowedInboxesMap[inbox] = InOutInfo(allowedDelayedInboxList.length, true); + allowedDelayedInboxList.push(inbox); } else { - allowedInboxList[info.index] = allowedInboxList[allowedInboxList.length - 1]; - allowedInboxesMap[allowedInboxList[info.index]].index = info.index; - allowedInboxList.pop(); + allowedDelayedInboxList[info.index] = allowedDelayedInboxList[ + allowedDelayedInboxList.length - 1 + ]; + allowedInboxesMap[allowedDelayedInboxList[info.index]].index = info.index; + allowedDelayedInboxList.pop(); delete allowedInboxesMap[inbox]; } } @@ -170,8 +200,12 @@ contract BridgeTester is OwnableUpgradeable, DelegateCallAware, IBridge { } } - function messageCount() external view override returns (uint256) { - return inboxAccs.length; + function delayedMessageCount() external view override returns (uint256) { + return delayedInboxAccs.length; + } + + function sequencerMessageCount() external view override returns (uint256) { + return sequencerInboxAccs.length; } receive() external payable {} diff --git a/contracts/test/contract/arbRollup.spec.ts b/contracts/test/contract/arbRollup.spec.ts index a70665254e8..90ed419d07a 100644 --- a/contracts/test/contract/arbRollup.spec.ts +++ b/contracts/test/contract/arbRollup.spec.ts @@ -161,7 +161,9 @@ const setup = async () => { oneStepProofEntry.address, challengeManagerTemplate.address, rollupAdminLogicTemplate.address, - rollupUserLogicTemplate.address + rollupUserLogicTemplate.address, + ethers.constants.AddressZero, + ethers.constants.AddressZero ); const nonce = await rollupCreator.signer.provider!.getTransactionCount(rollupCreator.address); @@ -211,10 +213,10 @@ const setup = async () => { rollupAdminLogicTemplate, rollupUserLogicTemplate, blockChallengeFactory: challengeManagerTemplateFac, - rollupEventBridge: await rollupAdmin.rollupEventBridge(), + rollupEventBridge: await rollupAdmin.rollupEventInbox(), outbox: await rollupAdmin.outbox(), sequencerInbox: rollupCreatedEvent.sequencerInbox, - delayedBridge: rollupCreatedEvent.delayedBridge, + delayedBridge: rollupCreatedEvent.bridge, }; }; @@ -350,12 +352,15 @@ describe("ArbRollup", () => { await expect( rollupAdmin.initialize(await getDefaultConfig(), { challengeManager: constants.AddressZero, - delayedBridge: constants.AddressZero, + bridge: constants.AddressZero, + inbox: constants.AddressZero, outbox: constants.AddressZero, rollupAdminLogic: constants.AddressZero, - rollupEventBridge: constants.AddressZero, + rollupEventInbox: constants.AddressZero, rollupUserLogic: constants.AddressZero, sequencerInbox: constants.AddressZero, + validatorUtils: constants.AddressZero, + validatorWalletCreator: constants.AddressZero, }) ).to.be.revertedWith("Initializable: contract is already initialized"); }); diff --git a/contracts/test/contract/sequencerInboxForceInclude.spec.ts b/contracts/test/contract/sequencerInboxForceInclude.spec.ts index 2de836f0078..49c93c2fc43 100644 --- a/contracts/test/contract/sequencerInboxForceInclude.spec.ts +++ b/contracts/test/contract/sequencerInboxForceInclude.spec.ts @@ -30,7 +30,7 @@ import { SequencerInbox__factory, TransparentUpgradeableProxy__factory, } from '../../build/types' -import { initializeAccounts } from './utils' +import { applyAlias, initializeAccounts } from './utils' import { Event } from '@ethersproject/contracts' import { Interface } from '@ethersproject/abi' import { @@ -84,16 +84,16 @@ describe('SequencerInboxForceInclude', async () => { amount: BigNumber, data: string, ) => { - const countBefore = (await bridge.functions.messageCount())[0].toNumber() + const countBefore = (await bridge.functions.delayedMessageCount())[0].toNumber() const sendUnsignedTx = await inbox .connect(sender) .sendUnsignedTransaction(l2Gas, l2GasPrice, nonce, destAddr, amount, data) const sendUnsignedTxReceipt = await sendUnsignedTx.wait() - const countAfter = (await bridge.functions.messageCount())[0].toNumber() + const countAfter = (await bridge.functions.delayedMessageCount())[0].toNumber() expect(countAfter, 'Unexpected inbox count').to.eq(countBefore + 1) - const senderAddr = await sender.getAddress() + const senderAddr = applyAlias(await sender.getAddress()) const messageDeliveredEvent = getMessageDeliveredEvents( sendUnsignedTxReceipt, @@ -102,7 +102,7 @@ describe('SequencerInboxForceInclude', async () => { const blockL1 = await sender.provider!.getBlock(l1BlockNumber) const baseFeeL1 = blockL1.baseFeePerGas!.toNumber() const l1BlockTimestamp = blockL1.timestamp - const delayedAcc = await bridge.inboxAccs(countBefore) + const delayedAcc = await bridge.delayedInboxAccs(countBefore) // need to hex pad the address const messageDataHash = ethers.utils.solidityKeccak256( @@ -138,7 +138,7 @@ describe('SequencerInboxForceInclude', async () => { expect(prevAccumulator, 'Incorrect prev accumulator').to.eq( countBefore === 0 ? ethers.utils.hexZeroPad('0x', 32) - : await bridge.inboxAccs(countBefore - 1), + : await bridge.delayedInboxAccs(countBefore - 1), ) const nextAcc = ( @@ -257,7 +257,6 @@ describe('SequencerInboxForceInclude', async () => { await sequencerInbox.initialize( bridgeProxy.address, - inbox.address, await dummyRollup.getAddress(), { delayBlocks: maxDelayBlocks, @@ -268,7 +267,8 @@ describe('SequencerInboxForceInclude', async () => { ) await inbox.initialize(bridgeProxy.address, sequencerInbox.address) - await bridge.setInbox(inbox.address, true) + await bridge.setDelayedInbox(inbox.address, true) + await bridge.setSequencerInbox(sequencerInbox.address) const messageTester = (await ( await ethers.getContractFactory('MessageTester') diff --git a/contracts/test/contract/utils.ts b/contracts/test/contract/utils.ts index 43548c6c190..6636685b95e 100644 --- a/contracts/test/contract/utils.ts +++ b/contracts/test/contract/utils.ts @@ -1,5 +1,21 @@ import { ethers } from 'hardhat' import { Signer } from '@ethersproject/abstract-signer' +import { getAddress } from '@ethersproject/address' + +const ADDRESS_ALIAS_OFFSET = BigInt("0x1111000000000000000000000000000000001111"); +const ADDRESS_BIT_LENGTH = 160; +const ADDRESS_NIBBLE_LENGTH = ADDRESS_BIT_LENGTH / 4; + +export const applyAlias = (addr: string) => { + // we use BigInts in here to allow for proper overflow behaviour + // BigInt.asUintN calculates the correct positive modulus + return getAddress( + "0x" + + BigInt.asUintN(ADDRESS_BIT_LENGTH, BigInt(addr) + ADDRESS_ALIAS_OFFSET) + .toString(16) + .padStart(ADDRESS_NIBBLE_LENGTH, "0") + ); +}; export async function initializeAccounts(): Promise { const [account0] = await ethers.getSigners() diff --git a/contracts/test/prover/one-step-proof.ts b/contracts/test/prover/one-step-proof.ts index c87011c59fd..56264490b04 100644 --- a/contracts/test/prover/one-step-proof.ts +++ b/contracts/test/prover/one-step-proof.ts @@ -59,7 +59,7 @@ describe("OneStepProof", function () { const proof = proofs[i]; isdone.push(false); const inboxLimit = 1000000; - const promise = osp.proveOneStep([inboxLimit, seqInbox.address, bridge.address], i, [...Buffer.from(proof.before, "hex")], [...Buffer.from(proof.proof, "hex")]) + const promise = osp.proveOneStep([inboxLimit, bridge.address], i, [...Buffer.from(proof.before, "hex")], [...Buffer.from(proof.proof, "hex")]) .catch((err: any) => { console.error("Error executing proof " + i, err.reason); throw err; diff --git a/go-ethereum b/go-ethereum index 4a00cd8cc4b..6d9e77d9bb5 160000 --- a/go-ethereum +++ b/go-ethereum @@ -1 +1 @@ -Subproject commit 4a00cd8cc4b2b0025c8ba9e98259714ed69e49cb +Subproject commit 6d9e77d9bb54ba268d8e75a6a3ca685ee2f6fbf8 diff --git a/precompiles/ArbOwner_test.go b/precompiles/ArbOwner_test.go index eaf1eb72be3..994e3b79550 100644 --- a/precompiles/ArbOwner_test.go +++ b/precompiles/ArbOwner_test.go @@ -32,8 +32,7 @@ func TestAddressSet(t *testing.T) { callCtx := testContext(caller, evm) // the zero address is an owner by default - ZeroAddressL2 := util.RemapL1Address(common.Address{}) - Require(t, prec.RemoveChainOwner(callCtx, evm, ZeroAddressL2)) + Require(t, prec.RemoveChainOwner(callCtx, evm, common.Address{})) Require(t, prec.AddChainOwner(callCtx, evm, addr1)) Require(t, prec.AddChainOwner(callCtx, evm, addr2))