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
74 changes: 66 additions & 8 deletions l1-contracts/src/core/Rollup.sol
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,15 @@
pragma solidity >=0.8.27;

import {EIP712} from "@oz/utils/cryptography/EIP712.sol";
import {ECDSA} from "@oz/utils/cryptography/ECDSA.sol";

import {IProofCommitmentEscrow} from "@aztec/core/interfaces/IProofCommitmentEscrow.sol";
import {IInbox} from "@aztec/core/interfaces/messagebridge/IInbox.sol";
import {IOutbox} from "@aztec/core/interfaces/messagebridge/IOutbox.sol";
import {IFeeJuicePortal} from "@aztec/core/interfaces/IFeeJuicePortal.sol";
import {IRollup, ITestRollup} from "@aztec/core/interfaces/IRollup.sol";
import {IVerifier} from "@aztec/core/interfaces/IVerifier.sol";
import {ISysstia} from "@aztec/governance/interfaces/ISysstia.sol";
import {IERC20} from "@oz/token/ERC20/IERC20.sol";

import {Constants} from "@aztec/core/libraries/ConstantsGen.sol";
import {DataStructures} from "@aztec/core/libraries/DataStructures.sol";
Expand All @@ -21,6 +22,8 @@ import {TxsDecoder} from "@aztec/core/libraries/TxsDecoder.sol";
import {MerkleLib} from "@aztec/core/libraries/crypto/MerkleLib.sol";
import {SignatureLib} from "@aztec/core/libraries/crypto/SignatureLib.sol";
import {SafeCast} from "@oz/utils/math/SafeCast.sol";
import {Math} from "@oz/utils/math/Math.sol";
import {SafeERC20} from "@oz/token/ERC20/utils/SafeERC20.sol";

