@aztec/stdlib 2.0.0-nightly.20250821 → 2.0.0-nightly.20250823

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 (56) hide show
  1. package/dest/block/l2_block.d.ts +2 -0
  2. package/dest/block/l2_block.d.ts.map +1 -1
  3. package/dest/block/l2_block.js +6 -0
  4. package/dest/interfaces/aztec-node-admin.d.ts +29 -15
  5. package/dest/interfaces/aztec-node-admin.d.ts.map +1 -1
  6. package/dest/interfaces/aztec-node-admin.js +7 -2
  7. package/dest/interfaces/epoch-prover.d.ts +1 -1
  8. package/dest/interfaces/slasher.d.ts +33 -17
  9. package/dest/interfaces/slasher.d.ts.map +1 -1
  10. package/dest/interfaces/slasher.js +8 -4
  11. package/dest/interfaces/world_state.d.ts +4 -4
  12. package/dest/interfaces/world_state.js +1 -1
  13. package/dest/l1-contracts/index.d.ts +2 -0
  14. package/dest/l1-contracts/index.d.ts.map +1 -0
  15. package/dest/l1-contracts/index.js +1 -0
  16. package/dest/l1-contracts/slash_factory.d.ts +44 -0
  17. package/dest/l1-contracts/slash_factory.d.ts.map +1 -0
  18. package/dest/l1-contracts/slash_factory.js +157 -0
  19. package/dest/slashing/empire.d.ts +31 -0
  20. package/dest/slashing/empire.d.ts.map +1 -0
  21. package/dest/slashing/empire.js +84 -0
  22. package/dest/slashing/helpers.d.ts +31 -0
  23. package/dest/slashing/helpers.d.ts.map +1 -0
  24. package/dest/slashing/helpers.js +62 -0
  25. package/dest/slashing/index.d.ts +6 -50
  26. package/dest/slashing/index.d.ts.map +1 -1
  27. package/dest/slashing/index.js +6 -54
  28. package/dest/slashing/interfaces.d.ts +11 -0
  29. package/dest/slashing/interfaces.d.ts.map +1 -0
  30. package/dest/slashing/interfaces.js +1 -0
  31. package/dest/slashing/serialization.d.ts +8 -0
  32. package/dest/slashing/serialization.d.ts.map +1 -0
  33. package/dest/slashing/serialization.js +78 -0
  34. package/dest/slashing/tally.d.ts +17 -0
  35. package/dest/slashing/tally.d.ts.map +1 -0
  36. package/dest/slashing/tally.js +36 -0
  37. package/dest/slashing/types.d.ts +161 -0
  38. package/dest/slashing/types.d.ts.map +1 -0
  39. package/dest/slashing/types.js +66 -0
  40. package/dest/stats/stats.d.ts +2 -2
  41. package/package.json +10 -8
  42. package/src/block/l2_block.ts +8 -0
  43. package/src/interfaces/aztec-node-admin.ts +11 -4
  44. package/src/interfaces/epoch-prover.ts +1 -1
  45. package/src/interfaces/slasher.ts +18 -9
  46. package/src/interfaces/world_state.ts +2 -2
  47. package/src/l1-contracts/index.ts +1 -0
  48. package/src/l1-contracts/slash_factory.ts +177 -0
  49. package/src/slashing/empire.ts +100 -0
  50. package/src/slashing/helpers.ts +87 -0
  51. package/src/slashing/index.ts +6 -74
  52. package/src/slashing/interfaces.ts +11 -0
  53. package/src/slashing/serialization.ts +103 -0
  54. package/src/slashing/tally.ts +51 -0
  55. package/src/slashing/types.ts +129 -0
  56. package/src/stats/stats.ts +2 -2
