Skip to content

Commit e69bb44

Browse files
0xValeraStanislavBreadlesskelemenozksync[bot]
authored
feat: add migration intervals tracking and verifying settlement layer (#1993)
# What ❔ <!-- What are the changes this PR brings about? --> <!-- Example: This PR adds a PR template to the repo. --> <!-- (For bigger PRs adding more context is appreciated) --> ## Why ❔ <!-- Why are these changes done? What goal do they contribute to? What are the principles behind them? --> <!-- Example: PR templates ensure PR reviewers, observers, and future iterators are in context about the evolution of repos. --> ## Checklist <!-- Check your PR fulfills the following items. --> <!-- For draft PRs check the boxes as you complete them. --> - [ ] PR title corresponds to the body of PR (we generate changelog entries from PRs). - [ ] Tests for the changes have been added / updated. - [ ] Documentation comments have been added / updated. --------- Co-authored-by: Stanislav Breadless <stanislavbezkor@gmail.com> Co-authored-by: kelemeno <34402761+kelemeno@users.noreply.github.com> Co-authored-by: zksync[bot] <zksync[bot]@users.noreply.github.com>
1 parent 469a4fd commit e69bb44

File tree

48 files changed

+1766
-951
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

48 files changed

+1766
-951
lines changed

AllContractsHashes.json

Lines changed: 396 additions & 396 deletions
Large diffs are not rendered by default.

l1-contracts/AGENTS.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,12 @@ address result = _tryAddress(target, "someFunction()");
4040
- They mask initialization issues and timing problems
4141
- The codebase should fail fast and clearly, not silently return defaults
4242

43+
### Constructors and Immutables
44+
45+
- ONLY contracts deployed on L1 should have immutables. Contracts on L2 are deployed within zksync os environment and so and so DO NOT SUPPORT CONSTRUCTORS ALL (and so no immutable can be set). It is important that the `*Base` contracts that the L2 contracts inherit from dont have immutables or constructors too.
46+
- If you want to add an immutable for L1, always double check whether it is possible to deterministically obtain from other contracts.
47+
- If there is variable that can be an immutable on L1, but we need a similar field on L2, a common pattern is to create a method in the base contract that can be inherited by both. On L2 it can be either a constant (esp if it is an L2 built-in contract address) or a storage variable that must be initialized within during the genesis. For example, look how `initL2` functions are used.
48+
4349
## Debugging Strategies
4450

4551
When debugging Solidity compilation or script failures:

l1-contracts/contracts/common/Config.sol

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -290,6 +290,15 @@ PubdataPricingMode constant DEFAULT_PUBDATA_PRICING_MODE = PubdataPricingMode.Ro
290290
/// @dev Default maximum gas limit for priority transactions during chain creation.
291291
uint64 constant DEFAULT_PRIORITY_TX_MAX_GAS_LIMIT = 72_000_000;
292292

293+
/// @dev Migration number used when a chain migrates from L1 to a settlement layer.
294+
uint256 constant MIGRATION_NUMBER_L1_TO_SETTLEMENT_LAYER = 1;
295+
296+
/// @dev Migration number used when a chain returns from a settlement layer back to L1.
297+
uint256 constant MIGRATION_NUMBER_SETTLEMENT_LAYER_TO_L1 = 2;
298+
299+
/// @dev Iterated migrations are not supported; chain can migrate only to settlement layer and back once.
300+
uint256 constant MAX_ALLOWED_NUMBER_OF_MIGRATIONS = 2;
301+
293302
/// @dev The mask that should be applied to the packed log data containing both the number of L2 and L1 transactions
294303
/// processed in the batch. Applying this mask is equivalent to calculating modulo 2**128.
295304
uint256 constant PACKED_NUMBER_OF_L1_TRANSACTIONS_LOG_MASK = 0xffffffffffffffffffffffffffffffff;

l1-contracts/contracts/common/L1ContractErrors.sol

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,8 +37,6 @@ error BadTransferDataLength();
3737
error BaseTokenGasPriceDenominatorNotSet();
3838
// 0x55ad3fd3
3939
error BatchHashMismatch(bytes32 expected, bytes32 actual);
40-
// 0x2078a6a0
41-
error BatchNotExecuted(uint256 batchNumber);
4240
// 0xbd4455ff
4341
error BatchNumberMismatch(uint256 expectedBatchNumber, uint256 providedBatchNumber);
4442
// 0x6cf12312

l1-contracts/contracts/core/bridgehub/BridgehubBase.sol

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -87,8 +87,10 @@ abstract contract BridgehubBase is IBridgehubBase, ReentrancyGuard, Ownable2Step
8787
/// @dev used to indicate the currently active settlement layer for a given chainId
8888
mapping(uint256 chainId => uint256 activeSettlementLayerChainId) public settlementLayer;
8989

90-
/// @notice shows whether the given chain can be used as a settlement layer.
91-
/// @dev the Gateway will be one of the possible settlement layers. The L1 is also a settlement layer.
90+
/// @notice Shows whether a chain can currently be selected as a migration target settlement layer.
91+
/// @dev This does NOT represent historical settlement layers used in message proof verification.
92+
/// @dev Historical settlement layer assignments are tracked in ChainAssetHandler `_migrationInterval`.
93+
/// @dev The Gateway will be one of the possible settlement layers. L1 is also a settlement layer.
9294
/// @dev Sync layer chain is expected to have .. as the base token.
9395
mapping(uint256 chainId => bool isWhitelistedSettlementLayer) public whitelistedSettlementLayers;
9496

l1-contracts/contracts/core/bridgehub/L1BridgehubErrors.sol

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -18,20 +18,26 @@ error ChainExists();
1818
error CurrentBatchNumberAlreadySet();
1919
// 0x68d91b49
2020
error DepthMoreThanOneForRecursiveMerkleProof();
21+
// 0xd9d3fc89
22+
error HistoricalSettlementLayerMismatch(uint256 expectedSettlementLayer, uint256 actualSettlementLayer);
2123
// 0x48857c1d
2224
error IncorrectChainAssetId(bytes32 assetId, bytes32 assetIdFromChainId);
2325
// 0xf5e39c1f
2426
error IncorrectSender(address prevMsgSender, address chainAdmin);
27+
// 0x896555dc
28+
error InvalidSettlementLayerForBatch(uint256 chainId, uint256 batchNumber, uint256 claimedSettlementLayer);
2529
// 0x47d42b1b
2630
error IteratedMigrationsNotSupported();
2731
// 0xc3bd3c65
2832
error LocallyNoChainsAtGenesis();
2933
// 0x913183d8
3034
error MessageRootNotRegistered();
35+
// 0x338fe0e7
36+
error MigrationIntervalInvalid();
37+
// 0x81c5808d
38+
error MigrationIntervalNotSet();
3139
// 0x4010a88d
3240
error MigrationNotToL1();
33-
// 0x12b08c62
34-
error MigrationNumberAlreadySet();
3541
// 0xde1362a2
3642
error MigrationNumberMismatch(uint256 _expected, uint256 _actual);
3743
// 0x7f4316f3
@@ -56,8 +62,6 @@ error NotOwnerViaRouter(address msgSender, address originalCaller);
5662
error NotRelayedSender(address msgSender, address settlementLayerRelaySender);
5763
// 0xb35a7373
5864
error NotSystemContext(address _sender);
59-
// 0xb30ebfd8
60-
error NotWhitelistedSettlementLayer(uint256 chainId);
6165
// 0x3db511f4
6266
error OnlyAssetTracker(address, address);
6367
// 0x527b87c7
@@ -68,10 +72,14 @@ error OnlyBridgehubOrChainAssetHandler(address sender, address bridgehub, addres
6872
error OnlyChain(address msgSender, address zkChainAddress);
6973
// 0xec76af13
7074
error OnlyGateway();
75+
// 0x8d14ca84
76+
error OnlyL1();
7177
// 0x6b75db8c
7278
error OnlyOnSettlementLayer();
7379
// 0xb78dbaa7
7480
error SecondBridgeAddressTooLow(address secondBridgeAddress, address minSecondBridgeAddress);
81+
// 0xefb272e2
82+
error SettlementLayerMustNotBeL1();
7583
// 0x36917565
7684
error SLHasDifferentCTM();
7785
// 0x90c7cbf1

l1-contracts/contracts/core/chain-asset-handler/ChainAssetHandlerBase.sol

Lines changed: 104 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,8 @@ import {IAssetRouterBase} from "../../bridge/asset-router/IAssetRouterBase.sol";
1818
import {IL1AssetRouter} from "../../bridge/asset-router/IL1AssetRouter.sol";
1919
import {INativeTokenVaultBase} from "../../bridge/ntv/INativeTokenVaultBase.sol";
2020

21-
import {L1_SETTLEMENT_LAYER_VIRTUAL_ADDRESS} from "../../common/Config.sol";
22-
import {IncorrectChainAssetId, IncorrectSender, MigrationNotToL1, MigrationNumberAlreadySet, MigrationNumberMismatch, NotSystemContext, OnlyChain, SLHasDifferentCTM, ZKChainNotRegistered, IteratedMigrationsNotSupported} from "../bridgehub/L1BridgehubErrors.sol";
21+
import {L1_SETTLEMENT_LAYER_VIRTUAL_ADDRESS, MIGRATION_NUMBER_L1_TO_SETTLEMENT_LAYER, MIGRATION_NUMBER_SETTLEMENT_LAYER_TO_L1, MAX_ALLOWED_NUMBER_OF_MIGRATIONS} from "../../common/Config.sol";
22+
import {IncorrectChainAssetId, IncorrectSender, MigrationNotToL1, MigrationNumberMismatch, NotSystemContext, OnlyChain, SLHasDifferentCTM, ZKChainNotRegistered, IteratedMigrationsNotSupported} from "../bridgehub/L1BridgehubErrors.sol";
2323
import {ChainIdNotRegistered, MigrationPaused, NotAssetRouter} from "../../common/L1ContractErrors.sol";
2424
import {L2_SYSTEM_CONTEXT_SYSTEM_CONTRACT_ADDR} from "../../common/l2-helpers/L2ContractAddresses.sol";
2525

@@ -76,6 +76,9 @@ abstract contract ChainAssetHandlerBase is
7676
IAssetRouterBase internal DEPRECATED_ASSET_ROUTER;
7777

7878
/// @notice Used to track the number of times each chain has migrated.
79+
/// @dev It is assumed that during the release of the v31 upgrade all chains settle on L1,
80+
/// so they will all start with `migrationNumber` equal to 0. Note, that ZKsync Era that used to settle on ZK Gateway
81+
/// will also start with migration number equal to 0.
7982
/// NOTE: this mapping may be deprecated in the future, don't rely on it!
8083
mapping(uint256 chainId => uint256 migrationNumber) public migrationNumber;
8184

@@ -122,18 +125,6 @@ abstract contract ChainAssetHandlerBase is
122125
_;
123126
}
124127

125-
/// @notice Sets the migration number for a chain on the Gateway when the chain's DiamondProxy upgrades.
126-
function setMigrationNumberForV31(uint256 _chainId) external onlyChain(_chainId) {
127-
require(migrationNumber[_chainId] == 0, MigrationNumberAlreadySet());
128-
bool isOnThisSettlementLayer = block.chainid == IBridgehubBase(_bridgehub()).settlementLayer(_chainId);
129-
bool shouldIncrementMigrationNumber = (isOnThisSettlementLayer && block.chainid != _l1ChainId()) ||
130-
(!isOnThisSettlementLayer && block.chainid == _l1ChainId());
131-
/// Note we don't increment the migration number if the chain migrated to GW and back to L1 previously.
132-
if (shouldIncrementMigrationNumber) {
133-
migrationNumber[_chainId] = 1;
134-
}
135-
}
136-
137128
/*//////////////////////////////////////////////////////////////
138129
Chain migration
139130
//////////////////////////////////////////////////////////////*/
@@ -161,21 +152,19 @@ abstract contract ChainAssetHandlerBase is
161152
returns (bytes memory bridgehubMintData)
162153
{
163154
BridgehubBurnCTMAssetData memory bridgehubBurnData = abi.decode(_data, (BridgehubBurnCTMAssetData));
155+
uint256 chainId = bridgehubBurnData.chainId;
164156
require(
165-
_assetId == IBridgehubBase(_bridgehub()).ctmAssetIdFromChainId(bridgehubBurnData.chainId),
166-
IncorrectChainAssetId(
167-
_assetId,
168-
IBridgehubBase(_bridgehub()).ctmAssetIdFromChainId(bridgehubBurnData.chainId)
169-
)
157+
_assetId == IBridgehubBase(_bridgehub()).ctmAssetIdFromChainId(chainId),
158+
IncorrectChainAssetId(_assetId, IBridgehubBase(_bridgehub()).ctmAssetIdFromChainId(chainId))
170159
);
171-
address zkChain = IBridgehubBase(_bridgehub()).getZKChain(bridgehubBurnData.chainId);
160+
address zkChain = IBridgehubBase(_bridgehub()).getZKChain(chainId);
172161

173162
bytes memory ctmMintData;
174163
// to avoid stack too deep
175164
{
176165
address ctm;
177166
(zkChain, ctm) = IBridgehubBase(_bridgehub()).forwardedBridgeBurnSetSettlementLayer(
178-
bridgehubBurnData.chainId,
167+
chainId,
179168
_settlementChainId
180169
);
181170

@@ -186,10 +175,7 @@ abstract contract ChainAssetHandlerBase is
186175
revert IncorrectSender(_originalCaller, IZKChain(zkChain).getAdmin());
187176
}
188177

189-
ctmMintData = IChainTypeManager(ctm).forwardedBridgeBurn(
190-
bridgehubBurnData.chainId,
191-
bridgehubBurnData.ctmData
192-
);
178+
ctmMintData = IChainTypeManager(ctm).forwardedBridgeBurn(chainId, bridgehubBurnData.ctmData);
193179

194180
// For security reasons, chain migration is temporarily restricted to settlement layers with the same CTM
195181
if (
@@ -202,23 +188,87 @@ abstract contract ChainAssetHandlerBase is
202188
if (block.chainid != _l1ChainId()) {
203189
require(_settlementChainId == _l1ChainId(), MigrationNotToL1());
204190
}
205-
_setMigrationInProgressOnL1(bridgehubBurnData.chainId);
191+
_setMigrationInProgressOnL1(chainId);
206192
}
207-
bytes memory chainMintData = IZKChain(zkChain).forwardedBridgeBurn(
193+
// to avoid stack too deep
194+
bridgehubMintData = _finalizeBridgeBurn({
195+
_chainId: chainId,
196+
_settlementChainId: _settlementChainId,
197+
_assetId: _assetId,
198+
_zkChain: zkChain,
199+
_originalCaller: _originalCaller,
200+
_ctmMintData: ctmMintData,
201+
_chainData: bridgehubBurnData.chainData
202+
});
203+
}
204+
205+
/// @dev Handles chain burn, migration bookkeeping, and builds the bridgehub mint data.
206+
/// @dev Extracted from bridgeBurn to avoid stack-too-deep.
207+
function _finalizeBridgeBurn(
208+
uint256 _chainId,
209+
uint256 _settlementChainId,
210+
bytes32 _assetId,
211+
address _zkChain,
212+
address _originalCaller,
213+
bytes memory _ctmMintData,
214+
bytes memory _chainData
215+
) internal returns (bytes memory) {
216+
bytes memory chainMintData = IZKChain(_zkChain).forwardedBridgeBurn(
208217
_settlementChainId == _l1ChainId()
209218
? L1_SETTLEMENT_LAYER_VIRTUAL_ADDRESS
210219
: IBridgehubBase(_bridgehub()).getZKChain(_settlementChainId),
211220
_originalCaller,
212-
bridgehubBurnData.chainData
221+
_chainData
213222
);
223+
uint256 currentMigrationNum = migrationNumber[_chainId];
214224
// Iterated migrations are not supported to avoid asset migration number complications related to token balance migration.
215225
// This means a chain can migrate to GW and back to L1 but only once.
216-
require(migrationNumber[bridgehubBurnData.chainId] < 2, IteratedMigrationsNotSupported());
217-
++migrationNumber[bridgehubBurnData.chainId];
226+
require(currentMigrationNum < MAX_ALLOWED_NUMBER_OF_MIGRATIONS, IteratedMigrationsNotSupported());
227+
++currentMigrationNum;
228+
migrationNumber[_chainId] = currentMigrationNum;
218229

219-
uint256 batchNumber = IMessageRoot(_messageRoot()).currentChainBatchNumber(bridgehubBurnData.chainId);
230+
uint256 batchNumber = IMessageRoot(_messageRoot()).currentChainBatchNumber(_chainId);
220231

221-
bytes32 assetId = IBridgehubBase(_bridgehub()).baseTokenAssetId(bridgehubBurnData.chainId);
232+
// Track migration interval for settlement layer validation.
233+
// When migrating FROM L1 TO a settlement layer, record the last L1 batch number and the SL chain ID.
234+
if (block.chainid == _l1ChainId()) {
235+
_recordMigrationToSL(_chainId, _settlementChainId, batchNumber, currentMigrationNum);
236+
}
237+
238+
bytes memory bridgehubMintData = _buildBridgehubMintData({
239+
_chainId: _chainId,
240+
_batchNumber: batchNumber,
241+
_ctmMintData: _ctmMintData,
242+
_chainMintData: chainMintData,
243+
_currentMigrationNum: currentMigrationNum
244+
});
245+
246+
emit MigrationStarted(_chainId, currentMigrationNum, _assetId, _settlementChainId);
247+
248+
return bridgehubMintData;
249+
}
250+
251+
function _recordMigrationToSL(
252+
uint256 _chainId,
253+
uint256 _settlementChainId,
254+
uint256 _batchNumber,
255+
uint256 _currentMigrationNum
256+
) internal virtual;
257+
258+
function _recordMigrationFromSL(
259+
uint256 _chainId,
260+
uint256 _batchNumber,
261+
uint256 _currentMigrationNum
262+
) internal virtual;
263+
264+
function _buildBridgehubMintData(
265+
uint256 _chainId,
266+
uint256 _batchNumber,
267+
bytes memory _ctmMintData,
268+
bytes memory _chainMintData,
269+
uint256 _currentMigrationNum
270+
) internal view returns (bytes memory) {
271+
bytes32 assetId = IBridgehubBase(_bridgehub()).baseTokenAssetId(_chainId);
222272
TokenBridgingData memory baseTokenBridgingData = TokenBridgingData({
223273
assetId: assetId,
224274
originToken: address(0),
@@ -233,22 +283,17 @@ abstract contract ChainAssetHandlerBase is
233283
baseTokenBridgingData.originChainId = l1Ntv.originChainId(assetId);
234284
}
235285

236-
BridgehubMintCTMAssetData memory bridgeMintStruct = BridgehubMintCTMAssetData({
237-
chainId: bridgehubBurnData.chainId,
238-
baseTokenBridgingData: baseTokenBridgingData,
239-
batchNumber: batchNumber,
240-
ctmData: ctmMintData,
241-
chainData: chainMintData,
242-
migrationNumber: migrationNumber[bridgehubBurnData.chainId]
243-
});
244-
bridgehubMintData = abi.encode(bridgeMintStruct);
245-
246-
emit MigrationStarted(
247-
bridgehubBurnData.chainId,
248-
migrationNumber[bridgehubBurnData.chainId],
249-
_assetId,
250-
_settlementChainId
251-
);
286+
return
287+
abi.encode(
288+
BridgehubMintCTMAssetData({
289+
chainId: _chainId,
290+
baseTokenBridgingData: baseTokenBridgingData,
291+
batchNumber: _batchNumber,
292+
ctmData: _ctmMintData,
293+
chainData: _chainMintData,
294+
migrationNumber: _currentMigrationNum
295+
})
296+
);
252297
}
253298

254299
function _setMigrationInProgressOnL1(uint256 _chainId) internal virtual {}
@@ -271,11 +316,20 @@ abstract contract ChainAssetHandlerBase is
271316
/// If we are not migrating for the first time, we check that the migration number is correct.
272317
if (currentMigrationNumber != 0 && block.chainid == _l1ChainId()) {
273318
require(
274-
currentMigrationNumber + 1 == bridgehubMintData.migrationNumber,
275-
MigrationNumberMismatch(currentMigrationNumber + 1, bridgehubMintData.migrationNumber)
319+
currentMigrationNumber == MIGRATION_NUMBER_L1_TO_SETTLEMENT_LAYER,
320+
MigrationNumberMismatch(MIGRATION_NUMBER_L1_TO_SETTLEMENT_LAYER, currentMigrationNumber)
321+
);
322+
require(
323+
bridgehubMintData.migrationNumber == MIGRATION_NUMBER_SETTLEMENT_LAYER_TO_L1,
324+
MigrationNumberMismatch(MIGRATION_NUMBER_SETTLEMENT_LAYER_TO_L1, bridgehubMintData.migrationNumber)
276325
);
277326
}
278327
migrationNumber[bridgehubMintData.chainId] = bridgehubMintData.migrationNumber;
328+
_recordMigrationFromSL(
329+
bridgehubMintData.chainId,
330+
bridgehubMintData.batchNumber,
331+
bridgehubMintData.migrationNumber
332+
);
279333

280334
(address zkChain, address ctm) = IBridgehubBase(_bridgehub()).forwardedBridgeMint(
281335
_assetId,

l1-contracts/contracts/core/chain-asset-handler/IChainAssetHandler.sol

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,18 @@ pragma solidity ^0.8.24;
44

55
import {IAssetHandler} from "../../bridge/interfaces/IAssetHandler.sol";
66

7+
/// @notice Tracks migration batch numbers for a chain that migrated to a settlement layer and back.
8+
/// @param migrateToGWBatchNumber The last batch executed on L1 before migrating TO the gateway.
9+
/// @param migrateFromGWBatchNumber The last batch executed on Gateway before migrating back to L1.
10+
/// @param settlementLayerChainId The chain ID of the settlement layer where the chain settled during the time period.
11+
/// @param isActive Whether the chain is actively settling on the settlement layer right now.
12+
struct MigrationInterval {
13+
uint256 migrateToGWBatchNumber;
14+
uint256 migrateFromGWBatchNumber;
15+
uint256 settlementLayerChainId;
16+
bool isActive;
17+
}
18+
719
/// @author Matter Labs
820
/// @custom:security-contact security@matterlabs.dev
921
interface IChainAssetHandler is IAssetHandler {
@@ -33,8 +45,6 @@ interface IChainAssetHandler is IAssetHandler {
3345

3446
function migrationNumber(uint256 _chainId) external view returns (uint256);
3547

36-
function setMigrationNumberForV31(uint256 _chainId) external;
37-
3848
/// @dev Denotes whether the migrations of chains is paused.
3949
function migrationPaused() external view returns (bool);
4050

0 commit comments

Comments
 (0)