Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
3d3e767
feat: ban-deposits-interop first RFC draft
skeletor-spaceman Aug 5, 2024
0d78e5a
fix: missing marshalling of tx
skeletor-spaceman Aug 5, 2024
37f17de
feat: code cleanup and renaming
skeletor-spaceman Aug 6, 2024
4f9621a
feat: cleaner implementation, always set isDeposit on
skeletor-spaceman Aug 6, 2024
65b6804
feat: simplified deposits complete tx and added Isthmus L1Info tx
skeletor-spaceman Aug 6, 2024
70b7db0
feat: L1Block make ecotone a public function
skeletor-spaceman Aug 7, 2024
24deb1c
feat: re-organized shared functions
skeletor-spaceman Aug 7, 2024
2e322c9
Merge branch 'develop' of github.com:ethereum-optimism/optimism into …
skeletor-spaceman Aug 7, 2024
785eaf4
fix: contracts compiler errors
0xDiscotech Aug 6, 2024
9ef55a2
feat: create some set l1 block values ithmus test
0xDiscotech Aug 7, 2024
f463e8a
feat: revamp unmarshalBinaryIsthmusAndEcotone function
skeletor-spaceman Aug 7, 2024
0567b20
chore: checkpoint debugging test
0xDiscotech Aug 7, 2024
7ee2530
fix: contracts compiler errors
0xDiscotech Aug 6, 2024
9fd174e
feat: create some set l1 block values ithmus test
0xDiscotech Aug 7, 2024
c86657f
chore: checkpoint debugging test
0xDiscotech Aug 7, 2024
63b9190
refactor: call public function
0xDiscotech Aug 7, 2024
7a74aaa
Merge branch 'feat/ban-deposits-interop' of github-defi:defi-wonderla…
0xDiscotech Aug 7, 2024
43fb36e
fix: update is deposit var storage slot
0xDiscotech Aug 7, 2024
dea7432
chore: update l2 cross inbox tests with the new is deposit check
0xDiscotech Aug 7, 2024
840de88
refactor: set is deposit as an unstructured storage slot var
0xDiscotech Aug 7, 2024
e758d9a
feat: add missing natspec
0xDiscotech Aug 7, 2024
49a02ff
feat: update semver
0xDiscotech Aug 7, 2024
eafba7a
chore: enhance natspec
0xDiscotech Aug 7, 2024
e162361
chore: underscore slot constant name
0xDiscotech Aug 7, 2024
fb0f259
Revert "chore: underscore slot constant name"
0xDiscotech Aug 7, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 11 additions & 1 deletion op-node/rollup/derive/attributes.go
Original file line number Diff line number Diff line change
Expand Up @@ -121,9 +121,19 @@ func (ba *FetchingAttributesBuilder) PreparePayloadAttributes(ctx context.Contex
return nil, NewCriticalError(fmt.Errorf("failed to create l1InfoTx: %w", err))
}

txs := make([]hexutil.Bytes, 0, 1+len(depositTxs)+len(upgradeTxs))
var postDeposits []hexutil.Bytes
if ba.rollupCfg.IsInterop(nextL2Time) {
depositsCompleteTx, err := DepositsCompleteBytes(seqNumber, l1Info)
if err != nil {
return nil, NewCriticalError(fmt.Errorf("failed to create depositsCompleteTx: %w", err))
}
postDeposits = append(postDeposits, depositsCompleteTx)
}

txs := make([]hexutil.Bytes, 0, 1+len(depositTxs)+len(postDeposits)+len(upgradeTxs))
txs = append(txs, l1InfoTx)
txs = append(txs, depositTxs...)
txs = append(txs, postDeposits...)
txs = append(txs, upgradeTxs...)

