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: tx verification & peer scoring on p2p layer #8298

Merged
merged 27 commits into from
Sep 17, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
081bff2
feat: tx verification on p2p layer
spypsy Aug 30, 2024
d28e5cd
remove unecessary complexity from p2p tx validation
spypsy Sep 2, 2024
a6c7551
update tests
spypsy Sep 2, 2024
aa77b00
update validator factory
spypsy Sep 2, 2024
df11b41
remove unnecessary global var builder
spypsy Sep 2, 2024
b9a2b82
Merge branch 'master' into spy/p2p-tx-validation
spypsy Sep 2, 2024
6659334
fix server tests
spypsy Sep 4, 2024
baea32b
use mockFn
spypsy Sep 4, 2024
d13cd69
merge with master
spypsy Sep 4, 2024
0d79216
fix test
spypsy Sep 4, 2024
b7327cf
Merge branch 'master' into spy/p2p-tx-validation
spypsy Sep 4, 2024
cafa5be
peer scoring according to tx validation errors
spypsy Sep 5, 2024
eae3fc6
Remove console statement
spypsy Sep 5, 2024
0a39e38
Merge branch 'master' into spy/p2p-tx-validation
spypsy Sep 9, 2024
3b08b7a
rm unused file
spypsy Sep 9, 2024
ceece5f
add unit tests for peer scoring
spypsy Sep 12, 2024
3e74c93
expose gossipsub config values
spypsy Sep 12, 2024
fad3232
remove set-up job
spypsy Sep 12, 2024
494c3f1
merge master conflicts
spypsy Sep 13, 2024
82b888f
fix build
spypsy Sep 13, 2024
90ad1ed
fix formatting
spypsy Sep 13, 2024
cc7bd10
Merge branch 'master' into spy/p2p-tx-validation
spypsy Sep 16, 2024
9d0be48
more gossipsub config values
spypsy Sep 16, 2024
3ac7ef5
Merge branch 'master' into spy/p2p-tx-validation
spypsy Sep 16, 2024
7ca89d8
accept peer scoring values in random order
spypsy Sep 17, 2024
b2042a6
merge with master
spypsy Sep 17, 2024
83568e8
feat: add bot support for EasyPrivateToken (#8566)
spypsy Sep 17, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion yarn-project/aztec-node/src/aztec-node/server.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import {
type L2LogsSource,
MerkleTreeId,
type MerkleTreeOperations,
type WorldStateSynchronizer,
mockTxForRollup,
} from '@aztec/circuit-types';
import { AztecAddress, EthAddress, Fr, GasFees, GlobalVariables, MaxBlockNumber } from '@aztec/circuits.js';
Expand All @@ -14,7 +15,6 @@ import { type P2P } from '@aztec/p2p';
import { type GlobalVariableBuilder } from '@aztec/sequencer-client';
import { NoopTelemetryClient } from '@aztec/telemetry-client/noop';
import { type ContractDataSource } from '@aztec/types/contracts';
import { type WorldStateSynchronizer } from '@aztec/world-state';

import { type MockProxy, mock, mockFn } from 'jest-mock-extended';

Expand Down
34 changes: 20 additions & 14 deletions yarn-project/aztec-node/src/aztec-node/server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import {
TxReceipt,
TxStatus,
type TxValidator,
type WorldStateSynchronizer,
partitionReverts,
} from '@aztec/circuit-types';
import {
Expand All @@ -53,20 +54,23 @@ import { Timer } from '@aztec/foundation/timer';
import { type AztecKVStore } from '@aztec/kv-store';
import { createStore, openTmpStore } from '@aztec/kv-store/utils';
import { SHA256Trunc, StandardTree, UnbalancedTree } from '@aztec/merkle-tree';
import { AztecKVTxPool, InMemoryAttestationPool, type P2P, createP2PClient } from '@aztec/p2p';
import { getCanonicalClassRegisterer } from '@aztec/protocol-contracts/class-registerer';
import { getCanonicalFeeJuice } from '@aztec/protocol-contracts/fee-juice';
import { getCanonicalInstanceDeployer } from '@aztec/protocol-contracts/instance-deployer';
import { getCanonicalKeyRegistryAddress } from '@aztec/protocol-contracts/key-registry';
import { getCanonicalMultiCallEntrypointAddress } from '@aztec/protocol-contracts/multi-call-entrypoint';
import {
AggregateTxValidator,
AztecKVTxPool,
DataTxValidator,
DoubleSpendTxValidator,
GlobalVariableBuilder,
InMemoryAttestationPool,
MetadataTxValidator,
SequencerClient,
} from '@aztec/sequencer-client';
type P2P,
TxProofValidator,
createP2PClient,
} from '@aztec/p2p';
import { getCanonicalClassRegisterer } from '@aztec/protocol-contracts/class-registerer';
import { getCanonicalFeeJuice } from '@aztec/protocol-contracts/fee-juice';
import { getCanonicalInstanceDeployer } from '@aztec/protocol-contracts/instance-deployer';
import { getCanonicalKeyRegistryAddress } from '@aztec/protocol-contracts/key-registry';
import { getCanonicalMultiCallEntrypointAddress } from '@aztec/protocol-contracts/multi-call-entrypoint';
import { GlobalVariableBuilder, SequencerClient } from '@aztec/sequencer-client';
import { PublicProcessorFactory, WASMSimulator, WorldStateDB, createSimulationProvider } from '@aztec/simulator';
import { type TelemetryClient } from '@aztec/telemetry-client';
import { NoopTelemetryClient } from '@aztec/telemetry-client/noop';
Expand All @@ -77,11 +81,10 @@ import {
type ProtocolContractAddresses,
} from '@aztec/types/contracts';
import { createValidatorClient } from '@aztec/validator-client';
import { MerkleTrees, type WorldStateSynchronizer, createWorldStateSynchronizer } from '@aztec/world-state';
import { MerkleTrees, createWorldStateSynchronizer } from '@aztec/world-state';

import { type AztecNodeConfig, getPackageInfo } from './config.js';
import { NodeMetrics } from './node_metrics.js';
import { TxProofValidator } from './tx_validator/tx_proof_validator.js';

/**
* The aztec node.
Expand Down Expand Up @@ -150,18 +153,21 @@ export class AztecNodeService implements AztecNode {
// this may well change in future
config.transactionProtocol = `/aztec/tx/${config.l1Contracts.rollupAddress.toString()}`;

// now create the merkle trees and the world state synchronizer
const worldStateSynchronizer = await createWorldStateSynchronizer(config, store, archiver, telemetry);

// create the tx pool and the p2p client, which will need the l2 block source
const p2pClient = await createP2PClient(
config,
store,
new AztecKVTxPool(store, telemetry),
new InMemoryAttestationPool(),
archiver,
new GlobalVariableBuilder(config),
new TestCircuitVerifier(),
worldStateSynchronizer,
Copy link
Contributor

Choose a reason for hiding this comment

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

Rather than passing all the deps for the TxValidators across the p2p client and lib p2p service, and creating a new TxValidator for every tx, I'd change it so this call accepts a TxValidator (or a TxValidatorFactory if needed) directly. So when we add new validations with new deps we don't need to keep changing the entire call chain.

Copy link
Member Author

Choose a reason for hiding this comment

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

I started writing it this way actually!

Issue is we sort of need to know which specific validation fails or not in order to then update peer scores based on the result

);

// now create the merkle trees and the world state synchronizer
const worldStateSynchronizer = await createWorldStateSynchronizer(config, store, archiver, telemetry);

// start both and wait for them to sync from the block source
await Promise.all([p2pClient.start(), worldStateSynchronizer.start()]);

Expand Down
21 changes: 21 additions & 0 deletions yarn-project/circuit-types/src/global_variable_builder.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import type { AztecAddress, EthAddress, Fr, GlobalVariables } from '@aztec/circuits.js';

/**
* Interface for building global variables for Aztec blocks.
*/
export interface GlobalVariableBuilder {
/**
* Builds global variables for a given block.
* @param blockNumber - The block number to build global variables for.
* @param coinbase - The address to receive block reward.
* @param feeRecipient - The address to receive fees.
* @param slotNumber - Optional. The slot number to use for the global variables. If undefined, it will be calculated.
* @returns A promise that resolves to the GlobalVariables for the given block number.
*/
buildGlobalVariables(
blockNumber: Fr,
coinbase: EthAddress,
feeRecipient: AztecAddress,
slotNumber?: bigint,
): Promise<GlobalVariables>;
}
1 change: 1 addition & 0 deletions yarn-project/circuit-types/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,3 +22,4 @@ export * from './tx/index.js';
export * from './tx_effect.js';
export * from './tx_execution_request.js';
export * from './p2p/index.js';
export * from './global_variable_builder.js';
1 change: 1 addition & 0 deletions yarn-project/circuit-types/src/interfaces/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,4 @@ export * from './server_circuit_prover.js';
export * from './private_kernel_prover.js';
export * from './tx-provider.js';
export * from './merkle_tree_operations.js';
export * from './world_state.js';
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { type MerkleTreeOperations } from '../world-state-db/index.js';
import { type MerkleTreeOperations } from './merkle_tree_operations.js';

/**
* Defines the possible states of the world state synchronizer.
Expand Down
1 change: 1 addition & 0 deletions yarn-project/p2p/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@
"@aztec/circuits.js": "workspace:^",
"@aztec/foundation": "workspace:^",
"@aztec/kv-store": "workspace:^",
"@aztec/simulator": "workspace:^",
Copy link
Collaborator

Choose a reason for hiding this comment

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

What does it need the simulator for?

Copy link
Member Author

Choose a reason for hiding this comment

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

WorldStateDB is required for the double spend validator

Copy link
Collaborator

Choose a reason for hiding this comment

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

Hmm, no it needs a NullifierSource which can be built in a much more lightweight fashion.

NullifierSource defines a single function getNullifierIndex which can be implemented by simply calling findLeafIndex on an instance of MerkleTreeOperations. No need to pull in the complete simulator here.

"@aztec/telemetry-client": "workspace:^",
"@chainsafe/discv5": "9.0.0",
"@chainsafe/enr": "3.0.0",
Expand Down
118 changes: 71 additions & 47 deletions yarn-project/p2p/src/client/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,9 @@
import { type L2BlockSource } from '@aztec/circuit-types';
import type {
ClientProtocolCircuitVerifier,
GlobalVariableBuilder,
L2BlockSource,
WorldStateSynchronizer,
} from '@aztec/circuit-types';
import { type AztecKVStore } from '@aztec/kv-store';

import { type AttestationPool } from '../attestation_pool/attestation_pool.js';
Expand All @@ -13,66 +18,85 @@ import { getPublicIp, resolveAddressIfNecessary, splitAddressPort } from '../uti
export * from './p2p_client.js';

export const createP2PClient = async (
config: P2PConfig,
_config: P2PConfig,
store: AztecKVStore,
txPool: TxPool,
attestationsPool: AttestationPool,
l2BlockSource: L2BlockSource,
globalVariableBuilder: GlobalVariableBuilder,
proofVerifier: ClientProtocolCircuitVerifier,
worldStateSynchronizer: WorldStateSynchronizer,
) => {
let p2pService;
let config = _config;

if (config.p2pEnabled) {
// If announceTcpAddress or announceUdpAddress are not provided, query for public IP if config allows

const {
tcpAnnounceAddress: configTcpAnnounceAddress,
udpAnnounceAddress: configUdpAnnounceAddress,
queryForIp,
} = config;

config.tcpAnnounceAddress = configTcpAnnounceAddress
? await resolveAddressIfNecessary(configTcpAnnounceAddress)
: undefined;
config.udpAnnounceAddress = configUdpAnnounceAddress
? await resolveAddressIfNecessary(configUdpAnnounceAddress)
: undefined;

// create variable for re-use if needed
let publicIp;

// check if no announce IP was provided
const splitTcpAnnounceAddress = splitAddressPort(configTcpAnnounceAddress || '', true);
if (splitTcpAnnounceAddress.length == 2 && splitTcpAnnounceAddress[0] === '') {
if (queryForIp) {
publicIp = await getPublicIp();
const tcpAnnounceAddress = `${publicIp}:${splitTcpAnnounceAddress[1]}`;
config.tcpAnnounceAddress = tcpAnnounceAddress;
} else {
throw new Error(
`Invalid announceTcpAddress provided: ${configTcpAnnounceAddress}. Expected format: <addr>:<port>`,
);
}
}

const splitUdpAnnounceAddress = splitAddressPort(configUdpAnnounceAddress || '', true);
if (splitUdpAnnounceAddress.length == 2 && splitUdpAnnounceAddress[0] === '') {
// If announceUdpAddress is not provided, use announceTcpAddress
if (!queryForIp && config.tcpAnnounceAddress) {
config.udpAnnounceAddress = config.tcpAnnounceAddress;
} else if (queryForIp) {
const udpPublicIp = publicIp || (await getPublicIp());
const udpAnnounceAddress = `${udpPublicIp}:${splitUdpAnnounceAddress[1]}`;
config.udpAnnounceAddress = udpAnnounceAddress;
}
}
if (_config.p2pEnabled) {
config = await configureP2PClientAddresses(_config);

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

p2pService = await LibP2PService.new(config, discoveryService, peerId, txPool, attestationsPool, store);
p2pService = await LibP2PService.new(
config,
discoveryService,
peerId,
txPool,
attestationsPool,
l2BlockSource,
globalVariableBuilder,
proofVerifier,
worldStateSynchronizer,
store,
);
} else {
p2pService = new DummyP2PService();
}
return new P2PClient(store, l2BlockSource, txPool, attestationsPool, p2pService, config.keepProvenTxsInPoolFor);
};

async function configureP2PClientAddresses(config: P2PConfig) {
const {
tcpAnnounceAddress: configTcpAnnounceAddress,
udpAnnounceAddress: configUdpAnnounceAddress,
queryForIp,
} = config;

config.tcpAnnounceAddress = configTcpAnnounceAddress
? await resolveAddressIfNecessary(configTcpAnnounceAddress)
: undefined;
config.udpAnnounceAddress = configUdpAnnounceAddress
? await resolveAddressIfNecessary(configUdpAnnounceAddress)
: undefined;

// create variable for re-use if needed
let publicIp;

// check if no announce IP was provided
const splitTcpAnnounceAddress = splitAddressPort(configTcpAnnounceAddress || '', true);
if (splitTcpAnnounceAddress.length == 2 && splitTcpAnnounceAddress[0] === '') {
if (queryForIp) {
publicIp = await getPublicIp();
const tcpAnnounceAddress = `${publicIp}:${splitTcpAnnounceAddress[1]}`;
config.tcpAnnounceAddress = tcpAnnounceAddress;
} else {
throw new Error(
`Invalid announceTcpAddress provided: ${configTcpAnnounceAddress}. Expected format: <addr>:<port>`,
);
}
}

const splitUdpAnnounceAddress = splitAddressPort(configUdpAnnounceAddress || '', true);
if (splitUdpAnnounceAddress.length == 2 && splitUdpAnnounceAddress[0] === '') {
// If announceUdpAddress is not provided, use announceTcpAddress
if (!queryForIp && config.tcpAnnounceAddress) {
config.udpAnnounceAddress = config.tcpAnnounceAddress;
} else if (queryForIp) {
const udpPublicIp = publicIp || (await getPublicIp());
const udpAnnounceAddress = `${udpPublicIp}:${splitUdpAnnounceAddress[1]}`;
config.udpAnnounceAddress = udpAnnounceAddress;
}
}

return config;
}
1 change: 1 addition & 0 deletions yarn-project/p2p/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,4 @@ export * from './tx_pool/index.js';
export * from './attestation_pool/index.js';
export * from './service/index.js';
export * from './bootstrap/bootstrap.js';
export * from './tx_validator/index.js';
Loading
Loading