@@ -1,74 +1,6 @@
1
- import type { EthAddress } from '@aztec/foundation/eth-address';
2
-
3
- import { z } from 'zod';
4
-
5
- import { type ZodFor, schemas } from '../schemas/index.js';
6
-
7
- export enum Offense {
8
- UNKNOWN = 0,
9
- DATA_WITHHOLDING = 1,
10
- VALID_EPOCH_PRUNED = 2,
11
- INACTIVITY = 3,
12
- /** A proposer sent an invalid block proposal over the p2p network to the committee */
13
- BROADCASTED_INVALID_BLOCK_PROPOSAL = 4,
14
- /** A proposer pushed to L1 a block with insufficient committee attestations */
15
- PROPOSED_INSUFFICIENT_ATTESTATIONS = 5,
16
- /** A proposer pushed to L1 a block with incorrect committee attestations (ie signature from a non-committee member) */
17
- PROPOSED_INCORRECT_ATTESTATIONS = 6,
18
- /** A committee member attested to a block that was built as a descendent of an invalid block (as in a block with invalid attestations) */
19
- ATTESTED_DESCENDANT_OF_INVALID = 7,
20
- }
21
-
22
- export const OffenseSchema = z.nativeEnum(Offense);
23
-
24
- export type MonitoredSlashPayload = {
25
- payloadAddress: EthAddress;
26
- validators: readonly EthAddress[];
27
- amounts: readonly bigint[];
28
- offenses: readonly Offense[];
29
- observedAtSeconds: number;
30
- totalAmount: bigint;
31
- };
32
-
33
- export const MonitoredSlashPayloadSchema = z.object({
34
- payloadAddress: schemas.EthAddress,
35
- validators: z.array(schemas.EthAddress),
36
- amounts: z.array(schemas.BigInt),
37
- offenses: z.array(OffenseSchema),
38
- observedAtSeconds: z.number(),
39
- totalAmount: schemas.BigInt,
40
- }) satisfies ZodFor<MonitoredSlashPayload>;
41
-
42
- export const OffenseToBigInt: Record<Offense, bigint> = {
43
- [Offense.UNKNOWN]: 0n,
44
- [Offense.DATA_WITHHOLDING]: 1n,
45
- [Offense.VALID_EPOCH_PRUNED]: 2n,
46
- [Offense.INACTIVITY]: 3n,
47
- [Offense.BROADCASTED_INVALID_BLOCK_PROPOSAL]: 4n,
48
- [Offense.PROPOSED_INSUFFICIENT_ATTESTATIONS]: 5n,
49
- [Offense.PROPOSED_INCORRECT_ATTESTATIONS]: 6n,
50
- [Offense.ATTESTED_DESCENDANT_OF_INVALID]: 7n,
51
- };
52
-
53
- export function bigIntToOffense(offense: bigint): Offense {
54
- switch (offense) {
55
- case 0n:
56
- return Offense.UNKNOWN;
57
- case 1n:
58
- return Offense.DATA_WITHHOLDING;
59
- case 2n:
60
- return Offense.VALID_EPOCH_PRUNED;
61
- case 3n:
62
- return Offense.INACTIVITY;
63
- case 4n:
64
- return Offense.BROADCASTED_INVALID_BLOCK_PROPOSAL;
65
- case 5n:
66
- return Offense.PROPOSED_INSUFFICIENT_ATTESTATIONS;
67
- case 6n:
68
- return Offense.PROPOSED_INCORRECT_ATTESTATIONS;
69
- case 7n:
70
- return Offense.ATTESTED_DESCENDANT_OF_INVALID;
71
- default:
72
- throw new Error(`Unknown offense: ${offense}`);
73
- }
74
- }
1
+ export * from './types.js';
2
+ export * from './interfaces.js';
3
+ export * from './helpers.js';
4
+ export * from './empire.js';
5
+ export * from './tally.js';
6
+ export * from './serialization.js';
@@ -0,0 +1,11 @@
1
+ import type { ProposerSlashAction } from './types.js';
2
+
3
+ export interface ProposerSlashActionProvider {
4
+ /**
5
+ * Returns the actions to take for the proposer in the current slot.
6
+ * This can include creating a slash payload or other actions.
7
+ * @param slotNumber - The current slot number
8
+ * @returns The actions to take
9
+ */
10
+ getProposerActions(slotNumber: bigint): Promise<ProposerSlashAction[]>;
11
+ }
@@ -0,0 +1,103 @@
1
+ import { times } from '@aztec/foundation/collection';
2
+ import { EthAddress } from '@aztec/foundation/eth-address';
3
+ import { BufferReader, bigintToUInt64BE, bigintToUInt128BE, serializeToBuffer } from '@aztec/foundation/serialize';
4
+
5
+ import type {
6
+ Offense,
7
+ OffenseType,
8
+ SlashPayload,
9
+ SlashPayloadRound,
10
+ ValidatorSlash,
11
+ ValidatorSlashOffense,
12
+ } from './types.js';
13
+
14
+ export function serializeOffense(offense: Offense): Buffer {
15
+ return serializeToBuffer(
16
+ offense.validator,
17
+ bigintToUInt128BE(offense.amount),
18
+ offense.offenseType,
19
+ bigintToUInt64BE(offense.epochOrSlot),
20
+ );
21
+ }
22
+
23
+ export function deserializeOffense(buffer: Buffer): Offense {
24
+ const reader = BufferReader.asReader(buffer);
25
+ const validator = reader.readObject(EthAddress);
26
+ const amount = reader.readUInt128();
27
+ const offense = reader.readNumber() as OffenseType;
28
+ const epochOrSlot = reader.readUInt64();
29
+
30
+ return { validator, amount, offenseType: offense, epochOrSlot };
31
+ }
32
+
33
+ function serializeValidatorSlashOffense(offense: ValidatorSlashOffense): Buffer {
34
+ return serializeToBuffer(bigintToUInt64BE(offense.epochOrSlot), offense.offenseType);
35
+ }
36
+
37
+ function deserializeValidatorSlashOffense(buffer: Buffer | BufferReader): ValidatorSlashOffense {
38
+ const reader = BufferReader.asReader(buffer);
39
+ return {
40
+ epochOrSlot: reader.readUInt64(),
41
+ offenseType: reader.readNumber() as OffenseType,
42
+ };
43
+ }
44
+
45
+ function serializeValidatorSlash(slash: ValidatorSlash): Buffer {
46
+ return serializeToBuffer(
47
+ slash.validator,
48
+ bigintToUInt128BE(slash.amount),
49
+ slash.offenses.length,
50
+ slash.offenses.map(serializeValidatorSlashOffense),
51
+ );
52
+ }
53
+
54
+ function deserializeValidatorSlash(buffer: Buffer | BufferReader): ValidatorSlash {
55
+ const reader = BufferReader.asReader(buffer);
56
+ const validator = reader.readObject(EthAddress);
57
+ const amount = reader.readUInt128();
58
+ const offensesCount = reader.readNumber();
59
+ const offenses = times(offensesCount, () => deserializeValidatorSlashOffense(reader));
60
+
61
+ return { validator, amount, offenses };
62
+ }
63
+
64
+ export function serializeSlashPayload(payload: SlashPayload): Buffer {
65
+ return serializeToBuffer(
66
+ payload.address,
67
+ payload.slashes.length,
68
+ payload.slashes.map(serializeValidatorSlash),
69
+ bigintToUInt64BE(payload.timestamp),
70
+ );
71
+ }
72
+
73
+ export function deserializeSlashPayload(buffer: Buffer): SlashPayload {
74
+ const reader = BufferReader.asReader(buffer);
75
+ const address = reader.readObject(EthAddress);
76
+ const slashesCount = reader.readNumber();
77
+ const slashes = times(slashesCount, () => deserializeValidatorSlash(reader));
78
+ const timestamp = reader.readUInt64();
79
+ return { address, slashes, timestamp };
80
+ }
81
+
82
+ export function serializeSlashPayloadRound(payload: SlashPayloadRound): Buffer {
83
+ return serializeToBuffer(
84
+ payload.address,
85
+ payload.slashes.length,
86
+ payload.slashes.map(serializeValidatorSlash),
87
+ bigintToUInt64BE(payload.timestamp),
88
+ Number(payload.votes),
89
+ bigintToUInt64BE(payload.round),
90
+ );
91
+ }
92
+
93
+ export function deserializeSlashPayloadRound(buffer: Buffer): SlashPayloadRound {
94
+ const reader = BufferReader.asReader(buffer);
95
+ const address = reader.readObject(EthAddress);
96
+ const slashesCount = reader.readNumber();
97
+ const slashes = times(slashesCount, () => deserializeValidatorSlash(reader));
98
+ const timestamp = reader.readUInt64();
99
+ const votes = BigInt(reader.readNumber());
100
+ const round = reader.readUInt64();
101
+
102
+ return { address, slashes, timestamp, votes, round };
103
+ }
@@ -0,0 +1,51 @@
1
+ import { minBigint, sumBigint } from '@aztec/foundation/bigint';
2
+ import type { EthAddress } from '@aztec/foundation/eth-address';
3
+
4
+ import type { Offense, ValidatorSlashVote } from './types.js';
5
+
6
+ /** How many slashing units a validator can be slashed in consensus-based slashing */
7
+ const MAX_SLASH_UNITS_PER_VALIDATOR = 3n;
8
+
9
+ /**
10
+ * Creates a consensus-slash vote for a given set of committees based on a set of Offenses
11
+ * @returns Array of ValidatorSlashVote, where each vote is how many slash units the validator in that position should be slashed
12
+ */
13
+ export function getSlashConsensusVotesFromOffenses(
14
+ offenses: Offense[],
15
+ committees: EthAddress[][],
16
+ settings: { slashingUnit: bigint },
17
+ ): ValidatorSlashVote[] {
18
+ const { slashingUnit } = settings;
19
+ const slashed: Set<string> = new Set();
20
+ const votes = committees.flatMap(committee =>
21
+ committee.map(validator => {
22
+ if (slashed.has(validator.toString())) {
23
+ return 0; // Already voted for slashing this validator
24
+ }
25
+ const validatorOffenses = offenses.filter(o => o.validator.equals(validator));
26
+ const slashAmount = sumBigint(validatorOffenses.map(o => o.amount));
27
+ const slashUnits = minBigint(slashAmount / slashingUnit, MAX_SLASH_UNITS_PER_VALIDATOR);
28
+ slashed.add(validator.toString());
29
+ return Number(slashUnits);
30
+ }),
31
+ );
32
+ return votes;
33
+ }
34
+
35
+ /**
36
+ * Encodes a set of slash votes into a Buffer for use in a consensus slashing vote transaction.
37
+ * Each vote is represented as a 2-bit value, which represents how many slashing units the validator should be slashed.
38
+ * @param votes - The array of slash votes to encode
39
+ * @returns A Buffer containing the encoded slash votes
40
+ */
41
+ export function encodeSlashConsensusVotes(votes: ValidatorSlashVote[]): Buffer {
42
+ if (votes.length % 4 !== 0) {
43
+ throw new Error('Votes array must have a length that is a multiple of 4');
44
+ }
45
+ const buffer = Buffer.alloc(votes.length / 4);
46
+ for (let i = 0; i < votes.length; i += 4) {
47
+ const voteByte = (votes[i] << 6) | (votes[i + 1] << 4) | (votes[i + 2] << 2) | votes[i + 3]; // Combine four votes into one byte
48
+ buffer.writeUInt8(voteByte, i / 4);
49
+ }
50
+ return buffer;
51
+ }
@@ -0,0 +1,129 @@
1
+ import { EthAddress } from '@aztec/foundation/eth-address';
2
+
3
+ import { z } from 'zod';
4
+
5
+ import { type ZodFor, schemas } from '../schemas/index.js';
6
+
7
+ export enum OffenseType {
8
+ UNKNOWN = 0,
9
+ /** The data for proving an epoch was not publicly available, we slash its committee */
10
+ DATA_WITHHOLDING = 1,
11
+ /** An epoch was not successfully proven in time, we slash its committee */
12
+ VALID_EPOCH_PRUNED = 2,
13
+ /** A proposer failed to attest or propose during an epoch according to the Sentinel */
14
+ INACTIVITY = 3,
15
+ /** A proposer sent an invalid block proposal over the p2p network to the committee */
16
+ BROADCASTED_INVALID_BLOCK_PROPOSAL = 4,
17
+ /** A proposer pushed to L1 a block with insufficient committee attestations */
18
+ PROPOSED_INSUFFICIENT_ATTESTATIONS = 5,
19
+ /** A proposer pushed to L1 a block with incorrect committee attestations (ie signature from a non-committee member) */
20
+ PROPOSED_INCORRECT_ATTESTATIONS = 6,
21
+ /** A committee member attested to a block that was built as a descendent of an invalid block (as in a block with invalid attestations) */
22
+ ATTESTED_DESCENDANT_OF_INVALID = 7,
23
+ }
24
+
25
+ export const OffenseTypeSchema = z.nativeEnum(OffenseType);
26
+
27
+ export const OffenseToBigInt: Record<OffenseType, bigint> = {
28
+ [OffenseType.UNKNOWN]: 0n,
29
+ [OffenseType.DATA_WITHHOLDING]: 1n,
30
+ [OffenseType.VALID_EPOCH_PRUNED]: 2n,
31
+ [OffenseType.INACTIVITY]: 3n,
32
+ [OffenseType.BROADCASTED_INVALID_BLOCK_PROPOSAL]: 4n,
33
+ [OffenseType.PROPOSED_INSUFFICIENT_ATTESTATIONS]: 5n,
34
+ [OffenseType.PROPOSED_INCORRECT_ATTESTATIONS]: 6n,
35
+ [OffenseType.ATTESTED_DESCENDANT_OF_INVALID]: 7n,
36
+ };
37
+
38
+ export function bigIntToOffense(offense: bigint): OffenseType {
39
+ switch (offense) {
40
+ case 0n:
41
+ return OffenseType.UNKNOWN;
42
+ case 1n:
43
+ return OffenseType.DATA_WITHHOLDING;
44
+ case 2n:
45
+ return OffenseType.VALID_EPOCH_PRUNED;
46
+ case 3n:
47
+ return OffenseType.INACTIVITY;
48
+ case 4n:
49
+ return OffenseType.BROADCASTED_INVALID_BLOCK_PROPOSAL;
50
+ case 5n:
51
+ return OffenseType.PROPOSED_INSUFFICIENT_ATTESTATIONS;
52
+ case 6n:
53
+ return OffenseType.PROPOSED_INCORRECT_ATTESTATIONS;
54
+ case 7n:
55
+ return OffenseType.ATTESTED_DESCENDANT_OF_INVALID;
56
+ default:
57
+ throw new Error(`Unknown offense: ${offense}`);
58
+ }
59
+ }
60
+
61
+ export type Offense = {
62
+ validator: EthAddress;
63
+ amount: bigint;
64
+ offenseType: OffenseType;
65
+ epochOrSlot: bigint;
66
+ };
67
+
68
+ export type OffenseIdentifier = Pick<Offense, 'validator' | 'offenseType' | 'epochOrSlot'>;
69
+
70
+ export const OffenseSchema = z.object({
71
+ validator: schemas.EthAddress,
72
+ amount: schemas.BigInt,
73
+ offenseType: OffenseTypeSchema,
74
+ epochOrSlot: schemas.BigInt,
75
+ }) satisfies ZodFor<Offense>;
76
+
77
+ /** Offense by a validator in the context of a slash payload */
78
+ export type ValidatorSlashOffense = {
79
+ epochOrSlot: bigint;
80
+ offenseType: OffenseType;
81
+ };
82
+
83
+ /** Slashed amount and total offenses by a validator in the context of a slash payload */
84
+ export type ValidatorSlash = {
85
+ validator: EthAddress;
86
+ amount: bigint;
87
+ offenses: ValidatorSlashOffense[];
88
+ };
89
+
90
+ /** Slash payload as published by the empire slash proposer */
91
+ export type SlashPayload = {
92
+ address: EthAddress;
93
+ slashes: ValidatorSlash[];
94
+ timestamp: bigint;
95
+ };
96
+
97
+ /** Slash payload with round information from empire slash proposer */
98
+ export type SlashPayloadRound = SlashPayload & { votes: bigint; round: bigint };
99
+
100
+ export const SlashPayloadRoundSchema = z.object({
101
+ address: schemas.EthAddress,
102
+ timestamp: schemas.BigInt,
103
+ votes: schemas.BigInt,
104
+ round: schemas.BigInt,
105
+ slashes: z.array(
106
+ z.object({
107
+ validator: schemas.EthAddress,
108
+ amount: schemas.BigInt,
109
+ offenses: z.array(z.object({ offenseType: OffenseTypeSchema, epochOrSlot: schemas.BigInt })),
110
+ }),
111
+ ),
112
+ }) satisfies ZodFor<SlashPayloadRound>;
113
+
114
+ /** Votes for a validator slash in the consensus slash proposer */
115
+ export type ValidatorSlashVote = number;
116
+
117
+ export type ProposerSlashAction =
118
+ /** Create a new slash payload on an empire-based slash proposer */
119
+ | { type: 'create-empire-payload'; data: ValidatorSlash[] }
120
+ /** Vote for a slashing payload on an empire-based slash proposer */
121
+ | { type: 'vote-empire-payload'; payload: EthAddress }
122
+ /** Execute a slashing payload on an empire-based slash proposer */
123
+ | { type: 'execute-empire-payload'; round: bigint }
124
+ /** Vote for offenses on a consensus slashing proposer */
125
+ | { type: 'vote-offenses'; votes: ValidatorSlashVote[]; committees: EthAddress[][]; round: bigint }
126
+ /** Execute a slashing round on a consensus slashing proposer */
127
+ | { type: 'execute-slash'; committees: EthAddress[][]; round: bigint };
128
+
129
+ export type ProposerSlashActionType = ProposerSlashAction['type'];
@@ -202,9 +202,9 @@ export type L2BlockHandledStats = {
202
202
  /** Total duration in ms. */
203
203
  duration: number;
204
204
  /** Pending block number. */
205
- unfinalisedBlockNumber: bigint;
205
+ unfinalizedBlockNumber: bigint;
206
206
  /** Proven block number. */
207
- finalisedBlockNumber: bigint;
207
+ finalizedBlockNumber: bigint;
208
208
  /** Oldest historic block number. */
209
209
  oldestHistoricBlock: bigint;
210
210
  } & L2BlockStats;