@aztec/stdlib 2.0.0-nightly.20250902 → 2.0.0-rc.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (54) hide show
  1. package/dest/avm/avm.d.ts +1 -1
  2. package/dest/avm/avm.d.ts.map +1 -1
  3. package/dest/avm/avm.js +2 -2
  4. package/dest/epoch-helpers/index.d.ts +6 -2
  5. package/dest/epoch-helpers/index.d.ts.map +1 -1
  6. package/dest/epoch-helpers/index.js +6 -0
  7. package/dest/fees/transaction_fee.d.ts.map +1 -1
  8. package/dest/fees/transaction_fee.js +3 -0
  9. package/dest/interfaces/aztec-node-admin.d.ts +25 -2
  10. package/dest/interfaces/aztec-node-admin.d.ts.map +1 -1
  11. package/dest/interfaces/aztec-node-admin.js +2 -1
  12. package/dest/interfaces/server.d.ts +1 -0
  13. package/dest/interfaces/server.d.ts.map +1 -1
  14. package/dest/interfaces/server.js +1 -0
  15. package/dest/interfaces/slasher.d.ts +4 -0
  16. package/dest/interfaces/slasher.d.ts.map +1 -1
  17. package/dest/interfaces/slasher.js +1 -0
  18. package/dest/interfaces/validator.d.ts +60 -0
  19. package/dest/interfaces/validator.d.ts.map +1 -0
  20. package/dest/interfaces/validator.js +10 -0
  21. package/dest/rollup/block_constant_data.d.ts +4 -4
  22. package/dest/rollup/block_constant_data.d.ts.map +1 -1
  23. package/dest/rollup/block_constant_data.js +4 -4
  24. package/dest/slashing/helpers.d.ts +2 -1
  25. package/dest/slashing/helpers.d.ts.map +1 -1
  26. package/dest/slashing/helpers.js +4 -1
  27. package/dest/slashing/tally.d.ts +4 -1
  28. package/dest/slashing/tally.d.ts.map +1 -1
  29. package/dest/slashing/tally.js +15 -12
  30. package/dest/tests/factories.d.ts +2 -1
  31. package/dest/tests/factories.d.ts.map +1 -1
  32. package/dest/tests/factories.js +5 -1
  33. package/dest/tx/tree_snapshots.d.ts +4 -4
  34. package/dest/tx/tree_snapshots.d.ts.map +1 -1
  35. package/dest/validators/schemas.d.ts +42 -4
  36. package/dest/validators/schemas.d.ts.map +1 -1
  37. package/dest/validators/schemas.js +5 -4
  38. package/dest/validators/types.d.ts +8 -10
  39. package/dest/validators/types.d.ts.map +1 -1
  40. package/package.json +9 -8
  41. package/src/avm/avm.ts +2 -5
  42. package/src/epoch-helpers/index.ts +16 -2
  43. package/src/fees/transaction_fee.ts +11 -0
  44. package/src/interfaces/aztec-node-admin.ts +6 -1
  45. package/src/interfaces/server.ts +1 -0
  46. package/src/interfaces/slasher.ts +2 -0
  47. package/src/interfaces/validator.ts +70 -0
  48. package/src/rollup/block_constant_data.ts +3 -3
  49. package/src/slashing/helpers.ts +13 -2
  50. package/src/slashing/tally.ts +23 -14
  51. package/src/tests/factories.ts +6 -0
  52. package/src/tx/tree_snapshots.ts +4 -4
  53. package/src/validators/schemas.ts +6 -4
  54. package/src/validators/types.ts +9 -10
@@ -21,4 +21,5 @@ export * from './server_circuit_prover.js';
21
21
  export * from './service.js';
22
22
  export * from './slasher.js';
23
23
  export * from './tx_provider.js';
24
+ export * from './validator.js';
24
25
  export * from './world_state.js';
