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: node verifies proofs #6735

Merged
merged 1 commit into from
May 30, 2024
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
1 change: 1 addition & 0 deletions yarn-project/aztec-node/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@
},
"dependencies": {
"@aztec/archiver": "workspace:^",
"@aztec/bb-prover": "workspace:^",
"@aztec/circuit-types": "workspace:^",
"@aztec/circuits.js": "workspace:^",
"@aztec/ethereum": "workspace:^",
Expand Down
58 changes: 50 additions & 8 deletions yarn-project/aztec-node/src/aztec-node/server.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import { type ArchiveSource, Archiver, KVArchiverDataStore, createArchiverClient } from '@aztec/archiver';
import { BBCircuitVerifier, TestCircuitVerifier } from '@aztec/bb-prover';
import {
AggregateTxValidator,
type AztecNode,
type FromLogType,
type GetUnencryptedLogsResponse,
Expand All @@ -24,6 +26,7 @@ import {
type TxHash,
TxReceipt,
TxStatus,
type TxValidator,
partitionReverts,
} from '@aztec/circuit-types';
import {
Expand All @@ -49,7 +52,7 @@ import { AztecLmdbStore } from '@aztec/kv-store/lmdb';
import { initStoreForRollup, openTmpStore } from '@aztec/kv-store/utils';
import { SHA256Trunc, StandardTree } from '@aztec/merkle-tree';
import { AztecKVTxPool, type P2P, createP2PClient } from '@aztec/p2p';
import { DummyProver, TxProver } from '@aztec/prover-client';
import { TxProver } from '@aztec/prover-client';
import { type GlobalVariableBuilder, SequencerClient, getGlobalVariableBuilder } from '@aztec/sequencer-client';
import { PublicProcessorFactory, WASMSimulator } from '@aztec/simulator';
import {
Expand All @@ -67,13 +70,15 @@ import {

import { type AztecNodeConfig } from './config.js';
import { getSimulationProvider } from './simulator-factory.js';
import { MetadataTxValidator } from './tx_validator/tx_metadata_validator.js';
import { TxProofValidator } from './tx_validator/tx_proof_validator.js';

/**
* The aztec node.
*/
export class AztecNodeService implements AztecNode {
constructor(
protected readonly config: AztecNodeConfig,
protected config: AztecNodeConfig,
protected readonly p2pClient: P2P,
protected readonly blockSource: L2BlockSource,
protected readonly encryptedLogsSource: L2LogsSource,
Expand All @@ -86,7 +91,8 @@ export class AztecNodeService implements AztecNode {
protected readonly version: number,
protected readonly globalVariableBuilder: GlobalVariableBuilder,
protected readonly merkleTreesDb: AztecKVStore,
private readonly prover: ProverClient,
private readonly prover: ProverClient | undefined,
private txValidator: TxValidator,
private log = createDebugLogger('aztec:node'),
) {
const message =
Expand Down Expand Up @@ -145,9 +151,21 @@ export class AztecNodeService implements AztecNode {
// start both and wait for them to sync from the block source
await Promise.all([p2pClient.start(), worldStateSynchronizer.start()]);

const proofVerifier = config.realProofs ? await BBCircuitVerifier.new(config) : new TestCircuitVerifier();
const txValidator = new AggregateTxValidator(
new MetadataTxValidator(config.chainId),
new TxProofValidator(proofVerifier),
);

// start the prover if we have been told to
const simulationProvider = await getSimulationProvider(config, log);
const prover = config.disableProver ? await DummyProver.new() : await TxProver.new(config, worldStateSynchronizer);
const prover = config.disableProver
? undefined
: await TxProver.new(config, await proofVerifier.getVerificationKeys(), worldStateSynchronizer);

if (!prover && !config.disableSequencer) {
throw new Error("Can't start a sequencer without a prover");
}

// now create the sequencer
const sequencer = config.disableSequencer
Expand All @@ -159,7 +177,7 @@ export class AztecNodeService implements AztecNode {
archiver,
archiver,
archiver,
prover,
prover!,
simulationProvider,
);

Expand All @@ -178,6 +196,7 @@ export class AztecNodeService implements AztecNode {
getGlobalVariableBuilder(config),
store,
prover,
txValidator,
log,
);
}
Expand All @@ -190,7 +209,7 @@ export class AztecNodeService implements AztecNode {
return this.sequencer;
}

public getProver(): ProverClient {
public getProver(): ProverClient | undefined {
return this.prover;
}

Expand Down Expand Up @@ -292,6 +311,13 @@ export class AztecNodeService implements AztecNode {
*/
public async sendTx(tx: Tx) {
this.log.info(`Received tx ${tx.getTxHash()}`);

const [_, invalidTxs] = await this.txValidator.validateTxs([tx]);
if (invalidTxs.length > 0) {
this.log.warn(`Rejecting tx ${tx.getTxHash()} because of validation errors`);
return;
}

await this.p2pClient!.sendTx(tx);
}

Expand Down Expand Up @@ -327,7 +353,7 @@ export class AztecNodeService implements AztecNode {
await this.p2pClient.stop();
await this.worldStateSynchronizer.stop();
await this.blockSource.stop();
await this.prover.stop();
await this.prover?.stop();
this.log.info(`Stopped`);
}

Expand Down Expand Up @@ -684,8 +710,24 @@ export class AztecNodeService implements AztecNode {
}

public async setConfig(config: Partial<SequencerConfig & ProverConfig>): Promise<void> {
const newConfig = { ...this.config, ...config };
this.sequencer?.updateSequencerConfig(config);
await this.prover.updateProverConfig(config);
await this.prover?.updateProverConfig(config);

if (newConfig.realProofs !== this.config.realProofs) {
const proofVerifier = config.realProofs ? await BBCircuitVerifier.new(newConfig) : new TestCircuitVerifier();

this.txValidator = new AggregateTxValidator(
new MetadataTxValidator(this.chainId),
new TxProofValidator(proofVerifier),
);

await this.prover?.updateProverConfig({
vks: await proofVerifier.getVerificationKeys(),
});
}

this.config = newConfig;
}

/**
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import { mockTx, mockTxForRollup } from '@aztec/circuit-types';
import { Fr } from '@aztec/circuits.js';

import { MetadataTxValidator } from './tx_metadata_validator.js';

describe('MetadataTxValidator', () => {
let chainId: Fr;
let validator: MetadataTxValidator;

beforeEach(() => {
chainId = new Fr(123);
validator = new MetadataTxValidator(chainId);
});

it('allows only transactions for the right chain', async () => {
const goodTxs = [mockTx(1), mockTxForRollup(2)];
const badTxs = [mockTx(3), mockTxForRollup(4)];

goodTxs.forEach(tx => {
tx.data.constants.txContext.chainId = chainId;
});

badTxs.forEach(tx => {
tx.data.constants.txContext.chainId = chainId.add(new Fr(1));
});

await expect(validator.validateTxs([...goodTxs, ...badTxs])).resolves.toEqual([goodTxs, badTxs]);
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import { Tx, type TxValidator } from '@aztec/circuit-types';
import { Fr } from '@aztec/circuits.js';
import { createDebugLogger } from '@aztec/foundation/log';

export class MetadataTxValidator implements TxValidator<Tx> {
#log = createDebugLogger('aztec:sequencer:tx_validator:tx_metadata');
#chainId: Fr;

constructor(chainId: number | Fr) {
this.#chainId = new Fr(chainId);
}

validateTxs(txs: Tx[]): Promise<[validTxs: Tx[], invalidTxs: Tx[]]> {
const validTxs: Tx[] = [];
const invalidTxs: Tx[] = [];
for (const tx of txs) {
if (!this.#hasCorrectChainId(tx)) {
invalidTxs.push(tx);
continue;
}

validTxs.push(tx);
}

return Promise.resolve([validTxs, invalidTxs]);
}

#hasCorrectChainId(tx: Tx): boolean {
if (!tx.data.constants.txContext.chainId.equals(this.#chainId)) {
this.#log.warn(
`Rejecting tx ${Tx.getHash(
tx,
)} because of incorrect chain ${tx.data.constants.txContext.chainId.toNumber()} != ${this.#chainId.toNumber()}`,
);
return false;
} else {
return true;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import { type ClientProtocolCircuitVerifier, Tx, type TxValidator } from '@aztec/circuit-types';
import { createDebugLogger } from '@aztec/foundation/log';

export class TxProofValidator implements TxValidator<Tx> {
#log = createDebugLogger('aztec:sequencer:tx_validator:private_proof');

constructor(private verifier: ClientProtocolCircuitVerifier) {}

async validateTxs(txs: Tx[]): Promise<[validTxs: Tx[], invalidTxs: Tx[]]> {
const validTxs: Tx[] = [];
const invalidTxs: Tx[] = [];

for (const tx of txs) {
if (await this.verifier.verifyProof(tx)) {
validTxs.push(tx);
} else {
this.#log.warn(`Rejecting tx ${Tx.getHash(tx)} for invalid proof`);
invalidTxs.push(tx);
}
}

return [validTxs, invalidTxs];
}

validateTx(tx: Tx): Promise<boolean> {
return this.verifier.verifyProof(tx);
}
}
3 changes: 3 additions & 0 deletions yarn-project/aztec-node/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,9 @@
{
"path": "../archiver"
},
{
"path": "../bb-prover"
},
{
"path": "../circuit-types"
},
Expand Down
2 changes: 1 addition & 1 deletion yarn-project/aztec/src/cli/cmds/start_node.ts
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ export const startNode = async (
services.push({ node: nodeServer });

if (!nodeConfig.disableProver) {
const provingJobSource = createProvingJobSourceServer(node.getProver().getProvingJobSource());
const provingJobSource = createProvingJobSourceServer(node.getProver()!.getProvingJobSource());
services.push({ provingJobSource });
}

Expand Down
1 change: 1 addition & 0 deletions yarn-project/bb-prover/src/test/index.ts
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
export * from './test_circuit_prover.js';
export * from './test_verifier.js';
12 changes: 12 additions & 0 deletions yarn-project/bb-prover/src/test/test_verifier.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { type ClientProtocolCircuitVerifier, type Tx } from '@aztec/circuit-types';
import { type VerificationKeys, getMockVerificationKeys } from '@aztec/circuits.js';

export class TestCircuitVerifier implements ClientProtocolCircuitVerifier {
verifyProof(_tx: Tx): Promise<boolean> {
return Promise.resolve(true);
}

getVerificationKeys(): Promise<VerificationKeys> {
return Promise.resolve(getMockVerificationKeys());
}
}
37 changes: 34 additions & 3 deletions yarn-project/bb-prover/src/verifier/bb_verifier.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,20 @@
import { type Proof, type VerificationKeyData } from '@aztec/circuits.js';
import { type ClientProtocolCircuitVerifier, Tx } from '@aztec/circuit-types';
import { type Proof, type VerificationKeyData, type VerificationKeys } from '@aztec/circuits.js';
import { runInDirectory } from '@aztec/foundation/fs';
import { type DebugLogger, type LogFn, createDebugLogger } from '@aztec/foundation/log';
import { type ProtocolArtifact, ProtocolCircuitArtifacts } from '@aztec/noir-protocol-circuits-types';
import {
type ClientProtocolArtifact,
type ProtocolArtifact,
ProtocolCircuitArtifacts,
} from '@aztec/noir-protocol-circuits-types';

import * as fs from 'fs/promises';

import { BB_RESULT, generateContractForCircuit, generateKeyForNoirCircuit, verifyProof } from '../bb/execute.js';
import { type BBConfig } from '../config.js';
import { extractVkData } from '../verification_key/verification_key_data.js';

export class BBCircuitVerifier {
export class BBCircuitVerifier implements ClientProtocolCircuitVerifier {
private constructor(
private config: BBConfig,
private verificationKeys = new Map<ProtocolArtifact, Promise<VerificationKeyData>>(),
Expand Down Expand Up @@ -112,4 +117,30 @@ export class BBCircuitVerifier {

return fs.readFile(result.contractPath!, 'utf-8');
}

async verifyProof(tx: Tx): Promise<boolean> {
const { proof, enqueuedPublicFunctionCalls } = tx;
const expectedCircuit: ClientProtocolArtifact =
enqueuedPublicFunctionCalls.length > 0 ? 'PrivateKernelTailToPublicArtifact' : 'PrivateKernelTailArtifact';

try {
await this.verifyProofForCircuit(expectedCircuit, proof);
return true;
} catch (err) {
this.logger.warn(`Failed to verify ${expectedCircuit} proof for tx ${Tx.getHash(tx)}: ${String(err)}`);
return false;
}
}

async getVerificationKeys(): Promise<VerificationKeys> {
const [privateKernelCircuit, privateKernelToPublicCircuit] = await Promise.all([
this.getVerificationKeyData('PrivateKernelTailArtifact'),
this.getVerificationKeyData('PrivateKernelTailToPublicArtifact'),
]);

return {
privateKernelCircuit,
privateKernelToPublicCircuit,
};
}
}
4 changes: 3 additions & 1 deletion yarn-project/circuit-types/src/interfaces/prover-client.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import { type VerificationKeys } from '@aztec/circuits.js';

import { type BlockProver } from './block-prover.js';
import { type ProvingJobSource } from './proving-job.js';

Expand Down Expand Up @@ -28,5 +30,5 @@ export interface ProverClient extends BlockProver {

getProvingJobSource(): ProvingJobSource;

updateProverConfig(config: Partial<ProverConfig>): Promise<void>;
updateProverConfig(config: Partial<ProverConfig & { vks: VerificationKeys }>): Promise<void>;
}
19 changes: 19 additions & 0 deletions yarn-project/circuit-types/src/interfaces/server_circuit_prover.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import {
type PublicInputsAndProof,
type PublicKernelNonTailRequest,
type PublicKernelTailRequest,
type Tx,
} from '@aztec/circuit-types';
import {
type BaseOrMergeRollupPublicInputs,
Expand All @@ -18,6 +19,7 @@ import {
type RootParityInputs,
type RootRollupInputs,
type RootRollupPublicInputs,
type VerificationKeys,
} from '@aztec/circuits.js';

/**
Expand Down Expand Up @@ -104,3 +106,20 @@ export interface PublicProver {
*/
getPublicKernelCircuitProof(publicInputs: PublicKernelCircuitPublicInputs): Promise<Proof>;
}

/**
* A verifier used by nodes to check tx proofs are valid.
*/
export interface ClientProtocolCircuitVerifier {
/**
* Verifies the private protocol circuit's proof.
* @param tx - The tx to verify the proof of
* @returns True if the proof is valid, false otherwise
*/
verifyProof(tx: Tx): Promise<boolean>;

/**
* Returns the verification keys used to verify tx proofs.
*/
getVerificationKeys(): Promise<VerificationKeys>;
}
Loading
Loading