Skip to content

Commit

Permalink
Holocene extensions to L1Block.sol
Browse files Browse the repository at this point in the history
  • Loading branch information
roberto-bayardo committed Sep 24, 2024
1 parent 53080c9 commit 439e177
Show file tree
Hide file tree
Showing 5 changed files with 223 additions and 5 deletions.
6 changes: 3 additions & 3 deletions packages/contracts-bedrock/semver-lock.json
Original file line number Diff line number Diff line change
Expand Up @@ -80,11 +80,11 @@
"sourceCodeHash": "0x4f21025d4b5c9c74cf7040db6f8e9ce605b82931e3012fee51d3f5d9fbd7b73f"
},
"src/L2/L1Block.sol": {
"initCodeHash": "0xd12353c5bf71c6765cc9292eecf262f216e67f117f4ba6287796a5207dbca00f",
"sourceCodeHash": "0xfe3a9585d9bfca8428e12759cab68a3114374e5c37371cfe08bb1976a9a5a041"
"initCodeHash": "0x48d118de2a69fb0fbf6a8da4603025e12da1360da8fb70a5e56342ba64b3ff5f",
"sourceCodeHash": "0xf56d017e3e892b6037f1b03e0ee0eec4ca079b8ac166aa23db17d6fd00123d75"
},
"src/L2/L1BlockIsthmus.sol": {
"initCodeHash": "0xb7a7a113056e4ac44824350b79fed5ea423e880223edcf1220e8f8b3172f50c5",
"initCodeHash": "0x4810ff018c42640a3a050a42ef2c9f39150f6782938a7e5db51c23bb6c75244e",
"sourceCodeHash": "0x6be7e7402c4dfc10e1407e070712a3f9f352db45f8a8ab296e8f6bc56a341f47"
},
"src/L2/L1FeeVault.sol": {
Expand Down
63 changes: 61 additions & 2 deletions packages/contracts-bedrock/src/L2/L1Block.sol
Original file line number Diff line number Diff line change
Expand Up @@ -57,9 +57,15 @@ contract L1Block is ISemver, IGasToken {
/// @notice The latest L1 blob base fee.
uint256 public blobBaseFee;

/// @custom:semver 1.5.1-beta.2
/// @notice The eip-1550 base fee change denominator value.
uint64 public eip1559Denominator;

/// @notice The eip-1550 base fee change elasticity value.
uint64 public eip1559Elasticity;

/// @custom:semver 1.5.1-beta.3
function version() public pure virtual returns (string memory) {
return "1.5.1-beta.2";
return "1.5.1-beta.3";
}

/// @notice Returns the gas paying token, its decimals, name and symbol.
Expand Down Expand Up @@ -168,6 +174,59 @@ contract L1Block is ISemver, IGasToken {
}
}

/// @notice Updates the L1 block values for a Holocene 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:
/// 1. _baseFeeScalar L1 base fee scalar
/// 2. _blobBaseFeeScalar L1 blob base fee scalar
/// 3. _sequenceNumber Number of L2 blocks since epoch start.
/// 4. _timestamp L1 timestamp.
/// 5. _number L1 blocknumber.
/// 6. _basefee L1 base fee.
/// 7. _blobBaseFee L1 blob base fee.
/// 8. _hash L1 blockhash.
/// 9. _batcherHash Versioned hash to authenticate batcher by.
/// 10. _eip1559Elasticity EIP-1559 elasticity multiplier value.
/// 11. _eip1559Denominator EIP-1559 base fee change denominator value.
function setL1BlockValuesHolocene() public {
_setL1BlockValuesHolocene();
}

/// @notice Updates the L1 block values for a Holocene 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:
/// 1. _baseFeeScalar L1 base fee scalar
/// 2. _blobBaseFeeScalar L1 blob base fee scalar
/// 3. _sequenceNumber Number of L2 blocks since epoch start.
/// 4. _timestamp L1 timestamp.
/// 5. _number L1 blocknumber.
/// 6. _basefee L1 base fee.
/// 7. _blobBaseFee L1 blob base fee.
/// 8. _hash L1 blockhash.
/// 9. _batcherHash Versioned hash to authenticate batcher by.
/// 10. _eip1559Elasticity EIP-1559 elasticity multiplier value.
/// 11. _eip1559Denominator EIP-1559 base fee change denominator value.
function _setL1BlockValuesHolocene() internal {
address depositor = DEPOSITOR_ACCOUNT();
assembly {
// Revert if the caller is not the depositor account.
if xor(caller(), depositor) {
mstore(0x00, 0x3cc50b45) // 0x3cc50b45 is the 4-byte selector of "NotDepositor()"
revert(0x1C, 0x04) // returns the stored 4-byte selector from above
}
// sequencenum (uint64), blobBaseFeeScalar (uint32), baseFeeScalar (uint32)
sstore(sequenceNumber.slot, shr(128, calldataload(4)))
// number (uint64) and timestamp (uint64)
sstore(number.slot, shr(128, calldataload(20)))
sstore(basefee.slot, calldataload(36)) // uint256
sstore(blobBaseFee.slot, calldataload(68)) // uint256
sstore(hash.slot, calldataload(100)) // bytes32
sstore(batcherHash.slot, calldataload(132)) // bytes32
// eip1559Denominator (uint64) and eip1559Elasticity (uint64)
sstore(eip1559Denominator.slot, shr(128, calldataload(164))) // uint64
}
}

/// @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/L2/interfaces/IL1Block.sol
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,9 @@ interface IL1Block {
)
external;
function setL1BlockValuesEcotone() external;
function setL1BlockValuesHolocene() external;
function timestamp() external view returns (uint64);
function version() external pure returns (string memory);
function eip1559Denominator() external view returns (uint64);
function eip1559Elasticity() external view returns (uint64);
}
46 changes: 46 additions & 0 deletions packages/contracts-bedrock/src/libraries/Encoding.sol
Original file line number Diff line number Diff line change
Expand Up @@ -174,6 +174,52 @@ library Encoding {
);
}

