Skip to content
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

Packed protocol version #492

Merged
merged 27 commits into from
May 29, 2024
Merged
Show file tree
Hide file tree
Changes from 16 commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
3e27dd2
semver for protocol version
StanislavBreadless May 23, 2024
5c61a50
adapt to work with server
StanislavBreadless May 23, 2024
504ddbf
fmt
StanislavBreadless May 24, 2024
cd2258f
add better checks
StanislavBreadless May 24, 2024
d67d456
cleanup + stricter rules
StanislavBreadless May 24, 2024
71c43d0
lint fix
StanislavBreadless May 24, 2024
d8def78
docs
StanislavBreadless May 24, 2024
8a28eb6
semver getter for STM
StanislavBreadless May 24, 2024
cc45658
restore hardhat unit tests + change how we display protocol version i…
StanislavBreadless May 24, 2024
f050432
Use new variable
perekopskiy May 24, 2024
61d4e39
Update l1-contracts/contracts/common/Config.sol
StanislavBreadless May 24, 2024
8fa6d1b
respond to some comments
StanislavBreadless May 24, 2024
0f3b853
sync with upstream
StanislavBreadless May 24, 2024
004bb38
Merge pull request #493 from matter-labs/mp-use-new-var-in-scripts
StanislavBreadless May 24, 2024
87ac548
update lib + safecast
StanislavBreadless May 24, 2024
cf215f1
Merge remote-tracking branch 'origin/sb-packed-protocol-version' into…
StanislavBreadless May 24, 2024
9f8144e
add test for migration to the new version
StanislavBreadless May 24, 2024
d92a34d
Update l1-contracts/contracts/upgrades/BaseZkSyncUpgrade.sol
StanislavBreadless May 24, 2024
c566f89
spellcheck
StanislavBreadless May 24, 2024
579d813
lint fix
StanislavBreadless May 24, 2024
06e3466
better test coverage for upgrade
StanislavBreadless May 24, 2024
1b11605
cover with more tests
StanislavBreadless May 24, 2024
7f184bf
typos
StanislavBreadless May 24, 2024
e255f91
add a todo for future
StanislavBreadless May 24, 2024
25d90f9
fixes
StanislavBreadless May 24, 2024
9647526
resolve nit
StanislavBreadless May 24, 2024
5f074a8
fix slither
StanislavBreadless May 24, 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
5 changes: 3 additions & 2 deletions l1-contracts/contracts/common/Config.sol
Original file line number Diff line number Diff line change
Expand Up @@ -27,10 +27,11 @@ uint256 constant PRIORITY_OPERATION_L2_TX_TYPE = 255;
/// @dev Denotes the type of the zkSync transaction that is used for system upgrades.
uint256 constant SYSTEM_UPGRADE_L2_TX_TYPE = 254;

/// @dev The maximal allowed difference between protocol versions in an upgrade. The 100 gap is needed
/// @dev The maximal allowed difference between protocol minor versions in an upgrade. The 100 gap is needed
/// in case a protocol version has been tested on testnet, but then not launched on mainnet, e.g.
/// due to a bug found.
uint256 constant MAX_ALLOWED_PROTOCOL_VERSION_DELTA = 100;
/// We are allowed to jump at most 100 minor versions at a time. The major version is always expected to be 0.
uint256 constant MAX_ALLOWED_MINOR_VERSION_DELTA = 100;

/// @dev The amount of time in seconds the validator has to process the priority transaction
/// NOTE: The constant is set to zero for the Alpha release period
Expand Down
32 changes: 32 additions & 0 deletions l1-contracts/contracts/common/libraries/SemVer.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
// SPDX-License-Identifier: MIT

pragma solidity 0.8.24;

/// @dev The number of bits dedicated to the "patch" portion of the protocol version.
/// This also defines the bit starting from which the "minor" part is located.
uint256 constant SEMVER_MINOR_OFFSET = 32;

