-
Notifications
You must be signed in to change notification settings - Fork 3.9k
draft: ban-deposits-interop first RFC draft #11362
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 19 commits
3d3e767
0d78e5a
37f17de
4f9621a
65b6804
70b7db0
24deb1c
2e322c9
f463e8a
e90db39
771b319
2838d6e
e40d49f
c89ffa4
ebc225d
198d683
1434e89
8e93fcc
abe646b
13096e8
057fc5b
5e0d64c
2c370f7
da494fd
09c549c
bc562de
a8fd430
0a803e5
78aa2c4
5eaf39d
d5ba5d1
f2d8fb8
ccf505d
a17e357
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -20,14 +20,19 @@ import ( | |
| const ( | ||
| L1InfoFuncBedrockSignature = "setL1BlockValues(uint64,uint64,uint256,bytes32,uint64,bytes32,uint256,uint256)" | ||
| L1InfoFuncEcotoneSignature = "setL1BlockValuesEcotone()" | ||
|
skeletor-spaceman marked this conversation as resolved.
|
||
| 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") | ||
|
|
@@ -144,7 +149,7 @@ func (info *L1BlockInfo) unmarshalBinaryBedrock(data []byte) error { | |
| return nil | ||
| } | ||
|
|
||
| // Ecotone Binary Format | ||
| // Isthmus & Ecotone Binary Format | ||
| // +---------+--------------------------+ | ||
| // | Bytes | Field | | ||
| // +---------+--------------------------+ | ||
|
|
@@ -159,10 +164,24 @@ func (info *L1BlockInfo) unmarshalBinaryBedrock(data []byte) error { | |
| // | 32 | BlockHash | | ||
| // | 32 | BatcherHash | | ||
| // +---------+--------------------------+ | ||
|
|
||
| // Marshal Ecotone and Isthmus | ||
| func (info *L1BlockInfo) marshalBinaryEcotone() ([]byte, error) { | ||
| w := bytes.NewBuffer(make([]byte, 0, L1InfoEcotoneLen)) | ||
| if err := solabi.WriteSignature(w, L1InfoFuncEcotoneBytes4); err != nil { | ||
| out, err := marshalBinaryWithSignature(info, L1InfoFuncEcotoneBytes4) | ||
| if err != nil { | ||
| return nil, fmt.Errorf("failed to marshal Ecotone l1 block info: %w", err) | ||
| } | ||
| return out, nil | ||
| } | ||
| func (info *L1BlockInfo) marshalBinaryIsthmus() ([]byte, error) { | ||
| out, err := marshalBinaryWithSignature(info, L1InfoFuncIsthmusBytes4) | ||
| if err != nil { | ||
| return nil, fmt.Errorf("failed to marshal Isthmus l1 block info: %w", err) | ||
| } | ||
| return out, nil | ||
| } | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. nit: no newline
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @tynes not sure what you mean. do you want a new-line? the other set of 3 |
||
| func marshalBinaryWithSignature(info *L1BlockInfo, signature []byte) ([]byte, error) { | ||
| w := bytes.NewBuffer(make([]byte, 0, L1InfoEcotoneLen)) // Ecotone and Isthmus have the same length | ||
| if err := solabi.WriteSignature(w, signature); err != nil { | ||
| return nil, err | ||
| } | ||
| if err := binary.Write(w, binary.BigEndian, info.BaseFeeScalar); err != nil { | ||
|
|
@@ -200,14 +219,21 @@ func (info *L1BlockInfo) marshalBinaryEcotone() ([]byte, error) { | |
| return w.Bytes(), nil | ||
| } | ||
|
|
||
| // Unmarshal Ecotone and Isthmus | ||
| func (info *L1BlockInfo) unmarshalBinaryEcotone(data []byte) error { | ||
| return unmarshalBinaryWithSignatureAndData(info, L1InfoFuncEcotoneBytes4, data) | ||
| } | ||
| func (info *L1BlockInfo) unmarshalBinaryIsthmus(data []byte) error { | ||
| return unmarshalBinaryWithSignatureAndData(info, L1InfoFuncIsthmusBytes4, 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 { | ||
|
|
@@ -250,9 +276,24 @@ 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 { | ||
| // note from Proto: | ||
| // Since we use the pre-interop L1 tx one last time during the upgrade block, | ||
| // we must disallow the deposit-txs from using the CrossL2Inbox during this block. | ||
| // If the CrossL2Inbox does not exist yet, then it is safe, | ||
| // but we have to ensure that the spec and code puts any Interop upgrade-txs after the user deposits. | ||
| return rollupCfg.IsInterop(l2BlockTime) && !rollupCfg.IsInteropActivationBlock(l2BlockTime) | ||
| } | ||
|
skeletor-spaceman marked this conversation as resolved.
|
||
|
|
||
| // 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) { | ||
|
skeletor-spaceman marked this conversation as resolved.
|
||
| return &info, info.unmarshalBinaryIsthmus(data) | ||
| } | ||
| if isEcotoneButNotFirstBlock(rollupCfg, l2BlockTime) { | ||
| return &info, info.unmarshalBinaryEcotone(data) | ||
| } | ||
|
|
@@ -271,6 +312,7 @@ func L1InfoDeposit(rollupCfg *rollup.Config, sysCfg eth.SystemConfig, seqNumber | |
| BatcherAddr: sysCfg.BatcherAddr, | ||
| } | ||
| var data []byte | ||
|
|
||
|
skeletor-spaceman marked this conversation as resolved.
|
||
| if isEcotoneButNotFirstBlock(rollupCfg, l2BlockTime) { | ||
| l1BlockInfo.BlobBaseFee = block.BlobBaseFee() | ||
| if l1BlockInfo.BlobBaseFee == nil { | ||
|
|
@@ -283,11 +325,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 isInteropButNotFirstBlock(rollupCfg, l2BlockTime) { | ||
|
skeletor-spaceman marked this conversation as resolved.
|
||
| out, err := l1BlockInfo.marshalBinaryIsthmus() | ||
| if err != nil { | ||
| return nil, fmt.Errorf("failed to marshal Isthmus l1 block info: %w", err) | ||
| } | ||
| data = out | ||
| } else { | ||
| out, err := l1BlockInfo.marshalBinaryEcotone() | ||
| if err != nil { | ||
| return nil, fmt.Errorf("failed to marshal Ecotone l1 block info: %w", err) | ||
| } | ||
| data = out | ||
| } | ||
| data = out | ||
| } else { | ||
| l1BlockInfo.L1FeeOverhead = sysCfg.Overhead | ||
| l1BlockInfo.L1FeeScalar = sysCfg.Scalar | ||
|
|
@@ -335,3 +385,33 @@ func L1InfoDepositBytes(rollupCfg *rollup.Config, sysCfg eth.SystemConfig, seqNu | |
| } | ||
| return opaqueL1Tx, nil | ||
| } | ||
|
|
||
| func DepositsCompleteDeposit(seqNumber uint64, block eth.BlockInfo) (*types.DepositTx, error) { | ||
| source := DepositSource{ | ||
| L1BlockHash: block.Hash(), | ||
| } | ||
| out := &types.DepositTx{ | ||
| SourceHash: source.SourceHash(), | ||
| From: L1InfoDepositerAddress, | ||
| To: &L1BlockAddress, | ||
| Mint: nil, | ||
| Value: big.NewInt(0), | ||
| Gas: 50_000, // TODO: check how much gas is actually needed | ||
|
skeletor-spaceman marked this conversation as resolved.
Outdated
|
||
| 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 | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -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 | ||
|
|
@@ -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; | ||
|
|
||
| /// @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"; | ||
| } | ||
|
|
||
| /// @notice Returns the gas paying token, its decimals, name and symbol. | ||
|
|
@@ -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. | ||
|
|
@@ -97,6 +112,7 @@ contract L1Block is ISemver, IGasToken { | |
| /// @param _batcherHash Versioned hash to authenticate batcher by. | ||
| /// @param _l1FeeOverhead L1 fee overhead. | ||
| /// @param _l1FeeScalar L1 fee scalar. | ||
| /// @param _isDeposit isDeposit flag | ||
| function setL1BlockValues( | ||
| uint64 _number, | ||
| uint64 _timestamp, | ||
|
|
@@ -105,7 +121,8 @@ contract L1Block is ISemver, IGasToken { | |
| uint64 _sequenceNumber, | ||
| bytes32 _batcherHash, | ||
| uint256 _l1FeeOverhead, | ||
| uint256 _l1FeeScalar | ||
| uint256 _l1FeeScalar, | ||
| bool _isDeposit | ||
|
skeletor-spaceman marked this conversation as resolved.
Outdated
|
||
| ) | ||
| external | ||
| { | ||
|
|
@@ -119,6 +136,21 @@ contract L1Block is ISemver, IGasToken { | |
| batcherHash = _batcherHash; | ||
| l1FeeOverhead = _l1FeeOverhead; | ||
| l1FeeScalar = _l1FeeScalar; | ||
| assembly { | ||
| sstore(IS_DEPOSIT_SLOT, _isDeposit) | ||
| } | ||
| } | ||
|
|
||
| /// @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. | ||
| /// It forwards the calldata to the internally-used `setL1BlockValuesEcotone` function. | ||
| function setL1BlockValuesIsthmus() external { | ||
|
skeletor-spaceman marked this conversation as resolved.
Outdated
|
||
| // Set the isDeposit flag to true. | ||
| assembly { | ||
| sstore(IS_DEPOSIT_SLOT, 1) | ||
| } | ||
|
|
||
| setL1BlockValuesEcotone(); | ||
| } | ||
|
|
||
| /// @notice Updates the L1 block values for an Ecotone upgraded chain. | ||
|
|
@@ -133,7 +165,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 { | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Changing it to Maybe instead instead we can change it to
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I like having a shared internal while keeping both functions external, what do you think @tynes ?
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. To address this, we should follow this design doc and introduce a |
||
| address depositor = DEPOSITOR_ACCOUNT(); | ||
| assembly { | ||
| // Revert if the caller is not the depositor account. | ||
|
|
@@ -152,6 +184,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 { // TODO modify spec or modify functionNaming | ||
| 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. | ||
|
|
@@ -162,4 +205,5 @@ contract L1Block is ISemver, IGasToken { | |
|
|
||
| emit GasPayingTokenSet({ token: _token, decimals: _decimals, name: _name, symbol: _symbol }); | ||
| } | ||
|
|
||
| } | ||
Uh oh!
There was an error while loading. Please reload this page.