diff --git a/.circleci/config.yml b/.circleci/config.yml index 150e60ad449..812255047dc 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -286,6 +286,17 @@ jobs: name: "Build and test" command: build p2p + p2p-bootstrap: + machine: + image: ubuntu-2004:202010-01 + resource_class: large + steps: + - *checkout + - *setup_env + - run: + name: "Build and test" + command: build p2p-bootstrap + acir-simulator: machine: image: ubuntu-2004:202010-01 @@ -352,6 +363,28 @@ jobs: name: "Build" command: build end-to-end + ethereum: + machine: + image: ubuntu-2004:202010-01 + resource_class: large + steps: + - *checkout + - *setup_env + - run: + name: "Build" + command: build ethereum + + rollup-provider: + machine: + image: ubuntu-2004:202010-01 + resource_class: large + steps: + - *checkout + - *setup_env + - run: + name: "Build" + command: build rollup-provider + e2e-deploy-contract: docker: - image: aztecprotocol/alpine-build-image @@ -429,6 +462,17 @@ jobs: name: "Test" command: cond_spot_run_tests end-to-end e2e_public_token_contract.test.ts + e2e-p2p: + docker: + - image: aztecprotocol/alpine-build-image + resource_class: small + steps: + - *checkout + - *setup_env + - run: + name: "Test" + command: cond_spot_run_tests end-to-end e2e_p2p_network.test.ts + e2e-join: docker: - image: cimg/base:current @@ -512,6 +556,7 @@ workflows: - aztec-js: *yarn_project - end-to-end: *yarn_project + - ethereum: *yarn_project - foundation: *yarn_project - world-state: *yarn_project - acir-simulator: *yarn_project @@ -520,16 +565,19 @@ workflows: - barretenberg-js: *yarn_project - merkle-tree: *yarn_project - p2p: *yarn_project + - p2p-bootstrap: *yarn_project - noir-contracts: *yarn_project - noir-compiler: *yarn_project - sequencer-client: *yarn_project - types: *yarn_project - circuits-js: *yarn_project + - rollup-provider: *yarn_project - e2e-join: requires: - aztec-js - end-to-end + - ethereum - foundation - world-state - acir-simulator @@ -538,11 +586,13 @@ workflows: - barretenberg-js - merkle-tree - p2p + - p2p-bootstrap - noir-contracts - noir-compiler - sequencer-client - types - circuits-js + - rollup-provider <<: *defaults - e2e-deploy-contract: *e2e_test @@ -552,6 +602,7 @@ workflows: - e2e-public-token-contract: *e2e_test - e2e-l2-to-l1-messaging: *e2e_test - integration-l1-publisher: *e2e_test + - e2e-p2p: *e2e_test - e2e-end: requires: @@ -562,4 +613,5 @@ workflows: - e2e-public-token-contract - e2e-l2-to-l1-messaging - integration-l1-publisher + - e2e-p2p <<: *defaults diff --git a/build_manifest.json b/build_manifest.json index 18aa18b408f..bd06955a8f7 100644 --- a/build_manifest.json +++ b/build_manifest.json @@ -104,6 +104,7 @@ "^yarn-project/archiver/" ], "dependencies": [ + "ethereum", "foundation", "l1-artifacts", "types" @@ -117,7 +118,12 @@ "^yarn-project/aztec-cli/" ], "dependencies": [ - "foundation" + "aztec-node", + "aztec.js", + "ethereum", + "foundation", + "l1-artifacts", + "noir-contracts" ] }, "aztec-rpc": { @@ -189,13 +195,27 @@ "aztec-node", "aztec.js", "circuits.js", + "ethereum", "foundation", "l1-artifacts", "noir-contracts", + "p2p", "sequencer-client", "world-state" ] }, + "ethereum": { + "buildDir": "yarn-project", + "projectDir": "yarn-project/ethereum", + "dockerfile": "ethereum/Dockerfile", + "rebuildPatterns": [ + "^yarn-project/ethereum/" + ], + "dependencies": [ + "foundation", + "l1-artifacts" + ] + }, "foundation": { "buildDir": "yarn-project", "projectDir": "yarn-project/foundation", @@ -265,6 +285,17 @@ "types" ] }, + "p2p-bootstrap": { + "buildDir": "yarn-project", + "projectDir": "yarn-project/p2p-bootstrap", + "dockerfile": "p2p/Dockerfile", + "rebuildPatterns": [ + "^yarn-project/p2p-bootstrap/" + ], + "dependencies": [ + "p2p" + ] + }, "prover-client": { "buildDir": "yarn-project", "projectDir": "yarn-project/prover-client", @@ -276,6 +307,17 @@ "foundation" ] }, + "rollup-provider": { + "buildDir": "yarn-project", + "projectDir": "yarn-project/rollup-provider", + "dockerfile": "rollup-provider/Dockerfile", + "rebuildPatterns": [ + "^yarn-project/rollup-provider/" + ], + "dependencies": [ + "aztec-node" + ] + }, "aztec-node": { "buildDir": "yarn-project", "projectDir": "yarn-project/aztec-node", @@ -305,6 +347,7 @@ "dependencies": [ "acir-simulator", "circuits.js", + "ethereum", "foundation", "l1-artifacts", "merkle-tree", diff --git a/circuits/cpp/src/aztec3/circuits/abis/private_kernel/private_call_data.hpp b/circuits/cpp/src/aztec3/circuits/abis/private_kernel/private_call_data.hpp index c7d37b0e2ab..05df82cca51 100644 --- a/circuits/cpp/src/aztec3/circuits/abis/private_kernel/private_call_data.hpp +++ b/circuits/cpp/src/aztec3/circuits/abis/private_kernel/private_call_data.hpp @@ -134,7 +134,7 @@ template std::ostream& operator<<(std::ostream& os, PrivateCallDa // specialize the name in msgpack schema generation // consumed by the typescript schema compiler, helps disambiguate templates template inline std::string msgpack_schema_name( - aztec3::circuits::abis::CallStackItem const&) // NOLINT + aztec3::circuits::abis::CallStackItem const&) // NOLINT { return "PrivateCallStackItem"; } diff --git a/circuits/cpp/src/aztec3/circuits/abis/public_kernel/public_call_data.hpp b/circuits/cpp/src/aztec3/circuits/abis/public_kernel/public_call_data.hpp index 8e4a964e9d7..f205f440cde 100644 --- a/circuits/cpp/src/aztec3/circuits/abis/public_kernel/public_call_data.hpp +++ b/circuits/cpp/src/aztec3/circuits/abis/public_kernel/public_call_data.hpp @@ -114,7 +114,7 @@ template std::ostream& operator<<(std::ostream& os, PublicCallDat // specialize the name in msgpack schema generation // consumed by the typescript schema compiler, helps disambiguate templates template inline std::string msgpack_schema_name( - aztec3::circuits::abis::CallStackItem const&) // NOLINT + aztec3::circuits::abis::CallStackItem const&) // NOLINT { return "PublicCallStackItem"; } diff --git a/circuits/cpp/src/aztec3/circuits/rollup/base/native_base_rollup_circuit.cpp b/circuits/cpp/src/aztec3/circuits/rollup/base/native_base_rollup_circuit.cpp index 39a67e1fd15..57168b00470 100644 --- a/circuits/cpp/src/aztec3/circuits/rollup/base/native_base_rollup_circuit.cpp +++ b/circuits/cpp/src/aztec3/circuits/rollup/base/native_base_rollup_circuit.cpp @@ -161,7 +161,7 @@ void perform_historical_contract_data_tree_membership_checks(DummyComposer& comp NT::fr const leaf = baseRollupInputs.kernel_data[i] .public_inputs.constants.historic_tree_roots.private_historic_tree_roots.contract_tree_root; - abis::MembershipWitness const historic_root_witness = + abis::MembershipWitness const historic_root_witness = baseRollupInputs.historic_contract_tree_root_membership_witnesses[i]; check_membership(composer, @@ -182,7 +182,7 @@ void perform_historical_l1_to_l2_message_tree_membership_checks(DummyComposer& c NT::fr const leaf = baseRollupInputs.kernel_data[i] .public_inputs.constants.historic_tree_roots.private_historic_tree_roots.l1_to_l2_messages_tree_root; - abis::MembershipWitness const historic_root_witness = + abis::MembershipWitness const historic_root_witness = baseRollupInputs.historic_l1_to_l2_msg_tree_root_membership_witnesses[i]; check_membership(composer, diff --git a/circuits/cpp/src/aztec3/circuits/rollup/test_utils/utils.cpp b/circuits/cpp/src/aztec3/circuits/rollup/test_utils/utils.cpp index daf2fe4b2c4..bfc2855a8a7 100644 --- a/circuits/cpp/src/aztec3/circuits/rollup/test_utils/utils.cpp +++ b/circuits/cpp/src/aztec3/circuits/rollup/test_utils/utils.cpp @@ -327,14 +327,16 @@ RootRollupInputs get_root_rollup_inputs(utils::DummyComposer& composer, historic_contract_tree.update_element(0, contract_tree.root()); historic_l1_to_l2_msg_tree.update_element(0, l1_to_l2_msg_tree.root()); + // Historic trees sibling paths auto historic_data_sibling_path = get_sibling_path(historic_private_data_tree, 1, 0); auto historic_contract_sibling_path = get_sibling_path(historic_contract_tree, 1, 0); auto historic_l1_to_l2_msg_sibling_path = get_sibling_path(historic_l1_to_l2_msg_tree, 1, 0); - auto l1_to_l2_tree_sibling_path = get_sibling_path( - l1_to_l2_msg_tree, 0, L1_TO_L2_MSG_SUBTREE_INCLUSION_CHECK_DEPTH); + // l1 to l2 tree + auto l1_to_l2_tree_sibling_path = + get_sibling_path(l1_to_l2_msg_tree, 0, L1_TO_L2_MSG_SUBTREE_DEPTH); // l1_to_l2_message tree snapshots AppendOnlyTreeSnapshot const start_l1_to_l2_msg_tree_snapshot = { diff --git a/circuits/cpp/src/aztec3/constants.hpp b/circuits/cpp/src/aztec3/constants.hpp index e1379d30710..5cb23cdb090 100644 --- a/circuits/cpp/src/aztec3/constants.hpp +++ b/circuits/cpp/src/aztec3/constants.hpp @@ -27,7 +27,7 @@ constexpr size_t KERNEL_PUBLIC_DATA_READS_LENGTH = 4; constexpr size_t VK_TREE_HEIGHT = 3; constexpr size_t FUNCTION_TREE_HEIGHT = 4; -constexpr size_t CONTRACT_TREE_HEIGHT = 4; +constexpr size_t CONTRACT_TREE_HEIGHT = 8; constexpr size_t PRIVATE_DATA_TREE_HEIGHT = 8; constexpr size_t NULLIFIER_TREE_HEIGHT = 8; constexpr size_t PUBLIC_DATA_TREE_HEIGHT = 254; diff --git a/circuits/cpp/src/aztec3/utils/msgpack_derived_equals.hpp b/circuits/cpp/src/aztec3/utils/msgpack_derived_equals.hpp index 4d494106450..8a91090a411 100644 --- a/circuits/cpp/src/aztec3/utils/msgpack_derived_equals.hpp +++ b/circuits/cpp/src/aztec3/utils/msgpack_derived_equals.hpp @@ -4,7 +4,7 @@ namespace aztec3::utils { // Auxiliary function for comparison of elements from tuples template -auto _compare_msgpack_tuples(const Tuple1& t1, const Tuple2& t2, std::index_sequence) // NOLINT +auto _compare_msgpack_tuples(const Tuple1& t1, const Tuple2& t2, std::index_sequence) // NOLINT { // Compare every 2nd value from our key1, value1, key2, value2 list return ((std::get(t1) == std::get(t2)) && ...); @@ -17,8 +17,8 @@ template BoolLike msgpack_derived_equals(const T { BoolLike are_equal; // De-serialize objects to alternating key-value tuples and compare the values - const_cast(obj1).msgpack([&](auto&... args1) { // NOLINT - const_cast(obj2).msgpack([&](auto&... args2) { // NOLINT + const_cast(obj1).msgpack([&](auto&... args1) { // NOLINT + const_cast(obj2).msgpack([&](auto&... args2) { // NOLINT auto tuple1 = std::tie(args1...); auto tuple2 = std::tie(args2...); are_equal = _compare_msgpack_tuples(tuple1, tuple2, std::make_index_sequence{}); diff --git a/circuits/cpp/src/aztec3/utils/msgpack_derived_output.hpp b/circuits/cpp/src/aztec3/utils/msgpack_derived_output.hpp index 88353ea1400..524a0ff7aae 100644 --- a/circuits/cpp/src/aztec3/utils/msgpack_derived_output.hpp +++ b/circuits/cpp/src/aztec3/utils/msgpack_derived_output.hpp @@ -22,12 +22,12 @@ inline void _msgpack_derived_output_helper(std::ostream& os, os << key << ": "; msgpack_derived_output(os, value); os << '\n'; - _msgpack_derived_output_helper(os, rest...); // NOLINT + _msgpack_derived_output_helper(os, rest...); // NOLINT } // Specialization if we have msgpack template void msgpack_derived_output(std::ostream& os, const T& value) { - const_cast(value).msgpack([&](auto&... args) { _msgpack_derived_output_helper(os, args...); }); // NOLINT + const_cast(value).msgpack([&](auto&... args) { _msgpack_derived_output_helper(os, args...); }); // NOLINT } // Otherwise diff --git a/l1-contracts/src/periphery/UnverifiedDataEmitter.sol b/l1-contracts/src/periphery/UnverifiedDataEmitter.sol index 936f7ba67dd..be717c96396 100644 --- a/l1-contracts/src/periphery/UnverifiedDataEmitter.sol +++ b/l1-contracts/src/periphery/UnverifiedDataEmitter.sol @@ -15,13 +15,14 @@ contract UnverifiedDataEmitter is IUnverifiedDataEmitter { * @notice Logs data on chain * @dev Emits an `UnverifiedData` event * @param _l2BlockNum - The l2 block number that the unverified data is related to + * @param _l2BlockHash - The hash of the L2 block that this is related to * @param _data - Raw data to share */ - function emitUnverifiedData(uint256 _l2BlockNum, bytes calldata _data) + function emitUnverifiedData(uint256 _l2BlockNum, bytes32 _l2BlockHash, bytes calldata _data) external override(IUnverifiedDataEmitter) { - emit UnverifiedData(_l2BlockNum, msg.sender, _data); + emit UnverifiedData(_l2BlockNum, msg.sender, _l2BlockHash, _data); } /** @@ -31,14 +32,16 @@ contract UnverifiedDataEmitter is IUnverifiedDataEmitter { * @param _l2BlockNum - The L2 block number that the contract deployment is related to * @param _aztecAddress - The address of the L2 counterparty * @param _portalAddress - The address of the L1 counterparty + * @param _l2BlockHash - The hash of the L2 block that this is related to * @param _acir - The acir bytecode of the L2 contract */ function emitContractDeployment( uint256 _l2BlockNum, bytes32 _aztecAddress, address _portalAddress, + bytes32 _l2BlockHash, bytes calldata _acir ) external override(IUnverifiedDataEmitter) { - emit ContractDeployment(_l2BlockNum, _aztecAddress, _portalAddress, _acir); + emit ContractDeployment(_l2BlockNum, _aztecAddress, _portalAddress, _l2BlockHash, _acir); } } diff --git a/l1-contracts/src/periphery/interfaces/IUnverifiedDataEmitter.sol b/l1-contracts/src/periphery/interfaces/IUnverifiedDataEmitter.sol index 7db6216f2ae..0f067f65d32 100644 --- a/l1-contracts/src/periphery/interfaces/IUnverifiedDataEmitter.sol +++ b/l1-contracts/src/periphery/interfaces/IUnverifiedDataEmitter.sol @@ -15,12 +15,14 @@ interface IUnverifiedDataEmitter { * @param l2BlockNum - The L2 block number that the information is related to * @param aztecAddress - The address of the L2 counterparty * @param portalAddress - The address of the L1 counterparty + * @param l2BlockHash - The hash of the L2 block that this is related to * @param acir - The acir bytecode of the L2 contract */ event ContractDeployment( uint256 indexed l2BlockNum, bytes32 indexed aztecAddress, address indexed portalAddress, + bytes32 l2BlockHash, bytes acir ); @@ -28,17 +30,22 @@ interface IUnverifiedDataEmitter { * @notice Used to share data which are not required to advance the state but are needed for other purposes * @param l2BlockNum - The L2 block number that the information is related to * @param sender - The address of the account sharing the information + * @param l2BlockHash - The hash of the L2 block that this is related to. * @param data - The information represented as raw bytes * @dev Typically contains `TxAuxData` (preimage, contract address and contract slot) */ - event UnverifiedData(uint256 indexed l2BlockNum, address indexed sender, bytes data); + event UnverifiedData( + uint256 indexed l2BlockNum, address indexed sender, bytes32 indexed l2BlockHash, bytes data + ); - function emitUnverifiedData(uint256 _l2BlockNum, bytes calldata _data) external; + function emitUnverifiedData(uint256 _l2BlockNum, bytes32 _l2BlockHash, bytes calldata _data) + external; function emitContractDeployment( uint256 _l2BlockNum, bytes32 _aztecAddress, address _portalAddress, + bytes32 _l2BlockHash, bytes calldata _acir ) external; } diff --git a/yarn-project/archiver/package.json b/yarn-project/archiver/package.json index 5487bacdcc5..3d9d76185be 100644 --- a/yarn-project/archiver/package.json +++ b/yarn-project/archiver/package.json @@ -34,13 +34,14 @@ "rootDir": "./src" }, "dependencies": { + "@aztec/ethereum": "workspace:^", "@aztec/foundation": "workspace:^", "@aztec/l1-artifacts": "workspace:^", "@aztec/types": "workspace:^", "debug": "^4.3.4", "tsc-watch": "^6.0.0", "tslib": "^2.5.0", - "viem": "^0.3.13", + "viem": "^0.3.14", "ws": "^8.13.0" }, "devDependencies": { diff --git a/yarn-project/archiver/src/archiver/archiver.test.ts b/yarn-project/archiver/src/archiver/archiver.test.ts index d28e5118ad3..da46a062d0c 100644 --- a/yarn-project/archiver/src/archiver/archiver.test.ts +++ b/yarn-project/archiver/src/archiver/archiver.test.ts @@ -28,6 +28,7 @@ describe('Archiver', () => { EthAddress.fromString(rollupAddress), EthAddress.fromString(inboxAddress), EthAddress.fromString(unverifiedDataEmitterAddress), + 0, archiverStore, 1000, ); @@ -37,18 +38,19 @@ describe('Archiver', () => { let latestUnverifiedDataBlockNum = await archiver.getLatestUnverifiedDataBlockNum(); expect(latestUnverifiedDataBlockNum).toEqual(0); - const rollupTxs = [1, 2, 3].map(makeRollupTx); + const blocks = [1, 2, 3].map(x => L2Block.random(x)); + const rollupTxs = blocks.map(makeRollupTx); publicClient.getBlockNumber.mockResolvedValue(2500n); // logs should be created in order of how archiver syncs. publicClient.getLogs .mockResolvedValueOnce([makeL2BlockProcessedEvent(100n, 1n)]) - .mockResolvedValueOnce([makeUnverifiedDataEvent(102n, 1n)]) - .mockResolvedValueOnce([makeContractDeployedEvent(104n, 1n)]) + .mockResolvedValueOnce([makeUnverifiedDataEvent(102n, blocks[0])]) + .mockResolvedValueOnce([makeContractDeployedEvent(104n, blocks[0])]) .mockResolvedValueOnce([makeL1ToL2MessageAddedEvent(101n)]) .mockResolvedValueOnce([makeL2BlockProcessedEvent(1100n, 2n), makeL2BlockProcessedEvent(1150n, 3n)]) - .mockResolvedValueOnce([makeUnverifiedDataEvent(1100n, 2n)]) - .mockResolvedValueOnce([makeContractDeployedEvent(1102n, 2n)]) + .mockResolvedValueOnce([makeUnverifiedDataEvent(1100n, blocks[1])]) + .mockResolvedValueOnce([makeContractDeployedEvent(1102n, blocks[1])]) .mockResolvedValueOnce([makeL1ToL2MessageAddedEvent(1101n)]) .mockResolvedValue([]); rollupTxs.forEach(tx => publicClient.getTransaction.mockResolvedValueOnce(tx)); @@ -95,28 +97,29 @@ function makeL2BlockProcessedEvent(l1BlockNum: bigint, l2BlockNum: bigint) { /** * Makes a fake UnverifiedData event for testing purposes. * @param l1BlockNum - L1 block number. - * @param l2BlockNum - L2Block number. + * @param l2Block - The l2Block this event is associated with. * @returns An UnverifiedData event log. */ -function makeUnverifiedDataEvent(l1BlockNum: bigint, l2BlockNum: bigint) { +function makeUnverifiedDataEvent(l1BlockNum: bigint, l2Block: L2Block) { return { blockNumber: l1BlockNum, args: { - l2BlockNum, + l2BlockNum: BigInt(l2Block.number), + l2BlockHash: `0x${l2Block.getCalldataHash().toString('hex')}`, sender: EthAddress.random().toString(), data: '0x' + createRandomUnverifiedData(16).toString('hex'), }, - transactionHash: `0x${l2BlockNum}`, + transactionHash: `0x${l2Block.number}`, } as Log; } /** * Makes a fake ContractDeployed event for testing purposes. * @param l1BlockNum - L1 block number. - * @param l2BlockNum - L2Block number. + * @param l2Block - The l2Block this event is associated with. * @returns An UnverifiedData event log. */ -function makeContractDeployedEvent(l1BlockNum: bigint, l2BlockNum: bigint) { +function makeContractDeployedEvent(l1BlockNum: bigint, l2Block: L2Block) { // const contractData = ContractData.random(); const aztecAddress = AztecAddress.random(); const portalAddress = EthAddress.random(); @@ -128,12 +131,13 @@ function makeContractDeployedEvent(l1BlockNum: bigint, l2BlockNum: bigint) { return { blockNumber: l1BlockNum, args: { - l2BlockNum, + l2BlockNum: BigInt(l2Block.number), aztecAddress: aztecAddress.toString(), portalAddress: portalAddress.toString(), + l2BlockHash: `0x${l2Block.getCalldataHash().toString('hex')}`, acir: '0x' + acir, }, - transactionHash: `0x${l2BlockNum}`, + transactionHash: `0x${l2Block.number}`, } as Log; } @@ -162,12 +166,12 @@ function makeL1ToL2MessageAddedEvent(l1BlockNum: bigint) { /** * Makes a fake rollup tx for testing purposes. - * @param blockNum - L2Block number. + * @param block - The L2Block. * @returns A fake tx with calldata that corresponds to calling process in the Rollup contract. */ -function makeRollupTx(blockNum: number) { +function makeRollupTx(l2Block: L2Block) { const proof = `0x`; - const block = toHex(L2Block.random(blockNum).encode()); + const block = toHex(l2Block.encode()); const input = encodeFunctionData({ abi: RollupAbi, functionName: 'process', args: [proof, block] }); return { input } as Transaction; } diff --git a/yarn-project/archiver/src/archiver/archiver.ts b/yarn-project/archiver/src/archiver/archiver.ts index 2b8e9aa049e..d95b84d368b 100644 --- a/yarn-project/archiver/src/archiver/archiver.ts +++ b/yarn-project/archiver/src/archiver/archiver.ts @@ -14,8 +14,8 @@ import { UnverifiedDataSource, } from '@aztec/types'; import { Chain, HttpTransport, PublicClient, createPublicClient, http } from 'viem'; -import { localhost } from 'viem/chains'; import { ArchiverConfig } from './config.js'; +import { createEthereumChain } from '@aztec/ethereum'; import { retrieveBlocks, retrieveNewContractData, @@ -46,6 +46,7 @@ export class Archiver implements L2BlockSource, UnverifiedDataSource, ContractDa * @param rollupAddress - Ethereum address of the rollup contract. * @param inboxAddress - Ethereum address of the inbox contract. * @param unverifiedDataEmitterAddress - Ethereum address of the unverifiedDataEmitter contract. + * @param searchStartBlock - The eth block from which to start searching for new blocks. * @param pollingIntervalMs - The interval for polling for rollup logs (in milliseconds). * @param store - An archiver data store for storage & retrieval of blocks, unverified data & contract data. * @param log - A logger. @@ -55,10 +56,13 @@ export class Archiver implements L2BlockSource, UnverifiedDataSource, ContractDa private readonly rollupAddress: EthAddress, private readonly inboxAddress: EthAddress, private readonly unverifiedDataEmitterAddress: EthAddress, + searchStartBlock: number, private readonly store: ArchiverDataStore, private readonly pollingIntervalMs = 10_000, private readonly log: DebugLogger = createDebugLogger('aztec:archiver'), - ) {} + ) { + this.nextL2BlockFromBlock = BigInt(searchStartBlock); + } /** * Creates a new instance of the Archiver and blocks until it syncs from chain. @@ -67,9 +71,10 @@ export class Archiver implements L2BlockSource, UnverifiedDataSource, ContractDa * @returns - An instance of the archiver. */ public static async createAndSync(config: ArchiverConfig, blockUntilSynced = true): Promise { + const chain = createEthereumChain(config.rpcUrl, config.apiKey); const publicClient = createPublicClient({ - chain: localhost, - transport: http(config.rpcUrl), + chain: chain.chainInfo, + transport: http(chain.rpcUrl), }); const archiverStore = new MemoryArchiverStore(); const archiver = new Archiver( @@ -77,6 +82,7 @@ export class Archiver implements L2BlockSource, UnverifiedDataSource, ContractDa config.rollupContract, config.inboxContract, config.unverifiedDataEmitterContract, + config.searchStartBlock, archiverStore, config.archiverPollingInterval, ); @@ -94,6 +100,7 @@ export class Archiver implements L2BlockSource, UnverifiedDataSource, ContractDa } if (blockUntilSynced) { + this.log(`Performing initial chain sync...`); await this.sync(blockUntilSynced); } @@ -123,6 +130,12 @@ export class Archiver implements L2BlockSource, UnverifiedDataSource, ContractDa this.nextL2BlockFromBlock, nextExpectedRollupId, ); + + // create the block number -> block hash mapping to ensure we retrieve the appropriate events + const blockHashMapping: { [key: number]: Buffer | undefined } = {}; + retrievedBlocks.retrievedData.forEach((block: L2Block) => { + blockHashMapping[block.number] = block.getCalldataHash(); + }); const retrievedUnverifiedData = await retrieveUnverifiedData( this.publicClient, this.unverifiedDataEmitterAddress, @@ -130,6 +143,7 @@ export class Archiver implements L2BlockSource, UnverifiedDataSource, ContractDa currentBlockNumber, this.nextL2BlockFromBlock, nextExpectedRollupId, + blockHashMapping, ); const retrievedContracts = await retrieveNewContractData( this.publicClient, @@ -137,6 +151,7 @@ export class Archiver implements L2BlockSource, UnverifiedDataSource, ContractDa blockUntilSynced, currentBlockNumber, this.nextL2BlockFromBlock, + blockHashMapping, ); const retrievedPendingL1ToL2Messages = await retrieveNewPendingL1ToL2Messages( this.publicClient, diff --git a/yarn-project/archiver/src/archiver/config.ts b/yarn-project/archiver/src/archiver/config.ts index 4a6bd936c6a..0d2bc570432 100644 --- a/yarn-project/archiver/src/archiver/config.ts +++ b/yarn-project/archiver/src/archiver/config.ts @@ -10,10 +10,20 @@ export interface ArchiverConfig extends L1Addresses { */ rpcUrl: string; + /** + * The key for the ethereum node. + */ + apiKey?: string; + /** * The polling interval in ms for retrieving new L2 blocks and unverified data. */ archiverPollingInterval?: number; + + /** + * Eth block from which we start scanning for L2Blocks. + */ + searchStartBlock: number; } /** @@ -26,8 +36,10 @@ export function getConfigEnvVars(): ArchiverConfig { ETHEREUM_HOST, ARCHIVER_POLLING_INTERVAL, ROLLUP_CONTRACT_ADDRESS, - INBOX_CONTRACT_ADDRESS, UNVERIFIED_DATA_EMITTER_ADDRESS, + SEARCH_START_BLOCK, + API_KEY, + INBOX_CONTRACT_ADDRESS, } = process.env; return { rpcUrl: ETHEREUM_HOST || 'http://127.0.0.1:8545/', @@ -37,5 +49,7 @@ export function getConfigEnvVars(): ArchiverConfig { unverifiedDataEmitterContract: UNVERIFIED_DATA_EMITTER_ADDRESS ? EthAddress.fromString(UNVERIFIED_DATA_EMITTER_ADDRESS) : EthAddress.ZERO, + searchStartBlock: SEARCH_START_BLOCK ? +SEARCH_START_BLOCK : 0, + apiKey: API_KEY, }; } diff --git a/yarn-project/archiver/src/archiver/data_retrieval.ts b/yarn-project/archiver/src/archiver/data_retrieval.ts index 174e38454b1..c3493090002 100644 --- a/yarn-project/archiver/src/archiver/data_retrieval.ts +++ b/yarn-project/archiver/src/archiver/data_retrieval.ts @@ -49,9 +49,7 @@ export async function retrieveBlocks( if (searchStartBlock > currentBlockNumber) { break; } - const l2BlockProcessedLogs = await getL2BlockProcessedLogs(publicClient, rollupAddress, searchStartBlock); - if (l2BlockProcessedLogs.length === 0) { break; } @@ -72,6 +70,7 @@ export async function retrieveBlocks( * @param currentBlockNumber - Latest available block number in the ETH node. * @param searchStartBlock - The block number to use for starting the search. * @param expectedNextRollupNumber - The next rollup id that we expect to find. + * @param blockHashMapping - A mapping from block number to relevant block hash. * @returns An array of UnverifiedData and the next eth block to search from. */ export async function retrieveUnverifiedData( @@ -81,6 +80,7 @@ export async function retrieveUnverifiedData( currentBlockNumber: bigint, searchStartBlock: bigint, expectedNextRollupNumber: bigint, + blockHashMapping: { [key: number]: Buffer | undefined }, ): Promise> { const newUnverifiedDataChunks: UnverifiedData[] = []; do { @@ -98,7 +98,7 @@ export async function retrieveUnverifiedData( break; } - const newChunks = processUnverifiedDataLogs(expectedNextRollupNumber, unverifiedDataLogs); + const newChunks = processUnverifiedDataLogs(expectedNextRollupNumber, blockHashMapping, unverifiedDataLogs); newUnverifiedDataChunks.push(...newChunks); searchStartBlock = unverifiedDataLogs[unverifiedDataLogs.length - 1].blockNumber + 1n; expectedNextRollupNumber += BigInt(newChunks.length); @@ -113,6 +113,7 @@ export async function retrieveUnverifiedData( * @param blockUntilSynced - If true, blocks until the archiver has fully synced. * @param currentBlockNumber - Latest available block number in the ETH node. * @param searchStartBlock - The block number to use for starting the search. + * @param blockHashMapping - A mapping from block number to relevant block hash. * @returns An array of ContractPublicData and their equivalent L2 Block number along with the next eth block to search from.. */ export async function retrieveNewContractData( @@ -121,21 +122,23 @@ export async function retrieveNewContractData( blockUntilSynced: boolean, currentBlockNumber: bigint, searchStartBlock: bigint, + blockHashMapping: { [key: number]: Buffer | undefined }, ): Promise> { let retrievedNewContracts: [ContractPublicData[], number][] = []; do { if (searchStartBlock > currentBlockNumber) { break; } - const contractDataLogs = await getContractDeploymentLogs( publicClient, unverifiedDataEmitterAddress, searchStartBlock, ); - const newContracts = processContractDeploymentLogs(contractDataLogs); + if (contractDataLogs.length === 0) { + break; + } + const newContracts = processContractDeploymentLogs(blockHashMapping, contractDataLogs); retrievedNewContracts = retrievedNewContracts.concat(newContracts); - searchStartBlock = (contractDataLogs.findLast(cd => !!cd)?.blockNumber || searchStartBlock) + 1n; } while (blockUntilSynced && searchStartBlock <= currentBlockNumber); return { nextEthBlockNumber: searchStartBlock, retrievedData: retrievedNewContracts }; diff --git a/yarn-project/archiver/src/archiver/eth_log_handlers.ts b/yarn-project/archiver/src/archiver/eth_log_handlers.ts index 4390e18776a..2d6faee4922 100644 --- a/yarn-project/archiver/src/archiver/eth_log_handlers.ts +++ b/yarn-project/archiver/src/archiver/eth_log_handlers.ts @@ -43,16 +43,23 @@ export function processPendingL1ToL2MessageAddedLogs( } /** * Processes newly received UnverifiedData logs. + * @param blockHashMapping - A mapping from block number to relevant block hash. * @param logs - ContractDeployment logs. * @returns The set of retrieved contract public data items. */ export function processContractDeploymentLogs( + blockHashMapping: { [key: number]: Buffer | undefined }, logs: Log[], ): [ContractPublicData[], number][] { const contractPublicData: [ContractPublicData[], number][] = []; for (let i = 0; i < logs.length; i++) { const log = logs[i]; const l2BlockNum = Number(log.args.l2BlockNum); + const blockHash = Buffer.from(hexToBytes(log.args.l2BlockHash)); + const expectedBlockHash = blockHashMapping[l2BlockNum]; + if (expectedBlockHash === undefined || !blockHash.equals(expectedBlockHash)) { + continue; + } const publicFnsReader = BufferReader.asReader(Buffer.from(log.args.acir.slice(2), 'hex')); const contractData = new ContractPublicData( new ContractData(AztecAddress.fromString(log.args.aztecAddress), EthAddress.fromString(log.args.portalAddress)), @@ -70,17 +77,25 @@ export function processContractDeploymentLogs( /** * Processes newly received UnverifiedData logs. * @param expectedRollupNumber - The next expected rollup number. + * @param blockHashMapping - A mapping from block number to relevant block hash. * @param logs - UnverifiedData logs. */ export function processUnverifiedDataLogs( expectedRollupNumber: bigint, + blockHashMapping: { [key: number]: Buffer | undefined }, logs: Log[], ) { const unverifiedDataChunks: UnverifiedData[] = []; for (const log of logs) { const l2BlockNum = log.args.l2BlockNum; - if (l2BlockNum !== expectedRollupNumber) { - throw new Error('Block number mismatch. Expected: ' + expectedRollupNumber + ' but got: ' + l2BlockNum + '.'); + const blockHash = Buffer.from(hexToBytes(log.args.l2BlockHash)); + const expectedBlockHash = blockHashMapping[Number(l2BlockNum)]; + if ( + l2BlockNum !== expectedRollupNumber || + expectedBlockHash === undefined || + !blockHash.equals(expectedBlockHash) + ) { + continue; } const unverifiedDataBuf = Buffer.from(hexToBytes(log.args.data)); const unverifiedData = UnverifiedData.fromBuffer(unverifiedDataBuf); diff --git a/yarn-project/archiver/src/index.ts b/yarn-project/archiver/src/index.ts index f7c81b4fa91..cb5e388e5cd 100644 --- a/yarn-project/archiver/src/index.ts +++ b/yarn-project/archiver/src/index.ts @@ -15,7 +15,7 @@ const log = createLogger('aztec:archiver_init'); // eslint-disable-next-line require-await async function main() { const config = getConfigEnvVars(); - const { rpcUrl, rollupContract, inboxContract, unverifiedDataEmitterContract } = config; + const { rpcUrl, rollupContract, inboxContract, unverifiedDataEmitterContract, searchStartBlock } = config; const publicClient = createPublicClient({ chain: localhost, @@ -29,6 +29,7 @@ async function main() { rollupContract, inboxContract, unverifiedDataEmitterContract, + searchStartBlock, archiverStore, ); diff --git a/yarn-project/archiver/tsconfig.json b/yarn-project/archiver/tsconfig.json index cf8af360206..c05779fbe19 100644 --- a/yarn-project/archiver/tsconfig.json +++ b/yarn-project/archiver/tsconfig.json @@ -6,6 +6,9 @@ "tsBuildInfoFile": ".tsbuildinfo" }, "references": [ + { + "path": "../ethereum" + }, { "path": "../foundation" }, diff --git a/yarn-project/aztec-cli/Dockerfile b/yarn-project/aztec-cli/Dockerfile index a82bcfd7325..69905541e8c 100644 --- a/yarn-project/aztec-cli/Dockerfile +++ b/yarn-project/aztec-cli/Dockerfile @@ -10,6 +10,6 @@ RUN yarn cache clean RUN yarn workspaces focus --production > /dev/null FROM node:18-alpine -COPY --from=builder /usr/src/yarn-project/aztec-cli /usr/src/yarn-project/aztec-cli +COPY --from=builder /usr/src/ /usr/src/ WORKDIR /usr/src/yarn-project/aztec-cli -ENTRYPOINT ["yarn"] \ No newline at end of file +ENTRYPOINT ["yarn", "start"] \ No newline at end of file diff --git a/yarn-project/aztec-cli/package.json b/yarn-project/aztec-cli/package.json index 4deb2329f00..16e2b2926dc 100644 --- a/yarn-project/aztec-cli/package.json +++ b/yarn-project/aztec-cli/package.json @@ -19,7 +19,8 @@ "clean": "rm -rf ./dest .tsbuildinfo", "formatting": "run -T prettier --check ./src && run -T eslint ./src", "formatting:fix": "run -T prettier -w ./src", - "test": "NODE_NO_WARNINGS=1 node --experimental-vm-modules $(yarn bin jest) --passWithNoTests" + "test": "NODE_NO_WARNINGS=1 node --experimental-vm-modules $(yarn bin jest) --passWithNoTests", + "start": "node ./dest/index.js" }, "inherits": [ "../package.common.json" @@ -33,9 +34,15 @@ "rootDir": "./src" }, "dependencies": { + "@aztec/aztec-node": "workspace:^", + "@aztec/aztec.js": "workspace:^", + "@aztec/ethereum": "workspace:^", "@aztec/foundation": "workspace:^", + "@aztec/l1-artifacts": "workspace:^", + "@aztec/noir-contracts": "workspace:^", "commander": "^9.0.0", - "tslib": "^2.4.0" + "tslib": "^2.4.0", + "viem": "^0.3.14" }, "devDependencies": { "@jest/globals": "^29.5.0", diff --git a/yarn-project/aztec-cli/src/deploy_l2_contract.ts b/yarn-project/aztec-cli/src/deploy_l2_contract.ts new file mode 100644 index 00000000000..2e3063fb83f --- /dev/null +++ b/yarn-project/aztec-cli/src/deploy_l2_contract.ts @@ -0,0 +1,90 @@ +import { AztecNode, HttpNode } from '@aztec/aztec-node'; +import { ContractDeployer, createAztecRPCServer, TxStatus, TxHash, Point } from '@aztec/aztec.js'; +import { DebugLogger } from '@aztec/foundation/log'; +import { toBigIntBE } from '@aztec/foundation/bigint-buffer'; +import { ContractAbi } from '@aztec/foundation/abi'; +import { sleep } from '@aztec/foundation/sleep'; +import { ZkTokenContractAbi } from '@aztec/noir-contracts/examples'; + +/** + * Helper function for creating an instance of the aztec rpc server. + * @param numberOfAccounts - The initial number of accounts to be added. + * @param aztecNode - The instance of aztec node to be used by the rpc server. + * @returns The instance of an aztec rpc server. + */ +export async function createAztecRpc(numberOfAccounts = 1, aztecNode: AztecNode) { + const arc = await createAztecRPCServer(aztecNode); + + for (let i = 0; i < numberOfAccounts; ++i) { + await arc.addAccount(); + } + + return arc; +} + +const pointToPublicKey = (point: Point) => { + const x = point.buffer.subarray(0, 32); + const y = point.buffer.subarray(32, 64); + return { + x: toBigIntBE(x), + y: toBigIntBE(y), + }; +}; + +/** + * Helper function to pause until the provided aztec node instance is ready for use. + * @param aztecNode - The instance of an aztec node that we need to wait for. + * @param logger - An instance of a logging object. + */ +export async function waitUntilReady(aztecNode: AztecNode, logger: DebugLogger) { + while (true) { + try { + const isReady = await aztecNode.isReady(); + if (!isReady) { + throw new Error('Not ready'); + } + break; + } catch (err) { + logger(`Aztec node not ready, will wait 10 seconds and check again...`); + await sleep(10000); + } + } +} + +/** + * Function for carrying out the 'deployL2' command. An instance of the ZKToken Contract will be deployed + * either once or repeatedly if specified. + * @param rollupProviderUrl - The url of the rollup provider service the contracts should be deployed to. + * @param intervalMs - The interval (ms) between repeated deployments. If 0, contract is only deployed once. + * @param logger - An instance of a logging object. + */ +export async function deployL2Contract(rollupProviderUrl: string, intervalMs: number, logger: DebugLogger) { + const node: AztecNode = new HttpNode(rollupProviderUrl); + await waitUntilReady(node, logger); + const aztecRpcServer = await createAztecRpc(2, node); + const accounts = await aztecRpcServer.getAccounts(); + const outstandingTxs: TxHash[] = []; + + do { + logger(`Deploying L2 contract...`); + const initialBalance = 1_000_000_000n; + const zkContract = ZkTokenContractAbi as ContractAbi; + const deployer = new ContractDeployer(zkContract, aztecRpcServer); + const owner = await aztecRpcServer.getAccountPublicKey(accounts[0]); + const d = deployer.deploy(initialBalance, pointToPublicKey(owner)); + await d.create(); + const tx = d.send(); + const txHash = await tx.getTxHash(); + outstandingTxs.push(txHash); + logger('L2 contract deployed'); + await sleep(intervalMs > 0 ? intervalMs : 1); + for (let i = 0; i < outstandingTxs.length; i++) { + const hash = outstandingTxs[i]; + const receipt = await aztecRpcServer.getTxReceipt(hash); + if (receipt.status == TxStatus.MINED) { + logger(`Tx ${hash.toString()} settled, contract address ${receipt.contractAddress}`); + outstandingTxs.splice(i, 1); + } + } + } while (intervalMs != 0); +} diff --git a/yarn-project/aztec-cli/src/index.ts b/yarn-project/aztec-cli/src/index.ts index d88187a3f67..0b4b7551b13 100644 --- a/yarn-project/aztec-cli/src/index.ts +++ b/yarn-project/aztec-cli/src/index.ts @@ -1,21 +1,71 @@ #!/usr/bin/env node import { createLogger } from '@aztec/foundation/log'; import { Command } from 'commander'; +import { mnemonicToAccount, privateKeyToAccount } from 'viem/accounts'; +import { createDebugLogger } from '@aztec/foundation/log'; +import { createEthereumChain, deployL1Contracts } from '@aztec/ethereum'; +import { deployL2Contract } from './deploy_l2_contract.js'; + +const logger = createDebugLogger('aztec:cli'); const program = new Command(); const log = createLogger('aztec:aztec-cli'); +/** + * Function to execute the 'deployRollupContracts' command. + * @param rpcUrl - The RPC URL of the ethereum node. + * @param apiKey - The api key of the ethereum node endpoint. + * @param privateKey - The private key to be used in contract deployment. + * @param mnemonic - The mnemonic to be used in contract deployment. + */ +async function deployRollupContracts(rpcUrl: string, apiKey: string, privateKey: string, mnemonic: string) { + const account = !privateKey ? mnemonicToAccount(mnemonic!) : privateKeyToAccount(`0x${privateKey}`); + const chain = createEthereumChain(rpcUrl, apiKey); + await deployL1Contracts(chain.rpcUrl, account, chain.chainInfo, logger); +} + /** * A placeholder for the Aztec-cli. */ async function main() { program .command('run') - .argument('', 'command') + .argument('', 'Command') .action((cmd: string) => { log(`Running '${cmd}'...`); }); + program + .command('deployRollupContracts') + .argument( + '[rpcUrl]', + 'Url of the ethereum host. Chain identifiers localhost and testnet can be used', + 'http://localhost:8545', + ) + .option('-a, --apiKey ', 'Api key for the ethereum host', undefined) + .option('-p, --privateKey ', 'The private key to use for deployment') + .option( + '-m, --mnemonic ', + 'The mnemonic to use in deployment', + 'test test test test test test test test test test test junk', + ) + .action(async (rpcUrl: string, options) => { + await deployRollupContracts(rpcUrl, options.apiKey ?? '', options.privateKey, options.mnemonic); + }); + + program + .command('deployL2') + .argument('[rpcUrl]', 'Url of the rollup provider', 'http://localhost:9000') + .argument('[interval]', 'Interval between contract deployments (seconds), 0 means only a single deployment', 60) + .action(async (rpcUrl: string, intervalArg: string) => { + try { + const interval = Number(intervalArg); + await deployL2Contract(rpcUrl, interval * 1000, logger); + } catch (err) { + logger(`Error`, err); + } + }); + await program.parseAsync(process.argv); } diff --git a/yarn-project/aztec-cli/tsconfig.json b/yarn-project/aztec-cli/tsconfig.json index 63f8ab3e9f7..ab9432bcc92 100644 --- a/yarn-project/aztec-cli/tsconfig.json +++ b/yarn-project/aztec-cli/tsconfig.json @@ -6,9 +6,25 @@ "tsBuildInfoFile": ".tsbuildinfo" }, "references": [ + { + "path": "../aztec-node" + }, + { + "path": "../aztec.js" + }, + { + "path": "../ethereum" + }, { "path": "../foundation" + }, + { + "path": "../l1-artifacts" + }, + { + "path": "../noir-contracts" } ], - "include": ["src"] + "include": ["src"], + "exclude": ["contracts"] } diff --git a/yarn-project/aztec-node/src/aztec-node/aztec-node.ts b/yarn-project/aztec-node/src/aztec-node/aztec-node.ts index 8e38fc2bd8e..7fc48936fa3 100644 --- a/yarn-project/aztec-node/src/aztec-node/aztec-node.ts +++ b/yarn-project/aztec-node/src/aztec-node/aztec-node.ts @@ -1,86 +1,20 @@ -import { Archiver } from '@aztec/archiver'; -import { PrimitivesWasm } from '@aztec/barretenberg.js/wasm'; -import { - CONTRACT_TREE_HEIGHT, - CircuitsWasm, - L1_TO_L2_MESSAGES_TREE_HEIGHT, - PRIVATE_DATA_TREE_HEIGHT, -} from '@aztec/circuits.js'; +import { CONTRACT_TREE_HEIGHT, L1_TO_L2_MESSAGES_TREE_HEIGHT, PRIVATE_DATA_TREE_HEIGHT } from '@aztec/circuits.js'; import { AztecAddress } from '@aztec/foundation/aztec-address'; -import { Fr } from '@aztec/foundation/fields'; +import { ContractPublicData, ContractData, L2Block, MerkleTreeId } from '@aztec/types'; import { SiblingPath } from '@aztec/merkle-tree'; -import { P2P, P2PClient } from '@aztec/p2p'; -import { SequencerClient } from '@aztec/sequencer-client'; -import { - ContractData, - ContractDataSource, - ContractPublicData, - L2Block, - L2BlockSource, - MerkleTreeId, - Tx, - TxHash, - UnverifiedData, - UnverifiedDataSource, -} from '@aztec/types'; -import { - MerkleTrees, - ServerWorldStateSynchroniser, - WorldStateSynchroniser, - computePublicDataTreeLeafIndex, -} from '@aztec/world-state'; -import { default as levelup } from 'levelup'; -import { MemDown, default as memdown } from 'memdown'; -import { AztecNodeConfig } from './config.js'; - -export const createMemDown = () => (memdown as any)() as MemDown; +import { Tx, TxHash } from '@aztec/types'; +import { UnverifiedData } from '@aztec/types'; +import { Fr } from '@aztec/foundation/fields'; /** * The aztec node. */ -export class AztecNode { - constructor( - protected p2pClient: P2P, - protected blockSource: L2BlockSource, - protected unverifiedDataSource: UnverifiedDataSource, - protected contractDataSource: ContractDataSource, - protected merkleTreeDB: MerkleTrees, - protected worldStateSynchroniser: WorldStateSynchroniser, - protected sequencer: SequencerClient, - ) {} - - /** - * Initialises the Aztec Node, wait for component to sync. - * @param config - The configuration to be used by the aztec node. - * @returns - A fully synced Aztec Node for use in development/testing. - */ - public static async createAndSync(config: AztecNodeConfig) { - // first create and sync the archiver - const archiver = await Archiver.createAndSync(config); - - // give the block source to the P2P network - const p2pClient = new P2PClient(archiver); - - // now create the merkle trees and the world state syncher - const merkleTreeDB = await MerkleTrees.new(levelup(createMemDown()), await CircuitsWasm.get()); - - const worldStateSynchroniser = new ServerWorldStateSynchroniser(merkleTreeDB, archiver); - - // start both and wait for them to sync from the block source - await Promise.all([p2pClient.start(), worldStateSynchroniser.start()]); - - // now create the sequencer - const sequencer = await SequencerClient.new(config, p2pClient, worldStateSynchroniser, archiver); - return new AztecNode(p2pClient, archiver, archiver, archiver, merkleTreeDB, worldStateSynchroniser, sequencer); - } - +export interface AztecNode { /** * Method to determine if the node is ready to accept transactions. * @returns - Flag indicating the readiness for tx submission. */ - public async isReady() { - return (await this.p2pClient.isReady()) ?? false; - } + isReady(): Promise; /** * Method to request blocks. Will attempt to return all requested blocks but will return only those available. @@ -88,17 +22,13 @@ export class AztecNode { * @param take - The number of blocks desired. * @returns The blocks requested. */ - public async getBlocks(from: number, take: number): Promise { - return (await this.blockSource.getL2Blocks(from, take)) ?? []; - } + getBlocks(from: number, take: number): Promise; /** * Method to fetch the current block height. * @returns The block height as a number. */ - public async getBlockHeight(): Promise { - return await this.blockSource.getBlockHeight(); - } + getBlockHeight(): Promise; /** * Lookup the L2 contract data for this contract. @@ -106,9 +36,7 @@ export class AztecNode { * @param contractAddress - The contract data address. * @returns The complete contract data including portal address & bytecode (if we didn't throw an error). */ - public async getContractData(contractAddress: AztecAddress): Promise { - return await this.contractDataSource.getL2ContractPublicData(contractAddress); - } + getContractData(contractAddress: AztecAddress): Promise; /** * Lookup the L2 contract info for this contract. @@ -116,9 +44,7 @@ export class AztecNode { * @param contractAddress - The contract data address. * @returns The contract's address & portal address. */ - public async getContractInfo(contractAddress: AztecAddress): Promise { - return await this.contractDataSource.getL2ContractInfo(contractAddress); - } + getContractInfo(contractAddress: AztecAddress): Promise; /** * Gets the `take` amount of unverified data starting from `from`. @@ -126,81 +52,54 @@ export class AztecNode { * @param take - The number of `unverifiedData` to return. * @returns The requested `unverifiedData`. */ - public getUnverifiedData(from: number, take: number): Promise { - return this.unverifiedDataSource.getUnverifiedData(from, take); - } + getUnverifiedData(from: number, take: number): Promise; /** * Method to submit a transaction to the p2p pool. * @param tx - The transaction to be submitted. */ - public async sendTx(tx: Tx) { - await this.p2pClient!.sendTx(tx); - } - - /** - * Method to stop the aztec node. - */ - public async stop() { - await this.sequencer.stop(); - await this.p2pClient.stop(); - await this.worldStateSynchroniser.stop(); - await this.blockSource.stop(); - await this.merkleTreeDB.stop(); - } + sendTx(tx: Tx): Promise; /** * Method to retrieve pending txs. * @returns The pending txs. */ - public async getPendingTxs() { - return await this.p2pClient!.getTxs(); - } + getPendingTxs(): Promise; /** * Method to retrieve a single pending tx. * @param txHash - The transaction hash to return. * @returns The pending tx if it exists. */ - public async getPendingTxByHash(txHash: TxHash) { - return await this.p2pClient!.getTxByhash(txHash); - } + getPendingTxByHash(txHash: TxHash): Promise; /** - * Returns the index of a contract in the committed contracts tree. - * @param leafValue - A contract leaf generated with computeContractLeaf. - * @returns The index of the contract in the tree or undefined if not found. + * Find the index of the given contract. + * @param leafValue - The value to search for. + * @returns The index of the given leaf in the contracts tree or undefined if not found. */ - public findContractIndex(leafValue: Buffer): Promise { - return this.merkleTreeDB.findLeafIndex(MerkleTreeId.CONTRACT_TREE, leafValue, false); - } + findContractIndex(leafValue: Buffer): Promise; /** - * Returns the sibling path for a leaf in the committed contracts tree. - * @param leafIndex - Index of the leaf in the tree. - * @returns The sibling path. + * Returns the sibling path for the given index in the contract tree. + * @param leafIndex - The index of the leaf for which the sibling path is required. + * @returns The sibling path for the leaf index. */ - public getContractPath(leafIndex: bigint): Promise> { - return this.merkleTreeDB.getSiblingPath(MerkleTreeId.CONTRACT_TREE, leafIndex, false); - } + getContractPath(leafIndex: bigint): Promise>; /** - * Returns the sibling path for a leaf in the committed private data tree. - * @param leafIndex - Index of the leaf in the tree. - * @returns The sibling path. + * Returns the sibling path for the given index in the data tree. + * @param leafIndex - The index of the leaf for which the sibling path is required. + * @returns The sibling path for the leaf index. */ - public getDataTreePath(leafIndex: bigint): Promise> { - return this.merkleTreeDB.getSiblingPath(MerkleTreeId.PRIVATE_DATA_TREE, leafIndex, false); - } + getDataTreePath(leafIndex: bigint): Promise>; /** * Returns the sibling path for a leaf in the committed l1 to l2 data tree. * @param leafIndex - Index of the leaf in the tree. * @returns The sibling path. */ - public getL1ToL2MessagesTreePath(leafIndex: bigint): Promise> { - return this.merkleTreeDB.getSiblingPath(MerkleTreeId.L1_TO_L2_MESSAGES_TREE, leafIndex, false); - } + getL1ToL2MessagesTreePath(leafIndex: bigint): Promise>; /** * Gets the storage value at the given contract slot. Our version of eth_getStorageAt. @@ -208,28 +107,11 @@ export class AztecNode { * @param slot - Slot to query. * @returns Storage value at the given contract slot (or undefined if not found). */ - public async getStorageAt(contract: AztecAddress, slot: bigint): Promise { - const leafIndex = computePublicDataTreeLeafIndex(contract, new Fr(slot), await PrimitivesWasm.get()); - return this.merkleTreeDB.getLeafValue(MerkleTreeId.PUBLIC_DATA_TREE, leafIndex, false); - } + getStorageAt(contract: AztecAddress, slot: bigint): Promise; /** * Returns the current committed roots for the data trees. * @returns The current committed roots for the data trees. */ - public async getTreeRoots(): Promise> { - const getTreeRoot = async (id: MerkleTreeId) => - Fr.fromBuffer((await this.merkleTreeDB.getTreeInfo(id, false)).root); - - return { - [MerkleTreeId.CONTRACT_TREE]: await getTreeRoot(MerkleTreeId.CONTRACT_TREE), - [MerkleTreeId.PRIVATE_DATA_TREE]: await getTreeRoot(MerkleTreeId.PRIVATE_DATA_TREE), - [MerkleTreeId.NULLIFIER_TREE]: await getTreeRoot(MerkleTreeId.NULLIFIER_TREE), - [MerkleTreeId.PUBLIC_DATA_TREE]: await getTreeRoot(MerkleTreeId.PUBLIC_DATA_TREE), - [MerkleTreeId.L1_TO_L2_MESSAGES_TREE]: await getTreeRoot(MerkleTreeId.L1_TO_L2_MESSAGES_TREE), - [MerkleTreeId.L1_TO_L2_MESSAGES_ROOTS_TREE]: await getTreeRoot(MerkleTreeId.L1_TO_L2_MESSAGES_ROOTS_TREE), - [MerkleTreeId.CONTRACT_TREE_ROOTS_TREE]: await getTreeRoot(MerkleTreeId.CONTRACT_TREE_ROOTS_TREE), - [MerkleTreeId.PRIVATE_DATA_TREE_ROOTS_TREE]: await getTreeRoot(MerkleTreeId.PRIVATE_DATA_TREE_ROOTS_TREE), - }; - } + getTreeRoots(): Promise>; } diff --git a/yarn-project/aztec-node/src/aztec-node/config.ts b/yarn-project/aztec-node/src/aztec-node/config.ts index 2f2a6d77229..748c8488c84 100644 --- a/yarn-project/aztec-node/src/aztec-node/config.ts +++ b/yarn-project/aztec-node/src/aztec-node/config.ts @@ -1,10 +1,11 @@ import { ArchiverConfig, getConfigEnvVars as getArchiverVars } from '@aztec/archiver'; import { SequencerClientConfig, getConfigEnvVars as getSequencerVars } from '@aztec/sequencer-client'; +import { P2PConfig, getP2PConfigEnvVars } from '@aztec/p2p'; /** * The configuration the aztec node. */ -export type AztecNodeConfig = ArchiverConfig & SequencerClientConfig; +export type AztecNodeConfig = ArchiverConfig & SequencerClientConfig & P2PConfig; /** * Returns the config of the aztec node from environment variables with reasonable defaults. @@ -14,6 +15,7 @@ export function getConfigEnvVars(): AztecNodeConfig { const allEnvVars: AztecNodeConfig = { ...getSequencerVars(), ...getArchiverVars(), + ...getP2PConfigEnvVars(), }; return allEnvVars; diff --git a/yarn-project/aztec-node/src/aztec-node/http-node.ts b/yarn-project/aztec-node/src/aztec-node/http-node.ts new file mode 100644 index 00000000000..e4b6a63e94d --- /dev/null +++ b/yarn-project/aztec-node/src/aztec-node/http-node.ts @@ -0,0 +1,293 @@ +import { AztecNode } from '@aztec/aztec-node'; +import { + AztecAddress, + CONTRACT_TREE_HEIGHT, + Fr, + KernelCircuitPublicInputs, + L1_TO_L2_MESSAGES_TREE_HEIGHT, + PRIVATE_DATA_TREE_HEIGHT, + Proof, + PublicCallRequest, + SignedTxRequest, +} from '@aztec/circuits.js'; +import { SiblingPath } from '@aztec/merkle-tree'; +import { + ContractData, + ContractPublicData, + EncodedContractFunction, + L2Block, + MerkleTreeId, + Tx, + TxHash, + UnverifiedData, +} from '@aztec/types'; + +/** + * Serialises a transaction to JSON representation. + * @param tx - The transaction to serialise. + * @returns The serialsied transaction. + */ +export function txToJson(tx: Tx) { + return { + data: tx.data?.toBuffer().toString('hex'), + unverified: tx.unverifiedData?.toBuffer().toString('hex'), + txRequest: tx.txRequest?.toBuffer().toString('hex'), + proof: tx.proof?.toBuffer().toString('hex'), + newContractPublicFunctions: tx.newContractPublicFunctions?.map(f => f.toBuffer().toString('hex')) ?? [], + enqueuedPublicFunctions: tx.enqueuedPublicFunctionCalls?.map(f => f.toBuffer().toString('hex')) ?? [], + }; +} + +/** + * Deserialises a transaction from JSON. + * @param json - The JSON representation of the transaction. + * @returns The deserialised transaction. + */ +export function txFromJson(json: any) { + const publicInputs = json.data ? KernelCircuitPublicInputs.fromBuffer(Buffer.from(json.data, 'hex')) : undefined; + const unverified = json.unverified ? UnverifiedData.fromBuffer(Buffer.from(json.unverified, 'hex')) : undefined; + const txRequest = json.txRequest ? SignedTxRequest.fromBuffer(Buffer.from(json.txRequest, 'hex')) : undefined; + const proof = json.proof ? Buffer.from(json.proof, 'hex') : undefined; + const newContractPublicFunctions = json.newContractPublicFunctions + ? json.newContractPublicFunctions.map((x: string) => EncodedContractFunction.fromBuffer(Buffer.from(x, 'hex'))) + : []; + const enqueuedPublicFunctions = json.enqueuedPublicFunctions + ? json.enqueuedPublicFunctions.map((x: string) => PublicCallRequest.fromBuffer(Buffer.from(x, 'hex'))) + : []; + if (txRequest) { + return Tx.createPublic(txRequest); + } + if (publicInputs && proof && unverified) { + return Tx.createPrivate( + publicInputs, + Proof.fromBuffer(proof), + unverified, + newContractPublicFunctions, + enqueuedPublicFunctions, + ); + } + return Tx.create(publicInputs, proof == undefined ? undefined : Proof.fromBuffer(proof), unverified, txRequest); +} + +/** + * A Http client based implementation of Aztec Node. + */ +export class HttpNode implements AztecNode { + private baseUrl: string; + constructor(baseUrl: string) { + this.baseUrl = baseUrl.toString().replace(/\/$/, ''); + } + /** + * Method to determine if the node is ready to accept transactions. + * @returns - Flag indicating the readiness for tx submission. + */ + public async isReady(): Promise { + const url = new URL(this.baseUrl); + const response = await fetch(url.toString()); + const respJson = await response.json(); + return respJson.isReady; + } + + /** + * Method to request blocks. Will attempt to return all requested blocks but will return only those available. + * @param from - The start of the range of blocks to return. + * @param take - The number of blocks desired. + * @returns The blocks requested. + */ + async getBlocks(from: number, take: number): Promise { + const url = new URL(`${this.baseUrl}/get-blocks`); + url.searchParams.append('from', from.toString()); + if (take !== undefined) { + url.searchParams.append('take', take.toString()); + } + const response = await (await fetch(url.toString())).json(); + const blocks = response.blocks as string[]; + if (!blocks) { + return Promise.resolve([]); + } + return Promise.resolve(blocks.map(x => L2Block.decode(Buffer.from(x, 'hex')))); + } + + /** + * Method to fetch the current block height. + * @returns The block height as a number. + */ + async getBlockHeight(): Promise { + const url = new URL(`${this.baseUrl}/get-block-height`); + const response = await fetch(url.toString()); + const respJson = await response.json(); + return respJson.blockHeight; + } + + /** + * Lookup the L2 contract data for this contract. + * Contains the ethereum portal address and bytecode. + * @param contractAddress - The contract data address. + * @returns The complete contract data including portal address & bytecode (if we didn't throw an error). + */ + async getContractData(contractAddress: AztecAddress): Promise { + const url = new URL(`${this.baseUrl}/contract-data`); + url.searchParams.append('address', contractAddress.toString()); + const response = await (await fetch(url.toString())).json(); + const contract = response.contractData as string; + return Promise.resolve(ContractPublicData.fromBuffer(Buffer.from(contract, 'hex'))); + } + + /** + * Lookup the L2 contract info for this contract. + * Contains the ethereum portal address . + * @param contractAddress - The contract data address. + * @returns The contract's address & portal address. + */ + async getContractInfo(contractAddress: AztecAddress): Promise { + const url = new URL(`${this.baseUrl}/contract-info`); + url.searchParams.append('address', contractAddress.toString()); + const response = await (await fetch(url.toString())).json(); + const contract = response.contractInfo as string; + return Promise.resolve(ContractData.fromBuffer(Buffer.from(contract, 'hex'))); + } + + /** + * Gets the `take` amount of unverified data starting from `from`. + * @param from - Number of the L2 block to which corresponds the first `unverifiedData` to be returned. + * @param take - The number of `unverifiedData` to return. + * @returns The requested `unverifiedData`. + */ + async getUnverifiedData(from: number, take: number): Promise { + const url = new URL(`${this.baseUrl}/get-unverified`); + url.searchParams.append('from', from.toString()); + if (take !== undefined) { + url.searchParams.append('take', take.toString()); + } + const response = await (await fetch(url.toString())).json(); + const unverified = response.unverified as string[]; + + if (!unverified) { + return Promise.resolve([]); + } + return Promise.resolve(unverified.map(x => UnverifiedData.fromBuffer(Buffer.from(x, 'hex')))); + } + + /** + * Method to submit a transaction to the p2p pool. + * @param tx - The transaction to be submitted. + */ + async sendTx(tx: Tx): Promise { + const url = new URL(`${this.baseUrl}/tx`); + const json = txToJson(tx); + const init: RequestInit = {}; + init['method'] = 'POST'; + init['body'] = JSON.stringify(json); + await fetch(url, init); + } + + /** + * Method to retrieve pending txs. + * @returns - The pending txs. + */ + getPendingTxs(): Promise { + return Promise.resolve([]); + } + + /** + * Method to retrieve a single pending tx. + * @param txHash - The transaction hash to return. + * @returns - The pending tx if it exists. + */ + async getPendingTxByHash(txHash: TxHash): Promise { + const url = new URL(`${this.baseUrl}/get-pending-tx`); + url.searchParams.append('hash', txHash.toString()); + const response = await (await fetch(url.toString())).json(); + return Promise.resolve(txFromJson(response)); + } + + /** + * Find the index of the given contract. + * @param leafValue - The value to search for. + * @returns The index of the given leaf in the contracts tree or undefined if not found. + */ + async findContractIndex(leafValue: Buffer): Promise { + const url = new URL(`${this.baseUrl}/contract-index`); + url.searchParams.append('leaf', leafValue.toString('hex')); + const response = await (await fetch(url.toString())).json(); + const index = response.index as string; + return Promise.resolve(BigInt(index)); + } + + /** + * Returns the sibling path for the given index in the contract tree. + * @param leafIndex - The index of the leaf for which the sibling path is required. + * @returns The sibling path for the leaf index. + */ + async getContractPath(leafIndex: bigint): Promise> { + const url = new URL(`${this.baseUrl}/contract-path`); + url.searchParams.append('leaf', leafIndex.toString()); + const response = await (await fetch(url.toString())).json(); + const path = response.path as string; + return Promise.resolve(SiblingPath.fromString(path)); + } + + /** + * Returns the sibling path for the given index in the data tree. + * @param leafIndex - The index of the leaf for which the sibling path is required. + * @returns The sibling path for the leaf index. + */ + async getDataTreePath(leafIndex: bigint): Promise> { + const url = new URL(`${this.baseUrl}/data-path`); + url.searchParams.append('leaf', leafIndex.toString()); + const response = await (await fetch(url.toString())).json(); + const path = response.path as string; + return Promise.resolve(SiblingPath.fromString(path)); + } + + /** + * Returns the sibling path for a leaf in the committed l1 to l2 data tree. + * @param leafIndex - Index of the leaf in the tree. + * @returns The sibling path. + */ + async getL1ToL2MessagesTreePath(leafIndex: bigint): Promise> { + const url = new URL(`${this.baseUrl}/l1-l2-path`); + url.searchParams.append('leaf', leafIndex.toString()); + const response = await (await fetch(url.toString())).json(); + const path = response.path as string; + return Promise.resolve(SiblingPath.fromString(path)); + } + + /** + * Gets the storage value at the given contract slot. + * @param contract - Address of the contract to query. + * @param slot - Slot to query. + * @returns Storage value at the given contract slot (or undefined if not found). + * Note: Aztec's version of `eth_getStorageAt`. + */ + async getStorageAt(contract: AztecAddress, slot: bigint): Promise { + const url = new URL(`${this.baseUrl}/storage-at`); + url.searchParams.append('address', contract.toString()); + url.searchParams.append('slot', slot.toString()); + const response = await (await fetch(url.toString())).json(); + const value = response.value as string; + return Promise.resolve(Buffer.from(value, 'hex')); + } + + /** + * Returns the current committed roots for the data trees. + * @returns The current committed roots for the data trees. + */ + async getTreeRoots(): Promise> { + const url = new URL(`${this.baseUrl}/tree-roots`); + const response = await (await fetch(url.toString())).json(); + + const extractRoot = (treeId: MerkleTreeId) => Fr.fromBuffer(Buffer.from(response.roots[`${treeId}`], 'hex')); + + return { + [MerkleTreeId.CONTRACT_TREE]: extractRoot(MerkleTreeId.CONTRACT_TREE), + [MerkleTreeId.PRIVATE_DATA_TREE]: extractRoot(MerkleTreeId.PRIVATE_DATA_TREE), + [MerkleTreeId.NULLIFIER_TREE]: extractRoot(MerkleTreeId.NULLIFIER_TREE), + [MerkleTreeId.PUBLIC_DATA_TREE]: extractRoot(MerkleTreeId.PUBLIC_DATA_TREE), + [MerkleTreeId.L1_TO_L2_MESSAGES_TREE]: extractRoot(MerkleTreeId.L1_TO_L2_MESSAGES_TREE), + [MerkleTreeId.L1_TO_L2_MESSAGES_ROOTS_TREE]: extractRoot(MerkleTreeId.L1_TO_L2_MESSAGES_ROOTS_TREE), + [MerkleTreeId.CONTRACT_TREE_ROOTS_TREE]: extractRoot(MerkleTreeId.CONTRACT_TREE_ROOTS_TREE), + [MerkleTreeId.PRIVATE_DATA_TREE_ROOTS_TREE]: extractRoot(MerkleTreeId.PRIVATE_DATA_TREE_ROOTS_TREE), + }; + } +} diff --git a/yarn-project/aztec-node/src/aztec-node/server.ts b/yarn-project/aztec-node/src/aztec-node/server.ts new file mode 100644 index 00000000000..f0982515711 --- /dev/null +++ b/yarn-project/aztec-node/src/aztec-node/server.ts @@ -0,0 +1,251 @@ +import { Archiver } from '@aztec/archiver'; +import { AztecAddress } from '@aztec/foundation/aztec-address'; +import { + ContractPublicData, + ContractData, + ContractDataSource, + L2Block, + L2BlockSource, + MerkleTreeId, +} from '@aztec/types'; +import { SiblingPath } from '@aztec/merkle-tree'; +import { InMemoryTxPool, P2P, createP2PClient } from '@aztec/p2p'; +import { SequencerClient, getCombinedHistoricTreeRoots } from '@aztec/sequencer-client'; +import { Tx, TxHash } from '@aztec/types'; +import { UnverifiedData, UnverifiedDataSource } from '@aztec/types'; +import { + MerkleTrees, + ServerWorldStateSynchroniser, + WorldStateSynchroniser, + computePublicDataTreeLeafIndex, +} from '@aztec/world-state'; +import { default as levelup } from 'levelup'; +import { default as memdown, MemDown } from 'memdown'; +import { AztecNodeConfig } from './config.js'; +import { + CONTRACT_TREE_HEIGHT, + CircuitsWasm, + Fr, + L1_TO_L2_MESSAGES_TREE_HEIGHT, + PRIVATE_DATA_TREE_HEIGHT, +} from '@aztec/circuits.js'; +import { PrimitivesWasm } from '@aztec/barretenberg.js/wasm'; +import { AztecNode } from './aztec-node.js'; + +export const createMemDown = () => (memdown as any)() as MemDown; + +/** + * The aztec node. + */ +export class AztecNodeService implements AztecNode { + constructor( + protected p2pClient: P2P, + protected blockSource: L2BlockSource, + protected unverifiedDataSource: UnverifiedDataSource, + protected contractDataSource: ContractDataSource, + protected merkleTreeDB: MerkleTrees, + protected worldStateSynchroniser: WorldStateSynchroniser, + protected sequencer: SequencerClient, + ) {} + + /** + * Initialises the Aztec Node, wait for component to sync. + * @param config - The configuration to be used by the aztec node. + * @returns - A fully synced Aztec Node for use in development/testing. + */ + public static async createAndSync(config: AztecNodeConfig) { + // first create and sync the archiver + const archiver = await Archiver.createAndSync(config); + + // we idenfity the P2P transaction protocol by using the rollup contract address. + // this may well change in future + config.transactionProtocol = `/aztec/tx/${config.rollupContract.toString()}`; + + // create the tx pool and the p2p client, which will need the l2 block source + const p2pClient = await createP2PClient(config, new InMemoryTxPool(), archiver); + + // now create the merkle trees and the world state syncher + const merkleTreeDB = await MerkleTrees.new(levelup(createMemDown()), await CircuitsWasm.get()); + const worldStateSynchroniser = new ServerWorldStateSynchroniser(merkleTreeDB, archiver); + + // start both and wait for them to sync from the block source + await Promise.all([p2pClient.start(), worldStateSynchroniser.start()]); + + // now create the sequencer + const sequencer = await SequencerClient.new(config, p2pClient, worldStateSynchroniser, archiver, archiver); + return new AztecNodeService( + p2pClient, + archiver, + archiver, + archiver, + merkleTreeDB, + worldStateSynchroniser, + sequencer, + ); + } + + /** + * Method to determine if the node is ready to accept transactions. + * @returns - Flag indicating the readiness for tx submission. + */ + public async isReady() { + return (await this.p2pClient.isReady()) ?? false; + } + + /** + * Method to request blocks. Will attempt to return all requested blocks but will return only those available. + * @param from - The start of the range of blocks to return. + * @param take - The number of blocks desired. + * @returns The blocks requested. + */ + public async getBlocks(from: number, take: number): Promise { + return (await this.blockSource.getL2Blocks(from, take)) ?? []; + } + + /** + * Method to fetch the current block height. + * @returns The block height as a number. + */ + public async getBlockHeight(): Promise { + return await this.blockSource.getBlockHeight(); + } + + /** + * Lookup the L2 contract data for this contract. + * Contains the ethereum portal address and bytecode. + * @param contractAddress - The contract data address. + * @returns The complete contract data including portal address & bytecode (if we didn't throw an error). + */ + public async getContractData(contractAddress: AztecAddress): Promise { + return await this.contractDataSource.getL2ContractPublicData(contractAddress); + } + + /** + * Lookup the L2 contract info for this contract. + * Contains the ethereum portal address . + * @param contractAddress - The contract data address. + * @returns The contract's address & portal address. + */ + public async getContractInfo(contractAddress: AztecAddress): Promise { + return await this.contractDataSource.getL2ContractInfo(contractAddress); + } + + /** + * Gets the `take` amount of unverified data starting from `from`. + * @param from - Number of the L2 block to which corresponds the first `unverifiedData` to be returned. + * @param take - The number of `unverifiedData` to return. + * @returns The requested `unverifiedData`. + */ + public getUnverifiedData(from: number, take: number): Promise { + return this.unverifiedDataSource.getUnverifiedData(from, take); + } + + /** + * Method to submit a transaction to the p2p pool. + * @param tx - The transaction to be submitted. + */ + public async sendTx(tx: Tx) { + // TODO: Patch tx to inject historic tree roots until the private kernel circuit supplies this value + if (tx.isPrivate() && tx.data.constants.historicTreeRoots.privateHistoricTreeRoots.isEmpty()) { + tx.data.constants.historicTreeRoots = await getCombinedHistoricTreeRoots(this.merkleTreeDB.asLatest()); + } + + await this.p2pClient!.sendTx(tx); + } + + /** + * Method to stop the aztec node. + */ + public async stop() { + await this.sequencer.stop(); + await this.p2pClient.stop(); + await this.worldStateSynchroniser.stop(); + await this.merkleTreeDB.stop(); + await this.blockSource.stop(); + } + + /** + * Method to retrieve pending txs. + * @returns - The pending txs. + */ + public async getPendingTxs() { + return await this.p2pClient!.getTxs(); + } + + /** + * Method to retrieve a single pending tx. + * @param txHash - The transaction hash to return. + * @returns - The pending tx if it exists. + */ + public async getPendingTxByHash(txHash: TxHash) { + return await this.p2pClient!.getTxByhash(txHash); + } + + /** + * Find the index of the given contract. + * @param leafValue - The value to search for. + * @returns The index of the given leaf in the contracts tree or undefined if not found. + */ + public findContractIndex(leafValue: Buffer): Promise { + return this.merkleTreeDB.findLeafIndex(MerkleTreeId.CONTRACT_TREE, leafValue, false); + } + + /** + * Returns the sibling path for the given index in the contract tree. + * @param leafIndex - The index of the leaf for which the sibling path is required. + * @returns The sibling path for the leaf index. + */ + public getContractPath(leafIndex: bigint): Promise> { + return this.merkleTreeDB.getSiblingPath(MerkleTreeId.CONTRACT_TREE, leafIndex, false); + } + + /** + * Returns the sibling path for the given index in the data tree. + * @param leafIndex - The index of the leaf for which the sibling path is required. + * @returns The sibling path for the leaf index. + */ + public getDataTreePath(leafIndex: bigint): Promise> { + return this.merkleTreeDB.getSiblingPath(MerkleTreeId.PRIVATE_DATA_TREE, leafIndex, false); + } + + /** + * Returns the sibling path for a leaf in the committed l1 to l2 data tree. + * @param leafIndex - Index of the leaf in the tree. + * @returns The sibling path. + */ + public getL1ToL2MessagesTreePath(leafIndex: bigint): Promise> { + return this.merkleTreeDB.getSiblingPath(MerkleTreeId.L1_TO_L2_MESSAGES_TREE, leafIndex, false); + } + + /** + * Gets the storage value at the given contract slot. + * @param contract - Address of the contract to query. + * @param slot - Slot to query. + * @returns Storage value at the given contract slot (or undefined if not found). + * Note: Aztec's version of `eth_getStorageAt`. + */ + public async getStorageAt(contract: AztecAddress, slot: bigint): Promise { + const leafIndex = computePublicDataTreeLeafIndex(contract, new Fr(slot), await PrimitivesWasm.get()); + return this.merkleTreeDB.getLeafValue(MerkleTreeId.PUBLIC_DATA_TREE, leafIndex, false); + } + + /** + * Returns the current committed roots for the data trees. + * @returns The current committed roots for the data trees. + */ + public async getTreeRoots(): Promise> { + const getTreeRoot = async (id: MerkleTreeId) => + Fr.fromBuffer((await this.merkleTreeDB.getTreeInfo(id, false)).root); + + return { + [MerkleTreeId.CONTRACT_TREE]: await getTreeRoot(MerkleTreeId.CONTRACT_TREE), + [MerkleTreeId.PRIVATE_DATA_TREE]: await getTreeRoot(MerkleTreeId.PRIVATE_DATA_TREE), + [MerkleTreeId.NULLIFIER_TREE]: await getTreeRoot(MerkleTreeId.NULLIFIER_TREE), + [MerkleTreeId.PUBLIC_DATA_TREE]: await getTreeRoot(MerkleTreeId.PUBLIC_DATA_TREE), + [MerkleTreeId.L1_TO_L2_MESSAGES_TREE]: await getTreeRoot(MerkleTreeId.L1_TO_L2_MESSAGES_TREE), + [MerkleTreeId.L1_TO_L2_MESSAGES_ROOTS_TREE]: await getTreeRoot(MerkleTreeId.L1_TO_L2_MESSAGES_ROOTS_TREE), + [MerkleTreeId.CONTRACT_TREE_ROOTS_TREE]: await getTreeRoot(MerkleTreeId.CONTRACT_TREE_ROOTS_TREE), + [MerkleTreeId.PRIVATE_DATA_TREE_ROOTS_TREE]: await getTreeRoot(MerkleTreeId.PRIVATE_DATA_TREE_ROOTS_TREE), + }; + } +} diff --git a/yarn-project/aztec-node/src/index.ts b/yarn-project/aztec-node/src/index.ts index c88603236fb..c260cc5df78 100644 --- a/yarn-project/aztec-node/src/index.ts +++ b/yarn-project/aztec-node/src/index.ts @@ -1,2 +1,4 @@ export * from './aztec-node/aztec-node.js'; export * from './aztec-node/config.js'; +export * from './aztec-node/server.js'; +export * from './aztec-node/http-node.js'; diff --git a/yarn-project/aztec-rpc/src/aztec_rpc_server/aztec_rpc_server.ts b/yarn-project/aztec-rpc/src/aztec_rpc_server/aztec_rpc_server.ts index 62ab2172d97..922bf5f92c8 100644 --- a/yarn-project/aztec-rpc/src/aztec_rpc_server/aztec_rpc_server.ts +++ b/yarn-project/aztec-rpc/src/aztec_rpc_server/aztec_rpc_server.ts @@ -278,6 +278,7 @@ export class AztecRPCServer implements AztecRPCClient { tx = Tx.createPublic(new SignedTxRequest(txRequest, signature)); } else if (txRequest.functionData.isConstructor) { newContract = contractAddress; + tx = await accountState.simulateAndProve(txRequest, signature, contractAddress); } else { toContract = contractAddress; diff --git a/yarn-project/circuits.js/src/structs/call_context.ts b/yarn-project/circuits.js/src/structs/call_context.ts index 00e38947f44..3ebed2b703e 100644 --- a/yarn-project/circuits.js/src/structs/call_context.ts +++ b/yarn-project/circuits.js/src/structs/call_context.ts @@ -2,6 +2,7 @@ import { serializeToBuffer } from '../utils/serialize.js'; import { AztecAddress } from '@aztec/foundation/aztec-address'; import { EthAddress } from '@aztec/foundation/eth-address'; import { FieldsOf } from '../utils/jsUtils.js'; +import { BufferReader } from '@aztec/foundation/serialize'; import { Fr } from './index.js'; /** @@ -80,4 +81,21 @@ export class CallContext { toBuffer() { return serializeToBuffer(...CallContext.getFields(this)); } + + /** + * Deserialise this from a buffer. + * @param buffer - The bufferable type from which to deserialise. + * @returns The deserialised instance of PublicCallRequest. + */ + static fromBuffer(buffer: Buffer | BufferReader) { + const reader = BufferReader.asReader(buffer); + return new CallContext( + new AztecAddress(reader.readBytes(32)), + new AztecAddress(reader.readBytes(32)), + new EthAddress(reader.readBytes(32)), + reader.readBoolean(), + reader.readBoolean(), + reader.readBoolean(), + ); + } } diff --git a/yarn-project/circuits.js/src/structs/constants.ts b/yarn-project/circuits.js/src/structs/constants.ts index 9ece1daca42..bbebf73e535 100644 --- a/yarn-project/circuits.js/src/structs/constants.ts +++ b/yarn-project/circuits.js/src/structs/constants.ts @@ -24,7 +24,7 @@ export const KERNEL_PUBLIC_DATA_READS_LENGTH = 4; export const VK_TREE_HEIGHT = 3; export const FUNCTION_TREE_HEIGHT = 4; -export const CONTRACT_TREE_HEIGHT = 4; +export const CONTRACT_TREE_HEIGHT = 8; export const PRIVATE_DATA_TREE_HEIGHT = 8; export const PUBLIC_DATA_TREE_HEIGHT = 254; export const NULLIFIER_TREE_HEIGHT = 8; diff --git a/yarn-project/circuits.js/src/structs/kernel/__snapshots__/index.test.ts.snap b/yarn-project/circuits.js/src/structs/kernel/__snapshots__/index.test.ts.snap index e7ee6e413ad..bc791b847bf 100644 --- a/yarn-project/circuits.js/src/structs/kernel/__snapshots__/index.test.ts.snap +++ b/yarn-project/circuits.js/src/structs/kernel/__snapshots__/index.test.ts.snap @@ -490,7 +490,7 @@ sibling_path: [ 0x1031 0x1032 0x1033 0x1034 ] contract_leaf_membership_witness: leaf_index: 0x1021 -sibling_path: [ 0x1021 0x1022 0x1023 0x1024 ] +sibling_path: [ 0x1021 0x1022 0x1023 0x1024 0x1025 0x1026 0x1027 0x1028 ] portal_contract_address: 0x4141414141414141414141414141414141414141 acir_hash: 0x1061 @@ -823,7 +823,7 @@ sibling_path: [ 0x1031 0x1032 0x1033 0x1034 ] contract_leaf_membership_witness: leaf_index: 0x1021 -sibling_path: [ 0x1021 0x1022 0x1023 0x1024 ] +sibling_path: [ 0x1021 0x1022 0x1023 0x1024 0x1025 0x1026 0x1027 0x1028 ] portal_contract_address: 0x4141414141414141414141414141414141414141 acir_hash: 0x1061 diff --git a/yarn-project/circuits.js/src/structs/public_call_request.ts b/yarn-project/circuits.js/src/structs/public_call_request.ts index 745cec0afa1..a3ec7846d22 100644 --- a/yarn-project/circuits.js/src/structs/public_call_request.ts +++ b/yarn-project/circuits.js/src/structs/public_call_request.ts @@ -1,3 +1,4 @@ +import { BufferReader } from '@aztec/foundation/serialize'; import { Tuple } from '@aztec/foundation/serialize'; import { FieldsOf } from '../index.js'; import { serializeToBuffer } from '../utils/serialize.js'; @@ -43,6 +44,21 @@ export class PublicCallRequest { return serializeToBuffer(this.contractAddress, this.functionData, this.callContext, this.args); } + /** + * Deserialise this from a buffer. + * @param buffer - The bufferable type from which to deserialise. + * @returns The deserialised instance of PublicCallRequest. + */ + static fromBuffer(buffer: Buffer | BufferReader) { + const reader = BufferReader.asReader(buffer); + return new PublicCallRequest( + new AztecAddress(reader.readBytes(32)), + FunctionData.fromBuffer(reader), + CallContext.fromBuffer(reader), + reader.readArray(ARGS_LENGTH, Fr), + ); + } + /** * Create PublicCallRequest from a fields dictionary. * @param fields - The dictionary. diff --git a/yarn-project/circuits.js/src/structs/rollup/__snapshots__/base_rollup.test.ts.snap b/yarn-project/circuits.js/src/structs/rollup/__snapshots__/base_rollup.test.ts.snap index f39ec6f2cd9..038620bf431 100644 --- a/yarn-project/circuits.js/src/structs/rollup/__snapshots__/base_rollup.test.ts.snap +++ b/yarn-project/circuits.js/src/structs/rollup/__snapshots__/base_rollup.test.ts.snap @@ -354,7 +354,7 @@ new_commitments_subtree_sibling_path: new_nullifiers_subtree_sibling_path: [ 0x4000 0x4001 0x4002 0x4003 0x4004 ] new_contracts_subtree_sibling_path: -[ 0x5000 0x5001 0x5002 ] +[ 0x5000 0x5001 0x5002 0x5003 0x5004 0x5005 0x5006 ] new_public_data_update_requests_sibling_paths: [ [ 0x6000 0x6001 0x6002 0x6003 0x6004 0x6005 0x6006 0x6007 0x6008 0x6009 0x600a 0x600b 0x600c 0x600d 0x600e 0x600f 0x6010 0x6011 0x6012 0x6013 0x6014 0x6015 0x6016 0x6017 0x6018 0x6019 0x601a 0x601b 0x601c 0x601d 0x601e 0x601f 0x6020 0x6021 0x6022 0x6023 0x6024 0x6025 0x6026 0x6027 0x6028 0x6029 0x602a 0x602b 0x602c 0x602d 0x602e 0x602f 0x6030 0x6031 0x6032 0x6033 0x6034 0x6035 0x6036 0x6037 0x6038 0x6039 0x603a 0x603b 0x603c 0x603d 0x603e 0x603f 0x6040 0x6041 0x6042 0x6043 0x6044 0x6045 0x6046 0x6047 0x6048 0x6049 0x604a 0x604b 0x604c 0x604d 0x604e 0x604f 0x6050 0x6051 0x6052 0x6053 0x6054 0x6055 0x6056 0x6057 0x6058 0x6059 0x605a 0x605b 0x605c 0x605d 0x605e 0x605f 0x6060 0x6061 0x6062 0x6063 0x6064 0x6065 0x6066 0x6067 0x6068 0x6069 0x606a 0x606b 0x606c 0x606d 0x606e 0x606f 0x6070 0x6071 0x6072 0x6073 0x6074 0x6075 0x6076 0x6077 0x6078 0x6079 0x607a 0x607b 0x607c 0x607d 0x607e 0x607f 0x6080 0x6081 0x6082 0x6083 0x6084 0x6085 0x6086 0x6087 0x6088 0x6089 0x608a 0x608b 0x608c 0x608d 0x608e 0x608f 0x6090 0x6091 0x6092 0x6093 0x6094 0x6095 0x6096 0x6097 0x6098 0x6099 0x609a 0x609b 0x609c 0x609d 0x609e 0x609f 0x60a0 0x60a1 0x60a2 0x60a3 0x60a4 0x60a5 0x60a6 0x60a7 0x60a8 0x60a9 0x60aa 0x60ab 0x60ac 0x60ad 0x60ae 0x60af 0x60b0 0x60b1 0x60b2 0x60b3 0x60b4 0x60b5 0x60b6 0x60b7 0x60b8 0x60b9 0x60ba 0x60bb 0x60bc 0x60bd 0x60be 0x60bf 0x60c0 0x60c1 0x60c2 0x60c3 0x60c4 0x60c5 0x60c6 0x60c7 0x60c8 0x60c9 0x60ca 0x60cb 0x60cc 0x60cd 0x60ce 0x60cf 0x60d0 0x60d1 0x60d2 0x60d3 0x60d4 0x60d5 0x60d6 0x60d7 0x60d8 0x60d9 0x60da 0x60db 0x60dc 0x60dd 0x60de 0x60df 0x60e0 0x60e1 0x60e2 0x60e3 0x60e4 0x60e5 0x60e6 0x60e7 0x60e8 0x60e9 0x60ea 0x60eb 0x60ec 0x60ed 0x60ee 0x60ef 0x60f0 0x60f1 0x60f2 0x60f3 0x60f4 0x60f5 0x60f6 0x60f7 0x60f8 0x60f9 0x60fa 0x60fb 0x60fc 0x60fd ] [ 0x6001 0x6002 0x6003 0x6004 0x6005 0x6006 0x6007 0x6008 0x6009 0x600a 0x600b 0x600c 0x600d 0x600e 0x600f 0x6010 0x6011 0x6012 0x6013 0x6014 0x6015 0x6016 0x6017 0x6018 0x6019 0x601a 0x601b 0x601c 0x601d 0x601e 0x601f 0x6020 0x6021 0x6022 0x6023 0x6024 0x6025 0x6026 0x6027 0x6028 0x6029 0x602a 0x602b 0x602c 0x602d 0x602e 0x602f 0x6030 0x6031 0x6032 0x6033 0x6034 0x6035 0x6036 0x6037 0x6038 0x6039 0x603a 0x603b 0x603c 0x603d 0x603e 0x603f 0x6040 0x6041 0x6042 0x6043 0x6044 0x6045 0x6046 0x6047 0x6048 0x6049 0x604a 0x604b 0x604c 0x604d 0x604e 0x604f 0x6050 0x6051 0x6052 0x6053 0x6054 0x6055 0x6056 0x6057 0x6058 0x6059 0x605a 0x605b 0x605c 0x605d 0x605e 0x605f 0x6060 0x6061 0x6062 0x6063 0x6064 0x6065 0x6066 0x6067 0x6068 0x6069 0x606a 0x606b 0x606c 0x606d 0x606e 0x606f 0x6070 0x6071 0x6072 0x6073 0x6074 0x6075 0x6076 0x6077 0x6078 0x6079 0x607a 0x607b 0x607c 0x607d 0x607e 0x607f 0x6080 0x6081 0x6082 0x6083 0x6084 0x6085 0x6086 0x6087 0x6088 0x6089 0x608a 0x608b 0x608c 0x608d 0x608e 0x608f 0x6090 0x6091 0x6092 0x6093 0x6094 0x6095 0x6096 0x6097 0x6098 0x6099 0x609a 0x609b 0x609c 0x609d 0x609e 0x609f 0x60a0 0x60a1 0x60a2 0x60a3 0x60a4 0x60a5 0x60a6 0x60a7 0x60a8 0x60a9 0x60aa 0x60ab 0x60ac 0x60ad 0x60ae 0x60af 0x60b0 0x60b1 0x60b2 0x60b3 0x60b4 0x60b5 0x60b6 0x60b7 0x60b8 0x60b9 0x60ba 0x60bb 0x60bc 0x60bd 0x60be 0x60bf 0x60c0 0x60c1 0x60c2 0x60c3 0x60c4 0x60c5 0x60c6 0x60c7 0x60c8 0x60c9 0x60ca 0x60cb 0x60cc 0x60cd 0x60ce 0x60cf 0x60d0 0x60d1 0x60d2 0x60d3 0x60d4 0x60d5 0x60d6 0x60d7 0x60d8 0x60d9 0x60da 0x60db 0x60dc 0x60dd 0x60de 0x60df 0x60e0 0x60e1 0x60e2 0x60e3 0x60e4 0x60e5 0x60e6 0x60e7 0x60e8 0x60e9 0x60ea 0x60eb 0x60ec 0x60ed 0x60ee 0x60ef 0x60f0 0x60f1 0x60f2 0x60f3 0x60f4 0x60f5 0x60f6 0x60f7 0x60f8 0x60f9 0x60fa 0x60fb 0x60fc 0x60fd 0x60fe ] [ 0x6002 0x6003 0x6004 0x6005 0x6006 0x6007 0x6008 0x6009 0x600a 0x600b 0x600c 0x600d 0x600e 0x600f 0x6010 0x6011 0x6012 0x6013 0x6014 0x6015 0x6016 0x6017 0x6018 0x6019 0x601a 0x601b 0x601c 0x601d 0x601e 0x601f 0x6020 0x6021 0x6022 0x6023 0x6024 0x6025 0x6026 0x6027 0x6028 0x6029 0x602a 0x602b 0x602c 0x602d 0x602e 0x602f 0x6030 0x6031 0x6032 0x6033 0x6034 0x6035 0x6036 0x6037 0x6038 0x6039 0x603a 0x603b 0x603c 0x603d 0x603e 0x603f 0x6040 0x6041 0x6042 0x6043 0x6044 0x6045 0x6046 0x6047 0x6048 0x6049 0x604a 0x604b 0x604c 0x604d 0x604e 0x604f 0x6050 0x6051 0x6052 0x6053 0x6054 0x6055 0x6056 0x6057 0x6058 0x6059 0x605a 0x605b 0x605c 0x605d 0x605e 0x605f 0x6060 0x6061 0x6062 0x6063 0x6064 0x6065 0x6066 0x6067 0x6068 0x6069 0x606a 0x606b 0x606c 0x606d 0x606e 0x606f 0x6070 0x6071 0x6072 0x6073 0x6074 0x6075 0x6076 0x6077 0x6078 0x6079 0x607a 0x607b 0x607c 0x607d 0x607e 0x607f 0x6080 0x6081 0x6082 0x6083 0x6084 0x6085 0x6086 0x6087 0x6088 0x6089 0x608a 0x608b 0x608c 0x608d 0x608e 0x608f 0x6090 0x6091 0x6092 0x6093 0x6094 0x6095 0x6096 0x6097 0x6098 0x6099 0x609a 0x609b 0x609c 0x609d 0x609e 0x609f 0x60a0 0x60a1 0x60a2 0x60a3 0x60a4 0x60a5 0x60a6 0x60a7 0x60a8 0x60a9 0x60aa 0x60ab 0x60ac 0x60ad 0x60ae 0x60af 0x60b0 0x60b1 0x60b2 0x60b3 0x60b4 0x60b5 0x60b6 0x60b7 0x60b8 0x60b9 0x60ba 0x60bb 0x60bc 0x60bd 0x60be 0x60bf 0x60c0 0x60c1 0x60c2 0x60c3 0x60c4 0x60c5 0x60c6 0x60c7 0x60c8 0x60c9 0x60ca 0x60cb 0x60cc 0x60cd 0x60ce 0x60cf 0x60d0 0x60d1 0x60d2 0x60d3 0x60d4 0x60d5 0x60d6 0x60d7 0x60d8 0x60d9 0x60da 0x60db 0x60dc 0x60dd 0x60de 0x60df 0x60e0 0x60e1 0x60e2 0x60e3 0x60e4 0x60e5 0x60e6 0x60e7 0x60e8 0x60e9 0x60ea 0x60eb 0x60ec 0x60ed 0x60ee 0x60ef 0x60f0 0x60f1 0x60f2 0x60f3 0x60f4 0x60f5 0x60f6 0x60f7 0x60f8 0x60f9 0x60fa 0x60fb 0x60fc 0x60fd 0x60fe 0x60ff ] [ 0x6003 0x6004 0x6005 0x6006 0x6007 0x6008 0x6009 0x600a 0x600b 0x600c 0x600d 0x600e 0x600f 0x6010 0x6011 0x6012 0x6013 0x6014 0x6015 0x6016 0x6017 0x6018 0x6019 0x601a 0x601b 0x601c 0x601d 0x601e 0x601f 0x6020 0x6021 0x6022 0x6023 0x6024 0x6025 0x6026 0x6027 0x6028 0x6029 0x602a 0x602b 0x602c 0x602d 0x602e 0x602f 0x6030 0x6031 0x6032 0x6033 0x6034 0x6035 0x6036 0x6037 0x6038 0x6039 0x603a 0x603b 0x603c 0x603d 0x603e 0x603f 0x6040 0x6041 0x6042 0x6043 0x6044 0x6045 0x6046 0x6047 0x6048 0x6049 0x604a 0x604b 0x604c 0x604d 0x604e 0x604f 0x6050 0x6051 0x6052 0x6053 0x6054 0x6055 0x6056 0x6057 0x6058 0x6059 0x605a 0x605b 0x605c 0x605d 0x605e 0x605f 0x6060 0x6061 0x6062 0x6063 0x6064 0x6065 0x6066 0x6067 0x6068 0x6069 0x606a 0x606b 0x606c 0x606d 0x606e 0x606f 0x6070 0x6071 0x6072 0x6073 0x6074 0x6075 0x6076 0x6077 0x6078 0x6079 0x607a 0x607b 0x607c 0x607d 0x607e 0x607f 0x6080 0x6081 0x6082 0x6083 0x6084 0x6085 0x6086 0x6087 0x6088 0x6089 0x608a 0x608b 0x608c 0x608d 0x608e 0x608f 0x6090 0x6091 0x6092 0x6093 0x6094 0x6095 0x6096 0x6097 0x6098 0x6099 0x609a 0x609b 0x609c 0x609d 0x609e 0x609f 0x60a0 0x60a1 0x60a2 0x60a3 0x60a4 0x60a5 0x60a6 0x60a7 0x60a8 0x60a9 0x60aa 0x60ab 0x60ac 0x60ad 0x60ae 0x60af 0x60b0 0x60b1 0x60b2 0x60b3 0x60b4 0x60b5 0x60b6 0x60b7 0x60b8 0x60b9 0x60ba 0x60bb 0x60bc 0x60bd 0x60be 0x60bf 0x60c0 0x60c1 0x60c2 0x60c3 0x60c4 0x60c5 0x60c6 0x60c7 0x60c8 0x60c9 0x60ca 0x60cb 0x60cc 0x60cd 0x60ce 0x60cf 0x60d0 0x60d1 0x60d2 0x60d3 0x60d4 0x60d5 0x60d6 0x60d7 0x60d8 0x60d9 0x60da 0x60db 0x60dc 0x60dd 0x60de 0x60df 0x60e0 0x60e1 0x60e2 0x60e3 0x60e4 0x60e5 0x60e6 0x60e7 0x60e8 0x60e9 0x60ea 0x60eb 0x60ec 0x60ed 0x60ee 0x60ef 0x60f0 0x60f1 0x60f2 0x60f3 0x60f4 0x60f5 0x60f6 0x60f7 0x60f8 0x60f9 0x60fa 0x60fb 0x60fc 0x60fd 0x60fe 0x60ff 0x6100 ] [ 0x6004 0x6005 0x6006 0x6007 0x6008 0x6009 0x600a 0x600b 0x600c 0x600d 0x600e 0x600f 0x6010 0x6011 0x6012 0x6013 0x6014 0x6015 0x6016 0x6017 0x6018 0x6019 0x601a 0x601b 0x601c 0x601d 0x601e 0x601f 0x6020 0x6021 0x6022 0x6023 0x6024 0x6025 0x6026 0x6027 0x6028 0x6029 0x602a 0x602b 0x602c 0x602d 0x602e 0x602f 0x6030 0x6031 0x6032 0x6033 0x6034 0x6035 0x6036 0x6037 0x6038 0x6039 0x603a 0x603b 0x603c 0x603d 0x603e 0x603f 0x6040 0x6041 0x6042 0x6043 0x6044 0x6045 0x6046 0x6047 0x6048 0x6049 0x604a 0x604b 0x604c 0x604d 0x604e 0x604f 0x6050 0x6051 0x6052 0x6053 0x6054 0x6055 0x6056 0x6057 0x6058 0x6059 0x605a 0x605b 0x605c 0x605d 0x605e 0x605f 0x6060 0x6061 0x6062 0x6063 0x6064 0x6065 0x6066 0x6067 0x6068 0x6069 0x606a 0x606b 0x606c 0x606d 0x606e 0x606f 0x6070 0x6071 0x6072 0x6073 0x6074 0x6075 0x6076 0x6077 0x6078 0x6079 0x607a 0x607b 0x607c 0x607d 0x607e 0x607f 0x6080 0x6081 0x6082 0x6083 0x6084 0x6085 0x6086 0x6087 0x6088 0x6089 0x608a 0x608b 0x608c 0x608d 0x608e 0x608f 0x6090 0x6091 0x6092 0x6093 0x6094 0x6095 0x6096 0x6097 0x6098 0x6099 0x609a 0x609b 0x609c 0x609d 0x609e 0x609f 0x60a0 0x60a1 0x60a2 0x60a3 0x60a4 0x60a5 0x60a6 0x60a7 0x60a8 0x60a9 0x60aa 0x60ab 0x60ac 0x60ad 0x60ae 0x60af 0x60b0 0x60b1 0x60b2 0x60b3 0x60b4 0x60b5 0x60b6 0x60b7 0x60b8 0x60b9 0x60ba 0x60bb 0x60bc 0x60bd 0x60be 0x60bf 0x60c0 0x60c1 0x60c2 0x60c3 0x60c4 0x60c5 0x60c6 0x60c7 0x60c8 0x60c9 0x60ca 0x60cb 0x60cc 0x60cd 0x60ce 0x60cf 0x60d0 0x60d1 0x60d2 0x60d3 0x60d4 0x60d5 0x60d6 0x60d7 0x60d8 0x60d9 0x60da 0x60db 0x60dc 0x60dd 0x60de 0x60df 0x60e0 0x60e1 0x60e2 0x60e3 0x60e4 0x60e5 0x60e6 0x60e7 0x60e8 0x60e9 0x60ea 0x60eb 0x60ec 0x60ed 0x60ee 0x60ef 0x60f0 0x60f1 0x60f2 0x60f3 0x60f4 0x60f5 0x60f6 0x60f7 0x60f8 0x60f9 0x60fa 0x60fb 0x60fc 0x60fd 0x60fe 0x60ff 0x6100 0x6101 ] [ 0x6005 0x6006 0x6007 0x6008 0x6009 0x600a 0x600b 0x600c 0x600d 0x600e 0x600f 0x6010 0x6011 0x6012 0x6013 0x6014 0x6015 0x6016 0x6017 0x6018 0x6019 0x601a 0x601b 0x601c 0x601d 0x601e 0x601f 0x6020 0x6021 0x6022 0x6023 0x6024 0x6025 0x6026 0x6027 0x6028 0x6029 0x602a 0x602b 0x602c 0x602d 0x602e 0x602f 0x6030 0x6031 0x6032 0x6033 0x6034 0x6035 0x6036 0x6037 0x6038 0x6039 0x603a 0x603b 0x603c 0x603d 0x603e 0x603f 0x6040 0x6041 0x6042 0x6043 0x6044 0x6045 0x6046 0x6047 0x6048 0x6049 0x604a 0x604b 0x604c 0x604d 0x604e 0x604f 0x6050 0x6051 0x6052 0x6053 0x6054 0x6055 0x6056 0x6057 0x6058 0x6059 0x605a 0x605b 0x605c 0x605d 0x605e 0x605f 0x6060 0x6061 0x6062 0x6063 0x6064 0x6065 0x6066 0x6067 0x6068 0x6069 0x606a 0x606b 0x606c 0x606d 0x606e 0x606f 0x6070 0x6071 0x6072 0x6073 0x6074 0x6075 0x6076 0x6077 0x6078 0x6079 0x607a 0x607b 0x607c 0x607d 0x607e 0x607f 0x6080 0x6081 0x6082 0x6083 0x6084 0x6085 0x6086 0x6087 0x6088 0x6089 0x608a 0x608b 0x608c 0x608d 0x608e 0x608f 0x6090 0x6091 0x6092 0x6093 0x6094 0x6095 0x6096 0x6097 0x6098 0x6099 0x609a 0x609b 0x609c 0x609d 0x609e 0x609f 0x60a0 0x60a1 0x60a2 0x60a3 0x60a4 0x60a5 0x60a6 0x60a7 0x60a8 0x60a9 0x60aa 0x60ab 0x60ac 0x60ad 0x60ae 0x60af 0x60b0 0x60b1 0x60b2 0x60b3 0x60b4 0x60b5 0x60b6 0x60b7 0x60b8 0x60b9 0x60ba 0x60bb 0x60bc 0x60bd 0x60be 0x60bf 0x60c0 0x60c1 0x60c2 0x60c3 0x60c4 0x60c5 0x60c6 0x60c7 0x60c8 0x60c9 0x60ca 0x60cb 0x60cc 0x60cd 0x60ce 0x60cf 0x60d0 0x60d1 0x60d2 0x60d3 0x60d4 0x60d5 0x60d6 0x60d7 0x60d8 0x60d9 0x60da 0x60db 0x60dc 0x60dd 0x60de 0x60df 0x60e0 0x60e1 0x60e2 0x60e3 0x60e4 0x60e5 0x60e6 0x60e7 0x60e8 0x60e9 0x60ea 0x60eb 0x60ec 0x60ed 0x60ee 0x60ef 0x60f0 0x60f1 0x60f2 0x60f3 0x60f4 0x60f5 0x60f6 0x60f7 0x60f8 0x60f9 0x60fa 0x60fb 0x60fc 0x60fd 0x60fe 0x60ff 0x6100 0x6101 0x6102 ] [ 0x6006 0x6007 0x6008 0x6009 0x600a 0x600b 0x600c 0x600d 0x600e 0x600f 0x6010 0x6011 0x6012 0x6013 0x6014 0x6015 0x6016 0x6017 0x6018 0x6019 0x601a 0x601b 0x601c 0x601d 0x601e 0x601f 0x6020 0x6021 0x6022 0x6023 0x6024 0x6025 0x6026 0x6027 0x6028 0x6029 0x602a 0x602b 0x602c 0x602d 0x602e 0x602f 0x6030 0x6031 0x6032 0x6033 0x6034 0x6035 0x6036 0x6037 0x6038 0x6039 0x603a 0x603b 0x603c 0x603d 0x603e 0x603f 0x6040 0x6041 0x6042 0x6043 0x6044 0x6045 0x6046 0x6047 0x6048 0x6049 0x604a 0x604b 0x604c 0x604d 0x604e 0x604f 0x6050 0x6051 0x6052 0x6053 0x6054 0x6055 0x6056 0x6057 0x6058 0x6059 0x605a 0x605b 0x605c 0x605d 0x605e 0x605f 0x6060 0x6061 0x6062 0x6063 0x6064 0x6065 0x6066 0x6067 0x6068 0x6069 0x606a 0x606b 0x606c 0x606d 0x606e 0x606f 0x6070 0x6071 0x6072 0x6073 0x6074 0x6075 0x6076 0x6077 0x6078 0x6079 0x607a 0x607b 0x607c 0x607d 0x607e 0x607f 0x6080 0x6081 0x6082 0x6083 0x6084 0x6085 0x6086 0x6087 0x6088 0x6089 0x608a 0x608b 0x608c 0x608d 0x608e 0x608f 0x6090 0x6091 0x6092 0x6093 0x6094 0x6095 0x6096 0x6097 0x6098 0x6099 0x609a 0x609b 0x609c 0x609d 0x609e 0x609f 0x60a0 0x60a1 0x60a2 0x60a3 0x60a4 0x60a5 0x60a6 0x60a7 0x60a8 0x60a9 0x60aa 0x60ab 0x60ac 0x60ad 0x60ae 0x60af 0x60b0 0x60b1 0x60b2 0x60b3 0x60b4 0x60b5 0x60b6 0x60b7 0x60b8 0x60b9 0x60ba 0x60bb 0x60bc 0x60bd 0x60be 0x60bf 0x60c0 0x60c1 0x60c2 0x60c3 0x60c4 0x60c5 0x60c6 0x60c7 0x60c8 0x60c9 0x60ca 0x60cb 0x60cc 0x60cd 0x60ce 0x60cf 0x60d0 0x60d1 0x60d2 0x60d3 0x60d4 0x60d5 0x60d6 0x60d7 0x60d8 0x60d9 0x60da 0x60db 0x60dc 0x60dd 0x60de 0x60df 0x60e0 0x60e1 0x60e2 0x60e3 0x60e4 0x60e5 0x60e6 0x60e7 0x60e8 0x60e9 0x60ea 0x60eb 0x60ec 0x60ed 0x60ee 0x60ef 0x60f0 0x60f1 0x60f2 0x60f3 0x60f4 0x60f5 0x60f6 0x60f7 0x60f8 0x60f9 0x60fa 0x60fb 0x60fc 0x60fd 0x60fe 0x60ff 0x6100 0x6101 0x6102 0x6103 ] [ 0x6007 0x6008 0x6009 0x600a 0x600b 0x600c 0x600d 0x600e 0x600f 0x6010 0x6011 0x6012 0x6013 0x6014 0x6015 0x6016 0x6017 0x6018 0x6019 0x601a 0x601b 0x601c 0x601d 0x601e 0x601f 0x6020 0x6021 0x6022 0x6023 0x6024 0x6025 0x6026 0x6027 0x6028 0x6029 0x602a 0x602b 0x602c 0x602d 0x602e 0x602f 0x6030 0x6031 0x6032 0x6033 0x6034 0x6035 0x6036 0x6037 0x6038 0x6039 0x603a 0x603b 0x603c 0x603d 0x603e 0x603f 0x6040 0x6041 0x6042 0x6043 0x6044 0x6045 0x6046 0x6047 0x6048 0x6049 0x604a 0x604b 0x604c 0x604d 0x604e 0x604f 0x6050 0x6051 0x6052 0x6053 0x6054 0x6055 0x6056 0x6057 0x6058 0x6059 0x605a 0x605b 0x605c 0x605d 0x605e 0x605f 0x6060 0x6061 0x6062 0x6063 0x6064 0x6065 0x6066 0x6067 0x6068 0x6069 0x606a 0x606b 0x606c 0x606d 0x606e 0x606f 0x6070 0x6071 0x6072 0x6073 0x6074 0x6075 0x6076 0x6077 0x6078 0x6079 0x607a 0x607b 0x607c 0x607d 0x607e 0x607f 0x6080 0x6081 0x6082 0x6083 0x6084 0x6085 0x6086 0x6087 0x6088 0x6089 0x608a 0x608b 0x608c 0x608d 0x608e 0x608f 0x6090 0x6091 0x6092 0x6093 0x6094 0x6095 0x6096 0x6097 0x6098 0x6099 0x609a 0x609b 0x609c 0x609d 0x609e 0x609f 0x60a0 0x60a1 0x60a2 0x60a3 0x60a4 0x60a5 0x60a6 0x60a7 0x60a8 0x60a9 0x60aa 0x60ab 0x60ac 0x60ad 0x60ae 0x60af 0x60b0 0x60b1 0x60b2 0x60b3 0x60b4 0x60b5 0x60b6 0x60b7 0x60b8 0x60b9 0x60ba 0x60bb 0x60bc 0x60bd 0x60be 0x60bf 0x60c0 0x60c1 0x60c2 0x60c3 0x60c4 0x60c5 0x60c6 0x60c7 0x60c8 0x60c9 0x60ca 0x60cb 0x60cc 0x60cd 0x60ce 0x60cf 0x60d0 0x60d1 0x60d2 0x60d3 0x60d4 0x60d5 0x60d6 0x60d7 0x60d8 0x60d9 0x60da 0x60db 0x60dc 0x60dd 0x60de 0x60df 0x60e0 0x60e1 0x60e2 0x60e3 0x60e4 0x60e5 0x60e6 0x60e7 0x60e8 0x60e9 0x60ea 0x60eb 0x60ec 0x60ed 0x60ee 0x60ef 0x60f0 0x60f1 0x60f2 0x60f3 0x60f4 0x60f5 0x60f6 0x60f7 0x60f8 0x60f9 0x60fa 0x60fb 0x60fc 0x60fd 0x60fe 0x60ff 0x6100 0x6101 0x6102 0x6103 0x6104 ] ] new_state_reads_sibling_paths: diff --git a/yarn-project/circuits.js/src/structs/shared.ts b/yarn-project/circuits.js/src/structs/shared.ts index e208972d59e..2e09eab5972 100644 --- a/yarn-project/circuits.js/src/structs/shared.ts +++ b/yarn-project/circuits.js/src/structs/shared.ts @@ -1,3 +1,4 @@ +import { BufferReader } from '@aztec/foundation/serialize'; import { assertMemberLength } from '../utils/jsUtils.js'; import { Bufferable, serializeToBuffer } from '../utils/serialize.js'; import { randomBytes } from '@aztec/foundation/crypto'; @@ -55,6 +56,11 @@ export class EcdsaSignature { return serializeToBuffer(this.r, this.s, this.v); } + static fromBuffer(buffer: Buffer | BufferReader): EcdsaSignature { + const reader = BufferReader.asReader(buffer); + return new EcdsaSignature(reader.readBytes(32), reader.readBytes(32), reader.readBytes(1)); + } + /** * Returns a random/placeholder ECDSA signature. * @returns A random placeholder ECDSA signature. diff --git a/yarn-project/circuits.js/src/structs/tx_request.ts b/yarn-project/circuits.js/src/structs/tx_request.ts index ec72b259e10..d46eed6202c 100644 --- a/yarn-project/circuits.js/src/structs/tx_request.ts +++ b/yarn-project/circuits.js/src/structs/tx_request.ts @@ -7,6 +7,7 @@ import { ARGS_LENGTH } from './constants.js'; import { FunctionData } from './function_data.js'; import { EcdsaSignature } from './shared.js'; import { TxContext } from './tx_context.js'; +import { BufferReader } from '@aztec/foundation/serialize'; /** * Signed transaction request. @@ -31,6 +32,16 @@ export class SignedTxRequest { toBuffer() { return serializeToBuffer(this.txRequest, this.signature); } + + /** + * Deserialises from a buffer. + * @param buffer - The buffer representation of the object. + * @returns The new object. + */ + static fromBuffer(buffer: Buffer | BufferReader): SignedTxRequest { + const reader = BufferReader.asReader(buffer); + return new SignedTxRequest(reader.readObject(TxRequest), reader.readObject(EcdsaSignature)); + } } /** @@ -117,6 +128,25 @@ export class TxRequest { * @returns The buffer. */ toBuffer() { - return serializeToBuffer(...TxRequest.getFields(this)); + const fields = TxRequest.getFields(this); + return serializeToBuffer([...fields]); + } + + /** + * Deserializes from a buffer or reader, corresponding to a write in cpp. + * @param buffer - Buffer to read from. + * @returns The deserialised TxRequest object. + */ + static fromBuffer(buffer: Buffer | BufferReader): TxRequest { + const reader = BufferReader.asReader(buffer); + return new TxRequest( + reader.readObject(AztecAddress), + reader.readObject(AztecAddress), + reader.readObject(FunctionData), + reader.readArray(ARGS_LENGTH, Fr), + reader.readFr(), + reader.readObject(TxContext), + reader.readFr(), + ); } } diff --git a/yarn-project/end-to-end/package.json b/yarn-project/end-to-end/package.json index 0f3d3e3da91..8a3318fbc34 100644 --- a/yarn-project/end-to-end/package.json +++ b/yarn-project/end-to-end/package.json @@ -27,9 +27,11 @@ "@aztec/aztec-node": "workspace:^", "@aztec/aztec.js": "workspace:^", "@aztec/circuits.js": "workspace:^", + "@aztec/ethereum": "workspace:^", "@aztec/foundation": "workspace:^", "@aztec/l1-artifacts": "workspace:^", "@aztec/noir-contracts": "workspace:^", + "@aztec/p2p": "workspace:^", "@aztec/sequencer-client": "workspace:^", "@aztec/world-state": "workspace:^", "@jest/globals": "^29.5.0", @@ -45,7 +47,7 @@ "ts-node": "^10.9.1", "tslib": "^2.4.0", "typescript": "^5.0.4", - "viem": "^0.3.13" + "viem": "^0.3.14" }, "devDependencies": { "@rushstack/eslint-patch": "^1.1.4", diff --git a/yarn-project/end-to-end/scripts/docker-compose-anvil.yml b/yarn-project/end-to-end/scripts/docker-compose-anvil.yml new file mode 100644 index 00000000000..5e69fbea59d --- /dev/null +++ b/yarn-project/end-to-end/scripts/docker-compose-anvil.yml @@ -0,0 +1,7 @@ +version: '3' +services: + fork: + image: ghcr.io/foundry-rs/foundry:nightly-a44aa13cfc23491ba32aaedc093e9488c1a6db43 + entrypoint: 'anvil -p 8545 --host 0.0.0.0 --chain-id 31337' + ports: + - '8545:8545' diff --git a/yarn-project/end-to-end/scripts/docker-compose-p2p.yml b/yarn-project/end-to-end/scripts/docker-compose-p2p.yml new file mode 100644 index 00000000000..851641e664b --- /dev/null +++ b/yarn-project/end-to-end/scripts/docker-compose-p2p.yml @@ -0,0 +1,38 @@ +version: '3' +services: + fork: + image: ghcr.io/foundry-rs/foundry:nightly-a44aa13cfc23491ba32aaedc093e9488c1a6db43 + entrypoint: 'anvil -p 8545 --host 0.0.0.0 --chain-id 31337' + ports: + - '8545:8545' + + p2p-bootstrap: + image: aztecprotocol/p2p-bootstrap:latest + ports: + - '40400:40400' + command: 'start' + environment: + DEBUG: 'aztec:*' + P2P_TCP_LISTEN_PORT: 40400 + P2P_TCP_LISTEN_IP: '0.0.0.0' + P2P_ANNOUNCE_HOSTNAME: 'p2p-bootstrap' + PEER_ID_PRIVATE_KEY: '0a260024080112205ea53185db2e52dae74d0d4d6cadc494174810d0a713cd09b0ac517c38bc781e1224080112205ea53185db2e52dae74d0d4d6cadc494174810d0a713cd09b0ac517c38bc781e1a44080112402df8b977f356c6e34fa021c9647973234dff4df706c185794405aafb556723cf5ea53185db2e52dae74d0d4d6cadc494174810d0a713cd09b0ac517c38bc781e' + + end-to-end: + image: aztecprotocol/end-to-end:latest + environment: + DEBUG: 'aztec:*' + ETHEREUM_HOST: http://fork:8545 + CHAIN_ID: 31337 + ARCHIVER_POLLING_INTERVAL: 500 + P2P_CHECK_INTERVAL: 50 + SEQ_TX_POLLING_INTERVAL: 50 + WS_CHECK_INTERVAL: 50 + SEQ_MAX_TX_PER_BLOCK: 32 + SEQ_MIN_TX_PER_BLOCK: 1 + P2P_TCP_LISTEN_IP: '0.0.0.0' + P2P_NAT_ENABLED: 'false' + P2P_SERVER: 'false' + P2P_ENABLED: 'true' + BOOTSTRAP_NODES: '/ip4/p2p-bootstrap/tcp/40400/p2p/12D3KooWGBpbC6qQFkaCYphjNeY6sV99o4SnEWyTeBigoVriDn4D' + command: ${TEST:-./src/e2e_p2p_network.test.ts} diff --git a/yarn-project/end-to-end/scripts/run_tests b/yarn-project/end-to-end/scripts/run_tests index 3eb686baab6..64760509aac 100755 --- a/yarn-project/end-to-end/scripts/run_tests +++ b/yarn-project/end-to-end/scripts/run_tests @@ -4,6 +4,7 @@ set -e export TEST=$1 +export COMPOSE_FILE=${2:-docker-compose.yml} if [ -n "$COMMIT_HASH" ]; then $(aws ecr get-login --region us-east-2 --no-include-email) 2> /dev/null @@ -15,4 +16,4 @@ if [ -n "$COMMIT_HASH" ]; then fi docker-compose rm -f -docker-compose up --exit-code-from end-to-end \ No newline at end of file +docker-compose -f $COMPOSE_FILE up --exit-code-from end-to-end \ No newline at end of file diff --git a/yarn-project/end-to-end/scripts/start_p2p_e2e.sh b/yarn-project/end-to-end/scripts/start_p2p_e2e.sh new file mode 100755 index 00000000000..144fda44211 --- /dev/null +++ b/yarn-project/end-to-end/scripts/start_p2p_e2e.sh @@ -0,0 +1,17 @@ +#! /bin/bash +set -eu +export DEBUG='aztec:*' +export ARCHIVER_POLLING_INTERVAL=500 +export P2P_CHECK_INTERVAL=50 +export WS_CHECK_INTERVAL=50 +export SEQ_TX_POLLING_INTERVAL=50 +export SEQ_MAX_TX_PER_BLOCK=32 +export SEQ_MIN_TX_PER_BLOCK=32 +export BOOTSTRAP_NODES='/ip4/127.0.0.1/tcp/40400/p2p/12D3KooWGBpbC6qQFkaCYphjNeY6sV99o4SnEWyTeBigoVriDn4D' +export P2P_TCP_LISTEN_IP='0.0.0.0' +export P2P_NAT_ENABLED='false' +export P2P_SERVER='false' +export P2P_ENABLED='true' +export DEBUG='aztec:*,libp2p:*' + +yarn test e2e_p2p_network.test.ts \ No newline at end of file diff --git a/yarn-project/end-to-end/src/e2e_account_contract.test.ts b/yarn-project/end-to-end/src/e2e_account_contract.test.ts index 58e3b2f5e77..761906739c7 100644 --- a/yarn-project/end-to-end/src/e2e_account_contract.test.ts +++ b/yarn-project/end-to-end/src/e2e_account_contract.test.ts @@ -1,5 +1,5 @@ /* eslint-disable camelcase */ -import { AztecNode, getConfigEnvVars } from '@aztec/aztec-node'; +import { AztecNodeService, getConfigEnvVars } from '@aztec/aztec-node'; import { AztecAddress, AztecRPCServer, @@ -21,15 +21,15 @@ import { secp256k1 } from '@noble/curves/secp256k1'; import times from 'lodash.times'; import { mnemonicToAccount } from 'viem/accounts'; import { createAztecRpcServer } from './create_aztec_rpc_client.js'; -import { deployL1Contracts } from './deploy_l1_contracts.js'; -import { MNEMONIC } from './fixtures.js'; +import { deployL1Contracts } from '@aztec/ethereum'; +import { MNEMONIC, localAnvil } from './fixtures.js'; const logger = createDebugLogger('aztec:e2e_account_contract'); const config = getConfigEnvVars(); describe('e2e_account_contract', () => { - let node: AztecNode; + let node: AztecNodeService; let aztecRpcServer: AztecRPCServer; let accounts: AztecAddress[]; @@ -39,13 +39,18 @@ describe('e2e_account_contract', () => { beforeEach(async () => { const hdAccount = mnemonicToAccount(MNEMONIC); const privKey = hdAccount.getHdKey().privateKey; - const { rollupAddress, unverifiedDataEmitterAddress } = await deployL1Contracts(config.rpcUrl, hdAccount, logger); + const { rollupAddress, unverifiedDataEmitterAddress } = await deployL1Contracts( + config.rpcUrl, + hdAccount, + localAnvil, + logger, + ); config.publisherPrivateKey = Buffer.from(privKey!); config.rollupContract = rollupAddress; config.unverifiedDataEmitterContract = unverifiedDataEmitterAddress; - node = await AztecNode.createAndSync(config); + node = await AztecNodeService.createAndSync(config); aztecRpcServer = await createAztecRpcServer(1, node); accounts = await aztecRpcServer.getAccounts(); diff --git a/yarn-project/end-to-end/src/e2e_block_building.test.ts b/yarn-project/end-to-end/src/e2e_block_building.test.ts index 44235781c14..5a43eb96097 100644 --- a/yarn-project/end-to-end/src/e2e_block_building.test.ts +++ b/yarn-project/end-to-end/src/e2e_block_building.test.ts @@ -1,19 +1,19 @@ -import { AztecNode, getConfigEnvVars } from '@aztec/aztec-node'; +import { AztecNodeService, getConfigEnvVars } from '@aztec/aztec-node'; import { AztecRPCServer, ContractDeployer, Fr, TxStatus } from '@aztec/aztec.js'; import { mnemonicToAccount } from 'viem/accounts'; import { createAztecRpcServer } from './create_aztec_rpc_client.js'; -import { deployL1Contracts } from './deploy_l1_contracts.js'; +import { deployL1Contracts } from '@aztec/ethereum'; import { TestContractAbi } from '@aztec/noir-contracts/examples'; import times from 'lodash.times'; import { createDebugLogger } from '@aztec/foundation/log'; -import { MNEMONIC } from './fixtures.js'; +import { MNEMONIC, localAnvil } from './fixtures.js'; const logger = createDebugLogger('aztec:e2e_block_building'); const config = getConfigEnvVars(); describe('e2e_block_building', () => { - let node: AztecNode; + let node: AztecNodeService; let aztecRpcServer: AztecRPCServer; const abi = TestContractAbi; @@ -21,13 +21,18 @@ describe('e2e_block_building', () => { beforeEach(async () => { const account = mnemonicToAccount(MNEMONIC); const privKey = account.getHdKey().privateKey; - const { rollupAddress, unverifiedDataEmitterAddress } = await deployL1Contracts(config.rpcUrl, account, logger); + const { rollupAddress, unverifiedDataEmitterAddress } = await deployL1Contracts( + config.rpcUrl, + account, + localAnvil, + logger, + ); config.publisherPrivateKey = Buffer.from(privKey!); config.rollupContract = rollupAddress; config.unverifiedDataEmitterContract = unverifiedDataEmitterAddress; - node = await AztecNode.createAndSync(config); + node = await AztecNodeService.createAndSync(config); aztecRpcServer = await createAztecRpcServer(1, node); }, 60_000); diff --git a/yarn-project/end-to-end/src/e2e_consume_l1_to_l2_msg.test.ts b/yarn-project/end-to-end/src/e2e_consume_l1_to_l2_msg.test.ts index 85e53611238..526f887d8bb 100644 --- a/yarn-project/end-to-end/src/e2e_consume_l1_to_l2_msg.test.ts +++ b/yarn-project/end-to-end/src/e2e_consume_l1_to_l2_msg.test.ts @@ -1,15 +1,15 @@ -import { AztecNode, getConfigEnvVars } from '@aztec/aztec-node'; +import { AztecNodeService, getConfigEnvVars } from '@aztec/aztec-node'; import { EthAddress } from '@aztec/foundation/eth-address'; import { AztecAddress, AztecRPCServer, Contract, ContractDeployer, TxStatus } from '@aztec/aztec.js'; import { NonNativeTokenContractAbi } from '@aztec/noir-contracts/examples'; import { Account, mnemonicToAccount } from 'viem/accounts'; import { createAztecRpcServer } from './create_aztec_rpc_client.js'; -import { deployL1Contract, deployL1Contracts } from './deploy_l1_contracts.js'; +import { deployL1Contract, deployL1Contracts } from '@aztec/ethereum'; import { createDebugLogger } from '@aztec/foundation/log'; import { Fr, Point } from '@aztec/foundation/fields'; import { toBigIntBE } from '@aztec/foundation/bigint-buffer'; -import { MNEMONIC } from './fixtures.js'; +import { MNEMONIC, localAnvil } from './fixtures.js'; import { PortalERC20Abi, PortalERC20Bytecode, TokenPortalAbi, TokenPortalBytecode } from '@aztec/l1-artifacts'; import { Chain, GetContractReturnType, HttpTransport, PublicClient, WalletClient, getContract } from 'viem'; import { computeSecretMessageHash } from '@aztec/circuits.js/abis'; @@ -21,7 +21,7 @@ const config = getConfigEnvVars(); // NOTE: this tests is just a scaffold, it is awaiting functionality to come from the aztec-node around indexing messages in the contract describe.skip('e2e_l1_to_l2_msg', () => { - let node: AztecNode; + let node: AztecNodeService; let aztecRpcServer: AztecRPCServer; let accounts: AztecAddress[]; let contract: Contract; @@ -54,7 +54,7 @@ describe.skip('e2e_l1_to_l2_msg', () => { unverifiedDataEmitterAddress, walletClient, publicClient, - } = await deployL1Contracts(config.rpcUrl, account, logger); + } = await deployL1Contracts(config.rpcUrl, account, localAnvil, logger); rollupRegistryAddress = registryAddress_; @@ -78,7 +78,7 @@ describe.skip('e2e_l1_to_l2_msg', () => { publicClient, }); - node = await AztecNode.createAndSync(config); + node = await AztecNodeService.createAndSync(config); aztecRpcServer = await createAztecRpcServer(2, node); accounts = await aztecRpcServer.getAccounts(); }, 60_000); diff --git a/yarn-project/end-to-end/src/e2e_deploy_contract.test.ts b/yarn-project/end-to-end/src/e2e_deploy_contract.test.ts index 9b95f1b085d..dee2245e190 100644 --- a/yarn-project/end-to-end/src/e2e_deploy_contract.test.ts +++ b/yarn-project/end-to-end/src/e2e_deploy_contract.test.ts @@ -1,32 +1,37 @@ -import { AztecNode, getConfigEnvVars } from '@aztec/aztec-node'; +import { AztecNodeService, getConfigEnvVars } from '@aztec/aztec-node'; import { AztecAddress, AztecRPCServer, ContractDeployer, Fr, TxStatus } from '@aztec/aztec.js'; import { createDebugLogger } from '@aztec/foundation/log'; import { TestContractAbi } from '@aztec/noir-contracts/examples'; import { mnemonicToAccount } from 'viem/accounts'; import { createAztecRpcServer } from './create_aztec_rpc_client.js'; -import { deployL1Contracts } from './deploy_l1_contracts.js'; -import { MNEMONIC } from './fixtures.js'; +import { deployL1Contracts } from '@aztec/ethereum'; +import { MNEMONIC, localAnvil } from './fixtures.js'; const logger = createDebugLogger('aztec:e2e_deploy_contract'); const config = getConfigEnvVars(); describe('e2e_deploy_contract', () => { - let node: AztecNode; + let node: AztecNodeService; let aztecRpcServer: AztecRPCServer; let accounts: AztecAddress[]; beforeEach(async () => { const account = mnemonicToAccount(MNEMONIC); const privKey = account.getHdKey().privateKey; - const { rollupAddress, unverifiedDataEmitterAddress } = await deployL1Contracts(config.rpcUrl, account, logger); + const { rollupAddress, unverifiedDataEmitterAddress } = await deployL1Contracts( + config.rpcUrl, + account, + localAnvil, + logger, + ); config.publisherPrivateKey = Buffer.from(privKey!); config.rollupContract = rollupAddress; config.unverifiedDataEmitterContract = unverifiedDataEmitterAddress; - node = await AztecNode.createAndSync(config); + node = await AztecNodeService.createAndSync(config); aztecRpcServer = await createAztecRpcServer(1, node); accounts = await aztecRpcServer.getAccounts(); }, 60_000); diff --git a/yarn-project/end-to-end/src/e2e_nested_contract.test.ts b/yarn-project/end-to-end/src/e2e_nested_contract.test.ts index f3420464ad6..17c906a0c72 100644 --- a/yarn-project/end-to-end/src/e2e_nested_contract.test.ts +++ b/yarn-project/end-to-end/src/e2e_nested_contract.test.ts @@ -1,4 +1,4 @@ -import { AztecNode, getConfigEnvVars } from '@aztec/aztec-node'; +import { AztecNodeService, getConfigEnvVars } from '@aztec/aztec-node'; import { AztecAddress, AztecRPCServer, Contract, ContractDeployer, Fr, TxStatus } from '@aztec/aztec.js'; import { ContractAbi } from '@aztec/foundation/abi'; import { createDebugLogger } from '@aztec/foundation/log'; @@ -7,15 +7,15 @@ import { ChildAbi, ParentAbi } from '@aztec/noir-contracts/examples'; import { toBigInt } from '@aztec/foundation/serialize'; import { mnemonicToAccount } from 'viem/accounts'; import { createAztecRpcServer } from './create_aztec_rpc_client.js'; -import { deployL1Contracts } from './deploy_l1_contracts.js'; -import { MNEMONIC } from './fixtures.js'; +import { deployL1Contracts } from '@aztec/ethereum'; +import { MNEMONIC, localAnvil } from './fixtures.js'; const logger = createDebugLogger('aztec:e2e_nested_contract'); const config = getConfigEnvVars(); describe('e2e_nested_contract', () => { - let node: AztecNode; + let node: AztecNodeService; let aztecRpcServer: AztecRPCServer; let accounts: AztecAddress[]; @@ -25,13 +25,18 @@ describe('e2e_nested_contract', () => { beforeEach(async () => { const account = mnemonicToAccount(MNEMONIC); const privKey = account.getHdKey().privateKey; - const { rollupAddress, unverifiedDataEmitterAddress } = await deployL1Contracts(config.rpcUrl, account, logger); + const { rollupAddress, unverifiedDataEmitterAddress } = await deployL1Contracts( + config.rpcUrl, + account, + localAnvil, + logger, + ); config.publisherPrivateKey = Buffer.from(privKey!); config.rollupContract = rollupAddress; config.unverifiedDataEmitterContract = unverifiedDataEmitterAddress; - node = await AztecNode.createAndSync(config); + node = await AztecNodeService.createAndSync(config); aztecRpcServer = await createAztecRpcServer(1, node); accounts = await aztecRpcServer.getAccounts(); diff --git a/yarn-project/end-to-end/src/e2e_p2p_network.test.ts b/yarn-project/end-to-end/src/e2e_p2p_network.test.ts new file mode 100644 index 00000000000..d4a8c134c40 --- /dev/null +++ b/yarn-project/end-to-end/src/e2e_p2p_network.test.ts @@ -0,0 +1,159 @@ +import { AztecNode, AztecNodeConfig, AztecNodeService, getConfigEnvVars } from '@aztec/aztec-node'; +import { AztecAddress, AztecRPCServer, ContractDeployer, SentTx, TxStatus } from '@aztec/aztec.js'; +import { BootstrapNode, P2PConfig, createLibP2PPeerId, exportLibP2PPeerIdToString } from '@aztec/p2p'; +import { createDebugLogger } from '@aztec/foundation/log'; +import { TestContractAbi } from '@aztec/noir-contracts/examples'; + +import { mnemonicToAccount } from 'viem/accounts'; +import { createAztecRpcServer } from './create_aztec_rpc_client.js'; +import { deployL1Contracts } from '@aztec/ethereum'; +import { MNEMONIC, localAnvil } from './fixtures.js'; + +const logger = createDebugLogger('aztec:e2e_p2p_network'); + +const config = getConfigEnvVars(); + +const NUM_NODES = 4; +const NUM_TXS_PER_BLOCK = 4; +const NUM_TXS_PER_NODE = 2; +const BOOT_NODE_TCP_PORT = 40400; + +interface NodeContext { + node: AztecNodeService; + rpcServer: AztecRPCServer; + txs: SentTx[]; + account: AztecAddress; +} + +const createBootstrapNode = async () => { + const peerId = await createLibP2PPeerId(); + const bootstrapNode = new BootstrapNode(logger); + const config: P2PConfig = { + p2pEnabled: true, + tcpListenPort: BOOT_NODE_TCP_PORT, + tcpListenIp: '0.0.0.0', + announceHostname: '127.0.0.1', + announcePort: BOOT_NODE_TCP_PORT, + peerIdPrivateKey: exportLibP2PPeerIdToString(peerId), + serverMode: true, + minPeerCount: 10, + maxPeerCount: 100, + + // TODO: the following config options are not applicable to bootstrap nodes + checkInterval: 1000, + l2QueueSize: 1, + transactionProtocol: '', + bootstrapNodes: [''], + }; + await bootstrapNode.start(config); + + return bootstrapNode; +}; + +// creates a P2P enabled instance of Aztec Node Service +const createNode = async (tcpListenPort: number, bootstrapNode: string) => { + const newConfig: AztecNodeConfig = { + ...config, + tcpListenPort, + tcpListenIp: '0.0.0.0', + enableNat: false, + bootstrapNodes: [bootstrapNode], + minTxsPerBlock: NUM_TXS_PER_BLOCK, + maxTxsPerBlock: NUM_TXS_PER_BLOCK, + p2pEnabled: true, + serverMode: false, + }; + return await AztecNodeService.createAndSync(newConfig); +}; + +// submits a set of transactions to the provided aztec rpc server +const submitTxsTo = async (aztecRpcServer: AztecRPCServer, account: AztecAddress, numTxs: number) => { + const txs: SentTx[] = []; + for (let i = 0; i < numTxs; i++) { + const deployer = new ContractDeployer(TestContractAbi, aztecRpcServer); + const tx = deployer.deploy().send(); + logger(`Tx sent with hash ${await tx.getTxHash()}`); + const receipt = await tx.getReceipt(); + expect(receipt).toEqual( + expect.objectContaining({ + from: account, + to: undefined, + status: TxStatus.PENDING, + error: '', + }), + ); + logger(`Receipt received and expecting contract deployment at ${receipt.contractAddress}`); + txs.push(tx); + } + return txs; +}; + +// creates and instance of the aztec rpc server and submit a given number of transactions to it. +const createAztecRpcServerAndSubmitTransactions = async (node: AztecNode, numTxs: number) => { + const aztecRpcServer = await createAztecRpcServer(1, node); + const accounts = await aztecRpcServer.getAccounts(); + + const txs = await submitTxsTo(aztecRpcServer, accounts[0], numTxs); + return { + txs, + account: accounts[0], + rpcServer: aztecRpcServer, + node, + } as NodeContext; +}; + +describe('e2e_p2p_network', () => { + beforeEach(async () => { + const account = mnemonicToAccount(MNEMONIC); + const privKey = account.getHdKey().privateKey; + const { rollupAddress, unverifiedDataEmitterAddress } = await deployL1Contracts( + config.rpcUrl, + account, + localAnvil, + logger, + ); + + config.publisherPrivateKey = Buffer.from(privKey!); + config.rollupContract = rollupAddress; + config.unverifiedDataEmitterContract = unverifiedDataEmitterAddress; + }, 60_000); + + it('should rollup txs from all peers', async () => { + // create the bootstrap node for the network + const bootstrapNode = await createBootstrapNode(); + const bootstrapNodeAddress = `/ip4/127.0.0.1/tcp/${BOOT_NODE_TCP_PORT}/p2p/${bootstrapNode + .getPeerId()! + .toString()}`; + // create our network of nodes and submit txs into each of them + // the number of txs per node and the number of txs per rollup + // should be set so that the only way for rollups to be built + // is if the txs are successfully gossiped around the nodes. + const contexts: NodeContext[] = []; + for (let i = 0; i < NUM_NODES; i++) { + const node = await createNode(i + 1 + BOOT_NODE_TCP_PORT, bootstrapNodeAddress); + const context = await createAztecRpcServerAndSubmitTransactions(node, NUM_TXS_PER_NODE); + contexts.push(context); + } + + // now ensure that all txs were successfully mined + for (const context of contexts) { + for (const tx of context.txs) { + const isMined = await tx.isMined(0, 0.1); + const receiptAfterMined = await tx.getReceipt(); + + expect(isMined).toBe(true); + expect(receiptAfterMined.status).toBe(TxStatus.MINED); + const contractAddress = receiptAfterMined.contractAddress!; + expect(await context.rpcServer.isContractDeployed(contractAddress)).toBe(true); + expect(await context.rpcServer.isContractDeployed(AztecAddress.random())).toBe(false); + } + } + + // shutdown all nodes. + for (const context of contexts) { + await context.node.stop(); + await context.rpcServer.stop(); + } + await bootstrapNode.stop(); + }, 60_000); +}); diff --git a/yarn-project/end-to-end/src/e2e_public_token_contract.test.ts b/yarn-project/end-to-end/src/e2e_public_token_contract.test.ts index 6da607d0a46..fe8b23259d4 100644 --- a/yarn-project/end-to-end/src/e2e_public_token_contract.test.ts +++ b/yarn-project/end-to-end/src/e2e_public_token_contract.test.ts @@ -1,6 +1,6 @@ import { mnemonicToAccount } from 'viem/accounts'; -import { AztecNode, getConfigEnvVars } from '@aztec/aztec-node'; +import { AztecNodeService, getConfigEnvVars } from '@aztec/aztec-node'; import { AztecAddress, AztecRPCServer, Contract, ContractDeployer, Fr, Point, TxStatus } from '@aztec/aztec.js'; import { pedersenCompressInputs } from '@aztec/barretenberg.js/crypto'; import { BarretenbergWasm } from '@aztec/barretenberg.js/wasm'; @@ -9,8 +9,8 @@ import { createDebugLogger } from '@aztec/foundation/log'; import { toBigIntBE } from '@aztec/foundation/bigint-buffer'; import { createAztecRpcServer } from './create_aztec_rpc_client.js'; -import { deployL1Contracts } from './deploy_l1_contracts.js'; -import { MNEMONIC } from './fixtures.js'; +import { deployL1Contracts } from '@aztec/ethereum'; +import { MNEMONIC, localAnvil } from './fixtures.js'; import times from 'lodash.times'; const logger = createDebugLogger('aztec:e2e_public_token_contract'); @@ -18,7 +18,7 @@ const logger = createDebugLogger('aztec:e2e_public_token_contract'); const config = getConfigEnvVars(); describe('e2e_public_token_contract', () => { - let node: AztecNode; + let node: AztecNodeService; let aztecRpcServer: AztecRPCServer; let accounts: AztecAddress[]; let contract: Contract; @@ -81,13 +81,18 @@ describe('e2e_public_token_contract', () => { beforeEach(async () => { const account = mnemonicToAccount(MNEMONIC); const privKey = account.getHdKey().privateKey; - const { rollupAddress, unverifiedDataEmitterAddress } = await deployL1Contracts(config.rpcUrl, account, logger); + const { rollupAddress, unverifiedDataEmitterAddress } = await deployL1Contracts( + config.rpcUrl, + account, + localAnvil, + logger, + ); config.rollupContract = rollupAddress; config.unverifiedDataEmitterContract = unverifiedDataEmitterAddress; config.publisherPrivateKey = Buffer.from(privKey!); - node = await AztecNode.createAndSync(config); + node = await AztecNodeService.createAndSync(config); aztecRpcServer = await createAztecRpcServer(1, node); accounts = await aztecRpcServer.getAccounts(); }, 30_000); diff --git a/yarn-project/end-to-end/src/e2e_rollup_native_asset_contract.test.ts b/yarn-project/end-to-end/src/e2e_rollup_native_asset_contract.test.ts index 847759fd365..a89c9aa9216 100644 --- a/yarn-project/end-to-end/src/e2e_rollup_native_asset_contract.test.ts +++ b/yarn-project/end-to-end/src/e2e_rollup_native_asset_contract.test.ts @@ -1,10 +1,10 @@ -import { AztecNode, getConfigEnvVars } from '@aztec/aztec-node'; +import { AztecNodeService, getConfigEnvVars } from '@aztec/aztec-node'; import { AztecAddress, AztecRPCServer, Contract, ContractDeployer, EthAddress, TxStatus } from '@aztec/aztec.js'; import { RollupNativeAssetContractAbi } from '@aztec/noir-contracts/examples'; import { HDAccount, mnemonicToAccount } from 'viem/accounts'; import { createAztecRpcServer } from './create_aztec_rpc_client.js'; -import { deployL1Contract, deployL1Contracts } from './deploy_l1_contracts.js'; +import { deployL1Contract, deployL1Contracts } from '@aztec/ethereum'; import { createDebugLogger } from '@aztec/foundation/log'; import { Fr, Point } from '@aztec/foundation/fields'; import { toBigIntBE, toBufferBE } from '@aztec/foundation/bigint-buffer'; @@ -24,7 +24,7 @@ import { Address, } from 'viem'; import { foundry } from 'viem/chains'; -import { MNEMONIC } from './fixtures.js'; +import { MNEMONIC, localAnvil } from './fixtures.js'; const logger = createDebugLogger('aztec:e2e_rollup_native_asset_contract'); @@ -36,7 +36,7 @@ const sha256ToField = (buf: Buffer): Fr => { }; describe('e2e_rollup_native_asset_contract', () => { - let node: AztecNode; + let node: AztecNodeService; let aztecRpcServer: AztecRPCServer; let account: HDAccount; let accounts: AztecAddress[]; @@ -57,7 +57,7 @@ describe('e2e_rollup_native_asset_contract', () => { registryAddress: registryAddress_, outboxAddress, unverifiedDataEmitterAddress, - } = await deployL1Contracts(config.rpcUrl, account, logger); + } = await deployL1Contracts(config.rpcUrl, account, localAnvil, logger); config.publisherPrivateKey = Buffer.from(privKey!); config.rollupContract = rollupAddress; @@ -65,7 +65,7 @@ describe('e2e_rollup_native_asset_contract', () => { registryAddress = getAddress(registryAddress_.toString()); - node = await AztecNode.createAndSync(config); + node = await AztecNodeService.createAndSync(config); aztecRpcServer = await createAztecRpcServer(2, node); accounts = await aztecRpcServer.getAccounts(); diff --git a/yarn-project/end-to-end/src/e2e_zk_token_contract.test.ts b/yarn-project/end-to-end/src/e2e_zk_token_contract.test.ts index 5769d07ad58..706395026af 100644 --- a/yarn-project/end-to-end/src/e2e_zk_token_contract.test.ts +++ b/yarn-project/end-to-end/src/e2e_zk_token_contract.test.ts @@ -1,21 +1,21 @@ -import { AztecNode, getConfigEnvVars } from '@aztec/aztec-node'; +import { AztecNodeService, getConfigEnvVars } from '@aztec/aztec-node'; import { AztecAddress, AztecRPCServer, Contract, ContractDeployer, TxStatus } from '@aztec/aztec.js'; import { ZkTokenContractAbi } from '@aztec/noir-contracts/examples'; import { mnemonicToAccount } from 'viem/accounts'; import { createAztecRpcServer } from './create_aztec_rpc_client.js'; -import { deployL1Contracts } from './deploy_l1_contracts.js'; +import { deployL1Contracts } from '@aztec/ethereum'; import { createDebugLogger } from '@aztec/foundation/log'; import { Point } from '@aztec/foundation/fields'; import { toBigIntBE } from '@aztec/foundation/bigint-buffer'; -import { MNEMONIC } from './fixtures.js'; +import { MNEMONIC, localAnvil } from './fixtures.js'; const logger = createDebugLogger('aztec:e2e_zk_token_contract'); const config = getConfigEnvVars(); describe('e2e_zk_token_contract', () => { - let node: AztecNode; + let node: AztecNodeService; let aztecRpcServer: AztecRPCServer; let accounts: AztecAddress[]; let contract: Contract; @@ -23,13 +23,18 @@ describe('e2e_zk_token_contract', () => { beforeEach(async () => { const account = mnemonicToAccount(MNEMONIC); const privKey = account.getHdKey().privateKey; - const { rollupAddress, unverifiedDataEmitterAddress } = await deployL1Contracts(config.rpcUrl, account, logger); + const { rollupAddress, unverifiedDataEmitterAddress } = await deployL1Contracts( + config.rpcUrl, + account, + localAnvil, + logger, + ); config.publisherPrivateKey = Buffer.from(privKey!); config.rollupContract = rollupAddress; config.unverifiedDataEmitterContract = unverifiedDataEmitterAddress; - node = await AztecNode.createAndSync(config); + node = await AztecNodeService.createAndSync(config); aztecRpcServer = await createAztecRpcServer(2, node); accounts = await aztecRpcServer.getAccounts(); }, 60_000); diff --git a/yarn-project/end-to-end/src/fixtures.ts b/yarn-project/end-to-end/src/fixtures.ts index a6750fc848f..9ac97afa0f4 100644 --- a/yarn-project/end-to-end/src/fixtures.ts +++ b/yarn-project/end-to-end/src/fixtures.ts @@ -1 +1,4 @@ +import { foundry } from 'viem/chains'; + export const MNEMONIC = 'test test test test test test test test test test test junk'; +export const localAnvil = foundry; diff --git a/yarn-project/end-to-end/src/integration_l1_publisher.test.ts b/yarn-project/end-to-end/src/integration_l1_publisher.test.ts index 745ab2bf684..bad5ab3cbb4 100644 --- a/yarn-project/end-to-end/src/integration_l1_publisher.test.ts +++ b/yarn-project/end-to-end/src/integration_l1_publisher.test.ts @@ -44,8 +44,9 @@ import { getContract, } from 'viem'; import { PrivateKeyAccount, privateKeyToAccount } from 'viem/accounts'; -import { deployL1Contracts } from './deploy_l1_contracts.js'; +import { deployL1Contracts } from '@aztec/ethereum'; import { L2Actor } from '@aztec/types'; +import { localAnvil } from './fixtures.js'; // Accounts 4 and 5 of Anvil default startup with mnemonic: 'test test test test test test test test test test test junk' const sequencerPK = '0x47e179ec197488593b187f80a00eb0da91f1b9d0b13f8733639f19c30a34926a'; @@ -92,7 +93,7 @@ describe('L1Publisher integration', () => { decoderHelperAddress: decoderHelperAddress_, publicClient: publicClient_, walletClient, - } = await deployL1Contracts(config.rpcUrl, deployerAccount, logger, true); + } = await deployL1Contracts(config.rpcUrl, deployerAccount, localAnvil, logger, true); publicClient = publicClient_; rollupAddress = getAddress(rollupAddress_.toString()); @@ -134,6 +135,7 @@ describe('L1Publisher integration', () => { publisher = getL1Publisher({ rpcUrl: config.rpcUrl, + apiKey: '', chainId: config.chainId, requiredConfirmations: 1, rollupContract: EthAddress.fromString(rollupAddress), diff --git a/yarn-project/end-to-end/tsconfig.json b/yarn-project/end-to-end/tsconfig.json index 29cb3e803c3..4904e777bff 100644 --- a/yarn-project/end-to-end/tsconfig.json +++ b/yarn-project/end-to-end/tsconfig.json @@ -15,6 +15,9 @@ { "path": "../circuits.js" }, + { + "path": "../ethereum" + }, { "path": "../foundation" }, @@ -24,6 +27,9 @@ { "path": "../noir-contracts" }, + { + "path": "../p2p" + }, { "path": "../sequencer-client" }, diff --git a/yarn-project/ethereum/.eslintrc.cjs b/yarn-project/ethereum/.eslintrc.cjs new file mode 100644 index 00000000000..e659927475c --- /dev/null +++ b/yarn-project/ethereum/.eslintrc.cjs @@ -0,0 +1 @@ +module.exports = require('@aztec/foundation/eslint'); diff --git a/yarn-project/ethereum/Dockerfile b/yarn-project/ethereum/Dockerfile new file mode 100644 index 00000000000..206d5034cd6 --- /dev/null +++ b/yarn-project/ethereum/Dockerfile @@ -0,0 +1,17 @@ +FROM 278380418400.dkr.ecr.eu-west-2.amazonaws.com/yarn-project-base AS builder + +COPY . . + +WORKDIR /usr/src/yarn-project/ethereum +RUN yarn build && yarn formatting + +RUN yarn test + +# Prune dev dependencies. See comment in base image. +RUN yarn cache clean +RUN yarn workspaces focus --production > /dev/null + +FROM node:18-alpine +COPY --from=builder /usr/src/ /usr/src/ +WORKDIR /usr/src/yarn-project/ethereum +ENTRYPOINT ["yarn", "start"] \ No newline at end of file diff --git a/yarn-project/ethereum/README.md b/yarn-project/ethereum/README.md new file mode 100644 index 00000000000..bfca4778db6 --- /dev/null +++ b/yarn-project/ethereum/README.md @@ -0,0 +1,3 @@ +# Ethereum + +This package provides configuration and code for common chain operations such as contract deployment etc. diff --git a/yarn-project/ethereum/package.json b/yarn-project/ethereum/package.json new file mode 100644 index 00000000000..1a617057d29 --- /dev/null +++ b/yarn-project/ethereum/package.json @@ -0,0 +1,57 @@ +{ + "name": "@aztec/ethereum", + "version": "0.0.0", + "type": "module", + "exports": "./dest/index.js", + "typedoc": { + "entryPoint": "./src/index.ts", + "displayName": "Ethereum", + "tsconfig": "./tsconfig.json" + }, + "scripts": { + "prepare": "node ../yarn-project-base/scripts/update_build_manifest.mjs package.json", + "prepare:check": "node ../yarn-project-base/scripts/update_build_manifest.mjs package.json --check", + "build": "yarn clean && tsc -b", + "build:dev": "tsc -b --watch", + "clean": "rm -rf ./dest .tsbuildinfo", + "formatting": "run -T prettier --check ./src && run -T eslint ./src", + "formatting:fix": "run -T prettier -w ./src", + "start:dev": "tsc-watch -p tsconfig.json --onSuccess 'yarn start'", + "start": "node ./dest/index.js", + "test": "NODE_NO_WARNINGS=1 node --experimental-vm-modules $(yarn bin jest) --passWithNoTests" + }, + "inherits": [ + "../package.common.json" + ], + "dependencies": { + "@aztec/foundation": "workspace:^", + "@aztec/l1-artifacts": "workspace:^", + "dotenv": "^16.0.3", + "tslib": "^2.4.0", + "viem": "^0.3.14" + }, + "devDependencies": { + "@jest/globals": "^29.5.0", + "@rushstack/eslint-patch": "^1.1.4", + "@types/jest": "^29.5.0", + "@types/node": "^18.14.6", + "jest": "^29.5.0", + "ts-jest": "^29.1.0", + "ts-node": "^10.9.1", + "typescript": "^5.0.4" + }, + "files": [ + "dest", + "src", + "!*.test.*" + ], + "types": "./dest/index.d.ts", + "jest": { + "preset": "ts-jest/presets/default-esm", + "moduleNameMapper": { + "^(\\.{1,2}/.*)\\.js$": "$1" + }, + "testRegex": "./src/.*\\.test\\.ts$", + "rootDir": "./src" + } +} diff --git a/yarn-project/end-to-end/src/deploy_l1_contracts.ts b/yarn-project/ethereum/src/deploy_l1_contracts.ts similarity index 98% rename from yarn-project/end-to-end/src/deploy_l1_contracts.ts rename to yarn-project/ethereum/src/deploy_l1_contracts.ts index 46a2df7df72..7ab17f1f210 100644 --- a/yarn-project/end-to-end/src/deploy_l1_contracts.ts +++ b/yarn-project/ethereum/src/deploy_l1_contracts.ts @@ -1,18 +1,18 @@ import { EthAddress } from '@aztec/foundation/eth-address'; import { DebugLogger } from '@aztec/foundation/log'; import { - DecoderHelperAbi, - DecoderHelperBytecode, - InboxAbi, - InboxBytecode, - OutboxAbi, - OutboxBytecode, - RegistryAbi, - RegistryBytecode, RollupAbi, RollupBytecode, UnverifiedDataEmitterAbi, UnverifiedDataEmitterBytecode, + RegistryAbi, + RegistryBytecode, + InboxAbi, + InboxBytecode, + OutboxAbi, + OutboxBytecode, + DecoderHelperAbi, + DecoderHelperBytecode, } from '@aztec/l1-artifacts'; import type { Abi, Narrow } from 'abitype'; import { @@ -29,7 +29,6 @@ import { http, } from 'viem'; import { HDAccount, PrivateKeyAccount } from 'viem/accounts'; -import { foundry } from 'viem/chains'; /** * Return type of the deployL1Contract function. @@ -73,6 +72,7 @@ type DeployL1Contracts = { * Deploys the aztec L1 contracts; Rollup, Unverified Data Emitter & (optionally) Decoder Helper. * @param rpcUrl - URL of the ETH RPC to use for deployment. * @param account - Private Key or HD Account that will deploy the contracts. + * @param chain - The chain instance to deploy to. * @param logger - A logger object. * @param deployDecoderHelper - Boolean, whether to deploy the decoder helper or not. * @returns A list of ETH addresses of the deployed contracts. @@ -80,6 +80,7 @@ type DeployL1Contracts = { export const deployL1Contracts = async ( rpcUrl: string, account: HDAccount | PrivateKeyAccount, + chain: Chain, logger: DebugLogger, deployDecoderHelper = false, ): Promise => { @@ -87,11 +88,11 @@ export const deployL1Contracts = async ( const walletClient = createWalletClient({ account, - chain: foundry, + chain, transport: http(rpcUrl), }); const publicClient = createPublicClient({ - chain: foundry, + chain, transport: http(rpcUrl), }); diff --git a/yarn-project/ethereum/src/ethereum_chain.ts b/yarn-project/ethereum/src/ethereum_chain.ts new file mode 100644 index 00000000000..91d66239a00 --- /dev/null +++ b/yarn-project/ethereum/src/ethereum_chain.ts @@ -0,0 +1,16 @@ +import { Chain } from 'viem'; + +/** + * Interface containing the connection and chain properties to interact with a blockchain. + */ +export interface EthereumChain { + /** + * An instance of the viem chain data. + */ + chainInfo: Chain; + + /** + * The actual url to be used. + */ + rpcUrl: string; +} diff --git a/yarn-project/ethereum/src/index.ts b/yarn-project/ethereum/src/index.ts new file mode 100644 index 00000000000..b0e81066ac4 --- /dev/null +++ b/yarn-project/ethereum/src/index.ts @@ -0,0 +1,24 @@ +import { foundry } from 'viem/chains'; +import { EthereumChain } from './ethereum_chain.js'; +import { createTestnetChain } from './testnet.js'; + +export * from './testnet.js'; +export * from './deploy_l1_contracts.js'; + +/** + * Helper function to create an instance of Aztec Chain from an rpc url and api key. + * @param rpcUrl - The rpc url of the chain or a chain identifer (e.g. 'testnet') + * @param apiKey - An optional apikey for the chain client. + */ +export function createEthereumChain(rpcUrl: string, apiKey?: string) { + if (rpcUrl === 'testnet') { + if (apiKey === undefined || apiKey === '') { + throw new Error('API Key must be provided for aztec testnet'); + } + return createTestnetChain(apiKey!); + } + return { + chainInfo: foundry, + rpcUrl, + } as EthereumChain; +} diff --git a/yarn-project/ethereum/src/testnet.ts b/yarn-project/ethereum/src/testnet.ts new file mode 100644 index 00000000000..2fa5943b8e4 --- /dev/null +++ b/yarn-project/ethereum/src/testnet.ts @@ -0,0 +1,27 @@ +import { Chain } from 'viem'; +import { EthereumChain } from './ethereum_chain.js'; + +export const createTestnetChain = (apiKey: string) => { + const chain: Chain = { + id: 677868, + name: 'testnet', + network: 'aztec', + nativeCurrency: { + name: 'Ether', + symbol: 'ETH', + decimals: 18, + }, + rpcUrls: { + default: { + http: [`https://aztec-connect-testnet-eth-host.aztec.network:8545/${apiKey}`], + }, + public: { + http: [`https://aztec-connect-testnet-eth-host.aztec.network:8545/${apiKey}`], + }, + }, + }; + return { + chainInfo: chain, + rpcUrl: chain.rpcUrls.default.http[0], + } as EthereumChain; +}; diff --git a/yarn-project/ethereum/tsconfig.json b/yarn-project/ethereum/tsconfig.json new file mode 100644 index 00000000000..b12e902b946 --- /dev/null +++ b/yarn-project/ethereum/tsconfig.json @@ -0,0 +1,17 @@ +{ + "extends": "..", + "compilerOptions": { + "outDir": "dest", + "rootDir": "src", + "tsBuildInfoFile": ".tsbuildinfo" + }, + "include": ["src"], + "references": [ + { + "path": "../foundation" + }, + { + "path": "../l1-artifacts" + } + ] +} diff --git a/yarn-project/merkle-tree/src/sibling_path/sibling_path.ts b/yarn-project/merkle-tree/src/sibling_path/sibling_path.ts index 444dba5ec81..9b7bae2dcc4 100644 --- a/yarn-project/merkle-tree/src/sibling_path/sibling_path.ts +++ b/yarn-project/merkle-tree/src/sibling_path/sibling_path.ts @@ -85,8 +85,8 @@ export class SiblingPath { * @param offset - An offset to start deserializing from. * @returns A SiblingPath object. */ - static fromBuffer(buf: Buffer, offset = 0): SiblingPath { - const { elem } = SiblingPath.deserialize(buf, offset); + static fromBuffer(buf: Buffer, offset = 0): SiblingPath { + const { elem } = SiblingPath.deserialize(buf, offset); return elem; } @@ -96,14 +96,14 @@ export class SiblingPath { * @param offset - An offset to start deserializing from. * @returns The deserialized sibling path and the number of bytes advanced. */ - static deserialize(buf: Buffer, offset = 0) { + static deserialize(buf: Buffer, offset = 0) { const deserializePath = (buf: Buffer, offset: number) => ({ elem: buf.slice(offset, offset + 32), adv: 32, }); const { elem, adv } = deserializeArrayFromVector(deserializePath, buf, offset); const size = elem.length; - return { elem: new SiblingPath(size, elem), adv }; + return { elem: new SiblingPath(size as N, elem), adv }; } /** @@ -119,8 +119,8 @@ export class SiblingPath { * @param repr - A hex string representation of the sibling path. * @returns A SiblingPath object. */ - public static fromString(repr: string): SiblingPath { - return SiblingPath.fromBuffer(Buffer.from(repr, 'hex')); + public static fromString(repr: string): SiblingPath { + return SiblingPath.fromBuffer(Buffer.from(repr, 'hex')); } /** diff --git a/yarn-project/merkle-tree/src/standard_indexed_tree/standard_indexed_tree.ts b/yarn-project/merkle-tree/src/standard_indexed_tree/standard_indexed_tree.ts index f60c2e204c7..5b1aee0adbf 100644 --- a/yarn-project/merkle-tree/src/standard_indexed_tree/standard_indexed_tree.ts +++ b/yarn-project/merkle-tree/src/standard_indexed_tree/standard_indexed_tree.ts @@ -456,7 +456,6 @@ export class StandardIndexedTree extends TreeBase implements IndexedTree { * @param leaves - Values to insert into the tree. * @param treeHeight - Height of the tree. * @param subtreeHeight - Height of the subtree. - * @param includeUncommitted - If true, the uncommitted changes are included in the search. * @returns The data for the leaves to be updated when inserting the new ones. */ public async batchInsert< @@ -467,7 +466,6 @@ export class StandardIndexedTree extends TreeBase implements IndexedTree { leaves: Buffer[], treeHeight: TreeHeight, subtreeHeight: SubtreeHeight, - includeUncommitted: boolean, ): Promise< | [LowLeafWitnessData[], SiblingPath] | [undefined, SiblingPath] @@ -481,7 +479,7 @@ export class StandardIndexedTree extends TreeBase implements IndexedTree { const pendingInsertionSubtree: LeafData[] = []; // Start info - const startInsertionIndex = this.getNumLeaves(includeUncommitted); + const startInsertionIndex = this.getNumLeaves(true); // Get insertion path for each leaf for (let i = 0; i < leaves.length; i++) { @@ -494,7 +492,7 @@ export class StandardIndexedTree extends TreeBase implements IndexedTree { continue; } - const indexOfPrevious = this.findIndexOfPreviousValue(newValue, includeUncommitted); + const indexOfPrevious = this.findIndexOfPreviousValue(newValue, true); // If a touched node has a value that is less greater than the current value const prevNodes = touched.get(indexOfPrevious.index); @@ -537,11 +535,11 @@ export class StandardIndexedTree extends TreeBase implements IndexedTree { } // get the low leaf - const lowLeaf = this.getLatestLeafDataCopy(indexOfPrevious.index, includeUncommitted); + const lowLeaf = this.getLatestLeafDataCopy(indexOfPrevious.index, true); if (lowLeaf === undefined) { - return [undefined, await this.getSubtreeSiblingPath(subtreeHeight, includeUncommitted)]; + return [undefined, await this.getSubtreeSiblingPath(subtreeHeight, true)]; } - const siblingPath = await this.getSiblingPath(BigInt(indexOfPrevious.index), includeUncommitted); + const siblingPath = await this.getSiblingPath(BigInt(indexOfPrevious.index), true); const witness: LowLeafWitnessData = { leafData: { ...lowLeaf }, @@ -569,7 +567,7 @@ export class StandardIndexedTree extends TreeBase implements IndexedTree { const newSubtreeSiblingPath = await this.getSubtreeSiblingPath( subtreeHeight, - includeUncommitted, + true, ); // Perform batch insertion of new pending values diff --git a/yarn-project/noir-contracts/src/examples/index.ts b/yarn-project/noir-contracts/src/examples/index.ts index 0c4113c373c..741ea839aa8 100644 --- a/yarn-project/noir-contracts/src/examples/index.ts +++ b/yarn-project/noir-contracts/src/examples/index.ts @@ -1,13 +1,13 @@ // TODO the verification keys in this contracts are mocked ATM -import TestContractJson from './test_contract.json'; -import ZkTokenContractJson from './zk_token_contract.json'; -import ParentJson from './parent_contract.json'; -import ChildJson from './child_contract.json'; -import PublicTokenContractJson from './public_token_contract.json'; -import NonNativeTokenContractJson from './non_native_token_contract.json'; -import RollupNativeAssetContractJson from './rollup_native_asset_contract.json'; -import AccountContractJson from './account_contract.json'; -import { ContractAbi } from '@aztec/foundation/abi'; +import TestContractJson from './test_contract.json' assert { type: 'json' }; +import ZkTokenContractJson from './zk_token_contract.json' assert { type: 'json' }; +import ParentJson from './parent_contract.json' assert { type: 'json' }; +import ChildJson from './child_contract.json' assert { type: 'json' }; +import PublicTokenContractJson from './public_token_contract.json' assert { type: 'json' }; +import { ContractAbi } from '@aztec/foundation/abi' assert { type: 'json' }; +import NonNativeTokenContractJson from './non_native_token_contract.json' assert { type: 'json' }; +import RollupNativeAssetContractJson from './rollup_native_asset_contract.json' assert { type: 'json' }; +import AccountContractJson from './account_contract.json' assert { type: 'json' }; export const TestContractAbi = TestContractJson as ContractAbi; export const ZkTokenContractAbi = ZkTokenContractJson as ContractAbi; diff --git a/yarn-project/p2p-bootstrap/.eslintrc.cjs b/yarn-project/p2p-bootstrap/.eslintrc.cjs new file mode 100644 index 00000000000..e659927475c --- /dev/null +++ b/yarn-project/p2p-bootstrap/.eslintrc.cjs @@ -0,0 +1 @@ +module.exports = require('@aztec/foundation/eslint'); diff --git a/yarn-project/p2p-bootstrap/Dockerfile b/yarn-project/p2p-bootstrap/Dockerfile new file mode 100644 index 00000000000..da22a6c8198 --- /dev/null +++ b/yarn-project/p2p-bootstrap/Dockerfile @@ -0,0 +1,17 @@ +FROM 278380418400.dkr.ecr.eu-west-2.amazonaws.com/yarn-project-base AS builder + +COPY . . + +WORKDIR /usr/src/yarn-project/p2p-bootstrap +RUN yarn build && yarn formatting + +RUN yarn test + +# Prune dev dependencies. See comment in base image. +RUN yarn cache clean +RUN yarn workspaces focus --production > /dev/null + +FROM node:18-alpine +COPY --from=builder /usr/src/ /usr/src/ +WORKDIR /usr/src/yarn-project/p2p-bootstrap +ENTRYPOINT ["yarn", "start"] \ No newline at end of file diff --git a/yarn-project/p2p-bootstrap/README.md b/yarn-project/p2p-bootstrap/README.md new file mode 100644 index 00000000000..65a12aa77f7 --- /dev/null +++ b/yarn-project/p2p-bootstrap/README.md @@ -0,0 +1,13 @@ +# P2P Bootstrap + +This package provides an implementation of a 'Bootstrap' P2P node. It's purpose is to assist new network participants in acquiring peers. + +To build the package simply type `yarn build`, to start the boot node, simply type `yarn start`. + +The node will require a number of environment variables: + +P2P_TCP_LISTEN_IP - The IP Address on which to listen for connections. +P2P_TCP_LISTEN_PORT - The port on which to listen for connections. +PEER_ID_PRIVATE_KEY - The private key to be used by the peer for secure communications with other peers. This key will also be used to derive the Peer ID. +P2P_ANNOUNCE_HOSTNAME - The IPAddress/Hostname that other peers should use to connect to this node, this may be different to P2P_TCP_LISTEN_IP if e.g. the node is behind a NAT. +P2P_ANNOUNCE_PORT - The port that other peers should use to connect to this node, this may be different to P2P_TCP_LISTEN_PORT if e.g. the node is behind a NAT. diff --git a/yarn-project/p2p-bootstrap/package.json b/yarn-project/p2p-bootstrap/package.json new file mode 100644 index 00000000000..463f34c4fc4 --- /dev/null +++ b/yarn-project/p2p-bootstrap/package.json @@ -0,0 +1,55 @@ +{ + "name": "@aztec/p2p-bootstrap", + "version": "0.0.0", + "type": "module", + "exports": "./dest/index.js", + "typedoc": { + "entryPoint": "./src/index.ts", + "displayName": "P2P Bootstrap", + "tsconfig": "./tsconfig.json" + }, + "scripts": { + "prepare": "node ../yarn-project-base/scripts/update_build_manifest.mjs package.json", + "prepare:check": "node ../yarn-project-base/scripts/update_build_manifest.mjs package.json --check", + "build": "yarn clean && tsc -b", + "build:dev": "tsc -b --watch", + "clean": "rm -rf ./dest .tsbuildinfo", + "formatting": "run -T prettier --check ./src && run -T eslint ./src", + "formatting:fix": "run -T prettier -w ./src", + "start:dev": "tsc-watch -p tsconfig.json --onSuccess 'yarn start'", + "start": "node ./dest/index.js", + "test": "NODE_NO_WARNINGS=1 node --experimental-vm-modules $(yarn bin jest) --passWithNoTests" + }, + "inherits": [ + "../package.common.json" + ], + "dependencies": { + "@aztec/p2p": "workspace:^", + "dotenv": "^16.0.3", + "tslib": "^2.4.0" + }, + "devDependencies": { + "@jest/globals": "^29.5.0", + "@rushstack/eslint-patch": "^1.1.4", + "@types/jest": "^29.5.0", + "@types/node": "^18.14.6", + "jest": "^29.5.0", + "ts-jest": "^29.1.0", + "ts-node": "^10.9.1", + "typescript": "^5.0.4" + }, + "files": [ + "dest", + "src", + "!*.test.*" + ], + "types": "./dest/index.d.ts", + "jest": { + "preset": "ts-jest/presets/default-esm", + "moduleNameMapper": { + "^(\\.{1,2}/.*)\\.js$": "$1" + }, + "testRegex": "./src/.*\\.test\\.ts$", + "rootDir": "./src" + } +} diff --git a/yarn-project/p2p-bootstrap/scripts/docker-compose-bootstrap.yml b/yarn-project/p2p-bootstrap/scripts/docker-compose-bootstrap.yml new file mode 100644 index 00000000000..353eb9ffaa2 --- /dev/null +++ b/yarn-project/p2p-bootstrap/scripts/docker-compose-bootstrap.yml @@ -0,0 +1,10 @@ +version: '3' +services: + p2p-bootstrap: + image: 278380418400.dkr.ecr.eu-west-2.amazonaws.com/p2p-bootstrap:latest + ports: + - '40400:40400' + environment: + DEBUG: 'aztec:*' + P2P_TCP_LISTEN_PORT: 40400 + PEER_ID: '0a260024080112205ea53185db2e52dae74d0d4d6cadc494174810d0a713cd09b0ac517c38bc781e1224080112205ea53185db2e52dae74d0d4d6cadc494174810d0a713cd09b0ac517c38bc781e1a44080112402df8b977f356c6e34fa021c9647973234dff4df706c185794405aafb556723cf5ea53185db2e52dae74d0d4d6cadc494174810d0a713cd09b0ac517c38bc781e' diff --git a/yarn-project/p2p-bootstrap/scripts/start_bootnode.sh b/yarn-project/p2p-bootstrap/scripts/start_bootnode.sh new file mode 100755 index 00000000000..613d1125971 --- /dev/null +++ b/yarn-project/p2p-bootstrap/scripts/start_bootnode.sh @@ -0,0 +1,8 @@ +#! /bin/bash +set -eu + +cd .. +export P2P_TCP_LISTEN_PORT=40400 +export PEER_ID='0a260024080112205ea53185db2e52dae74d0d4d6cadc494174810d0a713cd09b0ac517c38bc781e1224080112205ea53185db2e52dae74d0d4d6cadc494174810d0a713cd09b0ac517c38bc781e1a44080112402df8b977f356c6e34fa021c9647973234dff4df706c185794405aafb556723cf5ea53185db2e52dae74d0d4d6cadc494174810d0a713cd09b0ac517c38bc781e' +yarn build +yarn start \ No newline at end of file diff --git a/yarn-project/p2p-bootstrap/src/index.ts b/yarn-project/p2p-bootstrap/src/index.ts new file mode 100644 index 00000000000..fa73468881b --- /dev/null +++ b/yarn-project/p2p-bootstrap/src/index.ts @@ -0,0 +1,28 @@ +import 'dotenv/config'; +import { createDebugLogger } from '@aztec/foundation/log'; +import { BootstrapNode, getP2PConfigEnvVars } from '@aztec/p2p'; + +const logger = createDebugLogger('aztec:bootstrap_node'); + +/** + * The application entry point. + */ +async function main() { + const config = getP2PConfigEnvVars(); + const bootstrapNode = new BootstrapNode(logger); + await bootstrapNode.start(config); + logger('Node started'); + + const stop = async () => { + logger('Stopping bootstrap node...'); + await bootstrapNode.stop(); + logger('Node stopped'); + process.exit(0); + }; + process.on('SIGTERM', stop); + process.on('SIGINT', stop); +} + +main().catch(err => { + logger(err); +}); diff --git a/yarn-project/p2p-bootstrap/tsconfig.json b/yarn-project/p2p-bootstrap/tsconfig.json new file mode 100644 index 00000000000..de1c830a3ad --- /dev/null +++ b/yarn-project/p2p-bootstrap/tsconfig.json @@ -0,0 +1,14 @@ +{ + "extends": "..", + "compilerOptions": { + "outDir": "dest", + "rootDir": "src", + "tsBuildInfoFile": ".tsbuildinfo" + }, + "references": [ + { + "path": "../p2p" + } + ], + "include": ["src"] +} diff --git a/yarn-project/p2p/README.md b/yarn-project/p2p/README.md index 69cc3994e2d..375662609f7 100644 --- a/yarn-project/p2p/README.md +++ b/yarn-project/p2p/README.md @@ -1,11 +1,7 @@ # P2P -This package defined an interface for a client that keeps track of Aztec transaction requests. +This package includes the functionality of the P2P networking required by the aztec node. The `P2PClient` provides an interface for the introduction of transactions to the Tx Pool and propagation of those transactions through the network. The `P2PService` offers an abstraction of the P2P networking. -When a transaction goes through (mined in a block), the p2p client is also responsible for reconciling the pool. i.e. +The package depends on a block source in order to reconcile the transaction pool and remove settled transactions. -- Check for new confirmed blocks -- When new block is mined, go through its transactions and remove them from the pool. - -A very naive, in-memory implementation of the P2P client is also included in the package. -It's currently used by the implementations of the sequencer and node. +Additionally, the `BootstrapNode` class provides the functionality for running a P2P 'bootnode', one that serves the purpose of introducing new peers to the network. It does not participate in transaction exchange. diff --git a/yarn-project/p2p/package.json b/yarn-project/p2p/package.json index b770bd4ce7a..26790221925 100644 --- a/yarn-project/p2p/package.json +++ b/yarn-project/p2p/package.json @@ -35,7 +35,17 @@ "@aztec/circuits.js": "workspace:^", "@aztec/foundation": "workspace:^", "@aztec/types": "workspace:^", + "@chainsafe/libp2p-noise": "^12.0.0", + "@chainsafe/libp2p-yamux": "^4.0.2", + "@libp2p/bootstrap": "^8.0.0", + "@libp2p/interfaces": "^3.3.2", + "@libp2p/kad-dht": "^9.3.3", + "@libp2p/mplex": "^8.0.3", + "@libp2p/peer-id": "^2.0.3", + "@libp2p/tcp": "^7.0.1", + "libp2p": "^0.45.1", "lodash.times": "^4.3.2", + "multiaddr": "^10.0.1", "sha3": "^2.1.4", "tslib": "^2.4.0" }, @@ -46,6 +56,7 @@ "@types/lodash.times": "^4.3.7", "@types/node": "^18.14.6", "jest": "^29.5.0", + "jest-mock-extended": "^3.0.4", "ts-jest": "^29.1.0", "ts-node": "^10.9.1", "typescript": "^5.0.4" diff --git a/yarn-project/p2p/src/bootstrap/bootstrap.ts b/yarn-project/p2p/src/bootstrap/bootstrap.ts new file mode 100644 index 00000000000..a0d49f7dac9 --- /dev/null +++ b/yarn-project/p2p/src/bootstrap/bootstrap.ts @@ -0,0 +1,107 @@ +import { Libp2p, Libp2pOptions, ServiceFactoryMap, createLibp2p } from 'libp2p'; +import { tcp } from '@libp2p/tcp'; +import { noise } from '@chainsafe/libp2p-noise'; +import { mplex } from '@libp2p/mplex'; +import { kadDHT } from '@libp2p/kad-dht'; +import { createFromProtobuf } from '@libp2p/peer-id-factory'; +import { createDebugLogger } from '@aztec/foundation/log'; +import { P2PConfig } from '../config.js'; +import { yamux } from '@chainsafe/libp2p-yamux'; +import { identifyService } from 'libp2p/identify'; +import type { ServiceMap } from '@libp2p/interface-libp2p'; +import { createLibP2PPeerId } from '../index.js'; + +/** + * Encapsulates a 'Bootstrap' node, used for the purpose of assisting new joiners in acquiring peers. + */ +export class BootstrapNode { + private node?: Libp2p = undefined; + + constructor(private logger = createDebugLogger('aztec:p2p_bootstrap')) {} + + /** + * Starts the bootstrap node. + * @param config - The P2P configuration. + * @returns An empty promise. + */ + public async start(config: P2PConfig) { + const { peerIdPrivateKey, tcpListenIp, tcpListenPort, announceHostname, announcePort, minPeerCount, maxPeerCount } = + config; + const peerId = peerIdPrivateKey + ? await createFromProtobuf(Buffer.from(peerIdPrivateKey, 'hex')) + : await createLibP2PPeerId(); + this.logger( + `Starting bootstrap node ${peerId} on ${tcpListenIp}:${tcpListenPort} announced at ${announceHostname}:${announcePort}`, + ); + + const opts: Libp2pOptions = { + start: false, + peerId, + addresses: { + listen: [`/ip4/${tcpListenIp}/tcp/${tcpListenPort}`], + announce: [`/ip4/${announceHostname}/tcp/${announcePort ?? tcpListenPort}`], + }, + transports: [tcp()], + streamMuxers: [yamux(), mplex()], + connectionEncryption: [noise()], + connectionManager: { + minConnections: minPeerCount, + maxConnections: maxPeerCount, + }, + }; + + const services: ServiceFactoryMap = { + identify: identifyService({ + protocolPrefix: 'aztec', + }), + kadDHT: kadDHT({ + protocolPrefix: 'aztec', + clientMode: false, + }), + }; + + this.node = await createLibp2p({ + ...opts, + services, + }); + + await this.node.start(); + this.logger(`lib p2p has started`); + + // print out listening addresses + this.logger('listening on addresses:'); + this.node.getMultiaddrs().forEach(addr => { + this.logger(addr.toString()); + }); + + this.node.addEventListener('peer:discovery', evt => { + this.logger('Discovered %s', evt.detail.id.toString()); // Log discovered peer + }); + + this.node.addEventListener('peer:connect', evt => { + this.logger('Connected to %s', evt.detail.toString()); // Log connected peer + }); + + this.node.addEventListener('peer:disconnect', evt => { + this.logger('Disconnected from %s', evt.detail.toString()); // Log connected peer + }); + } + + /** + * Stops the bootstrap node. + * @returns And empty promise. + */ + public async stop() { + // stop libp2p + await this.node?.stop(); + this.logger('libp2p has stopped'); + } + + /** + * Returns the peerId of this node. + * @returns The node's peer Id + */ + public getPeerId() { + return this.node?.peerId; + } +} diff --git a/yarn-project/p2p/src/client/index.ts b/yarn-project/p2p/src/client/index.ts index ee127cbba40..fab61c92a1d 100644 --- a/yarn-project/p2p/src/client/index.ts +++ b/yarn-project/p2p/src/client/index.ts @@ -1 +1,10 @@ +import { L2BlockSource } from '@aztec/types'; +import { LibP2PService, P2PClient, P2PConfig, TxPool } from '../index.js'; +import { DummyP2PService } from '../service/dummy_service.js'; + export * from './p2p_client.js'; + +export const createP2PClient = async (config: P2PConfig, txPool: TxPool, l2BlockSource: L2BlockSource) => { + const p2pService = config.p2pEnabled ? await LibP2PService.new(config, txPool) : new DummyP2PService(); + return new P2PClient(l2BlockSource, txPool, p2pService); +}; diff --git a/yarn-project/p2p/src/client/p2p_client.test.ts b/yarn-project/p2p/src/client/p2p_client.test.ts index 6ed1463f4bc..a0ad7ac8206 100644 --- a/yarn-project/p2p/src/client/p2p_client.test.ts +++ b/yarn-project/p2p/src/client/p2p_client.test.ts @@ -5,6 +5,7 @@ import { P2PClient } from './p2p_client.js'; import { TxPool } from '../tx_pool/index.js'; import { MockBlockSource } from './mocks.js'; import { MockTx } from './mocks.js'; +import { P2PService } from '../index.js'; /** * Mockify helper for testing purposes. @@ -16,6 +17,7 @@ type Mockify = { describe('In-Memory P2P Client', () => { let txPool: Mockify; let blockSource: L2BlockSource; + let p2pService: Mockify; beforeEach(() => { txPool = { @@ -23,13 +25,22 @@ describe('In-Memory P2P Client', () => { getTxByHash: jest.fn().mockReturnValue(undefined), deleteTxs: jest.fn(), getAllTxs: jest.fn().mockReturnValue([]), + getAllTxHashes: jest.fn().mockReturnValue([]), + hasTx: jest.fn().mockReturnValue(false), + }; + + p2pService = { + start: jest.fn(), + stop: jest.fn(), + propagateTx: jest.fn(), + settledTxs: jest.fn(), }; blockSource = new MockBlockSource(); }); it('can start & stop', async () => { - const client = new P2PClient(blockSource, txPool); + const client = new P2PClient(blockSource, txPool, p2pService); expect(await client.isReady()).toEqual(false); await client.start(); @@ -40,7 +51,7 @@ describe('In-Memory P2P Client', () => { }); it('adds txs to pool', async () => { - const client = new P2PClient(blockSource, txPool); + const client = new P2PClient(blockSource, txPool, p2pService); await client.start(); const tx1 = MockTx(); const tx2 = MockTx(); @@ -52,7 +63,7 @@ describe('In-Memory P2P Client', () => { }); it('rejects txs after being stopped', async () => { - const client = new P2PClient(blockSource, txPool); + const client = new P2PClient(blockSource, txPool, p2pService); await client.start(); const tx1 = MockTx(); const tx2 = MockTx(); diff --git a/yarn-project/p2p/src/client/p2p_client.ts b/yarn-project/p2p/src/client/p2p_client.ts index 5de19e17fda..786feaa8232 100644 --- a/yarn-project/p2p/src/client/p2p_client.ts +++ b/yarn-project/p2p/src/client/p2p_client.ts @@ -2,9 +2,9 @@ import { L2Block, L2BlockContext, L2BlockDownloader, L2BlockSource } from '@azte import { Tx, TxHash } from '@aztec/types'; import { TxPool } from '../tx_pool/index.js'; -import { InMemoryTxPool } from '../tx_pool/memory_tx_pool.js'; -import { getConfigEnvVars } from '../config.js'; +import { getP2PConfigEnvVars } from '../config.js'; import { createDebugLogger } from '@aztec/foundation/log'; +import { P2PService } from '../service/service.js'; /** * Enum defining the possible states of the p2p client. @@ -114,14 +114,16 @@ export class P2PClient implements P2P { * In-memory P2P client constructor. * @param l2BlockSource - P2P client's source for fetching existing block data. * @param txPool - The client's instance of a transaction pool. Defaults to in-memory implementation. + * @param p2pService - The concrete instance of p2p networking to use. * @param log - A logger. */ constructor( private l2BlockSource: L2BlockSource, - private txPool: TxPool = new InMemoryTxPool(), + private txPool: TxPool, + private p2pService: P2PService, private log = createDebugLogger('aztec:p2p'), ) { - const { checkInterval, l2QueueSize } = getConfigEnvVars(); + const { checkInterval, l2QueueSize } = getP2PConfigEnvVars(); this.blockDownloader = new L2BlockDownloader(l2BlockSource, l2QueueSize, checkInterval); } @@ -153,6 +155,7 @@ export class P2PClient implements P2P { // if no blocks to be retrieved, go straight to running this.setCurrentState(P2PClientState.RUNNING); this.syncPromise = Promise.resolve(); + await this.p2pService.start(); this.log(`Next block ${blockToDownloadFrom} already beyond latest block at ${this.latestBlockNumberAtStart}`); } @@ -176,6 +179,7 @@ export class P2PClient implements P2P { public async stop() { this.log('Stopping p2p client...'); this.stopping = true; + await this.p2pService.stop(); await this.blockDownloader.stop(); await this.runningPromise; this.setCurrentState(P2PClientState.STOPPED); @@ -209,6 +213,7 @@ export class P2PClient implements P2P { throw new Error('P2P client not ready'); } await this.txPool.addTxs([tx]); + this.p2pService.propagateTx(tx); } /** @@ -261,10 +266,8 @@ export class P2PClient implements P2P { for (let i = 0; i < blocks.length; i++) { const blockContext = new L2BlockContext(blocks[i]); const txHashes = blockContext.getTxHashes(); - for (const txHash of txHashes) { - this.log(`Deleting tx hash ${txHash.toString()} from tx pool`); - } this.txPool.deleteTxs(txHashes); + this.p2pService.settledTxs(txHashes); } return Promise.resolve(); } @@ -285,6 +288,7 @@ export class P2PClient implements P2P { this.setCurrentState(P2PClientState.RUNNING); if (this.syncResolve !== undefined) { this.syncResolve(); + await this.p2pService.start(); } } } diff --git a/yarn-project/p2p/src/config.ts b/yarn-project/p2p/src/config.ts index 1bcbb1379b1..13abb83ec98 100644 --- a/yarn-project/p2p/src/config.ts +++ b/yarn-project/p2p/src/config.ts @@ -2,6 +2,11 @@ * P2P client configuration values. */ export interface P2PConfig { + /** + * A flag dictating whether the P2P subsystem should be enabled. + */ + p2pEnabled: boolean; + /** * The frequency in which to check. */ @@ -11,17 +16,98 @@ export interface P2PConfig { * Size of queue of L2 blocks to store. */ l2QueueSize: number; + + /** + * The tcp port on which the P2P service should listen for connections. + */ + tcpListenPort: number; + + /** + * The tcp IP on which the P2P service should listen for connections. + */ + tcpListenIp: string; + + /** + * An optional peer id private key. If blank, will generate a random key. + */ + peerIdPrivateKey?: string; + + /** + * A list of bootstrap peers to connect to. + */ + bootstrapNodes: string[]; + + /** + * Protocol identifier for transaction gossiping. + */ + transactionProtocol: string; + + /** + * Hostname to announce. + */ + announceHostname?: string; + + /** + * Port to announce. + */ + announcePort?: number; + + /** + * Optional specification to run as a server node. + */ + serverMode: boolean; + + /** + * Whether to enable NAT from libp2p (ignored for bootstrap node). + */ + enableNat?: boolean; + + /** + * The minimum number of peers (a peer count below this will cause the node to look for more peers) + */ + minPeerCount: number; + + /** + * The maximum number of peers (a peer count above this will cause the node to refuse connection attempts) + */ + maxPeerCount: number; } /** * Gets the config values for p2p client from environment variables. * @returns The config values for p2p client. */ -export function getConfigEnvVars(): P2PConfig { - const { P2P_CHECK_INTERVAL, P2P_L2_BLOCK_QUEUE_SIZE } = process.env; +export function getP2PConfigEnvVars(): P2PConfig { + const { + P2P_ENABLED, + P2P_CHECK_INTERVAL, + P2P_L2_BLOCK_QUEUE_SIZE, + P2P_TCP_LISTEN_PORT, + P2P_TCP_LISTEN_IP, + PEER_ID_PRIVATE_KEY, + BOOTSTRAP_NODES, + P2P_ANNOUNCE_HOSTNAME, + P2P_ANNOUNCE_PORT, + P2P_SERVER, + P2P_NAT_ENABLED, + P2P_MIN_PEERS, + P2P_MAX_PEERS, + } = process.env; const envVars: P2PConfig = { + p2pEnabled: P2P_ENABLED === 'true', checkInterval: P2P_CHECK_INTERVAL ? +P2P_CHECK_INTERVAL : 100, l2QueueSize: P2P_L2_BLOCK_QUEUE_SIZE ? +P2P_L2_BLOCK_QUEUE_SIZE : 1000, + tcpListenPort: P2P_TCP_LISTEN_PORT ? +P2P_TCP_LISTEN_PORT : 0, + tcpListenIp: P2P_TCP_LISTEN_IP ? P2P_TCP_LISTEN_IP : '0.0.0.0', + peerIdPrivateKey: PEER_ID_PRIVATE_KEY, + bootstrapNodes: BOOTSTRAP_NODES ? BOOTSTRAP_NODES.split(',') : [], + transactionProtocol: '', + announceHostname: P2P_ANNOUNCE_HOSTNAME, + announcePort: P2P_ANNOUNCE_PORT ? +P2P_ANNOUNCE_PORT : undefined, + serverMode: P2P_SERVER === 'true', + enableNat: P2P_NAT_ENABLED === 'true', + minPeerCount: P2P_MIN_PEERS ? +P2P_MIN_PEERS : 10, + maxPeerCount: P2P_MAX_PEERS ? +P2P_MAX_PEERS : 100, }; return envVars; } diff --git a/yarn-project/p2p/src/index.ts b/yarn-project/p2p/src/index.ts index 3eff155a07e..4a04a398da3 100644 --- a/yarn-project/p2p/src/index.ts +++ b/yarn-project/p2p/src/index.ts @@ -1 +1,5 @@ export * from './client/index.js'; +export * from './config.js'; +export * from './tx_pool/index.js'; +export * from './service/index.js'; +export * from './bootstrap/bootstrap.js'; diff --git a/yarn-project/p2p/src/service/dummy_service.ts b/yarn-project/p2p/src/service/dummy_service.ts new file mode 100644 index 00000000000..e6afb69493d --- /dev/null +++ b/yarn-project/p2p/src/service/dummy_service.ts @@ -0,0 +1,35 @@ +import { Tx, TxHash } from '@aztec/types'; +import { P2PService } from './service.js'; + +/** + * A dummy implementation of the P2P Service. + */ +export class DummyP2PService implements P2PService { + /** + * Starts the dummy implementation. + * @returns A resolved promise. + */ + public start() { + return Promise.resolve(); + } + + /** + * Stops the dummy imaplementation. + * @returns A resolved promise. + */ + public stop() { + return Promise.resolve(); + } + + /** + * Called to have the given transaction propagated through the P2P network. + * @param _ - The transaction to be propagated. + */ + public propagateTx(_: Tx) {} + + /** + * Called upon receipt of settled transactions. + * @param _ - The hashes of the settled transactions. + */ + public settledTxs(_: TxHash[]) {} +} diff --git a/yarn-project/p2p/src/service/index.ts b/yarn-project/p2p/src/service/index.ts new file mode 100644 index 00000000000..4e197f1c107 --- /dev/null +++ b/yarn-project/p2p/src/service/index.ts @@ -0,0 +1,2 @@ +export * from './service.js'; +export * from './libp2p_service.js'; diff --git a/yarn-project/p2p/src/service/known_txs.test.ts b/yarn-project/p2p/src/service/known_txs.test.ts new file mode 100644 index 00000000000..c36254f905f --- /dev/null +++ b/yarn-project/p2p/src/service/known_txs.test.ts @@ -0,0 +1,45 @@ +import { expect } from '@jest/globals'; +import { mock } from 'jest-mock-extended'; +import { PeerId, Ed25519PeerId } from '@libp2p/interface-peer-id'; +import { KnownTxLookup } from './known_txs.js'; +import { TxHash } from '@aztec/types'; +import { randomBytes } from '@aztec/foundation/crypto'; + +const createMockPeerId = (peerId: string): PeerId => { + return mock({ + toString: () => peerId, + }); +}; + +const createTxHash = () => { + return new TxHash(randomBytes(32)); +}; + +describe('Known Txs', () => { + it('Returns false when a peer has not seen a tx', () => { + const knownTxs = new KnownTxLookup(); + + const peer = createMockPeerId('Peer 1'); + const txHash = createTxHash(); + + expect(knownTxs.hasPeerSeenTx(peer, txHash.toString())).toEqual(false); + }); + + it('Returns true when a peer has seen a tx', () => { + const knownTxs = new KnownTxLookup(); + + const peer = createMockPeerId('Peer 1'); + const peer2 = createMockPeerId('Peer 2'); + const txHash = createTxHash(); + + knownTxs.addPeerForTx(peer, txHash.toString()); + + expect(knownTxs.hasPeerSeenTx(peer, txHash.toString())).toEqual(true); + expect(knownTxs.hasPeerSeenTx(peer2, txHash.toString())).toEqual(false); + + knownTxs.addPeerForTx(peer2, txHash.toString()); + + expect(knownTxs.hasPeerSeenTx(peer, txHash.toString())).toEqual(true); + expect(knownTxs.hasPeerSeenTx(peer2, txHash.toString())).toEqual(true); + }); +}); diff --git a/yarn-project/p2p/src/service/known_txs.ts b/yarn-project/p2p/src/service/known_txs.ts new file mode 100644 index 00000000000..b9a38f2cd0c --- /dev/null +++ b/yarn-project/p2p/src/service/known_txs.ts @@ -0,0 +1,56 @@ +import { PeerId } from '@libp2p/interface-peer-id'; + +/** + * Keeps a record of which Peers have 'seen' which transactions. + */ +export class KnownTxLookup { + private lookup: { [key: string]: { [key: string]: boolean } } = {}; + + constructor() {} + + /** + * Inform this lookup that a peer has 'seen' a transaction. + * @param peerId - The peerId of the peer that has 'seen' the transaction. + * @param txHash - The thHash of the 'seen' transaction. + */ + public addPeerForTx(peerId: PeerId, txHash: string) { + const peerIdAsString = peerId.toString(); + const existingLookup = this.lookup[txHash]; + if (existingLookup === undefined) { + const newLookup: { [key: string]: boolean } = {}; + newLookup[peerIdAsString] = true; + this.lookup[txHash] = newLookup; + return; + } + existingLookup[peerIdAsString] = true; + } + + /** + * Determine if a peer has 'seen' a transaction. + * @param peerId - The peerId of the peer. + * @param txHash - The thHash of the transaction. + * @returns A boolean indicating if the transaction has been 'seen' by the peer. + */ + public hasPeerSeenTx(peerId: PeerId, txHash: string) { + const existingLookup = this.lookup[txHash]; + if (existingLookup === undefined) { + return false; + } + const peerIdAsString = peerId.toString(); + return !!existingLookup[peerIdAsString]; + } + + /** + * Updates the lookup from the result of settled txs + * These txs will be cleared out of the lookup. + * It is possible that some txs could still be gossiped for a + * short period of time meaning they come back into this lookup + * but this should be infrequent and cause no undesirable effects + * @param txHashes - The hashes of the newly settled transactions + */ + public handleSettledTxs(txHashes: string[]) { + for (const txHash of txHashes) { + delete this.lookup[txHash]; + } + } +} diff --git a/yarn-project/p2p/src/service/libp2p_service.ts b/yarn-project/p2p/src/service/libp2p_service.ts new file mode 100644 index 00000000000..b0e0fff59aa --- /dev/null +++ b/yarn-project/p2p/src/service/libp2p_service.ts @@ -0,0 +1,385 @@ +import { Libp2p, Libp2pOptions, ServiceFactoryMap, createLibp2p } from 'libp2p'; +import { tcp } from '@libp2p/tcp'; +import { noise } from '@chainsafe/libp2p-noise'; +import { yamux } from '@chainsafe/libp2p-yamux'; +import { mplex } from '@libp2p/mplex'; +import { bootstrap } from '@libp2p/bootstrap'; +import { DualKadDHT, kadDHT } from '@libp2p/kad-dht'; +import { createEd25519PeerId, createFromProtobuf, exportToProtobuf } from '@libp2p/peer-id-factory'; +import type { ServiceMap } from '@libp2p/interface-libp2p'; +import { PeerId } from '@libp2p/interface-peer-id'; +import { IncomingStreamData } from '@libp2p/interface-registrar'; +import { P2PService } from './service.js'; +import { createDebugLogger } from '@aztec/foundation/log'; +import { SerialQueue } from '@aztec/foundation/fifo'; +import { P2PConfig } from '../config.js'; +import { Tx, TxHash } from '@aztec/types'; +import { pipe } from 'it-pipe'; +import { + Messages, + createGetTransactionsRequestMessage, + createTransactionHashesMessage, + createTransactionsMessage, + decodeGetTransactionsRequestMessage, + decodeTransactionHashesMessage, + decodeTransactionsMessage, + getEncodedMessage, +} from './tx_messages.js'; +import { KnownTxLookup } from './known_txs.js'; +import { TxPool } from '../index.js'; +import { autoNATService } from 'libp2p/autonat'; +import { identifyService } from 'libp2p/identify'; + +const INITIAL_PEER_REFRESH_INTERVAL = 20000; + +/** + * Create a libp2p peer ID. + * @returns The peer ID. + */ +export async function createLibP2PPeerId() { + return await createEd25519PeerId(); +} + +/** + * Exports a given peer id to a string representation. + * @param peerId - The peerId instance to be converted. + * @returns The peer id as a string. + */ +export function exportLibP2PPeerIdToString(peerId: PeerId) { + return Buffer.from(exportToProtobuf(peerId)).toString('hex'); +} + +/** + * Lib P2P implementation of the P2PService interface. + */ +export class LibP2PService implements P2PService { + private jobQueue: SerialQueue = new SerialQueue(); + private timeout: NodeJS.Timer | undefined = undefined; + private knownTxLookup: KnownTxLookup = new KnownTxLookup(); + constructor( + private config: P2PConfig, + private node: Libp2p, + private protocolId: string, + private txPool: TxPool, + private logger = createDebugLogger('aztec:libp2p_service'), + ) {} + + /** + * Starts the LibP2P service. + * @returns An empty promise. + */ + public async start() { + if (this.node.isStarted()) { + throw new Error('P2P service already started'); + } + const { enableNat, tcpListenIp, tcpListenPort, announceHostname, announcePort } = this.config; + this.logger(`Starting P2P node on ${tcpListenIp}:${tcpListenPort}`); + if (announceHostname) this.logger(`Announcing at ${announceHostname}:${announcePort ?? tcpListenPort}`); + if (enableNat) this.logger(`Enabling NAT in libp2p module`); + + this.node.addEventListener('peer:discovery', evt => { + const peerId = evt.detail.id; + if (this.isBootstrapPeer(peerId)) { + this.logger(`Discovered bootstrap peer ${peerId.toString()}`); + } + }); + + this.node.addEventListener('peer:connect', evt => { + const peerId = evt.detail; + this.handleNewConnection(peerId); + }); + + this.node.addEventListener('peer:disconnect', evt => { + const peerId = evt.detail; + if (this.isBootstrapPeer(peerId)) { + this.logger(`Disconnect from bootstrap peer ${peerId.toString()}`); + } else { + this.logger(`Disconnected from transaction peer ${peerId.toString()}`); + } + }); + + this.jobQueue.start(); + await this.node.start(); + await this.node.handle(this.protocolId, (incoming: IncomingStreamData) => + this.jobQueue.put(() => Promise.resolve(this.handleProtocolDial(incoming))), + ); + const dht = this.node.services['kadDHT'] as DualKadDHT; + this.logger(`Started P2P client as ${await dht.getMode()} with Peer ID ${this.node.peerId.toString()}`); + this.timeout = setTimeout(async () => { + this.logger(`Refreshing routing table...`); + await dht.refreshRoutingTable(); + }, INITIAL_PEER_REFRESH_INTERVAL); + } + + /** + * Stops the LibP2P service. + * @returns An empty promise. + */ + public async stop() { + if (this.timeout) { + clearTimeout(this.timeout); + } + await this.jobQueue.end(); + await this.node.stop(); + } + + /** + * Creates an instance of the LibP2P service. + * @param config - The configuration to use when creating the service. + * @param txPool - The transaction pool to be accessed by the service. + * @returns The new service. + */ + public static async new(config: P2PConfig, txPool: TxPool) { + const { + enableNat, + tcpListenIp, + tcpListenPort, + announceHostname, + announcePort, + serverMode, + minPeerCount, + maxPeerCount, + } = config; + const peerId = config.peerIdPrivateKey + ? await createFromProtobuf(Buffer.from(config.peerIdPrivateKey, 'hex')) + : await createLibP2PPeerId(); + + const opts: Libp2pOptions = { + start: false, + peerId, + addresses: { + listen: [`/ip4/${tcpListenIp}/tcp/${tcpListenPort}`], + announce: announceHostname ? [`/ip4/${announceHostname}/tcp/${announcePort ?? tcpListenPort}`] : [], + }, + transports: [tcp()], + streamMuxers: [yamux(), mplex()], + connectionEncryption: [noise()], + connectionManager: { + minConnections: minPeerCount, + maxConnections: maxPeerCount, + }, + peerDiscovery: [ + bootstrap({ + list: config.bootstrapNodes, + }), + ], + }; + + const services: ServiceFactoryMap = { + identify: identifyService({ + protocolPrefix: 'aztec', + }), + kadDHT: kadDHT({ + protocolPrefix: 'aztec', + clientMode: !serverMode, + }), + }; + + if (enableNat) { + services.nat = autoNATService({ + protocolPrefix: 'aztec', + }); + } + + const node = await createLibp2p({ + ...opts, + services, + }); + const protocolId = config.transactionProtocol; + return new LibP2PService(config, node, protocolId, txPool); + } + + /** + * Propagates the provided transaction to peers. + * @param tx - The transaction to propagate. + */ + public propagateTx(tx: Tx): void { + void this.jobQueue.put(() => Promise.resolve(this.sendTxToPeers(tx))); + } + + /** + * Handles the settling of a new batch of transactions. + * @param txHashes - The hashes of the newly settled transactions. + */ + public settledTxs(txHashes: TxHash[]): void { + this.knownTxLookup.handleSettledTxs(txHashes.map(x => x.toString())); + } + + private async handleProtocolDial(incomingStreamData: IncomingStreamData) { + try { + const { message, peer } = await this.consumeInboundStream(incomingStreamData); + if (!message.length) { + this.logger(`Ignoring 0 byte message from peer${peer.toString()}`); + } + await this.processMessage(message, peer); + } catch (err) { + this.logger( + `Failed to handle received message from peer ${incomingStreamData.connection.remotePeer.toString()}`, + err, + ); + } + } + + private async consumeInboundStream(incomingStreamData: IncomingStreamData) { + let buffer = Buffer.alloc(0); + await pipe(incomingStreamData.stream, async source => { + for await (const msg of source) { + const payload = msg.subarray(); + buffer = Buffer.concat([buffer, Buffer.from(payload)]); + } + }); + incomingStreamData.stream.close(); + return { message: buffer, peer: incomingStreamData.connection.remotePeer }; + } + + private handleNewConnection(peerId: PeerId) { + if (this.isBootstrapPeer(peerId)) { + this.logger(`Connected to bootstrap peer ${peerId.toString()}`); + } else { + this.logger(`Connected to transaction peer ${peerId.toString()}`); + // send the peer our current pooled transaction hashes + void this.jobQueue.put(async () => { + await this.sendTxHashesMessageToPeer(peerId); + }); + } + } + + private async processMessage(message: Buffer, peerId: PeerId) { + const type = message.readUInt32BE(0); + const encodedMessage = getEncodedMessage(message); + switch (type) { + case Messages.POOLED_TRANSACTIONS: + await this.processReceivedTxs(encodedMessage, peerId); + return; + case Messages.POOLED_TRANSACTION_HASHES: + await this.processReceivedTxHashes(encodedMessage, peerId); + return; + case Messages.GET_TRANSACTIONS: + await this.processReceivedGetTransactionsRequest(encodedMessage, peerId); + return; + } + throw new Error(`Unknown message type ${type}`); + } + + private async processReceivedTxHashes(encodedMessage: Buffer, peerId: PeerId) { + try { + const txHashes = decodeTransactionHashesMessage(encodedMessage); + this.logger(`Received tx hash messages from ${peerId.toString()}`); + // we send a message requesting the transactions that we don't have from the set of received hashes + const requiredHashes = txHashes.filter(hash => !this.txPool.hasTx(hash)); + if (!requiredHashes.length) { + return; + } + await this.sendGetTransactionsMessageToPeer(txHashes, peerId); + } catch (err) { + this.logger(`Failed to process received tx hashes`, err); + } + } + + private async processReceivedGetTransactionsRequest(encodedMessage: Buffer, peerId: PeerId) { + try { + this.logger(`Received get txs messages from ${peerId.toString()}`); + // get the transactions in the list that we have and return them + const removeUndefined = (value: S | undefined): value is S => value != undefined; + const txHashes = decodeGetTransactionsRequestMessage(encodedMessage); + const txs = txHashes.map(x => this.txPool.getTxByHash(x)).filter(removeUndefined); + if (!txs.length) { + return; + } + await this.sendTransactionsMessageToPeer(txs, peerId); + } catch (err) { + this.logger(`Failed to process get txs request`, err); + } + } + + private async processReceivedTxs(encodedMessage: Buffer, peerId: PeerId) { + try { + const txs = decodeTransactionsMessage(encodedMessage); + // Could optimise here and process all txs at once + // Propagation would need to filter and send custom tx set per peer + for (const tx of txs) { + await this.processTxFromPeer(tx, peerId); + } + } catch (err) { + this.logger(`Failed to process pooled transactions message`, err); + } + } + + private async processTxFromPeer(tx: Tx, peerId: PeerId): Promise { + const txHash = await tx.getTxHash(); + const txHashString = txHash.toString(); + this.knownTxLookup.addPeerForTx(peerId, txHashString); + this.logger(`Received tx ${txHashString} from peer ${peerId.toString()}`); + await this.txPool.addTxs([tx]); + this.propagateTx(tx); + } + + private async sendTxToPeers(tx: Tx) { + const txs = createTransactionsMessage([tx]); + const payload = new Uint8Array(txs); + const peers = this.getTxPeers(); + const txHash = await tx.getTxHash(); + const txHashString = txHash.toString(); + for (const peer of peers) { + try { + if (this.knownTxLookup.hasPeerSeenTx(peer, txHashString)) { + this.logger(`Not sending tx ${txHashString} to peer ${peer.toString()} as they have already seen it`); + continue; + } + this.logger(`Sending tx ${txHashString} to peer ${peer.toString()}`); + await this.sendRawMessageToPeer(payload, peer); + this.knownTxLookup.addPeerForTx(peer, txHashString); + } catch (err) { + this.logger(`Failed to send txs to peer ${peer.toString()}`, err); + continue; + } + } + } + + private async sendTxHashesMessageToPeer(peer: PeerId) { + try { + const hashes = this.txPool.getAllTxHashes(); + if (!hashes.length) { + return; + } + const message = createTransactionHashesMessage(hashes); + await this.sendRawMessageToPeer(new Uint8Array(message), peer); + } catch (err) { + this.logger(`Failed to send tx hashes to peer ${peer.toString()}`, err); + } + } + + private async sendGetTransactionsMessageToPeer(hashes: TxHash[], peer: PeerId) { + try { + const message = createGetTransactionsRequestMessage(hashes); + await this.sendRawMessageToPeer(new Uint8Array(message), peer); + } catch (err) { + this.logger(`Failed to send tx request to peer ${peer.toString()}`, err); + } + } + + private async sendTransactionsMessageToPeer(txs: Tx[], peer: PeerId) { + // don't filter out any transactions based on what we think the peer has seen, + // we have been explicitly asked for these transactions + const message = createTransactionsMessage(txs); + await this.sendRawMessageToPeer(message, peer); + for (const tx of txs) { + const hash = await tx.getTxHash(); + this.knownTxLookup.addPeerForTx(peer, hash.toString()); + } + } + + private async sendRawMessageToPeer(message: Uint8Array, peer: PeerId) { + const stream = await this.node.dialProtocol(peer, this.protocolId); + await pipe([message], stream); + stream.close(); + } + + private getTxPeers() { + return this.node.getPeers().filter(peer => !this.isBootstrapPeer(peer)); + } + + private isBootstrapPeer(peer: PeerId) { + return this.config.bootstrapNodes.findIndex(bootstrap => bootstrap.includes(peer.toString())) != -1; + } +} diff --git a/yarn-project/p2p/src/service/service.ts b/yarn-project/p2p/src/service/service.ts new file mode 100644 index 00000000000..7bdb41e1cf6 --- /dev/null +++ b/yarn-project/p2p/src/service/service.ts @@ -0,0 +1,30 @@ +import { Tx, TxHash } from '@aztec/types'; + +/** + * The interface for a P2P service implementation. + */ +export interface P2PService { + /** + * Starts the service. + * @returns An empty promise. + */ + start(): Promise; + + /** + * Stops the service. + * @returns An empty promise. + */ + stop(): Promise; + + /** + * Called to have the given transaction propagated through the P2P network. + * @param tx - The transaction to be propagated. + */ + propagateTx(tx: Tx): void; + + /** + * Called upon receipt of settled transactions. + * @param txHashes - The hashes of the settled transactions. + */ + settledTxs(txHashes: TxHash[]): void; +} diff --git a/yarn-project/p2p/src/service/tx_messages.test.ts b/yarn-project/p2p/src/service/tx_messages.test.ts new file mode 100644 index 00000000000..04edb1568a7 --- /dev/null +++ b/yarn-project/p2p/src/service/tx_messages.test.ts @@ -0,0 +1,130 @@ +import { makeKernelPublicInputs, makePublicCallRequest, makeSignedTxRequest } from '@aztec/circuits.js/factories'; +import { EncodedContractFunction, Tx, TxHash, UnverifiedData } from '@aztec/types'; +import { expect } from '@jest/globals'; +import { randomBytes } from 'crypto'; +import { + Messages, + createGetTransactionsRequestMessage, + createTransactionHashesMessage, + createTransactionsMessage, + decodeGetTransactionsRequestMessage, + decodeMessageType, + decodeTransactionHashesMessage, + decodeTransactionsMessage, + getEncodedMessage, + toTxMessage, + fromTxMessage, +} from './tx_messages.js'; +import { Fr, KERNEL_PUBLIC_CALL_STACK_LENGTH, Proof } from '@aztec/circuits.js'; +import times from 'lodash.times'; + +const makePrivateTx = () => { + const encodedPublicFunctions = [EncodedContractFunction.random(), EncodedContractFunction.random()]; + const enqueuedPublicFunctionCalls = times(KERNEL_PUBLIC_CALL_STACK_LENGTH, i => makePublicCallRequest(i)); + return Tx.createPrivate( + makeKernelPublicInputs(), + Proof.fromBuffer(Buffer.alloc(10, 9)), + UnverifiedData.random(8), + encodedPublicFunctions, + enqueuedPublicFunctionCalls, + ); +}; + +const makePublicTx = () => { + return Tx.createPublic(makeSignedTxRequest(1)); +}; + +const makePublicPrivateTx = () => { + const publicInputs = makeKernelPublicInputs(1); + publicInputs.end.publicCallStack = [Fr.ZERO, Fr.ZERO, Fr.ZERO, Fr.ZERO, Fr.ZERO, Fr.ZERO, Fr.ZERO, Fr.ZERO]; + return Tx.createPrivatePublic( + publicInputs, + Proof.fromBuffer(randomBytes(512)), + UnverifiedData.random(8), + makeSignedTxRequest(5), + ); +}; + +const makeTxHash = () => { + return new TxHash(randomBytes(32)); +}; + +const verifyPrivateTx = (actual: Tx, expected: Tx) => { + expect(actual.data!.toBuffer()).toEqual(expected.data?.toBuffer()); + expect(actual.proof!.toBuffer()).toEqual(expected.proof!.toBuffer()); + expect(actual.unverifiedData!.toBuffer()).toEqual(expected.unverifiedData?.toBuffer()); + expect(actual.newContractPublicFunctions!.length).toEqual(expected.newContractPublicFunctions!.length); + for (let i = 0; i < actual.newContractPublicFunctions!.length; i++) { + expect(actual.newContractPublicFunctions![i].toBuffer()).toEqual( + expected.newContractPublicFunctions![i].toBuffer(), + ); + } + expect(actual.txRequest).toBeUndefined(); +}; + +const verifyPublicTx = (actual: Tx, expected: Tx) => { + expect(actual.data).toBeUndefined(); + expect(actual.newContractPublicFunctions).toBeUndefined(); + expect(actual.proof).toBeUndefined(); + expect(actual.unverifiedData).toBeUndefined(); + expect(actual.txRequest!.toBuffer()).toEqual(expected.txRequest!.toBuffer()); +}; + +const verifyPublicPrivateTx = (actual: Tx, expected: Tx) => { + expect(actual.data!.toBuffer()).toEqual(expected.data?.toBuffer()); + expect(actual.proof).toEqual(expected.proof); + expect(actual.unverifiedData!.toBuffer()).toEqual(expected.unverifiedData?.toBuffer()); + expect(actual.txRequest!.toBuffer()).toEqual(expected.txRequest!.toBuffer()); + expect(actual.newContractPublicFunctions).toBeUndefined(); +}; + +describe('Messages', () => { + it('Correctly serialises and deserialises a single private transaction', () => { + const transaction = makePrivateTx(); + const message = toTxMessage(transaction); + const decodedTransaction = fromTxMessage(message); + verifyPrivateTx(decodedTransaction, transaction); + }); + + it('Correctly serialises and deserialises a single public transaction', () => { + const transaction = makePublicTx(); + const message = toTxMessage(transaction); + const decodedTransaction = fromTxMessage(message); + verifyPublicTx(decodedTransaction, transaction); + }); + + it('Correctly serialises and deserialises a single private/public transaction', () => { + const transaction = makePublicPrivateTx(); + const message = toTxMessage(transaction); + const decodedTransaction = fromTxMessage(message); + verifyPublicPrivateTx(decodedTransaction, transaction); + }); + + it('Correctly serialises and deserialises transactions messages', () => { + const privateTransaction = makePrivateTx(); + const publicTransaction = makePublicTx(); + const publicPrivateTransaction = makePublicPrivateTx(); + const message = createTransactionsMessage([privateTransaction, publicTransaction, publicPrivateTransaction]); + expect(decodeMessageType(message)).toBe(Messages.POOLED_TRANSACTIONS); + const decodedTransactions = decodeTransactionsMessage(getEncodedMessage(message)); + verifyPrivateTx(decodedTransactions[0], privateTransaction); + verifyPublicTx(decodedTransactions[1], publicTransaction); + verifyPublicPrivateTx(decodedTransactions[2], publicPrivateTransaction); + }); + + it('Correctly serialises and deserialises transaction hashes message', () => { + const txHashes = [makeTxHash(), makeTxHash(), makeTxHash()]; + const message = createTransactionHashesMessage(txHashes); + expect(decodeMessageType(message)).toEqual(Messages.POOLED_TRANSACTION_HASHES); + const decodedHashes = decodeTransactionHashesMessage(getEncodedMessage(message)); + expect(decodedHashes.map(x => x.toString())).toEqual(txHashes.map(x => x.toString())); + }); + + it('Correctly serialises and deserialises get transactions message', () => { + const txHashes = [makeTxHash(), makeTxHash(), makeTxHash()]; + const message = createGetTransactionsRequestMessage(txHashes); + expect(decodeMessageType(message)).toEqual(Messages.GET_TRANSACTIONS); + const decodedHashes = decodeGetTransactionsRequestMessage(getEncodedMessage(message)); + expect(decodedHashes.map(x => x.toString())).toEqual(txHashes.map(x => x.toString())); + }); +}); diff --git a/yarn-project/p2p/src/service/tx_messages.ts b/yarn-project/p2p/src/service/tx_messages.ts new file mode 100644 index 00000000000..6bcfcd2596d --- /dev/null +++ b/yarn-project/p2p/src/service/tx_messages.ts @@ -0,0 +1,205 @@ +import { KernelCircuitPublicInputs, Proof, PublicCallRequest, SignedTxRequest } from '@aztec/circuits.js'; +import { numToUInt32BE } from '@aztec/foundation/serialize'; +import { EncodedContractFunction, Tx, TxHash, UnverifiedData } from '@aztec/types'; + +/** + * Enumeration of P2P message types. + */ +export enum Messages { + POOLED_TRANSACTIONS = 1, + POOLED_TRANSACTION_HASHES = 2, + GET_TRANSACTIONS = 3, +} + +/** + * Create a P2P message from the message type and message data. + * @param type - The type of the message. + * @param messageData - The binary message data. + * @returns The encoded message. + */ +export function createMessage(type: Messages, messageData: Buffer) { + return Buffer.concat([numToUInt32BE(type), messageData]); +} + +/** + * Create a POOLED_TRANSACTIONS message from an array of transactions. + * @param txs - The transactions to encoded into a message. + * @returns The encoded message. + */ +export function createTransactionsMessage(txs: Tx[]) { + const messageData = txs.map(toTxMessage); + return createMessage(Messages.POOLED_TRANSACTIONS, Buffer.concat(messageData)); +} + +/** + * Decode a POOLED_TRANSACTIONS message into the original transaction objects. + * @param message - The binary message to be decoded. + * @returns - The array of transactions originally encoded into the message. + */ +export function decodeTransactionsMessage(message: Buffer) { + const lengthSize = 4; + let offset = 0; + const txs: Tx[] = []; + while (offset < message.length) { + const dataSize = message.readUInt32BE(offset); + const totalSizeOfMessage = lengthSize + dataSize; + txs.push(fromTxMessage(message.subarray(offset, offset + totalSizeOfMessage))); + offset += totalSizeOfMessage; + } + return txs; +} + +/** + * Create a POOLED_TRANSACTION_HASHES message. + * @param hashes - The transaction hashes to be sent. + * @returns The encoded message. + */ +export function createTransactionHashesMessage(hashes: TxHash[]) { + const messageData = hashes.map(x => x.buffer); + return createMessage(Messages.POOLED_TRANSACTION_HASHES, Buffer.concat(messageData)); +} + +/** + * Decode a POOLED_TRANSACTION_HASHESs message ito the original transaction hash objects. + * @param message - The binary message to be decoded. + * @returns - The array of transaction hashes originally encoded into the message. + */ +export function decodeTransactionHashesMessage(message: Buffer) { + let offset = 0; + const txHashes: TxHash[] = []; + while (offset < message.length) { + const slice = message.subarray(offset, offset + TxHash.SIZE); + if (slice.length < TxHash.SIZE) { + throw new Error(`Invalid message size when processing transaction hashes message`); + } + txHashes.push(new TxHash(slice)); + offset += TxHash.SIZE; + } + return txHashes; +} + +/** + * Create a GET_TRANSACTIONS message from an array of transaction hashes. + * @param hashes - The hashes of the transactions to be requested. + * @returns The encoded message. + */ +export function createGetTransactionsRequestMessage(hashes: TxHash[]) { + const messageData = hashes.map(x => x.buffer); + return createMessage(Messages.GET_TRANSACTIONS, Buffer.concat(messageData)); +} + +/** + * Decode a GET_TRANSACTIONS message into the original transaction hash objects. + * @param message - The binary message to be decoded. + * @returns - The array of transaction hashes originally encoded into the message. + */ +export function decodeGetTransactionsRequestMessage(message: Buffer) { + // for the time being this payload is effectively the same as the POOLED_TRANSACTION_HASHES message + return decodeTransactionHashesMessage(message); +} + +/** + * Decode the message type from a received message. + * @param message - The received message. + * @returns The decoded MessageType. + */ +export function decodeMessageType(message: Buffer) { + return message.readUInt32BE(0); +} + +/** + * Return the encoded message (minus the header) from received message buffer. + * @param message - The complete received message. + * @returns The encoded message, without the header. + */ +export function getEncodedMessage(message: Buffer) { + return message.subarray(4); +} + +/** + * Creates a tx 'message' for sending to a peer. + * @param tx - The transaction to convert to a message. + * @returns - The message. + */ +export function toTxMessage(tx: Tx): Buffer { + // eslint-disable-next-line jsdoc/require-jsdoc + const createMessageComponent = (obj?: { toBuffer: () => Buffer }) => { + if (!obj) { + // specify a length of 0 bytes + return numToUInt32BE(0); + } + const buffer = obj.toBuffer(); + return Buffer.concat([numToUInt32BE(buffer.length), buffer]); + }; + // eslint-disable-next-line jsdoc/require-jsdoc + const createMessageComponents = (obj?: { toBuffer: () => Buffer }[]) => { + if (!obj || !obj.length) { + // specify a length of 0 bytes + return numToUInt32BE(0); + } + const allComponents = Buffer.concat(obj.map(createMessageComponent)); + return Buffer.concat([numToUInt32BE(obj.length), allComponents]); + }; + const messageBuffer = Buffer.concat([ + createMessageComponent(tx.data), + createMessageComponent(tx.proof), + createMessageComponent(tx.txRequest), + createMessageComponent(tx.unverifiedData), + createMessageComponents(tx.newContractPublicFunctions), + createMessageComponents(tx.enqueuedPublicFunctionCalls), + ]); + const messageLength = numToUInt32BE(messageBuffer.length); + return Buffer.concat([messageLength, messageBuffer]); +} + +/** + * Reproduces a transaction from a transaction 'message' + * @param buffer - The message buffer to convert to a tx. + * @returns - The reproduced transaction. + */ +export function fromTxMessage(buffer: Buffer): Tx { + // eslint-disable-next-line jsdoc/require-jsdoc + const toObject = (objectBuffer: Buffer, factory: { fromBuffer: (b: Buffer) => T }) => { + const objectSize = objectBuffer.readUint32BE(0); + return { + remainingData: objectBuffer.subarray(objectSize + 4), + obj: objectSize === 0 ? undefined : factory.fromBuffer(objectBuffer.subarray(4, objectSize + 4)), + }; + }; + + // eslint-disable-next-line jsdoc/require-jsdoc + const toObjectArray = (objectBuffer: Buffer, factory: { fromBuffer: (b: Buffer) => T }) => { + const output: T[] = []; + const numItems = objectBuffer.readUint32BE(0); + let workingBuffer = objectBuffer.subarray(4); + for (let i = 0; i < numItems; i++) { + const obj = toObject(workingBuffer, factory); + workingBuffer = obj.remainingData; + if (obj !== undefined) { + output.push(obj.obj!); + } + } + return { + remainingData: workingBuffer, + objects: output, + }; + }; + // this is the opposite of the 'toMessage' function + // so the first 4 bytes is the complete length, skip it + const publicInputs = toObject(buffer.subarray(4), KernelCircuitPublicInputs); + const proof = toObject(publicInputs.remainingData, Proof); + const txRequest = toObject(proof.remainingData, SignedTxRequest); + const unverified = toObject(txRequest.remainingData, UnverifiedData); + if (!unverified.obj) { + unverified.obj = new UnverifiedData([]); + } + const functions = toObjectArray(unverified.remainingData, EncodedContractFunction); + const publicCalls = toObjectArray(functions.remainingData, PublicCallRequest); + // working buffer now begins with the first enqueued public call + if (txRequest.obj) { + return publicInputs.obj + ? Tx.createPrivatePublic(publicInputs.obj!, proof.obj!, unverified.obj, txRequest.obj!) + : Tx.createPublic(txRequest.obj!); + } + return Tx.createPrivate(publicInputs.obj!, proof.obj!, unverified.obj, functions.objects, publicCalls.objects); +} diff --git a/yarn-project/p2p/src/tx_pool/memory_tx_pool.ts b/yarn-project/p2p/src/tx_pool/memory_tx_pool.ts index 4971e12272d..1e8fc195ce9 100644 --- a/yarn-project/p2p/src/tx_pool/memory_tx_pool.ts +++ b/yarn-project/p2p/src/tx_pool/memory_tx_pool.ts @@ -27,7 +27,7 @@ export class InMemoryTxPool implements TxPool { */ public getTxByHash(txHash: TxHash): Tx | undefined { const result = this.txs.get(txHash.toBigInt()); - return result; + return result === undefined ? undefined : Tx.clone(result); } /** @@ -60,6 +60,23 @@ export class InMemoryTxPool implements TxPool { * @returns Array of tx objects in the order they were added to the pool. */ public getAllTxs(): Tx[] { - return Array.from(this.txs.values()); + return Array.from(this.txs.values()).map(x => Tx.clone(x)); + } + + /** + * Gets the hashes of all transactions currently in the tx pool. + * @returns An array of transaction hashes found in the tx pool. + */ + public getAllTxHashes(): TxHash[] { + return Array.from(this.txs.keys()).map(x => TxHash.fromBigInt(x)); + } + + /** + * Returns a boolean indicating if the transaction is present in the pool. + * @param txHash - The hash of the transaction to be queried. + * @returns True if the transaction present, false otherwise. + */ + public hasTx(txHash: TxHash): boolean { + return this.txs.has(txHash.toBigInt()); } } diff --git a/yarn-project/p2p/src/tx_pool/tx_pool.ts b/yarn-project/p2p/src/tx_pool/tx_pool.ts index e8d981bb624..148101fa670 100644 --- a/yarn-project/p2p/src/tx_pool/tx_pool.ts +++ b/yarn-project/p2p/src/tx_pool/tx_pool.ts @@ -28,4 +28,17 @@ export interface TxPool { * @returns An array of transaction objects found in the tx pool. */ getAllTxs(): Tx[]; + + /** + * Gets the hashes of all transactions currently in the tx pool. + * @returns An array of transaction hashes found in the tx pool. + */ + getAllTxHashes(): TxHash[]; + + /** + * Returns a boolean indicating if the transaction is present in the pool. + * @param txHash - The hash of the transaction to be queried. + * @returns True if the transaction present, false otherwise. + */ + hasTx(txHash: TxHash): boolean; } diff --git a/yarn-project/package.json b/yarn-project/package.json index f38b5fa2a6a..99168070034 100644 --- a/yarn-project/package.json +++ b/yarn-project/package.json @@ -25,6 +25,7 @@ "circuits.js", "docs", "end-to-end", + "ethereum", "foundation", "key-store", "merkle-tree", @@ -32,7 +33,9 @@ "noir-compiler", "l1-artifacts", "p2p", + "p2p-bootstrap", "prover-client", + "rollup-provider", "aztec-node", "sequencer-client", "types", diff --git a/yarn-project/rollup-provider/.dockerignore b/yarn-project/rollup-provider/.dockerignore new file mode 100644 index 00000000000..2b30eaf4896 --- /dev/null +++ b/yarn-project/rollup-provider/.dockerignore @@ -0,0 +1,4 @@ +data +dest +node_modules +Dockerfile \ No newline at end of file diff --git a/yarn-project/rollup-provider/.eslintrc.cjs b/yarn-project/rollup-provider/.eslintrc.cjs new file mode 100644 index 00000000000..e659927475c --- /dev/null +++ b/yarn-project/rollup-provider/.eslintrc.cjs @@ -0,0 +1 @@ +module.exports = require('@aztec/foundation/eslint'); diff --git a/yarn-project/rollup-provider/.gitignore b/yarn-project/rollup-provider/.gitignore new file mode 100644 index 00000000000..81efe293f26 --- /dev/null +++ b/yarn-project/rollup-provider/.gitignore @@ -0,0 +1 @@ +/data* diff --git a/yarn-project/rollup-provider/Dockerfile b/yarn-project/rollup-provider/Dockerfile new file mode 100644 index 00000000000..4c70de61f2b --- /dev/null +++ b/yarn-project/rollup-provider/Dockerfile @@ -0,0 +1,15 @@ +FROM 278380418400.dkr.ecr.eu-west-2.amazonaws.com/yarn-project-base AS builder + +COPY . . + +WORKDIR /usr/src/yarn-project/rollup-provider +RUN yarn build && yarn formatting + +# Prune dev dependencies. See comment in base image. +RUN yarn cache clean +RUN yarn workspaces focus --production > /dev/null + +FROM node:18-alpine +COPY --from=builder /usr/src /usr/src +WORKDIR /usr/src/yarn-project/rollup-provider +ENTRYPOINT ["yarn"] \ No newline at end of file diff --git a/yarn-project/rollup-provider/README.md b/yarn-project/rollup-provider/README.md new file mode 100644 index 00000000000..923ae3b99bb --- /dev/null +++ b/yarn-project/rollup-provider/README.md @@ -0,0 +1,5 @@ +# Rollup Provider + +This package contains a standalone instance of the Aztec Node with a simple http interface in front of it. + +To build, simply type `yarn build`. To start, type `yarn start`. diff --git a/yarn-project/rollup-provider/package.json b/yarn-project/rollup-provider/package.json new file mode 100644 index 00000000000..aaeef3ec6c6 --- /dev/null +++ b/yarn-project/rollup-provider/package.json @@ -0,0 +1,59 @@ +{ + "name": "@aztec/rollup-provider", + "version": "0.0.0", + "main": "dest/index.js", + "type": "module", + "exports": "./dest/index.js", + "typedoc": { + "entryPoint": "./src/index.ts", + "displayName": "Rollup Provider", + "tsconfig": "./tsconfig.json" + }, + "scripts": { + "prepare": "node ../yarn-project-base/scripts/update_build_manifest.mjs package.json", + "prepare:check": "node ../yarn-project-base/scripts/update_build_manifest.mjs package.json --check", + "build": "yarn clean && tsc -b", + "build:dev": "tsc -b --watch", + "clean": "rm -rf ./dest .tsbuildinfo", + "formatting": "run -T prettier --check ./src && run -T eslint ./src", + "formatting:fix": "run -T prettier -w ./src", + "test": "NODE_NO_WARNINGS=1 node --experimental-vm-modules $(yarn bin jest) --passWithNoTests", + "start": "node ./dest/index.js" + }, + "inherits": [ + "../package.common.json" + ], + "jest": { + "preset": "ts-jest/presets/default-esm", + "moduleNameMapper": { + "^(\\.{1,2}/.*)\\.js$": "$1" + }, + "testRegex": "./src/.*\\.test\\.ts$", + "rootDir": "./src" + }, + "dependencies": { + "@aztec/aztec-node": "workspace:^", + "dotenv": "^16.0.3", + "koa": "^2.14.2", + "koa-router": "^12.0.0", + "promise-readable": "^6.0.0", + "tslib": "^2.4.0" + }, + "devDependencies": { + "@jest/globals": "^29.5.0", + "@rushstack/eslint-patch": "^1.1.4", + "@types/jest": "^29.5.0", + "@types/koa": "^2.13.6", + "@types/node": "^18.7.23", + "jest": "^29.5.0", + "ts-jest": "^29.1.0", + "ts-node": "^10.9.1", + "typescript": "^5.0.4" + }, + "files": [ + "dest", + "src", + "!*.test.*" + ], + "types": "./dest/index.d.ts" +} diff --git a/yarn-project/rollup-provider/src/app.ts b/yarn-project/rollup-provider/src/app.ts new file mode 100644 index 00000000000..79353eb0a43 --- /dev/null +++ b/yarn-project/rollup-provider/src/app.ts @@ -0,0 +1,194 @@ +import { AztecNode, txFromJson, txToJson } from '@aztec/aztec-node'; +import { Fr } from '@aztec/circuits.js'; +import { AztecAddress } from '@aztec/foundation/aztec-address'; +import { createDebugLogger } from '@aztec/foundation/log'; +import { MerkleTreeId, TxHash } from '@aztec/types'; +import Koa, { Context, DefaultState } from 'koa'; +import Router from 'koa-router'; +import { PromiseReadable } from 'promise-readable'; + +const logger = createDebugLogger('aztec:http_router'); + +/** + * Factory method for constructing the http service. + * @param node - An instance of Aztec Node into which calls are forwared. + * @param prefix - A prefix for the http service's api routes + * @returns The constructed http servce. + */ +export function appFactory(node: AztecNode, prefix: string) { + const router = new Router({ prefix }); + + const checkReady = async (ctx: Context, next: () => Promise) => { + if (!(await node.isReady())) { + ctx.status = 503; + ctx.body = { error: 'Server not ready. Try again later.' }; + } else { + await next(); + } + }; + + const exceptionHandler = async (ctx: Koa.Context, next: () => Promise) => { + try { + await next(); + } catch (err: any) { + logger(err); + ctx.status = 400; + ctx.body = { error: err.message }; + } + }; + + router.get('/', async (ctx: Koa.Context) => { + ctx.body = { + serviceName: 'aztec rollup', + isReady: await node.isReady(), + }; + ctx.set('content-type', 'application/json'); + ctx.status = 200; + }); + + router.get('/get-blocks', async (ctx: Koa.Context) => { + const from = +ctx.query.from!; + const take = +ctx.query.take!; + const blocks = await node.getBlocks(from, take); + const strs = blocks.map(x => x.encode().toString('hex')); + ctx.set('content-type', 'application/json'); + ctx.body = { + blocks: strs, + }; + ctx.status = 200; + }); + + router.get('/get-block-height', async (ctx: Koa.Context) => { + ctx.set('content-type', 'application/json'); + ctx.body = { + blockHeight: await node.getBlockHeight(), + }; + ctx.status = 200; + }); + + router.get('/contract-data', async (ctx: Koa.Context) => { + const address = ctx.query.address; + ctx.set('content-type', 'application/json'); + ctx.body = { + contractData: await node.getContractData(AztecAddress.fromString(address as string)), + }; + ctx.status = 200; + }); + + router.get('/contract-info', async (ctx: Koa.Context) => { + const address = ctx.query.address; + ctx.set('content-type', 'application/json'); + ctx.body = { + contractInfo: await node.getContractData(AztecAddress.fromString(address as string)), + }; + ctx.status = 200; + }); + + router.get('/tree-roots', async (ctx: Koa.Context) => { + const roots: Record = await node.getTreeRoots(); + const output: { [key: string]: string } = {}; + for (const [key, value] of Object.entries(roots)) { + output[key] = value.toString(); + } + ctx.body = { + roots: output, + }; + ctx.status = 200; + }); + + router.get('/get-unverified', async (ctx: Koa.Context) => { + const from = +ctx.query.from!; + const take = +ctx.query.take!; + const blocks = await node.getUnverifiedData(from, take); + const strs = blocks.map(x => x.toBuffer().toString('hex')); + ctx.set('content-type', 'application/json'); + ctx.body = { + unverified: strs, + }; + ctx.status = 200; + }); + + router.get('/get-pending-tx', async (ctx: Koa.Context) => { + const hash = ctx.query.hash!; + const txHash = new TxHash(Buffer.from(hash as string, 'hex')); + const tx = await node.getPendingTxByHash(txHash); + ctx.set('content-type', 'application/json'); + ctx.body = { + tx: tx == undefined ? undefined : txToJson(tx), + }; + ctx.status = 200; + }); + + router.get('/contract-index', async (ctx: Koa.Context) => { + const leaf = ctx.query.leaf!; + const index = await node.findContractIndex(Buffer.from(leaf as string, 'hex')); + ctx.set('content-type', 'application/json'); + ctx.body = { + index, + }; + ctx.status = 200; + }); + + router.get('/contract-path', async (ctx: Koa.Context) => { + const leaf = ctx.query.leaf!; + const path = await node.getContractPath(BigInt(leaf as string)); + ctx.set('content-type', 'application/json'); + ctx.body = { + path: path.toString(), + }; + ctx.status = 200; + }); + + router.get('/data-path', async (ctx: Koa.Context) => { + const leaf = ctx.query.leaf!; + const index = BigInt(leaf as string); + const path = await node.getDataTreePath(index); + ctx.set('content-type', 'application/json'); + const pathAsString = path.toString(); + ctx.body = { + path: pathAsString, + }; + ctx.status = 200; + }); + + router.get('/l1-l2-path', async (ctx: Koa.Context) => { + const leaf = ctx.query.leaf!; + const path = await node.getL1ToL2MessagesTreePath(BigInt(leaf as string)); + ctx.set('content-type', 'application/json'); + ctx.body = { + path: path.toString(), + }; + ctx.status = 200; + }); + + router.get('/storage-at', async (ctx: Koa.Context) => { + logger('storage-at'); + const address = ctx.query.address!; + const slot = ctx.query.slot!; + const value = await node.getStorageAt(AztecAddress.fromString(address as string), BigInt(slot as string)); + ctx.set('content-type', 'application/json'); + ctx.body = { + value: value?.toString('hex'), + }; + ctx.status = 200; + }); + + router.post('/tx', checkReady, async (ctx: Koa.Context) => { + const stream = new PromiseReadable(ctx.req); + const postData = JSON.parse((await stream.readAll()) as string); + const tx = txFromJson(postData); + await node.sendTx(tx); + ctx.status = 200; + }); + + const app = new Koa(); + app.on('error', error => { + logger(`KOA app-level error. ${JSON.stringify({ error })}`); + }); + app.proxy = true; + app.use(exceptionHandler); + app.use(router.routes()); + app.use(router.allowedMethods()); + + return app; +} diff --git a/yarn-project/rollup-provider/src/index.ts b/yarn-project/rollup-provider/src/index.ts new file mode 100644 index 00000000000..ea5056c048d --- /dev/null +++ b/yarn-project/rollup-provider/src/index.ts @@ -0,0 +1,38 @@ +import 'dotenv/config'; +import { AztecNodeConfig, AztecNodeService, getConfigEnvVars } from '@aztec/aztec-node'; +import { appFactory } from './app.js'; +import http from 'http'; +import { createDebugLogger } from '@aztec/foundation/log'; + +const logger = createDebugLogger('aztec:rollup_provider'); + +const { SERVER_PORT = 9000 } = process.env; + +/** + * Entrypoint for the rollup provider service + * @returns An empty promise + */ +async function main() { + logger('Server started...'); + const aztecNodeConfig: AztecNodeConfig = getConfigEnvVars(); + const node = await AztecNodeService.createAndSync(aztecNodeConfig); + + const shutdown = async () => { + await node.stop(); + process.exit(0); + }; + + process.once('SIGINT', shutdown); + process.once('SIGTERM', shutdown); + + const app = appFactory(node, ''); + + const httpServer = http.createServer(app.callback()); + httpServer.listen(SERVER_PORT); + logger(`Server listening on port ${SERVER_PORT}.`); +} + +main().catch(err => { + logger(err); + process.exit(1); +}); diff --git a/yarn-project/rollup-provider/tsconfig.json b/yarn-project/rollup-provider/tsconfig.json new file mode 100644 index 00000000000..6a4f82c4870 --- /dev/null +++ b/yarn-project/rollup-provider/tsconfig.json @@ -0,0 +1,14 @@ +{ + "extends": "..", + "compilerOptions": { + "outDir": "dest", + "rootDir": "src", + "tsBuildInfoFile": ".tsbuildinfo" + }, + "references": [ + { + "path": "../aztec-node" + } + ], + "include": ["src", "../aztec-node/src/aztec-node/http-node.ts"] +} diff --git a/yarn-project/sequencer-client/package.json b/yarn-project/sequencer-client/package.json index 9528d8f1f52..019f83b7451 100644 --- a/yarn-project/sequencer-client/package.json +++ b/yarn-project/sequencer-client/package.json @@ -34,6 +34,7 @@ "dependencies": { "@aztec/acir-simulator": "workspace:^", "@aztec/circuits.js": "workspace:^", + "@aztec/ethereum": "workspace:^", "@aztec/foundation": "workspace:^", "@aztec/l1-artifacts": "workspace:^", "@aztec/merkle-tree": "workspace:^", @@ -45,7 +46,7 @@ "lodash.pick": "^4.4.0", "lodash.times": "^4.3.2", "tslib": "^2.4.0", - "viem": "^0.3.13" + "viem": "^0.3.14" }, "devDependencies": { "@jest/globals": "^29.5.0", diff --git a/yarn-project/sequencer-client/src/block_builder/solo_block_builder.ts b/yarn-project/sequencer-client/src/block_builder/solo_block_builder.ts index b9ea795ead5..6c8ee23ed63 100644 --- a/yarn-project/sequencer-client/src/block_builder/solo_block_builder.ts +++ b/yarn-project/sequencer-client/src/block_builder/solo_block_builder.ts @@ -473,7 +473,7 @@ export class SoloBlockBuilder implements BlockBuilder { const index = await this.db.findLeafIndex(treeId, value.toBuffer()); if (index === undefined) { - throw new Error(`Leaf with value ${value} not found in tree ${treeId}`); + throw new Error(`Leaf with value ${value} not found in tree ${MerkleTreeId[treeId]}`); } const path = await this.db.getSiblingPath(treeId, index); return new MembershipWitness(height, index, assertLength(path.toFieldArray(), height)); diff --git a/yarn-project/sequencer-client/src/client/sequencer-client.ts b/yarn-project/sequencer-client/src/client/sequencer-client.ts index 4a53cab1d9c..6f74982e382 100644 --- a/yarn-project/sequencer-client/src/client/sequencer-client.ts +++ b/yarn-project/sequencer-client/src/client/sequencer-client.ts @@ -1,7 +1,7 @@ import { P2P } from '@aztec/p2p'; import { WorldStateSynchroniser } from '@aztec/world-state'; -import { ContractDataSource } from '@aztec/types'; +import { ContractDataSource, L2BlockSource } from '@aztec/types'; import { SoloBlockBuilder } from '../block_builder/solo_block_builder.js'; import { SequencerClientConfig } from '../config.js'; import { getL1Publisher, getVerificationKeys, Sequencer } from '../index.js'; @@ -21,6 +21,7 @@ export class SequencerClient { * @param p2pClient - P2P client that provides the txs to be sequenced. * @param worldStateSynchroniser - Provides access to world state. * @param contractDataSource - Provides access to contract bytecode for public executions. + * @param l2BlockSource - Provides information about the previously published blocks. * @returns A new running instance. */ public static async new( @@ -28,6 +29,7 @@ export class SequencerClient { p2pClient: P2P, worldStateSynchroniser: WorldStateSynchroniser, contractDataSource: ContractDataSource, + l2BlockSource: L2BlockSource, ) { const publisher = getL1Publisher(config); const merkleTreeDb = worldStateSynchroniser.getLatest(); @@ -46,6 +48,7 @@ export class SequencerClient { p2pClient, worldStateSynchroniser, blockBuilder, + l2BlockSource, publicProcessorFactory, config, ); diff --git a/yarn-project/sequencer-client/src/config.ts b/yarn-project/sequencer-client/src/config.ts index 4a4032c8b32..bef9abbc9fd 100644 --- a/yarn-project/sequencer-client/src/config.ts +++ b/yarn-project/sequencer-client/src/config.ts @@ -15,10 +15,12 @@ export function getConfigEnvVars(): SequencerClientConfig { SEQ_PUBLISHER_PRIVATE_KEY, ETHEREUM_HOST, CHAIN_ID, + API_KEY, SEQ_REQUIRED_CONFS, SEQ_RETRY_INTERVAL, SEQ_TX_POLLING_INTERVAL, SEQ_MAX_TX_PER_BLOCK, + SEQ_MIN_TX_PER_BLOCK, ROLLUP_CONTRACT_ADDRESS, INBOX_CONTRACT_ADDRESS, UNVERIFIED_DATA_EMITTER_ADDRESS, @@ -27,6 +29,7 @@ export function getConfigEnvVars(): SequencerClientConfig { return { rpcUrl: ETHEREUM_HOST ? ETHEREUM_HOST : '', chainId: CHAIN_ID ? +CHAIN_ID : 31337, // 31337 is the default chain id for anvil + apiKey: API_KEY, requiredConfirmations: SEQ_REQUIRED_CONFS ? +SEQ_REQUIRED_CONFS : 1, retryIntervalMs: SEQ_RETRY_INTERVAL ? +SEQ_RETRY_INTERVAL : 1_000, transactionPollingInterval: SEQ_TX_POLLING_INTERVAL ? +SEQ_TX_POLLING_INTERVAL : 1_000, @@ -35,7 +38,8 @@ export function getConfigEnvVars(): SequencerClientConfig { unverifiedDataEmitterContract: UNVERIFIED_DATA_EMITTER_ADDRESS ? EthAddress.fromString(UNVERIFIED_DATA_EMITTER_ADDRESS) : EthAddress.ZERO, - publisherPrivateKey: Buffer.from(SEQ_PUBLISHER_PRIVATE_KEY || ''), + publisherPrivateKey: Buffer.from(SEQ_PUBLISHER_PRIVATE_KEY || '', 'hex'), maxTxsPerBlock: SEQ_MAX_TX_PER_BLOCK ? +SEQ_MAX_TX_PER_BLOCK : 32, + minTxsPerBlock: SEQ_MIN_TX_PER_BLOCK ? +SEQ_MIN_TX_PER_BLOCK : 1, }; } diff --git a/yarn-project/sequencer-client/src/publisher/config.ts b/yarn-project/sequencer-client/src/publisher/config.ts index b73374468a0..d6c91871254 100644 --- a/yarn-project/sequencer-client/src/publisher/config.ts +++ b/yarn-project/sequencer-client/src/publisher/config.ts @@ -14,6 +14,11 @@ export interface TxSenderConfig extends L1Addresses { */ rpcUrl: string; + /** + * The API key of the ethereum host. + */ + apiKey?: string; + /** * The chain id of the ethereum host. */ diff --git a/yarn-project/sequencer-client/src/publisher/l1-publisher.test.ts b/yarn-project/sequencer-client/src/publisher/l1-publisher.test.ts index cdfd000b7a6..51696faa56e 100644 --- a/yarn-project/sequencer-client/src/publisher/l1-publisher.test.ts +++ b/yarn-project/sequencer-client/src/publisher/l1-publisher.test.ts @@ -34,13 +34,13 @@ describe('L1Publisher', () => { expect(txSender.getTransactionReceipt).toHaveBeenCalledWith(txHash); }); - it('retries if sending a tx fails', async () => { + it('does not retry if sending a tx fails', async () => { txSender.sendProcessTx.mockReset().mockRejectedValueOnce(new Error()).mockResolvedValueOnce(txHash); const result = await publisher.processL2Block(l2Block); - expect(result).toEqual(true); - expect(txSender.sendProcessTx).toHaveBeenCalledTimes(2); + expect(result).toEqual(false); + expect(txSender.sendProcessTx).toHaveBeenCalledTimes(1); }); it('retries if fetching the receipt fails', async () => { diff --git a/yarn-project/sequencer-client/src/publisher/l1-publisher.ts b/yarn-project/sequencer-client/src/publisher/l1-publisher.ts index e74867e2f8c..62a4490357e 100644 --- a/yarn-project/sequencer-client/src/publisher/l1-publisher.ts +++ b/yarn-project/sequencer-client/src/publisher/l1-publisher.ts @@ -34,18 +34,28 @@ export interface L1PublisherTxSender { /** * Sends a tx to the unverified data emitter contract with unverified data. Returns once the tx has been mined. * @param l2BlockNum - Number of the L2 block that owns this unverified data. + * @param l2BlockHash - The hash of the block corresponding to this data. * @param unverifiedData - Data to publish. * @returns The hash of the mined tx. */ - sendEmitUnverifiedDataTx(l2BlockNum: number, unverifiedData: UnverifiedData): Promise; + sendEmitUnverifiedDataTx( + l2BlockNum: number, + l2BlockHash: Buffer, + unverifiedData: UnverifiedData, + ): Promise; /** * Sends a tx to the unverified data emitter contract with contract deployment data such as bytecode. Returns once the tx has been mined. * @param l2BlockNum - Number of the L2 block that owns this unverified data. + * @param l2BlockHash - The hash of the block corresponding to this data. * @param contractData - Data to publish. * @returns The hash of the mined tx. */ - sendEmitContractDeploymentTx(l2BlockNum: number, contractData: ContractPublicData[]): Promise<(string | undefined)[]>; + sendEmitContractDeploymentTx( + l2BlockNum: number, + l2BlockHash: Buffer, + contractData: ContractPublicData[], + ): Promise<(string | undefined)[]>; /** * Returns a tx receipt if the tx has been mined. @@ -139,10 +149,15 @@ export class L1Publisher implements L2BlockReceiver { /** * Publishes unverifiedData to L1. * @param l2BlockNum - The L2 block number that the unverifiedData is associated with. + * @param l2BlockHash - The hash of the block corresponding to this data. * @param unverifiedData - The unverifiedData to publish. * @returns True once the tx has been confirmed and is successful, false on revert or interrupt, blocks otherwise. */ - public async processUnverifiedData(l2BlockNum: number, unverifiedData: UnverifiedData): Promise { + public async processUnverifiedData( + l2BlockNum: number, + l2BlockHash: Buffer, + unverifiedData: UnverifiedData, + ): Promise { while (!this.interrupted) { if (!(await this.checkFeeDistributorBalance())) { this.log(`Fee distributor ETH balance too low, awaiting top up...`); @@ -150,7 +165,7 @@ export class L1Publisher implements L2BlockReceiver { continue; } - const txHash = await this.sendEmitUnverifiedDataTx(l2BlockNum, unverifiedData); + const txHash = await this.sendEmitUnverifiedDataTx(l2BlockNum, l2BlockHash, unverifiedData); if (!txHash) break; const receipt = await this.getTransactionReceipt(txHash); @@ -170,10 +185,11 @@ export class L1Publisher implements L2BlockReceiver { /** * Publishes new contract data to L1. * @param l2BlockNum - The L2 block number that the new contracts were deployed on. + * @param l2BlockHash - The hash of the block corresponding to this data. * @param contractData - The new contract data to publish. * @returns True once the tx has been confirmed and is successful, false on revert or interrupt, blocks otherwise. */ - public async processNewContractData(l2BlockNum: number, contractData: ContractPublicData[]) { + public async processNewContractData(l2BlockNum: number, l2BlockHash: Buffer, contractData: ContractPublicData[]) { let _contractData: ContractPublicData[] = []; while (!this.interrupted) { if (!(await this.checkFeeDistributorBalance())) { @@ -183,7 +199,7 @@ export class L1Publisher implements L2BlockReceiver { } const arr = _contractData.length ? _contractData : contractData; - const txHashes = await this.sendEmitNewContractDataTx(l2BlockNum, arr); + const txHashes = await this.sendEmitNewContractDataTx(l2BlockNum, l2BlockHash, arr); if (!txHashes) break; // filter successful txs _contractData = arr.filter((_, i) => !!txHashes[i]); @@ -236,19 +252,20 @@ export class L1Publisher implements L2BlockReceiver { try { return await this.txSender.sendProcessTx(encodedData); } catch (err) { - this.log(`Error sending L2 block tx to L1`, err); - await this.sleepOrInterrupted(); + this.log(`ROLLUP PUBLISH FAILED`, err); + return undefined; } } } private async sendEmitUnverifiedDataTx( l2BlockNum: number, + l2BlockHash: Buffer, unverifiedData: UnverifiedData, ): Promise { while (!this.interrupted) { try { - return await this.txSender.sendEmitUnverifiedDataTx(l2BlockNum, unverifiedData); + return await this.txSender.sendEmitUnverifiedDataTx(l2BlockNum, l2BlockHash, unverifiedData); } catch (err) { this.log(`Error sending unverified data tx to L1`, err); await this.sleepOrInterrupted(); @@ -256,10 +273,10 @@ export class L1Publisher implements L2BlockReceiver { } } - private async sendEmitNewContractDataTx(l2BlockNum: number, contractData: ContractPublicData[]) { + private async sendEmitNewContractDataTx(l2BlockNum: number, l2BlockHash: Buffer, contractData: ContractPublicData[]) { while (!this.interrupted) { try { - return await this.txSender.sendEmitContractDeploymentTx(l2BlockNum, contractData); + return await this.txSender.sendEmitContractDeploymentTx(l2BlockNum, l2BlockHash, contractData); } catch (err) { this.log(`Error sending contract data to L1`, err); await this.sleepOrInterrupted(); @@ -272,7 +289,7 @@ export class L1Publisher implements L2BlockReceiver { try { return await this.txSender.getTransactionReceipt(txHash); } catch (err) { - this.log(`Error getting tx receipt`, err); + //this.log(`Error getting tx receipt`, err); await this.sleepOrInterrupted(); } } diff --git a/yarn-project/sequencer-client/src/publisher/viem-tx-sender.ts b/yarn-project/sequencer-client/src/publisher/viem-tx-sender.ts index 616d41bd454..b9d13448f19 100644 --- a/yarn-project/sequencer-client/src/publisher/viem-tx-sender.ts +++ b/yarn-project/sequencer-client/src/publisher/viem-tx-sender.ts @@ -18,6 +18,7 @@ import { RollupAbi, UnverifiedDataEmitterAbi } from '@aztec/l1-artifacts'; import { PrivateKeyAccount, privateKeyToAccount } from 'viem/accounts'; import * as chains from 'viem/chains'; import { createDebugLogger } from '@aztec/foundation/log'; +import { createEthereumChain } from '@aztec/ethereum'; /** * Pushes transactions to the L1 rollup contract using viem. @@ -41,23 +42,22 @@ export class ViemTxSender implements L1PublisherTxSender { constructor(config: TxSenderConfig) { const { rpcUrl, - chainId, + apiKey, publisherPrivateKey, rollupContract: rollupContractAddress, unverifiedDataEmitterContract: unverifiedDataEmitterContractAddress, } = config; - + const chain = createEthereumChain(rpcUrl, apiKey); this.account = privateKeyToAccount(`0x${publisherPrivateKey.toString('hex')}`); - const chain = this.getChain(chainId); const walletClient = createWalletClient({ account: this.account, - chain, - transport: http(rpcUrl), + chain: chain.chainInfo, + transport: http(chain.rpcUrl), }); this.publicClient = createPublicClient({ - chain: chain, - transport: http(rpcUrl), + chain: chain.chainInfo, + transport: http(chain.rpcUrl), }); this.rollupContract = getContract({ @@ -118,11 +118,20 @@ export class ViemTxSender implements L1PublisherTxSender { /** * Sends a tx to the unverified data emitter contract with unverified data. Returns once the tx has been mined. * @param l2BlockNum - Number of the L2 block that owns this unverified data. + * @param l2BlockHash - The hash of the block corresponding to this data. * @param unverifiedData - Data to publish. * @returns The hash of the mined tx. */ - async sendEmitUnverifiedDataTx(l2BlockNum: number, unverifiedData: UnverifiedData): Promise { - const args = [BigInt(l2BlockNum), `0x${unverifiedData.toBuffer().toString('hex')}`] as const; + async sendEmitUnverifiedDataTx( + l2BlockNum: number, + l2BlockHash: Buffer, + unverifiedData: UnverifiedData, + ): Promise { + const args = [ + BigInt(l2BlockNum), + `0x${l2BlockHash.toString('hex')}`, + `0x${unverifiedData.toBuffer().toString('hex')}`, + ] as const; const gas = await this.unverifiedDataEmitterContract.estimateGas.emitUnverifiedData(args, { account: this.account, @@ -137,11 +146,13 @@ export class ViemTxSender implements L1PublisherTxSender { /** * Sends a tx to the unverified data emitter contract with contract deployment data such as bytecode. Returns once the tx has been mined. * @param l2BlockNum - Number of the L2 block that owns this unverified data. + * @param l2BlockHash - The hash of the block corresponding to this data. * @param newContractData - Data to publish. * @returns The hash of the mined tx. */ async sendEmitContractDeploymentTx( l2BlockNum: number, + l2BlockHash: Buffer, newContractData: ContractPublicData[], ): Promise<(string | undefined)[]> { const hashes: string[] = []; @@ -150,6 +161,7 @@ export class ViemTxSender implements L1PublisherTxSender { BigInt(l2BlockNum), contractPublicData.contractData.contractAddress.toString() as Hex, contractPublicData.contractData.portalContractAddress.toString() as Hex, + `0x${l2BlockHash.toString('hex')}`, `0x${contractPublicData.bytecode.toString('hex')}`, ] as const; diff --git a/yarn-project/sequencer-client/src/sequencer/config.ts b/yarn-project/sequencer-client/src/sequencer/config.ts index e4f8465de95..e6cd763b744 100644 --- a/yarn-project/sequencer-client/src/sequencer/config.ts +++ b/yarn-project/sequencer-client/src/sequencer/config.ts @@ -7,7 +7,11 @@ export interface SequencerConfig { */ transactionPollingInterval: number; /** - * Up to how many txs to include a block. + * The maximum number of txs to include in a block. */ maxTxsPerBlock: number; + /** + * The minimum number of txs to include in a block. + */ + minTxsPerBlock: number; } diff --git a/yarn-project/sequencer-client/src/sequencer/sequencer.test.ts b/yarn-project/sequencer-client/src/sequencer/sequencer.test.ts index 86fb4a32254..90ce1813423 100644 --- a/yarn-project/sequencer-client/src/sequencer/sequencer.test.ts +++ b/yarn-project/sequencer-client/src/sequencer/sequencer.test.ts @@ -1,6 +1,6 @@ import { CombinedHistoricTreeRoots, Fr, NUMBER_OF_L1_L2_MESSAGES_PER_ROLLUP, makeEmptyProof } from '@aztec/circuits.js'; import { P2P, P2PClientState } from '@aztec/p2p'; -import { L2Block, MerkleTreeId, PrivateTx, Tx, UnverifiedData } from '@aztec/types'; +import { L2Block, L2BlockSource, MerkleTreeId, PrivateTx, Tx, UnverifiedData } from '@aztec/types'; import { MerkleTreeOperations, WorldStateRunningState, WorldStateSynchroniser } from '@aztec/world-state'; import { MockProxy, mock } from 'jest-mock-extended'; import times from 'lodash.times'; @@ -17,6 +17,7 @@ describe('sequencer', () => { let blockBuilder: MockProxy; let merkleTreeOps: MockProxy; let publicProcessor: MockProxy; + let l2BlockSource: MockProxy; let publicProcessorFactory: MockProxy; let lastBlockNumber: number; @@ -48,7 +49,11 @@ describe('sequencer', () => { create: () => publicProcessor, }); - sequencer = new TestSubject(publisher, p2p, worldState, blockBuilder, publicProcessorFactory); + l2BlockSource = mock({ + getBlockHeight: () => Promise.resolve(lastBlockNumber), + }); + + sequencer = new TestSubject(publisher, p2p, worldState, blockBuilder, l2BlockSource, publicProcessorFactory); }); it('builds a block out of a single tx', async () => { @@ -73,7 +78,11 @@ describe('sequencer', () => { Array(NUMBER_OF_L1_L2_MESSAGES_PER_ROLLUP).fill(new Fr(0n)), ); expect(publisher.processL2Block).toHaveBeenCalledWith(block); - expect(publisher.processUnverifiedData).toHaveBeenCalledWith(lastBlockNumber + 1, expectedUnverifiedData); + expect(publisher.processUnverifiedData).toHaveBeenCalledWith( + lastBlockNumber + 1, + block.getCalldataHash(), + expectedUnverifiedData, + ); }); it('builds a block out of several txs rejecting double spends', async () => { @@ -110,7 +119,11 @@ describe('sequencer', () => { Array(NUMBER_OF_L1_L2_MESSAGES_PER_ROLLUP).fill(new Fr(0n)), ); expect(publisher.processL2Block).toHaveBeenCalledWith(block); - expect(publisher.processUnverifiedData).toHaveBeenCalledWith(lastBlockNumber + 1, expectedUnverifiedData); + expect(publisher.processUnverifiedData).toHaveBeenCalledWith( + lastBlockNumber + 1, + block.getCalldataHash(), + expectedUnverifiedData, + ); expect(p2p.deleteTxs).toHaveBeenCalledWith([await doubleSpendTx.getTxHash()]); }); }); diff --git a/yarn-project/sequencer-client/src/sequencer/sequencer.ts b/yarn-project/sequencer-client/src/sequencer/sequencer.ts index 4b844263a1f..d4ef7bfcf4b 100644 --- a/yarn-project/sequencer-client/src/sequencer/sequencer.ts +++ b/yarn-project/sequencer-client/src/sequencer/sequencer.ts @@ -6,22 +6,23 @@ import { P2P } from '@aztec/p2p'; import { ContractData, ContractPublicData, - L2Block, MerkleTreeId, PrivateTx, PublicTx, Tx, UnverifiedData, isPrivateTx, + L2Block, + L2BlockSource, } from '@aztec/types'; import { WorldStateStatus, WorldStateSynchroniser } from '@aztec/world-state'; import times from 'lodash.times'; import { BlockBuilder } from '../block_builder/index.js'; import { L1Publisher } from '../publisher/l1-publisher.js'; -import { ceilPowerOfTwo } from '../utils.js'; import { SequencerConfig } from './config.js'; import { ProcessedTx } from './processed_tx.js'; import { PublicProcessorFactory } from './public_processor.js'; +import { ceilPowerOfTwo } from '../utils.js'; /** * Sequencer client @@ -36,7 +37,8 @@ export class Sequencer { private runningPromise?: RunningPromise; private pollingIntervalMs: number; private maxTxsPerBlock = 32; - private lastBlockNumber = -1; + private minTxsPerBLock = 1; + private lastPublishedBlock = 0; private state = SequencerState.STOPPED; constructor( @@ -44,6 +46,7 @@ export class Sequencer { private p2pClient: P2P, private worldState: WorldStateSynchroniser, private blockBuilder: BlockBuilder, + private l2BlockSource: L2BlockSource, private publicProcessorFactory: PublicProcessorFactory, config?: SequencerConfig, private log = createDebugLogger('aztec:sequencer'), @@ -52,6 +55,9 @@ export class Sequencer { if (config?.maxTxsPerBlock) { this.maxTxsPerBlock = config.maxTxsPerBlock; } + if (config?.minTxsPerBlock) { + this.minTxsPerBLock = config.minTxsPerBlock; + } } /** @@ -95,7 +101,7 @@ export class Sequencer { protected async initialSync() { // TODO: Should we wait for worldstate to be ready, or is the caller expected to run await start? - this.lastBlockNumber = await this.worldState.status().then((s: WorldStateStatus) => s.syncedToL2Block); + this.lastPublishedBlock = await this.worldState.status().then((s: WorldStateStatus) => s.syncedToL2Block); } /** @@ -117,17 +123,15 @@ export class Sequencer { // Get txs to build the new block const pendingTxs = await this.p2pClient.getTxs(); - if (pendingTxs.length === 0) return; - this.log(`Processing ${pendingTxs.length} txs from P2P pool`); + if (pendingTxs.length < this.minTxsPerBLock) return; // Filter out invalid txs const validTxs = await this.takeValidTxs(pendingTxs); - if (validTxs.length === 0) { - this.log(`No valid txs left after processing`); + if (validTxs.length < this.minTxsPerBLock) { return; } - this.log(`Processing txs ${(await Tx.getHashes(validTxs)).join(', ')}`); + this.log(`Processing ${validTxs.length} txs...`); this.state = SequencerState.CREATING_BLOCK; // Process public txs and drop the ones that fail processing @@ -159,8 +163,9 @@ export class Sequencer { await this.publishL2Block(block); } catch (err) { - this.log(err, 'error'); - // TODO: Rollback changes to DB + this.log(err); + this.log(`Rolling back world state DB`); + await this.worldState.getLatest().rollback(); } } @@ -188,14 +193,17 @@ export class Sequencer { }) .filter((cd): cd is Exclude => cd !== undefined); - const publishedUnverifiedData = await this.publisher.processUnverifiedData(block.number, unverifiedData); + const blockHash = block.getCalldataHash(); + this.log(`Publishing data with block hash ${blockHash.toString('hex')}`); + + const publishedUnverifiedData = await this.publisher.processUnverifiedData(block.number, blockHash, unverifiedData); if (publishedUnverifiedData) { this.log(`Successfully published unverifiedData for block ${block.number}`); } else { this.log(`Failed to publish unverifiedData for block ${block.number}`); } - const publishedContractData = await this.publisher.processNewContractData(block.number, newContractData); + const publishedContractData = await this.publisher.processNewContractData(block.number, blockHash, newContractData); if (publishedContractData) { this.log(`Successfully published new contract data for block ${block.number}`); } else if (!publishedContractData && newContractData.length) { @@ -213,9 +221,9 @@ export class Sequencer { const publishedL2Block = await this.publisher.processL2Block(block); if (publishedL2Block) { this.log(`Successfully published block ${block.number}`); - this.lastBlockNumber++; + this.lastPublishedBlock = block.number; } else { - this.log(`Failed to publish block`); + throw new Error(`Failed to publish block`); } } @@ -260,10 +268,12 @@ export class Sequencer { * @returns Boolean indicating if our dependencies are synced to the latest block. */ protected async isBlockSynced() { - return ( - (await this.worldState.status().then((s: WorldStateStatus) => s.syncedToL2Block)) >= this.lastBlockNumber && - (await this.p2pClient.getStatus().then(s => s.syncedToL2Block)) >= this.lastBlockNumber - ); + const syncedBlocks = await Promise.all([ + this.worldState.status().then((s: WorldStateStatus) => s.syncedToL2Block), + this.p2pClient.getStatus().then(s => s.syncedToL2Block), + ]); + const min = Math.min(...syncedBlocks); + return min >= this.lastPublishedBlock; } /** @@ -279,7 +289,9 @@ export class Sequencer { const emptyTxCount = txsTargetSize - txs.length; const allTxs = [...txs, ...times(emptyTxCount, () => emptyTx)]; - const [block] = await this.blockBuilder.buildL2Block(this.lastBlockNumber + 1, allTxs, newL1ToL2Messages); + const blockNumber = (await this.l2BlockSource.getBlockHeight()) + 1; + this.log(`Building block ${blockNumber}`); + const [block] = await this.blockBuilder.buildL2Block(blockNumber, allTxs, newL1ToL2Messages); return block; } diff --git a/yarn-project/sequencer-client/tsconfig.json b/yarn-project/sequencer-client/tsconfig.json index df445b1e4bf..805fd022a15 100644 --- a/yarn-project/sequencer-client/tsconfig.json +++ b/yarn-project/sequencer-client/tsconfig.json @@ -12,6 +12,9 @@ { "path": "../circuits.js" }, + { + "path": "../ethereum" + }, { "path": "../foundation" }, diff --git a/yarn-project/tsconfig.json b/yarn-project/tsconfig.json index be39f6c740a..358d732ab9b 100644 --- a/yarn-project/tsconfig.json +++ b/yarn-project/tsconfig.json @@ -14,7 +14,8 @@ "declarationMap": true, "importHelpers": true, "resolveJsonModule": true, - "composite": true + "composite": true, + "skipLibCheck": true }, "references": [ { "path": "acir-simulator/tsconfig.json" }, @@ -33,7 +34,9 @@ { "path": "noir-contracts/tsconfig.json" }, { "path": "noir-compiler/tsconfig.json" }, { "path": "p2p/tsconfig.json" }, + { "path": "p2p-bootstrap/tsconfig.json" }, { "path": "prover-client/tsconfig.json" }, + { "path": "rollup-provider/tsconfig.json" }, { "path": "sequencer-client/tsconfig.json" }, { "path": "types/tsconfig.json" }, { "path": "world-state/tsconfig.json" } diff --git a/yarn-project/types/src/tx.ts b/yarn-project/types/src/tx.ts index f248ba6879a..5014a01e319 100644 --- a/yarn-project/types/src/tx.ts +++ b/yarn-project/types/src/tx.ts @@ -194,6 +194,33 @@ export class Tx { static async getHashes(txs: Tx[]): Promise { return await Promise.all(txs.map(tx => tx.getTxHash())); } + + /** + * Clones a tx, making a deep copy of all fields. + * @param tx - The transaction to be cloned. + * @returns The cloned transaction. + */ + static clone(tx: Tx): Tx { + const publicInputs = tx.data === undefined ? undefined : KernelCircuitPublicInputs.fromBuffer(tx.data.toBuffer()); + const proof = tx.proof === undefined ? undefined : Proof.fromBuffer(tx.proof.toBuffer()); + const unverified = + tx.unverifiedData === undefined ? undefined : UnverifiedData.fromBuffer(tx.unverifiedData.toBuffer()); + const signedTxRequest = + tx.txRequest === undefined ? undefined : SignedTxRequest.fromBuffer(tx.txRequest.toBuffer()); + const publicFunctions = + tx.newContractPublicFunctions === undefined + ? undefined + : tx.newContractPublicFunctions.map(x => { + return EncodedContractFunction.fromBuffer(x.toBuffer()); + }); + const enqueuedPublicFunctions = + tx.enqueuedPublicFunctionCalls === undefined + ? undefined + : tx.enqueuedPublicFunctionCalls.map(x => { + return PublicCallRequest.fromBuffer(x.toBuffer()); + }); + return new Tx(publicInputs, proof, unverified, signedTxRequest, publicFunctions, enqueuedPublicFunctions); + } } /** diff --git a/yarn-project/types/src/tx_hash.ts b/yarn-project/types/src/tx_hash.ts index b63c3957dfd..56d743ca520 100644 --- a/yarn-project/types/src/tx_hash.ts +++ b/yarn-project/types/src/tx_hash.ts @@ -1,5 +1,5 @@ +import { deserializeBigInt, serializeBigInt } from '@aztec/foundation/serialize'; import { assertMemberLength } from '@aztec/circuits.js'; -import { toBigInt } from '@aztec/foundation/serialize'; /** * A class representing hash of Aztec transaction. @@ -40,7 +40,15 @@ export class TxHash { * @returns The big int. */ public toBigInt() { - return toBigInt(this.buffer); + return deserializeBigInt(this.buffer, 0, TxHash.SIZE).elem; + } + /** + * Creates a tx hash from a bigint. + * @param hash - The tx hash as a big int. + * @returns The TxHash. + */ + public static fromBigInt(hash: bigint) { + return new TxHash(serializeBigInt(hash, TxHash.SIZE)); } /** * Converts this hash from a buffer of 28 bytes. diff --git a/yarn-project/world-state/src/merkle-tree/merkle_tree_operations_facade.ts b/yarn-project/world-state/src/merkle-tree/merkle_tree_operations_facade.ts index 893b6b606fb..2c33443fc0d 100644 --- a/yarn-project/world-state/src/merkle-tree/merkle_tree_operations_facade.ts +++ b/yarn-project/world-state/src/merkle-tree/merkle_tree_operations_facade.ts @@ -1,12 +1,12 @@ +import { LeafData, MerkleTreeDb, TreeInfo, MerkleTreeOperations } from '../index.js'; +import { L2Block, MerkleTreeId } from '@aztec/types'; import { LowLeafWitnessData, SiblingPath } from '@aztec/merkle-tree'; -import { LeafData, MerkleTreeDbOperations, MerkleTreeOperations, TreeInfo } from '../index.js'; -import { MerkleTreeId } from '@aztec/types'; /** * Wraps a MerkleTreeDbOperations to call all functions with a preset includeUncommitted flag. */ export class MerkleTreeOperationsFacade implements MerkleTreeOperations { - constructor(private trees: MerkleTreeDbOperations, private includeUncommitted: boolean) {} + constructor(private trees: MerkleTreeDb, private includeUncommitted: boolean) {} /** * Returns the tree info for the specified tree id. @@ -112,13 +112,37 @@ export class MerkleTreeOperationsFacade implements MerkleTreeOperations { return this.trees.updateHistoricRootsTrees(this.includeUncommitted); } + /** + * Handles a single L2 block (i.e. Inserts the new commitments into the merkle tree). + * @param block - The L2 block to handle. + * @returns Empty promise. + */ + public handleL2Block(block: L2Block): Promise { + return this.trees.handleL2Block(block); + } + + /** + * Commits all pending updates. + * @returns Empty promise. + */ + public async commit(): Promise { + return await this.trees.commit(); + } + + /** + * Rolls back all pending updates. + * @returns Empty promise. + */ + public async rollback(): Promise { + return await this.trees.rollback(); + } + /** * Batch insert multiple leaves into the tree. * @param treeId - The ID of the tree. * @param leaves - Leaves to insert into the tree. * @param treeHeight - Height of the tree. * @param subtreeHeight - Height of the subtree. - * @param includeUncommitted - If true, the uncommitted changes are included in the search. * @returns The data for the leaves to be updated when inserting the new ones. */ public batchInsert( @@ -127,6 +151,6 @@ export class MerkleTreeOperationsFacade implements MerkleTreeOperations { treeHeight: number, subtreeHeight: number, ): Promise<[LowLeafWitnessData[], SiblingPath] | [undefined, SiblingPath]> { - return this.trees.batchInsert(treeId, leaves, treeHeight, subtreeHeight, this.includeUncommitted); + return this.trees.batchInsert(treeId, leaves, treeHeight, subtreeHeight); } } diff --git a/yarn-project/world-state/src/synchroniser/server_world_state_synchroniser.test.ts b/yarn-project/world-state/src/synchroniser/server_world_state_synchroniser.test.ts index 2d5658411b0..91c8daf5b9d 100644 --- a/yarn-project/world-state/src/synchroniser/server_world_state_synchroniser.test.ts +++ b/yarn-project/world-state/src/synchroniser/server_world_state_synchroniser.test.ts @@ -1,7 +1,5 @@ import { BarretenbergWasm } from '@aztec/barretenberg.js/wasm'; import { AppendOnlyTreeSnapshot, NUMBER_OF_L1_L2_MESSAGES_PER_ROLLUP } from '@aztec/circuits.js'; -import { fr } from '@aztec/circuits.js/factories'; - import { INITIAL_LEAF, Pedersen, SiblingPath } from '@aztec/merkle-tree'; import { ContractData, L2Block, L2BlockSource, MerkleTreeId, PublicDataWrite } from '@aztec/types'; import { jest } from '@jest/globals'; @@ -99,6 +97,7 @@ describe('server_world_state_synchroniser', () => { updateHistoricRootsTrees: jest.fn().mockImplementation(() => Promise.resolve()), commit: jest.fn().mockImplementation(() => Promise.resolve()), rollback: jest.fn().mockImplementation(() => Promise.resolve()), + handleL2Block: jest.fn().mockImplementation(() => Promise.resolve()), } as any; it('can be constructed', () => { @@ -244,9 +243,8 @@ describe('server_world_state_synchroniser', () => { await expect(server.start()).rejects.toThrow(); }); - it('updates the contract tree', async () => { - merkleTreeDb.appendLeaves.mockReset(); - merkleTreeDb.updateHistoricRootsTrees.mockReset(); + it('adds the received L2 blocks', async () => { + merkleTreeDb.handleL2Block.mockReset(); const server = createSynchroniser(merkleTreeDb, rollupSource); const totalBlocks = LATEST_BLOCK_NUMBER + 1; nextBlocks = Array(totalBlocks) @@ -254,34 +252,8 @@ describe('server_world_state_synchroniser', () => { .map((_, index) => getMockBlock(index, [Buffer.alloc(32, index)])); // sync the server await server.start(); - // there are 4 data trees updated - expect(merkleTreeDb.appendLeaves).toHaveBeenCalledTimes(totalBlocks * 4); - // and 2 root trees - expect(merkleTreeDb.updateHistoricRootsTrees).toHaveBeenCalledTimes(totalBlocks); - // there should be a call to append to the contract tree for each block - for (let i = 0; i < totalBlocks; i++) { - expect( - merkleTreeDb.appendLeaves.mock.calls.findIndex(call => { - if (call[0] !== MerkleTreeId.CONTRACT_TREE) { - return false; - } - const leaves = call[1] as Buffer[]; - return leaves.length === 1 && leaves[0].equals(Buffer.alloc(32, i)); - }), - ).not.toBe(-1); - } - await server.stop(); - }); - - it('updates the public data tree', async () => { - merkleTreeDb.appendLeaves.mockReset(); - const server = createSynchroniser(merkleTreeDb, rollupSource); - const block = getMockBlock(LATEST_BLOCK_NUMBER + 1); - block.newPublicDataWrites[0] = new PublicDataWrite(fr(1), fr(2)); - nextBlocks = [block]; - await server.start(); - expect(merkleTreeDb.updateLeaf).toHaveBeenCalledWith(MerkleTreeId.PUBLIC_DATA_TREE, fr(2).toBuffer(), fr(1).value); + expect(merkleTreeDb.handleL2Block).toHaveBeenCalledTimes(totalBlocks); await server.stop(); }); }); diff --git a/yarn-project/world-state/src/synchroniser/server_world_state_synchroniser.ts b/yarn-project/world-state/src/synchroniser/server_world_state_synchroniser.ts index 5b5634923a8..a062483b02e 100644 --- a/yarn-project/world-state/src/synchroniser/server_world_state_synchroniser.ts +++ b/yarn-project/world-state/src/synchroniser/server_world_state_synchroniser.ts @@ -1,10 +1,9 @@ -import { L2Block, L2BlockDownloader, L2BlockSource, MerkleTreeId } from '@aztec/types'; +import { L2Block, L2BlockDownloader, L2BlockSource } from '@aztec/types'; import { MerkleTreeDb, MerkleTreeOperations } from '../index.js'; import { MerkleTreeOperationsFacade } from '../merkle-tree/merkle_tree_operations_facade.js'; import { WorldStateRunningState, WorldStateStatus, WorldStateSynchroniser } from './world_state_synchroniser.js'; import { getConfigEnvVars } from './config.js'; import { createDebugLogger } from '@aztec/foundation/log'; -import { Fr } from '@aztec/foundation/fields'; /** * Synchronises the world state with the L2 blocks from a L2BlockSource. @@ -128,55 +127,7 @@ export class ServerWorldStateSynchroniser implements WorldStateSynchroniser { * @param l2Block - The L2 block to handle. */ private async handleL2Block(l2Block: L2Block) { - const compareRoot = async (root: Fr, treeId: MerkleTreeId) => { - const treeInfo = await this.merkleTreeDb.getTreeInfo(treeId, true); - return treeInfo.root.equals(root.toBuffer()); - }; - const rootChecks = await Promise.all([ - compareRoot(l2Block.endContractTreeSnapshot.root, MerkleTreeId.CONTRACT_TREE), - compareRoot(l2Block.endNullifierTreeSnapshot.root, MerkleTreeId.NULLIFIER_TREE), - compareRoot(l2Block.endPrivateDataTreeSnapshot.root, MerkleTreeId.PRIVATE_DATA_TREE), - compareRoot(l2Block.endPublicDataTreeRoot, MerkleTreeId.PUBLIC_DATA_TREE), - compareRoot(l2Block.endTreeOfHistoricContractTreeRootsSnapshot.root, MerkleTreeId.CONTRACT_TREE_ROOTS_TREE), - compareRoot( - l2Block.endTreeOfHistoricPrivateDataTreeRootsSnapshot.root, - MerkleTreeId.PRIVATE_DATA_TREE_ROOTS_TREE, - ), - compareRoot(l2Block.endL1ToL2MessageTreeSnapshot.root, MerkleTreeId.L1_TO_L2_MESSAGES_TREE), - compareRoot( - l2Block.endTreeOfHistoricL1ToL2MessageTreeRootsSnapshot.root, - MerkleTreeId.L1_TO_L2_MESSAGES_ROOTS_TREE, - ), - ]); - const ourBlock = rootChecks.every(x => x); - if (ourBlock) { - this.log(`Block ${l2Block.number} is ours, committing world state..`); - await this.merkleTreeDb.commit(); - } else { - this.log(`Block ${l2Block.number} is not ours, rolling back world state and committing state from chain..`); - await this.merkleTreeDb.rollback(); - - for (const [tree, leaves] of [ - [MerkleTreeId.CONTRACT_TREE, l2Block.newContracts], - [MerkleTreeId.NULLIFIER_TREE, l2Block.newNullifiers], - [MerkleTreeId.PRIVATE_DATA_TREE, l2Block.newCommitments], - [MerkleTreeId.L1_TO_L2_MESSAGES_TREE, l2Block.newL1ToL2Messages], - ] as const) { - await this.merkleTreeDb.appendLeaves( - tree, - leaves.map(fr => fr.toBuffer()), - ); - } - - for (const dataWrite of l2Block.newPublicDataWrites) { - if (dataWrite.isEmpty()) continue; - const { newValue, leafIndex } = dataWrite; - await this.merkleTreeDb.updateLeaf(MerkleTreeId.PUBLIC_DATA_TREE, newValue.toBuffer(), leafIndex.value); - } - - await this.merkleTreeDb.updateHistoricRootsTrees(true); - await this.merkleTreeDb.commit(); - } + await this.merkleTreeDb.handleL2Block(l2Block); this.currentL2BlockNum = l2Block.number; if ( this.currentState === WorldStateRunningState.SYNCHING && diff --git a/yarn-project/world-state/src/world-state-db/index.ts b/yarn-project/world-state/src/world-state-db/index.ts index 72a27a74def..bbfacf8a11a 100644 --- a/yarn-project/world-state/src/world-state-db/index.ts +++ b/yarn-project/world-state/src/world-state-db/index.ts @@ -1,6 +1,6 @@ -import { createDebugLogger } from '@aztec/foundation/log'; import { LeafData, SiblingPath, LowLeafWitnessData } from '@aztec/merkle-tree'; -import { MerkleTreeId } from '@aztec/types'; +import { L2Block, MerkleTreeId } from '@aztec/types'; +import { createDebugLogger } from '@aztec/foundation/log'; export * from './merkle_trees.js'; export { LeafData } from '@aztec/merkle-tree'; @@ -55,12 +55,12 @@ type WithIncludeUncommitted = F extends (...args: [...infer Rest]) => infer R /** * Defines the names of the setters on Merkle Trees. */ -type MerkleTreeSetters = 'appendLeaves' | 'updateLeaf'; +type MerkleTreeSetters = 'appendLeaves' | 'updateLeaf' | 'commit' | 'rollback' | 'handleL2Block' | 'batchInsert'; /** * Defines the interface for operations on a set of Merkle Trees configuring whether to return committed or uncommitted data. */ -export type MerkleTreeDbOperations = { +export type MerkleTreeDb = { [Property in keyof MerkleTreeOperations as Exclude]: WithIncludeUncommitted< MerkleTreeOperations[Property] >; @@ -158,12 +158,13 @@ export interface MerkleTreeOperations { treeHeight: number, subtreeHeight: number, ): Promise<[LowLeafWitnessData[], SiblingPath] | [undefined, SiblingPath]>; -} -/** - * Defines the interface for a database that stores Merkle trees. - */ -export interface MerkleTreeDb extends MerkleTreeDbOperations { + /** + * Handles a single L2 block (i.e. Inserts the new commitments into the merkle tree). + * @param block - The L2 block to handle. + */ + handleL2Block(block: L2Block): Promise; + /** * Commits pending changes to the underlying store. */ diff --git a/yarn-project/world-state/src/world-state-db/merkle_trees.ts b/yarn-project/world-state/src/world-state-db/merkle_trees.ts index 69122be8d29..8e97863dd09 100644 --- a/yarn-project/world-state/src/world-state-db/merkle_trees.ts +++ b/yarn-project/world-state/src/world-state-db/merkle_trees.ts @@ -2,6 +2,7 @@ import { PrimitivesWasm } from '@aztec/barretenberg.js/wasm'; import { CONTRACT_TREE_HEIGHT, CONTRACT_TREE_ROOTS_TREE_HEIGHT, + Fr, L1_TO_L2_MESSAGES_ROOTS_TREE_HEIGHT, L1_TO_L2_MESSAGES_TREE_HEIGHT, NULLIFIER_TREE_HEIGHT, @@ -9,7 +10,6 @@ import { PRIVATE_DATA_TREE_ROOTS_TREE_HEIGHT, PUBLIC_DATA_TREE_HEIGHT, } from '@aztec/circuits.js'; - import { WasmWrapper } from '@aztec/foundation/wasm'; import { AppendOnlyTree, @@ -25,7 +25,6 @@ import { newTree, } from '@aztec/merkle-tree'; import { default as levelup } from 'levelup'; -import { MerkleTreeOperationsFacade } from '../merkle-tree/merkle_tree_operations_facade.js'; import { INITIAL_NULLIFIER_TREE_SIZE, IndexedTreeId, @@ -34,8 +33,10 @@ import { PublicTreeId, TreeInfo, } from './index.js'; -import { MerkleTreeId } from '@aztec/types'; +import { MerkleTreeOperationsFacade } from '../merkle-tree/merkle_tree_operations_facade.js'; +import { L2Block, MerkleTreeId } from '@aztec/types'; import { SerialQueue } from '@aztec/foundation/fifo'; +import { createDebugLogger } from '@aztec/foundation/log'; /** * A convenience class for managing multiple merkle trees. @@ -44,7 +45,7 @@ export class MerkleTrees implements MerkleTreeDb { private trees: (AppendOnlyTree | UpdateOnlyTree)[] = []; private jobQueue = new SerialQueue(); - constructor(private db: levelup.LevelUp) {} + constructor(private db: levelup.LevelUp, private log = createDebugLogger('aztec:merkle_trees')) {} /** * Initialises the collection of Merkle Trees. @@ -321,11 +322,15 @@ export class MerkleTrees implements MerkleTreeDb { * @returns Empty promise. */ public async updateLeaf(treeId: IndexedTreeId | PublicTreeId, leaf: LeafData | Buffer, index: bigint): Promise { - const tree = this.trees[treeId]; - if (!('updateLeaf' in tree)) { - throw new Error('Tree does not support `updateLeaf` method'); - } - return await this.synchronise(() => tree.updateLeaf(leaf, index)); + return await this.synchronise(() => this._updateLeaf(treeId, leaf, index)); + } + + /** + * Handles a single L2 block (i.e. Inserts the new commitments into the merkle tree). + * @param block - The L2 block to handle. + */ + public async handleL2Block(block: L2Block): Promise { + await this.synchronise(() => this._handleL2Block(block)); } /** @@ -334,7 +339,6 @@ export class MerkleTrees implements MerkleTreeDb { * @param leaves - Leaves to insert into the tree. * @param treeHeight - Height of the tree. * @param subtreeHeight - Height of the subtree. - * @param includeUncommitted - If true, the uncommitted changes are included in the search. * @returns The data for the leaves to be updated when inserting the new ones. */ public async batchInsert< @@ -346,7 +350,6 @@ export class MerkleTrees implements MerkleTreeDb { leaves: Buffer[], treeHeight: TreeHeight, subtreeHeight: SubtreeHeight, - includeUncommitted: boolean, ): Promise< | [LowLeafWitnessData[], SiblingPath] | [undefined, SiblingPath] @@ -355,7 +358,7 @@ export class MerkleTrees implements MerkleTreeDb { if (!('batchInsert' in tree)) { throw new Error('Tree does not support `batchInsert` method'); } - return await this.synchronise(() => tree.batchInsert(leaves, treeHeight, subtreeHeight, includeUncommitted)); + return await this.synchronise(() => tree.batchInsert(leaves, treeHeight, subtreeHeight)); } /** @@ -421,6 +424,18 @@ export class MerkleTrees implements MerkleTreeDb { return await tree.appendLeaves(leaves); } + private async _updateLeaf( + treeId: IndexedTreeId | PublicTreeId, + leaf: LeafData | Buffer, + index: bigint, + ): Promise { + const tree = this.trees[treeId]; + if (!('updateLeaf' in tree)) { + throw new Error('Tree does not support `updateLeaf` method'); + } + return await tree.updateLeaf(leaf, index); + } + /** * Commits all pending updates. * @returns Empty promise. @@ -440,4 +455,67 @@ export class MerkleTrees implements MerkleTreeDb { await tree.rollback(); } } + + /** + * Handles a single L2 block (i.e. Inserts the new commitments into the merkle tree). + * @param l2Block - The L2 block to handle. + */ + private async _handleL2Block(l2Block: L2Block) { + const compareRoot = (root: Fr, treeId: MerkleTreeId) => { + const treeRoot = this.trees[treeId].getRoot(true); + return treeRoot.equals(root.toBuffer()); + }; + const rootChecks = [ + compareRoot(l2Block.endContractTreeSnapshot.root, MerkleTreeId.CONTRACT_TREE), + compareRoot(l2Block.endNullifierTreeSnapshot.root, MerkleTreeId.NULLIFIER_TREE), + compareRoot(l2Block.endPrivateDataTreeSnapshot.root, MerkleTreeId.PRIVATE_DATA_TREE), + compareRoot(l2Block.endPublicDataTreeRoot, MerkleTreeId.PUBLIC_DATA_TREE), + compareRoot(l2Block.endTreeOfHistoricContractTreeRootsSnapshot.root, MerkleTreeId.CONTRACT_TREE_ROOTS_TREE), + compareRoot( + l2Block.endTreeOfHistoricPrivateDataTreeRootsSnapshot.root, + MerkleTreeId.PRIVATE_DATA_TREE_ROOTS_TREE, + ), + compareRoot(l2Block.endL1ToL2MessageTreeSnapshot.root, MerkleTreeId.L1_TO_L2_MESSAGES_TREE), + compareRoot( + l2Block.endTreeOfHistoricL1ToL2MessageTreeRootsSnapshot.root, + MerkleTreeId.L1_TO_L2_MESSAGES_ROOTS_TREE, + ), + ]; + const ourBlock = rootChecks.every(x => x); + if (ourBlock) { + this.log(`Block ${l2Block.number} is ours, committing world state..`); + await this._commit(); + } else { + this.log(`Block ${l2Block.number} is not ours, rolling back world state and committing state from chain..`); + await this._rollback(); + + for (const [tree, leaves] of [ + [MerkleTreeId.CONTRACT_TREE, l2Block.newContracts], + [MerkleTreeId.NULLIFIER_TREE, l2Block.newNullifiers], + [MerkleTreeId.PRIVATE_DATA_TREE, l2Block.newCommitments], + [MerkleTreeId.L1_TO_L2_MESSAGES_TREE, l2Block.newL1ToL2Messages], + ] as const) { + await this._appendLeaves( + tree, + leaves.map(fr => fr.toBuffer()), + ); + } + + for (const dataWrite of l2Block.newPublicDataWrites) { + if (dataWrite.isEmpty()) continue; + const { newValue, leafIndex } = dataWrite; + await this._updateLeaf(MerkleTreeId.PUBLIC_DATA_TREE, newValue.toBuffer(), leafIndex.value); + } + + for (const [newTree, rootTree] of [ + [MerkleTreeId.PRIVATE_DATA_TREE, MerkleTreeId.PRIVATE_DATA_TREE_ROOTS_TREE], + [MerkleTreeId.CONTRACT_TREE, MerkleTreeId.CONTRACT_TREE_ROOTS_TREE], + [MerkleTreeId.L1_TO_L2_MESSAGES_TREE, MerkleTreeId.L1_TO_L2_MESSAGES_ROOTS_TREE], + ] as const) { + const newTreeRoot = this.trees[newTree].getRoot(true); + await this._appendLeaves(rootTree, [newTreeRoot]); + } + await this._commit(); + } + } } diff --git a/yarn-project/yarn-project-base/Dockerfile b/yarn-project/yarn-project-base/Dockerfile index 7f110616287..41525a9f91c 100644 --- a/yarn-project/yarn-project-base/Dockerfile +++ b/yarn-project/yarn-project-base/Dockerfile @@ -25,6 +25,7 @@ COPY aztec-node/package.json aztec-node/package.json COPY aztec.js/package.json aztec.js/package.json COPY docs/package.json docs/package.json COPY end-to-end/package.json end-to-end/package.json +COPY ethereum/package.json ethereum/package.json COPY foundation/package.json foundation/package.json COPY key-store/package.json key-store/package.json COPY merkle-tree/package.json merkle-tree/package.json @@ -35,7 +36,9 @@ COPY l1-artifacts/package.json l1-artifacts/package.json COPY barretenberg.js/package.json barretenberg.js/package.json COPY circuits.js/package.json circuits.js/package.json COPY p2p/package.json p2p/package.json +COPY p2p-bootstrap/package.json p2p-bootstrap/package.json COPY prover-client/package.json prover-client/package.json +COPY rollup-provider/package.json rollup-provider/package.json COPY sequencer-client/package.json sequencer-client/package.json COPY types/package.json types/package.json COPY world-state/package.json world-state/package.json @@ -80,6 +83,7 @@ COPY aztec-rpc/tsconfig.json aztec-rpc/tsconfig.json COPY aztec-node/tsconfig.json aztec-node/tsconfig.json COPY aztec.js/tsconfig.json aztec.js/tsconfig.json COPY end-to-end/tsconfig.json end-to-end/tsconfig.json +COPY ethereum/tsconfig.json ethereum/tsconfig.json COPY foundation/tsconfig.json foundation/tsconfig.json COPY key-store/tsconfig.json key-store/tsconfig.json COPY merkle-tree/tsconfig.json merkle-tree/tsconfig.json @@ -88,8 +92,10 @@ COPY l1-artifacts/tsconfig.json l1-artifacts/tsconfig.json COPY barretenberg.js/tsconfig.json barretenberg.js/tsconfig.json COPY circuits.js/tsconfig.json circuits.js/tsconfig.json COPY p2p/tsconfig.json p2p/tsconfig.json +COPY p2p-bootstrap/tsconfig.json p2p-bootstrap/tsconfig.json COPY prover-client/tsconfig.json prover-client/tsconfig.json COPY sequencer-client/tsconfig.json sequencer-client/tsconfig.json +COPY rollup-provider/tsconfig.json rollup-provider/tsconfig.json COPY types/tsconfig.json types/tsconfig.json COPY world-state/tsconfig.json world-state/tsconfig.json diff --git a/yarn-project/yarn.lock b/yarn-project/yarn.lock index 34bec96df7a..2cbdc080205 100644 --- a/yarn-project/yarn.lock +++ b/yarn-project/yarn.lock @@ -5,6 +5,44 @@ __metadata: version: 6 cacheKey: 8 +"@achingbrain/ip-address@npm:^8.1.0": + version: 8.1.0 + resolution: "@achingbrain/ip-address@npm:8.1.0" + dependencies: + jsbn: 1.1.0 + sprintf-js: 1.1.2 + checksum: 2b845980a138faf9a5c1a58df2fcdba9e4bf0bc7a5b855bccaac9a61f6886d5707dd8e36ff59a4cfa2c83e29ee1518aae4579595e7534c7ebe01b91e07d86427 + languageName: node + linkType: hard + +"@achingbrain/nat-port-mapper@npm:^1.0.3": + version: 1.0.8 + resolution: "@achingbrain/nat-port-mapper@npm:1.0.8" + dependencies: + "@achingbrain/ssdp": ^4.0.1 + "@libp2p/logger": ^2.0.0 + default-gateway: ^6.0.2 + err-code: ^3.0.1 + it-first: ^3.0.1 + p-defer: ^4.0.0 + p-timeout: ^5.0.2 + xml2js: ^0.5.0 + checksum: f98b965865d0ac6ddff8ca198acf79f2747f327b841d38c5db00e8656b7f16e73391818109d0b0bd46ba0aefaba76cbf5e5ade9dff9238e6f8522335fdefd7d4 + languageName: node + linkType: hard + +"@achingbrain/ssdp@npm:^4.0.1": + version: 4.0.4 + resolution: "@achingbrain/ssdp@npm:4.0.4" + dependencies: + event-iterator: ^2.0.0 + freeport-promise: ^2.0.0 + merge-options: ^3.0.4 + xml2js: ^0.5.0 + checksum: ebd5b25f64a2cd8fd14e760f92824cc308784d833eba887bfabc68a678de692eab83ac6c6859e56a187b7ea78e4fbdc9403899ed2be9cb245e0591a925431565 + languageName: node + linkType: hard + "@adraffy/ens-normalize@npm:1.9.0": version: 1.9.0 resolution: "@adraffy/ens-normalize@npm:1.9.0" @@ -54,6 +92,7 @@ __metadata: version: 0.0.0-use.local resolution: "@aztec/archiver@workspace:archiver" dependencies: + "@aztec/ethereum": "workspace:^" "@aztec/foundation": "workspace:^" "@aztec/l1-artifacts": "workspace:^" "@aztec/types": "workspace:^" @@ -72,7 +111,7 @@ __metadata: tsc-watch: ^6.0.0 tslib: ^2.5.0 typescript: ^5.0.4 - viem: ^0.3.13 + viem: ^0.3.14 ws: ^8.13.0 languageName: unknown linkType: soft @@ -81,7 +120,12 @@ __metadata: version: 0.0.0-use.local resolution: "@aztec/aztec-cli@workspace:aztec-cli" dependencies: + "@aztec/aztec-node": "workspace:^" + "@aztec/aztec.js": "workspace:^" + "@aztec/ethereum": "workspace:^" "@aztec/foundation": "workspace:^" + "@aztec/l1-artifacts": "workspace:^" + "@aztec/noir-contracts": "workspace:^" "@jest/globals": ^29.5.0 "@rushstack/eslint-patch": ^1.1.4 "@types/jest": ^29.5.0 @@ -92,6 +136,7 @@ __metadata: ts-node: ^10.9.1 tslib: ^2.4.0 typescript: ^5.0.4 + viem: ^0.3.14 bin: aztec_cli: index.js languageName: unknown @@ -257,9 +302,11 @@ __metadata: "@aztec/aztec-node": "workspace:^" "@aztec/aztec.js": "workspace:^" "@aztec/circuits.js": "workspace:^" + "@aztec/ethereum": "workspace:^" "@aztec/foundation": "workspace:^" "@aztec/l1-artifacts": "workspace:^" "@aztec/noir-contracts": "workspace:^" + "@aztec/p2p": "workspace:^" "@aztec/sequencer-client": "workspace:^" "@aztec/world-state": "workspace:^" "@jest/globals": ^29.5.0 @@ -277,7 +324,27 @@ __metadata: ts-node: ^10.9.1 tslib: ^2.4.0 typescript: ^5.0.4 - viem: ^0.3.13 + viem: ^0.3.14 + languageName: unknown + linkType: soft + +"@aztec/ethereum@workspace:^, @aztec/ethereum@workspace:ethereum": + version: 0.0.0-use.local + resolution: "@aztec/ethereum@workspace:ethereum" + dependencies: + "@aztec/foundation": "workspace:^" + "@aztec/l1-artifacts": "workspace:^" + "@jest/globals": ^29.5.0 + "@rushstack/eslint-patch": ^1.1.4 + "@types/jest": ^29.5.0 + "@types/node": ^18.14.6 + dotenv: ^16.0.3 + jest: ^29.5.0 + ts-jest: ^29.1.0 + ts-node: ^10.9.1 + tslib: ^2.4.0 + typescript: ^5.0.4 + viem: ^0.3.14 languageName: unknown linkType: soft @@ -431,6 +498,24 @@ __metadata: languageName: unknown linkType: soft +"@aztec/p2p-bootstrap@workspace:p2p-bootstrap": + version: 0.0.0-use.local + resolution: "@aztec/p2p-bootstrap@workspace:p2p-bootstrap" + dependencies: + "@aztec/p2p": "workspace:^" + "@jest/globals": ^29.5.0 + "@rushstack/eslint-patch": ^1.1.4 + "@types/jest": ^29.5.0 + "@types/node": ^18.14.6 + dotenv: ^16.0.3 + jest: ^29.5.0 + ts-jest: ^29.1.0 + ts-node: ^10.9.1 + tslib: ^2.4.0 + typescript: ^5.0.4 + languageName: unknown + linkType: soft + "@aztec/p2p@workspace:^, @aztec/p2p@workspace:p2p": version: 0.0.0-use.local resolution: "@aztec/p2p@workspace:p2p" @@ -438,13 +523,24 @@ __metadata: "@aztec/circuits.js": "workspace:^" "@aztec/foundation": "workspace:^" "@aztec/types": "workspace:^" + "@chainsafe/libp2p-noise": ^12.0.0 + "@chainsafe/libp2p-yamux": ^4.0.2 "@jest/globals": ^29.5.0 + "@libp2p/bootstrap": ^8.0.0 + "@libp2p/interfaces": ^3.3.2 + "@libp2p/kad-dht": ^9.3.3 + "@libp2p/mplex": ^8.0.3 + "@libp2p/peer-id": ^2.0.3 + "@libp2p/tcp": ^7.0.1 "@rushstack/eslint-patch": ^1.1.4 "@types/jest": ^29.5.0 "@types/lodash.times": ^4.3.7 "@types/node": ^18.14.6 jest: ^29.5.0 + jest-mock-extended: ^3.0.4 + libp2p: ^0.45.1 lodash.times: ^4.3.2 + multiaddr: ^10.0.1 sha3: ^2.1.4 ts-jest: ^29.1.0 ts-node: ^10.9.1 @@ -470,12 +566,35 @@ __metadata: languageName: unknown linkType: soft +"@aztec/rollup-provider@workspace:rollup-provider": + version: 0.0.0-use.local + resolution: "@aztec/rollup-provider@workspace:rollup-provider" + dependencies: + "@aztec/aztec-node": "workspace:^" + "@jest/globals": ^29.5.0 + "@rushstack/eslint-patch": ^1.1.4 + "@types/jest": ^29.5.0 + "@types/koa": ^2.13.6 + "@types/node": ^18.7.23 + dotenv: ^16.0.3 + jest: ^29.5.0 + koa: ^2.14.2 + koa-router: ^12.0.0 + promise-readable: ^6.0.0 + ts-jest: ^29.1.0 + ts-node: ^10.9.1 + tslib: ^2.4.0 + typescript: ^5.0.4 + languageName: unknown + linkType: soft + "@aztec/sequencer-client@workspace:^, @aztec/sequencer-client@workspace:sequencer-client": version: 0.0.0-use.local resolution: "@aztec/sequencer-client@workspace:sequencer-client" dependencies: "@aztec/acir-simulator": "workspace:^" "@aztec/circuits.js": "workspace:^" + "@aztec/ethereum": "workspace:^" "@aztec/foundation": "workspace:^" "@aztec/l1-artifacts": "workspace:^" "@aztec/merkle-tree": "workspace:^" @@ -503,7 +622,7 @@ __metadata: ts-node: ^10.9.1 tslib: ^2.4.0 typescript: ^5.0.4 - viem: ^0.3.13 + viem: ^0.3.14 languageName: unknown linkType: soft @@ -955,6 +1074,65 @@ __metadata: languageName: node linkType: hard +"@chainsafe/is-ip@npm:^2.0.1": + version: 2.0.1 + resolution: "@chainsafe/is-ip@npm:2.0.1" + checksum: c5bbebe58eadc700d12112713fd75e41ae26020eedcac0e8b593fdda16a180239ede1d68d55b500fd8b30189c9a82b5e10769ed86a816e879c83248069152b79 + languageName: node + linkType: hard + +"@chainsafe/libp2p-noise@npm:^12.0.0": + version: 12.0.0 + resolution: "@chainsafe/libp2p-noise@npm:12.0.0" + dependencies: + "@libp2p/crypto": ^1.0.11 + "@libp2p/interface-connection-encrypter": ^4.0.0 + "@libp2p/interface-keys": ^1.0.6 + "@libp2p/interface-metrics": ^4.0.4 + "@libp2p/interface-peer-id": ^2.0.0 + "@libp2p/logger": ^2.0.5 + "@libp2p/peer-id": ^2.0.0 + "@noble/hashes": ^1.3.0 + "@stablelib/chacha20poly1305": ^1.0.1 + "@stablelib/x25519": ^1.0.3 + it-length-prefixed: ^9.0.1 + it-pair: ^2.0.2 + it-pb-stream: ^4.0.1 + it-pipe: ^3.0.1 + it-stream-types: ^2.0.1 + protons-runtime: ^5.0.0 + uint8arraylist: ^2.3.2 + uint8arrays: ^4.0.2 + checksum: 5983ff58428282e19d1b8faee64cced5a96b4c63671bbf2aa814eea0857c456820da304e723b97f012a2b84d8430fac1be57bb8db3956a8a07ceff8c0602a9e1 + languageName: node + linkType: hard + +"@chainsafe/libp2p-yamux@npm:^4.0.2": + version: 4.0.2 + resolution: "@chainsafe/libp2p-yamux@npm:4.0.2" + dependencies: + "@libp2p/interface-connection": ^5.1.0 + "@libp2p/interface-stream-muxer": ^4.1.2 + "@libp2p/interfaces": ^3.3.2 + "@libp2p/logger": ^2.0.7 + abortable-iterator: ^5.0.1 + any-signal: ^4.1.1 + it-pipe: ^3.0.1 + it-pushable: ^3.1.3 + uint8arraylist: ^2.4.3 + checksum: e117f03bf4484fd56e138f3d05139795256e3554ef1deb6ab7586ff3c78dd2bde45181ba4e474c00b721dc7d0b0d7e2f5fa4bb8876998cdc8c70d7ca0a2a33a0 + languageName: node + linkType: hard + +"@chainsafe/netmask@npm:^2.0.0": + version: 2.0.0 + resolution: "@chainsafe/netmask@npm:2.0.0" + dependencies: + "@chainsafe/is-ip": ^2.0.1 + checksum: 90d27154c11ff878130150766ebfc490c829cd5249a61f7018fa4cefe3a18d92394285bb435c38bd0dbe45261006a82572e95ac201e9b28157de80127a6a3d06 + languageName: node + linkType: hard + "@cspotcode/source-map-support@npm:^0.8.0": version: 0.8.1 resolution: "@cspotcode/source-map-support@npm:0.8.1" @@ -1528,6 +1706,596 @@ __metadata: languageName: node linkType: hard +"@libp2p/bootstrap@npm:^8.0.0": + version: 8.0.0 + resolution: "@libp2p/bootstrap@npm:8.0.0" + dependencies: + "@libp2p/interface-peer-discovery": ^2.0.0 + "@libp2p/interface-peer-info": ^1.0.7 + "@libp2p/interface-peer-store": ^2.0.0 + "@libp2p/interfaces": ^3.0.3 + "@libp2p/logger": ^2.0.1 + "@libp2p/peer-id": ^2.0.0 + "@multiformats/mafmt": ^12.0.0 + "@multiformats/multiaddr": ^12.0.0 + checksum: be0e31537f8667909ff772c5f88044a4a9aba61d7717cdb103fbeaa7104e4c22129b7f5fc29e7f6d21eda4da1e245a4084dca0af1154a2f7bc0280b7b6992262 + languageName: node + linkType: hard + +"@libp2p/crypto@npm:^1.0.0, @libp2p/crypto@npm:^1.0.11, @libp2p/crypto@npm:^1.0.15, @libp2p/crypto@npm:^1.0.4": + version: 1.0.17 + resolution: "@libp2p/crypto@npm:1.0.17" + dependencies: + "@libp2p/interface-keys": ^1.0.2 + "@libp2p/interfaces": ^3.2.0 + "@noble/ed25519": ^1.6.0 + "@noble/secp256k1": ^1.5.4 + multiformats: ^11.0.0 + node-forge: ^1.1.0 + protons-runtime: ^5.0.0 + uint8arraylist: ^2.4.3 + uint8arrays: ^4.0.2 + checksum: 178474409ffe56ba6fb6b0f691e0b5de7fafb61c18a1a1197d75d0f9471e614c67d77fce84e337238d0835ba4b7bbc7f4b72ff9447968706c2ba190ed93cf650 + languageName: node + linkType: hard + +"@libp2p/interface-address-manager@npm:^3.0.0": + version: 3.0.1 + resolution: "@libp2p/interface-address-manager@npm:3.0.1" + dependencies: + "@multiformats/multiaddr": ^12.0.0 + checksum: 04f884772672b13c181ec80af416427fa3ac39e6e7603c83cede97454e2fe095deedd1753c68b04e148d5b59cc8b40fbbe2ee4fe6364f475f3dc845f4fd17c63 + languageName: node + linkType: hard + +"@libp2p/interface-connection-encrypter@npm:^4.0.0": + version: 4.0.1 + resolution: "@libp2p/interface-connection-encrypter@npm:4.0.1" + dependencies: + "@libp2p/interface-peer-id": ^2.0.0 + it-stream-types: ^2.0.1 + checksum: 4c355dda12887d90f3eb2f59c3249ac3598492e0f75e6896c73ebe899518751844c9dd61479d088945d6d55ae6fb169e566f4260f7d9c24befe8ec2485087dc7 + languageName: node + linkType: hard + +"@libp2p/interface-connection-gater@npm:^3.0.0": + version: 3.0.1 + resolution: "@libp2p/interface-connection-gater@npm:3.0.1" + dependencies: + "@libp2p/interface-connection": ^5.0.0 + "@libp2p/interface-peer-id": ^2.0.0 + "@multiformats/multiaddr": ^12.0.0 + checksum: 0dfbf7edd3115c3a3351bb767a1ad7c10f20cfd38d17f5fe8ce1b4e1474fa99ebfef00c0dc74af8025c33e8571012dc742219d31fd65d0b7cb42d16e7757b9c4 + languageName: node + linkType: hard + +"@libp2p/interface-connection-manager@npm:^3.0.0": + version: 3.0.1 + resolution: "@libp2p/interface-connection-manager@npm:3.0.1" + dependencies: + "@libp2p/interface-connection": ^5.0.0 + "@libp2p/interface-peer-id": ^2.0.0 + "@libp2p/interfaces": ^3.0.0 + "@libp2p/peer-collections": ^3.0.1 + "@multiformats/multiaddr": ^12.0.0 + checksum: 56991bb4af06ad3b8aa0d6bc64e18bb2970c6dbb4c9312bfc0a322264a23028ecf3f0dbbc57b5d547ce925bbd44f7ead3c0795441214e2aab11c7821cd292c42 + languageName: node + linkType: hard + +"@libp2p/interface-connection@npm:^5.0.0, @libp2p/interface-connection@npm:^5.0.1": + version: 5.0.2 + resolution: "@libp2p/interface-connection@npm:5.0.2" + dependencies: + "@libp2p/interface-peer-id": ^2.0.0 + "@libp2p/interfaces": ^3.0.0 + "@multiformats/multiaddr": ^12.0.0 + it-stream-types: ^2.0.1 + uint8arraylist: ^2.4.3 + checksum: 0f2032ac1849ebc44bf97676fd1838f44b5e4b19319df7c9f8b0d66d9444c7af6724776cdbcd2adbba2d263157f44272c9254d160a0cb33d45ee502be0be1417 + languageName: node + linkType: hard + +"@libp2p/interface-connection@npm:^5.1.0": + version: 5.1.0 + resolution: "@libp2p/interface-connection@npm:5.1.0" + dependencies: + "@libp2p/interface-peer-id": ^2.0.0 + "@libp2p/interfaces": ^3.0.0 + "@multiformats/multiaddr": ^12.0.0 + it-stream-types: ^2.0.1 + uint8arraylist: ^2.4.3 + checksum: 1394df3d65734e289c2ba702d6a5ae5aad0048555032f925297e8352285680d23ae3951b93b53b70793413c4ec9c6120214f743f1850b984cd388d2090ed2a7b + languageName: node + linkType: hard + +"@libp2p/interface-content-routing@npm:^2.0.0, @libp2p/interface-content-routing@npm:^2.1.0": + version: 2.1.1 + resolution: "@libp2p/interface-content-routing@npm:2.1.1" + dependencies: + "@libp2p/interface-peer-info": ^1.0.0 + "@libp2p/interfaces": ^3.0.0 + multiformats: ^11.0.0 + checksum: 6913b26d2e27afe78f0407cb574d80359a11fa887db9e974dd503df81cbad8f881c0604c48960824dcf974b6f344222fbfeae318e204b43ce44d92c27f90a0f1 + languageName: node + linkType: hard + +"@libp2p/interface-dht@npm:^2.0.0": + version: 2.0.3 + resolution: "@libp2p/interface-dht@npm:2.0.3" + dependencies: + "@libp2p/interface-peer-discovery": ^2.0.0 + "@libp2p/interface-peer-id": ^2.0.0 + "@libp2p/interface-peer-info": ^1.0.0 + "@libp2p/interfaces": ^3.0.0 + multiformats: ^11.0.0 + checksum: 257d2179215ae6770edee5ee96b2a346105347f1d3fe4fd0170d8f4b29422ad9fb0be1e6af0e8cbd0fbb878f1d6963acc974afc0d126e375c366c0fc9c9f5f0c + languageName: node + linkType: hard + +"@libp2p/interface-keychain@npm:^2.0.0, @libp2p/interface-keychain@npm:^2.0.3, @libp2p/interface-keychain@npm:^2.0.4": + version: 2.0.5 + resolution: "@libp2p/interface-keychain@npm:2.0.5" + dependencies: + "@libp2p/interface-peer-id": ^2.0.0 + multiformats: ^11.0.0 + checksum: 242888f107aa586dfa6d11f3b579403b0b1ec2e60cb477984dec0d7afe4b69ef302230df7f23e351cb53de92b669733e4723ea832b9ec864314af6cbcd318557 + languageName: node + linkType: hard + +"@libp2p/interface-keys@npm:^1.0.2, @libp2p/interface-keys@npm:^1.0.6": + version: 1.0.8 + resolution: "@libp2p/interface-keys@npm:1.0.8" + checksum: 08c2976b3436b6e4e6159d1c817bb9e41af41d159cb94afaa8c63cf0fbf8842b0e5ca758d6c38453a59d8e4e87cec3117e3c574d316b94fed8db842a77f6efb0 + languageName: node + linkType: hard + +"@libp2p/interface-libp2p@npm:^3.1.0": + version: 3.2.0 + resolution: "@libp2p/interface-libp2p@npm:3.2.0" + dependencies: + "@libp2p/interface-connection": ^5.0.0 + "@libp2p/interface-content-routing": ^2.0.0 + "@libp2p/interface-keychain": ^2.0.0 + "@libp2p/interface-metrics": ^4.0.0 + "@libp2p/interface-peer-id": ^2.0.0 + "@libp2p/interface-peer-info": ^1.0.0 + "@libp2p/interface-peer-routing": ^1.0.0 + "@libp2p/interface-peer-store": ^2.0.0 + "@libp2p/interface-registrar": ^2.0.0 + "@libp2p/interface-transport": ^4.0.0 + "@libp2p/interfaces": ^3.0.0 + "@multiformats/multiaddr": ^12.0.0 + checksum: 76643668a8f94d9d13708f0c447a017415410fc78892a2d78d6917ccac7f444fbce1bce2f63b8e727ddf3e4bfcbe90100e77801a3d756b1c338e2cbc29b9e862 + languageName: node + linkType: hard + +"@libp2p/interface-metrics@npm:^4.0.0, @libp2p/interface-metrics@npm:^4.0.4": + version: 4.0.8 + resolution: "@libp2p/interface-metrics@npm:4.0.8" + dependencies: + "@libp2p/interface-connection": ^5.0.0 + checksum: 185e0c8476c95a90f5edd066379252d073d10734e02b96c0f264d13f9dcd82e47813d4b57ac8897c0f701571b9af1c834e628ea7f74caba13673180acd8c546f + languageName: node + linkType: hard + +"@libp2p/interface-peer-discovery@npm:^2.0.0": + version: 2.0.0 + resolution: "@libp2p/interface-peer-discovery@npm:2.0.0" + dependencies: + "@libp2p/interface-peer-info": ^1.0.0 + "@libp2p/interfaces": ^3.0.0 + checksum: fa9ce48b0923726b090c62c1fd90444d9d45d32ebd0075b211418a41754b58c438d9109140f6e00ffd95dfb62b0d2794b4147729f72d31e0121e346aa389a75d + languageName: node + linkType: hard + +"@libp2p/interface-peer-id@npm:^2.0.0, @libp2p/interface-peer-id@npm:^2.0.1": + version: 2.0.2 + resolution: "@libp2p/interface-peer-id@npm:2.0.2" + dependencies: + multiformats: ^11.0.0 + checksum: 70db48ee6757cf1c7badbc78b0c2357bb29724bc15f789e85cb00f0fdac80f0655c4474113b436fbe4e52c9cf627465dde7d7e3cd8d6a7ba53143d414f39f497 + languageName: node + linkType: hard + +"@libp2p/interface-peer-info@npm:^1.0.0, @libp2p/interface-peer-info@npm:^1.0.3, @libp2p/interface-peer-info@npm:^1.0.7": + version: 1.0.10 + resolution: "@libp2p/interface-peer-info@npm:1.0.10" + dependencies: + "@libp2p/interface-peer-id": ^2.0.0 + "@multiformats/multiaddr": ^12.0.0 + checksum: 2e13de3d77ef3ae1caf6a3d3ad1ce04c1e0ccad830d8db4a3e564dbbe02f1c8e877fa908081eb7ef4285411d37f999433d75d4f37cf7215677d470a8dbc65128 + languageName: node + linkType: hard + +"@libp2p/interface-peer-routing@npm:^1.0.0, @libp2p/interface-peer-routing@npm:^1.1.0": + version: 1.1.1 + resolution: "@libp2p/interface-peer-routing@npm:1.1.1" + dependencies: + "@libp2p/interface-peer-id": ^2.0.0 + "@libp2p/interface-peer-info": ^1.0.0 + "@libp2p/interfaces": ^3.0.0 + checksum: acea6188d706947edea80d82ceb2723b88f141679ce82c1a7ccf818a9ae53d485095c09b29adf638c72f9dd77dc17816989d2031d6202a51c9a575335a11f60b + languageName: node + linkType: hard + +"@libp2p/interface-peer-store@npm:^2.0.0": + version: 2.0.2 + resolution: "@libp2p/interface-peer-store@npm:2.0.2" + dependencies: + "@libp2p/interface-peer-id": ^2.0.0 + "@multiformats/multiaddr": ^12.0.0 + checksum: 2672f867938d294df4c49fd9d308dd069ad3910cf7cfab9e8847cd52f33b9b801b55872748f37d31408d8054bcf5c5c6edea069f6ef6442572e083176235c469 + languageName: node + linkType: hard + +"@libp2p/interface-peer-store@npm:^2.0.1, @libp2p/interface-peer-store@npm:^2.0.3": + version: 2.0.3 + resolution: "@libp2p/interface-peer-store@npm:2.0.3" + dependencies: + "@libp2p/interface-peer-id": ^2.0.0 + "@multiformats/multiaddr": ^12.0.0 + checksum: d2fbf79342a291db404f59a46e6fd23721262364d1ebac6969e7c34b60b4e9ad9c4ce452549743e78a2fe68440d11942b37061e05e0695afc2180d657d04817b + languageName: node + linkType: hard + +"@libp2p/interface-pubsub@npm:^4.0.0": + version: 4.0.1 + resolution: "@libp2p/interface-pubsub@npm:4.0.1" + dependencies: + "@libp2p/interface-connection": ^5.0.0 + "@libp2p/interface-peer-id": ^2.0.0 + "@libp2p/interfaces": ^3.0.0 + it-pushable: ^3.1.3 + uint8arraylist: ^2.4.3 + checksum: dfba6f9b1fd1a29d8aec51d651a38473b665869754285c564badb9c542861468c19d3b2f199f57bc6556c3e00d34292f84b8b7aa3122f6b598386723a47006cd + languageName: node + linkType: hard + +"@libp2p/interface-record@npm:^2.0.1, @libp2p/interface-record@npm:^2.0.6": + version: 2.0.7 + resolution: "@libp2p/interface-record@npm:2.0.7" + dependencies: + "@libp2p/interface-peer-id": ^2.0.0 + uint8arraylist: ^2.4.3 + checksum: 2bce52fa53eaf3fb2556c381b44e0a44ec73c98875e1eb92a4c556a8752ce344414b16b7a9dbc381752dd85cd5869742b8b9db5c2cc8b32c7e698e0d741baa85 + languageName: node + linkType: hard + +"@libp2p/interface-registrar@npm:^2.0.0, @libp2p/interface-registrar@npm:^2.0.11, @libp2p/interface-registrar@npm:^2.0.3": + version: 2.0.12 + resolution: "@libp2p/interface-registrar@npm:2.0.12" + dependencies: + "@libp2p/interface-connection": ^5.0.0 + "@libp2p/interface-peer-id": ^2.0.0 + checksum: f6e6e053f3c98328acad2e91f14ed787ac5309d9d6737b1fb1c3fc5f77cbbe0651cc6554001545c32315879bb47ae95e4d76946ea9ce1b09e2d468dd99ff1843 + languageName: node + linkType: hard + +"@libp2p/interface-stream-muxer@npm:^4.0.0": + version: 4.0.1 + resolution: "@libp2p/interface-stream-muxer@npm:4.0.1" + dependencies: + "@libp2p/interface-connection": ^5.0.0 + "@libp2p/interfaces": ^3.0.0 + it-stream-types: ^2.0.1 + uint8arraylist: ^2.4.3 + checksum: 6487fbc2d589797812b6bd40be3afa8e0fc35a9cf55dc26db23e27a0d14c2cc5bc3725b96e9f6d85cb361a005fd9c6500f75599216c7ec0740a3726a7b3ae5d6 + languageName: node + linkType: hard + +"@libp2p/interface-stream-muxer@npm:^4.1.2": + version: 4.1.2 + resolution: "@libp2p/interface-stream-muxer@npm:4.1.2" + dependencies: + "@libp2p/interface-connection": ^5.0.0 + "@libp2p/interfaces": ^3.0.0 + "@libp2p/logger": ^2.0.7 + abortable-iterator: ^5.0.1 + any-signal: ^4.1.1 + it-pushable: ^3.1.3 + it-stream-types: ^2.0.1 + uint8arraylist: ^2.4.3 + checksum: 146742f0361597e4d6e00c8658a37840923e901b203389df86e282c06ce97b76446d89dd7576e4299887ad0d14808e50b67ba8044f4b0d9490858f0c8bc5b387 + languageName: node + linkType: hard + +"@libp2p/interface-transport@npm:^4.0.0": + version: 4.0.2 + resolution: "@libp2p/interface-transport@npm:4.0.2" + dependencies: + "@libp2p/interface-connection": ^5.0.0 + "@libp2p/interface-stream-muxer": ^4.0.0 + "@libp2p/interfaces": ^3.0.0 + "@multiformats/multiaddr": ^12.0.0 + it-stream-types: ^2.0.1 + checksum: 7e8c0fda6be786e83129d43f1c51aeaf57facc5b6a11e168bb1839d242c435c92a62c30b51a05d1437202870d8c771c1a877bfd8717cdfa6fdff83705aa28bd1 + languageName: node + linkType: hard + +"@libp2p/interfaces@npm:^3.0.0, @libp2p/interfaces@npm:^3.0.3, @libp2p/interfaces@npm:^3.2.0, @libp2p/interfaces@npm:^3.3.1, @libp2p/interfaces@npm:^3.3.2": + version: 3.3.2 + resolution: "@libp2p/interfaces@npm:3.3.2" + checksum: 3071fa49dcbb81a4b218248a1f648fba1061fb9c51e4b5edab9b8a7b9425c25afec96fdf3351ea7a469e7039269e59d95265682a934aa9c21630226dfcb67313 + languageName: node + linkType: hard + +"@libp2p/kad-dht@npm:^9.3.3": + version: 9.3.3 + resolution: "@libp2p/kad-dht@npm:9.3.3" + dependencies: + "@libp2p/crypto": ^1.0.4 + "@libp2p/interface-address-manager": ^3.0.0 + "@libp2p/interface-connection": ^5.0.1 + "@libp2p/interface-connection-manager": ^3.0.0 + "@libp2p/interface-content-routing": ^2.1.0 + "@libp2p/interface-metrics": ^4.0.0 + "@libp2p/interface-peer-discovery": ^2.0.0 + "@libp2p/interface-peer-id": ^2.0.0 + "@libp2p/interface-peer-info": ^1.0.3 + "@libp2p/interface-peer-routing": ^1.1.0 + "@libp2p/interface-peer-store": ^2.0.0 + "@libp2p/interface-registrar": ^2.0.11 + "@libp2p/interfaces": ^3.2.0 + "@libp2p/logger": ^2.0.1 + "@libp2p/peer-collections": ^3.0.0 + "@libp2p/peer-id": ^2.0.0 + "@libp2p/record": ^3.0.0 + "@libp2p/topology": ^4.0.0 + "@multiformats/multiaddr": ^12.0.0 + "@types/sinon": ^10.0.14 + abortable-iterator: ^5.0.1 + any-signal: ^4.1.1 + datastore-core: ^9.0.1 + hashlru: ^2.3.0 + interface-datastore: ^8.0.0 + it-all: ^3.0.1 + it-drain: ^3.0.1 + it-first: ^3.0.1 + it-length: ^3.0.1 + it-length-prefixed: ^9.0.0 + it-map: ^3.0.1 + it-merge: ^3.0.0 + it-parallel: ^3.0.0 + it-pipe: ^3.0.0 + it-stream-types: ^2.0.1 + it-take: ^3.0.1 + multiformats: ^11.0.0 + p-defer: ^4.0.0 + p-queue: ^7.3.4 + private-ip: ^3.0.0 + progress-events: ^1.0.0 + protons-runtime: ^5.0.0 + uint8arraylist: ^2.0.0 + uint8arrays: ^4.0.2 + varint: ^6.0.0 + checksum: 904140c6c0d7c68c3607df91ade46f298fde52f838f39b177525562c48060af1a612a93c4944830c58e1477691452d63f3469667d7ce1c10f5e0647d2293f0fd + languageName: node + linkType: hard + +"@libp2p/keychain@npm:^2.0.0": + version: 2.0.0 + resolution: "@libp2p/keychain@npm:2.0.0" + dependencies: + "@libp2p/crypto": ^1.0.11 + "@libp2p/interface-keychain": ^2.0.3 + "@libp2p/interface-peer-id": ^2.0.1 + "@libp2p/interfaces": ^3.3.1 + "@libp2p/logger": ^2.0.5 + "@libp2p/peer-id": ^2.0.1 + interface-datastore: ^8.0.0 + merge-options: ^3.0.4 + sanitize-filename: ^1.6.3 + uint8arrays: ^4.0.3 + checksum: 6ac5a0e6e532b24854c90358a3ce6ecf75dcc077eb222bbdfcfa46be10bfaf53267645aab04a07a613745fa0f5bb7c301bc25cc3a4a0aca921b77e3ab80324e8 + languageName: node + linkType: hard + +"@libp2p/logger@npm:^2.0.0, @libp2p/logger@npm:^2.0.1, @libp2p/logger@npm:^2.0.5, @libp2p/logger@npm:^2.0.7": + version: 2.0.7 + resolution: "@libp2p/logger@npm:2.0.7" + dependencies: + "@libp2p/interface-peer-id": ^2.0.0 + debug: ^4.3.3 + interface-datastore: ^8.0.0 + multiformats: ^11.0.0 + checksum: c7ae3e7b18d50e96cf9d20625cedece91457ddcd2f5ac1a6e7feea9411decdd24dd9d1558c67193980dbbdff310176716f5ed1b0bdffd279be36629c9894a44f + languageName: node + linkType: hard + +"@libp2p/mplex@npm:^8.0.3": + version: 8.0.3 + resolution: "@libp2p/mplex@npm:8.0.3" + dependencies: + "@libp2p/interface-connection": ^5.0.0 + "@libp2p/interface-stream-muxer": ^4.1.2 + "@libp2p/interfaces": ^3.2.0 + "@libp2p/logger": ^2.0.0 + abortable-iterator: ^5.0.0 + any-signal: ^4.0.1 + benchmark: ^2.1.4 + it-batched-bytes: ^2.0.2 + it-pushable: ^3.1.0 + it-stream-types: ^2.0.1 + rate-limiter-flexible: ^2.3.9 + uint8arraylist: ^2.1.1 + uint8arrays: ^4.0.2 + varint: ^6.0.0 + checksum: 0fa77d194cdfb5e6723dcd8f214763701944b04e9a1a37590b299f7ce69a24b544b571206ad80db55223f14313f8b97ca0bfc52ac9e6beda6d21a334a1c6d585 + languageName: node + linkType: hard + +"@libp2p/multistream-select@npm:^3.1.8": + version: 3.1.8 + resolution: "@libp2p/multistream-select@npm:3.1.8" + dependencies: + "@libp2p/interfaces": ^3.2.0 + "@libp2p/logger": ^2.0.0 + abortable-iterator: ^5.0.0 + it-first: ^3.0.1 + it-handshake: ^4.1.3 + it-length-prefixed: ^9.0.0 + it-merge: ^3.0.0 + it-pipe: ^3.0.0 + it-pushable: ^3.1.0 + it-reader: ^6.0.1 + it-stream-types: ^2.0.1 + uint8arraylist: ^2.3.1 + uint8arrays: ^4.0.2 + checksum: c90f5b48547a83e01c7f2afd0456689a4857733720a59dfb248d92f8fd97330ab0e1569fa48cca2f5cffbaf6444a837dc4db748b2e74884a0ed12ed0262dd6b5 + languageName: node + linkType: hard + +"@libp2p/peer-collections@npm:^3.0.0, @libp2p/peer-collections@npm:^3.0.1": + version: 3.0.1 + resolution: "@libp2p/peer-collections@npm:3.0.1" + dependencies: + "@libp2p/interface-peer-id": ^2.0.0 + "@libp2p/peer-id": ^2.0.0 + checksum: 7fa2af88e658f39fffa26a7bc4b5b6ea4784f62f4926a060480b7e37c16adb0aa144facbe6b07fb06de4a264f0c78077225738eab4717ccf80d7a93ada7a78a8 + languageName: node + linkType: hard + +"@libp2p/peer-id-factory@npm:^2.0.0": + version: 2.0.3 + resolution: "@libp2p/peer-id-factory@npm:2.0.3" + dependencies: + "@libp2p/crypto": ^1.0.0 + "@libp2p/interface-keys": ^1.0.2 + "@libp2p/interface-peer-id": ^2.0.0 + "@libp2p/peer-id": ^2.0.0 + multiformats: ^11.0.0 + protons-runtime: ^5.0.0 + uint8arraylist: ^2.0.0 + uint8arrays: ^4.0.2 + checksum: 1231f24464d86ee118c214f42e0146c4b0e2173e598adac58209cb5113e5fa4fd0e51183035c1bba2c2819d7738f0d64a5bcf41a5ddabb9235e844b970530784 + languageName: node + linkType: hard + +"@libp2p/peer-id@npm:^2.0.0, @libp2p/peer-id@npm:^2.0.1, @libp2p/peer-id@npm:^2.0.3": + version: 2.0.3 + resolution: "@libp2p/peer-id@npm:2.0.3" + dependencies: + "@libp2p/interface-peer-id": ^2.0.0 + "@libp2p/interfaces": ^3.2.0 + multiformats: ^11.0.0 + uint8arrays: ^4.0.2 + checksum: 4a069861046286b412e8cef370796f91a3ee1c9609cf0ccdbb9366201558c604b752bfcfa3c6eac02c851d5046c0ea58bc6116094ebc1d2055fe970ea41184fd + languageName: node + linkType: hard + +"@libp2p/peer-record@npm:^5.0.0, @libp2p/peer-record@npm:^5.0.3": + version: 5.0.3 + resolution: "@libp2p/peer-record@npm:5.0.3" + dependencies: + "@libp2p/crypto": ^1.0.11 + "@libp2p/interface-peer-id": ^2.0.0 + "@libp2p/interface-record": ^2.0.1 + "@libp2p/interfaces": ^3.2.0 + "@libp2p/peer-id": ^2.0.0 + "@libp2p/utils": ^3.0.0 + "@multiformats/multiaddr": ^12.0.0 + protons-runtime: ^5.0.0 + uint8-varint: ^1.0.2 + uint8arraylist: ^2.1.0 + uint8arrays: ^4.0.2 + checksum: bd814b073b228cac3fb09a9721a0e24a0531185aeb8caa89f307b4f010b95fdcdf76a4699f17d4a50193c34720da540a17b5f0948e3d32d7d1d5647ffc130ea1 + languageName: node + linkType: hard + +"@libp2p/peer-store@npm:^8.1.0": + version: 8.1.2 + resolution: "@libp2p/peer-store@npm:8.1.2" + dependencies: + "@libp2p/crypto": ^1.0.15 + "@libp2p/interface-libp2p": ^3.1.0 + "@libp2p/interface-peer-id": ^2.0.0 + "@libp2p/interface-peer-store": ^2.0.1 + "@libp2p/interfaces": ^3.2.0 + "@libp2p/logger": ^2.0.7 + "@libp2p/peer-id": ^2.0.0 + "@libp2p/peer-record": ^5.0.3 + "@multiformats/multiaddr": ^12.0.0 + interface-datastore: ^8.0.0 + mortice: ^3.0.1 + multiformats: ^11.0.0 + protons-runtime: ^5.0.0 + uint8arraylist: ^2.1.1 + uint8arrays: ^4.0.2 + checksum: bf616375d975bb274cf405152a23de1f3b33319acb79ff0e20fa1e8441a50ff4dd6fd725e6c2c16f1ce3c6e7ec10e93d1e8b43dd0387c45f69838084a1807c59 + languageName: node + linkType: hard + +"@libp2p/record@npm:^3.0.0": + version: 3.0.3 + resolution: "@libp2p/record@npm:3.0.3" + dependencies: + "@libp2p/interface-dht": ^2.0.0 + "@libp2p/interfaces": ^3.2.0 + multiformats: ^11.0.0 + protons-runtime: ^5.0.0 + uint8arraylist: ^2.1.1 + uint8arrays: ^4.0.2 + checksum: 6275c6518cb980e832e7e5085c2aa3c66df81b8ffb724d1707f28e73f640df7d6b4c9cd019508a520c30af3cb71c8174c627debe34cbf69a2d30ac7a57334555 + languageName: node + linkType: hard + +"@libp2p/tcp@npm:^7.0.1": + version: 7.0.1 + resolution: "@libp2p/tcp@npm:7.0.1" + dependencies: + "@libp2p/interface-connection": ^5.0.0 + "@libp2p/interface-metrics": ^4.0.0 + "@libp2p/interface-transport": ^4.0.0 + "@libp2p/interfaces": ^3.2.0 + "@libp2p/logger": ^2.0.0 + "@libp2p/utils": ^3.0.2 + "@multiformats/mafmt": ^12.0.0 + "@multiformats/multiaddr": ^12.0.0 + stream-to-it: ^0.2.2 + checksum: 2c099e0334e1b49933fa46d4965a933950b4947983e13d4156ac37eb6170b789eb0ae4e28a13282c485c4220168ec5ca1c52d6705352d8022b6cbb72f86b7fe9 + languageName: node + linkType: hard + +"@libp2p/topology@npm:^4.0.0, @libp2p/topology@npm:^4.0.1": + version: 4.0.1 + resolution: "@libp2p/topology@npm:4.0.1" + dependencies: + "@libp2p/interface-peer-id": ^2.0.0 + "@libp2p/interface-registrar": ^2.0.3 + "@libp2p/logger": ^2.0.1 + it-all: ^2.0.0 + checksum: 25d04867a7fef8e01e07b53a719bafb381d70fdc6105050b9d23e686aaed1f8d883b066bbc92fa2404a5a8f933a86a58a11b39517a7a13304f032a852112d3f2 + languageName: node + linkType: hard + +"@libp2p/tracked-map@npm:^3.0.0": + version: 3.0.2 + resolution: "@libp2p/tracked-map@npm:3.0.2" + dependencies: + "@libp2p/interface-metrics": ^4.0.0 + checksum: 3d781ba66445d112cbd98230aa0a617bff5a176bfaf3757a917837351455e8875ee27fe5f533e406b6c71006bdce2d94a469bf41007cec49cc7ef253cbe1b306 + languageName: node + linkType: hard + +"@libp2p/utils@npm:^3.0.0, @libp2p/utils@npm:^3.0.10, @libp2p/utils@npm:^3.0.2": + version: 3.0.11 + resolution: "@libp2p/utils@npm:3.0.11" + dependencies: + "@achingbrain/ip-address": ^8.1.0 + "@libp2p/interface-connection": ^5.0.1 + "@libp2p/interface-peer-store": ^2.0.0 + "@libp2p/interfaces": ^3.2.0 + "@libp2p/logger": ^2.0.0 + "@multiformats/multiaddr": ^12.0.0 + abortable-iterator: ^5.0.0 + is-loopback-addr: ^2.0.1 + it-stream-types: ^2.0.1 + private-ip: ^3.0.0 + uint8arraylist: ^2.3.2 + checksum: d2ae2308310c384003ccda3c56800d7557b4ae1d50440bd45212c2a64e6104a5968c6896524abfc646dc405b2e33602d471f064e24f6b8edfd504b54ad0b5974 + languageName: node + linkType: hard + "@microsoft/tsdoc-config@npm:0.16.2": version: 0.16.2 resolution: "@microsoft/tsdoc-config@npm:0.16.2" @@ -1581,6 +2349,30 @@ __metadata: languageName: node linkType: hard +"@multiformats/mafmt@npm:^12.0.0": + version: 12.1.0 + resolution: "@multiformats/mafmt@npm:12.1.0" + dependencies: + "@multiformats/multiaddr": ^12.0.0 + checksum: 5e2cc93493c64a0cd5df0231b14f8bc6a28b8f7da0cc01f9df440e8b4a30a1d7d5b3afd056dd6ea5a6fee43daceb04ddd9bfbfb146dc1362e993e03214b75a32 + languageName: node + linkType: hard + +"@multiformats/multiaddr@npm:^12.0.0": + version: 12.1.2 + resolution: "@multiformats/multiaddr@npm:12.1.2" + dependencies: + "@chainsafe/is-ip": ^2.0.1 + "@chainsafe/netmask": ^2.0.0 + "@libp2p/interfaces": ^3.3.1 + dns-over-http-resolver: ^2.1.0 + multiformats: ^11.0.0 + uint8arrays: ^4.0.2 + varint: ^6.0.0 + checksum: 976c683501e3f40a9103b4b066e547a90d211b336d9923e7863e3fdd68ad146e0fe8062d980ab0a234832b28a3e71e83d2d38875d682b14dbe5f05a6861a7f21 + languageName: node + linkType: hard + "@noble/curves@npm:0.9.0": version: 0.9.0 resolution: "@noble/curves@npm:0.9.0" @@ -1608,13 +2400,27 @@ __metadata: languageName: node linkType: hard -"@noble/hashes@npm:1.3.0, @noble/hashes@npm:~1.3.0": +"@noble/ed25519@npm:^1.6.0": + version: 1.7.3 + resolution: "@noble/ed25519@npm:1.7.3" + checksum: 45169927d51de513e47bbeebff3a603433c4ac7579e1b8c5034c380a0afedbe85e6959be3d69584a7a5ed6828d638f8f28879003b9bb2fb5f22d8aa2d88fd5fe + languageName: node + linkType: hard + +"@noble/hashes@npm:1.3.0, @noble/hashes@npm:^1.3.0, @noble/hashes@npm:~1.3.0": version: 1.3.0 resolution: "@noble/hashes@npm:1.3.0" checksum: d7ddb6d7c60f1ce1f87facbbef5b724cdea536fc9e7f59ae96e0fc9de96c8f1a2ae2bdedbce10f7dcc621338dfef8533daa73c873f2b5c87fa1a4e05a95c2e2e languageName: node linkType: hard +"@noble/secp256k1@npm:^1.5.4": + version: 1.7.1 + resolution: "@noble/secp256k1@npm:1.7.1" + checksum: d2301f1f7690368d8409a3152450458f27e54df47e3f917292de3de82c298770890c2de7c967d237eff9c95b70af485389a9695f73eb05a43e2bd562d18b18cb + languageName: node + linkType: hard + "@nodelib/fs.scandir@npm:2.1.5": version: 2.1.5 resolution: "@nodelib/fs.scandir@npm:2.1.5" @@ -1692,6 +2498,79 @@ __metadata: languageName: node linkType: hard +"@protobufjs/aspromise@npm:^1.1.1, @protobufjs/aspromise@npm:^1.1.2": + version: 1.1.2 + resolution: "@protobufjs/aspromise@npm:1.1.2" + checksum: 011fe7ef0826b0fd1a95935a033a3c0fd08483903e1aa8f8b4e0704e3233406abb9ee25350ec0c20bbecb2aad8da0dcea58b392bbd77d6690736f02c143865d2 + languageName: node + linkType: hard + +"@protobufjs/base64@npm:^1.1.2": + version: 1.1.2 + resolution: "@protobufjs/base64@npm:1.1.2" + checksum: 67173ac34de1e242c55da52c2f5bdc65505d82453893f9b51dc74af9fe4c065cf4a657a4538e91b0d4a1a1e0a0642215e31894c31650ff6e3831471061e1ee9e + languageName: node + linkType: hard + +"@protobufjs/codegen@npm:^2.0.4": + version: 2.0.4 + resolution: "@protobufjs/codegen@npm:2.0.4" + checksum: 59240c850b1d3d0b56d8f8098dd04787dcaec5c5bd8de186fa548de86b86076e1c50e80144b90335e705a044edf5bc8b0998548474c2a10a98c7e004a1547e4b + languageName: node + linkType: hard + +"@protobufjs/eventemitter@npm:^1.1.0": + version: 1.1.0 + resolution: "@protobufjs/eventemitter@npm:1.1.0" + checksum: 0369163a3d226851682f855f81413cbf166cd98f131edb94a0f67f79e75342d86e89df9d7a1df08ac28be2bc77e0a7f0200526bb6c2a407abbfee1f0262d5fd7 + languageName: node + linkType: hard + +"@protobufjs/fetch@npm:^1.1.0": + version: 1.1.0 + resolution: "@protobufjs/fetch@npm:1.1.0" + dependencies: + "@protobufjs/aspromise": ^1.1.1 + "@protobufjs/inquire": ^1.1.0 + checksum: 3fce7e09eb3f1171dd55a192066450f65324fd5f7cc01a431df01bb00d0a895e6bfb5b0c5561ce157ee1d886349c90703d10a4e11a1a256418ff591b969b3477 + languageName: node + linkType: hard + +"@protobufjs/float@npm:^1.0.2": + version: 1.0.2 + resolution: "@protobufjs/float@npm:1.0.2" + checksum: 5781e1241270b8bd1591d324ca9e3a3128d2f768077a446187a049e36505e91bc4156ed5ac3159c3ce3d2ba3743dbc757b051b2d723eea9cd367bfd54ab29b2f + languageName: node + linkType: hard + +"@protobufjs/inquire@npm:^1.1.0": + version: 1.1.0 + resolution: "@protobufjs/inquire@npm:1.1.0" + checksum: ca06f02eaf65ca36fb7498fc3492b7fc087bfcc85c702bac5b86fad34b692bdce4990e0ef444c1e2aea8c034227bd1f0484be02810d5d7e931c55445555646f4 + languageName: node + linkType: hard + +"@protobufjs/path@npm:^1.1.2": + version: 1.1.2 + resolution: "@protobufjs/path@npm:1.1.2" + checksum: 856eeb532b16a7aac071cacde5c5620df800db4c80cee6dbc56380524736205aae21e5ae47739114bf669ab5e8ba0e767a282ad894f3b5e124197cb9224445ee + languageName: node + linkType: hard + +"@protobufjs/pool@npm:^1.1.0": + version: 1.1.0 + resolution: "@protobufjs/pool@npm:1.1.0" + checksum: d6a34fbbd24f729e2a10ee915b74e1d77d52214de626b921b2d77288bd8f2386808da2315080f2905761527cceffe7ec34c7647bd21a5ae41a25e8212ff79451 + languageName: node + linkType: hard + +"@protobufjs/utf8@npm:^1.1.0": + version: 1.1.0 + resolution: "@protobufjs/utf8@npm:1.1.0" + checksum: f9bf3163d13aaa3b6f5e6fbf37a116e094ea021c0e1f2a7ccd0e12a29e2ce08dafba4e8b36e13f8ed7397e1591610ce880ed1289af4d66cf4ace8a36a9557278 + languageName: node + linkType: hard + "@rushstack/eslint-patch@npm:^1.1.4, @rushstack/eslint-patch@npm:^1.2.0": version: 1.2.0 resolution: "@rushstack/eslint-patch@npm:1.2.0" @@ -1717,38 +2596,146 @@ __metadata: languageName: node linkType: hard -"@scure/bip39@npm:1.2.0": - version: 1.2.0 - resolution: "@scure/bip39@npm:1.2.0" +"@scure/bip39@npm:1.2.0": + version: 1.2.0 + resolution: "@scure/bip39@npm:1.2.0" + dependencies: + "@noble/hashes": ~1.3.0 + "@scure/base": ~1.1.0 + checksum: 980d761f53e63de04a9e4db840eb13bfb1bd1b664ecb04a71824c12c190f4972fd84146f3ed89b2a8e4c6bd2c17c15f8b592b7ac029e903323b0f9e2dae6916b + languageName: node + linkType: hard + +"@sinclair/typebox@npm:^0.25.16": + version: 0.25.24 + resolution: "@sinclair/typebox@npm:0.25.24" + checksum: 10219c58f40b8414c50b483b0550445e9710d4fe7b2c4dccb9b66533dd90ba8e024acc776026cebe81e87f06fa24b07fdd7bc30dd277eb9cc386ec50151a3026 + languageName: node + linkType: hard + +"@sinonjs/commons@npm:^2.0.0": + version: 2.0.0 + resolution: "@sinonjs/commons@npm:2.0.0" + dependencies: + type-detect: 4.0.8 + checksum: 5023ba17edf2b85ed58262313b8e9b59e23c6860681a9af0200f239fe939e2b79736d04a260e8270ddd57196851dde3ba754d7230be5c5234e777ae2ca8af137 + languageName: node + linkType: hard + +"@sinonjs/fake-timers@npm:^10.0.2": + version: 10.0.2 + resolution: "@sinonjs/fake-timers@npm:10.0.2" + dependencies: + "@sinonjs/commons": ^2.0.0 + checksum: c62aa98e7cefda8dedc101ce227abc888dc46b8ff9706c5f0a8dfd9c3ada97d0a5611384738d9ba0b26b59f99c2ba24efece8e779bb08329e9e87358fa309824 + languageName: node + linkType: hard + +"@stablelib/aead@npm:^1.0.1": + version: 1.0.1 + resolution: "@stablelib/aead@npm:1.0.1" + checksum: 1a6f68d138f105d17dd65349751515bd252ab0498c77255b8555478d28415600dde493f909eb718245047a993f838dfae546071e1687566ffb7b8c3e10c918d9 + languageName: node + linkType: hard + +"@stablelib/binary@npm:^1.0.1": + version: 1.0.1 + resolution: "@stablelib/binary@npm:1.0.1" + dependencies: + "@stablelib/int": ^1.0.1 + checksum: dca9b98eb1f56a4002b5b9e7351fbc49f3d8616af87007c01e833bd763ac89214eb5f3b7e18673c91ce59d4a0e4856a2eb661ace33d39f17fb1ad267271fccd8 + languageName: node + linkType: hard + +"@stablelib/bytes@npm:^1.0.1": + version: 1.0.1 + resolution: "@stablelib/bytes@npm:1.0.1" + checksum: 456267e08c3384abcb71d3ad3e97a6f99185ad754bac016f501ebea4e4886f37900589143b57e33bdbbf513a92fc89368c15dd4517e0540d0bdc79ecdf9dd087 + languageName: node + linkType: hard + +"@stablelib/chacha20poly1305@npm:^1.0.1": + version: 1.0.1 + resolution: "@stablelib/chacha20poly1305@npm:1.0.1" + dependencies: + "@stablelib/aead": ^1.0.1 + "@stablelib/binary": ^1.0.1 + "@stablelib/chacha": ^1.0.1 + "@stablelib/constant-time": ^1.0.1 + "@stablelib/poly1305": ^1.0.1 + "@stablelib/wipe": ^1.0.1 + checksum: 81f1a32330838d31e4dc3144d76eba7244b56d9ea38c1f604f2c34d93ed8e67e9a6167d2cfd72254c13cc46dfc1f5ce5157b37939a575295d69d9144abb4e4fb + languageName: node + linkType: hard + +"@stablelib/chacha@npm:^1.0.1": + version: 1.0.1 + resolution: "@stablelib/chacha@npm:1.0.1" + dependencies: + "@stablelib/binary": ^1.0.1 + "@stablelib/wipe": ^1.0.1 + checksum: f061f36c4ca4bf177dd7cac11e7c65ced164f141b6065885141ae5a55f32e16ba0209aefcdcc966aef013f1da616ce901a3a80653b4b6f833cf7e3397ae2d6bd + languageName: node + linkType: hard + +"@stablelib/constant-time@npm:^1.0.1": + version: 1.0.1 + resolution: "@stablelib/constant-time@npm:1.0.1" + checksum: dba4f4bf508de2ff15f7f0cbd875e70391aa3ba3698290fe1ed2feb151c243ba08a90fc6fb390ec2230e30fcc622318c591a7c0e35dcb8150afb50c797eac3d7 + languageName: node + linkType: hard + +"@stablelib/int@npm:^1.0.1": + version: 1.0.1 + resolution: "@stablelib/int@npm:1.0.1" + checksum: 65bfbf50a382eea70c68e05366bf379cfceff8fbc076f1c267ef2f2411d7aed64fd140c415cb6c29f19a3910d3b8b7805d4b32ad5721a5007a8e744a808c7ae3 + languageName: node + linkType: hard + +"@stablelib/keyagreement@npm:^1.0.1": + version: 1.0.1 + resolution: "@stablelib/keyagreement@npm:1.0.1" + dependencies: + "@stablelib/bytes": ^1.0.1 + checksum: 3c8ec904dd50f72f3162f5447a0fa8f1d9ca6e24cd272d3dbe84971267f3b47f9bd5dc4e4eeedf3fbac2fe01f2d9277053e57c8e60db8c5544bfb35c62d290dd + languageName: node + linkType: hard + +"@stablelib/poly1305@npm:^1.0.1": + version: 1.0.1 + resolution: "@stablelib/poly1305@npm:1.0.1" dependencies: - "@noble/hashes": ~1.3.0 - "@scure/base": ~1.1.0 - checksum: 980d761f53e63de04a9e4db840eb13bfb1bd1b664ecb04a71824c12c190f4972fd84146f3ed89b2a8e4c6bd2c17c15f8b592b7ac029e903323b0f9e2dae6916b + "@stablelib/constant-time": ^1.0.1 + "@stablelib/wipe": ^1.0.1 + checksum: 70b845bb0481c66b7ba3f3865d01e4c67a4dffc9616fc6de1d23efc5e828ec09de25f8e3be4e1f15a23b8e87e3036ee3d949c2fd4785047e6f7028bbec0ead18 languageName: node linkType: hard -"@sinclair/typebox@npm:^0.25.16": - version: 0.25.24 - resolution: "@sinclair/typebox@npm:0.25.24" - checksum: 10219c58f40b8414c50b483b0550445e9710d4fe7b2c4dccb9b66533dd90ba8e024acc776026cebe81e87f06fa24b07fdd7bc30dd277eb9cc386ec50151a3026 +"@stablelib/random@npm:^1.0.2": + version: 1.0.2 + resolution: "@stablelib/random@npm:1.0.2" + dependencies: + "@stablelib/binary": ^1.0.1 + "@stablelib/wipe": ^1.0.1 + checksum: f5ace0a588dc4c21f01cb85837892d4c872e994ae77a58a8eb7dd61aa0b26fb1e9b46b0445e71af57d963ef7d9f5965c64258fc0d04df7b2947bc48f2d3560c5 languageName: node linkType: hard -"@sinonjs/commons@npm:^2.0.0": - version: 2.0.0 - resolution: "@sinonjs/commons@npm:2.0.0" - dependencies: - type-detect: 4.0.8 - checksum: 5023ba17edf2b85ed58262313b8e9b59e23c6860681a9af0200f239fe939e2b79736d04a260e8270ddd57196851dde3ba754d7230be5c5234e777ae2ca8af137 +"@stablelib/wipe@npm:^1.0.1": + version: 1.0.1 + resolution: "@stablelib/wipe@npm:1.0.1" + checksum: 287802eb146810a46ba72af70b82022caf83a8aeebde23605f5ee0decf64fe2b97a60c856e43b6617b5801287c30cfa863cfb0469e7fcde6f02d143cf0c6cbf4 languageName: node linkType: hard -"@sinonjs/fake-timers@npm:^10.0.2": - version: 10.0.2 - resolution: "@sinonjs/fake-timers@npm:10.0.2" +"@stablelib/x25519@npm:^1.0.3": + version: 1.0.3 + resolution: "@stablelib/x25519@npm:1.0.3" dependencies: - "@sinonjs/commons": ^2.0.0 - checksum: c62aa98e7cefda8dedc101ce227abc888dc46b8ff9706c5f0a8dfd9c3ada97d0a5611384738d9ba0b26b59f99c2ba24efece8e779bb08329e9e87358fa309824 + "@stablelib/keyagreement": ^1.0.1 + "@stablelib/random": ^1.0.2 + "@stablelib/wipe": ^1.0.1 + checksum: f8537066b542b6770c1b5b2ae5ad0688d1b986e4bf818067c152c123a5471531987bbf024224f75f387f481ccc5b628e391e49e92102b8b1a3e2d449d6105402 languageName: node linkType: hard @@ -2065,7 +3052,7 @@ __metadata: languageName: node linkType: hard -"@types/koa@npm:*, @types/koa@npm:^2.13.5": +"@types/koa@npm:*, @types/koa@npm:^2.13.5, @types/koa@npm:^2.13.6": version: 2.13.6 resolution: "@types/koa@npm:2.13.6" dependencies: @@ -2234,6 +3221,13 @@ __metadata: languageName: node linkType: hard +"@types/node@npm:>=13.7.0": + version: 20.1.1 + resolution: "@types/node@npm:20.1.1" + checksum: 47961ee23f873c14c3f6045422ff3059f3bfb10231ef3080a7a72d7215cc8c2623fa8cedb7b246305962fa9c1e0c9e381e04b12eb3e9ec5d076025c6231ac8da + languageName: node + linkType: hard + "@types/normalize-package-data@npm:^2.4.0": version: 2.4.1 resolution: "@types/normalize-package-data@npm:2.4.1" @@ -2262,6 +3256,13 @@ __metadata: languageName: node linkType: hard +"@types/retry@npm:0.12.1": + version: 0.12.1 + resolution: "@types/retry@npm:0.12.1" + checksum: 5f46b2556053655f78262bb33040dc58417c900457cc63ff37d6c35349814471453ef511af0cec76a540c601296cd2b22f64bab1ab649c0dacc0223765ba876c + languageName: node + linkType: hard + "@types/semver@npm:^7.3.12": version: 7.3.13 resolution: "@types/semver@npm:7.3.13" @@ -2288,6 +3289,22 @@ __metadata: languageName: node linkType: hard +"@types/sinon@npm:^10.0.14": + version: 10.0.15 + resolution: "@types/sinon@npm:10.0.15" + dependencies: + "@types/sinonjs__fake-timers": "*" + checksum: cec6d7d9d5582ca3ac851b029d5d90451bfe6d376164253792a6eb6ddcd609a0411a7fac9ed92e1879e7d3ec091d2ea2e8dbb4f6140a1065439b81dc20cafa7c + languageName: node + linkType: hard + +"@types/sinonjs__fake-timers@npm:*": + version: 8.1.2 + resolution: "@types/sinonjs__fake-timers@npm:8.1.2" + checksum: bbc73a5ab6c0ec974929392f3d6e1e8db4ebad97ec506d785301e1c3d8a4f98a35b1aa95b97035daef02886fd8efd7788a2fa3ced2ec7105988bfd8dce61eedd + languageName: node + linkType: hard + "@types/stack-utils@npm:^2.0.0": version: 2.0.1 resolution: "@types/stack-utils@npm:2.0.1" @@ -2492,6 +3509,16 @@ __metadata: languageName: node linkType: hard +"abortable-iterator@npm:^5.0.0, abortable-iterator@npm:^5.0.1": + version: 5.0.1 + resolution: "abortable-iterator@npm:5.0.1" + dependencies: + get-iterator: ^2.0.0 + it-stream-types: ^2.0.1 + checksum: 9f50b2d2416d1c4312288d8a981f9cae8caeaaac6f4b0aa13be361c13f8e7e375b0ff2099d515ea46ade7cf2a91b9573f1f224434ff63966f76eb09be3202ed9 + languageName: node + linkType: hard + "abstract-leveldown@npm:^7.2.0": version: 7.2.0 resolution: "abstract-leveldown@npm:7.2.0" @@ -2631,6 +3658,13 @@ __metadata: languageName: node linkType: hard +"any-signal@npm:^4.0.1, any-signal@npm:^4.1.1": + version: 4.1.1 + resolution: "any-signal@npm:4.1.1" + checksum: 93819ee19f86205f4e228f8a8d39a8082f5ca67a696ac469d66669ba0ba721fdc9e12a85d85a2e73d08d8739417ce6720faa08fc8f7eb5e6fda007da8a8c715c + languageName: node + linkType: hard + "anymatch@npm:^3.0.3": version: 3.1.3 resolution: "anymatch@npm:3.1.3" @@ -2799,6 +3833,16 @@ __metadata: languageName: node linkType: hard +"benchmark@npm:^2.1.4": + version: 2.1.4 + resolution: "benchmark@npm:2.1.4" + dependencies: + lodash: ^4.17.4 + platform: ^1.3.3 + checksum: aa466561d4f2b0a2419a3069b8f90fd35ffacf26849697eea9de525ecfbd10b44da11070cc51c88d772076db8cb2415641b493de7d6c024fdf8551019c6fcf1c + languageName: node + linkType: hard + "brace-expansion@npm:^1.1.7": version: 1.1.11 resolution: "brace-expansion@npm:1.1.11" @@ -2920,6 +3964,24 @@ __metadata: languageName: node linkType: hard +"busboy@npm:^1.6.0": + version: 1.6.0 + resolution: "busboy@npm:1.6.0" + dependencies: + streamsearch: ^1.1.0 + checksum: 32801e2c0164e12106bf236291a00795c3c4e4b709ae02132883fe8478ba2ae23743b11c5735a0aae8afe65ac4b6ca4568b91f0d9fed1fdbc32ede824a73746e + languageName: node + linkType: hard + +"byte-access@npm:^1.0.0, byte-access@npm:^1.0.1": + version: 1.0.1 + resolution: "byte-access@npm:1.0.1" + dependencies: + uint8arraylist: ^2.0.0 + checksum: 12d5350d9fa6108da80844f5b8c6c80f54bc037cc90f914d81e3a27e374d6c5aac1ba875a8dc2da6b00c6d65c1726bde83a7d57914746cd6d52ff9939a5b21fa + languageName: node + linkType: hard + "bytes@npm:3.1.2, bytes@npm:^3.1.2": version: 3.1.2 resolution: "bytes@npm:3.1.2" @@ -3343,6 +4405,13 @@ __metadata: languageName: node linkType: hard +"core-js@npm:^3.6.5": + version: 3.30.1 + resolution: "core-js@npm:3.30.1" + checksum: 6d4a00b488694d4c715c424e15dfef31433ac7aa395c39c518a0cfacec918ada1c716fed74682033197e0164e23bbf38bfd598ee9a239c4aaa590ab1ba862ac8 + languageName: node + linkType: hard + "core-util-is@npm:^1.0.2": version: 1.0.3 resolution: "core-util-is@npm:1.0.3" @@ -3390,6 +4459,27 @@ __metadata: languageName: node linkType: hard +"datastore-core@npm:^9.0.0, datastore-core@npm:^9.0.1": + version: 9.1.1 + resolution: "datastore-core@npm:9.1.1" + dependencies: + "@libp2p/logger": ^2.0.0 + err-code: ^3.0.1 + interface-store: ^5.0.0 + it-all: ^3.0.1 + it-drain: ^3.0.1 + it-filter: ^3.0.0 + it-map: ^3.0.1 + it-merge: ^3.0.0 + it-pipe: ^3.0.0 + it-pushable: ^3.0.0 + it-sort: ^3.0.1 + it-take: ^3.0.1 + uint8arrays: ^4.0.2 + checksum: 25fb2d56d4e0f20b0201cb890183ccad4a9e532736a9373f6d3fd782439fa27a41c9f8c03a8dc267cda970c0f014c4733f5a8ed3e25818c0ccdb50c91af4a690 + languageName: node + linkType: hard + "date-fns@npm:^2.29.1, date-fns@npm:^2.29.3": version: 2.29.3 resolution: "date-fns@npm:2.29.3" @@ -3397,7 +4487,7 @@ __metadata: languageName: node linkType: hard -"debug@npm:4, debug@npm:^4.1.0, debug@npm:^4.1.1, debug@npm:^4.3.2, debug@npm:^4.3.3, debug@npm:^4.3.4": +"debug@npm:4, debug@npm:^4.1.0, debug@npm:^4.1.1, debug@npm:^4.3.1, debug@npm:^4.3.2, debug@npm:^4.3.3, debug@npm:^4.3.4": version: 4.3.4 resolution: "debug@npm:4.3.4" dependencies: @@ -3454,6 +4544,15 @@ __metadata: languageName: node linkType: hard +"default-gateway@npm:^6.0.2": + version: 6.0.3 + resolution: "default-gateway@npm:6.0.3" + dependencies: + execa: ^5.0.0 + checksum: 126f8273ecac8ee9ff91ea778e8784f6cd732d77c3157e8c5bdd6ed03651b5291f71446d05bc02d04073b1e67583604db5394ea3cf992ede0088c70ea15b7378 + languageName: node + linkType: hard + "deferred-leveldown@npm:^7.0.0": version: 7.0.0 resolution: "deferred-leveldown@npm:7.0.0" @@ -3556,6 +4655,29 @@ __metadata: languageName: node linkType: hard +"dns-over-http-resolver@npm:^1.2.3": + version: 1.2.3 + resolution: "dns-over-http-resolver@npm:1.2.3" + dependencies: + debug: ^4.3.1 + native-fetch: ^3.0.0 + receptacle: ^1.3.2 + checksum: 3cc1a1d77fc43e7a8a12453da987b80860ac96dc1031386c5eb1a39154775a87cfa1d50c0eaa5ea5e397e898791654608f6e2acf03f750f4098ab8822bb7d928 + languageName: node + linkType: hard + +"dns-over-http-resolver@npm:^2.1.0": + version: 2.1.1 + resolution: "dns-over-http-resolver@npm:2.1.1" + dependencies: + debug: ^4.3.1 + native-fetch: ^4.0.2 + receptacle: ^1.3.2 + undici: ^5.12.0 + checksum: 153a0f4ef705cd08c9b0c163d654988dbb087eabe44c8ab243481c108af97b52ec6343b7127ed1a6a5705a497e8dc06cd1e6c33fbe5aae3a08e357c1e93fc93e + languageName: node + linkType: hard + "doctrine@npm:^3.0.0": version: 3.0.0 resolution: "doctrine@npm:3.0.0" @@ -3565,6 +4687,13 @@ __metadata: languageName: node linkType: hard +"dotenv@npm:^16.0.3": + version: 16.0.3 + resolution: "dotenv@npm:16.0.3" + checksum: afcf03f373d7a6d62c7e9afea6328e62851d627a4e73f2e12d0a8deae1cd375892004f3021883f8aec85932cd2834b091f568ced92b4774625b321db83b827f8 + languageName: node + linkType: hard + "duplexer@npm:~0.1.1": version: 0.1.2 resolution: "duplexer@npm:0.1.2" @@ -3630,6 +4759,13 @@ __metadata: languageName: node linkType: hard +"err-code@npm:^3.0.1": + version: 3.0.1 + resolution: "err-code@npm:3.0.1" + checksum: aede1f1d5ebe6d6b30b5e3175e3cc13e67de2e2e1ad99ce4917e957d7b59e8451ed10ee37dbc6493521920a47082c479b9097e5c39438d4aff4cc84438568a5a + languageName: node + linkType: hard + "error-ex@npm:^1.3.1": version: 1.3.2 resolution: "error-ex@npm:1.3.2" @@ -3926,6 +5062,13 @@ __metadata: languageName: node linkType: hard +"event-iterator@npm:^2.0.0": + version: 2.0.0 + resolution: "event-iterator@npm:2.0.0" + checksum: ffa76b1a6b53255e4b08a271da7dba12c8377a1b6018ef6e9e41c30fd8c5f83d12fad6205cfeec3967a9df57362ed0f81335571d13dca537c12eab1d5daf5866 + languageName: node + linkType: hard + "event-stream@npm:=3.3.4": version: 3.3.4 resolution: "event-stream@npm:3.3.4" @@ -3941,6 +5084,13 @@ __metadata: languageName: node linkType: hard +"eventemitter3@npm:^4.0.7": + version: 4.0.7 + resolution: "eventemitter3@npm:4.0.7" + checksum: 1875311c42fcfe9c707b2712c32664a245629b42bb0a5a84439762dd0fd637fc54d078155ea83c2af9e0323c9ac13687e03cfba79b03af9f40c89b4960099374 + languageName: node + linkType: hard + "evp_bytestokey@npm:^1.0.0, evp_bytestokey@npm:^1.0.3": version: 1.0.3 resolution: "evp_bytestokey@npm:1.0.3" @@ -4126,6 +5276,13 @@ __metadata: languageName: node linkType: hard +"freeport-promise@npm:^2.0.0": + version: 2.0.0 + resolution: "freeport-promise@npm:2.0.0" + checksum: 4f8baa9ba8d304b6018213261948260f8a5b15b262d1439e629cae702baf98d1bc3dbbe57e2ef6a1cbca864b5f75a0556a2566ae22de76f13247c4e9c932f948 + languageName: node + linkType: hard + "fresh@npm:~0.5.2": version: 0.5.2 resolution: "fresh@npm:0.5.2" @@ -4241,6 +5398,20 @@ __metadata: languageName: node linkType: hard +"get-iterator@npm:^1.0.2": + version: 1.0.2 + resolution: "get-iterator@npm:1.0.2" + checksum: 4a819aa91ecb61f4fd507bd62e3468d55f642f06011f944c381a739a21f685c36a37feb9324c8971e7c0fc70ca172066c45874fa2d1dcdf4b4fb8e43f16058c2 + languageName: node + linkType: hard + +"get-iterator@npm:^2.0.0": + version: 2.0.0 + resolution: "get-iterator@npm:2.0.0" + checksum: 4ed81e7d100dd0a8dab11bf7dc7f88d8895a03057fa4a56d24190bdfe490fa4da3005d5b88fc6336429b33b423586f40dbcc40ce723f5dea55593cae0e2937de + languageName: node + linkType: hard + "get-package-type@npm:^0.1.0": version: 0.1.0 resolution: "get-package-type@npm:0.1.0" @@ -4425,6 +5596,13 @@ __metadata: languageName: node linkType: hard +"hashlru@npm:^2.3.0": + version: 2.3.0 + resolution: "hashlru@npm:2.3.0" + checksum: 38b3559e6fb9d19fa731edc52d8d7e72cd378f708dcb01cecd4a6ba0c52f06d7d06d6277249f5c43d9915d8dda9be31adad768a379eef188db213c3f2b09278d + languageName: node + linkType: hard + "hexoid@npm:^1.0.0": version: 1.0.0 resolution: "hexoid@npm:1.0.0" @@ -4625,6 +5803,38 @@ __metadata: languageName: node linkType: hard +"interface-datastore@npm:^8.0.0": + version: 8.2.0 + resolution: "interface-datastore@npm:8.2.0" + dependencies: + interface-store: ^5.0.0 + nanoid: ^4.0.0 + uint8arrays: ^4.0.2 + checksum: f05a0a2877c2f293d76d5c08a9b72ff82706bd09b1c6e4c51a7bad3b8f0fef3eaff9e566222af796641f83fc956f1f12bcac35fdee001d88d189a6234eb31a64 + languageName: node + linkType: hard + +"interface-store@npm:^5.0.0": + version: 5.1.0 + resolution: "interface-store@npm:5.1.0" + checksum: 8d222799e02387be72903eee4d17e402fd2ac50db771f262739988b4e022d3112bc9b7f97c177bb0775709f86b67bf3622e53f1c6de20a4d5e7b5de293758e46 + languageName: node + linkType: hard + +"ip-regex@npm:^4.0.0": + version: 4.3.0 + resolution: "ip-regex@npm:4.3.0" + checksum: 7ff904b891221b1847f3fdf3dbb3e6a8660dc39bc283f79eb7ed88f5338e1a3d1104b779bc83759159be266249c59c2160e779ee39446d79d4ed0890dfd06f08 + languageName: node + linkType: hard + +"ip-regex@npm:^5.0.0": + version: 5.0.0 + resolution: "ip-regex@npm:5.0.0" + checksum: 4098b2df89c015f1484a5946e733ec126af8c1828719d90e09f04af23ce487e1a852670e4d3f51b0dc6dfbaf7d8bfab23fd7893ca60e69833da99b7b1ee3623b + languageName: node + linkType: hard + "ip@npm:^2.0.0": version: 2.0.0 resolution: "ip@npm:2.0.0" @@ -4632,6 +5842,13 @@ __metadata: languageName: node linkType: hard +"ipaddr.js@npm:^2.0.1": + version: 2.0.1 + resolution: "ipaddr.js@npm:2.0.1" + checksum: dd194a394a843d470f88d17191b0948f383ed1c8e320813f850c336a0fcb5e9215d97ec26ca35ab4fbbd31392c8b3467f3e8344628029ed3710b2ff6b5d1034e + languageName: node + linkType: hard + "is-arrayish@npm:^0.2.1": version: 0.2.1 resolution: "is-arrayish@npm:0.2.1" @@ -4655,6 +5872,13 @@ __metadata: languageName: node linkType: hard +"is-electron@npm:^2.2.0": + version: 2.2.2 + resolution: "is-electron@npm:2.2.2" + checksum: de5aa8bd8d72c96675b8d0f93fab4cc21f62be5440f65bc05c61338ca27bd851a64200f31f1bf9facbaa01b3dbfed7997b2186741d84b93b63e0aff1db6a9494 + languageName: node + linkType: hard + "is-extglob@npm:^2.1.1": version: 2.1.1 resolution: "is-extglob@npm:2.1.1" @@ -4694,6 +5918,15 @@ __metadata: languageName: node linkType: hard +"is-ip@npm:^3.1.0": + version: 3.1.0 + resolution: "is-ip@npm:3.1.0" + dependencies: + ip-regex: ^4.0.0 + checksum: da2c2b282407194adf2320bade0bad94be9c9d0bdab85ff45b1b62d8185f31c65dff3884519d57bf270277e5ea2046c7916a6e5a6db22fe4b7ddcdd3760f23eb + languageName: node + linkType: hard + "is-lambda@npm:^1.0.1": version: 1.0.1 resolution: "is-lambda@npm:1.0.1" @@ -4701,6 +5934,13 @@ __metadata: languageName: node linkType: hard +"is-loopback-addr@npm:^2.0.1": + version: 2.0.1 + resolution: "is-loopback-addr@npm:2.0.1" + checksum: 77030f7a12875c124b0bb83f172c1fa29fcb14574d15a1d4ed13be71c441b9cdc393370967b2e4bf808c564072933467d48e9ea006a3fd2cb3c7437c6d913108 + languageName: node + linkType: hard + "is-number@npm:^7.0.0": version: 7.0.0 resolution: "is-number@npm:7.0.0" @@ -4722,6 +5962,13 @@ __metadata: languageName: node linkType: hard +"is-plain-obj@npm:^2.1.0": + version: 2.1.0 + resolution: "is-plain-obj@npm:2.1.0" + checksum: cec9100678b0a9fe0248a81743041ed990c2d4c99f893d935545cfbc42876cbe86d207f3b895700c690ad2fa520e568c44afc1605044b535a7820c1d40e38daa + languageName: node + linkType: hard + "is-stream@npm:^2.0.0": version: 2.0.1 resolution: "is-stream@npm:2.0.1" @@ -4765,35 +6012,225 @@ __metadata: languageName: node linkType: hard -"istanbul-lib-report@npm:^3.0.0": - version: 3.0.0 - resolution: "istanbul-lib-report@npm:3.0.0" - dependencies: - istanbul-lib-coverage: ^3.0.0 - make-dir: ^3.0.0 - supports-color: ^7.1.0 - checksum: 3f29eb3f53c59b987386e07fe772d24c7f58c6897f34c9d7a296f4000de7ae3de9eb95c3de3df91dc65b134c84dee35c54eee572a56243e8907c48064e34ff1b +"istanbul-lib-report@npm:^3.0.0": + version: 3.0.0 + resolution: "istanbul-lib-report@npm:3.0.0" + dependencies: + istanbul-lib-coverage: ^3.0.0 + make-dir: ^3.0.0 + supports-color: ^7.1.0 + checksum: 3f29eb3f53c59b987386e07fe772d24c7f58c6897f34c9d7a296f4000de7ae3de9eb95c3de3df91dc65b134c84dee35c54eee572a56243e8907c48064e34ff1b + languageName: node + linkType: hard + +"istanbul-lib-source-maps@npm:^4.0.0": + version: 4.0.1 + resolution: "istanbul-lib-source-maps@npm:4.0.1" + dependencies: + debug: ^4.1.1 + istanbul-lib-coverage: ^3.0.0 + source-map: ^0.6.1 + checksum: 21ad3df45db4b81852b662b8d4161f6446cd250c1ddc70ef96a585e2e85c26ed7cd9c2a396a71533cfb981d1a645508bc9618cae431e55d01a0628e7dec62ef2 + languageName: node + linkType: hard + +"istanbul-reports@npm:^3.1.3": + version: 3.1.5 + resolution: "istanbul-reports@npm:3.1.5" + dependencies: + html-escaper: ^2.0.0 + istanbul-lib-report: ^3.0.0 + checksum: 7867228f83ed39477b188ea07e7ccb9b4f5320b6f73d1db93a0981b7414fa4ef72d3f80c4692c442f90fc250d9406e71d8d7ab65bb615cb334e6292b73192b89 + languageName: node + linkType: hard + +"it-all@npm:^2.0.0": + version: 2.0.1 + resolution: "it-all@npm:2.0.1" + checksum: a8426afa4b5da389f66b2540e33251b282791f5e943d65e7228cb42ec42631a958c114382132ff94c593fd24337eb8829fb3aee4c0684e6c84053eb4c0d74e8a + languageName: node + linkType: hard + +"it-all@npm:^3.0.0, it-all@npm:^3.0.1": + version: 3.0.2 + resolution: "it-all@npm:3.0.2" + checksum: 37ce90f8ee6a579aa5dd710899912f21d0b2083dad8686d8ca648b2d0c2ffdb4856770e3d004c9e9e4d0a17a9c57221ec501ada98571690ac508021e6227cdab + languageName: node + linkType: hard + +"it-batched-bytes@npm:^2.0.2": + version: 2.0.3 + resolution: "it-batched-bytes@npm:2.0.3" + dependencies: + p-defer: ^4.0.0 + uint8arraylist: ^2.4.1 + checksum: a5bd5c1f2969f1b93dbaa1fbcfe756dee270ffd262485ac1175066a235059b39c09dc9da14f93281bf6ac33381d81483d755ccfc386869ea37441555d98ba30c + languageName: node + linkType: hard + +"it-drain@npm:^3.0.1": + version: 3.0.2 + resolution: "it-drain@npm:3.0.2" + checksum: 4ad8d8a829c2f35116f07c94bf80a82381b00ee2d431d3d0556c4848013d79fe16df0d71ebf39fc51bc5b75cef296d9a5d4c8101ef1828e1ef31b67087d2e414 + languageName: node + linkType: hard + +"it-filter@npm:^3.0.0, it-filter@npm:^3.0.1": + version: 3.0.2 + resolution: "it-filter@npm:3.0.2" + dependencies: + it-peekable: ^3.0.0 + checksum: 6eea68bcd7ee75ac52dc477149a8e822da8d7626631256179601294412f22635929ee5c0b1566413c721d078ef4bfb8c94b0e2f0d093259909582c6d1d31f1f7 + languageName: node + linkType: hard + +"it-first@npm:^3.0.1": + version: 3.0.2 + resolution: "it-first@npm:3.0.2" + checksum: cb0131bed94e13e13cc02b67fb864c0995dc8deaf8e79a3b4685165f48b331f439caf21d6576e18027308ac03556e9108b517b18c34c0e11ede8a742806dccd8 + languageName: node + linkType: hard + +"it-handshake@npm:^4.1.3": + version: 4.1.3 + resolution: "it-handshake@npm:4.1.3" + dependencies: + it-pushable: ^3.1.0 + it-reader: ^6.0.1 + it-stream-types: ^2.0.1 + p-defer: ^4.0.0 + uint8arraylist: ^2.0.0 + checksum: 508a33e09da02c16ede4453a669bf23fb49d8cc96f3ac85eb36111530da9817c5ada73e7647706400f0f60865afe6164cc51bd39187b786e6736db4b0e587b2f + languageName: node + linkType: hard + +"it-length-prefixed@npm:^9.0.0, it-length-prefixed@npm:^9.0.1": + version: 9.0.1 + resolution: "it-length-prefixed@npm:9.0.1" + dependencies: + err-code: ^3.0.1 + it-stream-types: ^2.0.1 + uint8-varint: ^1.0.1 + uint8arraylist: ^2.0.0 + uint8arrays: ^4.0.2 + checksum: a431aedef3450287c9c93cbc4017aebfaf2443cae43abbbd65384a9981f58373ed6cbb505819e60e5e7ac414eefd9f3e9aa1b10cca3726d92c3f0d4671c25b40 + languageName: node + linkType: hard + +"it-length@npm:^3.0.1": + version: 3.0.2 + resolution: "it-length@npm:3.0.2" + checksum: 688565353598557dc003324fc497896f5d24795f83011d03cc3625ced09207d9e66eda69e055080d1cd3c534673f65c76e5a5c48553b53e66b58a05995b49e81 + languageName: node + linkType: hard + +"it-map@npm:^3.0.1, it-map@npm:^3.0.2": + version: 3.0.3 + resolution: "it-map@npm:3.0.3" + dependencies: + it-peekable: ^3.0.0 + checksum: bf48828a40c19d98b49f42730fa3d2821b52056dd755dcdd9b4a1c1077fe8fcd79bf8ae810429e4a82d6cac5b2439e5f3aae8a3c7353c9b5c51ce14d038d9858 + languageName: node + linkType: hard + +"it-merge@npm:^3.0.0": + version: 3.0.1 + resolution: "it-merge@npm:3.0.1" + dependencies: + it-pushable: ^3.1.0 + checksum: 8401b0394868cd02ccd988687a641b2a250668de1a818d7a867650621fb77e6dcd4b041972f738293ab65880b0a3a16b45d94c8b7b42d6b434430c817eee7565 + languageName: node + linkType: hard + +"it-pair@npm:^2.0.2": + version: 2.0.6 + resolution: "it-pair@npm:2.0.6" + dependencies: + it-stream-types: ^2.0.1 + p-defer: ^4.0.0 + checksum: aadd87d4f6c4e3281dc6d77758252c32c55f8646b19d87b8273437a48ecd2f37aa8b3363aeeddc161466005df0f63ff3515245fe835c4a38d6a22988ea16285a + languageName: node + linkType: hard + +"it-parallel@npm:^3.0.0": + version: 3.0.3 + resolution: "it-parallel@npm:3.0.3" + dependencies: + p-defer: ^4.0.0 + checksum: 2326fe2d15134d59dede916b8b72a83092f79b356537921ede24f0b0d95616f2916d8bf1ebd1af6db5326303af2ac654c64dc144df64b8f48b57a95d47c1be5f + languageName: node + linkType: hard + +"it-pb-stream@npm:^4.0.1": + version: 4.0.1 + resolution: "it-pb-stream@npm:4.0.1" + dependencies: + err-code: ^3.0.1 + it-length-prefixed: ^9.0.0 + it-pushable: ^3.1.2 + it-stream-types: ^2.0.1 + protons-runtime: ^5.0.0 + uint8-varint: ^1.0.6 + uint8arraylist: ^2.0.0 + checksum: 2dbac8538ade4345c1b9ee4305d3d832532629702a18aff9b3e4e114f4dfdae523266bec3f8f89157af617a062005f9d57ba7415fc943f4e9c953366ce6d2e0b + languageName: node + linkType: hard + +"it-peekable@npm:^3.0.0": + version: 3.0.1 + resolution: "it-peekable@npm:3.0.1" + checksum: e327caf50bc205672b4133040e4c580cb39f6a8691565f420941ac5b7877ad1ca47c53ecf1ea62e4716abe36786b1cf07f2aea663eda6779fbf0d7a7cb0fbfdc + languageName: node + linkType: hard + +"it-pipe@npm:^3.0.0, it-pipe@npm:^3.0.1": + version: 3.0.1 + resolution: "it-pipe@npm:3.0.1" + dependencies: + it-merge: ^3.0.0 + it-pushable: ^3.1.2 + it-stream-types: ^2.0.1 + checksum: 4f6a67f32e26a965c2908be55f567ac6f6cc813ed3d29637dd7e1b3124045b5cef9d138626d4e60e5b3f1e0e5f9fd5fcba28609b24ebef761276ca4478d34089 + languageName: node + linkType: hard + +"it-pushable@npm:^3.0.0, it-pushable@npm:^3.1.0, it-pushable@npm:^3.1.2, it-pushable@npm:^3.1.3": + version: 3.1.3 + resolution: "it-pushable@npm:3.1.3" + checksum: 128dd12039af7067e7a27eccef52c3d00f32607bf8d1aeef5e62e8f84fe8f9a131afe5d8a6b455ae797bfbfb4cc942a3db100f362ca9698c61dd40478ddefc6b languageName: node linkType: hard -"istanbul-lib-source-maps@npm:^4.0.0": - version: 4.0.1 - resolution: "istanbul-lib-source-maps@npm:4.0.1" +"it-reader@npm:^6.0.1": + version: 6.0.4 + resolution: "it-reader@npm:6.0.4" dependencies: - debug: ^4.1.1 - istanbul-lib-coverage: ^3.0.0 - source-map: ^0.6.1 - checksum: 21ad3df45db4b81852b662b8d4161f6446cd250c1ddc70ef96a585e2e85c26ed7cd9c2a396a71533cfb981d1a645508bc9618cae431e55d01a0628e7dec62ef2 + it-stream-types: ^2.0.1 + uint8arraylist: ^2.0.0 + checksum: c2f89362bd7379814bcfe324361ee88e958d311a1f1c2d3cd5395e16517fbafb626b87d3a8aefb7a9d649e719c892f5e3f12aee214d03b0c28a983bb83b249e1 languageName: node linkType: hard -"istanbul-reports@npm:^3.1.3": - version: 3.1.5 - resolution: "istanbul-reports@npm:3.1.5" +"it-sort@npm:^3.0.1": + version: 3.0.2 + resolution: "it-sort@npm:3.0.2" dependencies: - html-escaper: ^2.0.0 - istanbul-lib-report: ^3.0.0 - checksum: 7867228f83ed39477b188ea07e7ccb9b4f5320b6f73d1db93a0981b7414fa4ef72d3f80c4692c442f90fc250d9406e71d8d7ab65bb615cb334e6292b73192b89 + it-all: ^3.0.0 + checksum: d45bb64413d949e1c7c459ad1b4418f14460959d5b73c830368ac18722277c8dcbc606740343ca87441c7c136838b7335a16f9174f0dd8ef03cdc80f8bcfdfbb + languageName: node + linkType: hard + +"it-stream-types@npm:^2.0.1": + version: 2.0.1 + resolution: "it-stream-types@npm:2.0.1" + checksum: 14c5a13dbef08ae3a9b824ae9d05a8f3eb25ef46994ede25f763472f8367498395bd4be13c88b93846fd4b56c9a4763beb268ef8fa26575b17ef8f9327f9bf77 + languageName: node + linkType: hard + +"it-take@npm:^3.0.1": + version: 3.0.2 + resolution: "it-take@npm:3.0.2" + checksum: 50a5449efa0832cd55878b7492f536b558861181786c7a95fb59d93c761646bc4bbc9dd46fd28a5b574b7349fa4caf028bbf66e3a86630473cc5907b811952a3 languageName: node linkType: hard @@ -5295,6 +6732,13 @@ __metadata: languageName: node linkType: hard +"jsbn@npm:1.1.0": + version: 1.1.0 + resolution: "jsbn@npm:1.1.0" + checksum: 944f924f2bd67ad533b3850eee47603eed0f6ae425fd1ee8c760f477e8c34a05f144c1bd4f5a5dd1963141dc79a2c55f89ccc5ab77d039e7077f3ad196b64965 + languageName: node + linkType: hard + "jsdoc-type-pratt-parser@npm:~4.0.0": version: 4.0.0 resolution: "jsdoc-type-pratt-parser@npm:4.0.0" @@ -5473,6 +6917,37 @@ __metadata: languageName: node linkType: hard +"koa@npm:^2.14.2": + version: 2.14.2 + resolution: "koa@npm:2.14.2" + dependencies: + accepts: ^1.3.5 + cache-content-type: ^1.0.0 + content-disposition: ~0.5.2 + content-type: ^1.0.4 + cookies: ~0.8.0 + debug: ^4.3.2 + delegates: ^1.0.0 + depd: ^2.0.0 + destroy: ^1.0.4 + encodeurl: ^1.0.2 + escape-html: ^1.0.3 + fresh: ~0.5.2 + http-assert: ^1.3.0 + http-errors: ^1.6.3 + is-generator-function: ^1.0.7 + koa-compose: ^4.1.0 + koa-convert: ^2.0.0 + on-finished: ^2.3.0 + only: ~0.0.2 + parseurl: ^1.3.2 + statuses: ^1.5.0 + type-is: ^1.6.16 + vary: ^1.1.2 + checksum: 17fe3b8f5e0b4759004a942cc6ba2a9507299943a697dff9766b85f41f45caed4077ca2645ac9ad254d3359fffedfc4c9ebdd7a70493e5df8cdfac159a8ee835 + languageName: node + linkType: hard + "level-concat-iterator@npm:^3.0.0": version: 3.1.0 resolution: "level-concat-iterator@npm:3.1.0" @@ -5549,6 +7024,78 @@ __metadata: languageName: node linkType: hard +"libp2p@npm:^0.45.1": + version: 0.45.1 + resolution: "libp2p@npm:0.45.1" + dependencies: + "@achingbrain/nat-port-mapper": ^1.0.3 + "@libp2p/crypto": ^1.0.4 + "@libp2p/interface-address-manager": ^3.0.0 + "@libp2p/interface-connection": ^5.0.0 + "@libp2p/interface-connection-encrypter": ^4.0.0 + "@libp2p/interface-connection-gater": ^3.0.0 + "@libp2p/interface-connection-manager": ^3.0.0 + "@libp2p/interface-content-routing": ^2.1.0 + "@libp2p/interface-keychain": ^2.0.4 + "@libp2p/interface-libp2p": ^3.1.0 + "@libp2p/interface-metrics": ^4.0.0 + "@libp2p/interface-peer-discovery": ^2.0.0 + "@libp2p/interface-peer-id": ^2.0.1 + "@libp2p/interface-peer-info": ^1.0.3 + "@libp2p/interface-peer-routing": ^1.1.0 + "@libp2p/interface-peer-store": ^2.0.3 + "@libp2p/interface-pubsub": ^4.0.0 + "@libp2p/interface-record": ^2.0.6 + "@libp2p/interface-registrar": ^2.0.3 + "@libp2p/interface-stream-muxer": ^4.0.0 + "@libp2p/interface-transport": ^4.0.0 + "@libp2p/interfaces": ^3.2.0 + "@libp2p/keychain": ^2.0.0 + "@libp2p/logger": ^2.0.1 + "@libp2p/multistream-select": ^3.1.8 + "@libp2p/peer-collections": ^3.0.0 + "@libp2p/peer-id": ^2.0.0 + "@libp2p/peer-id-factory": ^2.0.0 + "@libp2p/peer-record": ^5.0.0 + "@libp2p/peer-store": ^8.1.0 + "@libp2p/topology": ^4.0.1 + "@libp2p/tracked-map": ^3.0.0 + "@libp2p/utils": ^3.0.10 + "@multiformats/mafmt": ^12.0.0 + "@multiformats/multiaddr": ^12.0.0 + abortable-iterator: ^5.0.1 + any-signal: ^4.1.1 + datastore-core: ^9.0.0 + interface-datastore: ^8.0.0 + it-all: ^3.0.1 + it-drain: ^3.0.1 + it-filter: ^3.0.1 + it-first: ^3.0.1 + it-handshake: ^4.1.3 + it-length-prefixed: ^9.0.1 + it-map: ^3.0.2 + it-merge: ^3.0.0 + it-pair: ^2.0.2 + it-parallel: ^3.0.0 + it-pb-stream: ^4.0.1 + it-pipe: ^3.0.1 + it-stream-types: ^2.0.1 + merge-options: ^3.0.4 + multiformats: ^11.0.0 + p-defer: ^4.0.0 + p-queue: ^7.3.4 + p-retry: ^5.0.0 + private-ip: ^3.0.0 + protons-runtime: ^5.0.0 + rate-limiter-flexible: ^2.3.11 + uint8arraylist: ^2.3.2 + uint8arrays: ^4.0.2 + wherearewe: ^2.0.0 + xsalsa20: ^1.1.0 + checksum: f937982612365eda21e818920bd8367cca72bcb6b1166ea3759d0ec48aaae833fb8bbe7d86d1139ede6a7e1c5abfb526800901775491a6ded1bf65100a6d4d65 + languageName: node + linkType: hard + "lines-and-columns@npm:^1.1.6": version: 1.2.4 resolution: "lines-and-columns@npm:1.2.4" @@ -5663,13 +7210,30 @@ __metadata: languageName: node linkType: hard -"lodash@npm:^4.17.21": +"lodash@npm:^4.17.21, lodash@npm:^4.17.4": version: 4.17.21 resolution: "lodash@npm:4.17.21" checksum: eb835a2e51d381e561e508ce932ea50a8e5a68f4ebdd771ea240d3048244a8d13658acbd502cd4829768c56f2e16bdd4340b9ea141297d472517b83868e677f7 languageName: node linkType: hard +"long@npm:^5.0.0": + version: 5.2.3 + resolution: "long@npm:5.2.3" + checksum: 885ede7c3de4facccbd2cacc6168bae3a02c3e836159ea4252c87b6e34d40af819824b2d4edce330bfb5c4d6e8ce3ec5864bdcf9473fa1f53a4f8225860e5897 + languageName: node + linkType: hard + +"longbits@npm:^1.1.0": + version: 1.1.0 + resolution: "longbits@npm:1.1.0" + dependencies: + byte-access: ^1.0.1 + uint8arraylist: ^2.0.0 + checksum: 7f8ec8ddef64b160da22c31875fa549b40a4cff626c948c423dc2cb73c0d85a5c6a13ce3a611b5229fcf65dd6ffb4fcd1d5eaef61458570194172ad485112521 + languageName: node + linkType: hard + "lru-cache@npm:^5.1.1": version: 5.1.1 resolution: "lru-cache@npm:5.1.1" @@ -5838,6 +7402,15 @@ __metadata: languageName: node linkType: hard +"merge-options@npm:^3.0.4": + version: 3.0.4 + resolution: "merge-options@npm:3.0.4" + dependencies: + is-plain-obj: ^2.1.0 + checksum: d86ddb3dd6e85d558dbf25dc944f3527b6bacb944db3fdda6e84a3f59c4e4b85231095f58b835758b9a57708342dee0f8de0dffa352974a48221487fe9f4584f + languageName: node + linkType: hard + "merge-stream@npm:^2.0.0": version: 2.0.0 resolution: "merge-stream@npm:2.0.0" @@ -6039,6 +7612,18 @@ __metadata: languageName: node linkType: hard +"mortice@npm:^3.0.1": + version: 3.0.1 + resolution: "mortice@npm:3.0.1" + dependencies: + nanoid: ^4.0.0 + observable-webworkers: ^2.0.1 + p-queue: ^7.2.0 + p-timeout: ^6.0.0 + checksum: a6b23c98bdea9a18f25d0431424f0c9df54e6af5f2ebe9d5752b50bccc1fa19e2559007d813f7fc14171a9253ebc60c8702f0cda51a57c8fd0ae420abde83761 + languageName: node + linkType: hard + "ms@npm:2.1.2": version: 2.1.2 resolution: "ms@npm:2.1.2" @@ -6046,13 +7631,41 @@ __metadata: languageName: node linkType: hard -"ms@npm:^2.0.0": +"ms@npm:^2.0.0, ms@npm:^2.1.1": version: 2.1.3 resolution: "ms@npm:2.1.3" checksum: aa92de608021b242401676e35cfa5aa42dd70cbdc082b916da7fb925c542173e36bce97ea3e804923fe92c0ad991434e4a38327e15a1b5b5f945d66df615ae6d languageName: node linkType: hard +"multiaddr@npm:^10.0.1": + version: 10.0.1 + resolution: "multiaddr@npm:10.0.1" + dependencies: + dns-over-http-resolver: ^1.2.3 + err-code: ^3.0.1 + is-ip: ^3.1.0 + multiformats: ^9.4.5 + uint8arrays: ^3.0.0 + varint: ^6.0.0 + checksum: d53aaf7efd52ee5e6413ef36ececd29239ceb5c1f048c1fa9b820442226dc232067312d25e509a2571a14047465fb934dd35029c7f3166f4d02d13e3c501925d + languageName: node + linkType: hard + +"multiformats@npm:^11.0.0": + version: 11.0.2 + resolution: "multiformats@npm:11.0.2" + checksum: e587bbe709f29e42ae3c22458c960070269027d962183afc49a83b8ba26c31525e81ce2ac71082a52ba0a75e9aed4d0d044cac68d32656fdcd5cd340fb367fac + languageName: node + linkType: hard + +"multiformats@npm:^9.4.2, multiformats@npm:^9.4.5": + version: 9.9.0 + resolution: "multiformats@npm:9.9.0" + checksum: d3e8c1be400c09a014f557ea02251a2710dbc9fca5aa32cc702ff29f636c5471e17979f30bdcb0a9cbb556f162a8591dc2e1219c24fc21394a56115b820bb84e + languageName: node + linkType: hard + "nanoid@npm:^3.3.4": version: 3.3.6 resolution: "nanoid@npm:3.3.6" @@ -6062,6 +7675,15 @@ __metadata: languageName: node linkType: hard +"nanoid@npm:^4.0.0": + version: 4.0.2 + resolution: "nanoid@npm:4.0.2" + bin: + nanoid: bin/nanoid.js + checksum: 747c399cea4664dd0be1d0ec498ffd1ef8f1f5221676fc8b577e3f46f66d9afcddb9595d63d19a2e78d0bc6cc33984f65e66bf1682c850b9e26288883d96b53f + languageName: node + linkType: hard + "napi-macros@npm:~2.0.0": version: 2.0.0 resolution: "napi-macros@npm:2.0.0" @@ -6069,6 +7691,24 @@ __metadata: languageName: node linkType: hard +"native-fetch@npm:^3.0.0": + version: 3.0.0 + resolution: "native-fetch@npm:3.0.0" + peerDependencies: + node-fetch: "*" + checksum: eec8cc78d6da4d0f3f56055e3e557473ac86dd35fd40053ea268d644af7b20babc891d2b53ef821b77ed2428265f60b85e49d754c555de89bfa071a743b853bb + languageName: node + linkType: hard + +"native-fetch@npm:^4.0.2": + version: 4.0.2 + resolution: "native-fetch@npm:4.0.2" + peerDependencies: + undici: "*" + checksum: 11e6d075aa03d40665a5fc438c56b535622fb4ee98eb2b035277c5ba47733cb4c7bc3ddb45e5ab8154869b509fc18ca1c0188ab271139ae89db14f9f552fc064 + languageName: node + linkType: hard + "natural-compare-lite@npm:^1.4.0": version: 1.4.0 resolution: "natural-compare-lite@npm:1.4.0" @@ -6090,6 +7730,13 @@ __metadata: languageName: node linkType: hard +"netmask@npm:^2.0.2": + version: 2.0.2 + resolution: "netmask@npm:2.0.2" + checksum: c65cb8d3f7ea5669edddb3217e4c96910a60d0d9a4b52d9847ff6b28b2d0277cd8464eee0ef85133cdee32605c57940cacdd04a9a019079b091b6bba4cb0ec22 + languageName: node + linkType: hard + "node-cleanup@npm:^2.1.2": version: 2.1.2 resolution: "node-cleanup@npm:2.1.2" @@ -6111,6 +7758,13 @@ __metadata: languageName: node linkType: hard +"node-forge@npm:^1.1.0": + version: 1.3.1 + resolution: "node-forge@npm:1.3.1" + checksum: 08fb072d3d670599c89a1704b3e9c649ff1b998256737f0e06fbd1a5bf41cae4457ccaee32d95052d80bbafd9ffe01284e078c8071f0267dc9744e51c5ed42a9 + languageName: node + linkType: hard + "node-gyp-build@npm:^4.3.0": version: 4.6.0 resolution: "node-gyp-build@npm:4.6.0" @@ -6214,6 +7868,13 @@ __metadata: languageName: node linkType: hard +"observable-webworkers@npm:^2.0.1": + version: 2.0.1 + resolution: "observable-webworkers@npm:2.0.1" + checksum: ce278b74b62da92afc48f9ba782aa4cd794b95c89717d7e7ca2e80fe72c2f853abcd4d190eb198d222ff3eaa91f8a229a490bbc0194063f996f312a07985cff5 + languageName: node + linkType: hard + "on-finished@npm:^2.3.0": version: 2.4.1 resolution: "on-finished@npm:2.4.1" @@ -6262,6 +7923,13 @@ __metadata: languageName: node linkType: hard +"p-defer@npm:^4.0.0": + version: 4.0.0 + resolution: "p-defer@npm:4.0.0" + checksum: 646c9e86e62d2299ee9e8722b9857c9a2918afb8626c4eaf072d956de0d5b33c1cb132e5754516c923fc691eb33aa216755e168f848b045c1279186c8e2d852f + languageName: node + linkType: hard + "p-limit@npm:^2.2.0": version: 2.3.0 resolution: "p-limit@npm:2.3.0" @@ -6307,6 +7975,40 @@ __metadata: languageName: node linkType: hard +"p-queue@npm:^7.2.0, p-queue@npm:^7.3.4": + version: 7.3.4 + resolution: "p-queue@npm:7.3.4" + dependencies: + eventemitter3: ^4.0.7 + p-timeout: ^5.0.2 + checksum: a21b8a4dd75f64a4988e4468cc344d1b45132506ddd2c771932d3de446d108ee68713b629e0d3f0809c227bc10eafc613edde6ae741d9f60db89b6031e40921c + languageName: node + linkType: hard + +"p-retry@npm:^5.0.0": + version: 5.1.2 + resolution: "p-retry@npm:5.1.2" + dependencies: + "@types/retry": 0.12.1 + retry: ^0.13.1 + checksum: f063c08b1adc3cf7c01de01eb2dbda841970229f9f229c5167ebf4e2080d8a38b1f4e6eccefac74bca97cfaf4436d0a0eeb0b551175b26bc8b3116195f61bba8 + languageName: node + linkType: hard + +"p-timeout@npm:^5.0.2": + version: 5.1.0 + resolution: "p-timeout@npm:5.1.0" + checksum: f5cd4e17301ff1ff1d8dbf2817df0ad88c6bba99349fc24d8d181827176ad4f8aca649190b8a5b1a428dfd6ddc091af4606835d3e0cb0656e04045da5c9e270c + languageName: node + linkType: hard + +"p-timeout@npm:^6.0.0": + version: 6.1.1 + resolution: "p-timeout@npm:6.1.1" + checksum: 359613bbb7b98ee98dd0f7c6fcd7ef9671134d6554576cbda8f12867f867833df8748695067de4ed476f70572b7a461554cb8cddb05a1bf9d7e86dafd2170610 + languageName: node + linkType: hard + "p-try@npm:^2.0.0": version: 2.2.0 resolution: "p-try@npm:2.2.0" @@ -6423,6 +8125,13 @@ __metadata: languageName: node linkType: hard +"platform@npm:^1.3.3": + version: 1.3.6 + resolution: "platform@npm:1.3.6" + checksum: 6f472a09c61d418c7e26c1c16d0bdc029549d512dbec6526216a1e59ec68100d07007d0097dcba69dddad883d6f2a83361b4bdfe0094a3d9a2af24158643d85e + languageName: node + linkType: hard + "postcss@npm:^8.4.21": version: 8.4.21 resolution: "postcss@npm:8.4.21" @@ -6461,6 +8170,25 @@ __metadata: languageName: node linkType: hard +"private-ip@npm:^3.0.0": + version: 3.0.0 + resolution: "private-ip@npm:3.0.0" + dependencies: + "@chainsafe/is-ip": ^2.0.1 + ip-regex: ^5.0.0 + ipaddr.js: ^2.0.1 + netmask: ^2.0.2 + checksum: 599e9fbd229176b5a950242598aad2accb498af626b7c99083ed604a40069ac89da08d9f90d213f521c7ad2f64205b68ebe65a3e4c76d16fdd18c287afe6f979 + languageName: node + linkType: hard + +"progress-events@npm:^1.0.0": + version: 1.0.0 + resolution: "progress-events@npm:1.0.0" + checksum: 41e5fb20c21cf2c5a6ad0501840120fc793f83216873c8be5ca006a0c904ec53cefc6bdb0b79bed82926c7b4541f590dc3c97ff55bfbaa126dba2cfec8e0976e + languageName: node + linkType: hard + "promise-inflight@npm:^1.0.1": version: 1.0.1 resolution: "promise-inflight@npm:1.0.1" @@ -6468,6 +8196,15 @@ __metadata: languageName: node linkType: hard +"promise-readable@npm:^6.0.0": + version: 6.0.0 + resolution: "promise-readable@npm:6.0.0" + dependencies: + core-js: ^3.6.5 + checksum: 2e93f83457011ca721e7a55b9bb5045f2bf75dc1435d1912a2f20db1ba309e3343364eb68525e87b4c8b27baa0ab04619d0a3c064e944584260b620ebcbb1bef + languageName: node + linkType: hard + "promise-retry@npm:^2.0.1": version: 2.0.1 resolution: "promise-retry@npm:2.0.1" @@ -6488,6 +8225,38 @@ __metadata: languageName: node linkType: hard +"protobufjs@npm:^7.0.0": + version: 7.2.3 + resolution: "protobufjs@npm:7.2.3" + dependencies: + "@protobufjs/aspromise": ^1.1.2 + "@protobufjs/base64": ^1.1.2 + "@protobufjs/codegen": ^2.0.4 + "@protobufjs/eventemitter": ^1.1.0 + "@protobufjs/fetch": ^1.1.0 + "@protobufjs/float": ^1.0.2 + "@protobufjs/inquire": ^1.1.0 + "@protobufjs/path": ^1.1.2 + "@protobufjs/pool": ^1.1.0 + "@protobufjs/utf8": ^1.1.0 + "@types/node": ">=13.7.0" + long: ^5.0.0 + checksum: 9afa6de5fced0139a5180c063718508fac3ea734a9f1aceb99712367b15473a83327f91193f16b63540f9112b09a40912f5f0441a9b0d3f3c6a1c7f707d78249 + languageName: node + linkType: hard + +"protons-runtime@npm:^5.0.0": + version: 5.0.0 + resolution: "protons-runtime@npm:5.0.0" + dependencies: + protobufjs: ^7.0.0 + uint8arraylist: ^2.4.3 + peerDependencies: + uint8arraylist: ^2.3.2 + checksum: 3efbbdb8d7a64cbc49ef6f9a9dec69e2520b61d181475e247a5b9a6f514afbed2b94bf2cf58a8393dbb684953a0b2d87039cd2e618362e0749390c0a9d2f79ab + languageName: node + linkType: hard + "ps-tree@npm:^1.2.0": version: 1.2.0 resolution: "ps-tree@npm:1.2.0" @@ -6536,6 +8305,13 @@ __metadata: languageName: node linkType: hard +"rate-limiter-flexible@npm:^2.3.11, rate-limiter-flexible@npm:^2.3.9": + version: 2.4.1 + resolution: "rate-limiter-flexible@npm:2.4.1" + checksum: 5eea3ffbb6a11f634edd8b9575815c2bf239a8becdfdc82c4183cad92025e853913972a2b2f2d45c16e81aea1a3451fbad8da76dee1ba0e4549c22a0ba58c50f + languageName: node + linkType: hard + "raw-body@npm:^2.3.3": version: 2.5.2 resolution: "raw-body@npm:2.5.2" @@ -6589,6 +8365,15 @@ __metadata: languageName: node linkType: hard +"receptacle@npm:^1.3.2": + version: 1.3.2 + resolution: "receptacle@npm:1.3.2" + dependencies: + ms: ^2.1.1 + checksum: 7c5011f19e6ddcb759c1e6756877cee3c9eb78fbd1278eca4572d75f74993f0ccdc1e5f7761de6e682dff5344ee94f7a69bc492e2e8eb81d8777774a2399ce9c + languageName: node + linkType: hard + "redent@npm:^3.0.0": version: 3.0.0 resolution: "redent@npm:3.0.0" @@ -6696,6 +8481,13 @@ __metadata: languageName: node linkType: hard +"retry@npm:^0.13.1": + version: 0.13.1 + resolution: "retry@npm:0.13.1" + checksum: 47c4d5be674f7c13eee4cfe927345023972197dbbdfba5d3af7e461d13b44de1bfd663bfc80d2f601f8ef3fc8164c16dd99655a221921954a65d044a2fc1233b + languageName: node + linkType: hard + "reusify@npm:^1.0.4": version: 1.0.4 resolution: "reusify@npm:1.0.4" @@ -6770,6 +8562,22 @@ __metadata: languageName: node linkType: hard +"sanitize-filename@npm:^1.6.3": + version: 1.6.3 + resolution: "sanitize-filename@npm:1.6.3" + dependencies: + truncate-utf8-bytes: ^1.0.0 + checksum: aa733c012b7823cf65730603cf3b503c641cee6b239771d3164ca482f22d81a50e434a713938d994071db18e4202625669cc56bccc9d13d818b4c983b5f47fde + languageName: node + linkType: hard + +"sax@npm:>=0.6.0": + version: 1.2.4 + resolution: "sax@npm:1.2.4" + checksum: d3df7d32b897a2c2f28e941f732c71ba90e27c24f62ee918bd4d9a8cfb3553f2f81e5493c7f0be94a11c1911b643a9108f231dd6f60df3fa9586b5d2e3e9e1fe + languageName: node + linkType: hard + "semver-match@npm:0.1.1": version: 0.1.1 resolution: "semver-match@npm:0.1.1" @@ -7029,6 +8837,13 @@ __metadata: languageName: node linkType: hard +"sprintf-js@npm:1.1.2": + version: 1.1.2 + resolution: "sprintf-js@npm:1.1.2" + checksum: d4bb46464632b335e5faed381bd331157e0af64915a98ede833452663bc672823db49d7531c32d58798e85236581fb7342fd0270531ffc8f914e186187bf1c90 + languageName: node + linkType: hard + "sprintf-js@npm:~1.0.2": version: 1.0.3 resolution: "sprintf-js@npm:1.0.3" @@ -7077,6 +8892,22 @@ __metadata: languageName: node linkType: hard +"stream-to-it@npm:^0.2.2": + version: 0.2.4 + resolution: "stream-to-it@npm:0.2.4" + dependencies: + get-iterator: ^1.0.2 + checksum: 0725dd8ddb889829cab70b81a883d5a09cd34272ccd44fad195de9fb900a8588fbf801490b6418ae5e234c128743ad829c50cfcd6686fab3b50bb6e76d59238c + languageName: node + linkType: hard + +"streamsearch@npm:^1.1.0": + version: 1.1.0 + resolution: "streamsearch@npm:1.1.0" + checksum: 1cce16cea8405d7a233d32ca5e00a00169cc0e19fbc02aa839959985f267335d435c07f96e5e0edd0eadc6d39c98d5435fb5bbbdefc62c41834eadc5622ad942 + languageName: node + linkType: hard + "string-argv@npm:^0.3.1": version: 0.3.1 resolution: "string-argv@npm:0.3.1" @@ -7314,6 +9145,15 @@ __metadata: languageName: node linkType: hard +"truncate-utf8-bytes@npm:^1.0.0": + version: 1.0.2 + resolution: "truncate-utf8-bytes@npm:1.0.2" + dependencies: + utf8-byte-length: ^1.0.1 + checksum: ad097314709ea98444ad9c80c03aac8da805b894f37ceb5685c49ad297483afe3a5ec9572ebcaff699dda72b6cd447a2ba2a3fd10e96c2628cd16d94abeb328a + languageName: node + linkType: hard + "ts-dedent@npm:^2.2.0": version: 2.2.0 resolution: "ts-dedent@npm:2.2.0" @@ -7546,6 +9386,54 @@ __metadata: languageName: node linkType: hard +"uint8-varint@npm:^1.0.1, uint8-varint@npm:^1.0.2, uint8-varint@npm:^1.0.6": + version: 1.0.6 + resolution: "uint8-varint@npm:1.0.6" + dependencies: + byte-access: ^1.0.0 + longbits: ^1.1.0 + uint8arraylist: ^2.0.0 + uint8arrays: ^4.0.2 + checksum: 9d97312b9d032cc20976c3d9ea9e7548bc761a5f4483156e349d5dcd9ffed7c71f597dc564591cbe93816ee39d258751dee2efa6b43859d3662c83ad1cf6cd1a + languageName: node + linkType: hard + +"uint8arraylist@npm:^2.0.0, uint8arraylist@npm:^2.1.0, uint8arraylist@npm:^2.1.1, uint8arraylist@npm:^2.3.1, uint8arraylist@npm:^2.3.2, uint8arraylist@npm:^2.4.1, uint8arraylist@npm:^2.4.3": + version: 2.4.3 + resolution: "uint8arraylist@npm:2.4.3" + dependencies: + uint8arrays: ^4.0.2 + checksum: 95225fe2b8f6a4d8919b6c8e3dcff32ced35a08a53efaef757a50bc0082fb5b91f09e7e46f0d1c234243899930f7cb02ef9eb44b97ce03e2381d416de15e16c9 + languageName: node + linkType: hard + +"uint8arrays@npm:^3.0.0": + version: 3.1.1 + resolution: "uint8arrays@npm:3.1.1" + dependencies: + multiformats: ^9.4.2 + checksum: b93b6c3f0a526b116799f3a3409bd4b5d5553eb3e73e485998ece7974742254fbc0d2f7988dd21ac86c4b974552f45d9ae9cf9cba9647e529f8eb1fdd2ed84d0 + languageName: node + linkType: hard + +"uint8arrays@npm:^4.0.2, uint8arrays@npm:^4.0.3": + version: 4.0.3 + resolution: "uint8arrays@npm:4.0.3" + dependencies: + multiformats: ^11.0.0 + checksum: 1bf9ee4fbca26456868520af04af74b278e4fddc21b25b17cf294cea7a9905b6a6d16a3e75ccf8498195fa0d94e8abe2445ba49798fc65cc71a951b28ba0f05f + languageName: node + linkType: hard + +"undici@npm:^5.12.0": + version: 5.22.0 + resolution: "undici@npm:5.22.0" + dependencies: + busboy: ^1.6.0 + checksum: 8dc55240a60ae7680798df344e8f46ad0f872ed0fa434fb94cc4fd2b5b2f8053bdf11994d15902999d3880f9bf7cd875a2e90883d2702bf0f366dacd9cbf3fc6 + languageName: node + linkType: hard + "unique-filename@npm:^2.0.0": version: 2.0.1 resolution: "unique-filename@npm:2.0.1" @@ -7608,6 +9496,13 @@ __metadata: languageName: node linkType: hard +"utf8-byte-length@npm:^1.0.1": + version: 1.0.4 + resolution: "utf8-byte-length@npm:1.0.4" + checksum: f188ca076ec094d58e7009fcc32623c5830c7f0f3e15802bfa4fdd1e759454a481fc4ac05e0fa83b7736e77af628a9ee0e57dcc89683d688fde3811473e42143 + languageName: node + linkType: hard + "util-deprecate@npm:^1.0.1": version: 1.0.2 resolution: "util-deprecate@npm:1.0.2" @@ -7643,6 +9538,13 @@ __metadata: languageName: node linkType: hard +"varint@npm:^6.0.0": + version: 6.0.0 + resolution: "varint@npm:6.0.0" + checksum: 7684113c9d497c01e40396e50169c502eb2176203219b96e1c5ac965a3e15b4892bd22b7e48d87148e10fffe638130516b6dbeedd0efde2b2d0395aa1772eea7 + languageName: node + linkType: hard + "vary@npm:^1.1.2": version: 1.1.2 resolution: "vary@npm:1.1.2" @@ -7650,9 +9552,9 @@ __metadata: languageName: node linkType: hard -"viem@npm:^0.3.13": - version: 0.3.13 - resolution: "viem@npm:0.3.13" +"viem@npm:^0.3.14": + version: 0.3.14 + resolution: "viem@npm:0.3.14" dependencies: "@adraffy/ens-normalize": 1.9.0 "@noble/curves": 0.9.0 @@ -7663,7 +9565,7 @@ __metadata: abitype: 0.7.1 isomorphic-ws: 5.0.0 ws: 8.12.0 - checksum: 4a1fd40c0b910cf3abdba796c31288b221a6de9f79a9b0417e71ea92d7c2fd1ceb5bdb66ba45c1c6c8bdfa5470dcdd598140ad867cc229eddeccecc822fc8f11 + checksum: 4f58e1aef7ab39de27a3fc6b8fea6aa73ac5a2902a9c0efff0c1b1cd76fad2e7ff1747e9a2d6e065ca87e14e6d5aeb173bdc9bb40705440a93cec565c28b38c3 languageName: node linkType: hard @@ -7745,6 +9647,15 @@ __metadata: languageName: node linkType: hard +"wherearewe@npm:^2.0.0": + version: 2.0.1 + resolution: "wherearewe@npm:2.0.1" + dependencies: + is-electron: ^2.2.0 + checksum: 811475dba3bc4ce32ba3275cb5615840c67ab6874b0d5a191ed317bd09bfedf76a677f0469520ea3304878e66e5251c7fd3bb87729621bc460c0adab508590a8 + languageName: node + linkType: hard + "which@npm:^2.0.1, which@npm:^2.0.2": version: 2.0.2 resolution: "which@npm:2.0.2" @@ -7830,6 +9741,30 @@ __metadata: languageName: node linkType: hard +"xml2js@npm:^0.5.0": + version: 0.5.0 + resolution: "xml2js@npm:0.5.0" + dependencies: + sax: ">=0.6.0" + xmlbuilder: ~11.0.0 + checksum: 1aa71d62e5bc2d89138e3929b9ea46459157727759cbc62ef99484b778641c0cd21fb637696c052d901a22f82d092a3e740a16b4ce218e81ac59b933535124ea + languageName: node + linkType: hard + +"xmlbuilder@npm:~11.0.0": + version: 11.0.1 + resolution: "xmlbuilder@npm:11.0.1" + checksum: 7152695e16f1a9976658215abab27e55d08b1b97bca901d58b048d2b6e106b5af31efccbdecf9b07af37c8377d8e7e821b494af10b3a68b0ff4ae60331b415b0 + languageName: node + linkType: hard + +"xsalsa20@npm:^1.1.0": + version: 1.2.0 + resolution: "xsalsa20@npm:1.2.0" + checksum: 488fac04877d18cef54a49325277470685ba410e1b2fadc2108ae91a04ca474fdae682789bf13eb800e56e5a7017bb11187261f64253ea990281e86c59319617 + languageName: node + linkType: hard + "y18n@npm:^5.0.5": version: 5.0.8 resolution: "y18n@npm:5.0.8"