Skip to content

Commit

Permalink
feat: initial block reward + external libraries (#9297)
Browse files Browse the repository at this point in the history
Fixes #8791.
  • Loading branch information
LHerskind authored Oct 24, 2024
1 parent 84fdc52 commit 240e9b5
Show file tree
Hide file tree
Showing 24 changed files with 437 additions and 150 deletions.
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();

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

0 comments on commit 240e9b5

Please sign in to comment.