@aztec/p2p 3.0.0-nightly.20251113 → 3.0.0-nightly.20251115
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/errors/attestation-pool.error.d.ts +7 -0
- package/dest/errors/attestation-pool.error.d.ts.map +1 -0
- package/dest/errors/attestation-pool.error.js +12 -0
- package/dest/mem_pools/attestation_pool/attestation_pool.d.ts +21 -0
- package/dest/mem_pools/attestation_pool/attestation_pool.d.ts.map +1 -1
- package/dest/mem_pools/attestation_pool/kv_attestation_pool.d.ts +6 -0
- package/dest/mem_pools/attestation_pool/kv_attestation_pool.d.ts.map +1 -1
- package/dest/mem_pools/attestation_pool/kv_attestation_pool.js +26 -2
- package/dest/mem_pools/attestation_pool/memory_attestation_pool.d.ts +4 -0
- package/dest/mem_pools/attestation_pool/memory_attestation_pool.d.ts.map +1 -1
- package/dest/mem_pools/attestation_pool/memory_attestation_pool.js +20 -0
- package/dest/msg_validators/block_proposal_validator/block_proposal_validator.d.ts.map +1 -1
- package/dest/msg_validators/block_proposal_validator/block_proposal_validator.js +13 -1
- package/dest/services/libp2p/libp2p_service.d.ts.map +1 -1
- package/dest/services/libp2p/libp2p_service.js +50 -4
- package/dest/testbench/p2p_client_testbench_worker.js +3 -1
- package/package.json +14 -14
- package/src/errors/attestation-pool.error.ts +13 -0
- package/src/mem_pools/attestation_pool/attestation_pool.ts +23 -0
- package/src/mem_pools/attestation_pool/kv_attestation_pool.ts +45 -2
- package/src/mem_pools/attestation_pool/memory_attestation_pool.ts +30 -0
- package/src/msg_validators/block_proposal_validator/block_proposal_validator.ts +17 -1
- package/src/services/libp2p/libp2p_service.ts +46 -4
- package/src/testbench/p2p_client_testbench_worker.ts +2 -0
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export declare class AttestationPoolError extends Error {
|
|
2
|
+
constructor(message?: string);
|
|
3
|
+
}
|
|
4
|
+
export declare class ProposalSlotCapExceededError extends AttestationPoolError {
|
|
5
|
+
constructor(message?: string);
|
|
6
|
+
}
|
|
7
|
+
//# sourceMappingURL=attestation-pool.error.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"attestation-pool.error.d.ts","sourceRoot":"","sources":["../../src/errors/attestation-pool.error.ts"],"names":[],"mappings":"AAAA,qBAAa,oBAAqB,SAAQ,KAAK;gBACjC,OAAO,CAAC,EAAE,MAAM;CAI7B;AAED,qBAAa,4BAA6B,SAAQ,oBAAoB;gBACxD,OAAO,CAAC,EAAE,MAAM;CAI7B"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export class AttestationPoolError extends Error {
|
|
2
|
+
constructor(message){
|
|
3
|
+
super(message);
|
|
4
|
+
this.name = 'AttestationPoolError';
|
|
5
|
+
}
|
|
6
|
+
}
|
|
7
|
+
export class ProposalSlotCapExceededError extends AttestationPoolError {
|
|
8
|
+
constructor(message){
|
|
9
|
+
super(message);
|
|
10
|
+
this.name = 'ProposalSlotCapExceededError';
|
|
11
|
+
}
|
|
12
|
+
}
|
|
@@ -89,6 +89,27 @@ export interface AttestationPool {
|
|
|
89
89
|
* @return True if the attestation exists, false otherwise
|
|
90
90
|
*/
|
|
91
91
|
hasAttestation(attestation: BlockAttestation): Promise<boolean>;
|
|
92
|
+
/**
|
|
93
|
+
* Returns whether adding this proposal is permitted at current capacity:
|
|
94
|
+
* - True if the proposal already exists, allow overwrite to keep parity with tests.
|
|
95
|
+
* - True if the slot is below the proposal cap.
|
|
96
|
+
* - False if the slot is at/above cap and this would be a new unique proposal.
|
|
97
|
+
*
|
|
98
|
+
* @param block - The block proposal to check
|
|
99
|
+
* @returns True if the proposal can be added (or already exists), false otherwise.
|
|
100
|
+
*/
|
|
101
|
+
canAddProposal(block: BlockProposal): Promise<boolean>;
|
|
102
|
+
/**
|
|
103
|
+
* Returns whether an attestation would be accepted for (slot, proposalId):
|
|
104
|
+
* - True if the attestation already exists for this sender.
|
|
105
|
+
* - True if the attestation cap for (slot, proposalId) has not been reached.
|
|
106
|
+
* - False if the cap is reached and this attestation would be a new unique entry.
|
|
107
|
+
*
|
|
108
|
+
* @param attestation - The attestation to check
|
|
109
|
+
* @param committeeSize - Committee size for the attestation's slot, implementation may add a small buffer
|
|
110
|
+
* @returns True if the attestation can be added, false otherwise.
|
|
111
|
+
*/
|
|
112
|
+
canAddAttestation(attestation: BlockAttestation, committeeSize: number): Promise<boolean>;
|
|
92
113
|
/** Returns whether the pool is empty. */
|
|
93
114
|
isEmpty(): Promise<boolean>;
|
|
94
115
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"attestation_pool.d.ts","sourceRoot":"","sources":["../../../src/mem_pools/attestation_pool/attestation_pool.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AAEzE;;;;;GAKG;AACH,MAAM,WAAW,eAAe;IAC9B;;OAEG;IACH,gBAAgB,CAAC,aAAa,EAAE,aAAa,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAE9D;;;;;;OAMG;IACH,gBAAgB,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,GAAG,SAAS,CAAC,CAAC;IAEjE;;;;;;OAMG;IACH,gBAAgB,CAAC,YAAY,EAAE,MAAM,GAAG,aAAa,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IAEzE;;;;OAIG;IACH,eAAe,CAAC,YAAY,EAAE,gBAAgB,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAEjE;;;;OAIG;IACH,kBAAkB,CAAC,YAAY,EAAE,gBAAgB,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAEpE;;;;;;OAMG;IACH,2BAA2B,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAEzD;;;;;;OAMG;IACH,yBAAyB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAEvD;;;;;;;OAOG;IACH,oCAAoC,CAAC,IAAI,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAEtF;;;;;;;OAOG;IACH,sBAAsB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,gBAAgB,EAAE,CAAC,CAAC;IAElE;;;;;;;;OAQG;IACH,iCAAiC,CAAC,IAAI,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,gBAAgB,EAAE,CAAC,CAAC;IAEjG;;;;;OAKG;IACH,cAAc,CAAC,WAAW,EAAE,gBAAgB,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IAEhE,yCAAyC;IACzC,OAAO,IAAI,OAAO,CAAC,OAAO,CAAC,CAAC;CAC7B"}
|
|
1
|
+
{"version":3,"file":"attestation_pool.d.ts","sourceRoot":"","sources":["../../../src/mem_pools/attestation_pool/attestation_pool.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AAEzE;;;;;GAKG;AACH,MAAM,WAAW,eAAe;IAC9B;;OAEG;IACH,gBAAgB,CAAC,aAAa,EAAE,aAAa,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAE9D;;;;;;OAMG;IACH,gBAAgB,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,GAAG,SAAS,CAAC,CAAC;IAEjE;;;;;;OAMG;IACH,gBAAgB,CAAC,YAAY,EAAE,MAAM,GAAG,aAAa,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IAEzE;;;;OAIG;IACH,eAAe,CAAC,YAAY,EAAE,gBAAgB,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAEjE;;;;OAIG;IACH,kBAAkB,CAAC,YAAY,EAAE,gBAAgB,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAEpE;;;;;;OAMG;IACH,2BAA2B,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAEzD;;;;;;OAMG;IACH,yBAAyB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAEvD;;;;;;;OAOG;IACH,oCAAoC,CAAC,IAAI,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAEtF;;;;;;;OAOG;IACH,sBAAsB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,gBAAgB,EAAE,CAAC,CAAC;IAElE;;;;;;;;OAQG;IACH,iCAAiC,CAAC,IAAI,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,gBAAgB,EAAE,CAAC,CAAC;IAEjG;;;;;OAKG;IACH,cAAc,CAAC,WAAW,EAAE,gBAAgB,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IAEhE;;;;;;;;OAQG;IACH,cAAc,CAAC,KAAK,EAAE,aAAa,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IAEvD;;;;;;;;;OASG;IACH,iBAAiB,CAAC,WAAW,EAAE,gBAAgB,EAAE,aAAa,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IAE1F,yCAAyC;IACzC,OAAO,IAAI,OAAO,CAAC,OAAO,CAAC,CAAC;CAC7B"}
|
|
@@ -2,6 +2,8 @@ import type { AztecAsyncKVStore } from '@aztec/kv-store';
|
|
|
2
2
|
import { BlockAttestation, BlockProposal } from '@aztec/stdlib/p2p';
|
|
3
3
|
import { type TelemetryClient } from '@aztec/telemetry-client';
|
|
4
4
|
import type { AttestationPool } from './attestation_pool.js';
|
|
5
|
+
export declare const MAX_PROPOSALS_PER_SLOT = 5;
|
|
6
|
+
export declare const ATTESTATION_CAP_BUFFER = 10;
|
|
5
7
|
export declare class KvAttestationPool implements AttestationPool {
|
|
6
8
|
private store;
|
|
7
9
|
private log;
|
|
@@ -26,5 +28,9 @@ export declare class KvAttestationPool implements AttestationPool {
|
|
|
26
28
|
getBlockProposal(id: string): Promise<BlockProposal | undefined>;
|
|
27
29
|
hasBlockProposal(idOrProposal: string | BlockProposal): Promise<boolean>;
|
|
28
30
|
addBlockProposal(blockProposal: BlockProposal): Promise<void>;
|
|
31
|
+
hasReachedProposalCap(slot: bigint): Promise<boolean>;
|
|
32
|
+
hasReachedAttestationCap(slot: bigint, proposalId: string, committeeSize: number): Promise<boolean>;
|
|
33
|
+
canAddProposal(block: BlockProposal): Promise<boolean>;
|
|
34
|
+
canAddAttestation(attestation: BlockAttestation, committeeSize: number): Promise<boolean>;
|
|
29
35
|
}
|
|
30
36
|
//# sourceMappingURL=kv_attestation_pool.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"kv_attestation_pool.d.ts","sourceRoot":"","sources":["../../../src/mem_pools/attestation_pool/kv_attestation_pool.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,iBAAiB,EAAqC,MAAM,iBAAiB,CAAC;AAC5F,OAAO,EAAE,gBAAgB,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AACpE,OAAO,EAAE,KAAK,eAAe,EAAsB,MAAM,yBAAyB,CAAC;
|
|
1
|
+
{"version":3,"file":"kv_attestation_pool.d.ts","sourceRoot":"","sources":["../../../src/mem_pools/attestation_pool/kv_attestation_pool.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,iBAAiB,EAAqC,MAAM,iBAAiB,CAAC;AAC5F,OAAO,EAAE,gBAAgB,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AACpE,OAAO,EAAE,KAAK,eAAe,EAAsB,MAAM,yBAAyB,CAAC;AAInF,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AAE7D,eAAO,MAAM,sBAAsB,IAAI,CAAC;AACxC,eAAO,MAAM,sBAAsB,KAAK,CAAC;AAEzC,qBAAa,iBAAkB,YAAW,eAAe;IAYrD,OAAO,CAAC,KAAK;IAEb,OAAO,CAAC,GAAG;IAbb,OAAO,CAAC,OAAO,CAAwC;IAEvD,OAAO,CAAC,YAAY,CAAgC;IACpD,OAAO,CAAC,SAAS,CAGf;IACF,OAAO,CAAC,gBAAgB,CAAqC;IAC7D,OAAO,CAAC,uBAAuB,CAAqC;gBAG1D,KAAK,EAAE,iBAAiB,EAChC,SAAS,GAAE,eAAsC,EACzC,GAAG,yCAAyC;IAUtD,OAAO,CAAC,SAAS,CAIf;IAEW,OAAO,IAAI,OAAO,CAAC,OAAO,CAAC;IAOxC,OAAO,CAAC,cAAc;IAYtB,OAAO,CAAC,iBAAiB;IAIZ,eAAe,CAAC,YAAY,EAAE,gBAAgB,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC;IAqChE,sBAAsB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,gBAAgB,EAAE,CAAC;IAYjE,iCAAiC,CAAC,IAAI,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,gBAAgB,EAAE,CAAC;IAsBhG,2BAA2B,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAO9D,yBAAyB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAuBtD,oCAAoC,CAAC,IAAI,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAqBrF,kBAAkB,CAAC,YAAY,EAAE,gBAAgB,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC;IA8BnE,cAAc,CAAC,WAAW,EAAE,gBAAgB,GAAG,OAAO,CAAC,OAAO,CAAC;IAgB/D,gBAAgB,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,GAAG,SAAS,CAAC;IAahE,gBAAgB,CAAC,YAAY,EAAE,MAAM,GAAG,aAAa,GAAG,OAAO,CAAC,OAAO,CAAC;IAKxE,gBAAgB,CAAC,aAAa,EAAE,aAAa,GAAG,OAAO,CAAC,IAAI,CAAC;IAiB7D,qBAAqB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAMrD,wBAAwB,CAAC,IAAI,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,aAAa,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAKnG,cAAc,CAAC,KAAK,EAAE,aAAa,GAAG,OAAO,CAAC,OAAO,CAAC;IAOtD,iBAAiB,CAAC,WAAW,EAAE,gBAAgB,EAAE,aAAa,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;CAUvG"}
|
|
@@ -3,7 +3,10 @@ import { toArray } from '@aztec/foundation/iterable';
|
|
|
3
3
|
import { createLogger } from '@aztec/foundation/log';
|
|
4
4
|
import { BlockAttestation, BlockProposal } from '@aztec/stdlib/p2p';
|
|
5
5
|
import { getTelemetryClient } from '@aztec/telemetry-client';
|
|
6
|
+
import { ProposalSlotCapExceededError } from '../../errors/attestation-pool.error.js';
|
|
6
7
|
import { PoolInstrumentation, PoolName } from '../instrumentation.js';
|
|
8
|
+
export const MAX_PROPOSALS_PER_SLOT = 5;
|
|
9
|
+
export const ATTESTATION_CAP_BUFFER = 10;
|
|
7
10
|
export class KvAttestationPool {
|
|
8
11
|
store;
|
|
9
12
|
log;
|
|
@@ -183,8 +186,29 @@ export class KvAttestationPool {
|
|
|
183
186
|
}
|
|
184
187
|
async addBlockProposal(blockProposal) {
|
|
185
188
|
await this.store.transactionAsync(async ()=>{
|
|
186
|
-
|
|
187
|
-
|
|
189
|
+
const slotKey = blockProposal.slotNumber.toString();
|
|
190
|
+
const proposalId = blockProposal.archive.toString();
|
|
191
|
+
if (!await this.canAddProposal(blockProposal)) {
|
|
192
|
+
throw new ProposalSlotCapExceededError(`Maximum proposals per slot reached: slot=${slotKey} cap=${MAX_PROPOSALS_PER_SLOT} proposal=${proposalId}`);
|
|
193
|
+
}
|
|
194
|
+
await this.proposalsForSlot.set(slotKey, proposalId);
|
|
195
|
+
// Always update the stored proposal buffer so re-adds overwrite with latest data
|
|
196
|
+
await this.proposals.set(proposalId, blockProposal.toBuffer());
|
|
188
197
|
});
|
|
189
198
|
}
|
|
199
|
+
async hasReachedProposalCap(slot) {
|
|
200
|
+
const slotKey = new Fr(slot).toString();
|
|
201
|
+
const uniqueProposalCount = await this.proposalsForSlot.getValueCountAsync(slotKey);
|
|
202
|
+
return uniqueProposalCount >= MAX_PROPOSALS_PER_SLOT;
|
|
203
|
+
}
|
|
204
|
+
async hasReachedAttestationCap(slot, proposalId, committeeSize) {
|
|
205
|
+
const limit = committeeSize + ATTESTATION_CAP_BUFFER;
|
|
206
|
+
return await this.attestationsForProposal.getValueCountAsync(this.getProposalKey(slot, proposalId)) >= limit;
|
|
207
|
+
}
|
|
208
|
+
async canAddProposal(block) {
|
|
209
|
+
return await this.proposals.hasAsync(block.archive.toString()) || !await this.hasReachedProposalCap(block.slotNumber.toBigInt());
|
|
210
|
+
}
|
|
211
|
+
async canAddAttestation(attestation, committeeSize) {
|
|
212
|
+
return await this.hasAttestation(attestation) || !await this.hasReachedAttestationCap(attestation.payload.header.slotNumber.toBigInt(), attestation.archive.toString(), committeeSize);
|
|
213
|
+
}
|
|
190
214
|
}
|
|
@@ -21,5 +21,9 @@ export declare class InMemoryAttestationPool implements AttestationPool {
|
|
|
21
21
|
addBlockProposal(blockProposal: BlockProposal): Promise<void>;
|
|
22
22
|
getBlockProposal(id: string): Promise<BlockProposal | undefined>;
|
|
23
23
|
hasBlockProposal(idOrProposal: string | BlockProposal): Promise<boolean>;
|
|
24
|
+
hasReachedProposalCap(slot: bigint): Promise<boolean>;
|
|
25
|
+
hasReachedAttestationCap(slot: bigint, proposalId: string, committeeSize: number): Promise<boolean>;
|
|
26
|
+
canAddProposal(block: BlockProposal): Promise<boolean>;
|
|
27
|
+
canAddAttestation(attestation: BlockAttestation, committeeSize: number): Promise<boolean>;
|
|
24
28
|
}
|
|
25
29
|
//# sourceMappingURL=memory_attestation_pool.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"memory_attestation_pool.d.ts","sourceRoot":"","sources":["../../../src/mem_pools/attestation_pool/memory_attestation_pool.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,gBAAgB,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AACzE,OAAO,EAAE,KAAK,eAAe,EAAsB,MAAM,yBAAyB,CAAC;AAGnF,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;
|
|
1
|
+
{"version":3,"file":"memory_attestation_pool.d.ts","sourceRoot":"","sources":["../../../src/mem_pools/attestation_pool/memory_attestation_pool.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,gBAAgB,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AACzE,OAAO,EAAE,KAAK,eAAe,EAAsB,MAAM,yBAAyB,CAAC;AAGnF,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AAG7D,qBAAa,uBAAwB,YAAW,eAAe;;IAQ3D,OAAO,CAAC,GAAG;IAPb,OAAO,CAAC,OAAO,CAAwC;IAEvD,OAAO,CAAC,YAAY,CAAgG;IACpH,OAAO,CAAC,SAAS,CAA6B;gBAG5C,SAAS,GAAE,eAAsC,EACzC,GAAG,yCAAuC;IAOpD,OAAO,CAAC,SAAS,CAIf;IAEK,OAAO,IAAI,OAAO,CAAC,OAAO,CAAC;IAI3B,sBAAsB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,gBAAgB,EAAE,CAAC;IAQjE,iCAAiC,CAAC,IAAI,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,gBAAgB,EAAE,CAAC;IAWhG,eAAe,CAAC,YAAY,EAAE,gBAAgB,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC;IA6C1D,2BAA2B,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAqBpE,yBAAyB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAkBtD,oCAAoC,CAAC,IAAI,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAgBrF,kBAAkB,CAAC,YAAY,EAAE,gBAAgB,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC;IAwBnE,cAAc,CAAC,WAAW,EAAE,gBAAgB,GAAG,OAAO,CAAC,OAAO,CAAC;IAuB/D,gBAAgB,CAAC,aAAa,EAAE,aAAa,GAAG,OAAO,CAAC,IAAI,CAAC;IAU7D,gBAAgB,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,GAAG,SAAS,CAAC;IAIhE,gBAAgB,CAAC,YAAY,EAAE,MAAM,GAAG,aAAa,GAAG,OAAO,CAAC,OAAO,CAAC;IAKxE,qBAAqB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAMrD,wBAAwB,CAAC,IAAI,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,aAAa,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAM7F,cAAc,CAAC,KAAK,EAAE,aAAa,GAAG,OAAO,CAAC,OAAO,CAAC;IAMtD,iBAAiB,CAAC,WAAW,EAAE,gBAAgB,EAAE,aAAa,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;CAUvG"}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { createLogger } from '@aztec/foundation/log';
|
|
2
2
|
import { getTelemetryClient } from '@aztec/telemetry-client';
|
|
3
3
|
import { PoolInstrumentation, PoolName } from '../instrumentation.js';
|
|
4
|
+
import { ATTESTATION_CAP_BUFFER, MAX_PROPOSALS_PER_SLOT } from './kv_attestation_pool.js';
|
|
4
5
|
export class InMemoryAttestationPool {
|
|
5
6
|
log;
|
|
6
7
|
metrics;
|
|
@@ -168,6 +169,25 @@ export class InMemoryAttestationPool {
|
|
|
168
169
|
const id = typeof idOrProposal === 'string' ? idOrProposal : idOrProposal.payload.archive.toString();
|
|
169
170
|
return Promise.resolve(this.proposals.has(id));
|
|
170
171
|
}
|
|
172
|
+
hasReachedProposalCap(slot) {
|
|
173
|
+
const slotAttestationMap = this.attestations.get(slot);
|
|
174
|
+
const proposalCount = slotAttestationMap?.size ?? 0;
|
|
175
|
+
return Promise.resolve(proposalCount >= MAX_PROPOSALS_PER_SLOT);
|
|
176
|
+
}
|
|
177
|
+
hasReachedAttestationCap(slot, proposalId, committeeSize) {
|
|
178
|
+
const limit = committeeSize + ATTESTATION_CAP_BUFFER;
|
|
179
|
+
const count = this.attestations.get(slot)?.get(proposalId)?.size ?? 0;
|
|
180
|
+
return Promise.resolve(limit <= 0 || count >= limit);
|
|
181
|
+
}
|
|
182
|
+
async canAddProposal(block) {
|
|
183
|
+
return this.proposals.has(block.archive.toString()) || !await this.hasReachedProposalCap(block.slotNumber.toBigInt());
|
|
184
|
+
}
|
|
185
|
+
async canAddAttestation(attestation, committeeSize) {
|
|
186
|
+
const sender = attestation.getSender();
|
|
187
|
+
const slot = attestation.payload.header.slotNumber.toBigInt();
|
|
188
|
+
const pid = attestation.archive.toString();
|
|
189
|
+
return !!sender && ((this.attestations.get(slot)?.get(pid)?.has(sender.toString()) ?? false) || !await this.hasReachedAttestationCap(slot, pid, committeeSize));
|
|
190
|
+
}
|
|
171
191
|
}
|
|
172
192
|
/**
|
|
173
193
|
* Get Slot or Default
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"block_proposal_validator.d.ts","sourceRoot":"","sources":["../../../src/msg_validators/block_proposal_validator/block_proposal_validator.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,oBAAoB,CAAC;AAG9D,OAAO,EAAE,KAAK,aAAa,EAAE,KAAK,YAAY,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAC;AAE7F,qBAAa,sBAAuB,YAAW,YAAY,CAAC,aAAa,CAAC;IACxE,OAAO,CAAC,UAAU,CAAsB;IACxC,OAAO,CAAC,MAAM,CAAS;IACvB,OAAO,CAAC,YAAY,CAAU;gBAElB,UAAU,EAAE,mBAAmB,EAAE,IAAI,EAAE;QAAE,YAAY,EAAE,OAAO,CAAA;KAAE;IAMtE,QAAQ,CAAC,KAAK,EAAE,aAAa,GAAG,OAAO,CAAC,iBAAiB,GAAG,SAAS,CAAC;
|
|
1
|
+
{"version":3,"file":"block_proposal_validator.d.ts","sourceRoot":"","sources":["../../../src/msg_validators/block_proposal_validator/block_proposal_validator.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,oBAAoB,CAAC;AAG9D,OAAO,EAAE,KAAK,aAAa,EAAE,KAAK,YAAY,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAC;AAE7F,qBAAa,sBAAuB,YAAW,YAAY,CAAC,aAAa,CAAC;IACxE,OAAO,CAAC,UAAU,CAAsB;IACxC,OAAO,CAAC,MAAM,CAAS;IACvB,OAAO,CAAC,YAAY,CAAU;gBAElB,UAAU,EAAE,mBAAmB,EAAE,IAAI,EAAE;QAAE,YAAY,EAAE,OAAO,CAAA;KAAE;IAMtE,QAAQ,CAAC,KAAK,EAAE,aAAa,GAAG,OAAO,CAAC,iBAAiB,GAAG,SAAS,CAAC;CAgF7E"}
|
|
@@ -19,10 +19,22 @@ export class BlockProposalValidator {
|
|
|
19
19
|
return PeerErrorSeverity.MidToleranceError;
|
|
20
20
|
}
|
|
21
21
|
// Check if transactions are permitted when the proposal contains transaction hashes
|
|
22
|
-
|
|
22
|
+
const embeddedTxCount = block.txs?.length ?? 0;
|
|
23
|
+
if (!this.txsPermitted && (block.txHashes.length > 0 || embeddedTxCount > 0)) {
|
|
23
24
|
this.logger.debug(`Penalizing peer for block proposal with ${block.txHashes.length} transaction(s) when transactions are not permitted`);
|
|
24
25
|
return PeerErrorSeverity.MidToleranceError;
|
|
25
26
|
}
|
|
27
|
+
// If there are embedded txs, they must be listed in txHashes; if there are no txHashes, there must be no txs
|
|
28
|
+
const hashSet = new Set(block.txHashes.map((h)=>h.toString()));
|
|
29
|
+
const missingTxHashes = embeddedTxCount > 0 ? block.txs.filter((tx)=>!hashSet.has(tx.getTxHash().toString())).map((tx)=>tx.getTxHash().toString()) : [];
|
|
30
|
+
if (embeddedTxCount > 0 && missingTxHashes.length > 0) {
|
|
31
|
+
this.logger.warn('Penalizing peer for embedded transaction(s) not included in txHashes', {
|
|
32
|
+
embeddedTxCount,
|
|
33
|
+
txHashesLength: block.txHashes.length,
|
|
34
|
+
missingTxHashes
|
|
35
|
+
});
|
|
36
|
+
return PeerErrorSeverity.MidToleranceError;
|
|
37
|
+
}
|
|
26
38
|
const { currentProposer, nextProposer, currentSlot, nextSlot } = await this.epochCache.getProposerAttesterAddressInCurrentOrNextSlot();
|
|
27
39
|
// Check that the attestation is for the current or next slot
|
|
28
40
|
const slotNumberBigInt = block.payload.header.slotNumber.toBigInt();
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"libp2p_service.d.ts","sourceRoot":"","sources":["../../../src/services/libp2p/libp2p_service.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,oBAAoB,CAAC;AAG9D,OAAO,EAAE,KAAK,MAAM,EAA6C,MAAM,uBAAuB,CAAC;AAI/F,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,iBAAiB,CAAC;AAGzD,OAAO,KAAK,EAAE,UAAU,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACrE,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,wBAAwB,CAAC;AAEjE,OAAO,KAAK,EAAE,6BAA6B,EAAE,QAAQ,EAAE,sBAAsB,EAAE,MAAM,iCAAiC,CAAC;AACvH,OAAO,EACL,gBAAgB,EAChB,aAAa,EACb,KAAK,UAAU,EACf,aAAa,EAGb,SAAS,EAIV,MAAM,mBAAmB,CAAC;AAE3B,OAAO,EAAE,EAAE,EAA0D,MAAM,kBAAkB,CAAC;AAG9F,OAAO,EAAkC,KAAK,eAAe,EAAE,UAAU,EAAa,MAAM,yBAAyB,CAAC;AActH,OAAO,EAAE,KAAK,OAAO,EAA4B,KAAK,MAAM,EAAE,oBAAoB,EAAE,MAAM,mBAAmB,CAAC;AAI9G,OAAO,EAAE,GAAG,EAAE,MAAM,oBAAoB,CAAC;AAGzC,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;
|
|
1
|
+
{"version":3,"file":"libp2p_service.d.ts","sourceRoot":"","sources":["../../../src/services/libp2p/libp2p_service.ts"],"names":[],"mappings":"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"}
|
|
@@ -30,6 +30,7 @@ import { mplex } from '@libp2p/mplex';
|
|
|
30
30
|
import { tcp } from '@libp2p/tcp';
|
|
31
31
|
import { ENR } from '@nethermindeth/enr';
|
|
32
32
|
import { createLibp2p } from 'libp2p';
|
|
33
|
+
import { ProposalSlotCapExceededError } from '../../errors/attestation-pool.error.js';
|
|
33
34
|
import { AttestationValidator, BlockProposalValidator, FishermanAttestationValidator } from '../../msg_validators/index.js';
|
|
34
35
|
import { MessageSeenValidator } from '../../msg_validators/msg_seen_validator/msg_seen_validator.js';
|
|
35
36
|
import { getDefaultAllowedSetupFunctions } from '../../msg_validators/tx_validator/allowed_public_setup.js';
|
|
@@ -584,11 +585,20 @@ import { P2PInstrumentation } from './instrumentation.js';
|
|
|
584
585
|
*/ async processAttestationFromPeer(payloadData, msgId, source) {
|
|
585
586
|
const validationFunc = async ()=>{
|
|
586
587
|
const attestation = BlockAttestation.fromBuffer(payloadData);
|
|
588
|
+
const pool = this.mempools.attestationPool;
|
|
587
589
|
const isValid = await this.validateAttestation(source, attestation);
|
|
588
|
-
const exists = isValid && await
|
|
590
|
+
const exists = isValid && await pool.hasAttestation(attestation);
|
|
591
|
+
let canAdd = true;
|
|
592
|
+
if (isValid && !exists) {
|
|
593
|
+
const slot = attestation.payload.header.slotNumber.toBigInt();
|
|
594
|
+
const { committee } = await this.epochCache.getCommittee(slot);
|
|
595
|
+
const committeeSize = committee?.length ?? 0;
|
|
596
|
+
canAdd = await pool.canAddAttestation(attestation, committeeSize);
|
|
597
|
+
}
|
|
589
598
|
this.logger.trace(`Validate propagated block attestation`, {
|
|
590
599
|
isValid,
|
|
591
600
|
exists,
|
|
601
|
+
canAdd,
|
|
592
602
|
[Attributes.SLOT_NUMBER]: attestation.payload.header.slotNumber.toString(),
|
|
593
603
|
[Attributes.P2P_ID]: source.toString()
|
|
594
604
|
});
|
|
@@ -601,6 +611,16 @@ import { P2PInstrumentation } from './instrumentation.js';
|
|
|
601
611
|
result: TopicValidatorResult.Ignore,
|
|
602
612
|
obj: attestation
|
|
603
613
|
};
|
|
614
|
+
} else if (!canAdd) {
|
|
615
|
+
this.logger.warn(`Dropping block attestation due to per-(slot, proposalId) attestation cap`, {
|
|
616
|
+
slot: attestation.payload.header.slotNumber.toString(),
|
|
617
|
+
archive: attestation.archive.toString(),
|
|
618
|
+
source: source.toString()
|
|
619
|
+
});
|
|
620
|
+
return {
|
|
621
|
+
result: TopicValidatorResult.Ignore,
|
|
622
|
+
obj: attestation
|
|
623
|
+
};
|
|
604
624
|
} else {
|
|
605
625
|
return {
|
|
606
626
|
result: TopicValidatorResult.Accept,
|
|
@@ -626,12 +646,15 @@ import { P2PInstrumentation } from './instrumentation.js';
|
|
|
626
646
|
const validationFunc = async ()=>{
|
|
627
647
|
const block = BlockProposal.fromBuffer(payloadData);
|
|
628
648
|
const isValid = await this.validateBlockProposal(source, block);
|
|
649
|
+
const pool = this.mempools.attestationPool;
|
|
629
650
|
// Note that we dont have an attestation pool if we're a prover node, but we still
|
|
630
651
|
// subscribe to block proposal topics in order to prevent their txs from being cleared.
|
|
631
|
-
const exists = isValid && await
|
|
652
|
+
const exists = isValid && await pool?.hasBlockProposal(block);
|
|
653
|
+
const canAdd = isValid && await pool?.canAddProposal(block);
|
|
632
654
|
this.logger.trace(`Validate propagated block proposal`, {
|
|
633
655
|
isValid,
|
|
634
656
|
exists,
|
|
657
|
+
canAdd,
|
|
635
658
|
[Attributes.SLOT_NUMBER]: block.payload.header.slotNumber.toString(),
|
|
636
659
|
[Attributes.P2P_ID]: source.toString()
|
|
637
660
|
});
|
|
@@ -644,6 +667,16 @@ import { P2PInstrumentation } from './instrumentation.js';
|
|
|
644
667
|
result: TopicValidatorResult.Ignore,
|
|
645
668
|
obj: block
|
|
646
669
|
};
|
|
670
|
+
} else if (!canAdd) {
|
|
671
|
+
this.peerManager.penalizePeer(source, PeerErrorSeverity.MidToleranceError);
|
|
672
|
+
this.logger.warn(`Penalizing peer for block proposal exceeding per-slot cap`, {
|
|
673
|
+
slot: block.slotNumber.toString(),
|
|
674
|
+
archive: block.archive.toString(),
|
|
675
|
+
source: source.toString()
|
|
676
|
+
});
|
|
677
|
+
return {
|
|
678
|
+
result: TopicValidatorResult.Reject
|
|
679
|
+
};
|
|
647
680
|
} else {
|
|
648
681
|
return {
|
|
649
682
|
result: TopicValidatorResult.Accept,
|
|
@@ -671,9 +704,22 @@ import { P2PInstrumentation } from './instrumentation.js';
|
|
|
671
704
|
if (attestationsForPreviousSlot !== undefined) {
|
|
672
705
|
this.logger.verbose(`Received ${attestationsForPreviousSlot.length} attestations for slot ${previousSlot}`);
|
|
673
706
|
}
|
|
674
|
-
//
|
|
707
|
+
// Attempt to add proposal, then mark the txs in this proposal as non-evictable
|
|
708
|
+
try {
|
|
709
|
+
await this.mempools.attestationPool?.addBlockProposal(block);
|
|
710
|
+
} catch (err) {
|
|
711
|
+
// Drop proposals if we hit per-slot cap in the attestation pool; rethrow unknown errors
|
|
712
|
+
if (err instanceof ProposalSlotCapExceededError) {
|
|
713
|
+
this.logger.warn(`Dropping block proposal due to per-slot proposal cap`, {
|
|
714
|
+
slot: slot.toString(),
|
|
715
|
+
archive: block.archive.toString(),
|
|
716
|
+
error: err.message
|
|
717
|
+
});
|
|
718
|
+
return;
|
|
719
|
+
}
|
|
720
|
+
throw err;
|
|
721
|
+
}
|
|
675
722
|
await this.mempools.txPool.markTxsAsNonEvictable(block.txHashes);
|
|
676
|
-
await this.mempools.attestationPool?.addBlockProposal(block);
|
|
677
723
|
const attestations = await this.blockReceivedCallback(block, sender);
|
|
678
724
|
// TODO: fix up this pattern - the abstraction is not nice
|
|
679
725
|
// The attestation can be undefined if no handler is registered / the validator deems the block invalid / in fisherman mode
|
|
@@ -54,7 +54,9 @@ function mockAttestationPool() {
|
|
|
54
54
|
addBlockProposal: ()=>Promise.resolve(),
|
|
55
55
|
getBlockProposal: ()=>Promise.resolve(undefined),
|
|
56
56
|
hasBlockProposal: ()=>Promise.resolve(false),
|
|
57
|
-
hasAttestation: ()=>Promise.resolve(false)
|
|
57
|
+
hasAttestation: ()=>Promise.resolve(false),
|
|
58
|
+
canAddProposal: ()=>Promise.resolve(true),
|
|
59
|
+
canAddAttestation: ()=>Promise.resolve(true)
|
|
58
60
|
};
|
|
59
61
|
}
|
|
60
62
|
function mockEpochCache() {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@aztec/p2p",
|
|
3
|
-
"version": "3.0.0-nightly.
|
|
3
|
+
"version": "3.0.0-nightly.20251115",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"exports": {
|
|
6
6
|
".": "./dest/index.js",
|
|
@@ -67,17 +67,17 @@
|
|
|
67
67
|
]
|
|
68
68
|
},
|
|
69
69
|
"dependencies": {
|
|
70
|
-
"@aztec/constants": "3.0.0-nightly.
|
|
71
|
-
"@aztec/epoch-cache": "3.0.0-nightly.
|
|
72
|
-
"@aztec/ethereum": "3.0.0-nightly.
|
|
73
|
-
"@aztec/foundation": "3.0.0-nightly.
|
|
74
|
-
"@aztec/kv-store": "3.0.0-nightly.
|
|
75
|
-
"@aztec/noir-contracts.js": "3.0.0-nightly.
|
|
76
|
-
"@aztec/noir-protocol-circuits-types": "3.0.0-nightly.
|
|
77
|
-
"@aztec/protocol-contracts": "3.0.0-nightly.
|
|
78
|
-
"@aztec/simulator": "3.0.0-nightly.
|
|
79
|
-
"@aztec/stdlib": "3.0.0-nightly.
|
|
80
|
-
"@aztec/telemetry-client": "3.0.0-nightly.
|
|
70
|
+
"@aztec/constants": "3.0.0-nightly.20251115",
|
|
71
|
+
"@aztec/epoch-cache": "3.0.0-nightly.20251115",
|
|
72
|
+
"@aztec/ethereum": "3.0.0-nightly.20251115",
|
|
73
|
+
"@aztec/foundation": "3.0.0-nightly.20251115",
|
|
74
|
+
"@aztec/kv-store": "3.0.0-nightly.20251115",
|
|
75
|
+
"@aztec/noir-contracts.js": "3.0.0-nightly.20251115",
|
|
76
|
+
"@aztec/noir-protocol-circuits-types": "3.0.0-nightly.20251115",
|
|
77
|
+
"@aztec/protocol-contracts": "3.0.0-nightly.20251115",
|
|
78
|
+
"@aztec/simulator": "3.0.0-nightly.20251115",
|
|
79
|
+
"@aztec/stdlib": "3.0.0-nightly.20251115",
|
|
80
|
+
"@aztec/telemetry-client": "3.0.0-nightly.20251115",
|
|
81
81
|
"@chainsafe/libp2p-gossipsub": "13.0.0",
|
|
82
82
|
"@chainsafe/libp2p-noise": "^15.0.0",
|
|
83
83
|
"@chainsafe/libp2p-yamux": "^6.0.2",
|
|
@@ -104,8 +104,8 @@
|
|
|
104
104
|
"xxhash-wasm": "^1.1.0"
|
|
105
105
|
},
|
|
106
106
|
"devDependencies": {
|
|
107
|
-
"@aztec/archiver": "3.0.0-nightly.
|
|
108
|
-
"@aztec/world-state": "3.0.0-nightly.
|
|
107
|
+
"@aztec/archiver": "3.0.0-nightly.20251115",
|
|
108
|
+
"@aztec/world-state": "3.0.0-nightly.20251115",
|
|
109
109
|
"@jest/globals": "^30.0.0",
|
|
110
110
|
"@types/jest": "^30.0.0",
|
|
111
111
|
"@types/node": "^22.15.17",
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export class AttestationPoolError extends Error {
|
|
2
|
+
constructor(message?: string) {
|
|
3
|
+
super(message);
|
|
4
|
+
this.name = 'AttestationPoolError';
|
|
5
|
+
}
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export class ProposalSlotCapExceededError extends AttestationPoolError {
|
|
9
|
+
constructor(message?: string) {
|
|
10
|
+
super(message);
|
|
11
|
+
this.name = 'ProposalSlotCapExceededError';
|
|
12
|
+
}
|
|
13
|
+
}
|
|
@@ -101,6 +101,29 @@ export interface AttestationPool {
|
|
|
101
101
|
*/
|
|
102
102
|
hasAttestation(attestation: BlockAttestation): Promise<boolean>;
|
|
103
103
|
|
|
104
|
+
/**
|
|
105
|
+
* Returns whether adding this proposal is permitted at current capacity:
|
|
106
|
+
* - True if the proposal already exists, allow overwrite to keep parity with tests.
|
|
107
|
+
* - True if the slot is below the proposal cap.
|
|
108
|
+
* - False if the slot is at/above cap and this would be a new unique proposal.
|
|
109
|
+
*
|
|
110
|
+
* @param block - The block proposal to check
|
|
111
|
+
* @returns True if the proposal can be added (or already exists), false otherwise.
|
|
112
|
+
*/
|
|
113
|
+
canAddProposal(block: BlockProposal): Promise<boolean>;
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* Returns whether an attestation would be accepted for (slot, proposalId):
|
|
117
|
+
* - True if the attestation already exists for this sender.
|
|
118
|
+
* - True if the attestation cap for (slot, proposalId) has not been reached.
|
|
119
|
+
* - False if the cap is reached and this attestation would be a new unique entry.
|
|
120
|
+
*
|
|
121
|
+
* @param attestation - The attestation to check
|
|
122
|
+
* @param committeeSize - Committee size for the attestation's slot, implementation may add a small buffer
|
|
123
|
+
* @returns True if the attestation can be added, false otherwise.
|
|
124
|
+
*/
|
|
125
|
+
canAddAttestation(attestation: BlockAttestation, committeeSize: number): Promise<boolean>;
|
|
126
|
+
|
|
104
127
|
/** Returns whether the pool is empty. */
|
|
105
128
|
isEmpty(): Promise<boolean>;
|
|
106
129
|
}
|
|
@@ -5,9 +5,13 @@ import type { AztecAsyncKVStore, AztecAsyncMap, AztecAsyncMultiMap } from '@azte
|
|
|
5
5
|
import { BlockAttestation, BlockProposal } from '@aztec/stdlib/p2p';
|
|
6
6
|
import { type TelemetryClient, getTelemetryClient } from '@aztec/telemetry-client';
|
|
7
7
|
|
|
8
|
+
import { ProposalSlotCapExceededError } from '../../errors/attestation-pool.error.js';
|
|
8
9
|
import { PoolInstrumentation, PoolName, type PoolStatsCallback } from '../instrumentation.js';
|
|
9
10
|
import type { AttestationPool } from './attestation_pool.js';
|
|
10
11
|
|
|
12
|
+
export const MAX_PROPOSALS_PER_SLOT = 5;
|
|
13
|
+
export const ATTESTATION_CAP_BUFFER = 10;
|
|
14
|
+
|
|
11
15
|
export class KvAttestationPool implements AttestationPool {
|
|
12
16
|
private metrics: PoolInstrumentation<BlockAttestation>;
|
|
13
17
|
|
|
@@ -249,8 +253,47 @@ export class KvAttestationPool implements AttestationPool {
|
|
|
249
253
|
|
|
250
254
|
public async addBlockProposal(blockProposal: BlockProposal): Promise<void> {
|
|
251
255
|
await this.store.transactionAsync(async () => {
|
|
252
|
-
|
|
253
|
-
|
|
256
|
+
const slotKey = blockProposal.slotNumber.toString();
|
|
257
|
+
const proposalId = blockProposal.archive.toString();
|
|
258
|
+
|
|
259
|
+
if (!(await this.canAddProposal(blockProposal))) {
|
|
260
|
+
throw new ProposalSlotCapExceededError(
|
|
261
|
+
`Maximum proposals per slot reached: slot=${slotKey} cap=${MAX_PROPOSALS_PER_SLOT} proposal=${proposalId}`,
|
|
262
|
+
);
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
await this.proposalsForSlot.set(slotKey, proposalId);
|
|
266
|
+
// Always update the stored proposal buffer so re-adds overwrite with latest data
|
|
267
|
+
await this.proposals.set(proposalId, blockProposal.toBuffer());
|
|
254
268
|
});
|
|
255
269
|
}
|
|
270
|
+
|
|
271
|
+
public async hasReachedProposalCap(slot: bigint): Promise<boolean> {
|
|
272
|
+
const slotKey = new Fr(slot).toString();
|
|
273
|
+
const uniqueProposalCount = await this.proposalsForSlot.getValueCountAsync(slotKey);
|
|
274
|
+
return uniqueProposalCount >= MAX_PROPOSALS_PER_SLOT;
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
public async hasReachedAttestationCap(slot: bigint, proposalId: string, committeeSize: number): Promise<boolean> {
|
|
278
|
+
const limit = committeeSize + ATTESTATION_CAP_BUFFER;
|
|
279
|
+
return (await this.attestationsForProposal.getValueCountAsync(this.getProposalKey(slot, proposalId))) >= limit;
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
public async canAddProposal(block: BlockProposal): Promise<boolean> {
|
|
283
|
+
return (
|
|
284
|
+
(await this.proposals.hasAsync(block.archive.toString())) ||
|
|
285
|
+
!(await this.hasReachedProposalCap(block.slotNumber.toBigInt()))
|
|
286
|
+
);
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
public async canAddAttestation(attestation: BlockAttestation, committeeSize: number): Promise<boolean> {
|
|
290
|
+
return (
|
|
291
|
+
(await this.hasAttestation(attestation)) ||
|
|
292
|
+
!(await this.hasReachedAttestationCap(
|
|
293
|
+
attestation.payload.header.slotNumber.toBigInt(),
|
|
294
|
+
attestation.archive.toString(),
|
|
295
|
+
committeeSize,
|
|
296
|
+
))
|
|
297
|
+
);
|
|
298
|
+
}
|
|
256
299
|
}
|
|
@@ -4,6 +4,7 @@ import { type TelemetryClient, getTelemetryClient } from '@aztec/telemetry-clien
|
|
|
4
4
|
|
|
5
5
|
import { PoolInstrumentation, PoolName, type PoolStatsCallback } from '../instrumentation.js';
|
|
6
6
|
import type { AttestationPool } from './attestation_pool.js';
|
|
7
|
+
import { ATTESTATION_CAP_BUFFER, MAX_PROPOSALS_PER_SLOT } from './kv_attestation_pool.js';
|
|
7
8
|
|
|
8
9
|
export class InMemoryAttestationPool implements AttestationPool {
|
|
9
10
|
private metrics: PoolInstrumentation<BlockAttestation>;
|
|
@@ -214,6 +215,35 @@ export class InMemoryAttestationPool implements AttestationPool {
|
|
|
214
215
|
const id = typeof idOrProposal === 'string' ? idOrProposal : idOrProposal.payload.archive.toString();
|
|
215
216
|
return Promise.resolve(this.proposals.has(id));
|
|
216
217
|
}
|
|
218
|
+
|
|
219
|
+
public hasReachedProposalCap(slot: bigint): Promise<boolean> {
|
|
220
|
+
const slotAttestationMap = this.attestations.get(slot);
|
|
221
|
+
const proposalCount = slotAttestationMap?.size ?? 0;
|
|
222
|
+
return Promise.resolve(proposalCount >= MAX_PROPOSALS_PER_SLOT);
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
public hasReachedAttestationCap(slot: bigint, proposalId: string, committeeSize: number): Promise<boolean> {
|
|
226
|
+
const limit = committeeSize + ATTESTATION_CAP_BUFFER;
|
|
227
|
+
const count = this.attestations.get(slot)?.get(proposalId)?.size ?? 0;
|
|
228
|
+
return Promise.resolve(limit <= 0 || count >= limit);
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
public async canAddProposal(block: BlockProposal): Promise<boolean> {
|
|
232
|
+
return (
|
|
233
|
+
this.proposals.has(block.archive.toString()) || !(await this.hasReachedProposalCap(block.slotNumber.toBigInt()))
|
|
234
|
+
);
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
public async canAddAttestation(attestation: BlockAttestation, committeeSize: number): Promise<boolean> {
|
|
238
|
+
const sender = attestation.getSender();
|
|
239
|
+
const slot = attestation.payload.header.slotNumber.toBigInt();
|
|
240
|
+
const pid = attestation.archive.toString();
|
|
241
|
+
return (
|
|
242
|
+
!!sender &&
|
|
243
|
+
((this.attestations.get(slot)?.get(pid)?.has(sender.toString()) ?? false) ||
|
|
244
|
+
!(await this.hasReachedAttestationCap(slot, pid, committeeSize)))
|
|
245
|
+
);
|
|
246
|
+
}
|
|
217
247
|
}
|
|
218
248
|
|
|
219
249
|
/**
|
|
@@ -24,13 +24,29 @@ export class BlockProposalValidator implements P2PValidator<BlockProposal> {
|
|
|
24
24
|
}
|
|
25
25
|
|
|
26
26
|
// Check if transactions are permitted when the proposal contains transaction hashes
|
|
27
|
-
|
|
27
|
+
const embeddedTxCount = block.txs?.length ?? 0;
|
|
28
|
+
if (!this.txsPermitted && (block.txHashes.length > 0 || embeddedTxCount > 0)) {
|
|
28
29
|
this.logger.debug(
|
|
29
30
|
`Penalizing peer for block proposal with ${block.txHashes.length} transaction(s) when transactions are not permitted`,
|
|
30
31
|
);
|
|
31
32
|
return PeerErrorSeverity.MidToleranceError;
|
|
32
33
|
}
|
|
33
34
|
|
|
35
|
+
// If there are embedded txs, they must be listed in txHashes; if there are no txHashes, there must be no txs
|
|
36
|
+
const hashSet = new Set(block.txHashes.map(h => h.toString()));
|
|
37
|
+
const missingTxHashes =
|
|
38
|
+
embeddedTxCount > 0
|
|
39
|
+
? block.txs!.filter(tx => !hashSet.has(tx.getTxHash().toString())).map(tx => tx.getTxHash().toString())
|
|
40
|
+
: [];
|
|
41
|
+
if (embeddedTxCount > 0 && missingTxHashes.length > 0) {
|
|
42
|
+
this.logger.warn('Penalizing peer for embedded transaction(s) not included in txHashes', {
|
|
43
|
+
embeddedTxCount,
|
|
44
|
+
txHashesLength: block.txHashes.length,
|
|
45
|
+
missingTxHashes,
|
|
46
|
+
});
|
|
47
|
+
return PeerErrorSeverity.MidToleranceError;
|
|
48
|
+
}
|
|
49
|
+
|
|
34
50
|
const { currentProposer, nextProposer, currentSlot, nextSlot } =
|
|
35
51
|
await this.epochCache.getProposerAttesterAddressInCurrentOrNextSlot();
|
|
36
52
|
|
|
@@ -50,6 +50,7 @@ import { ENR } from '@nethermindeth/enr';
|
|
|
50
50
|
import { createLibp2p } from 'libp2p';
|
|
51
51
|
|
|
52
52
|
import type { P2PConfig } from '../../config.js';
|
|
53
|
+
import { ProposalSlotCapExceededError } from '../../errors/attestation-pool.error.js';
|
|
53
54
|
import type { MemPools } from '../../mem_pools/interface.js';
|
|
54
55
|
import {
|
|
55
56
|
AttestationValidator,
|
|
@@ -800,12 +801,22 @@ export class LibP2PService<T extends P2PClientType = P2PClientType.Full> extends
|
|
|
800
801
|
private async processAttestationFromPeer(payloadData: Buffer, msgId: string, source: PeerId): Promise<void> {
|
|
801
802
|
const validationFunc: () => Promise<ReceivedMessageValidationResult<BlockAttestation>> = async () => {
|
|
802
803
|
const attestation = BlockAttestation.fromBuffer(payloadData);
|
|
804
|
+
const pool = this.mempools.attestationPool!;
|
|
803
805
|
const isValid = await this.validateAttestation(source, attestation);
|
|
804
|
-
const exists = isValid && (await
|
|
806
|
+
const exists = isValid && (await pool.hasAttestation(attestation));
|
|
807
|
+
|
|
808
|
+
let canAdd = true;
|
|
809
|
+
if (isValid && !exists) {
|
|
810
|
+
const slot = attestation.payload.header.slotNumber.toBigInt();
|
|
811
|
+
const { committee } = await this.epochCache.getCommittee(slot);
|
|
812
|
+
const committeeSize = committee?.length ?? 0;
|
|
813
|
+
canAdd = await pool.canAddAttestation(attestation, committeeSize);
|
|
814
|
+
}
|
|
805
815
|
|
|
806
816
|
this.logger.trace(`Validate propagated block attestation`, {
|
|
807
817
|
isValid,
|
|
808
818
|
exists,
|
|
819
|
+
canAdd,
|
|
809
820
|
[Attributes.SLOT_NUMBER]: attestation.payload.header.slotNumber.toString(),
|
|
810
821
|
[Attributes.P2P_ID]: source.toString(),
|
|
811
822
|
});
|
|
@@ -814,6 +825,13 @@ export class LibP2PService<T extends P2PClientType = P2PClientType.Full> extends
|
|
|
814
825
|
return { result: TopicValidatorResult.Reject };
|
|
815
826
|
} else if (exists) {
|
|
816
827
|
return { result: TopicValidatorResult.Ignore, obj: attestation };
|
|
828
|
+
} else if (!canAdd) {
|
|
829
|
+
this.logger.warn(`Dropping block attestation due to per-(slot, proposalId) attestation cap`, {
|
|
830
|
+
slot: attestation.payload.header.slotNumber.toString(),
|
|
831
|
+
archive: attestation.archive.toString(),
|
|
832
|
+
source: source.toString(),
|
|
833
|
+
});
|
|
834
|
+
return { result: TopicValidatorResult.Ignore, obj: attestation };
|
|
817
835
|
} else {
|
|
818
836
|
return { result: TopicValidatorResult.Accept, obj: attestation };
|
|
819
837
|
}
|
|
@@ -847,14 +865,17 @@ export class LibP2PService<T extends P2PClientType = P2PClientType.Full> extends
|
|
|
847
865
|
const validationFunc: () => Promise<ReceivedMessageValidationResult<BlockProposal>> = async () => {
|
|
848
866
|
const block = BlockProposal.fromBuffer(payloadData);
|
|
849
867
|
const isValid = await this.validateBlockProposal(source, block);
|
|
868
|
+
const pool = this.mempools.attestationPool;
|
|
850
869
|
|
|
851
870
|
// Note that we dont have an attestation pool if we're a prover node, but we still
|
|
852
871
|
// subscribe to block proposal topics in order to prevent their txs from being cleared.
|
|
853
|
-
const exists = isValid && (await
|
|
872
|
+
const exists = isValid && (await pool?.hasBlockProposal(block));
|
|
873
|
+
const canAdd = isValid && (await pool?.canAddProposal(block));
|
|
854
874
|
|
|
855
875
|
this.logger.trace(`Validate propagated block proposal`, {
|
|
856
876
|
isValid,
|
|
857
877
|
exists,
|
|
878
|
+
canAdd,
|
|
858
879
|
[Attributes.SLOT_NUMBER]: block.payload.header.slotNumber.toString(),
|
|
859
880
|
[Attributes.P2P_ID]: source.toString(),
|
|
860
881
|
});
|
|
@@ -863,6 +884,14 @@ export class LibP2PService<T extends P2PClientType = P2PClientType.Full> extends
|
|
|
863
884
|
return { result: TopicValidatorResult.Reject };
|
|
864
885
|
} else if (exists) {
|
|
865
886
|
return { result: TopicValidatorResult.Ignore, obj: block };
|
|
887
|
+
} else if (!canAdd) {
|
|
888
|
+
this.peerManager.penalizePeer(source, PeerErrorSeverity.MidToleranceError);
|
|
889
|
+
this.logger.warn(`Penalizing peer for block proposal exceeding per-slot cap`, {
|
|
890
|
+
slot: block.slotNumber.toString(),
|
|
891
|
+
archive: block.archive.toString(),
|
|
892
|
+
source: source.toString(),
|
|
893
|
+
});
|
|
894
|
+
return { result: TopicValidatorResult.Reject };
|
|
866
895
|
} else {
|
|
867
896
|
return { result: TopicValidatorResult.Accept, obj: block };
|
|
868
897
|
}
|
|
@@ -902,9 +931,22 @@ export class LibP2PService<T extends P2PClientType = P2PClientType.Full> extends
|
|
|
902
931
|
this.logger.verbose(`Received ${attestationsForPreviousSlot.length} attestations for slot ${previousSlot}`);
|
|
903
932
|
}
|
|
904
933
|
|
|
905
|
-
//
|
|
934
|
+
// Attempt to add proposal, then mark the txs in this proposal as non-evictable
|
|
935
|
+
try {
|
|
936
|
+
await this.mempools.attestationPool?.addBlockProposal(block);
|
|
937
|
+
} catch (err: unknown) {
|
|
938
|
+
// Drop proposals if we hit per-slot cap in the attestation pool; rethrow unknown errors
|
|
939
|
+
if (err instanceof ProposalSlotCapExceededError) {
|
|
940
|
+
this.logger.warn(`Dropping block proposal due to per-slot proposal cap`, {
|
|
941
|
+
slot: slot.toString(),
|
|
942
|
+
archive: block.archive.toString(),
|
|
943
|
+
error: (err as Error).message,
|
|
944
|
+
});
|
|
945
|
+
return;
|
|
946
|
+
}
|
|
947
|
+
throw err;
|
|
948
|
+
}
|
|
906
949
|
await this.mempools.txPool.markTxsAsNonEvictable(block.txHashes);
|
|
907
|
-
await this.mempools.attestationPool?.addBlockProposal(block);
|
|
908
950
|
const attestations = await this.blockReceivedCallback(block, sender);
|
|
909
951
|
|
|
910
952
|
// TODO: fix up this pattern - the abstraction is not nice
|
|
@@ -74,6 +74,8 @@ function mockAttestationPool(): AttestationPool {
|
|
|
74
74
|
getBlockProposal: () => Promise.resolve(undefined),
|
|
75
75
|
hasBlockProposal: () => Promise.resolve(false),
|
|
76
76
|
hasAttestation: () => Promise.resolve(false),
|
|
77
|
+
canAddProposal: () => Promise.resolve(true),
|
|
78
|
+
canAddAttestation: () => Promise.resolve(true),
|
|
77
79
|
};
|
|
78
80
|
}
|
|
79
81
|
|