import {Inbox} from "@aztec/core/messagebridge/Inbox.sol";
import {Leonidas} from "@aztec/core/Leonidas.sol";
Expand All @@ -40,6 +43,7 @@ contract Rollup is EIP712("Aztec Rollup", "1"), Leonidas, IRollup, ITestRollup {
using SafeCast for uint256;
using SlotLib for Slot;
using EpochLib for Epoch;
using SafeERC20 for IERC20;

struct ChainTips {
uint256 pendingBlockNumber;
Expand All @@ -64,6 +68,8 @@ contract Rollup is EIP712("Aztec Rollup", "1"), Leonidas, IRollup, ITestRollup {
IProofCommitmentEscrow public immutable PROOF_COMMITMENT_ESCROW;
uint256 public immutable VERSION;
IFeeJuicePortal public immutable FEE_JUICE_PORTAL;
ISysstia public immutable SYSSTIA;
IERC20 public immutable ASSET;
IVerifier public epochProofVerifier;

ChainTips public tips;
Expand All @@ -85,14 +91,17 @@ contract Rollup is EIP712("Aztec Rollup", "1"), Leonidas, IRollup, ITestRollup {

constructor(
IFeeJuicePortal _fpcJuicePortal,
ISysstia _sysstia,
bytes32 _vkTreeRoot,
bytes32 _protocolContractTreeRoot,
address _ares,
address[] memory _validators
) Leonidas(_ares) {
epochProofVerifier = new MockVerifier();
FEE_JUICE_PORTAL = _fpcJuicePortal;
PROOF_COMMITMENT_ESCROW = new ProofCommitmentEscrow(_fpcJuicePortal.UNDERLYING(), address(this));
SYSSTIA = _sysstia;
ASSET = _fpcJuicePortal.UNDERLYING();
PROOF_COMMITMENT_ESCROW = new ProofCommitmentEscrow(ASSET, address(this));
INBOX = IInbox(address(new Inbox(address(this), Constants.L1_TO_L2_MSG_SUBTREE_HEIGHT)));
OUTBOX = IOutbox(address(new Outbox(address(this))));
vkTreeRoot = _vkTreeRoot;
Expand Down Expand Up @@ -262,13 +271,51 @@ contract Rollup is EIP712("Aztec Rollup", "1"), Leonidas, IRollup, ITestRollup {

// @note Only if the rollup is the canonical will it be able to meaningfully claim fees
// Otherwise, the fees are unbacked #7938.
if (address(this) == FEE_JUICE_PORTAL.canonicalRollup()) {
for (uint256 i = 0; i < Constants.AZTEC_EPOCH_DURATION; i++) {
bool isFeeCanonical = address(this) == FEE_JUICE_PORTAL.canonicalRollup();
bool isSysstiaCanonical = address(this) == SYSSTIA.canonicalRollup();
Comment on lines +274 to +275
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would we ever want these to differ?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Likely not. But done this way as they could. For example, it you want to run a Sysstia that allow rewards (of different size) to multiple rollups it can be supported this way, but there can only be one guy that is supported for the fee collateral.


uint256 totalProverReward = 0;

if (isFeeCanonical || isSysstiaCanonical) {
for (uint256 i = 0; i < _epochSize; i++) {
address coinbase = address(uint160(uint256(publicInputs[9 + i * 2])));
uint256 fees = uint256(publicInputs[10 + i * 2]);
if (coinbase != address(0) && fees > 0) {
FEE_JUICE_PORTAL.distributeFees(coinbase, fees);
uint256 reward = 0;
uint256 toProver = 0;

if (isFeeCanonical) {
uint256 fees = uint256(publicInputs[10 + i * 2]);
if (fees > 0) {
reward += fees;
FEE_JUICE_PORTAL.distributeFees(address(this), fees);
}
}

if (isSysstiaCanonical) {
reward += SYSSTIA.claim(address(this));
}

if (coinbase == address(0)) {
toProver = reward;
} else {
// @note We are getting value from the `proofClaim`, which are not cleared.
// So if someone is posting the proof before a new claim is made,
// the reward will calculated based on the previous values.
toProver = Math.mulDiv(reward, proofClaim.basisPointFee, 10_000);
}

uint256 toCoinbase = reward - toProver;
if (toCoinbase > 0) {
ASSET.safeTransfer(coinbase, toCoinbase);
}

totalProverReward += toProver;
}

if (totalProverReward > 0) {
// If there is a bond-provider give him the reward, otherwise give it to the submitter.
address proofRewardRecipient =
proofClaim.bondProvider == address(0) ? msg.sender : proofClaim.bondProvider;
ASSET.safeTransfer(proofRewardRecipient, totalProverReward);
}
}

Expand Down Expand Up @@ -481,12 +528,18 @@ contract Rollup is EIP712("Aztec Rollup", "1"), Leonidas, IRollup, ITestRollup {
if (blockNumber <= assumeProvenThroughBlockNumber) {
fakeBlockNumberAsProven(blockNumber);

if (header.globalVariables.coinbase != address(0) && header.totalFees > 0) {
bool isFeeCanonical = address(this) == FEE_JUICE_PORTAL.canonicalRollup();
bool isSysstiaCanonical = address(this) == SYSSTIA.canonicalRollup();

if (isFeeCanonical && header.globalVariables.coinbase != address(0) && header.totalFees > 0) {
// @note This will currently fail if there are insufficient funds in the bridge
// which WILL happen for the old version after an upgrade where the bridge follow.
// Consider allowing a failure. See #7938.
FEE_JUICE_PORTAL.distributeFees(header.globalVariables.coinbase, header.totalFees);
}
if (isSysstiaCanonical && header.globalVariables.coinbase != address(0)) {
SYSSTIA.claim(header.globalVariables.coinbase);
}

emit L2ProofVerified(blockNumber, "CHEAT");
}
Expand Down Expand Up @@ -642,6 +695,11 @@ contract Rollup is EIP712("Aztec Rollup", "1"), Leonidas, IRollup, ITestRollup {
address currentProposer = getCurrentProposer();
Epoch epochToProve = getEpochToProve();

require(
_quote.quote.basisPointFee <= 10_000,
Errors.Rollup__InvalidBasisPointFee(_quote.quote.basisPointFee)
);

require(
currentProposer == address(0) || currentProposer == msg.sender,
Errors.Leonidas__InvalidProposer(currentProposer, msg.sender)
Expand Down
5 changes: 3 additions & 2 deletions l1-contracts/src/core/libraries/Errors.sol
Original file line number Diff line number Diff line change
Expand Up @@ -72,8 +72,9 @@ library Errors {
error Rollup__TimestampTooOld(); // 0x72ed9c81
error Rollup__TryingToProveNonExistingBlock(); // 0x34ef4954
error Rollup__UnavailableTxs(bytes32 txsHash); // 0x414906c3
error Rollup__NonZeroDaFee();
error Rollup__NonZeroL2Fee();
error Rollup__NonZeroDaFee(); // 0xd9c75f52
error Rollup__NonZeroL2Fee(); // 0x7e728abc
error Rollup__InvalidBasisPointFee(uint256 basisPointFee); // 0x4292d136

//TxsDecoder
error TxsDecoder__InvalidLogsLength(uint256 expected, uint256 actual); // 0x829ca981
Expand Down
2 changes: 1 addition & 1 deletion l1-contracts/src/core/libraries/TxsDecoder.sol
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ library TxsDecoder {
* @param _body - The L2 block body calldata.
* @return The txs effects hash.
*/
function decode(bytes calldata _body) internal pure returns (bytes32) {
function decode(bytes calldata _body) external pure returns (bytes32) {
ArrayOffsets memory offsets;
Counts memory counts;
ConsumablesVars memory vars;
Expand Down
27 changes: 18 additions & 9 deletions l1-contracts/src/governance/Sysstia.sol
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,11 @@ contract Sysstia is ISysstia {
// This value is pulled out my ass. Don't take it seriously
uint256 public constant BLOCK_REWARD = 50e18;

IERC20 public immutable TST;
IERC20 public immutable ASSET;
IRegistry public immutable REGISTRY;

constructor(IERC20 _tst, IRegistry _registry) {
TST = _tst;
constructor(IERC20 _asset, IRegistry _registry) {
ASSET = _asset;
REGISTRY = _registry;
}

Expand All @@ -29,16 +29,25 @@ contract Sysstia is ISysstia {
* Note especially that it can be called any number of times.
* Essentially a placeholder until more nuanced logic is designed.
*
* @dev Does not check if the tokens are actually available first.
*
* @param _to - The address to receive the reward
*
* @return - the amount claimed
*/
function claim(address _to) external override(ISysstia) returns (uint256) {
address canonical = REGISTRY.getRollup();
require(msg.sender == canonical, Errors.Sysstia__InvalidCaller(msg.sender, canonical));
TST.safeTransfer(_to, BLOCK_REWARD);
return BLOCK_REWARD;
require(
msg.sender == canonicalRollup(), Errors.Sysstia__InvalidCaller(msg.sender, canonicalRollup())
);
uint256 bal = ASSET.balanceOf(address(this));
uint256 reward = bal > BLOCK_REWARD ? BLOCK_REWARD : bal;

if (reward > 0) {
ASSET.safeTransfer(_to, reward);
}

return reward;
}

function canonicalRollup() public view returns (address) {
return REGISTRY.getRollup();
}
}
1 change: 1 addition & 0 deletions l1-contracts/src/governance/interfaces/ISysstia.sol
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,5 @@ pragma solidity >=0.8.27;

interface ISysstia {
function claim(address _to) external returns (uint256);
function canonicalRollup() external view returns (address);
}
41 changes: 31 additions & 10 deletions l1-contracts/test/Rollup.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import {DataStructures} from "@aztec/core/libraries/DataStructures.sol";
import {Constants} from "@aztec/core/libraries/ConstantsGen.sol";
import {SignatureLib} from "@aztec/core/libraries/crypto/SignatureLib.sol";
import {EpochProofQuoteLib} from "@aztec/core/libraries/EpochProofQuoteLib.sol";
import {Math} from "@oz/utils/math/Math.sol";

import {Registry} from "@aztec/governance/Registry.sol";
import {Inbox} from "@aztec/core/messagebridge/Inbox.sol";
Expand All @@ -21,7 +22,7 @@ import {Leonidas} from "@aztec/core/Leonidas.sol";
import {NaiveMerkle} from "./merkle/Naive.sol";
import {MerkleTestUtil} from "./merkle/TestUtil.sol";
import {TestERC20} from "@aztec/mock/TestERC20.sol";

import {Sysstia} from "@aztec/governance/Sysstia.sol";
import {TxsDecoderHelper} from "./decoders/helpers/TxsDecoderHelper.sol";
import {IERC20Errors} from "@oz/interfaces/draft-IERC6093.sol";

Expand All @@ -46,12 +47,15 @@ contract RollupTest is DecoderBase {
TestERC20 internal testERC20;
FeeJuicePortal internal feeJuicePortal;
IProofCommitmentEscrow internal proofCommitmentEscrow;

Sysstia internal sysstia;
SignatureLib.Signature[] internal signatures;

EpochProofQuoteLib.EpochProofQuote internal quote;
EpochProofQuoteLib.SignedEpochProofQuote internal signedQuote;

uint256 internal privateKey;
address internal signer;

/**
* @notice Set up the contracts needed for the tests with time aligned to the provided block name
*/
Expand All @@ -72,7 +76,11 @@ contract RollupTest is DecoderBase {
);
testERC20.mint(address(feeJuicePortal), Constants.FEE_JUICE_INITIAL_MINT);
feeJuicePortal.initialize();
rollup = new Rollup(feeJuicePortal, bytes32(0), bytes32(0), address(this), new address[](0));
sysstia = new Sysstia(testERC20, registry);
testERC20.mint(address(sysstia), 1e6 ether);

rollup =
new Rollup(feeJuicePortal, sysstia, bytes32(0), bytes32(0), address(this), new address[](0));
inbox = Inbox(address(rollup.INBOX()));
outbox = Outbox(address(rollup.OUTBOX()));
proofCommitmentEscrow = IProofCommitmentEscrow(address(rollup.PROOF_COMMITMENT_ESCROW()));
Expand All @@ -82,15 +90,15 @@ contract RollupTest is DecoderBase {
merkleTestUtil = new MerkleTestUtil();
txsHelper = new TxsDecoderHelper();

uint256 privateKey = 0x123456789abcdef123456789abcdef123456789abcdef123456789abcdef1234;
address signer = vm.addr(privateKey);
privateKey = 0x123456789abcdef123456789abcdef123456789abcdef123456789abcdef1234;
signer = vm.addr(privateKey);
uint256 bond = rollup.PROOF_COMMITMENT_MIN_BOND_AMOUNT_IN_TST();
quote = EpochProofQuoteLib.EpochProofQuote({
epochToProve: Epoch.wrap(0),
validUntilSlot: Slot.wrap(1),
bondAmount: bond,
prover: signer,
basisPointFee: 0
basisPointFee: 500
});
signedQuote = _quoteToSignedQuote(quote);

Expand Down Expand Up @@ -569,6 +577,13 @@ contract RollupTest is DecoderBase {

(bytes32 preArchive, bytes32 preBlockHash,) = rollup.blocks(0);

quote.epochToProve = Epoch.wrap(1);
quote.validUntilSlot = Epoch.wrap(2).toSlots();
signedQuote = _quoteToSignedQuote(quote);

warpToL2Slot(Constants.AZTEC_EPOCH_DURATION + rollup.CLAIM_DURATION_IN_L2_SLOTS() - 1);
rollup.claimEpochProofRight(signedQuote);

{
vm.expectRevert(
abi.encodeWithSelector(
Expand All @@ -589,8 +604,9 @@ contract RollupTest is DecoderBase {
coinbase,
feeAmount
);
assertEq(testERC20.balanceOf(coinbase), 0, "invalid coinbase balance");
}
assertEq(testERC20.balanceOf(coinbase), 0, "invalid coinbase balance");
assertEq(testERC20.balanceOf(address(quote.prover)), 0, "invalid prover balance");

{
testERC20.mint(address(feeJuicePortal), feeAmount - portalBalance);
Expand All @@ -607,7 +623,13 @@ contract RollupTest is DecoderBase {
coinbase,
feeAmount
);
assertEq(testERC20.balanceOf(coinbase), feeAmount, "invalid coinbase balance");

uint256 expectedReward = sysstia.BLOCK_REWARD() + feeAmount;
uint256 expectedProverReward = Math.mulDiv(expectedReward, quote.basisPointFee, 10_000);
uint256 expectedSequencerReward = expectedReward - expectedProverReward;

assertEq(testERC20.balanceOf(coinbase), expectedSequencerReward, "invalid coinbase balance");
assertEq(testERC20.balanceOf(quote.prover), expectedProverReward, "invalid prover balance");
}
}

Expand Down Expand Up @@ -824,8 +846,7 @@ contract RollupTest is DecoderBase {
returns (EpochProofQuoteLib.SignedEpochProofQuote memory)
{
bytes32 digest = rollup.quoteToDigest(_quote);
(uint8 v, bytes32 r, bytes32 s) =
vm.sign(0x123456789abcdef123456789abcdef123456789abcdef123456789abcdef1234, digest);
(uint8 v, bytes32 r, bytes32 s) = vm.sign(privateKey, digest);
return EpochProofQuoteLib.SignedEpochProofQuote({
quote: _quote,
signature: SignatureLib.Signature({isEmpty: false, v: v, r: r, s: s})
Expand Down
9 changes: 6 additions & 3 deletions l1-contracts/test/fee_portal/depositToAztecPublic.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import {DataStructures} from "@aztec/core/libraries/DataStructures.sol";
import {Hash} from "@aztec/core/libraries/crypto/Hash.sol";
import {IInbox} from "@aztec/core/interfaces/messagebridge/IInbox.sol";
import {Inbox} from "@aztec/core/messagebridge/Inbox.sol";
import {Sysstia} from "@aztec/governance/Sysstia.sol";

contract DepositToAztecPublic is Test {
using Hash for DataStructures.L1ToL2Msg;
Expand All @@ -22,6 +23,7 @@ contract DepositToAztecPublic is Test {
TestERC20 internal token;
FeeJuicePortal internal feeJuicePortal;
Rollup internal rollup;
Sysstia internal sysstia;

function setUp() public {
registry = new Registry(OWNER);
Expand All @@ -31,8 +33,9 @@ contract DepositToAztecPublic is Test {

token.mint(address(feeJuicePortal), Constants.FEE_JUICE_INITIAL_MINT);
feeJuicePortal.initialize();

rollup = new Rollup(feeJuicePortal, bytes32(0), bytes32(0), address(this), new address[](0));
sysstia = new Sysstia(token, registry);
rollup =
new Rollup(feeJuicePortal, sysstia, bytes32(0), bytes32(0), address(this), new address[](0));

vm.prank(OWNER);
registry.upgrade(address(rollup));
Expand Down Expand Up @@ -64,7 +67,7 @@ contract DepositToAztecPublic is Test {
uint256 numberOfRollups = bound(_numberOfRollups, 1, 5);
for (uint256 i = 0; i < numberOfRollups; i++) {
Rollup freshRollup =
new Rollup(feeJuicePortal, bytes32(0), bytes32(0), address(this), new address[](0));
new Rollup(feeJuicePortal, sysstia, bytes32(0), bytes32(0), address(this), new address[](0));
vm.prank(OWNER);
registry.upgrade(address(freshRollup));
}
Expand Down
Loading