Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(p2p): persist node private p2p keys #10324

Merged
merged 12 commits into from
Dec 3, 2024
2 changes: 1 addition & 1 deletion yarn-project/accounts/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -102,4 +102,4 @@
"engines": {
"node": ">=18"
}
}
}
2 changes: 1 addition & 1 deletion yarn-project/circuits.js/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -99,4 +99,4 @@
]
]
}
}
}
1 change: 0 additions & 1 deletion yarn-project/end-to-end/src/e2e_p2p/gossip_network.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,6 @@ describe('e2e_p2p_network', () => {
t.logger.info('Creating nodes');
nodes = await createNodes(
t.ctx.aztecNodeConfig,
t.peerIdPrivateKeys,
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

rediscovery test is not longer provided the same private keys, it is expected to store it on its own

t.bootstrapNodeEnr,
NUM_NODES,
BOOT_NODE_UDP_PORT,
Expand Down
2 changes: 0 additions & 2 deletions yarn-project/end-to-end/src/e2e_p2p/p2p_network.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@ import {
PRIVATE_KEYS_START_INDEX,
createValidatorConfig,
generateNodePrivateKeys,
generatePeerIdPrivateKeys,
} from '../fixtures/setup_p2p_test.js';
import {
type ISnapshotManager,
Expand Down Expand Up @@ -66,7 +65,6 @@ export class P2PNetworkTest {
this.baseAccount = privateKeyToAccount(`0x${getPrivateKeyFromIndex(0)!.toString('hex')}`);
this.nodePrivateKeys = generateNodePrivateKeys(PRIVATE_KEYS_START_INDEX, numberOfNodes);
this.nodePublicKeys = this.nodePrivateKeys.map(privateKey => privateKeyToAccount(privateKey).address);
this.peerIdPrivateKeys = generatePeerIdPrivateKeys(numberOfNodes);

this.bootstrapNodeEnr = bootstrapNode.getENR().encodeTxt();

Expand Down
2 changes: 0 additions & 2 deletions yarn-project/end-to-end/src/e2e_p2p/rediscovery.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,6 @@ describe('e2e_p2p_rediscovery', () => {
const contexts: NodeContext[] = [];
nodes = await createNodes(
t.ctx.aztecNodeConfig,
t.peerIdPrivateKeys,
t.bootstrapNodeEnr,
NUM_NODES,
BOOT_NODE_UDP_PORT,
Expand All @@ -73,7 +72,6 @@ describe('e2e_p2p_rediscovery', () => {

const newNode = await createNode(
t.ctx.aztecNodeConfig,
t.peerIdPrivateKeys[i],
i + 1 + BOOT_NODE_UDP_PORT,
undefined,
i,
Expand Down
1 change: 0 additions & 1 deletion yarn-project/end-to-end/src/e2e_p2p/reex.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,6 @@ describe('e2e_p2p_reex', () => {

nodes = await createNodes(
t.ctx.aztecNodeConfig,
t.peerIdPrivateKeys,
t.bootstrapNodeEnr,
NUM_NODES,
BOOT_NODE_UDP_PORT,
Expand Down
1 change: 0 additions & 1 deletion yarn-project/end-to-end/src/e2e_p2p/reqresp.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,6 @@ describe('e2e_p2p_reqresp_tx', () => {
t.logger.info('Creating nodes');
nodes = await createNodes(
t.ctx.aztecNodeConfig,
t.peerIdPrivateKeys,
t.bootstrapNodeEnr,
NUM_NODES,
BOOT_NODE_UDP_PORT,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,6 @@ describe('e2e_p2p_governance_proposer', () => {
t.logger.info('Creating nodes');
nodes = await createNodes(
{ ...t.ctx.aztecNodeConfig, governanceProposerPayload: newPayloadAddress },
t.peerIdPrivateKeys,
t.bootstrapNodeEnr,
NUM_NODES,
BOOT_NODE_UDP_PORT,
Expand Down
30 changes: 1 addition & 29 deletions yarn-project/end-to-end/src/fixtures/setup_p2p_test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ import { type AztecAddress } from '@aztec/circuits.js';
import { type PXEService } from '@aztec/pxe';

import getPort from 'get-port';
import { generatePrivateKey } from 'viem/accounts';

import { getPrivateKeyFromIndex } from './utils.js';
import { getEndToEndTestTelemetryClient } from './with_telemetry_utils.js';
Expand All @@ -32,22 +31,8 @@ export function generateNodePrivateKeys(startIndex: number, numberOfNodes: numbe
return nodePrivateKeys;
}

export function generatePeerIdPrivateKey(): string {
// magic number is multiaddr prefix: https://multiformats.io/multiaddr/ for secp256k1
return '08021220' + generatePrivateKey().substr(2, 66);
}

export function generatePeerIdPrivateKeys(numberOfPeers: number): string[] {
const peerIdPrivateKeys = [];
for (let i = 0; i < numberOfPeers; i++) {
peerIdPrivateKeys.push(generatePeerIdPrivateKey());
}
return peerIdPrivateKeys;
}

export function createNodes(
config: AztecNodeConfig,
peerIdPrivateKeys: string[],
bootstrapNodeEnr: string,
numNodes: number,
bootNodePort: number,
Expand All @@ -60,15 +45,7 @@ export function createNodes(
const port = bootNodePort + i + 1;

const dataDir = dataDirectory ? `${dataDirectory}-${i}` : undefined;
const nodePromise = createNode(
config,
peerIdPrivateKeys[i],
port,
bootstrapNodeEnr,
i + PRIVATE_KEYS_START_INDEX,
dataDir,
metricsPort,
);
const nodePromise = createNode(config, port, bootstrapNodeEnr, i + PRIVATE_KEYS_START_INDEX, dataDir, metricsPort);
nodePromises.push(nodePromise);
}
return Promise.all(nodePromises);
Expand All @@ -77,7 +54,6 @@ export function createNodes(
// creates a P2P enabled instance of Aztec Node Service
export async function createNode(
config: AztecNodeConfig,
peerIdPrivateKey: string,
tcpPort: number,
bootstrapNode: string | undefined,
publisherAddressIndex: number,
Expand All @@ -88,7 +64,6 @@ export async function createNode(
config,
bootstrapNode,
tcpPort,
peerIdPrivateKey,
publisherAddressIndex,
dataDirectory,
);
Expand All @@ -105,11 +80,9 @@ export async function createValidatorConfig(
config: AztecNodeConfig,
bootstrapNodeEnr?: string,
port?: number,
peerIdPrivateKey?: string,
accountIndex: number = 1,
dataDirectory?: string,
) {
peerIdPrivateKey = peerIdPrivateKey ?? generatePeerIdPrivateKey();
port = port ?? (await getPort());

const privateKey = getPrivateKeyFromIndex(accountIndex);
Expand All @@ -120,7 +93,6 @@ export async function createValidatorConfig(

const nodeConfig: AztecNodeConfig = {
...config,
peerIdPrivateKey: peerIdPrivateKey,
udpListenAddress: `0.0.0.0:${port}`,
tcpListenAddress: `0.0.0.0:${port}`,
tcpAnnounceAddress: `127.0.0.1:${port}`,
Expand Down
5 changes: 2 additions & 3 deletions yarn-project/p2p/src/bootstrap/bootstrap.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,7 @@ import { type Multiaddr, multiaddr } from '@multiformats/multiaddr';

import type { BootnodeConfig } from '../config.js';
import { AZTEC_ENR_KEY, AZTEC_NET } from '../service/discV5_service.js';
import { createLibP2PPeerId } from '../service/index.js';
import { convertToMultiaddr } from '../util.js';
import { convertToMultiaddr, createLibP2PPeerIdFromPrivateKey } from '../util.js';

/**
* Encapsulates a 'Bootstrap' node, used for the purpose of assisting new joiners in acquiring peers.
Expand All @@ -27,7 +26,7 @@ export class BootstrapNode {
*/
public async start(config: BootnodeConfig) {
const { peerIdPrivateKey, udpListenAddress, udpAnnounceAddress } = config;
const peerId = await createLibP2PPeerId(peerIdPrivateKey);
const peerId = await createLibP2PPeerIdFromPrivateKey(peerIdPrivateKey);
this.peerId = peerId;
const enr = SignableENR.createFromPeerId(peerId);

Expand Down
8 changes: 5 additions & 3 deletions yarn-project/p2p/src/client/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,8 @@ import { type MemPools } from '../mem_pools/interface.js';
import { AztecKVTxPool, type TxPool } from '../mem_pools/tx_pool/index.js';
import { DiscV5Service } from '../service/discV5_service.js';
import { DummyP2PService } from '../service/dummy_service.js';
import { LibP2PService, createLibP2PPeerId } from '../service/index.js';
import { configureP2PClientAddresses } from '../util.js';
import { LibP2PService } from '../service/index.js';
import { configureP2PClientAddresses, getPeerIdPrivateKey, createLibP2PPeerIdFromPrivateKey } from '../util.js';

export * from './p2p_client.js';

Expand Down Expand Up @@ -49,7 +49,8 @@ export const createP2PClient = async (
config = await configureP2PClientAddresses(_config);

// Create peer discovery service
const peerId = await createLibP2PPeerId(config.peerIdPrivateKey);
const peerIdPrivateKey = await getPeerIdPrivateKey(config, store);
const peerId = await createLibP2PPeerIdFromPrivateKey(peerIdPrivateKey);
const discoveryService = new DiscV5Service(peerId, config, telemetry);

p2pService = await LibP2PService.new(
Expand All @@ -68,3 +69,4 @@ export const createP2PClient = async (
}
return new P2PClient(store, l2BlockSource, mempools, p2pService, config.keepProvenTxsInPoolFor, telemetry);
};

8 changes: 4 additions & 4 deletions yarn-project/p2p/src/mocks/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ import { BootstrapNode } from '../bootstrap/bootstrap.js';
import { type BootnodeConfig, type P2PConfig } from '../config.js';
import { type MemPools } from '../mem_pools/interface.js';
import { DiscV5Service } from '../service/discV5_service.js';
import { LibP2PService, createLibP2PPeerId } from '../service/libp2p_service.js';
import { LibP2PService } from '../service/libp2p_service.js';
import { type PeerManager } from '../service/peer_manager.js';
import { type P2PReqRespConfig } from '../service/reqresp/config.js';
import { pingHandler, statusHandler } from '../service/reqresp/handlers.js';
Expand All @@ -35,7 +35,7 @@ import {
noopValidator,
} from '../service/reqresp/interface.js';
import { ReqResp } from '../service/reqresp/reqresp.js';
import { type PubSubLibp2p } from '../util.js';
import { createLibP2PPeerIdFromPrivateKey, type PubSubLibp2p } from '../util.js';

/**
* Creates a libp2p node, pre configured.
Expand Down Expand Up @@ -102,7 +102,7 @@ export async function createTestLibP2PService(
port: number = 0,
peerId?: PeerId,
) {
peerId = peerId ?? (await createLibP2PPeerId());
peerId = peerId ?? (await createLibP2PPeerIdFromPrivateKey());
const config = {
tcpAnnounceAddress: `127.0.0.1:${port}`,
udpAnnounceAddress: `127.0.0.1:${port}`,
Expand Down Expand Up @@ -247,7 +247,7 @@ export async function createBootstrapNode(
port: number,
telemetry: TelemetryClient = new NoopTelemetryClient(),
): Promise<BootstrapNode> {
const peerId = await createLibP2PPeerId();
const peerId = await createLibP2PPeerIdFromPrivateKey();
const config = createBootstrapNodeConfig(Buffer.from(peerId.privateKey!).toString('hex'), port);

return startBootstrapNode(config, telemetry);
Expand Down
4 changes: 2 additions & 2 deletions yarn-project/p2p/src/service/discv5_service.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@ import type { PeerId } from '@libp2p/interface';
import { BootstrapNode } from '../bootstrap/bootstrap.js';
import { type P2PConfig, getP2PDefaultConfig } from '../config.js';
import { DiscV5Service } from './discV5_service.js';
import { createLibP2PPeerId } from './libp2p_service.js';
import { PeerDiscoveryState } from './service.js';
import { createLibP2PPeerIdFromPrivateKey } from '../util.js';

const waitForPeers = (node: DiscV5Service, expectedCount: number): Promise<void> => {
const timeout = 7_000;
Expand Down Expand Up @@ -123,7 +123,7 @@ describe('Discv5Service', () => {

const createNode = async (port: number) => {
const bootnodeAddr = bootNode.getENR().encodeTxt();
const peerId = await createLibP2PPeerId();
const peerId = await createLibP2PPeerIdFromPrivateKey();
const config: P2PConfig = {
...getP2PDefaultConfig(),
...baseConfig,
Expand Down
16 changes: 0 additions & 16 deletions yarn-project/p2p/src/service/libp2p_service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,6 @@ import { identify } from '@libp2p/identify';
import type { PeerId } from '@libp2p/interface';
import '@libp2p/kad-dht';
import { mplex } from '@libp2p/mplex';
import { createFromJSON, createSecp256k1PeerId } from '@libp2p/peer-id-factory';
import { tcp } from '@libp2p/tcp';
import { createLibp2p } from 'libp2p';

Expand Down Expand Up @@ -60,21 +59,6 @@ import {
import { ReqResp } from './reqresp/reqresp.js';
import type { P2PService, PeerDiscoveryService } from './service.js';

/**
* Create a libp2p peer ID from the private key if provided, otherwise creates a new random ID.
* @param privateKey - Optional peer ID private key as hex string
* @returns The peer ID.
*/
export async function createLibP2PPeerId(privateKey?: string): Promise<PeerId> {
if (!privateKey?.length) {
return await createSecp256k1PeerId();
}
const base64 = Buffer.from(privateKey, 'hex').toString('base64');
return await createFromJSON({
id: '',
privKey: base64,
});
}

/**
* Lib P2P implementation of the P2PService interface.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,8 @@ import { type AttestationPool } from '../../mem_pools/attestation_pool/attestati
import { type EpochProofQuotePool } from '../../mem_pools/epoch_proof_quote_pool/epoch_proof_quote_pool.js';
import { type TxPool } from '../../mem_pools/tx_pool/index.js';
import { AlwaysFalseCircuitVerifier, AlwaysTrueCircuitVerifier } from '../../mocks/index.js';
import { convertToMultiaddr } from '../../util.js';
import { convertToMultiaddr, createLibP2PPeerIdFromPrivateKey } from '../../util.js';
import { AZTEC_ENR_KEY, AZTEC_NET } from '../discV5_service.js';
import { createLibP2PPeerId } from '../index.js';
import { PeerErrorSeverity } from '../peer_scoring.js';

/**
Expand Down Expand Up @@ -98,7 +97,7 @@ describe('Req Resp p2p client integration', () => {

const peerEnrs = await Promise.all(
peerIdPrivateKeys.map(async (pk, i) => {
const peerId = await createLibP2PPeerId(pk);
const peerId = await createLibP2PPeerIdFromPrivateKey(pk);
const enr = SignableENR.createFromPeerId(peerId);

const udpAnnounceAddress = `127.0.0.1:${ports[i]}`;
Expand Down
44 changes: 44 additions & 0 deletions yarn-project/p2p/src/util.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@ import { resolve } from 'dns/promises';
import type { Libp2p } from 'libp2p';

import { type P2PConfig } from './config.js';
import { createFromJSON, createSecp256k1PeerId } from '@libp2p/peer-id-factory';
import { type AztecKVStore, type AztecSingleton } from '@aztec/kv-store';
import { type PeerId } from '@libp2p/interface';

export interface PubSubLibp2p extends Libp2p {
services: {
Expand Down Expand Up @@ -141,3 +144,44 @@ export async function configureP2PClientAddresses(

return config;
}

/**
* Get the peer id private key
*
* 1. Check we have a peer id private key persisted in the node
* 2. If not, check if we have a peer id private key in the config
* 3. If not, create a new one, then persist it in the node
*
*/
export async function getPeerIdPrivateKey(config: P2PConfig, store: AztecKVStore): Promise<string> {
const peerIdPrivateKeySingleton: AztecSingleton<string> = store.openSingleton('peerIdPrivateKey');
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we prioritise a configured value over a stored one? Currently, once a key is stored, it's impossible to change that key without wiping the data directory.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yep, i was thinking this today, will update

const storedPeerIdPrivateKey = peerIdPrivateKeySingleton.get();
if (storedPeerIdPrivateKey) {
return storedPeerIdPrivateKey;
}

if (config.peerIdPrivateKey) {
await peerIdPrivateKeySingleton.set(config.peerIdPrivateKey);
return config.peerIdPrivateKey;
}

const newPeerIdPrivateKey = (await createSecp256k1PeerId()).privateKey!.toString();
await peerIdPrivateKeySingleton.set(newPeerIdPrivateKey);
return newPeerIdPrivateKey;
}

/**
* Create a libp2p peer ID from the private key.
* @param privateKey - peer ID private key as hex string
* @returns The peer ID.
*/
export async function createLibP2PPeerIdFromPrivateKey(privateKey?: string): Promise<PeerId> {
if (!privateKey?.length) {
throw new Error('No peer private key provided');
}
const base64 = Buffer.from(privateKey, 'hex').toString('base64');
return await createFromJSON({
id: '',
privKey: base64,
});
}
Loading
Loading