Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
164 changes: 164 additions & 0 deletions yarn-project/prover-node/src/prover-node-publisher.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,164 @@
import { AGGREGATION_OBJECT_LENGTH } from '@aztec/constants';
import type { L1TxUtils, RollupContract } from '@aztec/ethereum';
import { times } from '@aztec/foundation/collection';
import { EthAddress } from '@aztec/foundation/eth-address';
import { Fr } from '@aztec/foundation/fields';
import type { PublisherConfig, TxSenderConfig } from '@aztec/sequencer-client';
import { Proof } from '@aztec/stdlib/proofs';
import { RootRollupPublicInputs } from '@aztec/stdlib/rollup';

import { type MockProxy, mock } from 'jest-mock-extended';

import { ProverNodePublisher } from './prover-node-publisher.js';

describe('prover-node-publisher', () => {
// Prover publisher dependencies
let rollup: MockProxy<RollupContract>;
let l1Utils: MockProxy<L1TxUtils>;

let publisher: ProverNodePublisher;

let config: TxSenderConfig & PublisherConfig;

beforeEach(() => {
rollup = mock<RollupContract>();
l1Utils = mock<L1TxUtils>();

config = {
l1ChainId: 1,
l1RpcUrls: ['http://localhost:8545'],
publisherPrivateKey: '0x1234',
l1PublishRetryIntervalMS: 1000,
viemPollingIntervalMS: 1000,
customForwarderContractAddress: EthAddress.random(),
l1Contracts: {
rollupAddress: EthAddress.random(),
registryAddress: EthAddress.random(),
inboxAddress: EthAddress.random(),
outboxAddress: EthAddress.random(),
rewardDistributorAddress: EthAddress.random(),
feeJuicePortalAddress: EthAddress.random(),
coinIssuerAddress: EthAddress.random(),
governanceAddress: EthAddress.random(),
governanceProposerAddress: EthAddress.random(),
feeJuiceAddress: EthAddress.random(),
stakingAssetAddress: EthAddress.random(),
},
};
});

beforeEach(() => {
publisher = new ProverNodePublisher(config, { rollupContract: rollup, l1TxUtils: l1Utils });
});

const testCases = [
// Usual case of proving full epoch
{ pendingBlockNumber: 65n, provenBlockNumber: 32n, fromBlock: 33, toBlock: 64, expectedPublish: true, message: '' },
// Failure case of proving beyond the pending chain
{
pendingBlockNumber: 65n,
provenBlockNumber: 32n,
fromBlock: 33,
toBlock: 66,
expectedPublish: false,
message: 'Cannot submit epoch proof for 33-66 as pending block is 65',
},
// Some successful partial epochs
{ pendingBlockNumber: 33n, provenBlockNumber: 32n, fromBlock: 33, toBlock: 33, expectedPublish: true, message: '' },
{ pendingBlockNumber: 65n, provenBlockNumber: 32n, fromBlock: 33, toBlock: 38, expectedPublish: true, message: '' },
{ pendingBlockNumber: 40n, provenBlockNumber: 32n, fromBlock: 33, toBlock: 33, expectedPublish: true, message: '' },

// Somebody else proved the entire epoch already

// We try and prove the full epoch - succeeds
{ pendingBlockNumber: 65n, provenBlockNumber: 64n, fromBlock: 33, toBlock: 64, expectedPublish: true, message: '' },

// We try and prove a partial epoch that falls short of the end - fails as pointless to publish
{
pendingBlockNumber: 65n,
provenBlockNumber: 64n,
fromBlock: 33,
toBlock: 35,
expectedPublish: false,
message: 'Cannot submit epoch proof for 33-35 as proven block is 64',
},

// Somebody else partially proved the epoch already

// We try and prove the rest of the epoch - succeeds
{ pendingBlockNumber: 65n, provenBlockNumber: 40n, fromBlock: 41, toBlock: 64, expectedPublish: true, message: '' },

// We try and prove all of the epoch - succeeds
{ pendingBlockNumber: 65n, provenBlockNumber: 40n, fromBlock: 33, toBlock: 64, expectedPublish: true, message: '' },

// We try and partially prove the epoch after their proof - succeeds again
{ pendingBlockNumber: 65n, provenBlockNumber: 40n, fromBlock: 41, toBlock: 45, expectedPublish: true, message: '' },

// We try and partially prove the epoch on top of their proof - succeeds again
{ pendingBlockNumber: 65n, provenBlockNumber: 40n, fromBlock: 33, toBlock: 45, expectedPublish: true, message: '' },

// We try and partially prove the epoch and partially on top of their proof - succeeds again
{ pendingBlockNumber: 65n, provenBlockNumber: 40n, fromBlock: 35, toBlock: 45, expectedPublish: true, message: '' },

// We try and partially prove the epoch but less than was already proven - fails as pointless
{
pendingBlockNumber: 65n,
provenBlockNumber: 40n,
fromBlock: 33,
toBlock: 39,
expectedPublish: false,
message: 'Cannot submit epoch proof for 33-39 as proven block is 40',
},

// We try and partially prove the epoch but the same as was already proven - should possibly fail but succeeds for now, quite an edge case
{
pendingBlockNumber: 65n,
provenBlockNumber: 40n,
fromBlock: 33,
toBlock: 40,
expectedPublish: true,
},
];

test.each(testCases)(
'submits proof for epoch with pendingBlock: $pendingBlockNumber, provenBlock: $provenBlockNumber, fromBlock: $fromBlock, toBlock: $toBlock',
async ({ pendingBlockNumber, provenBlockNumber, fromBlock, toBlock, expectedPublish, message }) => {
const publicInputs = RootRollupPublicInputs.random();
rollup.getTips.mockResolvedValue({
pendingBlockNumber,
provenBlockNumber,
});
rollup.getBlock.mockResolvedValueOnce({
blockHash: publicInputs.previousBlockHash.toString(),
archive: publicInputs.previousArchive.root.toString(),
slotNumber: provenBlockNumber - 1n,
});
rollup.getBlock.mockResolvedValueOnce({
blockHash: publicInputs.endBlockHash.toString(),
archive: publicInputs.endArchive.root.toString(),
slotNumber: pendingBlockNumber - 2n,
});
const totalFields = publicInputs.toFields().concat(times(AGGREGATION_OBJECT_LENGTH, Fr.zero));
rollup.getEpochProofPublicInputs.mockResolvedValue(totalFields.map(x => x.toString()));

const result = await publisher
.submitEpochProof({
epochNumber: 1,
fromBlock,
toBlock,
publicInputs,
proof: Proof.empty(),
})
.then(() => 'Success')
.catch(error => error.message);

if (expectedPublish) {
expect(result).toBe('Success');
expect(l1Utils.sendAndMonitorTransaction).toHaveBeenCalled();
} else {
expect(result).toBe(message);
expect(l1Utils.sendAndMonitorTransaction).not.toHaveBeenCalled();
}
},
);
});
4 changes: 3 additions & 1 deletion yarn-project/prover-node/src/prover-node-publisher.ts
Original file line number Diff line number Diff line change
Expand Up @@ -150,9 +150,11 @@ export class ProverNodePublisher {

// Check that the block numbers match the expected epoch to be proven
const { pendingBlockNumber: pending, provenBlockNumber: proven } = await this.rollupContract.getTips();
if (proven !== BigInt(fromBlock) - 1n) {
// Don't publish if proven is beyond our toBlock, pointless to do so
if (proven > BigInt(toBlock)) {
throw new Error(`Cannot submit epoch proof for ${fromBlock}-${toBlock} as proven block is ${proven}`);
}
// toBlock can't be greater than pending
if (toBlock > pending) {
throw new Error(`Cannot submit epoch proof for ${fromBlock}-${toBlock} as pending block is ${pending}`);
}
Expand Down