/// @dev The number of bits dedicated to the "patch" and "minor" portions of the protocol version.
/// This also defines the bit starting from which the "major" part is located.
/// Note, that currently, only major version of "0" is supported.
uint256 constant SEMVER_MAJOR_OFFSET = 64;

/**
* @author Matter Labs
* @custom:security-contact [email protected]
* @notice The library for managing SemVer for the protocol version.
*/
library SemVer {
/// @notice Unpacks the SemVer version from a single uint256 into major, minor and patch components.
/// @param _packedProtocolVersion The packed protocol version.
/// @return major The major version.
/// @return minor The minor version.
/// @return patch The patch version.
function unpackSemVer(
uint96 _packedProtocolVersion
) internal pure returns (uint32 major, uint32 minor, uint32 patch) {
patch = uint32(_packedProtocolVersion);
minor = uint32(_packedProtocolVersion >> SEMVER_MINOR_OFFSET);
major = uint32(_packedProtocolVersion >> SEMVER_MAJOR_OFFSET);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -28,16 +28,17 @@ contract CustomUpgradeTest is BaseZkSyncUpgrade {
/// @notice The main function that will be called by the upgrade proxy.
/// @param _proposedUpgrade The upgrade to be executed.
function upgrade(ProposedUpgrade calldata _proposedUpgrade) public override returns (bytes32) {
_setNewProtocolVersion(_proposedUpgrade.newProtocolVersion);
(uint32 newMinorVersion, bool isPatchOnly) = _setNewProtocolVersion(_proposedUpgrade.newProtocolVersion);
_upgradeL1Contract(_proposedUpgrade.l1ContractsUpgradeCalldata);
_upgradeVerifier(_proposedUpgrade.verifier, _proposedUpgrade.verifierParams);
_setBaseSystemContracts(_proposedUpgrade.bootloaderHash, _proposedUpgrade.defaultAccountHash);
_setBaseSystemContracts(_proposedUpgrade.bootloaderHash, _proposedUpgrade.defaultAccountHash, isPatchOnly);

bytes32 txHash;
txHash = _setL2SystemContractUpgrade(
_proposedUpgrade.l2ProtocolUpgradeTx,
_proposedUpgrade.factoryDeps,
_proposedUpgrade.newProtocolVersion
newMinorVersion,
isPatchOnly
);

_postUpgrade(_proposedUpgrade.postUpgradeCalldata);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -131,4 +131,6 @@ interface IStateTransitionManager {
uint256 _oldProtocolVersion,
Diamond.DiamondCutData calldata _diamondCut
) external;

function getSemverProtocolVersion() external view returns (uint32, uint32, uint32);
StanislavBreadless marked this conversation as resolved.
Show resolved Hide resolved
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
pragma solidity 0.8.24;

import {EnumerableMap} from "@openzeppelin/contracts/utils/structs/EnumerableMap.sol";
import {SafeCast} from "@openzeppelin/contracts/utils/math/SafeCast.sol";

import {Diamond} from "./libraries/Diamond.sol";
import {DiamondProxy} from "./chain-deps/DiamondProxy.sol";
Expand All @@ -21,6 +22,7 @@ import {ProposedUpgrade} from "../upgrades/BaseZkSyncUpgrade.sol";
import {ReentrancyGuard} from "../common/ReentrancyGuard.sol";
import {REQUIRED_L2_GAS_PRICE_PER_PUBDATA, L2_TO_L1_LOG_SERIALIZE_SIZE, DEFAULT_L2_LOGS_TREE_ROOT_HASH, EMPTY_STRING_KECCAK, SYSTEM_UPGRADE_L2_TX_TYPE, PRIORITY_TX_MAX_GAS_LIMIT} from "../common/Config.sol";
import {VerifierParams} from "./chain-interfaces/IVerifier.sol";
import {SemVer} from "../common/libraries/SemVer.sol";

/// @title State Transition Manager contract
/// @author Matter Labs
Expand All @@ -47,7 +49,7 @@ contract StateTransitionManager is IStateTransitionManager, ReentrancyGuard, Own
/// @dev The genesisUpgrade contract address, used to setChainId
address public genesisUpgrade;

/// @dev The current protocolVersion
/// @dev The current packed protocolVersion. To access human-readable version, use `getSemverProtocolVersion` function.
uint256 public protocolVersion;

/// @dev The timestamp when protocolVersion can be last used
Expand Down Expand Up @@ -84,6 +86,11 @@ contract StateTransitionManager is IStateTransitionManager, ReentrancyGuard, Own
_;
}

/// @return The tuple of (major, minor, patch) protocol version.
function getSemverProtocolVersion() external view returns (uint32, uint32, uint32) {
return SemVer.unpackSemVer(SafeCast.toUint96(protocolVersion));
}

/// @notice Returns all the registered hyperchain addresses
function getAllHyperchains() public view override returns (address[] memory chainAddresses) {
uint256[] memory keys = hyperchainMap.keys();
Expand Down Expand Up @@ -280,6 +287,10 @@ contract StateTransitionManager is IStateTransitionManager, ReentrancyGuard, Own
uint256[] memory uintEmptyArray;
bytes[] memory bytesEmptyArray;

uint256 cachedProtocolVersion = protocolVersion;
// slither-disable-next-line unused-return
(, uint32 minorVersion, ) = SemVer.unpackSemVer(SafeCast.toUint96(cachedProtocolVersion));

L2CanonicalTransaction memory l2ProtocolUpgradeTx = L2CanonicalTransaction({
txType: SYSTEM_UPGRADE_L2_TX_TYPE,
from: uint256(uint160(L2_FORCE_DEPLOYER_ADDR)),
Expand All @@ -289,8 +300,8 @@ contract StateTransitionManager is IStateTransitionManager, ReentrancyGuard, Own
maxFeePerGas: uint256(0),
maxPriorityFeePerGas: uint256(0),
paymaster: uint256(0),
// Note, that the protocol version is used as "nonce" for system upgrade transactions
nonce: protocolVersion,
// Note, that the `minor` of the protocol version is used as "nonce" for system upgrade transactions
nonce: uint256(minorVersion),
value: 0,
reserved: [uint256(0), 0, 0, 0],
data: systemContextCalldata,
Expand All @@ -314,7 +325,7 @@ contract StateTransitionManager is IStateTransitionManager, ReentrancyGuard, Own
l1ContractsUpgradeCalldata: new bytes(0),
postUpgradeCalldata: new bytes(0),
upgradeTimestamp: 0,
newProtocolVersion: protocolVersion
newProtocolVersion: cachedProtocolVersion
});

Diamond.FacetCut[] memory emptyArray;
Expand All @@ -325,7 +336,7 @@ contract StateTransitionManager is IStateTransitionManager, ReentrancyGuard, Own
});

IAdmin(_chainContract).executeUpgrade(cutData);
emit SetChainIdUpgrade(_chainContract, l2ProtocolUpgradeTx, protocolVersion);
emit SetChainIdUpgrade(_chainContract, l2ProtocolUpgradeTx, cachedProtocolVersion);
}

/// @dev used to register already deployed hyperchain contracts
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

pragma solidity 0.8.24;

import {SafeCast} from "@openzeppelin/contracts/utils/math/SafeCast.sol";

import {ZkSyncHyperchainBase} from "./ZkSyncHyperchainBase.sol";
import {PubdataPricingMode} from "../ZkSyncHyperchainStorage.sol";
import {VerifierParams} from "../../../state-transition/chain-interfaces/IVerifier.sol";
Expand All @@ -10,6 +12,7 @@ import {PriorityQueue, PriorityOperation} from "../../../state-transition/librar
import {UncheckedMath} from "../../../common/libraries/UncheckedMath.sol";
import {IGetters} from "../../chain-interfaces/IGetters.sol";
import {ILegacyGetters} from "../../chain-interfaces/ILegacyGetters.sol";
import {SemVer} from "../../../common/libraries/SemVer.sol";

// While formally the following import is not used, it is needed to inherit documentation from it
import {IZkSyncHyperchainBase} from "../../chain-interfaces/IZkSyncHyperchainBase.sol";
Expand Down Expand Up @@ -143,6 +146,11 @@ contract GettersFacet is ZkSyncHyperchainBase, IGetters, ILegacyGetters {
return s.protocolVersion;
}

/// @inheritdoc IGetters
function getSemverProtocolVersion() external view returns (uint32, uint32, uint32) {
return SemVer.unpackSemVer(SafeCast.toUint96(s.protocolVersion));
}

/// @inheritdoc IGetters
function getL2SystemContractsUpgradeTxHash() external view returns (bytes32) {
return s.l2SystemContractsUpgradeTxHash;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -85,9 +85,12 @@ interface IGetters is IZkSyncHyperchainBase {
/// @return Whether the diamond is frozen or not
function isDiamondStorageFrozen() external view returns (bool);

/// @return The current protocol version
/// @return The current packed protocol version. To access human-readable version, use `getSemverProtocolVersion` function.
function getProtocolVersion() external view returns (uint256);

/// @return The tuple of (major, minor, patch) protocol version.
function getSemverProtocolVersion() external view returns (uint32, uint32, uint32);

/// @return The upgrade system contract transaction hash, 0 if the upgrade is not initialized
function getL2SystemContractsUpgradeTxHash() external view returns (bytes32);

Expand Down
82 changes: 59 additions & 23 deletions l1-contracts/contracts/upgrades/BaseZkSyncUpgrade.sol
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,16 @@

pragma solidity 0.8.24;

import {SafeCast} from "@openzeppelin/contracts/utils/math/SafeCast.sol";

import {ZkSyncHyperchainBase} from "../state-transition/chain-deps/facets/ZkSyncHyperchainBase.sol";
import {VerifierParams} from "../state-transition/chain-interfaces/IVerifier.sol";
import {IVerifier} from "../state-transition/chain-interfaces/IVerifier.sol";
import {L2ContractHelper} from "../common/libraries/L2ContractHelper.sol";
import {TransactionValidator} from "../state-transition/libraries/TransactionValidator.sol";
import {MAX_NEW_FACTORY_DEPS, SYSTEM_UPGRADE_L2_TX_TYPE, MAX_ALLOWED_PROTOCOL_VERSION_DELTA} from "../common/Config.sol";
import {MAX_NEW_FACTORY_DEPS, SYSTEM_UPGRADE_L2_TX_TYPE, MAX_ALLOWED_MINOR_VERSION_DELTA} from "../common/Config.sol";
import {L2CanonicalTransaction} from "../common/Messaging.sol";
import {SemVer} from "../common/libraries/SemVer.sol";

/// @notice The struct that represents the upgrade proposal.
/// @param l2ProtocolUpgradeTx The system upgrade transaction.
Expand Down Expand Up @@ -70,15 +73,16 @@
// as the permitted delay window is reduced in the future.
require(block.timestamp >= _proposedUpgrade.upgradeTimestamp, "Upgrade is not ready yet");

_setNewProtocolVersion(_proposedUpgrade.newProtocolVersion);
(uint32 newMinorVersion, bool isPatchOnly) = _setNewProtocolVersion(_proposedUpgrade.newProtocolVersion);
_upgradeL1Contract(_proposedUpgrade.l1ContractsUpgradeCalldata);
_upgradeVerifier(_proposedUpgrade.verifier, _proposedUpgrade.verifierParams);
_setBaseSystemContracts(_proposedUpgrade.bootloaderHash, _proposedUpgrade.defaultAccountHash);
_setBaseSystemContracts(_proposedUpgrade.bootloaderHash, _proposedUpgrade.defaultAccountHash, isPatchOnly);

txHash = _setL2SystemContractUpgrade(
_proposedUpgrade.l2ProtocolUpgradeTx,
_proposedUpgrade.factoryDeps,
_proposedUpgrade.newProtocolVersion
newMinorVersion,
isPatchOnly
);

_postUpgrade(_proposedUpgrade.postUpgradeCalldata);
Expand All @@ -88,11 +92,14 @@

/// @notice Change default account bytecode hash, that is used on L2
/// @param _l2DefaultAccountBytecodeHash The hash of default account L2 bytecode
StanislavBreadless marked this conversation as resolved.
Show resolved Hide resolved
function _setL2DefaultAccountBytecodeHash(bytes32 _l2DefaultAccountBytecodeHash) private {
/// @param _patchOnly Whether the patch part of the protocol version semver has changed
function _setL2DefaultAccountBytecodeHash(bytes32 _l2DefaultAccountBytecodeHash, bool _patchOnly) private {
if (_l2DefaultAccountBytecodeHash == bytes32(0)) {
return;
}

require(!_patchOnly, "Patch only upgrade can not set new bootloader");
StanislavBreadless marked this conversation as resolved.
Show resolved Hide resolved

L2ContractHelper.validateBytecodeHash(_l2DefaultAccountBytecodeHash);

// Save previous value into the stack to put it into the event later
Expand All @@ -105,11 +112,14 @@

/// @notice Change bootloader bytecode hash, that is used on L2
/// @param _l2BootloaderBytecodeHash The hash of bootloader L2 bytecode
function _setL2BootloaderBytecodeHash(bytes32 _l2BootloaderBytecodeHash) private {
/// @param _patchOnly Whether the patch part of the protocol version semver has changed
function _setL2BootloaderBytecodeHash(bytes32 _l2BootloaderBytecodeHash, bool _patchOnly) private {
if (_l2BootloaderBytecodeHash == bytes32(0)) {
return;
}

require(!_patchOnly, "Patch only upgrade can not set new bootloader");

L2ContractHelper.validateBytecodeHash(_l2BootloaderBytecodeHash);

// Save previous value into the stack to put it into the event later
Expand Down Expand Up @@ -167,25 +177,32 @@
/// @notice Updates the bootloader hash and the hash of the default account
/// @param _bootloaderHash The hash of the new bootloader bytecode. If zero, it will not be updated.
/// @param _defaultAccountHash The hash of the new default account bytecode. If zero, it will not be updated.
function _setBaseSystemContracts(bytes32 _bootloaderHash, bytes32 _defaultAccountHash) internal {
StanislavBreadless marked this conversation as resolved.
Show resolved Hide resolved
_setL2BootloaderBytecodeHash(_bootloaderHash);
_setL2DefaultAccountBytecodeHash(_defaultAccountHash);
function _setBaseSystemContracts(bytes32 _bootloaderHash, bytes32 _defaultAccountHash, bool _patchOnly) internal {
_setL2BootloaderBytecodeHash(_bootloaderHash, _patchOnly);
_setL2DefaultAccountBytecodeHash(_defaultAccountHash, _patchOnly);
}

/// @notice Sets the hash of the L2 system contract upgrade transaction for the next batch to be committed
/// @dev If the transaction is noop (i.e. its type is 0) it does nothing and returns 0.
/// @param _l2ProtocolUpgradeTx The L2 system contract upgrade transaction.
/// @param _factoryDeps The factory dependencies that are used by the transaction.
/// @param _newMinorProtocolVersion The new minor protocol version. It must be used as the `nonce` field
/// of the `_l2ProtocolUpgradeTx`.
/// @param _patchOnly Whether the patch part of the protocol version semver has changed.
/// @return System contracts upgrade transaction hash. Zero if no upgrade transaction is set.
function _setL2SystemContractUpgrade(
L2CanonicalTransaction calldata _l2ProtocolUpgradeTx,
bytes[] calldata _factoryDeps,
uint256 _newProtocolVersion
uint32 _newMinorProtocolVersion,
bool _patchOnly
) internal returns (bytes32) {
// If the type is 0, it is considered as noop and so will not be required to be executed.
if (_l2ProtocolUpgradeTx.txType == 0) {
return bytes32(0);
}

require(!_patchOnly, "Patch only upgrade can not set upgrade transaction");

require(_l2ProtocolUpgradeTx.txType == SYSTEM_UPGRADE_L2_TX_TYPE, "L2 system upgrade tx type is wrong");

bytes memory encodedTransaction = abi.encode(_l2ProtocolUpgradeTx);
Expand All @@ -202,7 +219,7 @@
// We want the hashes of l2 system upgrade transactions to be unique.
// This is why we require that the `nonce` field is unique to each upgrade.
require(
_l2ProtocolUpgradeTx.nonce == _newProtocolVersion,
_l2ProtocolUpgradeTx.nonce == _newMinorProtocolVersion,
"The new protocol version should be included in the L2 system upgrade tx"
);

Expand Down Expand Up @@ -232,24 +249,43 @@

/// @notice Changes the protocol version
/// @param _newProtocolVersion The new protocol version
function _setNewProtocolVersion(uint256 _newProtocolVersion) internal virtual {
function _setNewProtocolVersion(
uint256 _newProtocolVersion
) internal virtual returns (uint32 newMinorVersion, bool patchOnly) {
uint256 previousProtocolVersion = s.protocolVersion;
require(
_newProtocolVersion > previousProtocolVersion,
"New protocol version is not greater than the current one"
);
require(
_newProtocolVersion - previousProtocolVersion <= MAX_ALLOWED_PROTOCOL_VERSION_DELTA,
"Too big protocol version difference"
);

// If the previous upgrade had an L2 system upgrade transaction, we require that it is finalized.
// Note it is important to keep this check, as otherwise hyperchains might skip upgrades by overwriting
require(s.l2SystemContractsUpgradeTxHash == bytes32(0), "Previous upgrade has not been finalized");
require(
s.l2SystemContractsUpgradeBatchNumber == 0,
"The batch number of the previous upgrade has not been cleaned"
);
uint32 newMajorVersion;
// slither-disable-next-line unused-return
(newMajorVersion, newMinorVersion, ) = SemVer.unpackSemVer(SafeCast.toUint96(_newProtocolVersion));
require(newMajorVersion == 0, "Major version change is not allowed");

// slither-disable-next-line unused-return
(uint32 majorDelta, uint32 minorDelta, ) = SemVer.unpackSemVer(SafeCast.toUint96(_newProtocolVersion - previousProtocolVersion));

if (minorDelta == 0) {
patchOnly = true;
}

// While this is implicitly enforced by other checks above, we still double check just in case
require(majorDelta == 0, "Major version change is not allowed");
require(minorDelta <= MAX_ALLOWED_MINOR_VERSION_DELTA, "Too big protocol version difference");

// If the minor version changes also, we need to ensure that the previous upgrade has been finalized.
// In case the minor version does not change, we permit to keep the old upgrade transaction in the system, but it
// must be ensured in the other parts of the upgrade that the is not overriden.

Check warning on line 279 in l1-contracts/contracts/upgrades/BaseZkSyncUpgrade.sol

View workflow job for this annotation

GitHub Actions / typos

"overriden" should be "overridden".
if (!patchOnly) {
// If the previous upgrade had an L2 system upgrade transaction, we require that it is finalized.
// Note it is important to keep this check, as otherwise hyperchains might skip upgrades by overwriting
require(s.l2SystemContractsUpgradeTxHash == bytes32(0), "Previous upgrade has not been finalized");
require(
s.l2SystemContractsUpgradeBatchNumber == 0,
"The batch number of the previous upgrade has not been cleaned"
);
}

s.protocolVersion = _newProtocolVersion;
emit NewProtocolVersion(previousProtocolVersion, _newProtocolVersion);
Expand Down
Loading
Loading