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
26 changes: 10 additions & 16 deletions l1-contracts/src/core/Rollup.sol
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ pragma solidity >=0.8.18;

// Interfaces
import {IRollup} from "./interfaces/IRollup.sol";
import {IAvailabilityOracle} from "./interfaces/IAvailabilityOracle.sol";
import {IInbox} from "./interfaces/messagebridge/IInbox.sol";
import {IOutbox} from "./interfaces/messagebridge/IOutbox.sol";
import {IRegistry} from "./interfaces/messagebridge/IRegistry.sol";
Expand All @@ -16,7 +17,6 @@ import {Errors} from "./libraries/Errors.sol";

// Contracts
import {MockVerifier} from "../mock/MockVerifier.sol";
import {AvailabilityOracle} from "./availability_oracle/AvailabilityOracle.sol";

/**
* @title Rollup
Expand All @@ -27,58 +27,52 @@ import {AvailabilityOracle} from "./availability_oracle/AvailabilityOracle.sol";
contract Rollup is IRollup {
MockVerifier public immutable VERIFIER;
IRegistry public immutable REGISTRY;
IAvailabilityOracle public immutable AVAILABILITY_ORACLE;
uint256 public immutable VERSION;
AvailabilityOracle public immutable AVAILABILITY_ORACLE;

bytes32 public archive; // Root of the archive tree
uint256 public lastBlockTs;
// Tracks the last time time was warped on L2 ("warp" is the testing cheatcode).
// See https://github.com/AztecProtocol/aztec-packages/issues/1614
uint256 public lastWarpedBlockTs;

constructor(IRegistry _registry) {
constructor(IRegistry _registry, IAvailabilityOracle _availabilityOracle) {
VERIFIER = new MockVerifier();
AVAILABILITY_ORACLE = new AvailabilityOracle();
REGISTRY = _registry;
AVAILABILITY_ORACLE = _availabilityOracle;
VERSION = 1;
}

/**
* @notice Process an incoming L2 block and progress the state
* @param _header - The L2 block header
* @param _archive - A root of the archive tree after the L2 block is applied
* @param _txsHash - Transactions hash.
* @param _body - The L2 block body
* @param _proof - The proof of correct execution
*/
function process(
bytes calldata _header,
bytes32 _archive,
bytes calldata _body, // TODO(#3944): this will be replaced with _txsHash once the separation is finished.
bytes32 _txsHash, // TODO(#3938) Update this to be actual txs hash and not the old block calldata hash.
bytes calldata _body, // TODO(#3938) Update this to pass in only th messages and not the whole body.
bytes memory _proof
) external override(IRollup) {
// Decode and validate header
HeaderLib.Header memory header = HeaderLib.decode(_header);
HeaderLib.validate(header, VERSION, lastBlockTs, archive);

// Check if the data is available using availability oracle (change availability oracle if you want a different DA layer)
bytes32 txsHash;
{
// @todo @LHerskind Hack such that the node is unchanged for now.
// should be removed when we have a proper block publication.
txsHash = AVAILABILITY_ORACLE.publish(_body);
}

if (!AVAILABILITY_ORACLE.isAvailable(txsHash)) {
// @todo @LHerskind Impossible to hit with above hack.
revert Errors.Rollup__UnavailableTxs(txsHash);
if (!AVAILABILITY_ORACLE.isAvailable(_txsHash)) {
revert Errors.Rollup__UnavailableTxs(_txsHash);
}

// Decode the cross-chain messages
(bytes32 inHash,, bytes32[] memory l1ToL2Msgs, bytes32[] memory l2ToL1Msgs) =
MessagesDecoder.decode(_body);

bytes32[] memory publicInputs = new bytes32[](1);
publicInputs[0] = _computePublicInputHash(_header, txsHash, inHash);
publicInputs[0] = _computePublicInputHash(_header, _txsHash, inHash);

// @todo @benesjan We will need `nextAvailableLeafIndex` of archive to verify the proof. This value is equal to
// current block number which is stored in the header (header.globalVariables.blockNumber).
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,15 @@ contract AvailabilityOracle is IAvailabilityOracle {

/**
* @notice Publishes transactions and marks its commitment, the TxsHash, as available
* @param _body - The L1 calldata
* @param _body - The block body
* @return txsHash - The TxsHash
*/
function publish(bytes calldata _body) external override(IAvailabilityOracle) returns (bytes32) {
bytes32 _txsHash = TxsDecoder.decode(_body);
isAvailable[_txsHash] = true;

emit TxsPublished(_txsHash);

return _txsHash;
}
}
2 changes: 2 additions & 0 deletions l1-contracts/src/core/interfaces/IAvailabilityOracle.sol
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
pragma solidity >=0.8.18;

interface IAvailabilityOracle {
event TxsPublished(bytes32 txsHash);

function publish(bytes calldata _body) external returns (bytes32);

function isAvailable(bytes32 _txsHash) external view returns (bool);
Expand Down
1 change: 1 addition & 0 deletions l1-contracts/src/core/interfaces/IRollup.sol
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ interface IRollup {
function process(
bytes calldata _header,
bytes32 _archive,
bytes32 _txsHash,
bytes calldata _body,
bytes memory _proof
) external;
Expand Down
2 changes: 1 addition & 1 deletion l1-contracts/src/core/libraries/HeaderLib.sol
Original file line number Diff line number Diff line change
Expand Up @@ -146,7 +146,7 @@ library HeaderLib {
revert Errors.Rollup__TimestampTooOld();
}

// @todo @LHerskind Proper genesis state. If the state is empty, we allow anything for now.
// TODO(#4148) Proper genesis state. If the state is empty, we allow anything for now.
if (_archive != bytes32(0) && _archive != _header.lastArchive.root) {
revert Errors.Rollup__InvalidArchive(_archive, _header.lastArchive.root);
}
Expand Down
25 changes: 19 additions & 6 deletions l1-contracts/test/Rollup.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import {Inbox} from "../src/core/messagebridge/Inbox.sol";
import {Outbox} from "../src/core/messagebridge/Outbox.sol";
import {Errors} from "../src/core/libraries/Errors.sol";
import {Rollup} from "../src/core/Rollup.sol";
import {AvailabilityOracle} from "../src/core/availability_oracle/AvailabilityOracle.sol";

/**
* Blocks are generated using the `integration_l1_publisher.test.ts` tests.
Expand All @@ -27,14 +28,16 @@ contract RollupTest is DecoderBase {
Inbox internal inbox;
Outbox internal outbox;
Rollup internal rollup;
AvailabilityOracle internal availabilityOracle;

function setUp() public virtual {
helper = new DecoderHelper();

registry = new Registry();
inbox = new Inbox(address(registry));
outbox = new Outbox(address(registry));
rollup = new Rollup(registry);
availabilityOracle = new AvailabilityOracle();
rollup = new Rollup(registry, availabilityOracle);

registry.upgrade(address(rollup), address(inbox), address(outbox));
}
Expand Down Expand Up @@ -67,8 +70,10 @@ contract RollupTest is DecoderBase {
mstore(add(header, 0x20), 0x420)
}

bytes32 txsHash = availabilityOracle.publish(body);

vm.expectRevert(abi.encodeWithSelector(Errors.Rollup__InvalidChainId.selector, 0x420, 31337));
rollup.process(header, archive, body, bytes(""));
rollup.process(header, archive, txsHash, body, bytes(""));
}

function testRevertInvalidVersion() public {
Expand All @@ -81,8 +86,10 @@ contract RollupTest is DecoderBase {
mstore(add(header, 0x40), 0x420)
}

bytes32 txsHash = availabilityOracle.publish(body);

vm.expectRevert(abi.encodeWithSelector(Errors.Rollup__InvalidVersion.selector, 0x420, 1));
rollup.process(header, archive, body, bytes(""));
rollup.process(header, archive, txsHash, body, bytes(""));
}

function testRevertTimestampInFuture() public {
Expand All @@ -96,8 +103,10 @@ contract RollupTest is DecoderBase {
mstore(add(header, 0x80), ts)
}

bytes32 txsHash = availabilityOracle.publish(body);

vm.expectRevert(abi.encodeWithSelector(Errors.Rollup__TimestampInFuture.selector));
rollup.process(header, archive, body, bytes(""));
rollup.process(header, archive, txsHash, body, bytes(""));
}

function testRevertTimestampTooOld() public {
Expand All @@ -109,8 +118,10 @@ contract RollupTest is DecoderBase {
// Overwrite in the rollup contract
vm.store(address(rollup), bytes32(uint256(1)), bytes32(uint256(block.timestamp)));

bytes32 txsHash = availabilityOracle.publish(body);

vm.expectRevert(abi.encodeWithSelector(Errors.Rollup__TimestampTooOld.selector));
rollup.process(header, archive, body, bytes(""));
rollup.process(header, archive, txsHash, body, bytes(""));
}

function _testBlock(string memory name) public {
Expand All @@ -131,8 +142,10 @@ contract RollupTest is DecoderBase {
assertTrue(inbox.contains(full.messages.l1ToL2Messages[i]), "msg not in inbox");
}

bytes32 txsHash = availabilityOracle.publish(body);

vm.record();
rollup.process(header, archive, body, bytes(""));
rollup.process(header, archive, txsHash, body, bytes(""));

(, bytes32[] memory inboxWrites) = vm.accesses(address(inbox));
(, bytes32[] memory outboxWrites) = vm.accesses(address(outbox));
Expand Down
3 changes: 2 additions & 1 deletion l1-contracts/test/portals/TokenPortal.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import "forge-std/Test.sol";

// Rollup Processor
import {Rollup} from "../../src/core/Rollup.sol";
import {AvailabilityOracle} from "../../src/core/availability_oracle/AvailabilityOracle.sol";
import {Inbox} from "../../src/core/messagebridge/Inbox.sol";
import {Registry} from "../../src/core/messagebridge/Registry.sol";
import {Outbox} from "../../src/core/messagebridge/Outbox.sol";
Expand Down Expand Up @@ -64,7 +65,7 @@ contract TokenPortalTest is Test {
registry = new Registry();
inbox = new Inbox(address(registry));
outbox = new Outbox(address(registry));
rollup = new Rollup(registry);
rollup = new Rollup(registry, new AvailabilityOracle());

registry.upgrade(address(rollup), address(inbox), address(outbox));

Expand Down
3 changes: 2 additions & 1 deletion l1-contracts/test/portals/UniswapPortal.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import "forge-std/Test.sol";

// Rollup Processor
import {Rollup} from "../../src/core/Rollup.sol";
import {AvailabilityOracle} from "../../src/core/availability_oracle/AvailabilityOracle.sol";
import {Inbox} from "../../src/core/messagebridge/Inbox.sol";
import {Registry} from "../../src/core/messagebridge/Registry.sol";
import {Outbox} from "../../src/core/messagebridge/Outbox.sol";
Expand Down Expand Up @@ -51,7 +52,7 @@ contract UniswapPortalTest is Test {
Registry registry = new Registry();
inbox = new Inbox(address(registry));
outbox = new Outbox(address(registry));
rollup = new Rollup(registry);
rollup = new Rollup(registry, new AvailabilityOracle());
registry.upgrade(address(rollup), address(inbox), address(outbox));

daiTokenPortal = new TokenPortal();
Expand Down
7 changes: 6 additions & 1 deletion yarn-project/archiver/src/archiver/archiver.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -351,8 +351,13 @@ function makeL1ToL2MessageCancelledEvents(l1BlockNum: bigint, entryKeys: string[
function makeRollupTx(l2Block: L2Block) {
const header = toHex(l2Block.header.toBuffer());
const archive = toHex(l2Block.archive.root.toBuffer());
const txsHash = toHex(l2Block.getCalldataHash());
const body = toHex(l2Block.bodyToBuffer());
const proof = `0x`;
const input = encodeFunctionData({ abi: RollupAbi, functionName: 'process', args: [header, archive, body, proof] });
const input = encodeFunctionData({
abi: RollupAbi,
functionName: 'process',
args: [header, archive, txsHash, body, proof],
});
return { input } as Transaction<bigint, number>;
}
4 changes: 4 additions & 0 deletions yarn-project/archiver/src/archiver/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ export function getConfigEnvVars(): ArchiverConfig {
ETHEREUM_HOST,
ARCHIVER_POLLING_INTERVAL_MS,
ARCHIVER_VIEM_POLLING_INTERVAL_MS,
AVAILABILITY_ORACLE_CONTRACT_ADDRESS,
ROLLUP_CONTRACT_ADDRESS,
CONTRACT_DEPLOYMENT_EMITTER_ADDRESS,
API_KEY,
Expand All @@ -66,6 +67,9 @@ export function getConfigEnvVars(): ArchiverConfig {
} = process.env;
// Populate the relevant addresses for use by the archiver.
const addresses: L1ContractAddresses = {
availabilityOracleAddress: AVAILABILITY_ORACLE_CONTRACT_ADDRESS
? EthAddress.fromString(AVAILABILITY_ORACLE_CONTRACT_ADDRESS)
: EthAddress.ZERO,
rollupAddress: ROLLUP_CONTRACT_ADDRESS ? EthAddress.fromString(ROLLUP_CONTRACT_ADDRESS) : EthAddress.ZERO,
registryAddress: REGISTRY_CONTRACT_ADDRESS ? EthAddress.fromString(REGISTRY_CONTRACT_ADDRESS) : EthAddress.ZERO,
inboxAddress: INBOX_CONTRACT_ADDRESS ? EthAddress.fromString(INBOX_CONTRACT_ADDRESS) : EthAddress.ZERO,
Expand Down
2 changes: 1 addition & 1 deletion yarn-project/archiver/src/archiver/eth_log_handlers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,7 @@ async function getBlockFromCallData(
if (functionName !== 'process') {
throw new Error(`Unexpected method called ${functionName}`);
}
const [headerHex, archiveRootHex, bodyHex] = args! as [Hex, Hex, Hex, Hex];
const [headerHex, archiveRootHex, , bodyHex] = args! as [Hex, Hex, Hex, Hex, Hex];

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

When you are working on #3938 would it not mess with your decoding here? It would not longer be practical to get the full block from just this kind of function call as the bodyHex would not be here.

This part seems to be very reliant on the content being published at the same time for getting any data.

In my mind you would need to fetch the body separately through the extra event you added. And then as l2 blocks are seen you can combine the two pieces together for the block.

As this is the synching part, it can be handled separately, a new pr for it might be nice to have the split fully dealt with? This seems a bit like cheating. But let us talk about it, as the new changes for messages would likely mess with it a bit, so an initial pr could focus on just needing to pass the messages and nothing else. That way we can address synching separately to the hashing updates.

@benesjan benesjan Jan 19, 2024

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

In my mind you would need to fetch the body separately through the extra event you added. And then as l2 blocks are seen you can combine the two pieces together for the block.

Yes, exactly. Would find the relevant event based on txsHash, from that get the transaction hash and from that the calldata.

And I just realized that for that I will need the index the hash in TxsPublished event. Will do that once I'll deal with this in that other PR.

const blockBuffer = Buffer.concat([
Buffer.from(hexToBytes(headerHex)),
Buffer.from(hexToBytes(archiveRootHex)), // L2Block.archive.root
Expand Down
6 changes: 6 additions & 0 deletions yarn-project/aztec-sandbox/src/sandbox.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ import {
import { createDebugLogger } from '@aztec/foundation/log';
import { retryUntil } from '@aztec/foundation/retry';
import {
AvailabilityOracleAbi,
AvailabilityOracleBytecode,
ContractDeploymentEmitterAbi,
ContractDeploymentEmitterBytecode,
InboxAbi,
Expand Down Expand Up @@ -90,6 +92,10 @@ export async function deployContractsToL1(aztecNodeConfig: AztecNodeConfig, hdAc
contractAbi: OutboxAbi,
contractBytecode: OutboxBytecode,
},
availabilityOracle: {
contractAbi: AvailabilityOracleAbi,
contractBytecode: AvailabilityOracleBytecode,
},
rollup: {
contractAbi: RollupAbi,
contractBytecode: RollupBytecode,
Expand Down
1 change: 1 addition & 0 deletions yarn-project/aztec.js/src/contract/contract.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ describe('Contract Class', () => {
const mockTxReceipt = { type: 'TxReceipt' } as any as TxReceipt;
const mockViewResultValue = 1;
const l1Addresses: L1ContractAddresses = {
availabilityOracleAddress: EthAddress.random(),
rollupAddress: EthAddress.random(),
registryAddress: EthAddress.random(),
inboxAddress: EthAddress.random(),
Expand Down
5 changes: 5 additions & 0 deletions yarn-project/cli/src/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { type L1ContractArtifactsForDeployment } from '@aztec/aztec.js/ethereum'
import { type PXE } from '@aztec/aztec.js/interfaces/pxe';
import { DebugLogger, LogFn } from '@aztec/foundation/log';
import { NoirPackageConfig } from '@aztec/foundation/noir';
import { AvailabilityOracleAbi, AvailabilityOracleBytecode } from '@aztec/l1-artifacts';

import TOML from '@iarna/toml';
import { CommanderError, InvalidArgumentError } from 'commander';
Expand Down Expand Up @@ -80,6 +81,10 @@ export async function deployAztecContracts(
contractAbi: OutboxAbi,
contractBytecode: OutboxBytecode,
},
availabilityOracle: {
contractAbi: AvailabilityOracleAbi,
contractBytecode: AvailabilityOracleBytecode,
},
rollup: {
contractAbi: RollupAbi,
contractBytecode: RollupBytecode,
Expand Down
13 changes: 7 additions & 6 deletions yarn-project/end-to-end/src/fixtures/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ import {
waitForPXE,
} from '@aztec/aztec.js';
import {
AvailabilityOracleAbi,
AvailabilityOracleBytecode,
ContractDeploymentEmitterAbi,
ContractDeploymentEmitterBytecode,
InboxAbi,
Expand Down Expand Up @@ -87,6 +89,10 @@ export const setupL1Contracts = async (
contractAbi: OutboxAbi,
contractBytecode: OutboxBytecode,
},
availabilityOracle: {
contractAbi: AvailabilityOracleAbi,
contractBytecode: AvailabilityOracleBytecode,
},
rollup: {
contractAbi: RollupAbi,
contractBytecode: RollupBytecode,
Expand Down Expand Up @@ -279,12 +285,7 @@ export async function setup(
opts.deployL1ContractsValues ?? (await setupL1Contracts(config.rpcUrl, hdAccount, logger));

config.publisherPrivateKey = `0x${publisherPrivKey!.toString('hex')}`;
config.l1Contracts.rollupAddress = deployL1ContractsValues.l1ContractAddresses.rollupAddress;
config.l1Contracts.registryAddress = deployL1ContractsValues.l1ContractAddresses.registryAddress;
config.l1Contracts.contractDeploymentEmitterAddress =
deployL1ContractsValues.l1ContractAddresses.contractDeploymentEmitterAddress;
config.l1Contracts.inboxAddress = deployL1ContractsValues.l1ContractAddresses.inboxAddress;
config.l1Contracts.outboxAddress = deployL1ContractsValues.l1ContractAddresses.outboxAddress;
config.l1Contracts = deployL1ContractsValues.l1ContractAddresses;

Comment thread
benesjan marked this conversation as resolved.
logger('Creating and synching an aztec node...');
const aztecNode = await AztecNodeService.createAndSync(config);
Expand Down
2 changes: 2 additions & 0 deletions yarn-project/end-to-end/src/integration_l1_publisher.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -407,6 +407,7 @@ describe('L1Publisher integration', () => {
args: [
`0x${block.header.toBuffer().toString('hex')}`,
`0x${block.archive.root.toBuffer().toString('hex')}`,
`0x${block.getCalldataHash().toString('hex')}`,
`0x${block.bodyToBuffer().toString('hex')}`,
`0x${l2Proof.toString('hex')}`,
],
Expand Down Expand Up @@ -475,6 +476,7 @@ describe('L1Publisher integration', () => {
args: [
`0x${block.header.toBuffer().toString('hex')}`,
`0x${block.archive.root.toBuffer().toString('hex')}`,
`0x${block.getCalldataHash().toString('hex')}`,
`0x${block.bodyToBuffer().toString('hex')}`,
`0x${l2Proof.toString('hex')}`,
],
Expand Down
Loading