@@ -13,6 +13,7 @@ export interface SlasherConfig {
13
13
  slashValidatorsAlways: EthAddress[]; // Array of validator addresses
14
14
  slashValidatorsNever: EthAddress[]; // Array of validator addresses
15
15
  slashInactivityTargetPercentage: number; // 0-1, 0.9 means 90%. Must be greater than 0
16
+ slashInactivityConsecutiveEpochThreshold: number; // Number of consecutive epochs a validator must be inactive before slashing
16
17
  slashPrunePenalty: bigint;
17
18
  slashDataWithholdingPenalty: bigint;
18
19
  slashInactivityPenalty: bigint;
@@ -34,6 +35,7 @@ export const SlasherConfigSchema = z.object({
34
35
  slashPrunePenalty: schemas.BigInt,
35
36
  slashDataWithholdingPenalty: schemas.BigInt,
36
37
  slashInactivityTargetPercentage: z.number(),
38
+ slashInactivityConsecutiveEpochThreshold: z.number(),
37
39
  slashInactivityPenalty: schemas.BigInt,
38
40
  slashProposeInvalidAttestationsPenalty: schemas.BigInt,
39
41
  slashAttestDescendantOfInvalidPenalty: schemas.BigInt,
@@ -0,0 +1,70 @@
1
+ import type { SecretValue } from '@aztec/foundation/config';
2
+ import type { EthAddress } from '@aztec/foundation/eth-address';
3
+ import { Fr } from '@aztec/foundation/fields';
4
+ import { type ZodFor, schemas } from '@aztec/foundation/schemas';
5
+ import type { SequencerConfig, SlasherConfig } from '@aztec/stdlib/interfaces/server';
6
+ import type { BlockAttestation, BlockProposal, BlockProposalOptions } from '@aztec/stdlib/p2p';
7
+ import type { ProposedBlockHeader, StateReference, Tx } from '@aztec/stdlib/tx';
8
+
9
+ import type { PeerId } from '@libp2p/interface';
10
+ import { z } from 'zod';
11
+
12
+ /**
13
+ * Validator client configuration
14
+ */
15
+ export interface ValidatorClientConfig {
16
+ /** The private keys of the validators participating in attestation duties */
17
+ validatorPrivateKeys?: SecretValue<`0x${string}`[]>;
18
+
19
+ /** The addresses of the validators to use with remote signers */
20
+ validatorAddresses?: EthAddress[];
21
+
22
+ /** Do not run the validator */
23
+ disableValidator: boolean;
24
+
25
+ /** Temporarily disable these specific validator addresses */
26
+ disabledValidators: EthAddress[];
27
+
28
+ /** Interval between polling for new attestations from peers */
29
+ attestationPollingIntervalMs: number;
30
+
31
+ /** Re-execute transactions before attesting */
32
+ validatorReexecute: boolean;
33
+
34
+ /** Will re-execute until this many milliseconds are left in the slot */
35
+ validatorReexecuteDeadlineMs: number;
36
+ }
37
+
38
+ export type ValidatorClientFullConfig = ValidatorClientConfig &
39
+ Pick<SequencerConfig, 'txPublicSetupAllowList'> &
40
+ Pick<SlasherConfig, 'slashBroadcastedInvalidBlockPenalty'>;
41
+
42
+ export const ValidatorClientConfigSchema = z.object({
43
+ validatorAddresses: z.array(schemas.EthAddress).optional(),
44
+ disableValidator: z.boolean(),
45
+ disabledValidators: z.array(schemas.EthAddress),
46
+ attestationPollingIntervalMs: z.number().min(0),
47
+ validatorReexecute: z.boolean(),
48
+ validatorReexecuteDeadlineMs: z.number().min(0),
49
+ }) satisfies ZodFor<Omit<ValidatorClientConfig, 'validatorPrivateKeys'>>;
50
+
51
+ export interface Validator {
52
+ start(): Promise<void>;
53
+ registerBlockProposalHandler(): void;
54
+ updateConfig(config: Partial<ValidatorClientFullConfig>): void;
55
+
56
+ // Block validation responsibilities
57
+ createBlockProposal(
58
+ blockNumber: number,
59
+ header: ProposedBlockHeader,
60
+ archive: Fr,
61
+ stateReference: StateReference,
62
+ txs: Tx[],
63
+ proposerAddress: EthAddress | undefined,
64
+ options: BlockProposalOptions,
65
+ ): Promise<BlockProposal | undefined>;
66
+ attestToProposal(proposal: BlockProposal, sender: PeerId): Promise<BlockAttestation[] | undefined>;
67
+
68
+ broadcastBlockProposal(proposal: BlockProposal): Promise<void>;
69
+ collectAttestations(proposal: BlockProposal, required: number, deadline: Date): Promise<BlockAttestation[]>;
70
+ }
@@ -12,8 +12,8 @@ export class BlockConstantData {
12
12
  constructor(
13
13
  /** Archive tree snapshot at the very beginning of the entire rollup. */
14
14
  public lastArchive: AppendOnlyTreeSnapshot,
15
- /** L1 to L2 message tree snapshot at the very beginning of the entire rollup. */
16
- public lastL1ToL2: AppendOnlyTreeSnapshot,
15
+ /** L1 to L2 message tree snapshot after this block lands. */
16
+ public newL1ToL2: AppendOnlyTreeSnapshot,
17
17
  /** Root of the verification key tree. */
18
18
  public vkTreeRoot: Fr,
19
19
  /** Root of the protocol contract tree. */
@@ -40,7 +40,7 @@ export class BlockConstantData {
40
40
  static getFields(fields: FieldsOf<BlockConstantData>) {
41
41
  return [
42
42
  fields.lastArchive,
43
- fields.lastL1ToL2,
43
+ fields.newL1ToL2,
44
44
  fields.vkTreeRoot,
45
45
  fields.protocolContractTreeRoot,
46
46
  fields.globalVariables,
@@ -106,12 +106,23 @@ export function getSlotForOffense(
106
106
  return getTimeUnitForOffense(offenseType) === 'epoch' ? epochOrSlot * BigInt(constants.epochDuration) : epochOrSlot;
107
107
  }
108
108
 
109
- /** Returns the epoch for a given offense. */
109
+ /** Returns the epoch for a given offense. If the offense type or epoch is not defined, returns undefined. */
110
110
  export function getEpochForOffense(
111
111
  offense: Pick<Offense, 'epochOrSlot' | 'offenseType'>,
112
112
  constants: Pick<L1RollupConstants, 'epochDuration'>,
113
- ): bigint {
113
+ ): bigint;
114
+ export function getEpochForOffense(
115
+ offense: Partial<Pick<Offense, 'epochOrSlot' | 'offenseType'>>,
116
+ constants: Pick<L1RollupConstants, 'epochDuration'>,
117
+ ): bigint | undefined;
118
+ export function getEpochForOffense(
119
+ offense: Partial<Pick<Offense, 'epochOrSlot' | 'offenseType'>>,
120
+ constants: Pick<L1RollupConstants, 'epochDuration'>,
121
+ ): bigint | undefined {
114
122
  const { epochOrSlot, offenseType } = offense;
123
+ if (epochOrSlot === undefined || offenseType === undefined) {
124
+ return undefined;
125
+ }
115
126
  return getTimeUnitForOffense(offenseType) === 'epoch' ? epochOrSlot : epochOrSlot / BigInt(constants.epochDuration);
116
127
  }
117
128
 
@@ -1,43 +1,52 @@
1
1
  import { sumBigint } from '@aztec/foundation/bigint';
2
2
  import { EthAddress } from '@aztec/foundation/eth-address';
3
+ import type { PartialBy } from '@aztec/foundation/types';
3
4
 
5
+ import { getEpochForOffense } from './helpers.js';
4
6
  import type { Offense, ValidatorSlashVote } from './types.js';
5
7
 
6
8
  /**
7
9
  * Creates a consensus-slash vote for a given set of committees based on a set of Offenses
8
10
  * @param offenses - Array of offenses to consider
9
11
  * @param committees - Array of committees (each containing array of validator addresses)
12
+ * @param epochsForCommittees - Array of epochs corresponding to each committee
10
13
  * @param settings - Settings including slashingAmounts and optional validator override lists
11
14
  * @returns Array of ValidatorSlashVote, where each vote is how many slash units the validator in that position should be slashed
12
15
  */
13
16
  export function getSlashConsensusVotesFromOffenses(
14
- offenses: Pick<Offense, 'validator' | 'amount'>[],
17
+ offenses: PartialBy<Offense, 'epochOrSlot'>[],
15
18
  committees: EthAddress[][],
19
+ epochsForCommittees: bigint[],
16
20
  settings: {
17
21
  slashingAmounts: [bigint, bigint, bigint];
22
+ epochDuration: number;
18
23
  },
19
24
  ): ValidatorSlashVote[] {
20
25
  const { slashingAmounts } = settings;
21
26
 
22
- const slashedSet: Set<string> = new Set();
27
+ if (committees.length !== epochsForCommittees.length) {
28
+ throw new Error('committees and epochsForCommittees must have the same length');
29
+ }
23
30
 
24
- const votes = committees.flatMap(committee =>
25
- committee.map(validator => {
26
- const validatorStr = validator.toString();
31
+ const votes = committees.flatMap((committee, committeeIndex) => {
32
+ const committeeEpoch = epochsForCommittees[committeeIndex];
27
33
 
28
- // If already voted for slashing this validator, skip
29
- if (slashedSet.has(validatorStr)) {
30
- return 0;
31
- }
34
+ return committee.map(validator => {
35
+ // Find offenses for this validator in this specific epoch.
36
+ // If an offense has no epoch, it is considered for all epochs due to a slashAlways setting.
37
+ const validatorOffenses = offenses.filter(
38
+ o =>
39
+ o.validator.equals(validator) &&
40
+ (o.epochOrSlot === undefined || getEpochForOffense(o, settings) === committeeEpoch),
41
+ );
32
42
 
33
- // Normal offense-based slashing logic
34
- const validatorOffenses = offenses.filter(o => o.validator.equals(validator));
43
+ // Sum up the penalties for this validator in this epoch
35
44
  const slashAmount = sumBigint(validatorOffenses.map(o => o.amount));
36
45
  const slashUnits = getSlashUnitsForAmount(slashAmount, slashingAmounts);
37
- slashedSet.add(validatorStr);
38
46
  return Number(slashUnits);
39
- }),
40
- );
47
+ });
48
+ });
49
+
41
50
  return votes;
42
51
  }
43
52
 
@@ -1607,6 +1607,7 @@ export async function makeBloatedProcessedTx({
1607
1607
  vkTreeRoot = Fr.ZERO,
1608
1608
  protocolContractTreeRoot = Fr.ZERO,
1609
1609
  globalVariables = GlobalVariables.empty(),
1610
+ newL1ToL2Snapshot = AppendOnlyTreeSnapshot.empty(),
1610
1611
  feePayer,
1611
1612
  feePaymentPublicDataWrite,
1612
1613
  privateOnly = false,
@@ -1619,6 +1620,7 @@ export async function makeBloatedProcessedTx({
1619
1620
  gasSettings?: GasSettings;
1620
1621
  vkTreeRoot?: Fr;
1621
1622
  globalVariables?: GlobalVariables;
1623
+ newL1ToL2Snapshot?: AppendOnlyTreeSnapshot;
1622
1624
  protocolContractTreeRoot?: Fr;
1623
1625
  feePayer?: AztecAddress;
1624
1626
  feePaymentPublicDataWrite?: PublicDataWrite;
@@ -1672,6 +1674,9 @@ export async function makeBloatedProcessedTx({
1672
1674
 
1673
1675
  // Create avm output.
1674
1676
  const avmOutput = AvmCircuitPublicInputs.empty();
1677
+ // Assign data from hints.
1678
+ avmOutput.startTreeSnapshots.l1ToL2MessageTree = newL1ToL2Snapshot;
1679
+ avmOutput.endTreeSnapshots.l1ToL2MessageTree = newL1ToL2Snapshot;
1675
1680
  // Assign data from private.
1676
1681
  avmOutput.globalVariables = globalVariables;
1677
1682
  avmOutput.startGasUsed = tx.data.gasUsed;
@@ -1715,6 +1720,7 @@ export async function makeBloatedProcessedTx({
1715
1720
  avmOutput.gasSettings = gasSettings;
1716
1721
 
1717
1722
  const avmCircuitInputs = await makeAvmCircuitInputs(seed + 0x3000, { publicInputs: avmOutput });
1723
+ avmCircuitInputs.hints.startingTreeRoots.l1ToL2MessageTree = newL1ToL2Snapshot;
1718
1724
 
1719
1725
  const gasUsed = {
1720
1726
  totalGas: Gas.empty(),
@@ -12,10 +12,10 @@ import { AppendOnlyTreeSnapshot } from '../trees/append_only_tree_snapshot.js';
12
12
  */
13
13
  export class TreeSnapshots {
14
14
  constructor(
15
- public readonly l1ToL2MessageTree: AppendOnlyTreeSnapshot,
16
- public readonly noteHashTree: AppendOnlyTreeSnapshot,
17
- public readonly nullifierTree: AppendOnlyTreeSnapshot,
18
- public readonly publicDataTree: AppendOnlyTreeSnapshot,
15
+ public l1ToL2MessageTree: AppendOnlyTreeSnapshot,
16
+ public noteHashTree: AppendOnlyTreeSnapshot,
17
+ public nullifierTree: AppendOnlyTreeSnapshot,
18
+ public publicDataTree: AppendOnlyTreeSnapshot,
19
19
  ) {}
20
20
 
21
21
  static get schema() {
@@ -4,6 +4,7 @@ import { z } from 'zod';
4
4
 
5
5
  import type {
6
6
  SingleValidatorStats,
7
+ ValidatorMissedStats,
7
8
  ValidatorStats,
8
9
  ValidatorStatusHistory,
9
10
  ValidatorStatusInSlot,
@@ -35,19 +36,20 @@ const ValidatorTimeStatSchema = z.object({
35
36
  date: z.string(),
36
37
  });
37
38
 
38
- const ValidatorFilteredHistorySchema = z.object({
39
+ const ValidatorMissedStatsSchema = z.object({
39
40
  currentStreak: schemas.Integer,
40
41
  rate: z.number().optional(),
41
42
  count: schemas.Integer,
42
- });
43
+ total: schemas.Integer,
44
+ }) satisfies ZodFor<ValidatorMissedStats>;
43
45
 
44
46
  export const ValidatorStatsSchema = z.object({
45
47
  address: schemas.EthAddress,
46
48
  lastProposal: ValidatorTimeStatSchema.optional(),
47
49
  lastAttestation: ValidatorTimeStatSchema.optional(),
48
50
  totalSlots: schemas.Integer,
49
- missedProposals: ValidatorFilteredHistorySchema,
50
- missedAttestations: ValidatorFilteredHistorySchema,
51
+ missedProposals: ValidatorMissedStatsSchema,
52
+ missedAttestations: ValidatorMissedStatsSchema,
51
53
  history: ValidatorStatusHistorySchema,
52
54
  }) satisfies ZodFor<ValidatorStats>;
53
55
 
@@ -11,21 +11,20 @@ export type ValidatorStatusInSlot =
11
11
 
12
12
  export type ValidatorStatusHistory = { slot: bigint; status: ValidatorStatusInSlot }[];
13
13
 
14
+ export type ValidatorMissedStats = {
15
+ currentStreak: number;
16
+ rate?: number;
17
+ count: number;
18
+ total: number;
19
+ };
20
+
14
21
  export type ValidatorStats = {
15
22
  address: EthAddress;
16
23
  lastProposal?: { timestamp: bigint; slot: bigint; date: string };
17
24
  lastAttestation?: { timestamp: bigint; slot: bigint; date: string };
18
25
  totalSlots: number;
19
- missedProposals: {
20
- currentStreak: number;
21
- rate?: number;
22
- count: number;
23
- };
24
- missedAttestations: {
25
- currentStreak: number;
26
- rate?: number;
27
- count: number;
28
- };
26
+ missedProposals: ValidatorMissedStats;
27
+ missedAttestations: ValidatorMissedStats;
29
28
  history: ValidatorStatusHistory;
30
29
  };
31
30