Skip to content

Commit

Permalink
feat: node verifies proofs
Browse files Browse the repository at this point in the history
  • Loading branch information
alexghr committed May 30, 2024
1 parent b46b9f3 commit 9634145
Show file tree
Hide file tree
Showing 24 changed files with 332 additions and 169 deletions.
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 = Fr.random();
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);
}
}
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>;
}
3 changes: 2 additions & 1 deletion yarn-project/circuit-types/src/tx/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,5 @@ export * from './tx_hash.js';
export * from './tx_receipt.js';
export * from './processed_tx.js';
export * from './public_simulation_output.js';
export * from './tx_validator.js';
export * from './validator/tx_validator.js';
export * from './validator/aggregate_tx_validator.js';
Loading

0 comments on commit 9634145

Please sign in to comment.