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: AVM uses native checkpointing trees instead of TS ephemeral forest #11955

Merged
merged 34 commits into from
Feb 21, 2025
Merged
Show file tree
Hide file tree
Changes from 30 commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
a0accc9
chore: avm TS test cleanup and refactors
dbanks12 Feb 15, 2025
33cf18b
new test in orchestrator
dbanks12 Feb 15, 2025
20397fc
Merge branch 'master' into db/testing-updates
dbanks12 Feb 15, 2025
0e00834
Merge branch 'master' into db/testing-updates
dbanks12 Feb 15, 2025
caeec38
fixes
dbanks12 Feb 18, 2025
504a63a
merge
dbanks12 Feb 18, 2025
e6b5622
fixes
dbanks12 Feb 18, 2025
b69a11d
fix
dbanks12 Feb 18, 2025
ba5fa4a
more fixes
dbanks12 Feb 18, 2025
8e5ac40
use new sha in benchmarking contract
dbanks12 Feb 18, 2025
5f05e9f
Fixes
dbanks12 Feb 18, 2025
4a6972c
fixes & cleanup
dbanks12 Feb 18, 2025
4c0c17f
Merge branch 'master' into db/testing-updates
dbanks12 Feb 18, 2025
41c536b
fix noir contract
dbanks12 Feb 18, 2025
bc5806d
fix contract updates test
dbanks12 Feb 18, 2025
6376465
revert incorrect type change
dbanks12 Feb 18, 2025
5b9afbc
fmt
dbanks12 Feb 18, 2025
e1adf6a
Merge branch 'master' into db/testing-updates
dbanks12 Feb 18, 2025
0fe5ec2
merge
dbanks12 Feb 18, 2025
3347f14
[WIP - attempt 2] feat: AVM uses native checkpointing trees instead o…
dbanks12 Feb 19, 2025
c1e8993
fix feePayer
dbanks12 Feb 19, 2025
d9defc5
fix feePayer
dbanks12 Feb 19, 2025
adf62ae
fix
dbanks12 Feb 19, 2025
a1758ed
merge
dbanks12 Feb 19, 2025
5b4c862
merge
dbanks12 Feb 19, 2025
c9d8cd0
merge
dbanks12 Feb 19, 2025
847f643
fix txe
dbanks12 Feb 20, 2025
bc0b6c3
merge
dbanks12 Feb 20, 2025
b87ba9b
fmt
dbanks12 Feb 20, 2025
0eb8470
address pr feedback - missing commit-checkpoints, add try/finally
dbanks12 Feb 21, 2025
66abe17
Checkpoint lifecycle
PhilWindle Feb 21, 2025
39e376e
Typo
PhilWindle Feb 21, 2025
b6fb9f0
Minor review change
PhilWindle Feb 21, 2025
3d2dfb9
move checkpoint file
dbanks12 Feb 21, 2025
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
4 changes: 4 additions & 0 deletions yarn-project/circuit-types/src/tx/validator/tx_validator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@ import { type Tx } from '../tx.js';

export type AnyTx = Tx | ProcessedTx;

export function hasPublicCalls(tx: AnyTx): boolean {
return tx.data.numberOfPublicCallRequests() > 0;
}