/// @notice Returns an appropriately encoded call to L1Block.setL1BlockValuesHolocene
/// @param baseFeeScalar L1 base fee Scalar
/// @param blobBaseFeeScalar L1 blob base fee Scalar
/// @param sequenceNumber Number of L2 blocks since epoch start.
/// @param timestamp L1 timestamp.
/// @param number L1 blocknumber.
/// @param baseFee L1 base fee.
/// @param blobBaseFee L1 blob base fee.
/// @param hash L1 blockhash.
/// @param batcherHash Versioned hash to authenticate batcher by.
/// @param eip1559Elasticity EIP-1559 elasticity parameter
/// @param eip1559Denominator EIP-1559 denominator parameter
function encodeSetL1BlockValuesHolocene(
uint32 baseFeeScalar,
uint32 blobBaseFeeScalar,
uint64 sequenceNumber,
uint64 timestamp,
uint64 number,
uint256 baseFee,
uint256 blobBaseFee,
bytes32 hash,
bytes32 batcherHash,
uint64 eip1559Elasticity,
uint64 eip1559Denominator
)
internal
pure
returns (bytes memory)
{
bytes4 functionSignature = bytes4(keccak256("setL1BlockValuesHolocene()"));
return abi.encodePacked(
functionSignature,
baseFeeScalar,
blobBaseFeeScalar,
sequenceNumber,
timestamp,
number,
baseFee,
blobBaseFee,
hash,
batcherHash,
eip1559Elasticity,
eip1559Denominator
);
}

/// @notice Returns an appropriately encoded call to L1Block.setL1BlockValuesInterop
/// @param _baseFeeScalar L1 base fee Scalar
/// @param _blobBaseFeeScalar L1 blob base fee Scalar
Expand Down
110 changes: 110 additions & 0 deletions packages/contracts-bedrock/test/L2/L1Block.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -165,6 +165,116 @@ contract L1BlockEcotone_Test is L1BlockTest {
}
}

