Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
3 changes: 3 additions & 0 deletions l1-contracts/src/core/libraries/Errors.sol
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,7 @@ library Errors {
error Rollup__InvalidFirstEpochProof();
error Rollup__InvalidCoinbase();
error Rollup__StaleTempBlockLog(uint256 blockNumber, uint256 pendingBlockNumber, uint256 size);
error Rollup__NoBlobsInBlock();

// ProposedHeaderLib
error HeaderLib__InvalidHeaderSize(uint256 expected, uint256 actual); // 0xf3ccb247
Expand Down Expand Up @@ -176,6 +177,8 @@ library Errors {
// RewardBooster
error RewardBooster__OnlyRollup(address caller);

error RewardLib__InvalidSequencerBps();

// TallySlashingProposer
error TallySlashingProposer__InvalidSignature();
error TallySlashingProposer__InvalidVoteLength(uint256 expected, uint256 actual);
Expand Down
60 changes: 0 additions & 60 deletions l1-contracts/src/core/libraries/crypto/MerkleLib.sol
Original file line number Diff line number Diff line change
Expand Up @@ -53,64 +53,4 @@ library MerkleLib {
require(indexAtHeight == 0, Errors.MerkleLib__InvalidIndexForPathLength());
require(subtreeRoot == _expectedRoot, Errors.MerkleLib__InvalidRoot(_expectedRoot, subtreeRoot, _leaf, _index));
}

/**
* @notice Computes the root for a binary Merkle-tree given the leafs.
* @dev Uses sha256.
* @param _leafs - The 32 bytes leafs to build the tree of.
* @return The root of the Merkle tree.
*/
function computeRoot(bytes32[] memory _leafs) internal pure returns (bytes32) {
// @todo Must pad the tree
uint256 treeDepth = 0;
while (2 ** treeDepth < _leafs.length) {
treeDepth++;
}
uint256 treeSize = 2 ** treeDepth;
assembly {
mstore(_leafs, treeSize)
}

for (uint256 i = 0; i < treeDepth; i++) {
for (uint256 j = 0; j < treeSize; j += 2) {
_leafs[j / 2] = Hash.sha256ToField(bytes.concat(_leafs[j], _leafs[j + 1]));
}
treeSize /= 2;
}

return _leafs[0];
}

/**
* @notice Computes the root for a binary unbalanced Merkle-tree given the leaves.
* @dev Filled in greedily with subtrees. Useful for outHash tree.
* @param _leaves - The 32 bytes leafs to build the tree of.
* @return The root of the Merkle tree.
*/
function computeUnbalancedRoot(bytes32[] memory _leaves) internal pure returns (bytes32) {
// e.g. an unbalanced tree of 7 txs will contain subtrees of 4, 2, and 1 tx(s) = 111
// e.g. an unbalanced tree of 9 txs will contain subtrees of 8 and 1 tx(s) = 1001
// We collect the roots of each subtree
bytes32 root;
uint256 currentSubtreeSize = 1;
uint256 numTxs = _leaves.length;
// We must calculate the smaller rightmost subtrees first, hence starting at 1
while (numTxs != 0) {
// If size & txs == 0, the subtree doesn't exist for this number of txs
if (currentSubtreeSize & numTxs == 0) {
currentSubtreeSize <<= 1;
continue;
}
bytes32[] memory leavesInSubtree = new bytes32[](currentSubtreeSize);
uint256 start = numTxs - currentSubtreeSize;
for (uint256 i = start; i < numTxs; i++) {
leavesInSubtree[i - start] = _leaves[i];
}
bytes32 subtreeRoot = computeRoot(leavesInSubtree);
root = numTxs == _leaves.length ? subtreeRoot : Hash.sha256ToField(bytes.concat(subtreeRoot, root));
numTxs -= currentSubtreeSize;
currentSubtreeSize <<= 1;
}
return root;
}
}
1 change: 1 addition & 0 deletions l1-contracts/src/core/libraries/rollup/BlobLib.sol
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,7 @@ library BlobLib {
// We cannot input the incorrect number of blobs below, as the blobsHash
// and epoch proof verification will fail.
uint8 numBlobs = uint8(_blobsInput[0]);
require(numBlobs > 0, Errors.Rollup__NoBlobsInBlock());
blobHashes = new bytes32[](numBlobs);
blobCommitments = new bytes[](numBlobs);
bytes32 blobHash;
Expand Down
12 changes: 8 additions & 4 deletions l1-contracts/src/core/libraries/rollup/RewardLib.sol
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@ library RewardLib {
address public constant BURN_ADDRESS = address(bytes20("CUAUHXICALLI"));

function setConfig(RewardConfig memory _config) internal {
require(Bps.unwrap(_config.sequencerBps) <= 10_000, Errors.RewardLib__InvalidSequencerBps());
RewardStorage storage rewardStorage = getStorage();
rewardStorage.config = _config;
}
Expand All @@ -86,8 +87,11 @@ library RewardLib {

RollupStore storage rollupStore = STFLib.getStorage();
uint256 amount = rewardStorage.sequencerRewards[_sequencer];
rewardStorage.sequencerRewards[_sequencer] = 0;
rollupStore.config.feeAsset.transfer(_sequencer, amount);

if (amount > 0) {
rewardStorage.sequencerRewards[_sequencer] = 0;
rollupStore.config.feeAsset.safeTransfer(_sequencer, amount);
}

return amount;
}
Expand Down Expand Up @@ -119,7 +123,7 @@ library RewardLib {
}
}

rollupStore.config.feeAsset.transfer(_prover, accumulatedRewards);
rollupStore.config.feeAsset.safeTransfer(_prover, accumulatedRewards);

return accumulatedRewards;
}
Expand Down Expand Up @@ -215,7 +219,7 @@ library RewardLib {
}

if (t.totalBurn > 0) {
rollupStore.config.feeAsset.transfer(BURN_ADDRESS, t.totalBurn);
rollupStore.config.feeAsset.safeTransfer(BURN_ADDRESS, t.totalBurn);
}
}
}
Expand Down
6 changes: 3 additions & 3 deletions l1-contracts/src/core/libraries/rollup/StakingLib.sol
Original file line number Diff line number Diff line change
Expand Up @@ -193,7 +193,7 @@ library StakingLib {
delete store.exits[_attester];

store.gse.finalizeWithdraw(exit.withdrawalId);
store.stakingAsset.transfer(exit.recipientOrWithdrawer, exit.amount);
store.stakingAsset.safeTransfer(exit.recipientOrWithdrawer, exit.amount);

emit IStakingCore.WithdrawFinalized(_attester, exit.recipientOrWithdrawer, exit.amount);
}
Expand Down Expand Up @@ -297,7 +297,7 @@ library StakingLib {
require(!store.exits[_attester].exists, Errors.Staking__AlreadyExiting(_attester));
uint256 amount = store.gse.ACTIVATION_THRESHOLD();

store.stakingAsset.transferFrom(msg.sender, address(this), amount);
store.stakingAsset.safeTransferFrom(msg.sender, address(this), amount);
store.entryQueue.enqueue(
_attester, _withdrawer, _publicKeyInG1, _publicKeyInG2, _proofOfPossession, _moveWithLatestRollup
);
Expand Down Expand Up @@ -369,7 +369,7 @@ library StakingLib {
// where someone could drain the queue without making any deposits.
// We can safely assume data.length == 0 means out of gas since we only call trusted GSE contract.
require(data.length > 0, Errors.Staking__DepositOutOfGas());
store.stakingAsset.transfer(args.withdrawer, amount);
store.stakingAsset.safeTransfer(args.withdrawer, amount);
emit IStakingCore.FailedDeposit(
args.attester, args.withdrawer, args.publicKeyInG1, args.publicKeyInG2, args.proofOfPossession
);
Expand Down
6 changes: 4 additions & 2 deletions l1-contracts/src/governance/GSE.sol
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import {BN254Lib, G1Point, G2Point} from "@aztec/shared/libraries/BN254Lib.sol";
import {Timestamp} from "@aztec/shared/libraries/TimeMath.sol";
import {Ownable} from "@oz/access/Ownable.sol";
import {IERC20} from "@oz/token/ERC20/IERC20.sol";
import {SafeERC20} from "@oz/token/ERC20/utils/SafeERC20.sol";
import {SafeCast} from "@oz/utils/math/SafeCast.sol";
import {Checkpoints} from "@oz/utils/structs/Checkpoints.sol";

Expand Down Expand Up @@ -121,6 +122,7 @@ contract GSECore is IGSECore, Ownable {
using SafeCast for uint224;
using Checkpoints for Checkpoints.Trace224;
using DepositDelegationLib for DepositAndDelegationAccounting;
using SafeERC20 for IERC20;

/**
* Create a special "bonus" address for use by the latest rollup.
Expand Down Expand Up @@ -336,7 +338,7 @@ contract GSECore is IGSECore, Ownable {
delegation.delegate(recipientInstance, _attester, recipientInstance);
delegation.increaseBalance(recipientInstance, _attester, ACTIVATION_THRESHOLD);

ASSET.transferFrom(msg.sender, address(this), ACTIVATION_THRESHOLD);
ASSET.safeTransferFrom(msg.sender, address(this), ACTIVATION_THRESHOLD);

Governance gov = getGovernance();
ASSET.approve(address(gov), ACTIVATION_THRESHOLD);
Expand Down Expand Up @@ -469,7 +471,7 @@ contract GSECore is IGSECore, Ownable {
Governance gov = getGovernance();
uint256 amount = gov.getConfiguration().proposeConfig.lockAmount;

ASSET.transferFrom(msg.sender, address(this), amount);
ASSET.safeTransferFrom(msg.sender, address(this), amount);
ASSET.approve(address(gov), amount);

gov.deposit(address(this), amount);
Expand Down
34 changes: 32 additions & 2 deletions l1-contracts/test/Rollup.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -681,6 +681,7 @@ contract RollupTest is RollupBase {
function testRevertInvalidTimestamp() public setUpFor("empty_block_1") {
DecoderBase.Data memory data = load("empty_block_1").block;
ProposedHeader memory header = data.header;
vm.blobhashes(this.getBlobHashes(data.blobCommitments));
bytes32 archive = data.archive;

Timestamp realTs = header.timestamp;
Expand All @@ -704,7 +705,7 @@ contract RollupTest is RollupBase {
AttestationLibHelper.packAttestations(attestations),
signers,
attestationsAndSignersSignature,
new bytes(144)
data.blobCommitments
);
}

Expand All @@ -720,6 +721,8 @@ contract RollupTest is RollupBase {
// Tweak the coinbase.
header.coinbase = address(0);

bytes32[] memory blobHashes = this.getBlobHashes(data.blobCommitments);
vm.blobhashes(blobHashes);
skipBlobCheck(address(rollup));
vm.expectRevert(abi.encodeWithSelector(Errors.Rollup__InvalidCoinbase.selector));
ProposeArgs memory args = ProposeArgs({
Expand All @@ -733,7 +736,7 @@ contract RollupTest is RollupBase {
AttestationLibHelper.packAttestations(attestations),
signers,
attestationsAndSignersSignature,
new bytes(144)
data.blobCommitments
);
}

Expand Down Expand Up @@ -777,6 +780,33 @@ contract RollupTest is RollupBase {
_submitEpochProof(1, 1, blockLog.archive, data.archive, blobProofInputs, address(0));
}

function testNoBlob() public setUpFor("empty_block_1") {
DecoderBase.Data memory data = load("empty_block_1").block;
ProposedHeader memory header = data.header;
bytes32 archive = data.archive;

Timestamp realTs = header.timestamp;

vm.warp(max(block.timestamp, Timestamp.unwrap(realTs)));

header.gasFees.feePerL2Gas = uint128(rollup.getManaBaseFeeAt(Timestamp.wrap(block.timestamp), true));

vm.expectRevert(abi.encodeWithSelector(Errors.Rollup__NoBlobsInBlock.selector));
ProposeArgs memory args = ProposeArgs({
header: header,
archive: archive,
stateReference: EMPTY_STATE_REFERENCE,
oracleInput: OracleInput(0)
});
rollup.propose(
args,
AttestationLibHelper.packAttestations(attestations),
signers,
attestationsAndSignersSignature,
new bytes(144)
);
}

function testTooManyBlocks() public setUpFor("mixed_block_1") {
_proposeBlock("mixed_block_1", 1);
DecoderBase.Data memory data = load("mixed_block_1").block;
Expand Down
59 changes: 58 additions & 1 deletion l1-contracts/test/merkle/helpers/MerkleLibHelper.sol
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
// Copyright 2024 Aztec Labs.
pragma solidity >=0.8.27;

import {Hash} from "@aztec/core/libraries/crypto/Hash.sol";
import {MerkleLib} from "@aztec/core/libraries/crypto/MerkleLib.sol";

// A wrapper used to be able to "call" library functions, instead of "jumping" to them, allowing forge to catch the
Expand Down Expand Up @@ -55,7 +56,63 @@ contract MerkleLibHelper {
return (min, max);
}

/**
* @notice Computes the root for a binary unbalanced Merkle-tree given the leaves.
* @dev Filled in greedily with subtrees. Useful for outHash tree.
* @param _leaves - The 32 bytes leafs to build the tree of.
* @return The root of the Merkle tree.
*/
function computeUnbalancedRoot(bytes32[] memory _leaves) external pure returns (bytes32) {
return MerkleLib.computeUnbalancedRoot(_leaves);
// e.g. an unbalanced tree of 7 txs will contain subtrees of 4, 2, and 1 tx(s) = 111
// e.g. an unbalanced tree of 9 txs will contain subtrees of 8 and 1 tx(s) = 1001
// We collect the roots of each subtree
bytes32 root;
uint256 currentSubtreeSize = 1;
uint256 numTxs = _leaves.length;
// We must calculate the smaller rightmost subtrees first, hence starting at 1
while (numTxs != 0) {
// If size & txs == 0, the subtree doesn't exist for this number of txs
if (currentSubtreeSize & numTxs == 0) {
currentSubtreeSize <<= 1;
continue;
}
bytes32[] memory leavesInSubtree = new bytes32[](currentSubtreeSize);
uint256 start = numTxs - currentSubtreeSize;
for (uint256 i = start; i < numTxs; i++) {
leavesInSubtree[i - start] = _leaves[i];
}
bytes32 subtreeRoot = computeRoot(leavesInSubtree);
root = numTxs == _leaves.length ? subtreeRoot : Hash.sha256ToField(bytes.concat(subtreeRoot, root));
numTxs -= currentSubtreeSize;
currentSubtreeSize <<= 1;
}
return root;
}

/**
* @notice Computes the root for a binary Merkle-tree given the leafs.
* @dev Uses sha256.
* @param _leafs - The 32 bytes leafs to build the tree of.
* @return The root of the Merkle tree.
*/
function computeRoot(bytes32[] memory _leafs) internal pure returns (bytes32) {
// @todo Must pad the tree
uint256 treeDepth = 0;
while (2 ** treeDepth < _leafs.length) {
treeDepth++;
}
uint256 treeSize = 2 ** treeDepth;
assembly {
mstore(_leafs, treeSize)
}

for (uint256 i = 0; i < treeDepth; i++) {
for (uint256 j = 0; j < treeSize; j += 2) {
_leafs[j / 2] = Hash.sha256ToField(bytes.concat(_leafs[j], _leafs[j + 1]));
}
treeSize /= 2;
}

return _leafs[0];
}
}
Loading