export type TxValidationResult =
| { result: 'valid' }
| { result: 'invalid'; reason: string[] }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,9 @@ describe('DoubleSpendTxValidator', () => {
let txValidator: DoubleSpendTxValidator<AnyTx>;
let nullifierSource: MockProxy<NullifierSource>;

const expectValid = async (tx: AnyTx) => {
await expect(txValidator.validateTx(tx)).resolves.toEqual({ result: 'valid' });
};
const expectInvalid = async (tx: AnyTx, reason: string) => {
await expect(txValidator.validateTx(tx)).resolves.toEqual({ result: 'invalid', reason: [reason] });
};
Expand All @@ -31,19 +34,24 @@ describe('DoubleSpendTxValidator', () => {
await expectInvalid(badTx, 'Duplicate nullifier in tx');
});

it('rejects duplicates across phases', async () => {
it('rejects duplicates against history', async () => {
const badTx = await mockTx(1, {
numberOfNonRevertiblePublicCallRequests: 0,
numberOfRevertiblePublicCallRequests: 0,
});
nullifierSource.nullifiersExist.mockResolvedValue([true]);
await expectInvalid(badTx, 'Existing nullifier');
});

// If the tx has public calls, all merkle insertions will be performed by the AVM,
// and the AVM will catch any duplicates. So we don't need to check during tx validation.
it('accepts duplicates if the tx has public calls', async () => {
const badTx = await mockTx(1, {
numberOfNonRevertiblePublicCallRequests: 1,
numberOfRevertiblePublicCallRequests: 1,
});
badTx.data.forPublic!.revertibleAccumulatedData.nullifiers[0] =
badTx.data.forPublic!.nonRevertibleAccumulatedData.nullifiers[0];
await expectInvalid(badTx, 'Duplicate nullifier in tx');
});

it('rejects duplicates against history', async () => {
const badTx = await mockTx();
nullifierSource.nullifiersExist.mockResolvedValue([true]);
await expectInvalid(badTx, 'Existing nullifier');
await expectValid(badTx);
});
});
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { type AnyTx, Tx, type TxValidationResult, type TxValidator } from '@aztec/circuit-types';
import { type AnyTx, Tx, type TxValidationResult, type TxValidator, hasPublicCalls } from '@aztec/circuit-types';
import { createLogger } from '@aztec/foundation/log';

export interface NullifierSource {
Expand All @@ -14,20 +14,25 @@ export class DoubleSpendTxValidator<T extends AnyTx> implements TxValidator<T> {
}

async validateTx(tx: T): Promise<TxValidationResult> {
const nullifiers = tx instanceof Tx ? tx.data.getNonEmptyNullifiers() : tx.txEffect.nullifiers;
// Don't need to check for duplicate nullifiers if the tx has public calls
// because the AVM will perform merkle insertions as it goes and will fail on
// duplicate nullifier. In fact we CANNOT check here because the nullifiers
// have already been inserted, and so they will exist in nullifierSource.
if (!hasPublicCalls(tx)) {
const nullifiers = tx instanceof Tx ? tx.data.getNonEmptyNullifiers() : tx.txEffect.nullifiers;

// Ditch this tx if it has repeated nullifiers
const uniqueNullifiers = new Set(nullifiers);
if (uniqueNullifiers.size !== nullifiers.length) {
this.#log.warn(`Rejecting tx ${await Tx.getHash(tx)} for emitting duplicate nullifiers`);
return { result: 'invalid', reason: ['Duplicate nullifier in tx'] };
}
// Ditch this tx if it has repeated nullifiers
const uniqueNullifiers = new Set(nullifiers);
if (uniqueNullifiers.size !== nullifiers.length) {
this.#log.warn(`Rejecting tx ${await Tx.getHash(tx)} for emitting duplicate nullifiers`);
return { result: 'invalid', reason: ['Duplicate nullifier in tx'] };
}

if ((await this.#nullifierSource.nullifiersExist(nullifiers.map(n => n.toBuffer()))).some(Boolean)) {
this.#log.warn(`Rejecting tx ${await Tx.getHash(tx)} for repeating a nullifier`);
return { result: 'invalid', reason: ['Existing nullifier'] };
if ((await this.#nullifierSource.nullifiersExist(nullifiers.map(n => n.toBuffer()))).some(Boolean)) {
this.#log.warn(`Rejecting tx ${await Tx.getHash(tx)} for repeating a nullifier`);
return { result: 'invalid', reason: ['Existing nullifier'] };
}
}

return { result: 'valid' };
Comment on lines -17 to -30
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

If public calls are present, can't check that nullifiers in txEffects don't already exist in the trees because the AVM now inserts them into the trees. Still need these checks for private-only txs.

}
}
13 changes: 9 additions & 4 deletions yarn-project/simulator/src/avm/avm_context.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ describe('Avm Context', () => {
const newAddress = await AztecAddress.random();
const newCalldata = [new Fr(1), new Fr(2)];
const allocatedGas = { l2Gas: 2, daGas: 3 }; // How much of the current call gas we pass to the nested call
const newContext = context.createNestedContractCallContext(newAddress, newCalldata, allocatedGas, 'CALL');
const newContext = await context.createNestedContractCallContext(newAddress, newCalldata, allocatedGas, 'CALL');

expect(newContext.environment).toEqual(
allSameExcept(context.environment, {
Expand All @@ -29,7 +29,7 @@ describe('Avm Context', () => {
);

// We stringify to remove circular references (parentJournal)
expect(JSON.stringify(newContext.persistableState)).toEqual(JSON.stringify(context.persistableState.fork()));
expect(JSON.stringify(newContext.persistableState)).toEqual(JSON.stringify(await context.persistableState.fork()));
});

it('New static call should fork context correctly', async () => {
Expand All @@ -39,7 +39,12 @@ describe('Avm Context', () => {
const newAddress = await AztecAddress.random();
const newCalldata = [new Fr(1), new Fr(2)];
const allocatedGas = { l2Gas: 2, daGas: 3 };
const newContext = context.createNestedContractCallContext(newAddress, newCalldata, allocatedGas, 'STATICCALL');
const newContext = await context.createNestedContractCallContext(
newAddress,
newCalldata,
allocatedGas,
'STATICCALL',
);

expect(newContext.environment).toEqual(
allSameExcept(context.environment, {
Expand All @@ -58,6 +63,6 @@ describe('Avm Context', () => {
);

// We stringify to remove circular references (parentJournal)
expect(JSON.stringify(newContext.persistableState)).toEqual(JSON.stringify(context.persistableState.fork()));
expect(JSON.stringify(newContext.persistableState)).toEqual(JSON.stringify(await context.persistableState.fork()));
});
});
6 changes: 3 additions & 3 deletions yarn-project/simulator/src/avm/avm_context.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,18 +43,18 @@ export class AvmContext {
* @param callType - Type of call (CALL or STATICCALL)
* @returns new AvmContext instance
*/
public createNestedContractCallContext(
public async createNestedContractCallContext(
address: AztecAddress,
calldata: Fr[],
allocatedGas: Gas,
callType: 'CALL' | 'STATICCALL',
): AvmContext {
): Promise<AvmContext> {
const deriveFn =
callType === 'CALL'
? this.environment.deriveEnvironmentForNestedCall
: this.environment.deriveEnvironmentForNestedStaticCall;
const newExecutionEnvironment = deriveFn.call(this.environment, address, calldata);
const forkedWorldState = this.persistableState.fork();
const forkedWorldState = await this.persistableState.fork();
const machineState = AvmMachineState.fromState(gasToGasLeft(allocatedGas));
return new AvmContext(forkedWorldState, newExecutionEnvironment, machineState);
}
Expand Down
Loading
Loading