Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: publish block body separately #4118

Merged
merged 14 commits into from
Jan 19, 2024
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
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.

Copy link
Contributor Author

@benesjan benesjan Jan 19, 2024

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;

benesjan marked this conversation as resolved.
Show resolved Hide 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