var withdrawals *types.Withdrawals
Expand Down
104 changes: 94 additions & 10 deletions op-node/rollup/derive/l1_block_info.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,14 +20,19 @@ import (
const (
L1InfoFuncBedrockSignature = "setL1BlockValues(uint64,uint64,uint256,bytes32,uint64,bytes32,uint256,uint256)"
L1InfoFuncEcotoneSignature = "setL1BlockValuesEcotone()"
L1InfoFuncIsthmusSignature = "setL2BlockValuesIsthmus()"
DepositsCompleteSignature = "depositsComplete()"
L1InfoArguments = 8
L1InfoBedrockLen = 4 + 32*L1InfoArguments
L1InfoEcotoneLen = 4 + 32*5 // after Ecotone upgrade, args are packed into 5 32-byte slots
DepositsCompleteLen = 4 // only the selector
)

var (
L1InfoFuncBedrockBytes4 = crypto.Keccak256([]byte(L1InfoFuncBedrockSignature))[:4]
L1InfoFuncEcotoneBytes4 = crypto.Keccak256([]byte(L1InfoFuncEcotoneSignature))[:4]
L1InfoFuncIsthmusBytes4 = crypto.Keccak256([]byte(L1InfoFuncIsthmusSignature))[:4]
DepositsCompleteBytes4 = crypto.Keccak256([]byte(DepositsCompleteSignature))[:4]
L1InfoDepositerAddress = common.HexToAddress("0xdeaddeaddeaddeaddeaddeaddeaddeaddead0001")
L1BlockAddress = predeploys.L1BlockAddr
ErrInvalidFormat = errors.New("invalid ecotone l1 block info format")
Expand Down Expand Up @@ -144,7 +149,7 @@ func (info *L1BlockInfo) unmarshalBinaryBedrock(data []byte) error {
return nil
}

// Ecotone Binary Format
// Isthmus & Ecotone Binary Format
// +---------+--------------------------+
// | Bytes | Field |
// +---------+--------------------------+
Expand All @@ -160,10 +165,32 @@ func (info *L1BlockInfo) unmarshalBinaryBedrock(data []byte) error {
// | 32 | BatcherHash |
// +---------+--------------------------+

func (info *L1BlockInfo) marshalBinaryIsthmus() ([]byte, error) {
out, err := marshalBinaryEcotoneOrIsthmus(info, true)
if err != nil {
return nil, fmt.Errorf("failed to marshal Isthmus l1 block info: %w", err)
}
return out, nil
}

func (info *L1BlockInfo) marshalBinaryEcotone() ([]byte, error) {
w := bytes.NewBuffer(make([]byte, 0, L1InfoEcotoneLen))
if err := solabi.WriteSignature(w, L1InfoFuncEcotoneBytes4); err != nil {
return nil, err
out, err := marshalBinaryEcotoneOrIsthmus(info, true)
if err != nil {
return nil, fmt.Errorf("failed to marshal Ecotone l1 block info: %w", err)
}
return out, nil
}

func marshalBinaryEcotoneOrIsthmus(info *L1BlockInfo, isIsthmus ...bool) ([]byte, error) {
w := bytes.NewBuffer(make([]byte, 0, L1InfoEcotoneLen)) // Ecotone and Isthmus have the same length
if isIsthmus != nil && isIsthmus[0] {
if err := solabi.WriteSignature(w, L1InfoFuncIsthmusBytes4); err != nil {
return nil, err
}
} else {
if err := solabi.WriteSignature(w, L1InfoFuncEcotoneBytes4); err != nil {
return nil, err
}
}
if err := binary.Write(w, binary.BigEndian, info.BaseFeeScalar); err != nil {
return nil, err
Expand Down Expand Up @@ -200,14 +227,21 @@ func (info *L1BlockInfo) marshalBinaryEcotone() ([]byte, error) {
return w.Bytes(), nil
}

func (info *L1BlockInfo) unmarshalBinaryIsthmus(data []byte) error {
return unmarshalBinaryWithSignatureAndData(info, L1InfoFuncIsthmusBytes4, data)
}
func (info *L1BlockInfo) unmarshalBinaryEcotone(data []byte) error {
return unmarshalBinaryWithSignatureAndData(info, L1InfoFuncEcotoneBytes4, data)
}

func unmarshalBinaryWithSignatureAndData(info *L1BlockInfo, signature []byte, data []byte) error {
if len(data) != L1InfoEcotoneLen {
return fmt.Errorf("data is unexpected length: %d", len(data))
}
r := bytes.NewReader(data)

var err error
if _, err := solabi.ReadAndValidateSignature(r, L1InfoFuncEcotoneBytes4); err != nil {
if _, err := solabi.ReadAndValidateSignature(r, signature); err != nil {
return err
}
if err := binary.Read(r, binary.BigEndian, &info.BaseFeeScalar); err != nil {
Expand Down Expand Up @@ -250,9 +284,19 @@ func isEcotoneButNotFirstBlock(rollupCfg *rollup.Config, l2BlockTime uint64) boo
return rollupCfg.IsEcotone(l2BlockTime) && !rollupCfg.IsEcotoneActivationBlock(l2BlockTime)
}

// isInteropButNotFirstBlock returns whether the specified block is subject to the Isthmus upgrade,
// but is not the actiation block itself.
func isInteropButNotFirstBlock(rollupCfg *rollup.Config, l2BlockTime uint64) bool {
return rollupCfg.IsInterop(l2BlockTime) && !rollupCfg.IsInteropActivationBlock(l2BlockTime)
}

// L1BlockInfoFromBytes is the inverse of L1InfoDeposit, to see where the L2 chain is derived from
func L1BlockInfoFromBytes(rollupCfg *rollup.Config, l2BlockTime uint64, data []byte) (*L1BlockInfo, error) {
var info L1BlockInfo
// Important, this should be ordered from most recent to oldest
if isInteropButNotFirstBlock(rollupCfg, l2BlockTime) {
return &info, info.unmarshalBinaryIsthmus(data)
}
if isEcotoneButNotFirstBlock(rollupCfg, l2BlockTime) {
return &info, info.unmarshalBinaryEcotone(data)
}
Expand All @@ -271,7 +315,8 @@ func L1InfoDeposit(rollupCfg *rollup.Config, sysCfg eth.SystemConfig, seqNumber
BatcherAddr: sysCfg.BatcherAddr,
}
var data []byte
if isEcotoneButNotFirstBlock(rollupCfg, l2BlockTime) {

if isInteropButNotFirstBlock(rollupCfg, l2BlockTime) {
l1BlockInfo.BlobBaseFee = block.BlobBaseFee()
if l1BlockInfo.BlobBaseFee == nil {
// The L2 spec states to use the MIN_BLOB_GASPRICE from EIP-4844 if not yet active on L1.
Expand All @@ -283,11 +328,19 @@ func L1InfoDeposit(rollupCfg *rollup.Config, sysCfg eth.SystemConfig, seqNumber
}
l1BlockInfo.BlobBaseFeeScalar = scalars.BlobBaseFeeScalar
l1BlockInfo.BaseFeeScalar = scalars.BaseFeeScalar
out, err := l1BlockInfo.marshalBinaryEcotone()
if err != nil {
return nil, fmt.Errorf("failed to marshal Ecotone l1 block info: %w", err)
if isEcotoneButNotFirstBlock(rollupCfg, l2BlockTime) {
out, err := l1BlockInfo.marshalBinaryEcotone()
if err != nil {
return nil, fmt.Errorf("failed to marshal Ecotone l1 block info: %w", err)
}
data = out
} else {
out, err := l1BlockInfo.marshalBinaryIsthmus()
if err != nil {
return nil, fmt.Errorf("failed to marshal Isthmus l1 block info: %w", err)
}
data = out
}
data = out
} else {
l1BlockInfo.L1FeeOverhead = sysCfg.Overhead
l1BlockInfo.L1FeeScalar = sysCfg.Scalar
Expand Down Expand Up @@ -335,3 +388,34 @@ func L1InfoDepositBytes(rollupCfg *rollup.Config, sysCfg eth.SystemConfig, seqNu
}
return opaqueL1Tx, nil
}

func DepositsCompleteDeposit(seqNumber uint64, block eth.BlockInfo) (*types.DepositTx, error) {
source := L1InfoDepositSource{
L1BlockHash: block.Hash(),
SeqNumber: seqNumber,
}
out := &types.DepositTx{
SourceHash: source.SourceHash(),
From: L1InfoDepositerAddress,
To: &L1BlockAddress,
Mint: nil,
Value: big.NewInt(0),
Gas: 50_000,
IsSystemTransaction: false,
Data: DepositsCompleteBytes4,
}
return out, nil
}

func DepositsCompleteBytes(seqNumber uint64, l1Info eth.BlockInfo) ([]byte, error) {
dep, err := DepositsCompleteDeposit(seqNumber, l1Info)
if err != nil {
return nil, fmt.Errorf("failed to create DepositsComplete tx: %w", err)
}
depositsCompleteTx := types.NewTx(dep)
opaqueDepositsCompleteTx, err := depositsCompleteTx.MarshalBinary()
if err != nil {
return nil, fmt.Errorf("failed to encode DepositsComplete tx: %w", err)
}
return opaqueDepositsCompleteTx, nil
}
1 change: 1 addition & 0 deletions op-node/rollup/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -499,6 +499,7 @@ func (c *Config) IsHoloceneActivationBlock(l2BlockTime uint64) bool {
!c.IsHolocene(l2BlockTime-c.BlockTime)
}

// TODO rename to IsIsthmusActivationBlock (this will require quite a bit of renaming)
func (c *Config) IsInteropActivationBlock(l2BlockTime uint64) bool {
return c.IsInterop(l2BlockTime) &&
l2BlockTime >= c.BlockTime &&
Expand Down
18 changes: 16 additions & 2 deletions packages/contracts-bedrock/src/L2/CrossL2Inbox.sol
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,14 @@ interface IDependencySet {
function isInDependencySet(uint256 _chainId) external view returns (bool);
}

/// @title IL1Block
/// @notice Interface for L1Block with only `isDeposit()` method.
interface IL1Block {
/// @notice Returns whether the call was triggered from a a deposit or not.
/// @return True if the current call was triggered by a deposit transaction, and false otherwise.
function isDeposit() external view returns (bool);
}

Comment thread
skeletor-spaceman marked this conversation as resolved.
/// @notice Thrown when a non-written transient storage slot is attempted to be read from.
error NotEntered();

Expand All @@ -29,6 +37,9 @@ error InvalidChainId();
/// @notice Thrown when trying to execute a cross chain message and the target call fails.
error TargetCallFailed();

/// @notice Thrown when trying to execute a cross chain message on a deposit transaction.
error NoExecutingDeposits();

/// @custom:proxied
/// @custom:predeploy 0x4200000000000000000000000000000000000022
/// @title CrossL2Inbox
Expand Down Expand Up @@ -56,8 +67,8 @@ contract CrossL2Inbox is ICrossL2Inbox, ISemver, TransientReentrancyAware {
bytes32 internal constant CHAINID_SLOT = 0x6e0446e8b5098b8c8193f964f1b567ec3a2bdaeba33d36acb85c1f1d3f92d313;

/// @notice Semantic version.
/// @custom:semver 1.0.0-beta.3
string public constant version = "1.0.0-beta.3";
/// @custom:semver 1.1.0-beta.3
string public constant version = "1.1.0-beta.3";
Comment thread
skeletor-spaceman marked this conversation as resolved.

/// @notice Emitted when a cross chain message is being executed.
/// @param msgHash Hash of message payload being executed.
Expand Down Expand Up @@ -114,6 +125,9 @@ contract CrossL2Inbox is ICrossL2Inbox, ISemver, TransientReentrancyAware {
payable
reentrantAware
{
// We need to know if this is being called on a depositTx
if (IL1Block(Predeploys.L1_BLOCK_ATTRIBUTES).isDeposit()) revert NoExecutingDeposits();

if (_id.timestamp > block.timestamp) revert InvalidTimestamp();
if (!IDependencySet(Predeploys.L1_BLOCK_ATTRIBUTES).isInDependencySet(_id.chainId)) {
revert InvalidChainId();
Expand Down
43 changes: 40 additions & 3 deletions packages/contracts-bedrock/src/L2/L1Block.sol
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ pragma solidity 0.8.15;
import { ISemver } from "src/universal/ISemver.sol";
import { Constants } from "src/libraries/Constants.sol";
import { GasPayingToken, IGasToken } from "src/libraries/GasPayingToken.sol";
import { Predeploys } from "src/libraries/Predeploys.sol";
import "src/libraries/L1BlockErrors.sol";

/// @custom:proxied
Expand Down Expand Up @@ -57,9 +58,14 @@ contract L1Block is ISemver, IGasToken {
/// @notice The latest L1 blob base fee.
uint256 public blobBaseFee;

/// @custom:semver 1.4.1-beta.1
/// @notice Storage slot that the isDeposit is stored at.
/// This is a custom slot that is not part of the standard storage layout.
/// keccak256(abi.encode(uint256(keccak256("l1Block.identifier.isDeposit")) - 1)) & ~bytes32(uint256(0xff))
uint256 internal constant IS_DEPOSIT_SLOT = 0x921bd3a089295c6e5540e8fba8195448d253efd6f2e3e495b499b627dc36a300;
Comment thread
skeletor-spaceman marked this conversation as resolved.

/// @custom:semver 1.5.1-beta.1
function version() public pure virtual returns (string memory) {
return "1.4.1-beta.1";
return "1.5.1-beta.1";
Comment thread
skeletor-spaceman marked this conversation as resolved.
}

/// @notice Returns the gas paying token, its decimals, name and symbol.
Expand Down Expand Up @@ -87,6 +93,15 @@ contract L1Block is ISemver, IGasToken {
return token != Constants.ETHER;
}

/// @notice Returns whether the call was triggered from a a deposit or not.
/// @notice This function is only callable by the CrossL2Inbox contract.
function isDeposit() external view returns (bool isDeposit_) {
if (msg.sender != Predeploys.CROSS_L2_INBOX) revert NotCrossL2Inbox();
assembly {
isDeposit_ := sload(IS_DEPOSIT_SLOT)
}
}

/// @custom:legacy
/// @notice Updates the L1 block values.
/// @param _number L1 blocknumber.
Expand Down Expand Up @@ -121,6 +136,17 @@ contract L1Block is ISemver, IGasToken {
l1FeeScalar = _l1FeeScalar;
}

/// @notice Updates the `isDeposit` flag and sets the L1 block values for an Isthmus upgraded chain.
/// It updates the L1 block values through the `setL1BlockValuesEcotone` function.
function setL1BlockValuesIsthmus() external {
// Set the isDeposit flag to true.
assembly {
sstore(IS_DEPOSIT_SLOT, 1)
}

setL1BlockValuesEcotone();
}

/// @notice Updates the L1 block values for an Ecotone upgraded chain.
/// Params are packed and passed in as raw msg.data instead of ABI to reduce calldata size.
/// Params are expected to be in the following order:
Expand All @@ -133,7 +159,7 @@ contract L1Block is ISemver, IGasToken {
/// 7. _blobBaseFee L1 blob base fee.
/// 8. _hash L1 blockhash.
/// 9. _batcherHash Versioned hash to authenticate batcher by.
function setL1BlockValuesEcotone() external {
function setL1BlockValuesEcotone() public {
address depositor = DEPOSITOR_ACCOUNT();
assembly {
// Revert if the caller is not the depositor account.
Expand All @@ -152,6 +178,17 @@ contract L1Block is ISemver, IGasToken {
}
}

/// @notice Resets the isDeposit flag.
/// Should only be called by the depositor account after the deposits are complete.
function depositsComplete() external {
if (msg.sender != DEPOSITOR_ACCOUNT()) revert NotDepositor();

// Set the isDeposit flag to false.
assembly {
sstore(IS_DEPOSIT_SLOT, 0)
}
}

/// @notice Sets the gas paying token for the L2 system. Can only be called by the special
/// depositor account. This function is not called on every L2 block but instead
/// only called by specially crafted L1 deposit transactions.
Expand Down
3 changes: 3 additions & 0 deletions packages/contracts-bedrock/src/libraries/L1BlockErrors.sol
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@ pragma solidity ^0.8.0;
/// @notice Error returns when a non-depositor account tries to set L1 block values.
error NotDepositor();

/// @notice Error when a non-cross L2 Inbox sender tries to call the `isDeposit()` method.
error NotCrossL2Inbox();

/// @notice Error when a chain ID is not in the interop dependency set.
error NotDependency();

Expand Down
Loading