@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.
- package/dest/avm/avm.d.ts +1 -1
- package/dest/avm/avm.d.ts.map +1 -1
- package/dest/avm/avm.js +2 -2
- package/dest/epoch-helpers/index.d.ts +6 -2
- package/dest/epoch-helpers/index.d.ts.map +1 -1
- package/dest/epoch-helpers/index.js +6 -0
- package/dest/fees/transaction_fee.d.ts.map +1 -1
- package/dest/fees/transaction_fee.js +3 -0
- package/dest/interfaces/aztec-node-admin.d.ts +25 -2
- package/dest/interfaces/aztec-node-admin.d.ts.map +1 -1
- package/dest/interfaces/aztec-node-admin.js +2 -1
- package/dest/interfaces/server.d.ts +1 -0
- package/dest/interfaces/server.d.ts.map +1 -1
- package/dest/interfaces/server.js +1 -0
- package/dest/interfaces/slasher.d.ts +4 -0
- package/dest/interfaces/slasher.d.ts.map +1 -1
- package/dest/interfaces/slasher.js +1 -0
- package/dest/interfaces/validator.d.ts +60 -0
- package/dest/interfaces/validator.d.ts.map +1 -0
- package/dest/interfaces/validator.js +10 -0
- package/dest/rollup/block_constant_data.d.ts +4 -4
- package/dest/rollup/block_constant_data.d.ts.map +1 -1
- package/dest/rollup/block_constant_data.js +4 -4
- package/dest/slashing/helpers.d.ts +2 -1
- package/dest/slashing/helpers.d.ts.map +1 -1
- package/dest/slashing/helpers.js +4 -1
- package/dest/slashing/tally.d.ts +4 -1
- package/dest/slashing/tally.d.ts.map +1 -1
- package/dest/slashing/tally.js +15 -12
- package/dest/tests/factories.d.ts +2 -1
- package/dest/tests/factories.d.ts.map +1 -1
- package/dest/tests/factories.js +5 -1
- package/dest/tx/tree_snapshots.d.ts +4 -4
- package/dest/tx/tree_snapshots.d.ts.map +1 -1
- package/dest/validators/schemas.d.ts +42 -4
- package/dest/validators/schemas.d.ts.map +1 -1
- package/dest/validators/schemas.js +5 -4
- package/dest/validators/types.d.ts +8 -10
- package/dest/validators/types.d.ts.map +1 -1
- package/package.json +9 -8
- package/src/avm/avm.ts +2 -5
- package/src/epoch-helpers/index.ts +16 -2
- package/src/fees/transaction_fee.ts +11 -0
- package/src/interfaces/aztec-node-admin.ts +6 -1
- package/src/interfaces/server.ts +1 -0
- package/src/interfaces/slasher.ts +2 -0
- package/src/interfaces/validator.ts +70 -0
- package/src/rollup/block_constant_data.ts +3 -3
- package/src/slashing/helpers.ts +13 -2
- package/src/slashing/tally.ts +23 -14
- package/src/tests/factories.ts +6 -0
- package/src/tx/tree_snapshots.ts +4 -4
- package/src/validators/schemas.ts +6 -4
- package/src/validators/types.ts +9 -10
package/src/interfaces/server.ts
CHANGED
|
@@ -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
|
|
16
|
-
public
|
|
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.
|
|
43
|
+
fields.newL1ToL2,
|
|
44
44
|
fields.vkTreeRoot,
|
|
45
45
|
fields.protocolContractTreeRoot,
|
|
46
46
|
fields.globalVariables,
|
package/src/slashing/helpers.ts
CHANGED
|
@@ -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
|
|
package/src/slashing/tally.ts
CHANGED
|
@@ -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:
|
|
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
|
-
|
|
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
|
-
|
|
26
|
-
const validatorStr = validator.toString();
|
|
31
|
+
const votes = committees.flatMap((committee, committeeIndex) => {
|
|
32
|
+
const committeeEpoch = epochsForCommittees[committeeIndex];
|
|
27
33
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
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
|
-
//
|
|
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
|
|
package/src/tests/factories.ts
CHANGED
|
@@ -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(),
|
package/src/tx/tree_snapshots.ts
CHANGED
|
@@ -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
|
|
16
|
-
public
|
|
17
|
-
public
|
|
18
|
-
public
|
|
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
|
|
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:
|
|
50
|
-
missedAttestations:
|
|
51
|
+
missedProposals: ValidatorMissedStatsSchema,
|
|
52
|
+
missedAttestations: ValidatorMissedStatsSchema,
|
|
51
53
|
history: ValidatorStatusHistorySchema,
|
|
52
54
|
}) satisfies ZodFor<ValidatorStats>;
|
|
53
55
|
|
package/src/validators/types.ts
CHANGED
|
@@ -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
|
-
|
|
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
|
|