contract L1BlockHolocene_Test is L1BlockTest {
/// @dev Tests that setL1BlockValuesHolocene updates the values appropriately.
function testFuzz_setL1BlockValuesHolocene_succeeds(
uint32 baseFeeScalar,
uint32 blobBaseFeeScalar,
uint64 sequenceNumber,
uint64 timestamp,
uint64 number,
uint256 baseFee,
uint256 blobBaseFee,
bytes32 hash,
bytes32 batcherHash,
uint64 eip1559Elasticity,
uint64 eip1559Denominator
)
external
{
bytes memory functionCallDataPacked = Encoding.encodeSetL1BlockValuesHolocene(
baseFeeScalar,
blobBaseFeeScalar,
sequenceNumber,
timestamp,
number,
baseFee,
blobBaseFee,
hash,
batcherHash,
eip1559Elasticity,
eip1559Denominator
);

vm.prank(depositor);
(bool success,) = address(l1Block).call(functionCallDataPacked);
assertTrue(success, "Function call failed");

assertEq(l1Block.baseFeeScalar(), baseFeeScalar);
assertEq(l1Block.blobBaseFeeScalar(), blobBaseFeeScalar);
assertEq(l1Block.sequenceNumber(), sequenceNumber);
assertEq(l1Block.timestamp(), timestamp);
assertEq(l1Block.number(), number);
assertEq(l1Block.basefee(), baseFee);
assertEq(l1Block.blobBaseFee(), blobBaseFee);
assertEq(l1Block.hash(), hash);
assertEq(l1Block.batcherHash(), batcherHash);
assertEq(l1Block.eip1559Denominator(), eip1559Denominator);
assertEq(l1Block.eip1559Elasticity(), eip1559Elasticity);

// ensure we didn't accidentally pollute the 128 bits of the sequencenum+scalars slot that
// should be empty
bytes32 scalarsSlot = vm.load(address(l1Block), bytes32(uint256(3)));
bytes32 mask128 = hex"FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000000000000000000000000000000";

assertEq(0, scalarsSlot & mask128);

// ensure we didn't accidentally pollute the 128 bits of the number & timestamp slot that
// should be empty
bytes32 numberTimestampSlot = vm.load(address(l1Block), bytes32(uint256(0)));
assertEq(0, numberTimestampSlot & mask128);

// ensure we didn't accidentally pollute the 128 bits of the eip-1559 parameters slot that
// should be empty
bytes32 eip1559ParamsSlot = vm.load(address(l1Block), bytes32(uint256(9)));
assertEq(0, eip1559ParamsSlot & mask128);
}

/// @dev Tests that `setL1BlockValuesHolocene` succeeds if sender address is the depositor
function test_setL1BlockValuesHolocene_isDepositor_succeeds() external {
bytes memory functionCallDataPacked = Encoding.encodeSetL1BlockValuesHolocene(
type(uint32).max,
type(uint32).max,
type(uint64).max,
type(uint64).max,
type(uint64).max,
type(uint256).max,
type(uint256).max,
bytes32(type(uint256).max),
bytes32(type(uint256).max),
type(uint64).max,
type(uint64).max
);

vm.prank(depositor);
(bool success,) = address(l1Block).call(functionCallDataPacked);
assertTrue(success, "function call failed");
}

/// @dev Tests that `setL1BlockValuesEcotone` reverts if sender address is not the depositor
function test_setL1BlockValuesHolocene_notDepositor_reverts() external {
bytes memory functionCallDataPacked = Encoding.encodeSetL1BlockValuesHolocene(
type(uint32).max,
type(uint32).max,
type(uint64).max,
type(uint64).max,
type(uint64).max,
type(uint256).max,
type(uint256).max,
bytes32(type(uint256).max),
bytes32(type(uint256).max),
type(uint64).max,
type(uint64).max
);

(bool success, bytes memory data) = address(l1Block).call(functionCallDataPacked);
assertTrue(!success, "function call should have failed");
// make sure return value is the expected function selector for "NotDepositor()"
bytes memory expReturn = hex"3cc50b45";
assertEq(data, expReturn);
}
}

contract L1BlockCustomGasToken_Test is L1BlockTest {
function testFuzz_setGasPayingToken_succeeds(
address _token,
Expand Down

0 comments on commit 439e177

Please sign in to comment.