Skip to content

Commit cdd1cbd

Browse files
authored
feat(p2p): more comprehensive peer management, dial retries, persistence fix (#6953)
1 parent fa70876 commit cdd1cbd

File tree

14 files changed

+454
-272
lines changed

14 files changed

+454
-272
lines changed

yarn-project/aztec-node/src/aztec-node/server.ts

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -122,7 +122,11 @@ export class AztecNodeService implements AztecNode {
122122
* @param config - The configuration to be used by the aztec node.
123123
* @returns - A fully synced Aztec Node for use in development/testing.
124124
*/
125-
public static async createAndSync(config: AztecNodeConfig) {
125+
public static async createAndSync(
126+
config: AztecNodeConfig,
127+
log = createDebugLogger('aztec:node'),
128+
storeLog = createDebugLogger('aztec:node:lmdb'),
129+
) {
126130
const ethereumChain = createEthereumChain(config.rpcUrl, config.apiKey);
127131
//validate that the actual chain id matches that specified in configuration
128132
if (config.chainId !== ethereumChain.chainInfo.id) {
@@ -131,8 +135,6 @@ export class AztecNodeService implements AztecNode {
131135
);
132136
}
133137

134-
const log = createDebugLogger('aztec:node');
135-
const storeLog = createDebugLogger('aztec:node:lmdb');
136138
const store = await initStoreForRollup(
137139
AztecLmdbStore.open(config.dataDirectory, false, storeLog),
138140
config.l1Contracts.rollupAddress,

yarn-project/end-to-end/Earthfile

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,9 @@ E2E_TEST:
4646
# Run our docker compose, ending whenever sandbox ends, filtering out noisy eth_getLogs
4747
RUN docker run -e HARDWARE_CONCURRENCY=$hardware_concurrency --rm aztecprotocol/end-to-end:$AZTEC_DOCKER_TAG $test || $allow_fail
4848

49+
e2e-p2p:
50+
DO +E2E_TEST --test=./src/e2e_p2p_network.test.ts
51+
4952
e2e-2-pxes:
5053
DO +E2E_TEST --test=./src/e2e_2_pxes.test.ts
5154

yarn-project/end-to-end/src/flakey_e2e_p2p_network.test.ts renamed to yarn-project/end-to-end/src/e2e_p2p_network.test.ts

Lines changed: 136 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,13 @@ import {
88
GrumpkinScalar,
99
type SentTx,
1010
TxStatus,
11+
createDebugLogger,
12+
sleep,
1113
} from '@aztec/aztec.js';
1214
import { type BootNodeConfig, BootstrapNode, createLibP2PPeerId } from '@aztec/p2p';
1315
import { type PXEService, createPXEService, getPXEServiceConfig as getRpcConfig } from '@aztec/pxe';
1416

17+
import fs from 'fs';
1518
import { mnemonicToAccount } from 'viem/accounts';
1619

1720
import { MNEMONIC } from './fixtures/fixtures.js';
@@ -30,21 +33,36 @@ interface NodeContext {
3033
account: AztecAddress;
3134
}
3235

36+
const PEER_ID_PRIVATE_KEYS = [
37+
'0802122002f651fd8653925529e3baccb8489b3af4d7d9db440cbf5df4a63ff04ea69683',
38+
'08021220c3bd886df5fe5b33376096ad0dab3d2dc86ed2a361d5fde70f24d979dc73da41',
39+
'080212206b6567ac759db5434e79495ec7458e5e93fe479a5b80713446e0bce5439a5655',
40+
'08021220366453668099bdacdf08fab476ee1fced6bf00ddc1223d6c2ee626e7236fb526',
41+
];
42+
3343
describe('e2e_p2p_network', () => {
3444
let config: AztecNodeConfig;
3545
let logger: DebugLogger;
3646
let teardown: () => Promise<void>;
47+
let bootstrapNode: BootstrapNode;
48+
let bootstrapNodeEnr: string;
3749

3850
beforeEach(async () => {
39-
({ teardown, config, logger } = await setup(1));
51+
({ teardown, config, logger } = await setup(0));
52+
bootstrapNode = await createBootstrapNode();
53+
bootstrapNodeEnr = bootstrapNode.getENR().encodeTxt();
4054
});
4155

4256
afterEach(() => teardown());
4357

58+
afterAll(() => {
59+
for (let i = 0; i < NUM_NODES; i++) {
60+
fs.rmSync(`./data-${i}`, { recursive: true, force: true });
61+
}
62+
});
63+
4464
it('should rollup txs from all peers', async () => {
4565
// create the bootstrap node for the network
46-
const bootstrapNode = await createBootstrapNode();
47-
const bootstrapNodeEnr = bootstrapNode.getENR();
4866
if (!bootstrapNodeEnr) {
4967
throw new Error('Bootstrap node ENR is not available');
5068
}
@@ -53,14 +71,29 @@ describe('e2e_p2p_network', () => {
5371
// should be set so that the only way for rollups to be built
5472
// is if the txs are successfully gossiped around the nodes.
5573
const contexts: NodeContext[] = [];
74+
const nodes: AztecNodeService[] = [];
5675
for (let i = 0; i < NUM_NODES; i++) {
57-
const node = await createNode(i + 1 + BOOT_NODE_UDP_PORT, bootstrapNodeEnr?.encodeTxt(), i);
76+
const node = await createNode(i + 1 + BOOT_NODE_UDP_PORT, bootstrapNodeEnr, i);
77+
nodes.push(node);
78+
}
79+
80+
// wait a bit for peers to discover each other
81+
await sleep(2000);
82+
83+
for (const node of nodes) {
5884
const context = await createPXEServiceAndSubmitTransactions(node, NUM_TXS_PER_NODE);
5985
contexts.push(context);
6086
}
6187

6288
// now ensure that all txs were successfully mined
63-
await Promise.all(contexts.flatMap(context => context.txs.map(tx => tx.wait())));
89+
await Promise.all(
90+
contexts.flatMap((context, i) =>
91+
context.txs.map(async (tx, j) => {
92+
logger.info(`Waiting for tx ${i}-${j}: ${await tx.getTxHash()} to be mined`);
93+
return tx.wait();
94+
}),
95+
),
96+
);
6497

6598
// shutdown all nodes.
6699
for (const context of contexts) {
@@ -70,6 +103,61 @@ describe('e2e_p2p_network', () => {
70103
await bootstrapNode.stop();
71104
});
72105

106+
it('should re-discover stored peers without bootstrap node', async () => {
107+
const contexts: NodeContext[] = [];
108+
const nodes: AztecNodeService[] = [];
109+
for (let i = 0; i < NUM_NODES; i++) {
110+
const node = await createNode(i + 1 + BOOT_NODE_UDP_PORT, bootstrapNodeEnr, i, `./data-${i}`);
111+
nodes.push(node);
112+
}
113+
// wait a bit for peers to discover each other
114+
await sleep(3000);
115+
116+
// stop bootstrap node
117+
await bootstrapNode.stop();
118+
119+
// create new nodes from datadir
120+
const newNodes: AztecNodeService[] = [];
121+
122+
// stop all nodes
123+
for (let i = 0; i < NUM_NODES; i++) {
124+
const node = nodes[i];
125+
await node.stop();
126+
logger.info(`Node ${i} stopped`);
127+
await sleep(1200);
128+
const newNode = await createNode(i + 1 + BOOT_NODE_UDP_PORT, undefined, i, `./data-${i}`);
129+
logger.info(`Node ${i} restarted`);
130+
newNodes.push(newNode);
131+
// const context = await createPXEServiceAndSubmitTransactions(node, NUM_TXS_PER_NODE);
132+
// contexts.push(context);
133+
}
134+
135+
// wait a bit for peers to discover each other
136+
await sleep(2000);
137+
138+
for (const node of newNodes) {
139+
const context = await createPXEServiceAndSubmitTransactions(node, NUM_TXS_PER_NODE);
140+
contexts.push(context);
141+
}
142+
143+
// now ensure that all txs were successfully mined
144+
await Promise.all(
145+
contexts.flatMap((context, i) =>
146+
context.txs.map(async (tx, j) => {
147+
logger.info(`Waiting for tx ${i}-${j}: ${await tx.getTxHash()} to be mined`);
148+
return tx.wait();
149+
}),
150+
),
151+
);
152+
153+
// shutdown all nodes.
154+
// for (const context of contexts) {
155+
for (const context of contexts) {
156+
await context.node.stop();
157+
await context.pxeService.stop();
158+
}
159+
});
160+
73161
const createBootstrapNode = async () => {
74162
const peerId = await createLibP2PPeerId();
75163
const bootstrapNode = new BootstrapNode();
@@ -87,7 +175,12 @@ describe('e2e_p2p_network', () => {
87175
};
88176

89177
// creates a P2P enabled instance of Aztec Node Service
90-
const createNode = async (tcpListenPort: number, bootstrapNode: string, publisherAddressIndex: number) => {
178+
const createNode = async (
179+
tcpListenPort: number,
180+
bootstrapNode: string | undefined,
181+
publisherAddressIndex: number,
182+
dataDirectory?: string,
183+
) => {
91184
// We use different L1 publisher accounts in order to avoid duplicate tx nonces. We start from
92185
// publisherAddressIndex + 1 because index 0 was already used during test environment setup.
93186
const hdAccount = mnemonicToAccount(MNEMONIC, { addressIndex: publisherAddressIndex + 1 });
@@ -96,38 +189,21 @@ describe('e2e_p2p_network', () => {
96189

97190
const newConfig: AztecNodeConfig = {
98191
...config,
192+
peerIdPrivateKey: PEER_ID_PRIVATE_KEYS[publisherAddressIndex],
99193
udpListenAddress: `0.0.0.0:${tcpListenPort}`,
100194
tcpListenAddress: `0.0.0.0:${tcpListenPort}`,
101195
tcpAnnounceAddress: `127.0.0.1:${tcpListenPort}`,
102196
udpAnnounceAddress: `127.0.0.1:${tcpListenPort}`,
103-
bootstrapNodes: [bootstrapNode],
104197
minTxsPerBlock: NUM_TXS_PER_BLOCK,
105198
maxTxsPerBlock: NUM_TXS_PER_BLOCK,
106199
p2pEnabled: true,
107200
p2pBlockCheckIntervalMS: 1000,
108201
p2pL2QueueSize: 1,
109202
transactionProtocol: '',
203+
dataDirectory,
204+
bootstrapNodes: bootstrapNode ? [bootstrapNode] : [],
110205
};
111-
return await AztecNodeService.createAndSync(newConfig);
112-
};
113-
114-
// submits a set of transactions to the provided Private eXecution Environment (PXE)
115-
const submitTxsTo = async (pxe: PXEService, account: AztecAddress, numTxs: number) => {
116-
const txs: SentTx[] = [];
117-
for (let i = 0; i < numTxs; i++) {
118-
const tx = getSchnorrAccount(pxe, Fr.random(), GrumpkinScalar.random(), Fr.random()).deploy();
119-
logger.info(`Tx sent with hash ${await tx.getTxHash()}`);
120-
const receipt = await tx.getReceipt();
121-
expect(receipt).toEqual(
122-
expect.objectContaining({
123-
status: TxStatus.PENDING,
124-
error: '',
125-
}),
126-
);
127-
logger.info(`Receipt received for ${await tx.getTxHash()}`);
128-
txs.push(tx);
129-
}
130-
return txs;
206+
return await AztecNodeService.createAndSync(newConfig, createDebugLogger(`aztec:node-${tcpListenPort}`));
131207
};
132208

133209
// creates an instance of the PXE and submit a given number of transactions to it.
@@ -142,12 +218,44 @@ describe('e2e_p2p_network', () => {
142218
const completeAddress = CompleteAddress.fromSecretKeyAndPartialAddress(secretKey, Fr.random());
143219
await pxeService.registerAccount(secretKey, completeAddress.partialAddress);
144220

145-
const txs = await submitTxsTo(pxeService, completeAddress.address, numTxs);
221+
const txs = await submitTxsTo(pxeService, numTxs);
146222
return {
147223
txs,
148224
account: completeAddress.address,
149225
pxeService,
150226
node,
151227
};
152228
};
229+
230+
// submits a set of transactions to the provided Private eXecution Environment (PXE)
231+
const submitTxsTo = async (pxe: PXEService, numTxs: number) => {
232+
const txs: SentTx[] = [];
233+
for (let i = 0; i < numTxs; i++) {
234+
// const tx = getSchnorrAccount(pxe, Fr.random(), GrumpkinScalar.random(), Fr.random()).deploy();
235+
const accountManager = getSchnorrAccount(pxe, Fr.random(), GrumpkinScalar.random(), Fr.random());
236+
const deployMethod = await accountManager.getDeployMethod();
237+
await deployMethod.create({
238+
contractAddressSalt: accountManager.salt,
239+
skipClassRegistration: true,
240+
skipPublicDeployment: true,
241+
universalDeploy: true,
242+
});
243+
await deployMethod.prove({});
244+
const tx = deployMethod.send();
245+
246+
const txHash = await tx.getTxHash();
247+
248+
logger.info(`Tx sent with hash ${txHash}`);
249+
const receipt = await tx.getReceipt();
250+
expect(receipt).toEqual(
251+
expect.objectContaining({
252+
status: TxStatus.PENDING,
253+
error: '',
254+
}),
255+
);
256+
logger.info(`Receipt received for ${txHash}`);
257+
txs.push(tx);
258+
}
259+
return txs;
260+
};
153261
});

yarn-project/p2p/src/client/index.ts

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import { type AztecKVStore } from '@aztec/kv-store';
44
import { P2PClient } from '../client/p2p_client.js';
55
import { type P2PConfig } from '../config.js';
66
import { DiscV5Service } from '../service/discV5_service.js';
7-
import { DummyP2PService, DummyPeerDiscoveryService } from '../service/dummy_service.js';
7+
import { DummyP2PService } from '../service/dummy_service.js';
88
import { LibP2PService, createLibP2PPeerId } from '../service/index.js';
99
import { type TxPool } from '../tx_pool/index.js';
1010
import { getPublicIp, splitAddressPort } from '../util.js';
@@ -17,7 +17,6 @@ export const createP2PClient = async (
1717
txPool: TxPool,
1818
l2BlockSource: L2BlockSource,
1919
) => {
20-
let discv5Service;
2120
let p2pService;
2221

2322
if (config.p2pEnabled) {
@@ -40,7 +39,7 @@ export const createP2PClient = async (
4039
config.tcpAnnounceAddress = tcpAnnounceAddress;
4140
} else {
4241
throw new Error(
43-
`Invalid announceTcpAddress provided: ${splitTcpAnnounceAddress}. Expected format: <addr>:<port>`,
42+
`Invalid announceTcpAddress provided: ${configTcpAnnounceAddress}. Expected format: <addr>:<port>`,
4443
);
4544
}
4645
}
@@ -59,11 +58,10 @@ export const createP2PClient = async (
5958

6059
// Create peer discovery service
6160
const peerId = await createLibP2PPeerId(config.peerIdPrivateKey);
62-
discv5Service = new DiscV5Service(peerId, config);
63-
p2pService = await LibP2PService.new(config, discv5Service, peerId, txPool);
61+
const discoveryService = new DiscV5Service(peerId, config);
62+
p2pService = await LibP2PService.new(config, discoveryService, peerId, txPool, store);
6463
} else {
6564
p2pService = new DummyP2PService();
66-
discv5Service = new DummyPeerDiscoveryService();
6765
}
6866
return new P2PClient(store, l2BlockSource, txPool, p2pService);
6967
};

yarn-project/p2p/src/client/p2p_client.test.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,6 @@ describe('In-Memory P2P Client', () => {
3737
start: jest.fn(),
3838
stop: jest.fn(),
3939
propagateTx: jest.fn(),
40-
settledTxs: jest.fn(),
4140
};
4241

4342
blockSource = new MockBlockSource();

yarn-project/p2p/src/client/p2p_client.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -194,7 +194,7 @@ export class P2PClient implements P2P {
194194
this.log.debug('Stopped block downloader');
195195
await this.runningPromise;
196196
this.setCurrentState(P2PClientState.STOPPED);
197-
this.log.info('P2P client stopped...');
197+
this.log.info('P2P client stopped.');
198198
}
199199

200200
/**
@@ -278,7 +278,6 @@ export class P2PClient implements P2P {
278278
for (const block of blocks) {
279279
const txHashes = block.body.txEffects.map(txEffect => txEffect.txHash);
280280
await this.txPool.deleteTxs(txHashes);
281-
this.p2pService.settledTxs(txHashes);
282281
}
283282
}
284283

0 commit comments

Comments
 (0)