@aztec/p2p 0.77.0-testnet-ignition.30 → 0.77.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/client/factory.js +2 -2
- package/dest/config.d.ts +2 -7
- package/dest/config.d.ts.map +1 -1
- package/dest/config.js +3 -8
- package/dest/services/libp2p/libp2p_service.d.ts +1 -1
- package/dest/services/libp2p/libp2p_service.d.ts.map +1 -1
- package/dest/services/libp2p/libp2p_service.js +10 -13
- package/dest/testbench/testbench.js +12 -92
- package/dest/testbench/worker_client_manager.d.ts +56 -0
- package/dest/testbench/worker_client_manager.d.ts.map +1 -0
- package/dest/testbench/worker_client_manager.js +266 -0
- package/package.json +10 -10
- package/src/client/factory.ts +2 -2
- package/src/config.ts +4 -14
- package/src/services/libp2p/libp2p_service.ts +10 -14
- package/src/testbench/testbench.ts +16 -107
- package/src/testbench/worker_client_manager.ts +318 -0
package/dest/client/factory.js
CHANGED
|
@@ -14,8 +14,8 @@ export const createP2PClient = async (clientType, _config, l2BlockSource, proofV
|
|
|
14
14
|
..._config
|
|
15
15
|
};
|
|
16
16
|
const logger = deps.logger ?? createLogger('p2p');
|
|
17
|
-
const store = deps.store ?? await createStore('p2p', config, createLogger('p2p:lmdb-v2'));
|
|
18
|
-
const archive = await createStore('p2p-archive', config, createLogger('p2p-archive:lmdb-v2'));
|
|
17
|
+
const store = deps.store ?? await createStore('p2p', 1, config, createLogger('p2p:lmdb-v2'));
|
|
18
|
+
const archive = await createStore('p2p-archive', 1, config, createLogger('p2p-archive:lmdb-v2'));
|
|
19
19
|
const mempools = {
|
|
20
20
|
txPool: deps.txPool ?? new AztecKVTxPool(store, archive, telemetry, config.archivedTxLimit),
|
|
21
21
|
attestationPool: clientType === P2PClientType.Full ? deps.attestationPool ?? new InMemoryAttestationPool(telemetry) : undefined
|
package/dest/config.d.ts
CHANGED
|
@@ -58,10 +58,6 @@ export interface P2PConfig extends P2PReqRespConfig, ChainConfig {
|
|
|
58
58
|
bootstrapNodeEnrVersionCheck: boolean;
|
|
59
59
|
/** Whether to consider any configured bootnodes as full peers, e.g. for transaction gossiping */
|
|
60
60
|
bootstrapNodesAsFullPeers: boolean;
|
|
61
|
-
/**
|
|
62
|
-
* Protocol identifier for transaction gossiping.
|
|
63
|
-
*/
|
|
64
|
-
transactionProtocol: string;
|
|
65
61
|
/**
|
|
66
62
|
* The maximum number of peers (a peer count above this will cause the node to refuse connection attempts)
|
|
67
63
|
*/
|
|
@@ -109,7 +105,7 @@ export interface P2PConfig extends P2PReqRespConfig, ChainConfig {
|
|
|
109
105
|
/**
|
|
110
106
|
* The 'age' (in # of L2 blocks) of a processed tx after which we heavily penalize a peer for re-sending it.
|
|
111
107
|
*/
|
|
112
|
-
|
|
108
|
+
doubleSpendSeverePeerPenaltyWindow: number;
|
|
113
109
|
/**
|
|
114
110
|
* The weight of the tx topic for the gossipsub protocol. This determines how much the score for this specific topic contributes to the overall peer score.
|
|
115
111
|
*/
|
|
@@ -162,7 +158,6 @@ export declare const bootnodeConfigMappings: ConfigMappingsType<Pick<{
|
|
|
162
158
|
bootstrapNodes: unknown;
|
|
163
159
|
bootstrapNodeEnrVersionCheck: unknown;
|
|
164
160
|
bootstrapNodesAsFullPeers: unknown;
|
|
165
|
-
transactionProtocol: unknown;
|
|
166
161
|
maxPeerCount: unknown;
|
|
167
162
|
queryForIp: unknown;
|
|
168
163
|
keepProvenTxsInPoolFor: unknown;
|
|
@@ -175,7 +170,7 @@ export declare const bootnodeConfigMappings: ConfigMappingsType<Pick<{
|
|
|
175
170
|
gossipsubFloodPublish: unknown;
|
|
176
171
|
gossipsubMcacheLength: unknown;
|
|
177
172
|
gossipsubMcacheGossip: unknown;
|
|
178
|
-
|
|
173
|
+
doubleSpendSeverePeerPenaltyWindow: unknown;
|
|
179
174
|
gossipsubTxTopicWeight: unknown;
|
|
180
175
|
gossipsubTxInvalidMessageDeliveriesWeight: unknown;
|
|
181
176
|
gossipsubTxInvalidMessageDeliveriesDecay: unknown;
|
package/dest/config.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,KAAK,kBAAkB,EAMxB,MAAM,0BAA0B,CAAC;AAClC,OAAO,EAAE,KAAK,eAAe,EAAsB,MAAM,wBAAwB,CAAC;AAClF,OAAO,EAAE,KAAK,WAAW,EAAuB,MAAM,sBAAsB,CAAC;AAE7E,OAAO,EAAE,KAAK,gBAAgB,EAA4B,MAAM,8BAA8B,CAAC;AAE/F;;GAEG;AACH,MAAM,WAAW,SAAU,SAAQ,gBAAgB,EAAE,WAAW;IAC9D;;OAEG;IACH,UAAU,EAAE,OAAO,CAAC;IAEpB;;OAEG;IACH,oBAAoB,EAAE,MAAM,CAAC;IAE7B;;OAEG;IACH,qBAAqB,EAAE,MAAM,CAAC;IAE9B;;OAEG;IACH,6BAA6B,EAAE,OAAO,CAAC;IAEvC;;OAEG;IACH,mBAAmB,EAAE,MAAM,CAAC;IAE5B;;OAEG;IACH,WAAW,EAAE,MAAM,CAAC;IAEpB;;OAEG;IACH,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAE5B;;OAEG;IACH,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAE5B;;OAEG;IACH,gBAAgB,EAAE,MAAM,CAAC;IAEzB;;OAEG;IACH,gBAAgB,EAAE,MAAM,CAAC;IAEzB;;OAEG;IACH,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAE1B;;OAEG;IACH,cAAc,EAAE,MAAM,EAAE,CAAC;IAEzB,sEAAsE;IACtE,4BAA4B,EAAE,OAAO,CAAC;IAEtC,iGAAiG;IACjG,yBAAyB,EAAE,OAAO,CAAC;IAEnC;;OAEG;IACH,
|
|
1
|
+
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,KAAK,kBAAkB,EAMxB,MAAM,0BAA0B,CAAC;AAClC,OAAO,EAAE,KAAK,eAAe,EAAsB,MAAM,wBAAwB,CAAC;AAClF,OAAO,EAAE,KAAK,WAAW,EAAuB,MAAM,sBAAsB,CAAC;AAE7E,OAAO,EAAE,KAAK,gBAAgB,EAA4B,MAAM,8BAA8B,CAAC;AAE/F;;GAEG;AACH,MAAM,WAAW,SAAU,SAAQ,gBAAgB,EAAE,WAAW;IAC9D;;OAEG;IACH,UAAU,EAAE,OAAO,CAAC;IAEpB;;OAEG;IACH,oBAAoB,EAAE,MAAM,CAAC;IAE7B;;OAEG;IACH,qBAAqB,EAAE,MAAM,CAAC;IAE9B;;OAEG;IACH,6BAA6B,EAAE,OAAO,CAAC;IAEvC;;OAEG;IACH,mBAAmB,EAAE,MAAM,CAAC;IAE5B;;OAEG;IACH,WAAW,EAAE,MAAM,CAAC;IAEpB;;OAEG;IACH,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAE5B;;OAEG;IACH,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAE5B;;OAEG;IACH,gBAAgB,EAAE,MAAM,CAAC;IAEzB;;OAEG;IACH,gBAAgB,EAAE,MAAM,CAAC;IAEzB;;OAEG;IACH,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAE1B;;OAEG;IACH,cAAc,EAAE,MAAM,EAAE,CAAC;IAEzB,sEAAsE;IACtE,4BAA4B,EAAE,OAAO,CAAC;IAEtC,iGAAiG;IACjG,yBAAyB,EAAE,OAAO,CAAC;IAEnC;;OAEG;IACH,YAAY,EAAE,MAAM,CAAC;IAErB;;OAEG;IACH,UAAU,EAAE,OAAO,CAAC;IAEpB,+HAA+H;IAC/H,sBAAsB,EAAE,MAAM,CAAC;IAE/B,+CAA+C;IAC/C,yBAAyB,EAAE,MAAM,CAAC;IAElC;;OAEG;IACH,iBAAiB,EAAE,MAAM,CAAC;IAE1B;;OAEG;IACH,UAAU,EAAE,MAAM,CAAC;IAEnB;;OAEG;IACH,YAAY,EAAE,MAAM,CAAC;IAErB;;OAEG;IACH,YAAY,EAAE,MAAM,CAAC;IAErB;;OAEG;IACH,cAAc,EAAE,MAAM,CAAC;IAEvB;;OAEG;IACH,qBAAqB,EAAE,OAAO,CAAC;IAE/B;;OAEG;IACH,qBAAqB,EAAE,MAAM,CAAC;IAE9B;;OAEG;IACH,qBAAqB,EAAE,MAAM,CAAC;IAE9B;;OAEG;IACH,kCAAkC,EAAE,MAAM,CAAC;IAE3C;;OAEG;IACH,sBAAsB,EAAE,MAAM,CAAC;IAE/B;;OAEG;IACH,yCAAyC,EAAE,MAAM,CAAC;IAElD;;OAEG;IACH,wCAAwC,EAAE,MAAM,CAAC;IAEjD;;OAEG;IACH,iBAAiB,EAAE,MAAM,EAAE,CAAC;IAE5B,sIAAsI;IACtI,eAAe,EAAE,MAAM,CAAC;CACzB;AAED,eAAO,MAAM,iBAAiB,EAAE,kBAAkB,CAAC,SAAS,CAuK3D,CAAC;AAEF;;;GAGG;AACH,wBAAgB,mBAAmB,IAAI,SAAS,CAE/C;AAED,wBAAgB,mBAAmB,IAAI,SAAS,CAE/C;AAED;;GAEG;AACH,MAAM,MAAM,cAAc,GAAG,IAAI,CAAC,SAAS,EAAE,oBAAoB,GAAG,kBAAkB,GAAG,gBAAgB,CAAC,GACxG,QAAQ,CAAC,IAAI,CAAC,SAAS,EAAE,kBAAkB,CAAC,CAAC,GAC7C,IAAI,CAAC,eAAe,EAAE,eAAe,GAAG,oBAAoB,CAAC,GAC7D,IAAI,CAAC,WAAW,EAAE,WAAW,CAAC,CAAC;AAYjC,eAAO,MAAM,sBAAsB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;6IAGlC,CAAC"}
|
package/dest/config.js
CHANGED
|
@@ -65,11 +65,6 @@ export const p2pConfigMappings = {
|
|
|
65
65
|
description: 'Whether to consider our configured bootnodes as full peers',
|
|
66
66
|
...booleanConfigHelper(false)
|
|
67
67
|
},
|
|
68
|
-
transactionProtocol: {
|
|
69
|
-
env: 'P2P_TX_PROTOCOL',
|
|
70
|
-
description: 'Protocol identifier for transaction gossiping.',
|
|
71
|
-
defaultValue: '/aztec/0.1.0'
|
|
72
|
-
},
|
|
73
68
|
maxPeerCount: {
|
|
74
69
|
env: 'P2P_MAX_PEERS',
|
|
75
70
|
description: 'The maximum number of peers to connect to.',
|
|
@@ -113,7 +108,7 @@ export const p2pConfigMappings = {
|
|
|
113
108
|
gossipsubDLazy: {
|
|
114
109
|
env: 'P2P_GOSSIPSUB_DLAZY',
|
|
115
110
|
description: 'The Dlazy parameter for the gossipsub protocol.',
|
|
116
|
-
...numberConfigHelper(
|
|
111
|
+
...numberConfigHelper(8)
|
|
117
112
|
},
|
|
118
113
|
gossipsubFloodPublish: {
|
|
119
114
|
env: 'P2P_GOSSIPSUB_FLOOD_PUBLISH',
|
|
@@ -155,8 +150,8 @@ export const p2pConfigMappings = {
|
|
|
155
150
|
50
|
|
156
151
|
]
|
|
157
152
|
},
|
|
158
|
-
|
|
159
|
-
env: '
|
|
153
|
+
doubleSpendSeverePeerPenaltyWindow: {
|
|
154
|
+
env: 'P2P_DOUBLE_SPEND_SEVERE_PEER_PENALTY_WINDOW',
|
|
160
155
|
description: 'The "age" (in L2 blocks) of a tx after which we heavily penalize a peer for sending it.',
|
|
161
156
|
...numberConfigHelper(30)
|
|
162
157
|
},
|
|
@@ -162,7 +162,7 @@ export declare class LibP2PService<T extends P2PClientType> extends WithTracer i
|
|
|
162
162
|
* @param tx - The tx that failed the double spend validator.
|
|
163
163
|
* @param blockNumber - The block number of the tx.
|
|
164
164
|
* @param peerId - The peer ID of the peer that sent the tx.
|
|
165
|
-
* @returns
|
|
165
|
+
* @returns Severity
|
|
166
166
|
*/
|
|
167
167
|
private handleDoubleSpendFailure;
|
|
168
168
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"libp2p_service.d.ts","sourceRoot":"","sources":["../../../src/services/libp2p/libp2p_service.ts"],"names":[],"mappings":"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
|
|
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;AAK9D,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,iBAAiB,CAAC;AACzD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACzD,OAAO,KAAK,EAAE,6BAA6B,EAAE,QAAQ,EAAE,sBAAsB,EAAE,MAAM,iCAAiC,CAAC;AACvH,OAAO,EACL,gBAAgB,EAChB,aAAa,EACb,KAAK,UAAU,EACf,aAAa,EAKd,MAAM,mBAAmB,CAAC;AAG3B,OAAO,EAAkC,KAAK,eAAe,EAAE,UAAU,EAAa,MAAM,yBAAyB,CAAC;AAEtH,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,gBAAgB,CAAC;AAa1C,OAAO,EAAgB,KAAK,MAAM,EAAwB,MAAM,mBAAmB,CAAC;AAEpF,OAAO,iBAAiB,CAAC;AAKzB,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AACjD,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,8BAA8B,CAAC;AAS7D,OAAO,EAAE,KAAK,YAAY,EAAsB,MAAM,eAAe,CAAC;AAMtE,OAAO,EAAmC,kBAAkB,EAAE,KAAK,cAAc,EAAE,MAAM,yBAAyB,CAAC;AAGnH,OAAO,EAAE,OAAO,EAAE,MAAM,uBAAuB,CAAC;AAChD,OAAO,KAAK,EAAE,UAAU,EAAE,oBAAoB,EAAE,MAAM,eAAe,CAAC;AAiBtE;;GAEG;AACH,qBAAa,aAAa,CAAC,CAAC,SAAS,aAAa,CAAE,SAAQ,UAAW,YAAW,UAAU;IAoBxF,OAAO,CAAC,UAAU;IAClB,OAAO,CAAC,MAAM;IACd,OAAO,CAAC,IAAI;IACZ,OAAO,CAAC,oBAAoB;IAC5B,OAAO,CAAC,QAAQ;IAChB,OAAO,CAAC,aAAa;IAErB,OAAO,CAAC,aAAa;IACrB,OAAO,CAAC,sBAAsB;IAE9B,OAAO,CAAC,MAAM;IA7BhB,OAAO,CAAC,QAAQ,CAAkC;IAClD,OAAO,CAAC,WAAW,CAAc;IACjC,OAAO,CAAC,uBAAuB,CAAC,CAAiB;IAGjD,OAAO,CAAC,oBAAoB,CAAuB;IACnD,OAAO,CAAC,sBAAsB,CAAyB;IAGhD,OAAO,EAAE,OAAO,CAAC;IAExB;;;;OAIG;IACH,OAAO,CAAC,qBAAqB,CAAkE;gBAGrF,UAAU,EAAE,CAAC,EACb,MAAM,EAAE,SAAS,EACjB,IAAI,EAAE,YAAY,EAClB,oBAAoB,EAAE,oBAAoB,EAC1C,QAAQ,EAAE,QAAQ,CAAC,CAAC,CAAC,EACrB,aAAa,EAAE,aAAa,EACpC,UAAU,EAAE,mBAAmB,EACvB,aAAa,EAAE,6BAA6B,EAC5C,sBAAsB,EAAE,sBAAsB,EACtD,SAAS,EAAE,eAAe,EAClB,MAAM,yCAAqC;IAmCrD;;;;;OAKG;WACiB,GAAG,CAAC,CAAC,SAAS,aAAa,EAC7C,UAAU,EAAE,CAAC,EACb,MAAM,EAAE,SAAS,EACjB,oBAAoB,EAAE,oBAAoB,EAC1C,MAAM,EAAE,MAAM,EACd,QAAQ,EAAE,QAAQ,CAAC,CAAC,CAAC,EACrB,aAAa,EAAE,aAAa,EAC5B,UAAU,EAAE,mBAAmB,EAC/B,aAAa,EAAE,6BAA6B,EAC5C,sBAAsB,EAAE,sBAAsB,EAC9C,KAAK,EAAE,iBAAiB,EACxB,SAAS,EAAE,eAAe,EAC1B,MAAM,yCAAqC;IAqH7C;;;OAGG;IACU,KAAK;IA6DlB;;;OAGG;IACU,IAAI;IAqBV,QAAQ,CAAC,cAAc,CAAC,EAAE,OAAO,GAAG,QAAQ,EAAE;IAIrD,OAAO,CAAC,oBAAoB;IAa5B;;;;;;;;;OASG;IACH,WAAW,CAAC,WAAW,SAAS,kBAAkB,EAChD,QAAQ,EAAE,WAAW,EACrB,OAAO,EAAE,YAAY,CAAC,cAAc,CAAC,WAAW,CAAC,CAAC,SAAS,CAAC,CAAC,GAC5D,OAAO,CAAC,YAAY,CAAC,cAAc,CAAC,WAAW,CAAC,CAAC,UAAU,CAAC,CAAC,GAAG,SAAS,CAAC;IAI7E;;;;;OAKG;IACH,gBAAgB,CAAC,WAAW,SAAS,kBAAkB,EACrD,QAAQ,EAAE,WAAW,EACrB,QAAQ,EAAE,YAAY,CAAC,cAAc,CAAC,WAAW,CAAC,CAAC,SAAS,CAAC,CAAC,EAAE,GAC/D,OAAO,CAAC,YAAY,CAAC,cAAc,CAAC,WAAW,CAAC,CAAC,UAAU,CAAC,CAAC,EAAE,GAAG,SAAS,CAAC;IAI/E;;;OAGG;IACI,MAAM,IAAI,GAAG,GAAG,SAAS;IAIzB,6BAA6B,CAAC,QAAQ,EAAE,CAAC,KAAK,EAAE,aAAa,KAAK,OAAO,CAAC,gBAAgB,GAAG,SAAS,CAAC;IAK9G;;;OAGG;IACH,OAAO,CAAC,gBAAgB;IAOxB;;;;;OAKG;YACW,cAAc;IAS5B;;;;OAIG;YACW,sBAAsB;YActB,uBAAuB;YAoBvB,gBAAgB;IAiB9B;;;;OAIG;YACW,0BAA0B;YA+B1B,oBAAoB;YAyBpB,yBAAyB;IA4BvC;;;OAGG;YAOW,oBAAoB;IAIlC;;;OAGG;IACU,SAAS,CAAC,CAAC,SAAS,UAAU,EAAE,OAAO,EAAE,CAAC;IAYvD;;;;;;;;;;;;;OAaG;YAIW,mBAAmB;YAuBnB,oBAAoB;IAoBlC;;;;;;;;OAQG;IACH,OAAO,CAAC,uBAAuB;IA2B/B;;;;;OAKG;YACW,cAAc;IA8B5B;;;;;;;;;;OAUG;YACW,wBAAwB;IAuBtC;;;;;OAKG;IAOU,mBAAmB,CAAC,MAAM,EAAE,MAAM,EAAE,WAAW,EAAE,gBAAgB,GAAG,OAAO,CAAC,OAAO,CAAC;IAUjG;;;;;OAKG;IAIU,qBAAqB,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,aAAa,GAAG,OAAO,CAAC,OAAO,CAAC;IAUnF,YAAY,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM;YAI7B,WAAW;YAcX,UAAU;CAYzB"}
|
|
@@ -509,13 +509,11 @@ import { ReqResp } from '../reqresp/reqresp.js';
|
|
|
509
509
|
if (outcome.allPassed) {
|
|
510
510
|
return true;
|
|
511
511
|
}
|
|
512
|
-
const { name
|
|
512
|
+
const { name } = outcome.failure;
|
|
513
|
+
let { severity } = outcome.failure;
|
|
513
514
|
// Double spend validator has a special case handler
|
|
514
515
|
if (name === 'doubleSpendValidator') {
|
|
515
|
-
|
|
516
|
-
if (isValid) {
|
|
517
|
-
return true;
|
|
518
|
-
}
|
|
516
|
+
severity = await this.handleDoubleSpendFailure(tx, blockNumber);
|
|
519
517
|
}
|
|
520
518
|
this.peerManager.penalizePeer(peerId, severity);
|
|
521
519
|
return false;
|
|
@@ -597,24 +595,23 @@ import { ReqResp } from '../reqresp/reqresp.js';
|
|
|
597
595
|
* @param tx - The tx that failed the double spend validator.
|
|
598
596
|
* @param blockNumber - The block number of the tx.
|
|
599
597
|
* @param peerId - The peer ID of the peer that sent the tx.
|
|
600
|
-
* @returns
|
|
601
|
-
*/ async handleDoubleSpendFailure(tx, blockNumber
|
|
602
|
-
if (blockNumber <= this.config.
|
|
603
|
-
return
|
|
598
|
+
* @returns Severity
|
|
599
|
+
*/ async handleDoubleSpendFailure(tx, blockNumber) {
|
|
600
|
+
if (blockNumber <= this.config.doubleSpendSeverePeerPenaltyWindow) {
|
|
601
|
+
return PeerErrorSeverity.HighToleranceError;
|
|
604
602
|
}
|
|
605
603
|
const snapshotValidator = new DoubleSpendTxValidator({
|
|
606
604
|
nullifiersExist: async (nullifiers)=>{
|
|
607
|
-
const merkleTree = this.worldStateSynchronizer.getSnapshot(blockNumber - this.config.
|
|
605
|
+
const merkleTree = this.worldStateSynchronizer.getSnapshot(blockNumber - this.config.doubleSpendSeverePeerPenaltyWindow);
|
|
608
606
|
const indices = await merkleTree.findLeafIndices(MerkleTreeId.NULLIFIER_TREE, nullifiers);
|
|
609
607
|
return indices.map((index)=>index !== undefined);
|
|
610
608
|
}
|
|
611
609
|
});
|
|
612
610
|
const validSnapshot = await snapshotValidator.validateTx(tx);
|
|
613
611
|
if (validSnapshot.result !== 'valid') {
|
|
614
|
-
|
|
615
|
-
return false;
|
|
612
|
+
return PeerErrorSeverity.LowToleranceError;
|
|
616
613
|
}
|
|
617
|
-
return
|
|
614
|
+
return PeerErrorSeverity.HighToleranceError;
|
|
618
615
|
}
|
|
619
616
|
/**
|
|
620
617
|
* Validate an attestation.
|
|
@@ -1,98 +1,13 @@
|
|
|
1
|
-
import { EthAddress } from '@aztec/foundation/eth-address';
|
|
2
1
|
import { createLogger } from '@aztec/foundation/log';
|
|
3
2
|
import { sleep } from '@aztec/foundation/sleep';
|
|
4
3
|
import { ClientIvcProof } from '@aztec/stdlib/proofs';
|
|
5
4
|
import { mockTx } from '@aztec/stdlib/testing';
|
|
6
|
-
import
|
|
5
|
+
import assert from 'assert';
|
|
7
6
|
import path from 'path';
|
|
8
7
|
import { fileURLToPath } from 'url';
|
|
9
|
-
import {
|
|
10
|
-
import { generatePeerIdPrivateKeys } from '../test-helpers/generate-peer-id-private-keys.js';
|
|
11
|
-
import { getPorts } from '../test-helpers/get-ports.js';
|
|
12
|
-
import { makeEnrs } from '../test-helpers/make-enrs.js';
|
|
8
|
+
import { WorkerClientManager, testChainConfig } from './worker_client_manager.js';
|
|
13
9
|
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
14
|
-
const workerPath = path.join(__dirname, '../../dest/testbench/p2p_client_testbench_worker.js');
|
|
15
10
|
const logger = createLogger('testbench');
|
|
16
|
-
let processes = [];
|
|
17
|
-
const testChainConfig = {
|
|
18
|
-
l1ChainId: 31337,
|
|
19
|
-
version: 1,
|
|
20
|
-
l1Contracts: {
|
|
21
|
-
rollupAddress: EthAddress.random()
|
|
22
|
-
}
|
|
23
|
-
};
|
|
24
|
-
/**
|
|
25
|
-
* Cleanup function to kill all child processes
|
|
26
|
-
*/ async function cleanup() {
|
|
27
|
-
logger.info('Cleaning up processes...');
|
|
28
|
-
await Promise.all(processes.map((proc)=>new Promise((resolve)=>{
|
|
29
|
-
proc.once('exit', ()=>resolve());
|
|
30
|
-
proc.send({
|
|
31
|
-
type: 'STOP'
|
|
32
|
-
});
|
|
33
|
-
})));
|
|
34
|
-
process.exit(0);
|
|
35
|
-
}
|
|
36
|
-
// Handle cleanup on process termination
|
|
37
|
-
process.on('SIGINT', ()=>void cleanup());
|
|
38
|
-
process.on('SIGTERM', ()=>void cleanup());
|
|
39
|
-
/**
|
|
40
|
-
* Creates a number of worker clients in separate processes
|
|
41
|
-
* All are configured to connect to each other and overrided with the test specific config
|
|
42
|
-
*
|
|
43
|
-
* @param numberOfClients - The number of clients to create
|
|
44
|
-
* @param p2pConfig - The P2P config to use for the clients
|
|
45
|
-
* @returns The ENRs of the created clients
|
|
46
|
-
*/ async function makeWorkerClients(numberOfClients, p2pConfig) {
|
|
47
|
-
const peerIdPrivateKeys = generatePeerIdPrivateKeys(numberOfClients);
|
|
48
|
-
const ports = await getPorts(numberOfClients);
|
|
49
|
-
const peerEnrs = await makeEnrs(peerIdPrivateKeys, ports, testChainConfig);
|
|
50
|
-
processes = [];
|
|
51
|
-
const readySignals = [];
|
|
52
|
-
for(let i = 0; i < numberOfClients; i++){
|
|
53
|
-
logger.info(`Creating client ${i}`);
|
|
54
|
-
const addr = `127.0.0.1:${ports[i]}`;
|
|
55
|
-
const listenAddr = `0.0.0.0:${ports[i]}`;
|
|
56
|
-
// Maximum seed with 10 other peers to allow peer discovery to connect them at a smoother rate
|
|
57
|
-
const otherNodes = peerEnrs.filter((_, ind)=>ind < Math.min(i, 10));
|
|
58
|
-
const config = {
|
|
59
|
-
...getP2PDefaultConfig(),
|
|
60
|
-
p2pEnabled: true,
|
|
61
|
-
peerIdPrivateKey: peerIdPrivateKeys[i],
|
|
62
|
-
tcpListenAddress: listenAddr,
|
|
63
|
-
udpListenAddress: listenAddr,
|
|
64
|
-
tcpAnnounceAddress: addr,
|
|
65
|
-
udpAnnounceAddress: addr,
|
|
66
|
-
bootstrapNodes: [
|
|
67
|
-
...otherNodes
|
|
68
|
-
],
|
|
69
|
-
...p2pConfig
|
|
70
|
-
};
|
|
71
|
-
const childProcess = fork(workerPath);
|
|
72
|
-
childProcess.send({
|
|
73
|
-
type: 'START',
|
|
74
|
-
config,
|
|
75
|
-
clientIndex: i
|
|
76
|
-
});
|
|
77
|
-
// Wait for ready signal
|
|
78
|
-
readySignals.push(new Promise((resolve, reject)=>{
|
|
79
|
-
childProcess.once('message', (msg)=>{
|
|
80
|
-
if (msg.type === 'READY') {
|
|
81
|
-
resolve(undefined);
|
|
82
|
-
}
|
|
83
|
-
if (msg.type === 'ERROR') {
|
|
84
|
-
reject(new Error(msg.error));
|
|
85
|
-
}
|
|
86
|
-
});
|
|
87
|
-
}));
|
|
88
|
-
processes.push(childProcess);
|
|
89
|
-
}
|
|
90
|
-
// Wait for peers to all connect with each other
|
|
91
|
-
await sleep(4000);
|
|
92
|
-
// Wait for all peers to be booted up
|
|
93
|
-
await Promise.all(readySignals);
|
|
94
|
-
return peerEnrs;
|
|
95
|
-
}
|
|
96
11
|
async function main() {
|
|
97
12
|
try {
|
|
98
13
|
// Read configuration file name from command line args
|
|
@@ -112,7 +27,8 @@ async function main() {
|
|
|
112
27
|
};
|
|
113
28
|
const numberOfClients = config.default.numberOfClients;
|
|
114
29
|
// Setup clients in separate processes
|
|
115
|
-
|
|
30
|
+
const workerClientManager = new WorkerClientManager(logger, testConfig);
|
|
31
|
+
await workerClientManager.makeWorkerClients(numberOfClients);
|
|
116
32
|
// wait a bit longer for all peers to be ready
|
|
117
33
|
await sleep(5000);
|
|
118
34
|
logger.info('Workers Ready');
|
|
@@ -120,7 +36,7 @@ async function main() {
|
|
|
120
36
|
const tx = await mockTx(1, {
|
|
121
37
|
clientIvcProof: ClientIvcProof.random()
|
|
122
38
|
});
|
|
123
|
-
processes[0].send({
|
|
39
|
+
workerClientManager.processes[0].send({
|
|
124
40
|
type: 'SEND_TX',
|
|
125
41
|
tx: tx.toBuffer()
|
|
126
42
|
});
|
|
@@ -128,14 +44,18 @@ async function main() {
|
|
|
128
44
|
// Give time for message propagation
|
|
129
45
|
await sleep(30000);
|
|
130
46
|
logger.info('Checking message propagation results');
|
|
131
|
-
|
|
47
|
+
// Check message propagation results
|
|
48
|
+
const numberOfClientsThatReceivedMessage = workerClientManager.numberOfClientsThatReceivedMessage();
|
|
49
|
+
logger.info(`Number of clients that received message: ${numberOfClientsThatReceivedMessage}`);
|
|
50
|
+
assert(numberOfClientsThatReceivedMessage === numberOfClients - 1);
|
|
51
|
+
logger.info('Test passed, cleaning up');
|
|
52
|
+
// cleanup
|
|
53
|
+
await workerClientManager.cleanup();
|
|
132
54
|
} catch (error) {
|
|
133
55
|
logger.error('Test failed with error:', error);
|
|
134
|
-
await cleanup();
|
|
135
56
|
process.exit(1);
|
|
136
57
|
}
|
|
137
58
|
}
|
|
138
59
|
main().catch((error)=>{
|
|
139
60
|
logger.error('Unhandled error:', error);
|
|
140
|
-
cleanup().catch(()=>process.exit(1));
|
|
141
61
|
});
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
/// <reference types="node" resolution-mode="require"/>
|
|
2
|
+
import type { Logger } from '@aztec/foundation/log';
|
|
3
|
+
import type { ChainConfig } from '@aztec/stdlib/config';
|
|
4
|
+
import { type ChildProcess } from 'child_process';
|
|
5
|
+
import { type P2PConfig } from '../config.js';
|
|
6
|
+
declare const testChainConfig: ChainConfig;
|
|
7
|
+
declare class WorkerClientManager {
|
|
8
|
+
processes: ChildProcess[];
|
|
9
|
+
peerIdPrivateKeys: string[];
|
|
10
|
+
peerEnrs: string[];
|
|
11
|
+
ports: number[];
|
|
12
|
+
private p2pConfig;
|
|
13
|
+
private logger;
|
|
14
|
+
private messageReceivedByClient;
|
|
15
|
+
constructor(logger: Logger, p2pConfig: Partial<P2PConfig>);
|
|
16
|
+
destroy(): void;
|
|
17
|
+
/**
|
|
18
|
+
* Creates address strings from a port
|
|
19
|
+
*/
|
|
20
|
+
private getAddresses;
|
|
21
|
+
/**
|
|
22
|
+
* Creates a client configuration object
|
|
23
|
+
*/
|
|
24
|
+
private createClientConfig;
|
|
25
|
+
/**
|
|
26
|
+
* Spawns a worker process and returns a promise that resolves when the worker is ready
|
|
27
|
+
*/
|
|
28
|
+
private spawnWorkerProcess;
|
|
29
|
+
/**
|
|
30
|
+
* Creates a number of worker clients in separate processes
|
|
31
|
+
* All are configured to connect to each other and overrided with the test specific config
|
|
32
|
+
*
|
|
33
|
+
* @param numberOfClients - The number of clients to create
|
|
34
|
+
* @returns The ENRs of the created clients
|
|
35
|
+
*/
|
|
36
|
+
makeWorkerClients(numberOfClients: number): Promise<string[]>;
|
|
37
|
+
purgeMessageReceivedByClient(): void;
|
|
38
|
+
numberOfClientsThatReceivedMessage(): number;
|
|
39
|
+
/**
|
|
40
|
+
* Changes the port for a specific client
|
|
41
|
+
*
|
|
42
|
+
* @param clientIndex - The index of the client to change port for
|
|
43
|
+
* @param newPort - The new port to use
|
|
44
|
+
*/
|
|
45
|
+
changePort(clientIndex: number, newPort: number): Promise<void>;
|
|
46
|
+
/**
|
|
47
|
+
* Terminate a single process with timeout and force kill if needed
|
|
48
|
+
*/
|
|
49
|
+
private terminateProcess;
|
|
50
|
+
/**
|
|
51
|
+
* Cleans up all worker processes with timeout and force kill if needed
|
|
52
|
+
*/
|
|
53
|
+
cleanup(): Promise<void>;
|
|
54
|
+
}
|
|
55
|
+
export { WorkerClientManager, testChainConfig };
|
|
56
|
+
//# sourceMappingURL=worker_client_manager.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"worker_client_manager.d.ts","sourceRoot":"","sources":["../../src/testbench/worker_client_manager.ts"],"names":[],"mappings":";AACA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,uBAAuB,CAAC;AAEpD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAC;AAExD,OAAO,EAAE,KAAK,YAAY,EAAQ,MAAM,eAAe,CAAC;AAIxD,OAAO,EAAE,KAAK,SAAS,EAAuB,MAAM,cAAc,CAAC;AAQnE,QAAA,MAAM,eAAe,EAAE,WAMtB,CAAC;AAEF,cAAM,mBAAmB;IAChB,SAAS,EAAE,YAAY,EAAE,CAAM;IAC/B,iBAAiB,EAAE,MAAM,EAAE,CAAM;IACjC,QAAQ,EAAE,MAAM,EAAE,CAAM;IACxB,KAAK,EAAE,MAAM,EAAE,CAAM;IAC5B,OAAO,CAAC,SAAS,CAAqB;IACtC,OAAO,CAAC,MAAM,CAAS;IACvB,OAAO,CAAC,uBAAuB,CAAgB;gBAEnC,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,OAAO,CAAC,SAAS,CAAC;IAKzD,OAAO;IAOP;;OAEG;IACH,OAAO,CAAC,YAAY;IAOpB;;OAEG;IACH,OAAO,CAAC,kBAAkB;IAgB1B;;OAEG;IACH,OAAO,CAAC,kBAAkB;IAoD1B;;;;;;OAMG;IACG,iBAAiB,CAAC,eAAe,EAAE,MAAM;IA2C/C,4BAA4B;IAI5B,kCAAkC;IAIlC;;;;;OAKG;IACG,UAAU,CAAC,WAAW,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM;IA4CrD;;OAEG;IACH,OAAO,CAAC,gBAAgB;IAsCxB;;OAEG;IACG,OAAO;CAiCd;AAED,OAAO,EAAE,mBAAmB,EAAE,eAAe,EAAE,CAAC"}
|
|
@@ -0,0 +1,266 @@
|
|
|
1
|
+
import { EthAddress } from '@aztec/foundation/eth-address';
|
|
2
|
+
import { sleep } from '@aztec/foundation/sleep';
|
|
3
|
+
import { fork } from 'child_process';
|
|
4
|
+
import path from 'path';
|
|
5
|
+
import { fileURLToPath } from 'url';
|
|
6
|
+
import { getP2PDefaultConfig } from '../config.js';
|
|
7
|
+
import { generatePeerIdPrivateKeys } from '../test-helpers/generate-peer-id-private-keys.js';
|
|
8
|
+
import { getPorts } from '../test-helpers/get-ports.js';
|
|
9
|
+
import { makeEnr, makeEnrs } from '../test-helpers/make-enrs.js';
|
|
10
|
+
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
11
|
+
const workerPath = path.join(__dirname, '../../dest/testbench/p2p_client_testbench_worker.js');
|
|
12
|
+
const testChainConfig = {
|
|
13
|
+
l1ChainId: 31337,
|
|
14
|
+
version: 1,
|
|
15
|
+
l1Contracts: {
|
|
16
|
+
rollupAddress: EthAddress.random()
|
|
17
|
+
}
|
|
18
|
+
};
|
|
19
|
+
class WorkerClientManager {
|
|
20
|
+
processes = [];
|
|
21
|
+
peerIdPrivateKeys = [];
|
|
22
|
+
peerEnrs = [];
|
|
23
|
+
ports = [];
|
|
24
|
+
p2pConfig;
|
|
25
|
+
logger;
|
|
26
|
+
messageReceivedByClient = [];
|
|
27
|
+
constructor(logger, p2pConfig){
|
|
28
|
+
this.logger = logger;
|
|
29
|
+
this.p2pConfig = p2pConfig;
|
|
30
|
+
}
|
|
31
|
+
destroy() {
|
|
32
|
+
this.cleanup().catch((error)=>{
|
|
33
|
+
this.logger.error('Failed to cleanup worker client manager', error);
|
|
34
|
+
process.exit(1);
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Creates address strings from a port
|
|
39
|
+
*/ getAddresses(port) {
|
|
40
|
+
return {
|
|
41
|
+
addr: `127.0.0.1:${port}`,
|
|
42
|
+
listenAddr: `0.0.0.0:${port}`
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Creates a client configuration object
|
|
47
|
+
*/ createClientConfig(clientIndex, port, otherNodes) {
|
|
48
|
+
const { addr, listenAddr } = this.getAddresses(port);
|
|
49
|
+
return {
|
|
50
|
+
...getP2PDefaultConfig(),
|
|
51
|
+
p2pEnabled: true,
|
|
52
|
+
peerIdPrivateKey: this.peerIdPrivateKeys[clientIndex],
|
|
53
|
+
tcpListenAddress: listenAddr,
|
|
54
|
+
udpListenAddress: listenAddr,
|
|
55
|
+
tcpAnnounceAddress: addr,
|
|
56
|
+
udpAnnounceAddress: addr,
|
|
57
|
+
bootstrapNodes: [
|
|
58
|
+
...otherNodes
|
|
59
|
+
],
|
|
60
|
+
...this.p2pConfig
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Spawns a worker process and returns a promise that resolves when the worker is ready
|
|
65
|
+
*/ spawnWorkerProcess(config, clientIndex) {
|
|
66
|
+
const childProcess = fork(workerPath);
|
|
67
|
+
childProcess.send({
|
|
68
|
+
type: 'START',
|
|
69
|
+
config,
|
|
70
|
+
clientIndex
|
|
71
|
+
});
|
|
72
|
+
// Handle unexpected child process exit
|
|
73
|
+
childProcess.on('exit', (code, signal)=>{
|
|
74
|
+
if (code !== 0) {
|
|
75
|
+
this.logger.warn(`Worker ${clientIndex} exited unexpectedly with code ${code} and signal ${signal}`);
|
|
76
|
+
}
|
|
77
|
+
});
|
|
78
|
+
childProcess.on('message', (msg)=>{
|
|
79
|
+
if (msg.type === 'GOSSIP_RECEIVED') {
|
|
80
|
+
this.messageReceivedByClient[clientIndex] = msg.count;
|
|
81
|
+
}
|
|
82
|
+
});
|
|
83
|
+
// Create ready signal promise
|
|
84
|
+
const readySignal = new Promise((resolve, reject)=>{
|
|
85
|
+
// Set a timeout to avoid hanging indefinitely
|
|
86
|
+
const timeout = setTimeout(()=>{
|
|
87
|
+
reject(new Error(`Timeout waiting for worker ${clientIndex} to be ready`));
|
|
88
|
+
}, 30000); // 30 second timeout
|
|
89
|
+
childProcess.once('message', (msg)=>{
|
|
90
|
+
clearTimeout(timeout);
|
|
91
|
+
if (msg.type === 'READY') {
|
|
92
|
+
resolve();
|
|
93
|
+
}
|
|
94
|
+
// For future use
|
|
95
|
+
if (msg.type === 'ERROR') {
|
|
96
|
+
reject(new Error(msg.error));
|
|
97
|
+
}
|
|
98
|
+
});
|
|
99
|
+
// Also resolve/reject if process exits before sending message
|
|
100
|
+
childProcess.once('exit', (code)=>{
|
|
101
|
+
clearTimeout(timeout);
|
|
102
|
+
if (code === 0) {
|
|
103
|
+
resolve();
|
|
104
|
+
} else {
|
|
105
|
+
reject(new Error(`Worker ${clientIndex} exited with code ${code} before becoming ready`));
|
|
106
|
+
}
|
|
107
|
+
});
|
|
108
|
+
});
|
|
109
|
+
return [
|
|
110
|
+
childProcess,
|
|
111
|
+
readySignal
|
|
112
|
+
];
|
|
113
|
+
}
|
|
114
|
+
/**
|
|
115
|
+
* Creates a number of worker clients in separate processes
|
|
116
|
+
* All are configured to connect to each other and overrided with the test specific config
|
|
117
|
+
*
|
|
118
|
+
* @param numberOfClients - The number of clients to create
|
|
119
|
+
* @returns The ENRs of the created clients
|
|
120
|
+
*/ async makeWorkerClients(numberOfClients) {
|
|
121
|
+
try {
|
|
122
|
+
this.messageReceivedByClient = new Array(numberOfClients).fill(0);
|
|
123
|
+
this.peerIdPrivateKeys = generatePeerIdPrivateKeys(numberOfClients);
|
|
124
|
+
this.ports = await getPorts(numberOfClients);
|
|
125
|
+
this.peerEnrs = await makeEnrs(this.peerIdPrivateKeys, this.ports, testChainConfig);
|
|
126
|
+
this.processes = [];
|
|
127
|
+
const readySignals = [];
|
|
128
|
+
for(let i = 0; i < numberOfClients; i++){
|
|
129
|
+
this.logger.info(`Creating client ${i}`);
|
|
130
|
+
// Maximum seed with 10 other peers to allow peer discovery to connect them at a smoother rate
|
|
131
|
+
const otherNodes = this.peerEnrs.filter((_, ind)=>ind < Math.min(i, 10));
|
|
132
|
+
const config = this.createClientConfig(i, this.ports[i], otherNodes);
|
|
133
|
+
const [childProcess, readySignal] = this.spawnWorkerProcess(config, i);
|
|
134
|
+
readySignals.push(readySignal);
|
|
135
|
+
this.processes.push(childProcess);
|
|
136
|
+
}
|
|
137
|
+
// Wait for peers to all connect with each other
|
|
138
|
+
await sleep(10000);
|
|
139
|
+
// Wait for all peers to be booted up with timeout
|
|
140
|
+
await Promise.race([
|
|
141
|
+
Promise.all(readySignals),
|
|
142
|
+
new Promise((_, reject)=>setTimeout(()=>reject(new Error('Timeout waiting for all workers to be ready')), 30000))
|
|
143
|
+
]);
|
|
144
|
+
return this.peerEnrs;
|
|
145
|
+
} catch (error) {
|
|
146
|
+
// Clean up any processes that were created if there's an error
|
|
147
|
+
this.logger.error('Error during makeWorkerClients:', error);
|
|
148
|
+
await this.cleanup();
|
|
149
|
+
throw error;
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
purgeMessageReceivedByClient() {
|
|
153
|
+
this.messageReceivedByClient = new Array(this.processes.length).fill(0);
|
|
154
|
+
}
|
|
155
|
+
numberOfClientsThatReceivedMessage() {
|
|
156
|
+
return this.messageReceivedByClient.filter((count)=>count > 0).length;
|
|
157
|
+
}
|
|
158
|
+
/**
|
|
159
|
+
* Changes the port for a specific client
|
|
160
|
+
*
|
|
161
|
+
* @param clientIndex - The index of the client to change port for
|
|
162
|
+
* @param newPort - The new port to use
|
|
163
|
+
*/ async changePort(clientIndex, newPort) {
|
|
164
|
+
try {
|
|
165
|
+
if (clientIndex < 0 || clientIndex >= this.processes.length) {
|
|
166
|
+
throw new Error(`Invalid client index: ${clientIndex}`);
|
|
167
|
+
}
|
|
168
|
+
this.processes[clientIndex].send({
|
|
169
|
+
type: 'STOP'
|
|
170
|
+
});
|
|
171
|
+
// Wait for the process to be ready with a timeout
|
|
172
|
+
await sleep(10000);
|
|
173
|
+
this.logger.info(`Changing port for client ${clientIndex} to ${newPort}`);
|
|
174
|
+
// Update the port in the ports array
|
|
175
|
+
this.ports[clientIndex] = newPort;
|
|
176
|
+
// Update the port in the peerEnrs array
|
|
177
|
+
this.peerEnrs[clientIndex] = await makeEnr(this.peerIdPrivateKeys[clientIndex], newPort, testChainConfig);
|
|
178
|
+
// Maximum seed with 10 other peers to allow peer discovery to connect them at a smoother rate
|
|
179
|
+
const otherNodes = this.peerEnrs.filter((_, ind)=>ind !== clientIndex && ind < Math.min(this.peerEnrs.length, 10));
|
|
180
|
+
const config = this.createClientConfig(clientIndex, newPort, otherNodes);
|
|
181
|
+
const [childProcess, readySignal] = this.spawnWorkerProcess(config, clientIndex);
|
|
182
|
+
this.processes[clientIndex] = childProcess;
|
|
183
|
+
// Wait for the process to be ready with a timeout
|
|
184
|
+
await Promise.race([
|
|
185
|
+
readySignal,
|
|
186
|
+
new Promise((_, reject)=>setTimeout(()=>reject(new Error(`Timeout waiting for client ${clientIndex} to be ready`)), 30000))
|
|
187
|
+
]);
|
|
188
|
+
} catch (error) {
|
|
189
|
+
this.logger.error(`Error during changePort for client ${clientIndex}:`, error);
|
|
190
|
+
// Only clean up the specific process that had an issue
|
|
191
|
+
await this.terminateProcess(this.processes[clientIndex], clientIndex);
|
|
192
|
+
throw error;
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
/**
|
|
196
|
+
* Terminate a single process with timeout and force kill if needed
|
|
197
|
+
*/ terminateProcess(process1, index) {
|
|
198
|
+
if (!process1 || process1.killed) {
|
|
199
|
+
return Promise.resolve();
|
|
200
|
+
}
|
|
201
|
+
return new Promise((resolve)=>{
|
|
202
|
+
// Set a timeout for the graceful exit
|
|
203
|
+
const forceKillTimeout = setTimeout(()=>{
|
|
204
|
+
this.logger.warn(`Process ${index} didn't exit gracefully, force killing...`);
|
|
205
|
+
try {
|
|
206
|
+
process1.kill('SIGKILL'); // Force kill
|
|
207
|
+
} catch (e) {
|
|
208
|
+
this.logger.error(`Error force killing process ${index}:`, e);
|
|
209
|
+
}
|
|
210
|
+
}, 10000); // 10 second timeout for graceful exit
|
|
211
|
+
// Listen for process exit
|
|
212
|
+
process1.once('exit', ()=>{
|
|
213
|
+
clearTimeout(forceKillTimeout);
|
|
214
|
+
resolve();
|
|
215
|
+
});
|
|
216
|
+
// Try to gracefully stop the process
|
|
217
|
+
try {
|
|
218
|
+
process1.send({
|
|
219
|
+
type: 'STOP'
|
|
220
|
+
});
|
|
221
|
+
} catch (e) {
|
|
222
|
+
// If sending the message fails, force kill immediately
|
|
223
|
+
clearTimeout(forceKillTimeout);
|
|
224
|
+
try {
|
|
225
|
+
process1.kill('SIGKILL');
|
|
226
|
+
} catch (killError) {
|
|
227
|
+
this.logger.error(`Error force killing process ${index}:`, killError);
|
|
228
|
+
}
|
|
229
|
+
resolve();
|
|
230
|
+
}
|
|
231
|
+
});
|
|
232
|
+
}
|
|
233
|
+
/**
|
|
234
|
+
* Cleans up all worker processes with timeout and force kill if needed
|
|
235
|
+
*/ async cleanup() {
|
|
236
|
+
this.logger.info(`Cleaning up ${this.processes.length} worker processes`);
|
|
237
|
+
// Create array of promises for each process termination
|
|
238
|
+
const terminationPromises = this.processes.map((process1, index)=>this.terminateProcess(process1, index));
|
|
239
|
+
// Wait for all processes to terminate with a timeout
|
|
240
|
+
try {
|
|
241
|
+
await Promise.race([
|
|
242
|
+
Promise.all(terminationPromises),
|
|
243
|
+
new Promise((resolve)=>{
|
|
244
|
+
setTimeout(()=>{
|
|
245
|
+
this.logger.warn('Some processes did not terminate in time, force killing all remaining...');
|
|
246
|
+
this.processes.forEach((p)=>{
|
|
247
|
+
try {
|
|
248
|
+
if (!p.killed) {
|
|
249
|
+
p.kill('SIGKILL');
|
|
250
|
+
}
|
|
251
|
+
} catch (e) {
|
|
252
|
+
// Ignore errors when force killing
|
|
253
|
+
}
|
|
254
|
+
});
|
|
255
|
+
resolve();
|
|
256
|
+
}, 30000); // 30 second timeout for all processes
|
|
257
|
+
})
|
|
258
|
+
]);
|
|
259
|
+
} catch (error) {
|
|
260
|
+
this.logger.error('Error during cleanup:', error);
|
|
261
|
+
}
|
|
262
|
+
this.processes = [];
|
|
263
|
+
this.logger.info('All worker processes cleaned up');
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
export { WorkerClientManager, testChainConfig };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@aztec/p2p",
|
|
3
|
-
"version": "0.77.
|
|
3
|
+
"version": "0.77.1",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"exports": {
|
|
6
6
|
".": "./dest/index.js",
|
|
@@ -65,14 +65,14 @@
|
|
|
65
65
|
]
|
|
66
66
|
},
|
|
67
67
|
"dependencies": {
|
|
68
|
-
"@aztec/constants": "0.77.
|
|
69
|
-
"@aztec/epoch-cache": "0.77.
|
|
70
|
-
"@aztec/foundation": "0.77.
|
|
71
|
-
"@aztec/kv-store": "0.77.
|
|
72
|
-
"@aztec/noir-protocol-circuits-types": "0.77.
|
|
73
|
-
"@aztec/protocol-contracts": "0.77.
|
|
74
|
-
"@aztec/stdlib": "0.77.
|
|
75
|
-
"@aztec/telemetry-client": "0.77.
|
|
68
|
+
"@aztec/constants": "0.77.1",
|
|
69
|
+
"@aztec/epoch-cache": "0.77.1",
|
|
70
|
+
"@aztec/foundation": "0.77.1",
|
|
71
|
+
"@aztec/kv-store": "0.77.1",
|
|
72
|
+
"@aztec/noir-protocol-circuits-types": "0.77.1",
|
|
73
|
+
"@aztec/protocol-contracts": "0.77.1",
|
|
74
|
+
"@aztec/stdlib": "0.77.1",
|
|
75
|
+
"@aztec/telemetry-client": "0.77.1",
|
|
76
76
|
"@chainsafe/discv5": "9.0.0",
|
|
77
77
|
"@chainsafe/enr": "3.0.0",
|
|
78
78
|
"@chainsafe/libp2p-gossipsub": "13.0.0",
|
|
@@ -101,7 +101,7 @@
|
|
|
101
101
|
"xxhash-wasm": "^1.1.0"
|
|
102
102
|
},
|
|
103
103
|
"devDependencies": {
|
|
104
|
-
"@aztec/archiver": "0.77.
|
|
104
|
+
"@aztec/archiver": "0.77.1",
|
|
105
105
|
"@jest/globals": "^29.5.0",
|
|
106
106
|
"@types/jest": "^29.5.0",
|
|
107
107
|
"@types/node": "^18.14.6",
|
package/src/client/factory.ts
CHANGED
|
@@ -38,8 +38,8 @@ export const createP2PClient = async <T extends P2PClientType>(
|
|
|
38
38
|
) => {
|
|
39
39
|
let config = { ..._config };
|
|
40
40
|
const logger = deps.logger ?? createLogger('p2p');
|
|
41
|
-
const store = deps.store ?? (await createStore('p2p', config, createLogger('p2p:lmdb-v2')));
|
|
42
|
-
const archive = await createStore('p2p-archive', config, createLogger('p2p-archive:lmdb-v2'));
|
|
41
|
+
const store = deps.store ?? (await createStore('p2p', 1, config, createLogger('p2p:lmdb-v2')));
|
|
42
|
+
const archive = await createStore('p2p-archive', 1, config, createLogger('p2p-archive:lmdb-v2'));
|
|
43
43
|
|
|
44
44
|
const mempools: MemPools<T> = {
|
|
45
45
|
txPool: deps.txPool ?? new AztecKVTxPool(store, archive, telemetry, config.archivedTxLimit),
|
package/src/config.ts
CHANGED
|
@@ -81,11 +81,6 @@ export interface P2PConfig extends P2PReqRespConfig, ChainConfig {
|
|
|
81
81
|
/** Whether to consider any configured bootnodes as full peers, e.g. for transaction gossiping */
|
|
82
82
|
bootstrapNodesAsFullPeers: boolean;
|
|
83
83
|
|
|
84
|
-
/**
|
|
85
|
-
* Protocol identifier for transaction gossiping.
|
|
86
|
-
*/
|
|
87
|
-
transactionProtocol: string;
|
|
88
|
-
|
|
89
84
|
/**
|
|
90
85
|
* The maximum number of peers (a peer count above this will cause the node to refuse connection attempts)
|
|
91
86
|
*/
|
|
@@ -145,7 +140,7 @@ export interface P2PConfig extends P2PReqRespConfig, ChainConfig {
|
|
|
145
140
|
/**
|
|
146
141
|
* The 'age' (in # of L2 blocks) of a processed tx after which we heavily penalize a peer for re-sending it.
|
|
147
142
|
*/
|
|
148
|
-
|
|
143
|
+
doubleSpendSeverePeerPenaltyWindow: number;
|
|
149
144
|
|
|
150
145
|
/**
|
|
151
146
|
* The weight of the tx topic for the gossipsub protocol. This determines how much the score for this specific topic contributes to the overall peer score.
|
|
@@ -236,11 +231,6 @@ export const p2pConfigMappings: ConfigMappingsType<P2PConfig> = {
|
|
|
236
231
|
description: 'Whether to consider our configured bootnodes as full peers',
|
|
237
232
|
...booleanConfigHelper(false),
|
|
238
233
|
},
|
|
239
|
-
transactionProtocol: {
|
|
240
|
-
env: 'P2P_TX_PROTOCOL',
|
|
241
|
-
description: 'Protocol identifier for transaction gossiping.',
|
|
242
|
-
defaultValue: '/aztec/0.1.0',
|
|
243
|
-
},
|
|
244
234
|
maxPeerCount: {
|
|
245
235
|
env: 'P2P_MAX_PEERS',
|
|
246
236
|
description: 'The maximum number of peers to connect to.',
|
|
@@ -286,7 +276,7 @@ export const p2pConfigMappings: ConfigMappingsType<P2PConfig> = {
|
|
|
286
276
|
gossipsubDLazy: {
|
|
287
277
|
env: 'P2P_GOSSIPSUB_DLAZY',
|
|
288
278
|
description: 'The Dlazy parameter for the gossipsub protocol.',
|
|
289
|
-
...numberConfigHelper(
|
|
279
|
+
...numberConfigHelper(8),
|
|
290
280
|
},
|
|
291
281
|
gossipsubFloodPublish: {
|
|
292
282
|
env: 'P2P_GOSSIPSUB_FLOOD_PUBLISH',
|
|
@@ -325,8 +315,8 @@ export const p2pConfigMappings: ConfigMappingsType<P2PConfig> = {
|
|
|
325
315
|
'The values for the peer scoring system. Passed as a comma separated list of values in order: low, mid, high tolerance errors.',
|
|
326
316
|
defaultValue: [2, 10, 50],
|
|
327
317
|
},
|
|
328
|
-
|
|
329
|
-
env: '
|
|
318
|
+
doubleSpendSeverePeerPenaltyWindow: {
|
|
319
|
+
env: 'P2P_DOUBLE_SPEND_SEVERE_PEER_PENALTY_WINDOW',
|
|
330
320
|
description: 'The "age" (in L2 blocks) of a tx after which we heavily penalize a peer for sending it.',
|
|
331
321
|
...numberConfigHelper(30),
|
|
332
322
|
},
|
|
@@ -679,15 +679,12 @@ export class LibP2PService<T extends P2PClientType> extends WithTracer implement
|
|
|
679
679
|
if (outcome.allPassed) {
|
|
680
680
|
return true;
|
|
681
681
|
}
|
|
682
|
-
|
|
683
|
-
|
|
682
|
+
const { name } = outcome.failure;
|
|
683
|
+
let { severity } = outcome.failure;
|
|
684
684
|
|
|
685
685
|
// Double spend validator has a special case handler
|
|
686
686
|
if (name === 'doubleSpendValidator') {
|
|
687
|
-
|
|
688
|
-
if (isValid) {
|
|
689
|
-
return true;
|
|
690
|
-
}
|
|
687
|
+
severity = await this.handleDoubleSpendFailure(tx, blockNumber);
|
|
691
688
|
}
|
|
692
689
|
|
|
693
690
|
this.peerManager.penalizePeer(peerId, severity);
|
|
@@ -775,17 +772,17 @@ export class LibP2PService<T extends P2PClientType> extends WithTracer implement
|
|
|
775
772
|
* @param tx - The tx that failed the double spend validator.
|
|
776
773
|
* @param blockNumber - The block number of the tx.
|
|
777
774
|
* @param peerId - The peer ID of the peer that sent the tx.
|
|
778
|
-
* @returns
|
|
775
|
+
* @returns Severity
|
|
779
776
|
*/
|
|
780
|
-
private async handleDoubleSpendFailure(tx: Tx, blockNumber: number
|
|
781
|
-
if (blockNumber <= this.config.
|
|
782
|
-
return
|
|
777
|
+
private async handleDoubleSpendFailure(tx: Tx, blockNumber: number): Promise<PeerErrorSeverity> {
|
|
778
|
+
if (blockNumber <= this.config.doubleSpendSeverePeerPenaltyWindow) {
|
|
779
|
+
return PeerErrorSeverity.HighToleranceError;
|
|
783
780
|
}
|
|
784
781
|
|
|
785
782
|
const snapshotValidator = new DoubleSpendTxValidator({
|
|
786
783
|
nullifiersExist: async (nullifiers: Buffer[]) => {
|
|
787
784
|
const merkleTree = this.worldStateSynchronizer.getSnapshot(
|
|
788
|
-
blockNumber - this.config.
|
|
785
|
+
blockNumber - this.config.doubleSpendSeverePeerPenaltyWindow,
|
|
789
786
|
);
|
|
790
787
|
const indices = await merkleTree.findLeafIndices(MerkleTreeId.NULLIFIER_TREE, nullifiers);
|
|
791
788
|
return indices.map(index => index !== undefined);
|
|
@@ -794,11 +791,10 @@ export class LibP2PService<T extends P2PClientType> extends WithTracer implement
|
|
|
794
791
|
|
|
795
792
|
const validSnapshot = await snapshotValidator.validateTx(tx);
|
|
796
793
|
if (validSnapshot.result !== 'valid') {
|
|
797
|
-
|
|
798
|
-
return false;
|
|
794
|
+
return PeerErrorSeverity.LowToleranceError;
|
|
799
795
|
}
|
|
800
796
|
|
|
801
|
-
return
|
|
797
|
+
return PeerErrorSeverity.HighToleranceError;
|
|
802
798
|
}
|
|
803
799
|
|
|
804
800
|
/**
|
|
@@ -1,117 +1,17 @@
|
|
|
1
|
-
import { EthAddress } from '@aztec/foundation/eth-address';
|
|
2
1
|
import { createLogger } from '@aztec/foundation/log';
|
|
3
2
|
import { sleep } from '@aztec/foundation/sleep';
|
|
4
|
-
import type { ChainConfig } from '@aztec/stdlib/config';
|
|
5
3
|
import { ClientIvcProof } from '@aztec/stdlib/proofs';
|
|
6
4
|
import { mockTx } from '@aztec/stdlib/testing';
|
|
7
5
|
|
|
8
|
-
import
|
|
6
|
+
import assert from 'assert';
|
|
9
7
|
import path from 'path';
|
|
10
8
|
import { fileURLToPath } from 'url';
|
|
11
9
|
|
|
12
|
-
import {
|
|
13
|
-
import { generatePeerIdPrivateKeys } from '../test-helpers/generate-peer-id-private-keys.js';
|
|
14
|
-
import { getPorts } from '../test-helpers/get-ports.js';
|
|
15
|
-
import { makeEnrs } from '../test-helpers/make-enrs.js';
|
|
10
|
+
import { WorkerClientManager, testChainConfig } from './worker_client_manager.js';
|
|
16
11
|
|
|
17
12
|
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
18
|
-
const workerPath = path.join(__dirname, '../../dest/testbench/p2p_client_testbench_worker.js');
|
|
19
13
|
const logger = createLogger('testbench');
|
|
20
14
|
|
|
21
|
-
let processes: ChildProcess[] = [];
|
|
22
|
-
|
|
23
|
-
const testChainConfig: ChainConfig = {
|
|
24
|
-
l1ChainId: 31337,
|
|
25
|
-
version: 1,
|
|
26
|
-
l1Contracts: {
|
|
27
|
-
rollupAddress: EthAddress.random(),
|
|
28
|
-
},
|
|
29
|
-
};
|
|
30
|
-
|
|
31
|
-
/**
|
|
32
|
-
* Cleanup function to kill all child processes
|
|
33
|
-
*/
|
|
34
|
-
async function cleanup() {
|
|
35
|
-
logger.info('Cleaning up processes...');
|
|
36
|
-
await Promise.all(
|
|
37
|
-
processes.map(
|
|
38
|
-
proc =>
|
|
39
|
-
new Promise<void>(resolve => {
|
|
40
|
-
proc.once('exit', () => resolve());
|
|
41
|
-
proc.send({ type: 'STOP' });
|
|
42
|
-
}),
|
|
43
|
-
),
|
|
44
|
-
);
|
|
45
|
-
process.exit(0);
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
// Handle cleanup on process termination
|
|
49
|
-
process.on('SIGINT', () => void cleanup());
|
|
50
|
-
process.on('SIGTERM', () => void cleanup());
|
|
51
|
-
|
|
52
|
-
/**
|
|
53
|
-
* Creates a number of worker clients in separate processes
|
|
54
|
-
* All are configured to connect to each other and overrided with the test specific config
|
|
55
|
-
*
|
|
56
|
-
* @param numberOfClients - The number of clients to create
|
|
57
|
-
* @param p2pConfig - The P2P config to use for the clients
|
|
58
|
-
* @returns The ENRs of the created clients
|
|
59
|
-
*/
|
|
60
|
-
async function makeWorkerClients(numberOfClients: number, p2pConfig: Partial<P2PConfig>) {
|
|
61
|
-
const peerIdPrivateKeys = generatePeerIdPrivateKeys(numberOfClients);
|
|
62
|
-
const ports = await getPorts(numberOfClients);
|
|
63
|
-
const peerEnrs = await makeEnrs(peerIdPrivateKeys, ports, testChainConfig);
|
|
64
|
-
|
|
65
|
-
processes = [];
|
|
66
|
-
const readySignals: Promise<void>[] = [];
|
|
67
|
-
for (let i = 0; i < numberOfClients; i++) {
|
|
68
|
-
logger.info(`Creating client ${i}`);
|
|
69
|
-
const addr = `127.0.0.1:${ports[i]}`;
|
|
70
|
-
const listenAddr = `0.0.0.0:${ports[i]}`;
|
|
71
|
-
|
|
72
|
-
// Maximum seed with 10 other peers to allow peer discovery to connect them at a smoother rate
|
|
73
|
-
const otherNodes = peerEnrs.filter((_, ind) => ind < Math.min(i, 10));
|
|
74
|
-
|
|
75
|
-
const config: P2PConfig & Partial<ChainConfig> = {
|
|
76
|
-
...getP2PDefaultConfig(),
|
|
77
|
-
p2pEnabled: true,
|
|
78
|
-
peerIdPrivateKey: peerIdPrivateKeys[i],
|
|
79
|
-
tcpListenAddress: listenAddr,
|
|
80
|
-
udpListenAddress: listenAddr,
|
|
81
|
-
tcpAnnounceAddress: addr,
|
|
82
|
-
udpAnnounceAddress: addr,
|
|
83
|
-
bootstrapNodes: [...otherNodes],
|
|
84
|
-
...p2pConfig,
|
|
85
|
-
};
|
|
86
|
-
|
|
87
|
-
const childProcess = fork(workerPath);
|
|
88
|
-
childProcess.send({ type: 'START', config, clientIndex: i });
|
|
89
|
-
|
|
90
|
-
// Wait for ready signal
|
|
91
|
-
readySignals.push(
|
|
92
|
-
new Promise((resolve, reject) => {
|
|
93
|
-
childProcess.once('message', (msg: any) => {
|
|
94
|
-
if (msg.type === 'READY') {
|
|
95
|
-
resolve(undefined);
|
|
96
|
-
}
|
|
97
|
-
if (msg.type === 'ERROR') {
|
|
98
|
-
reject(new Error(msg.error));
|
|
99
|
-
}
|
|
100
|
-
});
|
|
101
|
-
}),
|
|
102
|
-
);
|
|
103
|
-
|
|
104
|
-
processes.push(childProcess);
|
|
105
|
-
}
|
|
106
|
-
// Wait for peers to all connect with each other
|
|
107
|
-
await sleep(4000);
|
|
108
|
-
|
|
109
|
-
// Wait for all peers to be booted up
|
|
110
|
-
await Promise.all(readySignals);
|
|
111
|
-
|
|
112
|
-
return peerEnrs;
|
|
113
|
-
}
|
|
114
|
-
|
|
115
15
|
async function main() {
|
|
116
16
|
try {
|
|
117
17
|
// Read configuration file name from command line args
|
|
@@ -126,7 +26,8 @@ async function main() {
|
|
|
126
26
|
const numberOfClients = config.default.numberOfClients;
|
|
127
27
|
|
|
128
28
|
// Setup clients in separate processes
|
|
129
|
-
|
|
29
|
+
const workerClientManager = new WorkerClientManager(logger, testConfig);
|
|
30
|
+
await workerClientManager.makeWorkerClients(numberOfClients);
|
|
130
31
|
|
|
131
32
|
// wait a bit longer for all peers to be ready
|
|
132
33
|
await sleep(5000);
|
|
@@ -136,22 +37,30 @@ async function main() {
|
|
|
136
37
|
const tx = await mockTx(1, {
|
|
137
38
|
clientIvcProof: ClientIvcProof.random(),
|
|
138
39
|
});
|
|
139
|
-
|
|
40
|
+
|
|
41
|
+
workerClientManager.processes[0].send({ type: 'SEND_TX', tx: tx.toBuffer() });
|
|
140
42
|
logger.info('Transaction sent from client 0');
|
|
141
43
|
|
|
142
44
|
// Give time for message propagation
|
|
143
45
|
await sleep(30000);
|
|
144
46
|
logger.info('Checking message propagation results');
|
|
145
47
|
|
|
146
|
-
|
|
48
|
+
// Check message propagation results
|
|
49
|
+
const numberOfClientsThatReceivedMessage = workerClientManager.numberOfClientsThatReceivedMessage();
|
|
50
|
+
logger.info(`Number of clients that received message: ${numberOfClientsThatReceivedMessage}`);
|
|
51
|
+
|
|
52
|
+
assert(numberOfClientsThatReceivedMessage === numberOfClients - 1);
|
|
53
|
+
|
|
54
|
+
logger.info('Test passed, cleaning up');
|
|
55
|
+
|
|
56
|
+
// cleanup
|
|
57
|
+
await workerClientManager.cleanup();
|
|
147
58
|
} catch (error) {
|
|
148
59
|
logger.error('Test failed with error:', error);
|
|
149
|
-
await cleanup();
|
|
150
60
|
process.exit(1);
|
|
151
61
|
}
|
|
152
62
|
}
|
|
153
63
|
|
|
154
64
|
main().catch(error => {
|
|
155
65
|
logger.error('Unhandled error:', error);
|
|
156
|
-
cleanup().catch(() => process.exit(1));
|
|
157
66
|
});
|
|
@@ -0,0 +1,318 @@
|
|
|
1
|
+
import { EthAddress } from '@aztec/foundation/eth-address';
|
|
2
|
+
import type { Logger } from '@aztec/foundation/log';
|
|
3
|
+
import { sleep } from '@aztec/foundation/sleep';
|
|
4
|
+
import type { ChainConfig } from '@aztec/stdlib/config';
|
|
5
|
+
|
|
6
|
+
import { type ChildProcess, fork } from 'child_process';
|
|
7
|
+
import path from 'path';
|
|
8
|
+
import { fileURLToPath } from 'url';
|
|
9
|
+
|
|
10
|
+
import { type P2PConfig, getP2PDefaultConfig } from '../config.js';
|
|
11
|
+
import { generatePeerIdPrivateKeys } from '../test-helpers/generate-peer-id-private-keys.js';
|
|
12
|
+
import { getPorts } from '../test-helpers/get-ports.js';
|
|
13
|
+
import { makeEnr, makeEnrs } from '../test-helpers/make-enrs.js';
|
|
14
|
+
|
|
15
|
+
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
16
|
+
const workerPath = path.join(__dirname, '../../dest/testbench/p2p_client_testbench_worker.js');
|
|
17
|
+
|
|
18
|
+
const testChainConfig: ChainConfig = {
|
|
19
|
+
l1ChainId: 31337,
|
|
20
|
+
version: 1,
|
|
21
|
+
l1Contracts: {
|
|
22
|
+
rollupAddress: EthAddress.random(),
|
|
23
|
+
},
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
class WorkerClientManager {
|
|
27
|
+
public processes: ChildProcess[] = [];
|
|
28
|
+
public peerIdPrivateKeys: string[] = [];
|
|
29
|
+
public peerEnrs: string[] = [];
|
|
30
|
+
public ports: number[] = [];
|
|
31
|
+
private p2pConfig: Partial<P2PConfig>;
|
|
32
|
+
private logger: Logger;
|
|
33
|
+
private messageReceivedByClient: number[] = [];
|
|
34
|
+
|
|
35
|
+
constructor(logger: Logger, p2pConfig: Partial<P2PConfig>) {
|
|
36
|
+
this.logger = logger;
|
|
37
|
+
this.p2pConfig = p2pConfig;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
destroy() {
|
|
41
|
+
this.cleanup().catch((error: Error) => {
|
|
42
|
+
this.logger.error('Failed to cleanup worker client manager', error);
|
|
43
|
+
process.exit(1);
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Creates address strings from a port
|
|
49
|
+
*/
|
|
50
|
+
private getAddresses(port: number) {
|
|
51
|
+
return {
|
|
52
|
+
addr: `127.0.0.1:${port}`,
|
|
53
|
+
listenAddr: `0.0.0.0:${port}`,
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Creates a client configuration object
|
|
59
|
+
*/
|
|
60
|
+
private createClientConfig(clientIndex: number, port: number, otherNodes: string[]) {
|
|
61
|
+
const { addr, listenAddr } = this.getAddresses(port);
|
|
62
|
+
|
|
63
|
+
return {
|
|
64
|
+
...getP2PDefaultConfig(),
|
|
65
|
+
p2pEnabled: true,
|
|
66
|
+
peerIdPrivateKey: this.peerIdPrivateKeys[clientIndex],
|
|
67
|
+
tcpListenAddress: listenAddr,
|
|
68
|
+
udpListenAddress: listenAddr,
|
|
69
|
+
tcpAnnounceAddress: addr,
|
|
70
|
+
udpAnnounceAddress: addr,
|
|
71
|
+
bootstrapNodes: [...otherNodes],
|
|
72
|
+
...this.p2pConfig,
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Spawns a worker process and returns a promise that resolves when the worker is ready
|
|
78
|
+
*/
|
|
79
|
+
private spawnWorkerProcess(
|
|
80
|
+
config: P2PConfig & Partial<ChainConfig>,
|
|
81
|
+
clientIndex: number,
|
|
82
|
+
): [ChildProcess, Promise<void>] {
|
|
83
|
+
const childProcess = fork(workerPath);
|
|
84
|
+
childProcess.send({ type: 'START', config, clientIndex });
|
|
85
|
+
|
|
86
|
+
// Handle unexpected child process exit
|
|
87
|
+
childProcess.on('exit', (code, signal) => {
|
|
88
|
+
if (code !== 0) {
|
|
89
|
+
this.logger.warn(`Worker ${clientIndex} exited unexpectedly with code ${code} and signal ${signal}`);
|
|
90
|
+
}
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
childProcess.on('message', (msg: any) => {
|
|
94
|
+
if (msg.type === 'GOSSIP_RECEIVED') {
|
|
95
|
+
this.messageReceivedByClient[clientIndex] = msg.count;
|
|
96
|
+
}
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
// Create ready signal promise
|
|
100
|
+
const readySignal = new Promise<void>((resolve, reject) => {
|
|
101
|
+
// Set a timeout to avoid hanging indefinitely
|
|
102
|
+
const timeout = setTimeout(() => {
|
|
103
|
+
reject(new Error(`Timeout waiting for worker ${clientIndex} to be ready`));
|
|
104
|
+
}, 30000); // 30 second timeout
|
|
105
|
+
|
|
106
|
+
childProcess.once('message', (msg: any) => {
|
|
107
|
+
clearTimeout(timeout);
|
|
108
|
+
if (msg.type === 'READY') {
|
|
109
|
+
resolve();
|
|
110
|
+
}
|
|
111
|
+
// For future use
|
|
112
|
+
if (msg.type === 'ERROR') {
|
|
113
|
+
reject(new Error(msg.error));
|
|
114
|
+
}
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
// Also resolve/reject if process exits before sending message
|
|
118
|
+
childProcess.once('exit', code => {
|
|
119
|
+
clearTimeout(timeout);
|
|
120
|
+
if (code === 0) {
|
|
121
|
+
resolve();
|
|
122
|
+
} else {
|
|
123
|
+
reject(new Error(`Worker ${clientIndex} exited with code ${code} before becoming ready`));
|
|
124
|
+
}
|
|
125
|
+
});
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
return [childProcess, readySignal];
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
/**
|
|
132
|
+
* Creates a number of worker clients in separate processes
|
|
133
|
+
* All are configured to connect to each other and overrided with the test specific config
|
|
134
|
+
*
|
|
135
|
+
* @param numberOfClients - The number of clients to create
|
|
136
|
+
* @returns The ENRs of the created clients
|
|
137
|
+
*/
|
|
138
|
+
async makeWorkerClients(numberOfClients: number) {
|
|
139
|
+
try {
|
|
140
|
+
this.messageReceivedByClient = new Array(numberOfClients).fill(0);
|
|
141
|
+
this.peerIdPrivateKeys = generatePeerIdPrivateKeys(numberOfClients);
|
|
142
|
+
this.ports = await getPorts(numberOfClients);
|
|
143
|
+
this.peerEnrs = await makeEnrs(this.peerIdPrivateKeys, this.ports, testChainConfig);
|
|
144
|
+
|
|
145
|
+
this.processes = [];
|
|
146
|
+
const readySignals: Promise<void>[] = [];
|
|
147
|
+
|
|
148
|
+
for (let i = 0; i < numberOfClients; i++) {
|
|
149
|
+
this.logger.info(`Creating client ${i}`);
|
|
150
|
+
|
|
151
|
+
// Maximum seed with 10 other peers to allow peer discovery to connect them at a smoother rate
|
|
152
|
+
const otherNodes = this.peerEnrs.filter((_, ind) => ind < Math.min(i, 10));
|
|
153
|
+
|
|
154
|
+
const config = this.createClientConfig(i, this.ports[i], otherNodes);
|
|
155
|
+
const [childProcess, readySignal] = this.spawnWorkerProcess(config, i);
|
|
156
|
+
|
|
157
|
+
readySignals.push(readySignal);
|
|
158
|
+
this.processes.push(childProcess);
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
// Wait for peers to all connect with each other
|
|
162
|
+
await sleep(10000);
|
|
163
|
+
|
|
164
|
+
// Wait for all peers to be booted up with timeout
|
|
165
|
+
await Promise.race([
|
|
166
|
+
Promise.all(readySignals),
|
|
167
|
+
new Promise((_, reject) =>
|
|
168
|
+
setTimeout(() => reject(new Error('Timeout waiting for all workers to be ready')), 30000),
|
|
169
|
+
),
|
|
170
|
+
]);
|
|
171
|
+
|
|
172
|
+
return this.peerEnrs;
|
|
173
|
+
} catch (error) {
|
|
174
|
+
// Clean up any processes that were created if there's an error
|
|
175
|
+
this.logger.error('Error during makeWorkerClients:', error);
|
|
176
|
+
await this.cleanup();
|
|
177
|
+
throw error;
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
purgeMessageReceivedByClient() {
|
|
182
|
+
this.messageReceivedByClient = new Array(this.processes.length).fill(0);
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
numberOfClientsThatReceivedMessage() {
|
|
186
|
+
return this.messageReceivedByClient.filter(count => count > 0).length;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
/**
|
|
190
|
+
* Changes the port for a specific client
|
|
191
|
+
*
|
|
192
|
+
* @param clientIndex - The index of the client to change port for
|
|
193
|
+
* @param newPort - The new port to use
|
|
194
|
+
*/
|
|
195
|
+
async changePort(clientIndex: number, newPort: number) {
|
|
196
|
+
try {
|
|
197
|
+
if (clientIndex < 0 || clientIndex >= this.processes.length) {
|
|
198
|
+
throw new Error(`Invalid client index: ${clientIndex}`);
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
this.processes[clientIndex].send({ type: 'STOP' });
|
|
202
|
+
|
|
203
|
+
// Wait for the process to be ready with a timeout
|
|
204
|
+
await sleep(10000);
|
|
205
|
+
|
|
206
|
+
this.logger.info(`Changing port for client ${clientIndex} to ${newPort}`);
|
|
207
|
+
|
|
208
|
+
// Update the port in the ports array
|
|
209
|
+
this.ports[clientIndex] = newPort;
|
|
210
|
+
|
|
211
|
+
// Update the port in the peerEnrs array
|
|
212
|
+
this.peerEnrs[clientIndex] = await makeEnr(this.peerIdPrivateKeys[clientIndex], newPort, testChainConfig);
|
|
213
|
+
|
|
214
|
+
// Maximum seed with 10 other peers to allow peer discovery to connect them at a smoother rate
|
|
215
|
+
const otherNodes = this.peerEnrs.filter(
|
|
216
|
+
(_, ind) => ind !== clientIndex && ind < Math.min(this.peerEnrs.length, 10),
|
|
217
|
+
);
|
|
218
|
+
|
|
219
|
+
const config = this.createClientConfig(clientIndex, newPort, otherNodes);
|
|
220
|
+
const [childProcess, readySignal] = this.spawnWorkerProcess(config, clientIndex);
|
|
221
|
+
|
|
222
|
+
this.processes[clientIndex] = childProcess;
|
|
223
|
+
|
|
224
|
+
// Wait for the process to be ready with a timeout
|
|
225
|
+
await Promise.race([
|
|
226
|
+
readySignal,
|
|
227
|
+
new Promise((_, reject) =>
|
|
228
|
+
setTimeout(() => reject(new Error(`Timeout waiting for client ${clientIndex} to be ready`)), 30000),
|
|
229
|
+
),
|
|
230
|
+
]);
|
|
231
|
+
} catch (error) {
|
|
232
|
+
this.logger.error(`Error during changePort for client ${clientIndex}:`, error);
|
|
233
|
+
// Only clean up the specific process that had an issue
|
|
234
|
+
await this.terminateProcess(this.processes[clientIndex], clientIndex);
|
|
235
|
+
throw error;
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
/**
|
|
240
|
+
* Terminate a single process with timeout and force kill if needed
|
|
241
|
+
*/
|
|
242
|
+
private terminateProcess(process: ChildProcess, index: number): Promise<void> {
|
|
243
|
+
if (!process || process.killed) {
|
|
244
|
+
return Promise.resolve();
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
return new Promise<void>(resolve => {
|
|
248
|
+
// Set a timeout for the graceful exit
|
|
249
|
+
const forceKillTimeout = setTimeout(() => {
|
|
250
|
+
this.logger.warn(`Process ${index} didn't exit gracefully, force killing...`);
|
|
251
|
+
try {
|
|
252
|
+
process.kill('SIGKILL'); // Force kill
|
|
253
|
+
} catch (e) {
|
|
254
|
+
this.logger.error(`Error force killing process ${index}:`, e);
|
|
255
|
+
}
|
|
256
|
+
}, 10000); // 10 second timeout for graceful exit
|
|
257
|
+
|
|
258
|
+
// Listen for process exit
|
|
259
|
+
process.once('exit', () => {
|
|
260
|
+
clearTimeout(forceKillTimeout);
|
|
261
|
+
resolve();
|
|
262
|
+
});
|
|
263
|
+
|
|
264
|
+
// Try to gracefully stop the process
|
|
265
|
+
try {
|
|
266
|
+
process.send({ type: 'STOP' });
|
|
267
|
+
} catch (e) {
|
|
268
|
+
// If sending the message fails, force kill immediately
|
|
269
|
+
clearTimeout(forceKillTimeout);
|
|
270
|
+
try {
|
|
271
|
+
process.kill('SIGKILL');
|
|
272
|
+
} catch (killError) {
|
|
273
|
+
this.logger.error(`Error force killing process ${index}:`, killError);
|
|
274
|
+
}
|
|
275
|
+
resolve();
|
|
276
|
+
}
|
|
277
|
+
});
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
/**
|
|
281
|
+
* Cleans up all worker processes with timeout and force kill if needed
|
|
282
|
+
*/
|
|
283
|
+
async cleanup() {
|
|
284
|
+
this.logger.info(`Cleaning up ${this.processes.length} worker processes`);
|
|
285
|
+
|
|
286
|
+
// Create array of promises for each process termination
|
|
287
|
+
const terminationPromises = this.processes.map((process, index) => this.terminateProcess(process, index));
|
|
288
|
+
|
|
289
|
+
// Wait for all processes to terminate with a timeout
|
|
290
|
+
try {
|
|
291
|
+
await Promise.race([
|
|
292
|
+
Promise.all(terminationPromises),
|
|
293
|
+
new Promise<void>(resolve => {
|
|
294
|
+
setTimeout(() => {
|
|
295
|
+
this.logger.warn('Some processes did not terminate in time, force killing all remaining...');
|
|
296
|
+
this.processes.forEach(p => {
|
|
297
|
+
try {
|
|
298
|
+
if (!p.killed) {
|
|
299
|
+
p.kill('SIGKILL');
|
|
300
|
+
}
|
|
301
|
+
} catch (e) {
|
|
302
|
+
// Ignore errors when force killing
|
|
303
|
+
}
|
|
304
|
+
});
|
|
305
|
+
resolve();
|
|
306
|
+
}, 30000); // 30 second timeout for all processes
|
|
307
|
+
}),
|
|
308
|
+
]);
|
|
309
|
+
} catch (error) {
|
|
310
|
+
this.logger.error('Error during cleanup:', error);
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
this.processes = [];
|
|
314
|
+
this.logger.info('All worker processes cleaned up');
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
export { WorkerClientManager, testChainConfig };
|