From 140e1d4f022d2a785796ec1c0abccf943c923d3b Mon Sep 17 00:00:00 2001 From: PhilWindle Date: Wed, 22 May 2024 08:17:12 +0000 Subject: [PATCH 01/46] Reinstate cli --- yarn-project/cli/.eslintrc.cjs | 1 + yarn-project/cli/CHANGELOG.md | 171 ++++++ yarn-project/cli/Dockerfile | 10 + yarn-project/cli/README.md | 450 ++++++++++++++++ yarn-project/cli/aztec-cli-dest | 3 + yarn-project/cli/package.json | 79 +++ yarn-project/cli/src/bin/index.ts | 24 + yarn-project/cli/src/client.test.ts | 31 ++ yarn-project/cli/src/client.ts | 68 +++ yarn-project/cli/src/cmds/add_contract.ts | 39 ++ yarn-project/cli/src/cmds/add_note.ts | 22 + yarn-project/cli/src/cmds/block_number.ts | 9 + yarn-project/cli/src/cmds/call.ts | 32 ++ yarn-project/cli/src/cmds/check_deploy.ts | 19 + yarn-project/cli/src/cmds/compute_selector.ts | 7 + yarn-project/cli/src/cmds/create_account.ts | 38 ++ yarn-project/cli/src/cmds/deploy.ts | 82 +++ .../cli/src/cmds/deploy_l1_contracts.ts | 22 + .../cli/src/cmds/example_contracts.ts | 9 + .../cli/src/cmds/generate_p2p_private_key.ts | 10 + .../cli/src/cmds/generate_private_key.ts | 20 + yarn-project/cli/src/cmds/get_account.ts | 15 + yarn-project/cli/src/cmds/get_accounts.ts | 36 ++ .../cli/src/cmds/get_contract_data.ts | 32 ++ yarn-project/cli/src/cmds/get_logs.ts | 69 +++ yarn-project/cli/src/cmds/get_node_info.ts | 13 + yarn-project/cli/src/cmds/get_recipient.ts | 15 + yarn-project/cli/src/cmds/get_recipients.ts | 16 + yarn-project/cli/src/cmds/get_tx_receipt.ts | 15 + yarn-project/cli/src/cmds/inspect_contract.ts | 43 ++ .../cli/src/cmds/parse_parameter_struct.ts | 27 + yarn-project/cli/src/cmds/register_account.ts | 21 + .../cli/src/cmds/register_recipient.ts | 19 + yarn-project/cli/src/cmds/send.ts | 39 ++ yarn-project/cli/src/encoding.ts | 117 ++++ yarn-project/cli/src/github.ts | 3 + yarn-project/cli/src/index.ts | 503 ++++++++++++++++++ yarn-project/cli/src/parse_args.ts | 248 +++++++++ yarn-project/cli/src/update/common.ts | 16 + yarn-project/cli/src/update/noir.ts | 57 ++ yarn-project/cli/src/update/npm.ts | 154 ++++++ yarn-project/cli/src/update/update.ts | 79 +++ yarn-project/cli/src/utils.ts | 239 +++++++++ yarn-project/cli/tsconfig.json | 39 ++ yarn-project/yarn.lock | 229 +++++++- 45 files changed, 3183 insertions(+), 7 deletions(-) create mode 100644 yarn-project/cli/.eslintrc.cjs create mode 100644 yarn-project/cli/CHANGELOG.md create mode 100644 yarn-project/cli/Dockerfile create mode 100644 yarn-project/cli/README.md create mode 100755 yarn-project/cli/aztec-cli-dest create mode 100644 yarn-project/cli/package.json create mode 100644 yarn-project/cli/src/bin/index.ts create mode 100644 yarn-project/cli/src/client.test.ts create mode 100644 yarn-project/cli/src/client.ts create mode 100644 yarn-project/cli/src/cmds/add_contract.ts create mode 100644 yarn-project/cli/src/cmds/add_note.ts create mode 100644 yarn-project/cli/src/cmds/block_number.ts create mode 100644 yarn-project/cli/src/cmds/call.ts create mode 100644 yarn-project/cli/src/cmds/check_deploy.ts create mode 100644 yarn-project/cli/src/cmds/compute_selector.ts create mode 100644 yarn-project/cli/src/cmds/create_account.ts create mode 100644 yarn-project/cli/src/cmds/deploy.ts create mode 100644 yarn-project/cli/src/cmds/deploy_l1_contracts.ts create mode 100644 yarn-project/cli/src/cmds/example_contracts.ts create mode 100644 yarn-project/cli/src/cmds/generate_p2p_private_key.ts create mode 100644 yarn-project/cli/src/cmds/generate_private_key.ts create mode 100644 yarn-project/cli/src/cmds/get_account.ts create mode 100644 yarn-project/cli/src/cmds/get_accounts.ts create mode 100644 yarn-project/cli/src/cmds/get_contract_data.ts create mode 100644 yarn-project/cli/src/cmds/get_logs.ts create mode 100644 yarn-project/cli/src/cmds/get_node_info.ts create mode 100644 yarn-project/cli/src/cmds/get_recipient.ts create mode 100644 yarn-project/cli/src/cmds/get_recipients.ts create mode 100644 yarn-project/cli/src/cmds/get_tx_receipt.ts create mode 100644 yarn-project/cli/src/cmds/inspect_contract.ts create mode 100644 yarn-project/cli/src/cmds/parse_parameter_struct.ts create mode 100644 yarn-project/cli/src/cmds/register_account.ts create mode 100644 yarn-project/cli/src/cmds/register_recipient.ts create mode 100644 yarn-project/cli/src/cmds/send.ts create mode 100644 yarn-project/cli/src/encoding.ts create mode 100644 yarn-project/cli/src/github.ts create mode 100644 yarn-project/cli/src/index.ts create mode 100644 yarn-project/cli/src/parse_args.ts create mode 100644 yarn-project/cli/src/update/common.ts create mode 100644 yarn-project/cli/src/update/noir.ts create mode 100644 yarn-project/cli/src/update/npm.ts create mode 100644 yarn-project/cli/src/update/update.ts create mode 100644 yarn-project/cli/src/utils.ts create mode 100644 yarn-project/cli/tsconfig.json diff --git a/yarn-project/cli/.eslintrc.cjs b/yarn-project/cli/.eslintrc.cjs new file mode 100644 index 00000000000..e659927475c --- /dev/null +++ b/yarn-project/cli/.eslintrc.cjs @@ -0,0 +1 @@ +module.exports = require('@aztec/foundation/eslint'); diff --git a/yarn-project/cli/CHANGELOG.md b/yarn-project/cli/CHANGELOG.md new file mode 100644 index 00000000000..f9ef9369f54 --- /dev/null +++ b/yarn-project/cli/CHANGELOG.md @@ -0,0 +1,171 @@ +# Changelog + +## [0.32.0](https://github.com/AztecProtocol/aztec-packages/compare/aztec-cli-v0.31.0...aztec-cli-v0.32.0) (2024-03-27) + + +### Miscellaneous + +* **aztec-cli:** Synchronize aztec-packages versions + +## [0.31.0](https://github.com/AztecProtocol/aztec-packages/compare/aztec-cli-v0.30.1...aztec-cli-v0.31.0) (2024-03-26) + + +### Features + +* Capture broadcasted functions in node ([#5353](https://github.com/AztecProtocol/aztec-packages/issues/5353)) ([bc05db2](https://github.com/AztecProtocol/aztec-packages/commit/bc05db26c864c9a9dae43f149814e082cdcfd7df)) + + +### Bug Fixes + +* **cli:** Support initializers not named constructor in cli ([#5397](https://github.com/AztecProtocol/aztec-packages/issues/5397)) ([85f14c5](https://github.com/AztecProtocol/aztec-packages/commit/85f14c5dc84c46910b8de498472959fa561d593c)) + +## [0.30.1](https://github.com/AztecProtocol/aztec-packages/compare/aztec-cli-v0.30.0...aztec-cli-v0.30.1) (2024-03-20) + + +### Miscellaneous + +* **aztec-cli:** Synchronize aztec-packages versions + +## [0.30.0](https://github.com/AztecProtocol/aztec-packages/compare/aztec-cli-v0.29.0...aztec-cli-v0.30.0) (2024-03-19) + + +### Features + +* Allow registering contract classes in PXE ([#5291](https://github.com/AztecProtocol/aztec-packages/issues/5291)) ([b811207](https://github.com/AztecProtocol/aztec-packages/commit/b811207bad691f519b31a6391967b9215a9e17d3)), closes [#4055](https://github.com/AztecProtocol/aztec-packages/issues/4055) + + +### Miscellaneous + +* Add gas portal to l1 contract addresses ([#5265](https://github.com/AztecProtocol/aztec-packages/issues/5265)) ([640c89a](https://github.com/AztecProtocol/aztec-packages/commit/640c89a04d7b780795d40e239be3b3db73a16923)), closes [#5022](https://github.com/AztecProtocol/aztec-packages/issues/5022) + +## [0.29.0](https://github.com/AztecProtocol/aztec-packages/compare/aztec-cli-v0.28.1...aztec-cli-v0.29.0) (2024-03-18) + + +### Features + +* Use deployer in address computation ([#5201](https://github.com/AztecProtocol/aztec-packages/issues/5201)) ([258ff4a](https://github.com/AztecProtocol/aztec-packages/commit/258ff4a00208be8695e2e59aecc14d6a92eaac1c)) + + +### Miscellaneous + +* Delete ContractData ([#5258](https://github.com/AztecProtocol/aztec-packages/issues/5258)) ([e516f9b](https://github.com/AztecProtocol/aztec-packages/commit/e516f9b94d1fbdc126a9d0d7d79c571d61914980)) +* Delete ExtendedContractData struct ([#5248](https://github.com/AztecProtocol/aztec-packages/issues/5248)) ([8ae0c13](https://github.com/AztecProtocol/aztec-packages/commit/8ae0c13ceaf8a1f3db09d0e61f0a3781c8926ca6)) +* Removing redundant receipts check ([#5271](https://github.com/AztecProtocol/aztec-packages/issues/5271)) ([5ab07fb](https://github.com/AztecProtocol/aztec-packages/commit/5ab07fb8b395b6edbda6167845c7ea864e9395a3)) + +## [0.28.1](https://github.com/AztecProtocol/aztec-packages/compare/aztec-cli-v0.28.0...aztec-cli-v0.28.1) (2024-03-14) + + +### Miscellaneous + +* **aztec-cli:** Synchronize aztec-packages versions + +## [0.28.0](https://github.com/AztecProtocol/aztec-packages/compare/aztec-cli-v0.27.2...aztec-cli-v0.28.0) (2024-03-14) + + +### ⚠ BREAKING CHANGES + +* Support contracts with no constructor ([#5175](https://github.com/AztecProtocol/aztec-packages/issues/5175)) + +### Features + +* Support contracts with no constructor ([#5175](https://github.com/AztecProtocol/aztec-packages/issues/5175)) ([df7fa32](https://github.com/AztecProtocol/aztec-packages/commit/df7fa32f34e790231e091c38a4a6e84be5407763)) + +## [0.27.2](https://github.com/AztecProtocol/aztec-packages/compare/aztec-cli-v0.27.1...aztec-cli-v0.27.2) (2024-03-13) + + +### Miscellaneous + +* **aztec-cli:** Synchronize aztec-packages versions + +## [0.27.1](https://github.com/AztecProtocol/aztec-packages/compare/aztec-cli-v0.27.0...aztec-cli-v0.27.1) (2024-03-12) + + +### Miscellaneous + +* **aztec-cli:** Synchronize aztec-packages versions + +## [0.27.0](https://github.com/AztecProtocol/aztec-packages/compare/aztec-cli-v0.26.6...aztec-cli-v0.27.0) (2024-03-12) + + +### Miscellaneous + +* Remove old contract deployment flow ([#4970](https://github.com/AztecProtocol/aztec-packages/issues/4970)) ([6d15947](https://github.com/AztecProtocol/aztec-packages/commit/6d1594736e96cd744ea691a239fcd3a46bdade60)) + +## [0.26.6](https://github.com/AztecProtocol/aztec-packages/compare/aztec-cli-v0.26.5...aztec-cli-v0.26.6) (2024-03-08) + + +### Features + +* Show bytecode size per function in CLI inspect-contract ([#5059](https://github.com/AztecProtocol/aztec-packages/issues/5059)) ([cb9fdc6](https://github.com/AztecProtocol/aztec-packages/commit/cb9fdc6b5069ee2ab8fb1f68f369e360039fa18b)) + +## [0.26.5](https://github.com/AztecProtocol/aztec-packages/compare/aztec-cli-v0.26.4...aztec-cli-v0.26.5) (2024-03-07) + + +### Miscellaneous + +* **aztec-cli:** Synchronize aztec-packages versions + +## [0.26.4](https://github.com/AztecProtocol/aztec-packages/compare/aztec-cli-v0.26.3...aztec-cli-v0.26.4) (2024-03-06) + + +### Miscellaneous + +* **aztec-cli:** Synchronize aztec-packages versions + +## [0.26.3](https://github.com/AztecProtocol/aztec-packages/compare/aztec-cli-v0.26.2...aztec-cli-v0.26.3) (2024-03-06) + + +### Miscellaneous + +* **aztec-cli:** Synchronize aztec-packages versions + +## [0.26.2](https://github.com/AztecProtocol/aztec-packages/compare/aztec-cli-v0.26.1...aztec-cli-v0.26.2) (2024-03-06) + + +### Miscellaneous + +* **aztec-cli:** Synchronize aztec-packages versions + +## [0.26.1](https://github.com/AztecProtocol/aztec-packages/compare/aztec-cli-v0.26.0...aztec-cli-v0.26.1) (2024-03-06) + + +### Miscellaneous + +* **aztec-cli:** Synchronize aztec-packages versions + +## [0.26.0](https://github.com/AztecProtocol/aztec-packages/compare/aztec-cli-v0.25.0...aztec-cli-v0.26.0) (2024-03-05) + + +### ⚠ BREAKING CHANGES + +* Use new deployment flow in ContractDeployer ([#4497](https://github.com/AztecProtocol/aztec-packages/issues/4497)) +* move noir out of yarn-project ([#4479](https://github.com/AztecProtocol/aztec-packages/issues/4479)) +* note type ids ([#4500](https://github.com/AztecProtocol/aztec-packages/issues/4500)) +* Include contract class id in deployment info ([#4223](https://github.com/AztecProtocol/aztec-packages/issues/4223)) +* aztec binary ([#3927](https://github.com/AztecProtocol/aztec-packages/issues/3927)) + +### Features + +* **avm-transpiler:** Brillig to AVM transpiler ([#4227](https://github.com/AztecProtocol/aztec-packages/issues/4227)) ([c366c6e](https://github.com/AztecProtocol/aztec-packages/commit/c366c6e6d5c9f28a5dc92a303dcab4a23fb2d84e)) +* Aztec binary ([#3927](https://github.com/AztecProtocol/aztec-packages/issues/3927)) ([12356d9](https://github.com/AztecProtocol/aztec-packages/commit/12356d9e34994a239d5612798c1bc82fa3d26562)) +* Aztec.js API for registering a contract class ([#4469](https://github.com/AztecProtocol/aztec-packages/issues/4469)) ([d566c74](https://github.com/AztecProtocol/aztec-packages/commit/d566c74786a1ea960e9beee4599c1fdedc7ae6eb)) +* Include contract class id in deployment info ([#4223](https://github.com/AztecProtocol/aztec-packages/issues/4223)) ([0ed4126](https://github.com/AztecProtocol/aztec-packages/commit/0ed41261ae43e21f695c35ad753e07adfaaa55f9)), closes [#4054](https://github.com/AztecProtocol/aztec-packages/issues/4054) +* Moving the unbox option to npx command ([#4718](https://github.com/AztecProtocol/aztec-packages/issues/4718)) ([4c3bb92](https://github.com/AztecProtocol/aztec-packages/commit/4c3bb9294fc10ff4663275c952e277eaa7ecd647)) +* Note type ids ([#4500](https://github.com/AztecProtocol/aztec-packages/issues/4500)) ([e1da2fd](https://github.com/AztecProtocol/aztec-packages/commit/e1da2fd509c75d7886b95655d233165e087cf2ed)) +* Parallel native/wasm bb builds. Better messaging around using ci cache. ([#4766](https://github.com/AztecProtocol/aztec-packages/issues/4766)) ([a924e55](https://github.com/AztecProtocol/aztec-packages/commit/a924e55393daa89fbba3a87cf019977286104b59)) +* Use new deployment flow in ContractDeployer ([#4497](https://github.com/AztecProtocol/aztec-packages/issues/4497)) ([0702dc6](https://github.com/AztecProtocol/aztec-packages/commit/0702dc6988149258124184b85d38db930effe0e7)) + + +### Bug Fixes + +* Add new oracle contract to devnet in CI ([#4687](https://github.com/AztecProtocol/aztec-packages/issues/4687)) ([920fa10](https://github.com/AztecProtocol/aztec-packages/commit/920fa10d4d5fb476cd6d868439310452f6e8dcc5)) +* Load contract artifact from json ([#4352](https://github.com/AztecProtocol/aztec-packages/issues/4352)) ([47a0a79](https://github.com/AztecProtocol/aztec-packages/commit/47a0a79f6beaa241eafc94fcae84103488a9dcef)) + + +### Miscellaneous + +* **docs:** Fix a few links to docs ([#4260](https://github.com/AztecProtocol/aztec-packages/issues/4260)) ([1c8ea49](https://github.com/AztecProtocol/aztec-packages/commit/1c8ea497fb1d64da64cb240917a60d57bd1efef8)) +* Move noir out of yarn-project ([#4479](https://github.com/AztecProtocol/aztec-packages/issues/4479)) ([1fe674b](https://github.com/AztecProtocol/aztec-packages/commit/1fe674b046c694e1cbbbb2edaf5a855828bb5340)), closes [#4107](https://github.com/AztecProtocol/aztec-packages/issues/4107) +* Remove stubbed docs ([#4196](https://github.com/AztecProtocol/aztec-packages/issues/4196)) ([25a4bc4](https://github.com/AztecProtocol/aztec-packages/commit/25a4bc490a53304110e7e1f79e99f4c8b7639164)) +* Squash yp ypb + other build improvements. ([#4901](https://github.com/AztecProtocol/aztec-packages/issues/4901)) ([be5855c](https://github.com/AztecProtocol/aztec-packages/commit/be5855cdbd1993155bd228afbeafee2c447b46a5)) +* Updating viem ([#4783](https://github.com/AztecProtocol/aztec-packages/issues/4783)) ([23bc26a](https://github.com/AztecProtocol/aztec-packages/commit/23bc26a4859d9777c3e6dd49e351a4e6b13a989a)) diff --git a/yarn-project/cli/Dockerfile b/yarn-project/cli/Dockerfile new file mode 100644 index 00000000000..c69606b278c --- /dev/null +++ b/yarn-project/cli/Dockerfile @@ -0,0 +1,10 @@ +FROM aztecprotocol/yarn-project AS yarn-project +ENTRYPOINT ["node", "--no-warnings", "/usr/src/yarn-project/cli/dest/bin/index.js"] + +# The version has been updated in yarn-project. +# Adding COMMIT_TAG here to rebuild versioned image. +ARG COMMIT_TAG="" + +RUN mkdir /cache && chmod 777 /cache +ENV XDG_CACHE_HOME /cache +VOLUME "/cache" diff --git a/yarn-project/cli/README.md b/yarn-project/cli/README.md new file mode 100644 index 00000000000..56bf674e842 --- /dev/null +++ b/yarn-project/cli/README.md @@ -0,0 +1,450 @@ +# Aztec CLI Documentation + +The Aztec CLI `aztec-cli` is a command-line interface (CLI) tool for interacting with Aztec. It provides various commands for deploying contracts, creating accounts, interacting with contracts, and retrieving blockchain data. + +## Installation + +1. In your terminal, download the sandbox by running + +``` +bash -i <(curl -s install.aztec.network) +``` + +2. Verify the installation: After the installation is complete, run the following command to verify that `aztec-cli` is installed correctly: + + ```shell + aztec-cli --version + ``` + + This command will display the version number of `aztec-cli` if the installation was successful. + +## Usage + +To use `aztec-cli`, open a terminal or command prompt and run the `aztec-cli` command followed by the desired command and its options. + +Here's the basic syntax for running a command: + +```shell +aztec-cli [options] +``` + +Replace `` with the actual command you want to execute and `[options]` with any optional flags or parameters required by the command. + +### Environment Variables + +Some options can be set globally as environment variables to avoid having to re-enter them every time you call `aztec-cli.` +These options are: + +- `PRIVATE_KEY` -> `-k, --private-key` for all commands that require a private key. +- `PUBLIC_KEY` -> `-k, --public-key` for all commands that require a public key. +- `PXE_URL` -> `-u, --rpc-url` for commands that require a PXE +- `API_KEY` -> `a, --api-key` for `deploy-l1-contracts`. +- `ETHEREUM_RPC_HOST` -> `-u, --rpc-url` for `deploy-l1-contracts`. + +So if for example you are running your Private eXecution Environment (PXE) remotely you can do: + +```shell +export PXE_URL=http://external.site/rpc:8080 +aztec-cli deploy my_contract.json +``` + +And this will send the request to `http://external.site/rpc:8080`. + +**NOTE**: Entering an option value will override the environment variable. + +## Available Commands + +`aztec-cli` provides the following commands for interacting with Aztec: + +### deploy-l1-contracts + +Deploys all necessary Ethereum contracts for Aztec. + +Syntax: + +```shell +aztec-cli deploy-l1-contracts [rpcUrl] [options] +``` + +- `rpcUrl` (optional): URL of the Ethereum host. Chain identifiers `localhost` and `testnet` can be used. Default: `http://localhost:8545`. + +Options: + +- `-a, --api-key `: API key for the Ethereum host. +- `-p, --private-key `: The private key to use for deployment. +- `-m, --mnemonic `: The mnemonic to use in deployment. Default: `test test test test test test test test test test test junk`. + +This command deploys all the necessary Ethereum contracts required for Aztec. It creates the rollup contract, registry contract, inbox contract, outbox contract, and contract deployment emitter. The command displays the addresses of the deployed contracts. + +Example usage: + +```shell +aztec-cli deploy-l1-contracts +``` + +### create-private-key + +Generates a 32-byte private key. + +Syntax: + +```shell +aztec-cli create-private-key [options] +``` + +Options: + +- `-m, --mnemonic`: A mnemonic string that can be used for the private key generation. + +This command generates a random 32-byte private key or derives one from the provided mnemonic string. It displays the generated private key. + +Example usage: + +```shell +aztec-cli create-private-key +``` + +### create-account + +Creates an Aztec account that can be used for transactions. + +Syntax: + +```shell +aztec-cli create-account [options] +``` + +Options: + +- `-k, --private-key`: Private key to use for the account generation. Uses a random key by default. +- `-u, --rpc-url `: URL of PXE Service. Default: `http://localhost:8080`. + +This command creates an Aztec account that can be used for transactions. It generates a new account with a private key or uses the provided private key. The command displays the account's address and public key. + +Example usage: + +```shell +aztec-cli create-account +``` + +### deploy + +Deploys a compiled Aztec.nr contract to Aztec. + +Syntax: + +```shell +aztec-cli deploy [options] +``` + +Options: + +- `-c, --contract-artifact `: Path to the compiled Aztec.nr contract's artifact file in JSON format. You can also use one of Aztec's example contracts found in [@aztec/noir-contracts](https://www.npmjs.com/package/@aztec/noir-contracts), e.g. PrivateTokenContractArtifact. You can get a full ist of the available contracts with `aztec-cli example-contracts` +- `-a, --args ` (optional): Contract constructor arguments Default: []. +- `-u, --rpc-url `: URL of PXE Service. Default: `http://localhost:8080`. +- `-k, --public-key `: Public key of the deployer. If not provided, it will check the RPC for existing ones. + +This command deploys a compiled Aztec.nr contract to Aztec. It requires the path to the contract's artifact file in JSON format. Optionally, you can specify the public key of the deployer and provide constructor arguments for the contract. The command displays the address of the deployed contract. + +Example usage: + +```shell +aztec-cli deploy -c path/to/contract.artifact.json -a ...args +``` + +With an Aztec example contract: + +```shell +aztec-cli deploy -c PrivateTokenContractArtifact -a 333 0x134567890abcdef +``` + +### check-deploy + +Checks if a contract is deployed to the specified Aztec address. + +Syntax: + +```shell +aztec-cli check-deploy [options] +``` + +Options: + +- `-ca, --contract-address
`: An Aztec address to check if the contract has been deployed to. +- `-u, --rpc-url `: URL of PXE Service. Default: `http://localhost:8080`. + +This command checks if a contract is deployed to the specified Aztec address. It verifies if the contract is present at the given address and displays the result. + +Example usage: + +```shell +aztec-cli check-deploy -ca 0x123456789abcdef123456789abcdef12345678 +``` + +### get-tx-receipt + +Gets the receipt for the specified transaction hash. + +Syntax: + +```shell +aztec-cli get-tx-receipt [options] +``` + +- `txHash`: A transaction hash to get the receipt for. + +Options: + +- `-u, --rpc-url `: URL of PXE Service. Default: `http://localhost:8080`. + +This command retrieves and displays the receipt for the specified transaction hash. It shows details such as the transaction status, block number, and block hash. + +Example usage: + +```shell +aztec-cli get-tx-receipt 0xabcdef1234567890abcdef1234567890abcdef1234567890abcdef12345678 +``` + +### get-contract-data + +Gets information about the Aztec contract deployed at the specified address. + +Syntax: + +```shell +aztec-cli get-contract-data [options] +``` + +- `contractAddress`: Aztec address of the contract. + +Options: + +- `-u, --rpc-url `: URL of PXE Service. Default: `http://localhost:8080`. +- `-b, --include-bytecode`: Include the contract's public function bytecode, if any. + +This command retrieves and displays information about the Aztec contract deployed at the specified address. It shows the contract address, portal contract address, and optionally, the bytecode of the contract's public functions. + +Example usage: + +```shell +aztec-cli get-contract-data 0x123456789abcdef123456789abcdef12345678 +``` + +### register-recipient + +Register a recipient account on the PXE (called recipient because we can only send notes to this account and not receive them via this PXE). +To read about how keys are generated and used, head to our docs [here](https://github.com/AztecProtocol/aztec-packages/blob/master/docs/docs/aztec/developer/wallet-providers/keys.md#addresses-partial-addresses-and-public-keys). + +Syntax: + +```shell +aztec-cli register-recipient [options] +``` + +Options: + +- `-a, --address `: The account's Aztec address. +- `-p, --public-key `: 'The account public key.' +- `-pa, --partial-address `: URL of PXE Service. Default: `http://localhost:8080`. + +Example usage: + +```shell +aztec-cli register-recipient -p 0x20d9d93c4a9eb2b4bdb70ead07d28d1edb74bfd78443a8c36b098b024cd26f0e0647f5dbe3619453f42eb788c2beed0294c84676425047aadac23294605c4af9 -a 0x111fdc0f6bf831ca59f05863199762d643b782699d7ce6feaae40a923baf60af -pa 0x72bf7c9537875b0af267b4a8c497927e251f5988af6e30527feb16299042ed +``` + +### get-accounts + +Gets all the Aztec accounts stored in a PXE. + +Syntax: + +```shell +aztec-cli get-accounts [options] +``` + +Options: + +- `-u, --rpc-url `: URL of PXE Service. Default: `http://localhost:8080`. + +This command retrieves and displays all the Aztec accounts available in the system. + +Example usage: + +```shell +aztec-cli get-accounts +``` + +### get-account + +Gets an account given its Aztec address. + +Syntax: + +```shell +aztec-cli get-account
[options] +``` + +- `address`: The Aztec address to get the public key for. + +Options: + +- `-u, --rpc-url `: URL of PXE Service. Default: `http://localhost:8080`. + +This command retrieves and displays the public key of an account given its Aztec address. + +Example usage: + +```shell +aztec-cli get-account 0x123456789abcdef123456789abcdef12345678 +``` + +### send + +Sends a transaction invoking a function on an Aztec contract. + +Syntax: + +```shell +aztec-cli send --args [functionArgs...] --contract-artifact --contract-address --private-key +``` + +- `functionName`: Name of the function to call. + +Options: + +- `'-a, --args [functionArgs...]` (optional): Function arguments. Default: []. +- `-c, --contract-artifact `: The compiled contract's artifact in JSON format. You can also use one of Aztec's example contracts found in (@aztec/noir-contracts)[https://www.npmjs.com/package/@aztec/noir-contracts], e.g. PrivateTokenContractArtifact. +- `-ca, --contract-address
`: Address of the contract. +- `-k, --private-key `: The sender's private key. +- `-u, --rpc-url `: URL of PXE Service. Default: `http://localhost:8080`. + +This command calls a function on an Aztec contract. It requires the contract's artifact, address, function name, and optionally, function arguments. The command executes the function call and displays the transaction details. + +Example usage: + +```shell +aztec-cli send transfer -ca 0x123456789abcdef123456789abcdef12345678 -a 100 -c path/to/artifact.json +``` + +### call + +Calls a view (read-only) function on a deployed contract. +Unlike transactions, view calls do not modify the state of the contract. + +Syntax: + +```shell +aztec-cli call -a [functionArgs...] -c -ca -f +``` + +- `functionName`: Name of the function to view. + +Options: + +- `'-a, --args [functionArgs...]` (optional): Function arguments. Default: []. +- `-c, --contract-artifact `: The compiled contract's artifact in JSON format. You can also use one of Aztec's example contracts found in (@aztec/noir-contracts)[https://www.npmjs.com/package/@aztec/noir-contracts], e.g. PrivateTokenContractArtifact. +- `-ca, --contract-address
`: Address of the contract. +- `-f, --from `: Address of the caller. If empty, first account in the Private eXecution Environment (PXE) will be used. +- `-u, --rpc-url `: URL of PXE Service. Default: `http://localhost:8080`. + +This command simulates the execution of a view function on a deployed contract without modifying the state. It requires the contract's artifact, address, function name, and optionally, function arguments. The command displays the result of the view function. + +Example usage: + +```shell +aztec-cli call balanceOf -c path/to/contract.artifact.json -ca 0x123456789abcdef123456789abcdef12345678 -a balanceOf 0xabcdef1234567890abcdef1234567890abcdef12 +``` + +### parse-parameter-struct + +Helper for parsing an encoded string into a contract's parameter struct. + +Syntax: + +```shell +aztec-cli parse-parameter-struct +``` + +- `encodedString`: The encoded hex string. +- `contractArtifact`: The compiled contract's artifact in JSON format. +- `parameterName`: The name of the struct parameter to decode into. + +This command is a helper for parsing an encoded hex string into a contract's parameter struct. It requires the encoded string, the contract's artifact, and the name of the struct parameter. The command decodes the string and displays the struct data. + +Example usage: + +```shell +aztec-cli parse-parameter-struct 0xabcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890 path/to/contract.artifact.json paramName +``` + +### get-logs + +Applies filter and returns the resulting unencrypted logs. +The filter is applied by doing an intersection of all its params. + +Syntax: + +```shell +aztec-cli get-logs --fromBlock +``` + +Options: + +- `-u, --rpc-url `: URL of PXE Service. Default: `http://localhost:8080`. + +This command retrieves and displays all the unencrypted logs from L2 blocks in the specified range or from a specific transaction. +Example usage: + +```shell +aztec-cli get-logs --txHash 21fef567e01f8508e30843ebcef9c5f6ff27b29d66783cfcdbd070c3a9174234 +aztec-cli get-logs --fromBlock 4 --toBlock 5 --contractAddress 0x1db5f68861c5960c37205d3d5b23466240359c115c49e45982865ea7ace69a02 +aztec-cli get-logs --fromBlock 4 --toBlock 5 --contractAddress 0x1db5f68861c5960c37205d3d5b23466240359c115c49e45982865ea7ace69a02 --selector 00000005 +``` + +Run `aztec-cli get-logs --help` for more information on the filtering options. + +### block-number + +Gets the current Aztec L2 block number. + +Syntax: + +```shell +aztec-cli block-number +``` + +Options: + +- `-u, --rpc-url `: URL of PXE Service. Default: `http://localhost:8080`. + +This command retrieves and displays the current Aztec L2 block number. + +### example-contracts + +Lists the contracts available in [@aztec/noir-contracts](https://github.com/AztecProtocol/aztec-packages/tree/master/noir-contracts) + +Syntax: + +```shell +aztec-cli example-contracts +``` + +### get-node-info + +Gets information of an Aztec node at the specified URL. + +Syntax: + +```shell +aztec-cli get-node-info +``` + +Options: + +- `-u, --rpc-url `: URL of PXE Service. Default: `http://localhost:8080`. + +## Conclusion + +That covers the available commands and their usage in the `aztec-cli`. You can now use these commands to interact with Aztec and perform various actions such as deploying contracts, creating accounts, executing functions, and retrieving blockchain data. diff --git a/yarn-project/cli/aztec-cli-dest b/yarn-project/cli/aztec-cli-dest new file mode 100755 index 00000000000..d2fc2aa3694 --- /dev/null +++ b/yarn-project/cli/aztec-cli-dest @@ -0,0 +1,3 @@ +#!/bin/sh +SCRIPT_PATH=$(dirname $(realpath $0)) +node --no-warnings $SCRIPT_PATH/dest/bin/index.js $@ diff --git a/yarn-project/cli/package.json b/yarn-project/cli/package.json new file mode 100644 index 00000000000..1808405ee9a --- /dev/null +++ b/yarn-project/cli/package.json @@ -0,0 +1,79 @@ +{ + "name": "@aztec/cli", + "version": "0.32.0", + "type": "module", + "main": "./dest/index.js", + "bin": { + "aztec-cli": "./dest/bin/index.js" + }, + "typedocOptions": { + "entryPoints": [ + "./src/index.ts" + ], + "name": "Aztec CLI", + "tsconfig": "./tsconfig.json" + }, + "scripts": { + "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 eslint --fix ./src && run -T prettier -w ./src", + "test": "NODE_NO_WARNINGS=1 node --experimental-vm-modules $(yarn bin jest) --passWithNoTests", + "start": "node --no-warnings ./dest/bin/index.js" + }, + "inherits": [ + "../package.common.json" + ], + "jest": { + "preset": "ts-jest/presets/default-esm", + "moduleNameMapper": { + "^(\\.{1,2}/.*)\\.[cm]?js$": "$1" + }, + "testRegex": "./src/.*\\.test\\.(js|mjs|ts)$", + "rootDir": "./src" + }, + "dependencies": { + "@aztec/accounts": "workspace:^", + "@aztec/aztec.js": "workspace:^", + "@aztec/circuit-types": "workspace:^", + "@aztec/circuits.js": "workspace:^", + "@aztec/ethereum": "workspace:^", + "@aztec/foundation": "workspace:^", + "@aztec/l1-artifacts": "workspace:^", + "@aztec/noir-contracts.js": "workspace:^", + "@aztec/types": "workspace:^", + "@iarna/toml": "^2.2.5", + "@libp2p/peer-id-factory": "^3.0.4", + "commander": "^9.0.0", + "jszip": "^3.10.1", + "lodash.startcase": "^4.4.0", + "node-fetch": "^3.3.2", + "semver": "^7.5.4", + "source-map-support": "^0.5.21", + "tslib": "^2.4.0", + "viem": "^2.7.15" + }, + "devDependencies": { + "@jest/globals": "^29.5.0", + "@types/jest": "^29.5.0", + "@types/lodash.startcase": "^4.4.7", + "@types/node": "^18.7.23", + "@types/semver": "^7.5.2", + "@types/source-map-support": "^0.5.10", + "jest": "^29.5.0", + "jest-mock-extended": "^3.0.5", + "ts-jest": "^29.1.0", + "ts-node": "^10.9.1", + "typescript": "^5.0.4" + }, + "files": [ + "dest", + "src", + "!*.test.*" + ], + "types": "./dest/index.d.ts", + "engines": { + "node": ">=18" + } +} diff --git a/yarn-project/cli/src/bin/index.ts b/yarn-project/cli/src/bin/index.ts new file mode 100644 index 00000000000..0d79e796d50 --- /dev/null +++ b/yarn-project/cli/src/bin/index.ts @@ -0,0 +1,24 @@ +#!/usr/bin/env -S node --no-warnings +import { createConsoleLogger, createDebugLogger } from '@aztec/foundation/log'; + +import 'source-map-support/register.js'; + +import { getProgram } from '../index.js'; + +const debugLogger = createDebugLogger('aztec:cli-client'); +const log = createConsoleLogger(); + +/** CLI main entrypoint */ +async function main() { + process.once('SIGINT', () => process.exit(0)); + process.once('SIGTERM', () => process.exit(0)); + + const program = getProgram(log, debugLogger); + await program.parseAsync(process.argv); +} + +main().catch(err => { + log(`Error in command execution`); + log(err); + process.exit(1); +}); diff --git a/yarn-project/cli/src/client.test.ts b/yarn-project/cli/src/client.test.ts new file mode 100644 index 00000000000..8e879f762ba --- /dev/null +++ b/yarn-project/cli/src/client.test.ts @@ -0,0 +1,31 @@ +import { NodeInfo } from '@aztec/aztec.js'; +import { PXE } from '@aztec/circuit-types'; + +import { MockProxy, mock } from 'jest-mock-extended'; + +import { checkServerVersion } from './client.js'; + +describe('client', () => { + describe('checkServerVersion', () => { + let pxe: MockProxy; + + beforeEach(() => { + pxe = mock(); + }); + + it('checks versions match', async () => { + pxe.getNodeInfo.mockResolvedValue({ nodeVersion: '0.1.0-alpha47' } as NodeInfo); + await checkServerVersion(pxe, '0.1.0-alpha47'); + }); + + it('reports mismatch on older pxe version', async () => { + pxe.getNodeInfo.mockResolvedValue({ nodeVersion: '0.1.0-alpha47' } as NodeInfo); + await expect(checkServerVersion(pxe, '0.1.0-alpha48')).rejects.toThrow(/is older than the expected by this CLI/); + }); + + it('reports mismatch on newer pxe version', async () => { + pxe.getNodeInfo.mockResolvedValue({ nodeVersion: '0.1.0-alpha48' } as NodeInfo); + await expect(checkServerVersion(pxe, '0.1.0-alpha47')).rejects.toThrow(/is newer than the expected by this CLI/); + }); + }); +}); diff --git a/yarn-project/cli/src/client.ts b/yarn-project/cli/src/client.ts new file mode 100644 index 00000000000..18ea8bd2d5e --- /dev/null +++ b/yarn-project/cli/src/client.ts @@ -0,0 +1,68 @@ +import { PXE, createPXEClient } from '@aztec/aztec.js'; +import { DebugLogger } from '@aztec/foundation/log'; +import { fileURLToPath } from '@aztec/foundation/url'; + +import { readFileSync } from 'fs'; +import { dirname, resolve } from 'path'; +import { gtr, ltr, satisfies, valid } from 'semver'; + +/** + * Creates a PXE client with a given set of retries on non-server errors. + * Checks that PXE matches the expected version, and warns if not. + * @param rpcUrl - URL of the RPC server wrapping the PXE. + * @param logger - Debug logger to warn version incompatibilities. + * @returns A PXE client. + */ +export async function createCompatibleClient(rpcUrl: string, logger: DebugLogger) { + const pxe = createPXEClient(rpcUrl); + const packageJsonPath = resolve(dirname(fileURLToPath(import.meta.url)), '../package.json'); + const packageJsonContents = JSON.parse(readFileSync(packageJsonPath).toString()); + const expectedVersionRange = packageJsonContents.version; + + try { + await checkServerVersion(pxe, expectedVersionRange); + } catch (err) { + if (err instanceof VersionMismatchError) { + logger.warn(err.message); + } else { + throw err; + } + } + + return pxe; +} + +/** Mismatch between server and client versions. */ +class VersionMismatchError extends Error {} + +/** + * Checks that Private eXecution Environment (PXE) version matches the expected one by this CLI. Throws if not. + * @param pxe - PXE client. + * @param expectedVersionRange - Expected version by CLI. + */ +export async function checkServerVersion(pxe: PXE, expectedVersionRange: string) { + const serverName = 'Aztec Node'; + const { nodeVersion } = await pxe.getNodeInfo(); + if (!nodeVersion) { + throw new VersionMismatchError(`Couldn't determine ${serverName} version. You may run into issues.`); + } + if (!nodeVersion || !valid(nodeVersion)) { + throw new VersionMismatchError( + `Missing or invalid version identifier for ${serverName} (${nodeVersion ?? 'empty'}).`, + ); + } else if (!satisfies(nodeVersion, expectedVersionRange)) { + if (gtr(nodeVersion, expectedVersionRange)) { + throw new VersionMismatchError( + `${serverName} is running version ${nodeVersion} which is newer than the expected by this CLI (${expectedVersionRange}). Consider upgrading your CLI to a newer version.`, + ); + } else if (ltr(nodeVersion, expectedVersionRange)) { + throw new VersionMismatchError( + `${serverName} is running version ${nodeVersion} which is older than the expected by this CLI (${expectedVersionRange}). Consider upgrading your ${serverName} to a newer version.`, + ); + } else { + throw new VersionMismatchError( + `${serverName} is running version ${nodeVersion} which does not match the expected by this CLI (${expectedVersionRange}).`, + ); + } + } +} diff --git a/yarn-project/cli/src/cmds/add_contract.ts b/yarn-project/cli/src/cmds/add_contract.ts new file mode 100644 index 00000000000..40a116422e7 --- /dev/null +++ b/yarn-project/cli/src/cmds/add_contract.ts @@ -0,0 +1,39 @@ +import { AztecAddress, type ContractInstanceWithAddress, type Fr, getContractClassFromArtifact } from '@aztec/aztec.js'; +import { type PublicKeys } from '@aztec/circuits.js'; +import { computeContractAddressFromInstance } from '@aztec/circuits.js/contract'; +import { type DebugLogger, type LogFn } from '@aztec/foundation/log'; + +import { createCompatibleClient } from '../client.js'; +import { getContractArtifact } from '../utils.js'; + +export async function addContract( + rpcUrl: string, + contractArtifactPath: string, + address: AztecAddress, + initializationHash: Fr, + salt: Fr, + publicKeys: PublicKeys, + deployer: AztecAddress | undefined, + debugLogger: DebugLogger, + log: LogFn, +) { + const artifact = await getContractArtifact(contractArtifactPath, log); + const instance: ContractInstanceWithAddress = { + version: 1, + salt, + initializationHash, + contractClassId: getContractClassFromArtifact(artifact).id, + publicKeysHash: publicKeys.hash(), + address, + deployer: deployer ?? AztecAddress.ZERO, + }; + const computed = computeContractAddressFromInstance(instance); + if (!computed.equals(address)) { + throw new Error(`Contract address ${address.toString()} does not match computed address ${computed.toString()}`); + } + + const client = await createCompatibleClient(rpcUrl, debugLogger); + + await client.registerContract({ artifact, instance }); + log(`\nContract added to PXE at ${address.toString()} with class ${instance.contractClassId.toString()}\n`); +} diff --git a/yarn-project/cli/src/cmds/add_note.ts b/yarn-project/cli/src/cmds/add_note.ts new file mode 100644 index 00000000000..988c2a45c12 --- /dev/null +++ b/yarn-project/cli/src/cmds/add_note.ts @@ -0,0 +1,22 @@ +import { AztecAddress, Fr } from '@aztec/aztec.js'; +import { ExtendedNote, Note, TxHash } from '@aztec/circuit-types'; +import { DebugLogger } from '@aztec/foundation/log'; + +import { createCompatibleClient } from '../client.js'; +import { parseFields } from '../parse_args.js'; + +export async function addNote( + address: AztecAddress, + contractAddress: AztecAddress, + storageSlot: Fr, + noteTypeId: Fr, + txHash: TxHash, + noteFields: string[], + rpcUrl: string, + debugLogger: DebugLogger, +) { + const note = new Note(parseFields(noteFields)); + const extendedNote = new ExtendedNote(note, address, contractAddress, storageSlot, noteTypeId, txHash); + const client = await createCompatibleClient(rpcUrl, debugLogger); + await client.addNote(extendedNote); +} diff --git a/yarn-project/cli/src/cmds/block_number.ts b/yarn-project/cli/src/cmds/block_number.ts new file mode 100644 index 00000000000..c5aed126443 --- /dev/null +++ b/yarn-project/cli/src/cmds/block_number.ts @@ -0,0 +1,9 @@ +import { DebugLogger, LogFn } from '@aztec/foundation/log'; + +import { createCompatibleClient } from '../client.js'; + +export async function blockNumber(rpcUrl: string, debugLogger: DebugLogger, log: LogFn) { + const client = await createCompatibleClient(rpcUrl, debugLogger); + const num = await client.getBlockNumber(); + log(`${num}\n`); +} diff --git a/yarn-project/cli/src/cmds/call.ts b/yarn-project/cli/src/cmds/call.ts new file mode 100644 index 00000000000..503a9a66d8c --- /dev/null +++ b/yarn-project/cli/src/cmds/call.ts @@ -0,0 +1,32 @@ +import { AztecAddress } from '@aztec/aztec.js'; +import { DebugLogger, LogFn } from '@aztec/foundation/log'; + +import { format } from 'util'; + +import { createCompatibleClient } from '../client.js'; +import { getFunctionArtifact, getTxSender, prepTx } from '../utils.js'; + +export async function call( + functionName: string, + functionArgsIn: any[], + contractArtifactPath: string, + contractAddress: AztecAddress, + fromAddress: string | undefined, + rpcUrl: string, + debugLogger: DebugLogger, + log: LogFn, +) { + const { functionArgs, contractArtifact } = await prepTx(contractArtifactPath, functionName, functionArgsIn, log); + + const fnArtifact = getFunctionArtifact(contractArtifact, functionName); + if (fnArtifact.parameters.length !== functionArgs.length) { + throw Error( + `Invalid number of args passed. Expected ${fnArtifact.parameters.length}; Received: ${functionArgs.length}`, + ); + } + + const client = await createCompatibleClient(rpcUrl, debugLogger); + const from = await getTxSender(client, fromAddress); + const result = await client.simulateUnconstrained(functionName, functionArgs, contractAddress, from); + log(format('\nView result: ', result, '\n')); +} diff --git a/yarn-project/cli/src/cmds/check_deploy.ts b/yarn-project/cli/src/cmds/check_deploy.ts new file mode 100644 index 00000000000..c9777522e10 --- /dev/null +++ b/yarn-project/cli/src/cmds/check_deploy.ts @@ -0,0 +1,19 @@ +import { AztecAddress } from '@aztec/aztec.js'; +import { DebugLogger, LogFn } from '@aztec/foundation/log'; + +import { createCompatibleClient } from '../client.js'; + +export async function checkDeploy(rpcUrl: string, contractAddress: AztecAddress, debugLogger: DebugLogger, log: LogFn) { + const client = await createCompatibleClient(rpcUrl, debugLogger); + const isPrivatelyDeployed = await client.getContractInstance(contractAddress); + const isPubliclyDeployed = await client.isContractPubliclyDeployed(contractAddress); + if (isPubliclyDeployed && isPrivatelyDeployed) { + log(`\nContract is publicly deployed at ${contractAddress.toString()}\n`); + } else if (isPrivatelyDeployed) { + log(`\nContract is registered in the local pxe at ${contractAddress.toString()} but not publicly deployed\n`); + } else if (isPubliclyDeployed) { + log(`\nContract is publicly deployed at ${contractAddress.toString()} but not registered in the local pxe\n`); + } else { + log(`\nNo contract found at ${contractAddress.toString()}\n`); + } +} diff --git a/yarn-project/cli/src/cmds/compute_selector.ts b/yarn-project/cli/src/cmds/compute_selector.ts new file mode 100644 index 00000000000..074be33597a --- /dev/null +++ b/yarn-project/cli/src/cmds/compute_selector.ts @@ -0,0 +1,7 @@ +import { FunctionSelector } from '@aztec/foundation/abi'; +import { LogFn } from '@aztec/foundation/log'; + +export function computeSelector(functionSignature: string, log: LogFn) { + const selector = FunctionSelector.fromSignature(functionSignature); + log(`${selector}`); +} diff --git a/yarn-project/cli/src/cmds/create_account.ts b/yarn-project/cli/src/cmds/create_account.ts new file mode 100644 index 00000000000..5af15fbb182 --- /dev/null +++ b/yarn-project/cli/src/cmds/create_account.ts @@ -0,0 +1,38 @@ +import { getSchnorrAccount } from '@aztec/accounts/schnorr'; +import { Fq, Fr } from '@aztec/foundation/fields'; +import { type DebugLogger, type LogFn } from '@aztec/foundation/log'; + +import { createCompatibleClient } from '../client.js'; + +export async function createAccount( + rpcUrl: string, + encryptionPrivateKey: Fr, + signingPrivateKey: Fq, + wait: boolean, + debugLogger: DebugLogger, + log: LogFn, +) { + const client = await createCompatibleClient(rpcUrl, debugLogger); + const actualEncryptionPrivateKey = encryptionPrivateKey ?? Fr.random(); + const actualSigningPrivateKey = signingPrivateKey ?? Fq.random(); + + const account = getSchnorrAccount(client, actualEncryptionPrivateKey, actualSigningPrivateKey, Fr.ZERO); + const { address, publicKeys, partialAddress } = account.getCompleteAddress(); + const tx = account.deploy(); + const txHash = tx.getTxHash(); + debugLogger.debug(`Account contract tx sent with hash ${txHash}`); + if (wait) { + log(`\nWaiting for account contract deployment...`); + await tx.wait(); + } else { + log(`\nAccount deployment transaction hash: ${txHash}\n`); + } + + log(`\nNew account:\n`); + log(`Address: ${address.toString()}`); + log(`Public key: ${publicKeys.toString()}`); + if (!signingPrivateKey) { + log(`Private key: ${actualSigningPrivateKey.toString()}`); + } + log(`Partial address: ${partialAddress.toString()}`); +} diff --git a/yarn-project/cli/src/cmds/deploy.ts b/yarn-project/cli/src/cmds/deploy.ts new file mode 100644 index 00000000000..3def185b7a7 --- /dev/null +++ b/yarn-project/cli/src/cmds/deploy.ts @@ -0,0 +1,82 @@ +import { getSchnorrAccount } from '@aztec/accounts/schnorr'; +import { ContractDeployer, type EthAddress, type Fq, Fr, Point, PublicKey } from '@aztec/aztec.js'; +import { type PublicKeys } from '@aztec/circuits.js'; +import { getInitializer } from '@aztec/foundation/abi'; +import { type DebugLogger, type LogFn } from '@aztec/foundation/log'; + +import { createCompatibleClient } from '../client.js'; +import { encodeArgs } from '../encoding.js'; +import { GITHUB_TAG_PREFIX } from '../github.js'; +import { getContractArtifact } from '../utils.js'; + +export async function deploy( + artifactPath: string, + json: boolean, + rpcUrl: string, + publicKeys: PublicKeys, + rawArgs: any[], + portalAddress: EthAddress, + salt: Fr, + privateKey: Fr, + signingPrivateKey: Fq, + initializer: string | undefined, + wait: boolean, + debugLogger: DebugLogger, + log: LogFn, + logJson: (output: any) => void, +) { + const contractArtifact = await getContractArtifact(artifactPath, log); + const constructorArtifact = getInitializer(contractArtifact, initializer); + + const client = await createCompatibleClient(rpcUrl, debugLogger); + const nodeInfo = await client.getNodeInfo(); + const expectedAztecNrVersion = `${GITHUB_TAG_PREFIX}-v${nodeInfo.nodeVersion}`; + if (contractArtifact.aztecNrVersion && contractArtifact.aztecNrVersion !== expectedAztecNrVersion) { + log( + `\nWarning: Contract was compiled with a different version of Aztec.nr: ${contractArtifact.aztecNrVersion}. Consider updating Aztec.nr to ${expectedAztecNrVersion}\n`, + ); + } + + const wallet = await getSchnorrAccount(client, privateKey, signingPrivateKey, Fr.ZERO).getWallet(); + const deployer = new ContractDeployer(contractArtifact, wallet, publicKeys.hash(), initializer); + + let args = []; + if (rawArgs.length > 0) { + if (!constructorArtifact) { + throw new Error(`Cannot process constructor arguments as no constructor was found`); + } + debugLogger.debug(`Input arguments: ${rawArgs.map((x: any) => `"${x}"`).join(', ')}`); + args = encodeArgs(rawArgs, constructorArtifact!.parameters); + debugLogger.debug(`Encoded arguments: ${args.join(', ')}`); + } + + const deploy = deployer.deploy(...args); + + await deploy.create({ contractAddressSalt: salt }); + const tx = deploy.send({ contractAddressSalt: salt }); + const txHash = await tx.getTxHash(); + debugLogger.debug(`Deploy tx sent with hash ${txHash}`); + if (wait) { + const deployed = await tx.wait(); + const { address, partialAddress } = deployed.contract; + if (json) { + logJson({ address: address.toString(), partialAddress: partialAddress.toString() }); + } else { + log(`\nContract deployed at ${address.toString()}\n`); + log(`Contract partial address ${partialAddress.toString()}\n`); + } + } else { + const { address, partialAddress } = deploy; + if (json) { + logJson({ + address: address?.toString() ?? 'N/A', + partialAddress: partialAddress?.toString() ?? 'N/A', + txHash: txHash.toString(), + }); + } else { + log(`\nContract Address: ${address?.toString() ?? 'N/A'}`); + log(`Contract Partial Address: ${partialAddress?.toString() ?? 'N/A'}`); + log(`Deployment transaction hash: ${txHash}\n`); + } + } +} diff --git a/yarn-project/cli/src/cmds/deploy_l1_contracts.ts b/yarn-project/cli/src/cmds/deploy_l1_contracts.ts new file mode 100644 index 00000000000..a7a5af57fe1 --- /dev/null +++ b/yarn-project/cli/src/cmds/deploy_l1_contracts.ts @@ -0,0 +1,22 @@ +import { DebugLogger, LogFn } from '@aztec/foundation/log'; + +import { deployAztecContracts } from '../utils.js'; + +export async function deployL1Contracts( + rpcUrl: string, + apiKey: string, + privateKey: string, + mnemonic: string, + log: LogFn, + debugLogger: DebugLogger, +) { + const { l1ContractAddresses } = await deployAztecContracts(rpcUrl, apiKey, privateKey, mnemonic, debugLogger); + + log('\n'); + log(`Rollup Address: ${l1ContractAddresses.rollupAddress.toString()}`); + log(`Registry Address: ${l1ContractAddresses.registryAddress.toString()}`); + log(`L1 -> L2 Inbox Address: ${l1ContractAddresses.inboxAddress.toString()}`); + log(`L2 -> L1 Outbox address: ${l1ContractAddresses.outboxAddress.toString()}`); + log(`Availability Oracle Address: ${l1ContractAddresses.availabilityOracleAddress.toString()}`); + log('\n'); +} diff --git a/yarn-project/cli/src/cmds/example_contracts.ts b/yarn-project/cli/src/cmds/example_contracts.ts new file mode 100644 index 00000000000..c7ee019eccc --- /dev/null +++ b/yarn-project/cli/src/cmds/example_contracts.ts @@ -0,0 +1,9 @@ +import { LogFn } from '@aztec/foundation/log'; + +import { getExampleContractArtifacts } from '../utils.js'; + +export async function exampleContracts(log: LogFn) { + const abisList = await getExampleContractArtifacts(); + const names = Object.keys(abisList).filter(name => name !== 'AvmTestContractArtifact'); + names.forEach(name => log(name)); +} diff --git a/yarn-project/cli/src/cmds/generate_p2p_private_key.ts b/yarn-project/cli/src/cmds/generate_p2p_private_key.ts new file mode 100644 index 00000000000..928e61974ba --- /dev/null +++ b/yarn-project/cli/src/cmds/generate_p2p_private_key.ts @@ -0,0 +1,10 @@ +import { LogFn } from '@aztec/foundation/log'; + +import { createSecp256k1PeerId } from '@libp2p/peer-id-factory'; + +export async function generateP2PPrivateKey(log: LogFn) { + const peerId = await createSecp256k1PeerId(); + const exportedPeerId = Buffer.from(peerId.privateKey!).toString('hex'); + log(`Private key: ${exportedPeerId}`); + log(`Peer Id: ${peerId}`); +} diff --git a/yarn-project/cli/src/cmds/generate_private_key.ts b/yarn-project/cli/src/cmds/generate_private_key.ts new file mode 100644 index 00000000000..b447cdcc738 --- /dev/null +++ b/yarn-project/cli/src/cmds/generate_private_key.ts @@ -0,0 +1,20 @@ +import { GrumpkinScalar, generatePublicKey } from '@aztec/aztec.js'; +import { LogFn } from '@aztec/foundation/log'; + +import { mnemonicToAccount } from 'viem/accounts'; + +export function generatePrivateKey(mnemonic: string | undefined, log: LogFn) { + let privKey; + let publicKey; + if (mnemonic) { + const acc = mnemonicToAccount(mnemonic); + // TODO(#2052): This reduction is not secure enough. TACKLE THIS ISSUE BEFORE MAINNET. + const key = GrumpkinScalar.fromBufferReduce(Buffer.from(acc.getHdKey().privateKey!)); + publicKey = generatePublicKey(key); + } else { + const key = GrumpkinScalar.random(); + privKey = key.toString(); + publicKey = generatePublicKey(key); + } + log(`\nPrivate Key: ${privKey}\nPublic Key: ${publicKey.toString()}\n`); +} diff --git a/yarn-project/cli/src/cmds/get_account.ts b/yarn-project/cli/src/cmds/get_account.ts new file mode 100644 index 00000000000..8b3e9359d5f --- /dev/null +++ b/yarn-project/cli/src/cmds/get_account.ts @@ -0,0 +1,15 @@ +import { AztecAddress } from '@aztec/aztec.js'; +import { DebugLogger, LogFn } from '@aztec/foundation/log'; + +import { createCompatibleClient } from '../client.js'; + +export async function getAccount(aztecAddress: AztecAddress, rpcUrl: string, debugLogger: DebugLogger, log: LogFn) { + const client = await createCompatibleClient(rpcUrl, debugLogger); + const account = await client.getRegisteredAccount(aztecAddress); + + if (!account) { + log(`Unknown account ${aztecAddress.toString()}`); + } else { + log(account.toReadableString()); + } +} diff --git a/yarn-project/cli/src/cmds/get_accounts.ts b/yarn-project/cli/src/cmds/get_accounts.ts new file mode 100644 index 00000000000..15e62de7edb --- /dev/null +++ b/yarn-project/cli/src/cmds/get_accounts.ts @@ -0,0 +1,36 @@ +import { DebugLogger, LogFn } from '@aztec/foundation/log'; + +import { createCompatibleClient } from '../client.js'; + +export async function getAccounts( + rpcUrl: string, + json: boolean, + debugLogger: DebugLogger, + log: LogFn, + logJson: (output: any) => void, +) { + const client = await createCompatibleClient(rpcUrl, debugLogger); + const accounts = await client.getRegisteredAccounts(); + if (!accounts.length) { + if (json) { + logJson([]); + } else { + log('No accounts found.'); + } + return; + } + if (json) { + logJson( + accounts.map(a => ({ + address: a.address.toString(), + publicKeys: a.publicKeys.toString(), + partialAddress: a.partialAddress.toString(), + })), + ); + } else { + log(`Accounts found: \n`); + for (const account of accounts) { + log(account.toReadableString()); + } + } +} diff --git a/yarn-project/cli/src/cmds/get_contract_data.ts b/yarn-project/cli/src/cmds/get_contract_data.ts new file mode 100644 index 00000000000..d11180f7e4b --- /dev/null +++ b/yarn-project/cli/src/cmds/get_contract_data.ts @@ -0,0 +1,32 @@ +import { AztecAddress } from '@aztec/aztec.js'; +import { DebugLogger, LogFn } from '@aztec/foundation/log'; + +import { createCompatibleClient } from '../client.js'; + +export async function getContractData( + rpcUrl: string, + contractAddress: AztecAddress, + includeBytecode: boolean, + debugLogger: DebugLogger, + log: LogFn, +) { + const client = await createCompatibleClient(rpcUrl, debugLogger); + const instance = await client.getContractInstance(contractAddress); + const contractClass = includeBytecode && instance && (await client.getContractClass(instance?.contractClassId)); + + if (!instance) { + log(`No contract found at ${contractAddress}`); + return; + } + + log(`\nContract Data:`); + Object.entries(instance).forEach(([key, value]) => { + const capitalized = key.charAt(0).toUpperCase() + key.slice(1); + log(`${capitalized}: ${value.toString()}`); + }); + + if (contractClass) { + log(`Bytecode: ${contractClass.packedBytecode.toString('base64')}`); + } + log('\n'); +} diff --git a/yarn-project/cli/src/cmds/get_logs.ts b/yarn-project/cli/src/cmds/get_logs.ts new file mode 100644 index 00000000000..76c27c76726 --- /dev/null +++ b/yarn-project/cli/src/cmds/get_logs.ts @@ -0,0 +1,69 @@ +import { AztecAddress, LogFilter, LogId, TxHash } from '@aztec/aztec.js'; +import { EventSelector } from '@aztec/foundation/abi'; +import { DebugLogger, LogFn } from '@aztec/foundation/log'; +import { sleep } from '@aztec/foundation/sleep'; + +import { createCompatibleClient } from '../client.js'; + +export async function getLogs( + txHash: TxHash, + fromBlock: number, + toBlock: number, + afterLog: LogId, + contractAddress: AztecAddress, + selector: EventSelector, + rpcUrl: string, + follow: boolean, + debugLogger: DebugLogger, + log: LogFn, +) { + const pxe = await createCompatibleClient(rpcUrl, debugLogger); + + if (follow) { + if (txHash) { + throw Error('Cannot use --follow with --tx-hash'); + } + if (toBlock) { + throw Error('Cannot use --follow with --to-block'); + } + } + + const filter: LogFilter = { txHash, fromBlock, toBlock, afterLog, contractAddress, selector }; + + const fetchLogs = async () => { + const response = await pxe.getUnencryptedLogs(filter); + const logs = response.logs; + + if (!logs.length) { + const filterOptions = Object.entries(filter) + .filter(([, value]) => value !== undefined) + .map(([key, value]) => `${key}: ${value}`) + .join(', '); + if (!follow) { + log(`No logs found for filter: {${filterOptions}}`); + } + } else { + if (!follow && !filter.afterLog) { + log('Logs found: \n'); + } + logs.forEach(unencryptedLog => log(unencryptedLog.toHumanReadable())); + // Set the continuation parameter for the following requests + filter.afterLog = logs[logs.length - 1].id; + } + return response.maxLogsHit; + }; + + if (follow) { + log('Fetching logs...'); + while (true) { + const maxLogsHit = await fetchLogs(); + if (!maxLogsHit) { + await sleep(1000); + } + } + } else { + while (await fetchLogs()) { + // Keep fetching logs until we reach the end. + } + } +} diff --git a/yarn-project/cli/src/cmds/get_node_info.ts b/yarn-project/cli/src/cmds/get_node_info.ts new file mode 100644 index 00000000000..b775c08aa0d --- /dev/null +++ b/yarn-project/cli/src/cmds/get_node_info.ts @@ -0,0 +1,13 @@ +import { DebugLogger, LogFn } from '@aztec/foundation/log'; + +import { createCompatibleClient } from '../client.js'; + +export async function getNodeInfo(rpcUrl: string, debugLogger: DebugLogger, log: LogFn) { + const client = await createCompatibleClient(rpcUrl, debugLogger); + const info = await client.getNodeInfo(); + log(`\nNode Info:\n`); + log(`Node Version: ${info.nodeVersion}\n`); + log(`Chain Id: ${info.chainId}\n`); + log(`Protocol Version: ${info.protocolVersion}\n`); + log(`Rollup Address: ${info.l1ContractAddresses.rollupAddress.toString()}`); +} diff --git a/yarn-project/cli/src/cmds/get_recipient.ts b/yarn-project/cli/src/cmds/get_recipient.ts new file mode 100644 index 00000000000..270bbad9ac2 --- /dev/null +++ b/yarn-project/cli/src/cmds/get_recipient.ts @@ -0,0 +1,15 @@ +import { AztecAddress } from '@aztec/aztec.js'; +import { DebugLogger, LogFn } from '@aztec/foundation/log'; + +import { createCompatibleClient } from '../client.js'; + +export async function getRecipient(aztecAddress: AztecAddress, rpcUrl: string, debugLogger: DebugLogger, log: LogFn) { + const client = await createCompatibleClient(rpcUrl, debugLogger); + const recipient = await client.getRecipient(aztecAddress); + + if (!recipient) { + log(`Unknown recipient ${aztecAddress.toString()}`); + } else { + log(recipient.toReadableString()); + } +} diff --git a/yarn-project/cli/src/cmds/get_recipients.ts b/yarn-project/cli/src/cmds/get_recipients.ts new file mode 100644 index 00000000000..875b84b6038 --- /dev/null +++ b/yarn-project/cli/src/cmds/get_recipients.ts @@ -0,0 +1,16 @@ +import { DebugLogger, LogFn } from '@aztec/foundation/log'; + +import { createCompatibleClient } from '../client.js'; + +export async function getRecipients(rpcUrl: string, debugLogger: DebugLogger, log: LogFn) { + const client = await createCompatibleClient(rpcUrl, debugLogger); + const recipients = await client.getRecipients(); + if (!recipients.length) { + log('No recipients found.'); + } else { + log(`Recipients found: \n`); + for (const recipient of recipients) { + log(recipient.toReadableString()); + } + } +} diff --git a/yarn-project/cli/src/cmds/get_tx_receipt.ts b/yarn-project/cli/src/cmds/get_tx_receipt.ts new file mode 100644 index 00000000000..beaa53e2f9b --- /dev/null +++ b/yarn-project/cli/src/cmds/get_tx_receipt.ts @@ -0,0 +1,15 @@ +import { TxHash } from '@aztec/aztec.js'; +import { JsonStringify } from '@aztec/foundation/json-rpc'; +import { DebugLogger, LogFn } from '@aztec/foundation/log'; + +import { createCompatibleClient } from '../client.js'; + +export async function getTxReceipt(rpcUrl: string, txHash: TxHash, debugLogger: DebugLogger, log: LogFn) { + const client = await createCompatibleClient(rpcUrl, debugLogger); + const receipt = await client.getTxReceipt(txHash); + if (!receipt) { + log(`No receipt found for transaction hash ${txHash.toString()}`); + } else { + log(`\nTransaction receipt: \n${JsonStringify(receipt, true)}\n`); + } +} diff --git a/yarn-project/cli/src/cmds/inspect_contract.ts b/yarn-project/cli/src/cmds/inspect_contract.ts new file mode 100644 index 00000000000..49967561084 --- /dev/null +++ b/yarn-project/cli/src/cmds/inspect_contract.ts @@ -0,0 +1,43 @@ +import { getContractClassFromArtifact } from '@aztec/circuits.js'; +import { + type FunctionArtifact, + FunctionSelector, + decodeFunctionSignature, + decodeFunctionSignatureWithParameterNames, +} from '@aztec/foundation/abi'; +import { sha256 } from '@aztec/foundation/crypto'; +import { type DebugLogger, type LogFn } from '@aztec/foundation/log'; + +import { getContractArtifact } from '../utils.js'; + +export async function inspectContract(contractArtifactFile: string, debugLogger: DebugLogger, log: LogFn) { + const contractArtifact = await getContractArtifact(contractArtifactFile, log); + const contractFns = contractArtifact.functions.filter(f => f.name !== 'compute_note_hash_and_nullifier'); + if (contractFns.length === 0) { + log(`No functions found for contract ${contractArtifact.name}`); + } + const contractClass = getContractClassFromArtifact(contractArtifact); + const bytecodeLengthInFields = 1 + Math.ceil(contractClass.packedBytecode.length / 31); + + log(`Contract class details:`); + log(`\tidentifier: ${contractClass.id.toString()}`); + log(`\tartifact hash: ${contractClass.artifactHash.toString()}`); + log(`\tprivate function tree root: ${contractClass.privateFunctionsRoot.toString()}`); + log(`\tpublic bytecode commitment: ${contractClass.publicBytecodeCommitment.toString()}`); + log(`\tpublic bytecode length: ${contractClass.packedBytecode.length} bytes (${bytecodeLengthInFields} fields)`); + log(`\nExternal functions:`); + contractFns.filter(f => !f.isInternal).forEach(f => logFunction(f, log)); + log(`\nInternal functions:`); + contractFns.filter(f => f.isInternal).forEach(f => logFunction(f, log)); +} + +function logFunction(fn: FunctionArtifact, log: LogFn) { + const signatureWithParameterNames = decodeFunctionSignatureWithParameterNames(fn.name, fn.parameters); + const signature = decodeFunctionSignature(fn.name, fn.parameters); + const selector = FunctionSelector.fromSignature(signature); + const bytecodeSize = fn.bytecode.length; + const bytecodeHash = sha256(fn.bytecode).toString('hex'); + log( + `${fn.functionType} ${signatureWithParameterNames} \n\tfunction signature: ${signature}\n\tselector: ${selector}\n\tbytecode: ${bytecodeSize} bytes (sha256 ${bytecodeHash})`, + ); +} diff --git a/yarn-project/cli/src/cmds/parse_parameter_struct.ts b/yarn-project/cli/src/cmds/parse_parameter_struct.ts new file mode 100644 index 00000000000..d8b29211d3a --- /dev/null +++ b/yarn-project/cli/src/cmds/parse_parameter_struct.ts @@ -0,0 +1,27 @@ +import { StructType } from '@aztec/foundation/abi'; +import { JsonStringify } from '@aztec/foundation/json-rpc'; +import { LogFn } from '@aztec/foundation/log'; + +import { parseStructString } from '../encoding.js'; +import { getContractArtifact } from '../utils.js'; + +export async function parseParameterStruct( + encodedString: string, + contractArtifactPath: string, + parameterName: string, + log: LogFn, +) { + const contractArtifact = await getContractArtifact(contractArtifactPath, log); + const parameterAbitype = contractArtifact.functions + .map(({ parameters }) => parameters) + .flat() + .find(({ name, type }) => name === parameterName && type.kind === 'struct'); + + if (!parameterAbitype) { + log(`No struct parameter found with name ${parameterName}`); + return; + } + + const data = parseStructString(encodedString, parameterAbitype.type as StructType); + log(`\nStruct Data: \n${JsonStringify(data, true)}\n`); +} diff --git a/yarn-project/cli/src/cmds/register_account.ts b/yarn-project/cli/src/cmds/register_account.ts new file mode 100644 index 00000000000..86853523708 --- /dev/null +++ b/yarn-project/cli/src/cmds/register_account.ts @@ -0,0 +1,21 @@ +import { type Fr } from '@aztec/foundation/fields'; +import { type DebugLogger, type LogFn } from '@aztec/foundation/log'; + +import { createCompatibleClient } from '../client.js'; + +export async function registerAccount( + rpcUrl: string, + privateKey: Fr, + partialAddress: Fr, + debugLogger: DebugLogger, + log: LogFn, +) { + const client = await createCompatibleClient(rpcUrl, debugLogger); + + const { address, publicKeys } = await client.registerAccount(privateKey, partialAddress); + + log(`\nRegistered account:\n`); + log(`Address: ${address.toString()}`); + log(`Public key: ${publicKeys.toString()}`); + log(`Partial address: ${partialAddress.toString()}`); +} diff --git a/yarn-project/cli/src/cmds/register_recipient.ts b/yarn-project/cli/src/cmds/register_recipient.ts new file mode 100644 index 00000000000..57b2e186317 --- /dev/null +++ b/yarn-project/cli/src/cmds/register_recipient.ts @@ -0,0 +1,19 @@ +import { type AztecAddress, type Fr } from '@aztec/aztec.js'; +import { CompleteAddress } from '@aztec/circuit-types'; +import { type PublicKeys } from '@aztec/circuits.js'; +import { type DebugLogger, type LogFn } from '@aztec/foundation/log'; + +import { createCompatibleClient } from '../client.js'; + +export async function registerRecipient( + aztecAddress: AztecAddress, + publicKeys: PublicKeys, + partialAddress: Fr, + rpcUrl: string, + debugLogger: DebugLogger, + log: LogFn, +) { + const client = await createCompatibleClient(rpcUrl, debugLogger); + await client.registerRecipient(new CompleteAddress(aztecAddress, publicKeys, partialAddress)); + log(`\nRegistered details for account with address: ${aztecAddress}\n`); +} diff --git a/yarn-project/cli/src/cmds/send.ts b/yarn-project/cli/src/cmds/send.ts new file mode 100644 index 00000000000..d9f9341f3bb --- /dev/null +++ b/yarn-project/cli/src/cmds/send.ts @@ -0,0 +1,39 @@ +import { getSchnorrAccount } from '@aztec/accounts/schnorr'; +import { type AztecAddress, Contract, type Fq, Fr } from '@aztec/aztec.js'; +import { type DebugLogger, type LogFn } from '@aztec/foundation/log'; + +import { createCompatibleClient } from '../client.js'; +import { prepTx } from '../utils.js'; + +export async function send( + functionName: string, + functionArgsIn: any[], + contractArtifactPath: string, + contractAddress: AztecAddress, + encryptionPrivateKey: Fr, + signingPrivateKey: Fq, + rpcUrl: string, + wait: boolean, + debugLogger: DebugLogger, + log: LogFn, +) { + const { functionArgs, contractArtifact } = await prepTx(contractArtifactPath, functionName, functionArgsIn, log); + + const client = await createCompatibleClient(rpcUrl, debugLogger); + const wallet = await getSchnorrAccount(client, encryptionPrivateKey, signingPrivateKey, Fr.ZERO).getWallet(); + const contract = await Contract.at(contractAddress, contractArtifact, wallet); + const tx = contract.methods[functionName](...functionArgs).send(); + log(`\nTransaction hash: ${(await tx.getTxHash()).toString()}`); + if (wait) { + await tx.wait(); + + log('Transaction has been mined'); + + const receipt = await tx.getReceipt(); + log(`Status: ${receipt.status}\n`); + log(`Block number: ${receipt.blockNumber}`); + log(`Block hash: ${receipt.blockHash?.toString('hex')}`); + } else { + log('Transaction pending. Check status with get-tx-receipt'); + } +} diff --git a/yarn-project/cli/src/encoding.ts b/yarn-project/cli/src/encoding.ts new file mode 100644 index 00000000000..d05c90b894f --- /dev/null +++ b/yarn-project/cli/src/encoding.ts @@ -0,0 +1,117 @@ +import { ABIParameter, AbiType, StructType } from '@aztec/foundation/abi'; +import { Fr } from '@aztec/foundation/fields'; + +/** + * Parses a hex string into an ABI struct type. + * @param str - The encoded hex string. + * @param abiType - The ABI Struct type. + * @returns An object in the ABI struct type's format. + */ +export function parseStructString(str: string, abiType: StructType) { + // Assign string bytes to struct fields. + const buf = Buffer.from(str.replace(/^0x/i, ''), 'hex'); + const struct: any = {}; + let byteIndex = 0; + let argIndex = 0; + while (byteIndex < buf.length) { + const { name } = abiType.fields[argIndex]; + struct[name] = Fr.fromBuffer(buf.subarray(byteIndex, byteIndex + 32)); + byteIndex += 32; + argIndex += 1; + } + + return struct; +} + +/** + * Helper function to encode CLI string args to an appropriate JS type. + * @param arg - The CLI argument. + * @param abiType - The type as described by the contract's ABI. + * @returns The encoded argument. + */ +function encodeArg(arg: string, abiType: AbiType, name: string): any { + const { kind } = abiType; + if (kind === 'field' || kind === 'integer') { + let res: bigint; + try { + res = BigInt(arg); + } catch (err) { + throw new Error( + `Invalid value passed for ${name}. Could not parse ${arg} as a${kind === 'integer' ? 'n' : ''} ${kind}.`, + ); + } + return res; + } else if (kind === 'boolean') { + if (arg === 'true') { + return true; + } + if (arg === 'false') { + return false; + } else { + throw Error(`Invalid boolean value passed for ${name}: ${arg}.`); + } + } else if (kind === 'string') { + return arg; + } else if (kind === 'array') { + let arr; + const res = []; + try { + arr = JSON.parse(arg); + } catch { + throw new Error(`Unable to parse arg ${arg} as array for ${name} parameter`); + } + if (!Array.isArray(arr)) { + throw Error(`Invalid argument ${arg} passed for array parameter ${name}.`); + } + if (arr.length !== abiType.length) { + throw Error(`Invalid array length passed for ${name}. Expected ${abiType.length}, received ${arr.length}.`); + } + for (let i = 0; i < abiType.length; i += 1) { + res.push(encodeArg(arr[i], abiType.type, name)); + } + return res; + } else if (kind === 'struct') { + // check if input is encoded long string + if (arg.startsWith('0x')) { + return parseStructString(arg, abiType); + } + let obj; + try { + obj = JSON.parse(arg); + } catch { + throw new Error(`Unable to parse arg ${arg} as struct`); + } + if (Array.isArray(obj)) { + throw Error(`Array passed for arg ${name}. Expected a struct.`); + } + const res: any = {}; + for (const field of abiType.fields) { + // Remove field name from list as it's present + const arg = obj[field.name]; + if (!arg) { + throw Error(`Expected field ${field.name} not found in struct ${name}.`); + } + res[field.name] = encodeArg(obj[field.name], field.type, field.name); + } + return res; + } +} + +/** + * Tries to encode function args to their equivalent TS type. + * @param args - An array of function's / constructor's args. + * @returns The encoded array. + */ +export function encodeArgs(args: any[], params: ABIParameter[]) { + if (args.length !== params.length) { + throw new Error( + `Invalid args provided.\nExpected args: [${params + .map(param => param.name + ': ' + param.type.kind) + .join(', ')}]\nReceived args: ${args.join(', ')}`, + ); + } + return args.map((arg: any, index) => { + const { type, name } = params[index]; + return encodeArg(arg, type, name); + }); +} diff --git a/yarn-project/cli/src/github.ts b/yarn-project/cli/src/github.ts new file mode 100644 index 00000000000..0486c382369 --- /dev/null +++ b/yarn-project/cli/src/github.ts @@ -0,0 +1,3 @@ +export const GITHUB_OWNER = 'AztecProtocol'; +export const GITHUB_REPO = 'aztec-packages'; +export const GITHUB_TAG_PREFIX = 'aztec-packages'; diff --git a/yarn-project/cli/src/index.ts b/yarn-project/cli/src/index.ts new file mode 100644 index 00000000000..d24b1631b9f --- /dev/null +++ b/yarn-project/cli/src/index.ts @@ -0,0 +1,503 @@ +import { Fr } from '@aztec/circuits.js'; +import { DebugLogger, LogFn } from '@aztec/foundation/log'; +import { fileURLToPath } from '@aztec/foundation/url'; + +import { Command, Option } from 'commander'; +import { lookup } from 'dns/promises'; +import { readFileSync } from 'fs'; +import { dirname, resolve } from 'path'; + +import { + parseAztecAddress, + parseEthereumAddress, + parseField, + parseFieldFromHexString, + parseOptionalAztecAddress, + parseOptionalInteger, + parseOptionalLogId, + parseOptionalSelector, + parseOptionalTxHash, + parsePartialAddress, + parsePrivateKey, + parsePublicKey, + parseTxHash, +} from './parse_args.js'; + +/** + * If we can successfully resolve 'host.docker.internal', then we are running in a container, and we should treat + * localhost as being host.docker.internal. + */ +const getLocalhost = () => + lookup('host.docker.internal') + .then(() => 'host.docker.internal') + .catch(() => 'localhost'); + +const LOCALHOST = await getLocalhost(); +const { ETHEREUM_HOST = `http://${LOCALHOST}:8545`, PRIVATE_KEY, API_KEY, CLI_VERSION } = process.env; + +/** + * Returns commander program that defines the CLI. + * @param log - Console logger. + * @param debugLogger - Debug logger. + * @returns The CLI. + */ +export function getProgram(log: LogFn, debugLogger: DebugLogger): Command { + const program = new Command(); + + const packageJsonPath = resolve(dirname(fileURLToPath(import.meta.url)), '../package.json'); + const cliVersion: string = CLI_VERSION || JSON.parse(readFileSync(packageJsonPath).toString()).version; + const logJson = (obj: object) => log(JSON.stringify(obj, null, 2)); + + program.name('aztec-cli').description('CLI for interacting with Aztec.').version(cliVersion); + + const pxeOption = new Option('-u, --rpc-url ', 'URL of the PXE') + .env('PXE_URL') + .default(`http://${LOCALHOST}:8080`) + .makeOptionMandatory(true); + + const createPrivateKeyOption = (description: string, mandatory: boolean) => + new Option('-k, --private-key ', description) + .env('PRIVATE_KEY') + .argParser(parsePrivateKey) + .makeOptionMandatory(mandatory); + + program + .command('deploy-l1-contracts') + .description('Deploys all necessary Ethereum contracts for Aztec.') + .requiredOption( + '-u, --rpc-url ', + 'Url of the ethereum host. Chain identifiers localhost and testnet can be used', + ETHEREUM_HOST, + ) + .option('-a, --api-key ', 'Api key for the ethereum host', API_KEY) + .requiredOption('-p, --private-key ', 'The private key to use for deployment', PRIVATE_KEY) + .option( + '-m, --mnemonic ', + 'The mnemonic to use in deployment', + 'test test test test test test test test test test test junk', + ) + .action(async options => { + const { deployL1Contracts } = await import('./cmds/deploy_l1_contracts.js'); + await deployL1Contracts( + options.rpcUrl, + options.apiKey ?? '', + options.privateKey, + options.mnemonic, + log, + debugLogger, + ); + }); + + program + .command('generate-private-key') + .summary('Generates an encryption private key.') + .description( + 'Generates a private key which fits into the scalar field used by Grumpkin curve, can be used as an encryption private key.', + ) + .option( + '-m, --mnemonic', + 'An optional mnemonic string used for the private key generation. If not provided, random private key will be generated.', + ) + .action(async options => { + const { generatePrivateKey } = await import('./cmds/generate_private_key.js'); + generatePrivateKey(options.mnemonic, log); + }); + + program + .command('generate-p2p-private-key') + .summary('Generates a LibP2P peer private key.') + .description('Generates a private key that can be used for running a node on a LibP2P network.') + .action(async () => { + const { generateP2PPrivateKey } = await import('./cmds/generate_p2p_private_key.js'); + await generateP2PPrivateKey(log); + }); + + program + .command('create-account') + .description( + 'Creates an aztec account that can be used for sending transactions. Registers the account on the PXE and deploys an account contract. Uses a Schnorr single-key account which uses the same key for encryption and authentication (not secure for production usage).', + ) + .summary('Creates an aztec account that can be used for sending transactions.') + .addOption( + createPrivateKeyOption('Private key for note encryption and transaction signing. Uses random by default.', false), + ) + .addOption(pxeOption) + // `options.wait` is default true. Passing `--no-wait` will set it to false. + // https://github.com/tj/commander.js#other-option-types-negatable-boolean-and-booleanvalue + .option('--no-wait', 'Skip waiting for the contract to be deployed. Print the hash of deployment transaction') + .action(async ({ rpcUrl, privateKey, wait }) => { + const { createAccount } = await import('./cmds/create_account.js'); + await createAccount(rpcUrl, privateKey, wait, debugLogger, log); + }); + + program + .command('register-account') + .description( + 'Registers an aztec account that can be used for sending transactions. Registers the account on the PXE. Uses a Schnorr single-key account which uses the same key for encryption and authentication (not secure for production usage).', + ) + .summary('Registers an aztec account that can be used for sending transactions.') + .addOption(createPrivateKeyOption('Private key for note encryption and transaction signing.', true)) + .requiredOption( + '-pa, --partial-address ', + 'The partially computed address of the account contract.', + parsePartialAddress, + ) + .addOption(pxeOption) + .action(async ({ rpcUrl, privateKey, partialAddress }) => { + const { registerAccount } = await import('./cmds/register_account.js'); + await registerAccount(rpcUrl, privateKey, partialAddress, debugLogger, log); + }); + + program + .command('deploy') + .description('Deploys a compiled Aztec.nr contract to Aztec.') + .argument( + '', + "A compiled Aztec.nr contract's artifact in JSON format or name of a contract artifact exported by @aztec/noir-contracts.js", + ) + .option('--initializer ', 'The contract initializer function to call') + .option('-a, --args ', 'Contract constructor arguments', []) + .addOption(pxeOption) + .option( + '-k, --public-key ', + 'Optional encryption public key for this address. Set this value only if this contract is expected to receive private notes, which will be encrypted using this public key.', + parsePublicKey, + ) + .option( + '-p, --portal-address ', + 'Optional L1 portal address to link the contract to.', + parseEthereumAddress, + ) + .option( + '-s, --salt ', + 'Optional deployment salt as a hex string for generating the deployment address.', + parseFieldFromHexString, + ) + .addOption(createPrivateKeyOption("The sender's private key.", true)) + .option('--json', 'Emit output as json') + // `options.wait` is default true. Passing `--no-wait` will set it to false. + // https://github.com/tj/commander.js#other-option-types-negatable-boolean-and-booleanvalue + .option('--no-wait', 'Skip waiting for the contract to be deployed. Print the hash of deployment transaction') + .action( + async ( + artifactPath, + { json, rpcUrl, publicKey, args: rawArgs, portalAddress, salt, wait, privateKey, initializer }, + ) => { + const { deploy } = await import('./cmds/deploy.js'); + await deploy( + artifactPath, + json, + rpcUrl, + publicKey, + rawArgs, + portalAddress, + salt, + privateKey, + initializer, + wait, + debugLogger, + log, + logJson, + ); + }, + ); + + program + .command('check-deploy') + .description('Checks if a contract is deployed to the specified Aztec address.') + .requiredOption( + '-ca, --contract-address
', + 'An Aztec address to check if contract has been deployed to.', + parseAztecAddress, + ) + .addOption(pxeOption) + .action(async options => { + const { checkDeploy } = await import('./cmds/check_deploy.js'); + await checkDeploy(options.rpcUrl, options.contractAddress, debugLogger, log); + }); + + program + .command('add-contract') + .description( + 'Adds an existing contract to the PXE. This is useful if you have deployed a contract outside of the PXE and want to use it with the PXE.', + ) + .requiredOption( + '-c, --contract-artifact ', + "A compiled Aztec.nr contract's ABI in JSON format or name of a contract ABI exported by @aztec/noir-contracts.js", + ) + .requiredOption('-ca, --contract-address
', 'Aztec address of the contract.', parseAztecAddress) + .requiredOption('--init-hash ', 'Initialization hash', parseFieldFromHexString) + .option('--salt ', 'Optional deployment salt', parseFieldFromHexString) + .option('-p, --public-key ', 'Optional public key for this contract', parsePublicKey) + .option('--portal-address
', 'Optional address to a portal contract on L1', parseEthereumAddress) + .option('--deployer-address
', 'Optional address of the contract deployer', parseAztecAddress) + .addOption(pxeOption) + .action(async options => { + const { addContract } = await import('./cmds/add_contract.js'); + await addContract( + options.rpcUrl, + options.contractArtifact, + options.contractAddress, + options.initHash, + options.salt ?? Fr.ZERO, + options.publicKey, + options.portalContract, + options.deployerAddress, + debugLogger, + log, + ); + }); + + program + .command('get-tx-receipt') + .description('Gets the receipt for the specified transaction hash.') + .argument('', 'A transaction hash to get the receipt for.', parseTxHash) + .addOption(pxeOption) + .action(async (txHash, options) => { + const { getTxReceipt } = await import('./cmds/get_tx_receipt.js'); + await getTxReceipt(options.rpcUrl, txHash, debugLogger, log); + }); + + program + .command('get-contract-data') + .description('Gets information about the Aztec contract deployed at the specified address.') + .argument('', 'Aztec address of the contract.', parseAztecAddress) + .addOption(pxeOption) + .option('-b, --include-bytecode ', "Include the contract's public function bytecode, if any.", false) + .action(async (contractAddress, options) => { + const { getContractData } = await import('./cmds/get_contract_data.js'); + await getContractData(options.rpcUrl, contractAddress, options.includeBytecode, debugLogger, log); + }); + + program + .command('get-logs') + .description('Gets all the unencrypted logs from an intersection of all the filter params.') + .option('-tx, --tx-hash ', 'A transaction hash to get the receipt for.', parseOptionalTxHash) + .option( + '-fb, --from-block ', + 'Initial block number for getting logs (defaults to 1).', + parseOptionalInteger, + ) + .option('-tb, --to-block ', 'Up to which block to fetch logs (defaults to latest).', parseOptionalInteger) + .option('-al --after-log ', 'ID of a log after which to fetch the logs.', parseOptionalLogId) + .option('-ca, --contract-address
', 'Contract address to filter logs by.', parseOptionalAztecAddress) + .option('-s, --selector ', 'Event selector to filter logs by.', parseOptionalSelector) + .addOption(pxeOption) + .option('--follow', 'If set, will keep polling for new logs until interrupted.') + .action(async ({ txHash, fromBlock, toBlock, afterLog, contractAddress, selector, rpcUrl, follow }) => { + const { getLogs } = await import('./cmds/get_logs.js'); + await getLogs(txHash, fromBlock, toBlock, afterLog, contractAddress, selector, rpcUrl, follow, debugLogger, log); + }); + + program + .command('register-recipient') + .description('Register a recipient in the PXE.') + .requiredOption('-a, --address ', "The account's Aztec address.", parseAztecAddress) + .requiredOption('-p, --public-key ', 'The account public key.', parsePublicKey) + .requiredOption( + '-pa, --partial-address ', + 'The partially computed address of the account contract.', + parsePartialAddress, + ) + .addOption(pxeOption) + .action(async ({ address, publicKey, partialAddress, rpcUrl }) => { + const { registerRecipient } = await import('./cmds/register_recipient.js'); + await registerRecipient(address, publicKey, partialAddress, rpcUrl, debugLogger, log); + }); + + program + .command('get-accounts') + .description('Gets all the Aztec accounts stored in the PXE.') + .addOption(pxeOption) + .option('--json', 'Emit output as json') + .action(async (options: any) => { + const { getAccounts } = await import('./cmds/get_accounts.js'); + await getAccounts(options.rpcUrl, options.json, debugLogger, log, logJson); + }); + + program + .command('get-account') + .description('Gets an account given its Aztec address.') + .argument('
', 'The Aztec address to get account for', parseAztecAddress) + .addOption(pxeOption) + .action(async (address, options) => { + const { getAccount } = await import('./cmds/get_account.js'); + await getAccount(address, options.rpcUrl, debugLogger, log); + }); + + program + .command('get-recipients') + .description('Gets all the recipients stored in the PXE.') + .addOption(pxeOption) + .action(async (options: any) => { + const { getRecipients } = await import('./cmds/get_recipients.js'); + await getRecipients(options.rpcUrl, debugLogger, log); + }); + + program + .command('get-recipient') + .description('Gets a recipient given its Aztec address.') + .argument('
', 'The Aztec address to get recipient for', parseAztecAddress) + .addOption(pxeOption) + .action(async (address, options) => { + const { getRecipient } = await import('./cmds/get_recipient.js'); + await getRecipient(address, options.rpcUrl, debugLogger, log); + }); + + program + .command('send') + .description('Calls a function on an Aztec contract.') + .argument('', 'Name of function to execute') + .option('-a, --args [functionArgs...]', 'Function arguments', []) + .requiredOption( + '-c, --contract-artifact ', + "A compiled Aztec.nr contract's ABI in JSON format or name of a contract ABI exported by @aztec/noir-contracts.js", + ) + .requiredOption('-ca, --contract-address
', 'Aztec address of the contract.', parseAztecAddress) + .addOption(createPrivateKeyOption("The sender's private key.", true)) + .addOption(pxeOption) + .option('--no-wait', 'Print transaction hash without waiting for it to be mined') + .action(async (functionName, options) => { + const { send } = await import('./cmds/send.js'); + await send( + functionName, + options.args, + options.contractArtifact, + options.contractAddress, + options.privateKey, + options.rpcUrl, + !options.noWait, + debugLogger, + log, + ); + }); + + program + .command('call') + .description( + 'Simulates the execution of a view (read-only) function on a deployed contract, without modifying state.', + ) + .argument('', 'Name of function to call') + .option('-a, --args [functionArgs...]', 'Function arguments', []) + .requiredOption( + '-c, --contract-artifact ', + "A compiled Aztec.nr contract's ABI in JSON format or name of a contract ABI exported by @aztec/noir-contracts.js", + ) + .requiredOption('-ca, --contract-address
', 'Aztec address of the contract.', parseAztecAddress) + .option('-f, --from ', 'Aztec address of the caller. If empty, will use the first account from RPC.') + .addOption(pxeOption) + .action(async (functionName, options) => { + const { call } = await import('./cmds/call.js'); + await call( + functionName, + options.args, + options.contractArtifact, + options.contractAddress, + options.from, + options.rpcUrl, + debugLogger, + log, + ); + }); + + program + .command('add-note') + .description('Adds a note to the database in the PXE.') + .argument('
', 'The Aztec address of the note owner.', parseAztecAddress) + .argument('', 'Aztec address of the contract.', parseAztecAddress) + .argument('', 'The storage slot of the note.', parseField) + .argument('', 'The type ID of the note.', parseField) + .argument('', 'The tx hash of the tx containing the note.', parseTxHash) + .requiredOption('-n, --note [note...]', 'The members of a Note serialized as hex strings.', []) + .addOption(pxeOption) + .action(async (address, contractAddress, storageSlot, noteTypeId, txHash, options) => { + const { addNote } = await import('./cmds/add_note.js'); + await addNote( + address, + contractAddress, + storageSlot, + noteTypeId, + txHash, + options.note, + options.rpcUrl, + debugLogger, + ); + }); + + // Helper for users to decode hex strings into structs if needed. + program + .command('parse-parameter-struct') + .description("Helper for parsing an encoded string into a contract's parameter struct.") + .argument('', 'The encoded hex string') + .requiredOption( + '-c, --contract-artifact ', + "A compiled Aztec.nr contract's ABI in JSON format or name of a contract ABI exported by @aztec/noir-contracts.js", + ) + .requiredOption('-p, --parameter ', 'The name of the struct parameter to decode into') + .action(async (encodedString, options) => { + const { parseParameterStruct } = await import('./cmds/parse_parameter_struct.js'); + await parseParameterStruct(encodedString, options.contractArtifact, options.parameter, log); + }); + + program + .command('block-number') + .description('Gets the current Aztec L2 block number.') + .addOption(pxeOption) + .action(async (options: any) => { + const { blockNumber } = await import('./cmds/block_number.js'); + await blockNumber(options.rpcUrl, debugLogger, log); + }); + + program + .command('example-contracts') + .description('Lists the example contracts available to deploy from @aztec/noir-contracts.js') + .action(async () => { + const { exampleContracts } = await import('./cmds/example_contracts.js'); + await exampleContracts(log); + }); + + program + .command('get-node-info') + .description('Gets the information of an aztec node at a URL.') + .addOption(pxeOption) + .action(async options => { + const { getNodeInfo } = await import('./cmds/get_node_info.js'); + await getNodeInfo(options.rpcUrl, debugLogger, log); + }); + + program + .command('inspect-contract') + .description('Shows list of external callable functions for a contract') + .argument( + '', + `A compiled Noir contract's artifact in JSON format or name of a contract artifact exported by @aztec/noir-contracts.js`, + ) + .action(async (contractArtifactFile: string) => { + const { inspectContract } = await import('./cmds/inspect_contract.js'); + await inspectContract(contractArtifactFile, debugLogger, log); + }); + + program + .command('compute-selector') + .description('Given a function signature, it computes a selector') + .argument('', 'Function signature to compute selector for e.g. foo(Field)') + .action(async (functionSignature: string) => { + const { computeSelector } = await import('./cmds/compute_selector.js'); + computeSelector(functionSignature, log); + }); + + program + .command('update') + .description('Updates Nodejs and Noir dependencies') + .argument('[projectPath]', 'Path to the project directory', process.cwd()) + .option('--contract [paths...]', 'Paths to contracts to update dependencies', []) + .option('--aztec-version ', 'The version to update Aztec packages to. Defaults to latest', 'latest') + .addOption(pxeOption) + .action(async (projectPath: string, options) => { + const { update } = await import('./update/update.js'); + const { contract, aztecVersion, rpcUrl } = options; + await update(projectPath, contract, rpcUrl, aztecVersion, log); + }); + + return program; +} diff --git a/yarn-project/cli/src/parse_args.ts b/yarn-project/cli/src/parse_args.ts new file mode 100644 index 00000000000..d71129d48dd --- /dev/null +++ b/yarn-project/cli/src/parse_args.ts @@ -0,0 +1,248 @@ +import { FunctionSelector } from '@aztec/aztec.js/abi'; +import { AztecAddress } from '@aztec/aztec.js/aztec_address'; +import { EthAddress } from '@aztec/aztec.js/eth_address'; +import { Fr, GrumpkinScalar, Point } from '@aztec/aztec.js/fields'; +import { LogId } from '@aztec/aztec.js/log_id'; +import { TxHash } from '@aztec/aztec.js/tx_hash'; + +import { InvalidArgumentError } from 'commander'; + +/** + * Removes the leading 0x from a hex string. If no leading 0x is found the string is returned unchanged. + * @param hex - A hex string + * @returns A new string with leading 0x removed + */ +const stripLeadingHex = (hex: string) => { + if (hex.length > 2 && hex.startsWith('0x')) { + return hex.substring(2); + } + return hex; +}; + +/** + * Parses a hex encoded string to an Fr integer + * @param str - Hex encoded string + * @returns A integer + */ +export function parseFieldFromHexString(str: string): Fr { + const hex = stripLeadingHex(str); + + // ensure it's a hex string + if (!hex.match(/^[0-9a-f]+$/i)) { + throw new InvalidArgumentError('Invalid hex string'); + } + + // pad it so that we may read it as a buffer. + // Buffer needs _exactly_ two hex characters per byte + const padded = hex.length % 2 === 1 ? '0' + hex : hex; + + // finally, turn it into an integer + return Fr.fromBuffer(Buffer.from(padded, 'hex')); +} + +/** + * Parses an AztecAddress from a string. + * @param address - A serialized Aztec address + * @returns An Aztec address + * @throws InvalidArgumentError if the input string is not valid. + */ +export function parseAztecAddress(address: string): AztecAddress { + try { + return AztecAddress.fromString(address); + } catch { + throw new InvalidArgumentError(`Invalid address: ${address}`); + } +} + +/** + * Parses an Ethereum address from a string. + * @param address - A serialized Ethereum address + * @returns An Ethereum address + * @throws InvalidArgumentError if the input string is not valid. + */ +export function parseEthereumAddress(address: string): EthAddress { + try { + return EthAddress.fromString(address); + } catch { + throw new InvalidArgumentError(`Invalid address: ${address}`); + } +} + +/** + * Parses an AztecAddress from a string. + * @param address - A serialized Aztec address + * @returns An Aztec address + * @throws InvalidArgumentError if the input string is not valid. + */ +export function parseOptionalAztecAddress(address: string): AztecAddress | undefined { + if (!address) { + return undefined; + } + return parseAztecAddress(address); +} + +/** + * Parses an optional log ID string into a LogId object. + * + * @param logId - The log ID string to parse. + * @returns The parsed LogId object, or undefined if the log ID is missing or empty. + */ +export function parseOptionalLogId(logId: string): LogId | undefined { + if (!logId) { + return undefined; + } + return LogId.fromString(logId); +} + +/** + * Parses a selector from a string. + * @param selector - A serialized selector. + * @returns A selector. + * @throws InvalidArgumentError if the input string is not valid. + */ +export function parseOptionalSelector(selector: string): FunctionSelector | undefined { + if (!selector) { + return undefined; + } + try { + return FunctionSelector.fromString(selector); + } catch { + throw new InvalidArgumentError(`Invalid selector: ${selector}`); + } +} + +/** + * Parses a string into an integer or returns undefined if the input is falsy. + * + * @param value - The string to parse into an integer. + * @returns The parsed integer, or undefined if the input string is falsy. + * @throws If the input is not a valid integer. + */ +export function parseOptionalInteger(value: string): number | undefined { + if (!value) { + return undefined; + } + const parsed = Number(value); + if (!Number.isInteger(parsed)) { + throw new InvalidArgumentError('Invalid integer.'); + } + return parsed; +} + +/** + * Parses a TxHash from a string. + * @param txHash - A transaction hash + * @returns A TxHash instance + * @throws InvalidArgumentError if the input string is not valid. + */ +export function parseTxHash(txHash: string): TxHash { + try { + return TxHash.fromString(txHash); + } catch { + throw new InvalidArgumentError(`Invalid transaction hash: ${txHash}`); + } +} + +/** + * Parses an optional TxHash from a string. + * Calls parseTxHash internally. + * @param txHash - A transaction hash + * @returns A TxHash instance, or undefined if the input string is falsy. + * @throws InvalidArgumentError if the input string is not valid. + */ +export function parseOptionalTxHash(txHash: string): TxHash | undefined { + if (!txHash) { + return undefined; + } + return parseTxHash(txHash); +} + +/** + * Parses a public key from a string. + * @param publicKey - A public key + * @returns A Point instance + * @throws InvalidArgumentError if the input string is not valid. + */ +export function parsePublicKey(publicKey: string): Point { + try { + return Point.fromString(publicKey); + } catch (err) { + throw new InvalidArgumentError(`Invalid public key: ${publicKey}`); + } +} + +/** + * Parses a partial address from a string. + * @param address - A partial address + * @returns A Fr instance + * @throws InvalidArgumentError if the input string is not valid. + */ +export function parsePartialAddress(address: string): Fr { + try { + return Fr.fromString(address); + } catch (err) { + throw new InvalidArgumentError(`Invalid partial address: ${address}`); + } +} + +/** + * Parses a private key from a string. + * @param privateKey - A string + * @returns A private key + * @throws InvalidArgumentError if the input string is not valid. + */ +export function parsePrivateKey(privateKey: string): GrumpkinScalar { + try { + const value = GrumpkinScalar.fromString(privateKey); + // most likely a badly formatted key was passed + if (value.isZero()) { + throw new Error('Private key must not be zero'); + } + + return value; + } catch (err) { + throw new InvalidArgumentError(`Invalid private key: ${privateKey}`); + } +} + +/** + * Parses a field from a string. + * @param field - A string representing the field. + * @returns A field. + * @throws InvalidArgumentError if the input string is not valid. + */ +export function parseField(field: string): Fr { + try { + const isHex = field.startsWith('0x') || field.match(new RegExp(`^[0-9a-f]{${Fr.SIZE_IN_BYTES * 2}}$`, 'i')); + if (isHex) { + return Fr.fromString(field); + } + + if (['true', 'false'].includes(field)) { + return new Fr(field === 'true'); + } + + const isNumber = +field || field === '0'; + if (isNumber) { + return new Fr(BigInt(field)); + } + + const isBigInt = field.endsWith('n'); + if (isBigInt) { + return new Fr(BigInt(field.replace(/n$/, ''))); + } + + return new Fr(BigInt(field)); + } catch (err) { + throw new InvalidArgumentError(`Invalid field: ${field}`); + } +} + +/** + * Parses an array of strings to Frs. + * @param fields - An array of strings representing the fields. + * @returns An array of Frs. + */ +export function parseFields(fields: string[]): Fr[] { + return fields.map(parseField); +} diff --git a/yarn-project/cli/src/update/common.ts b/yarn-project/cli/src/update/common.ts new file mode 100644 index 00000000000..6041c1266e4 --- /dev/null +++ b/yarn-project/cli/src/update/common.ts @@ -0,0 +1,16 @@ +/** + * Tracks changes to dependencies + */ +export type DependencyChanges = { + /** Which file was changed */ + file: string; + /** changes done to the file */ + dependencies: Array<{ + /** Name of the dependency being changed */ + name: string; + /** Previous version of the dependency */ + from: string; + /** New version of the dependency (after the update) */ + to: string; + }>; +}; diff --git a/yarn-project/cli/src/update/noir.ts b/yarn-project/cli/src/update/noir.ts new file mode 100644 index 00000000000..0864293f4da --- /dev/null +++ b/yarn-project/cli/src/update/noir.ts @@ -0,0 +1,57 @@ +import { LogFn } from '@aztec/foundation/log'; +import { parseNoirPackageConfig } from '@aztec/foundation/noir'; + +import TOML from '@iarna/toml'; +import { readFile } from 'fs/promises'; +import { join, relative, resolve } from 'path'; + +import { atomicUpdateFile, prettyPrintNargoToml } from '../utils.js'; +import { DependencyChanges } from './common.js'; + +/** + * Updates Aztec.nr dependencies + * @param contractPath - Path to the contract to be updated + * @param tag - The tag to update to + * @param log - Logging function + */ +export async function updateAztecNr(contractPath: string, tag: string, log: LogFn): Promise { + const configFilepath = resolve(join(contractPath, 'Nargo.toml')); + const packageConfig = parseNoirPackageConfig(TOML.parse(await readFile(configFilepath, 'utf-8'))); + const changes: DependencyChanges = { + dependencies: [], + file: configFilepath, + }; + + log(`Updating Aztec.nr libraries to ${tag} in ${relative(process.cwd(), changes.file)}`); + for (const dep of Object.values(packageConfig.dependencies)) { + if (!('git' in dep)) { + continue; + } + + // remove trailing slash + const gitUrl = dep.git.toLowerCase().replace(/\/$/, ''); + if (gitUrl !== 'https://github.com/aztecprotocol/aztec-packages') { + continue; + } + + if (dep.tag !== tag) { + // show the Aztec.nr package name rather than the lib name + const dirParts = dep.directory?.split('/') ?? []; + changes.dependencies.push({ + name: dirParts.slice(-2).join('/'), + from: dep.tag, + to: tag, + }); + + dep.tag = tag; + dep.directory = dep.directory?.replace('yarn-project/', 'noir-projects/'); + } + } + + if (changes.dependencies.length > 0) { + const contents = prettyPrintNargoToml(packageConfig); + await atomicUpdateFile(configFilepath, contents); + } + + return changes; +} diff --git a/yarn-project/cli/src/update/npm.ts b/yarn-project/cli/src/update/npm.ts new file mode 100644 index 00000000000..ae085e6dfe7 --- /dev/null +++ b/yarn-project/cli/src/update/npm.ts @@ -0,0 +1,154 @@ +import { LogFn } from '@aztec/foundation/log'; + +import { spawnSync } from 'child_process'; +import { existsSync } from 'fs'; +import { readFile } from 'fs/promises'; +import { join, relative, resolve } from 'path'; +import { SemVer, parse } from 'semver'; + +import { atomicUpdateFile } from '../utils.js'; +import { DependencyChanges } from './common.js'; + +const deprecatedNpmPackages = new Set(['@aztec/cli', '@aztec/aztec-sandbox']); +const npmDeprecationMessage = ` +The following packages have been deprecated and will no longer be updated on the npm registry: +${Array.from(deprecatedNpmPackages) + .map(pkg => ` - ${pkg}`) + .join('\n')} +Remove them from package.json +`; + +/** + * Looks up a package.json file and returns its contents + * @param projectPath - Path to Nodejs project + * @returns The parsed package.json + */ +export async function readPackageJson(projectPath: string): Promise<{ + /** dependencies */ + dependencies?: Record; + /** devDependencies */ + devDependencies?: Record; +}> { + const configFilepath = resolve(join(projectPath, 'package.json')); + const pkg = JSON.parse(await readFile(configFilepath, 'utf-8')); + + return pkg; +} + +/** + * Queries the npm registry for the latest version of a package + * @param packageName - The package to query + * @param distTag - The distribution tag + * @returns The latest version of the package on that distribution tag + */ +export async function getNewestVersion(packageName: string, distTag = 'latest'): Promise { + const url = new URL(packageName, 'https://registry.npmjs.org/'); + const response = await fetch(url); + if (!response.ok) { + throw new Error(`Failed to fetch ${url}`); + } + + const body = await response.json(); + const latestVersion = parse(body['dist-tags'][distTag]); + if (!latestVersion) { + throw new Error(`Failed to get latest version from registry`); + } + + return latestVersion; +} + +/** + * Updates a project's \@aztec/* dependencies to the specific version + * @param projectPath - Path to Nodejs project + * @param aztecVersion - The version to update to + * @returns True if the project was updated + */ +export async function updateAztecDeps( + projectPath: string, + aztecVersion: SemVer, + log: LogFn, +): Promise { + const pkg = await readPackageJson(projectPath); + const changes: DependencyChanges = { + file: resolve(join(projectPath, 'package.json')), + dependencies: [], + }; + + log(`Updating @aztec packages to ${aztecVersion} in ${relative(process.cwd(), changes.file)}`); + const version = aztecVersion.version; + + let detectedDeprecatedPackages = false; + + for (const depType of ['dependencies', 'devDependencies'] as const) { + const dependencies = pkg[depType]; + if (!dependencies) { + continue; + } + + for (const name of Object.keys(dependencies)) { + if (!name.startsWith('@aztec/')) { + continue; + } + + // different release schedule + if (name === '@aztec/aztec-ui') { + continue; + } + + if (deprecatedNpmPackages.has(name)) { + detectedDeprecatedPackages = true; + continue; + } + + if (dependencies[name] !== version) { + changes.dependencies.push({ + name, + from: dependencies[name], + to: version, + }); + + dependencies[name] = version; + } + } + } + + if (detectedDeprecatedPackages) { + log(npmDeprecationMessage); + } + + if (changes.dependencies.length > 0) { + const contents = JSON.stringify(pkg, null, 2) + '\n'; + await atomicUpdateFile(resolve(join(projectPath, 'package.json')), contents); + } + + return changes; +} + +/** + * Updates a project's yarn.lock or package-lock.json + * @param projectPath - Path to Nodejs project + */ +export function updateLockfile(projectPath: string, log: LogFn): void { + const isNpm = existsSync(resolve(join(projectPath, 'package-lock.json'))); + const isYarn = existsSync(resolve(join(projectPath, 'yarn.lock'))); + const isPnpm = existsSync(resolve(join(projectPath, 'pnpm-lock.yaml'))); + + if (isPnpm) { + spawnSync('pnpm', ['install'], { + cwd: projectPath, + stdio: 'inherit', + }); + } else if (isYarn) { + spawnSync('yarn', ['install'], { + cwd: projectPath, + stdio: 'inherit', + }); + } else if (isNpm) { + spawnSync('npm', ['install'], { + cwd: projectPath, + stdio: 'inherit', + }); + } else { + log(`No lockfile found in ${projectPath}. Skipping lockfile update...`); + } +} diff --git a/yarn-project/cli/src/update/update.ts b/yarn-project/cli/src/update/update.ts new file mode 100644 index 00000000000..5c946f9953d --- /dev/null +++ b/yarn-project/cli/src/update/update.ts @@ -0,0 +1,79 @@ +/* eslint-disable jsdoc/require-jsdoc */ +import { LogFn } from '@aztec/foundation/log'; + +import { relative, resolve } from 'path'; +import { parse } from 'semver'; + +import { GITHUB_TAG_PREFIX } from '../github.js'; +import { DependencyChanges } from './common.js'; +import { updateAztecNr } from './noir.js'; +import { getNewestVersion, updateAztecDeps, updateLockfile } from './npm.js'; + +const AZTECJS_PACKAGE = '@aztec/aztec.js'; +const UPDATE_DOCS_URL = 'https://docs.aztec.network/developers/updating'; + +export async function update( + projectPath: string, + contracts: string[], + pxeUrl: string, + aztecVersion: string, + log: LogFn, +): Promise { + const targetAztecVersion = + aztecVersion === 'latest' ? await getNewestVersion(AZTECJS_PACKAGE, 'latest') : parse(aztecVersion); + + if (!targetAztecVersion) { + throw new Error(`Invalid aztec version ${aztecVersion}`); + } + + const projectDependencyChanges: DependencyChanges[] = []; + try { + const npmChanges = await updateAztecDeps(resolve(process.cwd(), projectPath), targetAztecVersion, log); + if (npmChanges.dependencies.length > 0) { + updateLockfile(projectPath, log); + } + + projectDependencyChanges.push(npmChanges); + } catch (err) { + if (err instanceof Error && 'code' in err && err.code === 'ENOENT') { + log(`No package.json found in ${projectPath}. Skipping npm update...`); + } else { + throw err; + } + } + + for (const contract of contracts) { + try { + projectDependencyChanges.push( + await updateAztecNr( + resolve(process.cwd(), projectPath, contract), + `${GITHUB_TAG_PREFIX}-v${targetAztecVersion.version}`, + log, + ), + ); + } catch (err) { + if (err instanceof Error && 'code' in err && err.code === 'ENOENT') { + log(`No Nargo.toml found in ${relative(process.cwd(), contract)}. Skipping...`); + } else { + throw err; + } + } + } + + log(`To update Docker containers follow instructions at ${UPDATE_DOCS_URL}`); + + projectDependencyChanges.forEach(changes => { + printChanges(changes, log); + }); +} + +function printChanges(changes: DependencyChanges, log: LogFn): void { + log(`\nIn ${relative(process.cwd(), changes.file)}:`); + if (changes.dependencies.length === 0) { + log(' No changes'); + } else { + changes.dependencies.forEach(({ name, from, to }) => { + log(` Updated ${name} from ${from} to ${to}`); + }); + } +} diff --git a/yarn-project/cli/src/utils.ts b/yarn-project/cli/src/utils.ts new file mode 100644 index 00000000000..756b3193c7b --- /dev/null +++ b/yarn-project/cli/src/utils.ts @@ -0,0 +1,239 @@ +import { type ContractArtifact, type FunctionArtifact, loadContractArtifact } from '@aztec/aztec.js/abi'; +import { AztecAddress } from '@aztec/aztec.js/aztec_address'; +import { type L1ContractArtifactsForDeployment } from '@aztec/aztec.js/ethereum'; +import { type PXE } from '@aztec/aztec.js/interfaces/pxe'; +import { DebugLogger, LogFn } from '@aztec/foundation/log'; +import { NoirPackageConfig } from '@aztec/foundation/noir'; +import { + AvailabilityOracleAbi, + AvailabilityOracleBytecode, + GasPortalAbi, + GasPortalBytecode, + PortalERC20Abi, + PortalERC20Bytecode, +} from '@aztec/l1-artifacts'; + +import TOML from '@iarna/toml'; +import { CommanderError, InvalidArgumentError } from 'commander'; +import { readFile, rename, writeFile } from 'fs/promises'; + +import { encodeArgs } from './encoding.js'; + +/** + * Helper type to dynamically import contracts. + */ +interface ArtifactsType { + [key: string]: ContractArtifact; +} + +/** + * Helper to get an ABI function or throw error if it doesn't exist. + * @param artifact - Contract's build artifact in JSON format. + * @param fnName - Function name to be found. + * @returns The function's ABI. + */ +export function getFunctionArtifact(artifact: ContractArtifact, fnName: string): FunctionArtifact { + const fn = artifact.functions.find(({ name }) => name === fnName); + if (!fn) { + throw Error(`Function ${fnName} not found in contract ABI.`); + } + return fn; +} + +/** + * 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. + */ +export async function deployAztecContracts( + rpcUrl: string, + apiKey: string, + privateKey: string, + mnemonic: string, + debugLogger: DebugLogger, +) { + const { + InboxAbi, + InboxBytecode, + OutboxAbi, + OutboxBytecode, + RegistryAbi, + RegistryBytecode, + RollupAbi, + RollupBytecode, + } = await import('@aztec/l1-artifacts'); + const { createEthereumChain, deployL1Contracts } = await import('@aztec/ethereum'); + const { mnemonicToAccount, privateKeyToAccount } = await import('viem/accounts'); + + const account = !privateKey + ? mnemonicToAccount(mnemonic!) + : privateKeyToAccount(`${privateKey.startsWith('0x') ? '' : '0x'}${privateKey}` as `0x${string}`); + const chain = createEthereumChain(rpcUrl, apiKey); + const l1Artifacts: L1ContractArtifactsForDeployment = { + registry: { + contractAbi: RegistryAbi, + contractBytecode: RegistryBytecode, + }, + inbox: { + contractAbi: InboxAbi, + contractBytecode: InboxBytecode, + }, + outbox: { + contractAbi: OutboxAbi, + contractBytecode: OutboxBytecode, + }, + availabilityOracle: { + contractAbi: AvailabilityOracleAbi, + contractBytecode: AvailabilityOracleBytecode, + }, + rollup: { + contractAbi: RollupAbi, + contractBytecode: RollupBytecode, + }, + gasToken: { + contractAbi: PortalERC20Abi, + contractBytecode: PortalERC20Bytecode, + }, + gasPortal: { + contractAbi: GasPortalAbi, + contractBytecode: GasPortalBytecode, + }, + }; + return await deployL1Contracts(chain.rpcUrl, account, chain.chainInfo, debugLogger, l1Artifacts); +} + +/** + * Gets all contracts available in \@aztec/noir-contracts.js. + * @returns The contract ABIs. + */ +export async function getExampleContractArtifacts(): Promise { + const imports: any = await import('@aztec/noir-contracts.js'); + return Object.fromEntries(Object.entries(imports).filter(([key]) => key.endsWith('Artifact'))) as any; +} + +/** + * Reads a file and converts it to an Aztec Contract ABI. + * @param fileDir - The directory of the compiled contract ABI. + * @returns The parsed contract artifact. + */ +export async function getContractArtifact(fileDir: string, log: LogFn) { + // first check if it's a noir-contracts example + const artifacts = await getExampleContractArtifacts(); + if (artifacts[fileDir]) { + return artifacts[fileDir] as ContractArtifact; + } + + let contents: string; + try { + contents = await readFile(fileDir, 'utf8'); + } catch { + throw Error(`Contract ${fileDir} not found`); + } + + try { + return loadContractArtifact(JSON.parse(contents)); + } catch (err) { + log('Invalid file used. Please try again.'); + throw err; + } +} + +/** + * Utility to select a TX sender either from user input + * or from the first account that is found in a PXE instance. + * @param pxe - The PXE instance that will be checked for an account. + * @param _from - The user input. + * @returns An Aztec address. Will throw if one can't be found in either options. + */ +export async function getTxSender(pxe: PXE, _from?: string) { + let from: AztecAddress; + if (_from) { + try { + from = AztecAddress.fromString(_from); + } catch { + throw new InvalidArgumentError(`Invalid option 'from' passed: ${_from}`); + } + } else { + const accounts = await pxe.getRegisteredAccounts(); + if (!accounts.length) { + throw new Error('No accounts found in PXE instance.'); + } + from = accounts[0].address; + } + return from; +} + +/** + * Performs necessary checks, conversions & operations to call a contract fn from the CLI. + * @param contractFile - Directory of the compiled contract ABI. + * @param functionName - Name of the function to be called. + * @param _functionArgs - Arguments to call the function with. + * @param log - Logger instance that will output to the CLI + * @returns Formatted contract address, function arguments and caller's aztec address. + */ +export async function prepTx(contractFile: string, functionName: string, _functionArgs: string[], log: LogFn) { + const contractArtifact = await getContractArtifact(contractFile, log); + const functionArtifact = getFunctionArtifact(contractArtifact, functionName); + const functionArgs = encodeArgs(_functionArgs, functionArtifact.parameters); + + return { functionArgs, contractArtifact }; +} + +/** + * Removes the leading 0x from a hex string. If no leading 0x is found the string is returned unchanged. + * @param hex - A hex string + * @returns A new string with leading 0x removed + */ +export const stripLeadingHex = (hex: string) => { + if (hex.length > 2 && hex.startsWith('0x')) { + return hex.substring(2); + } + return hex; +}; + +/** + * Updates a file in place atomically. + * @param filePath - Path to file + * @param contents - New contents to write + */ +export async function atomicUpdateFile(filePath: string, contents: string) { + const tmpFilepath = filePath + '.tmp'; + try { + await writeFile(tmpFilepath, contents, { + // let's crash if the tmp file already exists + flag: 'wx', + }); + await rename(tmpFilepath, filePath); + } catch (e) { + if (e instanceof Error && 'code' in e && e.code === 'EEXIST') { + const commanderError = new CommanderError( + 1, + e.code, + `Temporary file already exists: ${tmpFilepath}. Delete this file and try again.`, + ); + commanderError.nestedError = e.message; + throw commanderError; + } else { + throw e; + } + } +} + +/** + * Pretty prints Nargo.toml contents to a string + * @param config - Nargo.toml contents + * @returns The Nargo.toml contents as a string + */ +export function prettyPrintNargoToml(config: NoirPackageConfig): string { + const withoutDependencies = Object.fromEntries(Object.entries(config).filter(([key]) => key !== 'dependencies')); + + const partialToml = TOML.stringify(withoutDependencies); + const dependenciesToml = Object.entries(config.dependencies).map(([name, dep]) => { + const depToml = TOML.stringify.value(dep); + return `${name} = ${depToml}`; + }); + + return partialToml + '\n[dependencies]\n' + dependenciesToml.join('\n') + '\n'; +} diff --git a/yarn-project/cli/tsconfig.json b/yarn-project/cli/tsconfig.json new file mode 100644 index 00000000000..d686ac2a146 --- /dev/null +++ b/yarn-project/cli/tsconfig.json @@ -0,0 +1,39 @@ +{ + "extends": "..", + "compilerOptions": { + "outDir": "dest", + "rootDir": "src", + "tsBuildInfoFile": ".tsbuildinfo" + }, + "references": [ + { + "path": "../accounts" + }, + { + "path": "../aztec.js" + }, + { + "path": "../circuit-types" + }, + { + "path": "../circuits.js" + }, + { + "path": "../ethereum" + }, + { + "path": "../foundation" + }, + { + "path": "../l1-artifacts" + }, + { + "path": "../noir-contracts.js" + }, + { + "path": "../types" + } + ], + "include": ["src"], + "exclude": ["contracts"] +} diff --git a/yarn-project/yarn.lock b/yarn-project/yarn.lock index f8cd6fc6dc7..762912ddd76 100644 --- a/yarn-project/yarn.lock +++ b/yarn-project/yarn.lock @@ -342,6 +342,45 @@ __metadata: languageName: unknown linkType: soft +"@aztec/cli@workspace:cli": + version: 0.0.0-use.local + resolution: "@aztec/cli@workspace:cli" + dependencies: + "@aztec/accounts": "workspace:^" + "@aztec/aztec.js": "workspace:^" + "@aztec/circuit-types": "workspace:^" + "@aztec/circuits.js": "workspace:^" + "@aztec/ethereum": "workspace:^" + "@aztec/foundation": "workspace:^" + "@aztec/l1-artifacts": "workspace:^" + "@aztec/noir-contracts.js": "workspace:^" + "@aztec/types": "workspace:^" + "@iarna/toml": ^2.2.5 + "@jest/globals": ^29.5.0 + "@libp2p/peer-id-factory": ^3.0.4 + "@types/jest": ^29.5.0 + "@types/lodash.startcase": ^4.4.7 + "@types/node": ^18.7.23 + "@types/semver": ^7.5.2 + "@types/source-map-support": ^0.5.10 + commander: ^9.0.0 + jest: ^29.5.0 + jest-mock-extended: ^3.0.5 + jszip: ^3.10.1 + lodash.startcase: ^4.4.0 + node-fetch: ^3.3.2 + semver: ^7.5.4 + source-map-support: ^0.5.21 + ts-jest: ^29.1.0 + ts-node: ^10.9.1 + tslib: ^2.4.0 + typescript: ^5.0.4 + viem: ^2.7.15 + bin: + aztec-cli: ./dest/bin/index.js + languageName: unknown + linkType: soft + "@aztec/docs@workspace:docs": version: 0.0.0-use.local resolution: "@aztec/docs@workspace:docs" @@ -2191,7 +2230,7 @@ __metadata: languageName: node linkType: hard -"@libp2p/crypto@npm:^2.0.3": +"@libp2p/crypto@npm:^2.0.3, @libp2p/crypto@npm:^2.0.8": version: 2.0.8 resolution: "@libp2p/crypto@npm:2.0.8" dependencies: @@ -2433,6 +2472,21 @@ __metadata: languageName: node linkType: hard +"@libp2p/peer-id-factory@npm:^3.0.4": + version: 3.0.11 + resolution: "@libp2p/peer-id-factory@npm:3.0.11" + dependencies: + "@libp2p/crypto": ^2.0.8 + "@libp2p/interface": ^0.1.6 + "@libp2p/peer-id": ^3.0.6 + multiformats: ^12.0.1 + protons-runtime: ^5.0.0 + uint8arraylist: ^2.4.3 + uint8arrays: ^4.0.6 + checksum: bdcee4fef7f8aace6a8316e523e8c82753986a42c58b51f59d04534a1095c9c1eec8193e859614aa2589a7f5e43e64e529bb0b475e7bad7150b2034b2ebc0aa2 + languageName: node + linkType: hard + "@libp2p/peer-id@npm:4.0.7": version: 4.0.7 resolution: "@libp2p/peer-id@npm:4.0.7" @@ -3745,6 +3799,15 @@ __metadata: languageName: node linkType: hard +"@types/lodash.startcase@npm:^4.4.7": + version: 4.4.9 + resolution: "@types/lodash.startcase@npm:4.4.9" + dependencies: + "@types/lodash": "*" + checksum: 448203f0b6d31c1af9fe8292d5417af670bee560bb0af0cac3a6047b90c2d60ba03197367c2defae21e3982c665763197343863ce7d97131efa8e13e6431fe9f + languageName: node + linkType: hard + "@types/lodash.times@npm:^4.3.7": version: 4.3.9 resolution: "@types/lodash.times@npm:4.3.9" @@ -3851,7 +3914,7 @@ __metadata: languageName: node linkType: hard -"@types/semver@npm:^7.5.0, @types/semver@npm:^7.5.4": +"@types/semver@npm:^7.5.0, @types/semver@npm:^7.5.2, @types/semver@npm:^7.5.4": version: 7.5.8 resolution: "@types/semver@npm:7.5.8" checksum: ea6f5276f5b84c55921785a3a27a3cd37afee0111dfe2bcb3e03c31819c197c782598f17f0b150a69d453c9584cd14c4c4d7b9a55d2c5e6cacd4d66fdb3b3663 @@ -5163,6 +5226,15 @@ __metadata: languageName: node linkType: hard +"bs-logger@npm:0.x": + version: 0.2.6 + resolution: "bs-logger@npm:0.2.6" + dependencies: + fast-json-stable-stringify: 2.x + checksum: d34bdaf68c64bd099ab97c3ea608c9ae7d3f5faa1178b3f3f345acd94e852e608b2d4f9103fb2e503f5e69780e98293df41691b84be909b41cf5045374d54606 + languageName: node + linkType: hard + "bser@npm:2.1.1": version: 2.1.1 resolution: "bser@npm:2.1.1" @@ -5889,6 +5961,13 @@ __metadata: languageName: node linkType: hard +"data-uri-to-buffer@npm:^4.0.0": + version: 4.0.1 + resolution: "data-uri-to-buffer@npm:4.0.1" + checksum: 0d0790b67ffec5302f204c2ccca4494f70b4e2d940fea3d36b09f0bb2b8539c2e86690429eb1f1dc4bcc9e4df0644193073e63d9ee48ac9fce79ec1506e4aa4c + languageName: node + linkType: hard + "data-uri-to-buffer@npm:^6.0.2": version: 6.0.2 resolution: "data-uri-to-buffer@npm:6.0.2" @@ -7274,7 +7353,7 @@ __metadata: languageName: node linkType: hard -"fast-json-stable-stringify@npm:^2.0.0, fast-json-stable-stringify@npm:^2.1.0": +"fast-json-stable-stringify@npm:2.x, fast-json-stable-stringify@npm:^2.0.0, fast-json-stable-stringify@npm:^2.1.0": version: 2.1.0 resolution: "fast-json-stable-stringify@npm:2.1.0" checksum: b191531e36c607977e5b1c47811158733c34ccb3bfde92c44798929e9b4154884378536d26ad90dfecd32e1ffc09c545d23535ad91b3161a27ddbb8ebe0cbecb @@ -7336,6 +7415,16 @@ __metadata: languageName: node linkType: hard +"fetch-blob@npm:^3.1.2, fetch-blob@npm:^3.1.4": + version: 3.2.0 + resolution: "fetch-blob@npm:3.2.0" + dependencies: + node-domexception: ^1.0.0 + web-streams-polyfill: ^3.0.3 + checksum: f19bc28a2a0b9626e69fd7cf3a05798706db7f6c7548da657cbf5026a570945f5eeaedff52007ea35c8bcd3d237c58a20bf1543bc568ab2422411d762dd3d5bf + languageName: node + linkType: hard + "file-entry-cache@npm:^6.0.1": version: 6.0.1 resolution: "file-entry-cache@npm:6.0.1" @@ -7503,6 +7592,15 @@ __metadata: languageName: node linkType: hard +"formdata-polyfill@npm:^4.0.10": + version: 4.0.10 + resolution: "formdata-polyfill@npm:4.0.10" + dependencies: + fetch-blob: ^3.1.2 + checksum: 82a34df292afadd82b43d4a740ce387bc08541e0a534358425193017bf9fb3567875dc5f69564984b1da979979b70703aa73dee715a17b6c229752ae736dd9db + languageName: node + linkType: hard + "formidable@npm:^2.1.2": version: 2.1.2 resolution: "formidable@npm:2.1.2" @@ -8160,6 +8258,13 @@ __metadata: languageName: node linkType: hard +"immediate@npm:~3.0.5": + version: 3.0.6 + resolution: "immediate@npm:3.0.6" + checksum: f9b3486477555997657f70318cc8d3416159f208bec4cca3ff3442fd266bc23f50f0c9bd8547e1371a6b5e82b821ec9a7044a4f7b944798b25aa3cc6d5e63e62 + languageName: node + linkType: hard + "import-fresh@npm:^3.2.1, import-fresh@npm:^3.3.0": version: 3.3.0 resolution: "import-fresh@npm:3.3.0" @@ -9366,7 +9471,7 @@ __metadata: languageName: node linkType: hard -"jest-util@npm:^29.7.0": +"jest-util@npm:^29.0.0, jest-util@npm:^29.7.0": version: 29.7.0 resolution: "jest-util@npm:29.7.0" dependencies: @@ -9580,6 +9685,18 @@ __metadata: languageName: node linkType: hard +"jszip@npm:^3.10.1": + version: 3.10.1 + resolution: "jszip@npm:3.10.1" + dependencies: + lie: ~3.3.0 + pako: ~1.0.2 + readable-stream: ~2.3.6 + setimmediate: ^1.0.5 + checksum: abc77bfbe33e691d4d1ac9c74c8851b5761fba6a6986630864f98d876f3fcc2d36817dfc183779f32c00157b5d53a016796677298272a714ae096dfe6b1c8b60 + languageName: node + linkType: hard + "keygrip@npm:~1.1.0": version: 1.1.0 resolution: "keygrip@npm:1.1.0" @@ -9846,6 +9963,15 @@ __metadata: languageName: node linkType: hard +"lie@npm:~3.3.0": + version: 3.3.0 + resolution: "lie@npm:3.3.0" + dependencies: + immediate: ~3.0.5 + checksum: 33102302cf19766f97919a6a98d481e01393288b17a6aa1f030a3542031df42736edde8dab29ffdbf90bebeffc48c761eb1d064dc77592ca3ba3556f9fe6d2a8 + languageName: node + linkType: hard + "lines-and-columns@npm:^1.1.6": version: 1.2.4 resolution: "lines-and-columns@npm:1.2.4" @@ -9995,6 +10121,13 @@ __metadata: languageName: node linkType: hard +"lodash.memoize@npm:4.x": + version: 4.1.2 + resolution: "lodash.memoize@npm:4.1.2" + checksum: 9ff3942feeccffa4f1fafa88d32f0d24fdc62fd15ded5a74a5f950ff5f0c6f61916157246744c620173dddf38d37095a92327d5fd3861e2063e736a5c207d089 + languageName: node + linkType: hard + "lodash.merge@npm:^4.6.2": version: 4.6.2 resolution: "lodash.merge@npm:4.6.2" @@ -10016,6 +10149,13 @@ __metadata: languageName: node linkType: hard +"lodash.startcase@npm:^4.4.0": + version: 4.4.0 + resolution: "lodash.startcase@npm:4.4.0" + checksum: c03a4a784aca653845fe09d0ef67c902b6e49288dc45f542a4ab345a9c406a6dc194c774423fa313ee7b06283950301c1221dd2a1d8ecb2dac8dfbb9ed5606b5 + languageName: node + linkType: hard + "lodash.times@npm:^4.3.2": version: 4.3.2 resolution: "lodash.times@npm:4.3.2" @@ -10146,7 +10286,7 @@ __metadata: languageName: node linkType: hard -"make-error@npm:^1.1.1": +"make-error@npm:1.x, make-error@npm:^1.1.1": version: 1.3.6 resolution: "make-error@npm:1.3.6" checksum: b86e5e0e25f7f777b77fabd8e2cbf15737972869d852a22b7e73c17623928fccb826d8e46b9951501d3f20e51ad74ba8c59ed584f610526a48f8ccf88aaec402 @@ -10726,6 +10866,24 @@ __metadata: languageName: node linkType: hard +"node-domexception@npm:^1.0.0": + version: 1.0.0 + resolution: "node-domexception@npm:1.0.0" + checksum: ee1d37dd2a4eb26a8a92cd6b64dfc29caec72bff5e1ed9aba80c294f57a31ba4895a60fd48347cf17dd6e766da0ae87d75657dfd1f384ebfa60462c2283f5c7f + languageName: node + linkType: hard + +"node-fetch@npm:^3.3.2": + version: 3.3.2 + resolution: "node-fetch@npm:3.3.2" + dependencies: + data-uri-to-buffer: ^4.0.0 + fetch-blob: ^3.1.4 + formdata-polyfill: ^4.0.10 + checksum: 06a04095a2ddf05b0830a0d5302699704d59bda3102894ea64c7b9d4c865ecdff2d90fd042df7f5bc40337266961cb6183dcc808ea4f3000d024f422b462da92 + languageName: node + linkType: hard + "node-forge@npm:^1.1.0": version: 1.3.1 resolution: "node-forge@npm:1.3.1" @@ -11167,6 +11325,13 @@ __metadata: languageName: node linkType: hard +"pako@npm:~1.0.2": + version: 1.0.11 + resolution: "pako@npm:1.0.11" + checksum: 1be2bfa1f807608c7538afa15d6f25baa523c30ec870a3228a89579e474a4d992f4293859524e46d5d87fd30fa17c5edf34dbef0671251d9749820b488660b16 + languageName: node + linkType: hard + "parent-module@npm:^1.0.0": version: 1.0.1 resolution: "parent-module@npm:1.0.1" @@ -11790,7 +11955,7 @@ __metadata: languageName: node linkType: hard -"readable-stream@npm:^2.3.8": +"readable-stream@npm:^2.3.8, readable-stream@npm:~2.3.6": version: 2.3.8 resolution: "readable-stream@npm:2.3.8" dependencies: @@ -12265,6 +12430,13 @@ __metadata: languageName: node linkType: hard +"setimmediate@npm:^1.0.5": + version: 1.0.5 + resolution: "setimmediate@npm:1.0.5" + checksum: c9a6f2c5b51a2dabdc0247db9c46460152ffc62ee139f3157440bd48e7c59425093f42719ac1d7931f054f153e2d26cf37dfeb8da17a794a58198a2705e527fd + languageName: node + linkType: hard + "setprototypeof@npm:1.1.0": version: 1.1.0 resolution: "setprototypeof@npm:1.1.0" @@ -13145,6 +13317,42 @@ __metadata: languageName: node linkType: hard +"ts-jest@npm:^29.1.0": + version: 29.1.3 + resolution: "ts-jest@npm:29.1.3" + dependencies: + bs-logger: 0.x + fast-json-stable-stringify: 2.x + jest-util: ^29.0.0 + json5: ^2.2.3 + lodash.memoize: 4.x + make-error: 1.x + semver: ^7.5.3 + yargs-parser: ^21.0.1 + peerDependencies: + "@babel/core": ">=7.0.0-beta.0 <8" + "@jest/transform": ^29.0.0 + "@jest/types": ^29.0.0 + babel-jest: ^29.0.0 + jest: ^29.0.0 + typescript: ">=4.3 <6" + peerDependenciesMeta: + "@babel/core": + optional: true + "@jest/transform": + optional: true + "@jest/types": + optional: true + babel-jest: + optional: true + esbuild: + optional: true + bin: + ts-jest: cli.js + checksum: c5b2c0501680a9056c50541a3315de7b3b85a611056b978062b4defc96fb0066d12bf1e15715021799a3779723343fb98a9a4ba01dc01709f274899b6c28453d + languageName: node + linkType: hard + "ts-loader@npm:^9.4.4": version: 9.5.1 resolution: "ts-loader@npm:9.5.1" @@ -13806,6 +14014,13 @@ __metadata: languageName: node linkType: hard +"web-streams-polyfill@npm:^3.0.3": + version: 3.3.3 + resolution: "web-streams-polyfill@npm:3.3.3" + checksum: 21ab5ea08a730a2ef8023736afe16713b4f2023ec1c7085c16c8e293ee17ed085dff63a0ad8722da30c99c4ccbd4ccd1b2e79c861829f7ef2963d7de7004c2cb + languageName: node + linkType: hard + "webpack-cli@npm:^5.1.4": version: 5.1.4 resolution: "webpack-cli@npm:5.1.4" @@ -14127,7 +14342,7 @@ __metadata: languageName: node linkType: hard -"yargs-parser@npm:^21.1.1": +"yargs-parser@npm:^21.0.1, yargs-parser@npm:^21.1.1": version: 21.1.1 resolution: "yargs-parser@npm:21.1.1" checksum: ed2d96a616a9e3e1cc7d204c62ecc61f7aaab633dcbfab2c6df50f7f87b393993fe6640d017759fe112d0cb1e0119f2b4150a87305cc873fd90831c6a58ccf1c From 27952a4ea7cac09f66b4b397791280e307529e63 Mon Sep 17 00:00:00 2001 From: Alex Gherghisan Date: Thu, 23 May 2024 11:16:08 +0000 Subject: [PATCH 02/46] feat: add keys --- .../cli/src/cmds/generate_private_key.ts | 24 ++--- yarn-project/cli/src/index.ts | 91 ++++++++++--------- 2 files changed, 52 insertions(+), 63 deletions(-) diff --git a/yarn-project/cli/src/cmds/generate_private_key.ts b/yarn-project/cli/src/cmds/generate_private_key.ts index b447cdcc738..e2ff58f6767 100644 --- a/yarn-project/cli/src/cmds/generate_private_key.ts +++ b/yarn-project/cli/src/cmds/generate_private_key.ts @@ -1,20 +1,8 @@ -import { GrumpkinScalar, generatePublicKey } from '@aztec/aztec.js'; -import { LogFn } from '@aztec/foundation/log'; +import { Fr, GrumpkinPrivateKey } from '@aztec/aztec.js'; -import { mnemonicToAccount } from 'viem/accounts'; - -export function generatePrivateKey(mnemonic: string | undefined, log: LogFn) { - let privKey; - let publicKey; - if (mnemonic) { - const acc = mnemonicToAccount(mnemonic); - // TODO(#2052): This reduction is not secure enough. TACKLE THIS ISSUE BEFORE MAINNET. - const key = GrumpkinScalar.fromBufferReduce(Buffer.from(acc.getHdKey().privateKey!)); - publicKey = generatePublicKey(key); - } else { - const key = GrumpkinScalar.random(); - privKey = key.toString(); - publicKey = generatePublicKey(key); - } - log(`\nPrivate Key: ${privKey}\nPublic Key: ${publicKey.toString()}\n`); +export function generateKeys() { + return { + privateEncryptionKey: Fr.random(), + privateSigningKey: GrumpkinPrivateKey.random(), + }; } diff --git a/yarn-project/cli/src/index.ts b/yarn-project/cli/src/index.ts index d24b1631b9f..19fbf7395be 100644 --- a/yarn-project/cli/src/index.ts +++ b/yarn-project/cli/src/index.ts @@ -99,8 +99,9 @@ export function getProgram(log: LogFn, debugLogger: DebugLogger): Command { 'An optional mnemonic string used for the private key generation. If not provided, random private key will be generated.', ) .action(async options => { - const { generatePrivateKey } = await import('./cmds/generate_private_key.js'); - generatePrivateKey(options.mnemonic, log); + const { generateKeys } = await import('./cmds/generate_private_key.js'); + const { privateEncryptionKey, privateSigningKey } = generateKeys(); + log(`Encryption Private Key: ${privateEncryptionKey}\nSigning Private key: ${privateSigningKey}\n`); }); program @@ -126,8 +127,8 @@ export function getProgram(log: LogFn, debugLogger: DebugLogger): Command { // https://github.com/tj/commander.js#other-option-types-negatable-boolean-and-booleanvalue .option('--no-wait', 'Skip waiting for the contract to be deployed. Print the hash of deployment transaction') .action(async ({ rpcUrl, privateKey, wait }) => { - const { createAccount } = await import('./cmds/create_account.js'); - await createAccount(rpcUrl, privateKey, wait, debugLogger, log); + // const { createAccount } = await import('./cmds/create_account.js'); + // await createAccount(rpcUrl, privateKey, wait, debugLogger, log); }); program @@ -183,22 +184,22 @@ export function getProgram(log: LogFn, debugLogger: DebugLogger): Command { artifactPath, { json, rpcUrl, publicKey, args: rawArgs, portalAddress, salt, wait, privateKey, initializer }, ) => { - const { deploy } = await import('./cmds/deploy.js'); - await deploy( - artifactPath, - json, - rpcUrl, - publicKey, - rawArgs, - portalAddress, - salt, - privateKey, - initializer, - wait, - debugLogger, - log, - logJson, - ); + // const { deploy } = await import('./cmds/deploy.js'); + // await deploy( + // artifactPath, + // json, + // rpcUrl, + // publicKey, + // rawArgs, + // portalAddress, + // salt, + // privateKey, + // initializer, + // wait, + // debugLogger, + // log, + // logJson, + // ); }, ); @@ -233,19 +234,19 @@ export function getProgram(log: LogFn, debugLogger: DebugLogger): Command { .option('--deployer-address
', 'Optional address of the contract deployer', parseAztecAddress) .addOption(pxeOption) .action(async options => { - const { addContract } = await import('./cmds/add_contract.js'); - await addContract( - options.rpcUrl, - options.contractArtifact, - options.contractAddress, - options.initHash, - options.salt ?? Fr.ZERO, - options.publicKey, - options.portalContract, - options.deployerAddress, - debugLogger, - log, - ); + // const { addContract } = await import('./cmds/add_contract.js'); + // await addContract( + // options.rpcUrl, + // options.contractArtifact, + // options.contractAddress, + // options.initHash, + // options.salt ?? Fr.ZERO, + // options.publicKey, + // options.portalContract, + // options.deployerAddress, + // debugLogger, + // log, + // ); }); program @@ -358,18 +359,18 @@ export function getProgram(log: LogFn, debugLogger: DebugLogger): Command { .addOption(pxeOption) .option('--no-wait', 'Print transaction hash without waiting for it to be mined') .action(async (functionName, options) => { - const { send } = await import('./cmds/send.js'); - await send( - functionName, - options.args, - options.contractArtifact, - options.contractAddress, - options.privateKey, - options.rpcUrl, - !options.noWait, - debugLogger, - log, - ); + // const { send } = await import('./cmds/send.js'); + // await send( + // functionName, + // options.args, + // options.contractArtifact, + // options.contractAddress, + // options.privateKey, + // options.rpcUrl, + // !options.noWait, + // debugLogger, + // log, + // ); }); program From 3f7dbe6fbf8557f7b7ab1f2b409cdd6987eb0c22 Mon Sep 17 00:00:00 2001 From: Alex Gherghisan Date: Thu, 23 May 2024 11:26:46 +0000 Subject: [PATCH 03/46] feat: create account --- yarn-project/cli/src/index.ts | 30 ++++++++++++++++++------------ yarn-project/cli/src/parse_args.ts | 12 ++++++++++-- 2 files changed, 28 insertions(+), 14 deletions(-) diff --git a/yarn-project/cli/src/index.ts b/yarn-project/cli/src/index.ts index 19fbf7395be..f6f5788ec82 100644 --- a/yarn-project/cli/src/index.ts +++ b/yarn-project/cli/src/index.ts @@ -9,6 +9,7 @@ import { dirname, resolve } from 'path'; import { parseAztecAddress, + parseEncryptionPrivateKey, parseEthereumAddress, parseField, parseFieldFromHexString, @@ -18,8 +19,8 @@ import { parseOptionalSelector, parseOptionalTxHash, parsePartialAddress, - parsePrivateKey, parsePublicKey, + parseSigningPrivateKey, parseTxHash, } from './parse_args.js'; @@ -55,10 +56,16 @@ export function getProgram(log: LogFn, debugLogger: DebugLogger): Command { .default(`http://${LOCALHOST}:8080`) .makeOptionMandatory(true); - const createPrivateKeyOption = (description: string, mandatory: boolean) => + const createSigningPrivateKeyOption = (description: string, mandatory: boolean) => new Option('-k, --private-key ', description) .env('PRIVATE_KEY') - .argParser(parsePrivateKey) + .argParser(parseSigningPrivateKey) + .makeOptionMandatory(mandatory); + + const createEncryptionPrivateKeyOption = (description: string, mandatory: boolean) => + new Option('-e, --enc-private-key ', description) + .env('ENC_PRIVATE_KEY') + .argParser(parseEncryptionPrivateKey) .makeOptionMandatory(mandatory); program @@ -119,16 +126,15 @@ export function getProgram(log: LogFn, debugLogger: DebugLogger): Command { 'Creates an aztec account that can be used for sending transactions. Registers the account on the PXE and deploys an account contract. Uses a Schnorr single-key account which uses the same key for encryption and authentication (not secure for production usage).', ) .summary('Creates an aztec account that can be used for sending transactions.') - .addOption( - createPrivateKeyOption('Private key for note encryption and transaction signing. Uses random by default.', false), - ) + .addOption(createSigningPrivateKeyOption('Private key for transaction signing. Uses random by default.', false)) + .addOption(createEncryptionPrivateKeyOption('Private key for note encryption. Uses random by default.', false)) .addOption(pxeOption) // `options.wait` is default true. Passing `--no-wait` will set it to false. // https://github.com/tj/commander.js#other-option-types-negatable-boolean-and-booleanvalue .option('--no-wait', 'Skip waiting for the contract to be deployed. Print the hash of deployment transaction') - .action(async ({ rpcUrl, privateKey, wait }) => { - // const { createAccount } = await import('./cmds/create_account.js'); - // await createAccount(rpcUrl, privateKey, wait, debugLogger, log); + .action(async ({ rpcUrl, privateKey, encPrivateKey, wait }) => { + const { createAccount } = await import('./cmds/create_account.js'); + await createAccount(rpcUrl, encPrivateKey, privateKey, wait, debugLogger, log); }); program @@ -137,7 +143,7 @@ export function getProgram(log: LogFn, debugLogger: DebugLogger): Command { 'Registers an aztec account that can be used for sending transactions. Registers the account on the PXE. Uses a Schnorr single-key account which uses the same key for encryption and authentication (not secure for production usage).', ) .summary('Registers an aztec account that can be used for sending transactions.') - .addOption(createPrivateKeyOption('Private key for note encryption and transaction signing.', true)) + .addOption(createSigningPrivateKeyOption('Private key for note encryption and transaction signing.', true)) .requiredOption( '-pa, --partial-address ', 'The partially computed address of the account contract.', @@ -174,7 +180,7 @@ export function getProgram(log: LogFn, debugLogger: DebugLogger): Command { 'Optional deployment salt as a hex string for generating the deployment address.', parseFieldFromHexString, ) - .addOption(createPrivateKeyOption("The sender's private key.", true)) + .addOption(createSigningPrivateKeyOption("The sender's private key.", true)) .option('--json', 'Emit output as json') // `options.wait` is default true. Passing `--no-wait` will set it to false. // https://github.com/tj/commander.js#other-option-types-negatable-boolean-and-booleanvalue @@ -355,7 +361,7 @@ export function getProgram(log: LogFn, debugLogger: DebugLogger): Command { "A compiled Aztec.nr contract's ABI in JSON format or name of a contract ABI exported by @aztec/noir-contracts.js", ) .requiredOption('-ca, --contract-address
', 'Aztec address of the contract.', parseAztecAddress) - .addOption(createPrivateKeyOption("The sender's private key.", true)) + .addOption(createSigningPrivateKeyOption("The sender's private key.", true)) .addOption(pxeOption) .option('--no-wait', 'Print transaction hash without waiting for it to be mined') .action(async (functionName, options) => { diff --git a/yarn-project/cli/src/parse_args.ts b/yarn-project/cli/src/parse_args.ts index d71129d48dd..94b2bca4650 100644 --- a/yarn-project/cli/src/parse_args.ts +++ b/yarn-project/cli/src/parse_args.ts @@ -191,7 +191,7 @@ export function parsePartialAddress(address: string): Fr { * @returns A private key * @throws InvalidArgumentError if the input string is not valid. */ -export function parsePrivateKey(privateKey: string): GrumpkinScalar { +export function parseSigningPrivateKey(privateKey: string): GrumpkinScalar { try { const value = GrumpkinScalar.fromString(privateKey); // most likely a badly formatted key was passed @@ -201,7 +201,15 @@ export function parsePrivateKey(privateKey: string): GrumpkinScalar { return value; } catch (err) { - throw new InvalidArgumentError(`Invalid private key: ${privateKey}`); + throw new InvalidArgumentError(`Invalid signing private key: ${privateKey}`); + } +} + +export function parseEncryptionPrivateKey(privateKey: string): Fr { + try { + return Fr.fromString(privateKey); + } catch (err) { + throw new InvalidArgumentError(`Invalid encryption private key: ${privateKey}`); } } From 7435e0cd0bc00ec07d5de17977783e695416905a Mon Sep 17 00:00:00 2001 From: PhilWindle Date: Thu, 23 May 2024 15:19:20 +0100 Subject: [PATCH 04/46] WIP --- yarn-project/cli/package.json | 2 +- yarn-project/cli/src/cmds/create_account.ts | 1 + yarn-project/cli/src/index.ts | 6 +++--- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/yarn-project/cli/package.json b/yarn-project/cli/package.json index 1808405ee9a..74b2a0b2fd4 100644 --- a/yarn-project/cli/package.json +++ b/yarn-project/cli/package.json @@ -20,7 +20,7 @@ "formatting": "run -T prettier --check ./src && run -T eslint ./src", "formatting:fix": "run -T eslint --fix ./src && run -T prettier -w ./src", "test": "NODE_NO_WARNINGS=1 node --experimental-vm-modules $(yarn bin jest) --passWithNoTests", - "start": "node --no-warnings ./dest/bin/index.js" + "start": "yarn build && node --no-warnings ./dest/bin/index.js" }, "inherits": [ "../package.common.json" diff --git a/yarn-project/cli/src/cmds/create_account.ts b/yarn-project/cli/src/cmds/create_account.ts index 5af15fbb182..09decfa220c 100644 --- a/yarn-project/cli/src/cmds/create_account.ts +++ b/yarn-project/cli/src/cmds/create_account.ts @@ -18,6 +18,7 @@ export async function createAccount( const account = getSchnorrAccount(client, actualEncryptionPrivateKey, actualSigningPrivateKey, Fr.ZERO); const { address, publicKeys, partialAddress } = account.getCompleteAddress(); + await account.register(); const tx = account.deploy(); const txHash = tx.getTxHash(); debugLogger.debug(`Account contract tx sent with hash ${txHash}`); diff --git a/yarn-project/cli/src/index.ts b/yarn-project/cli/src/index.ts index f6f5788ec82..40b52b8de9e 100644 --- a/yarn-project/cli/src/index.ts +++ b/yarn-project/cli/src/index.ts @@ -96,10 +96,10 @@ export function getProgram(log: LogFn, debugLogger: DebugLogger): Command { }); program - .command('generate-private-key') - .summary('Generates an encryption private key.') + .command('generate-keys') + .summary('Generates encryption and signing private keys.') .description( - 'Generates a private key which fits into the scalar field used by Grumpkin curve, can be used as an encryption private key.', + 'Generates and encryption and signing private key pair.', ) .option( '-m, --mnemonic', From edb7b78fb9ae87474e4fcbfa88ad0c808b568441 Mon Sep 17 00:00:00 2001 From: Alex Gherghisan Date: Thu, 23 May 2024 14:31:59 +0000 Subject: [PATCH 05/46] feat: bootstrap --- yarn-project/cli/package.json | 1 + yarn-project/cli/src/cmds/bootstrap.ts | 53 ++++++++++++++++++++++++++ yarn-project/cli/src/index.ts | 9 +++++ yarn-project/yarn.lock | 1 + 4 files changed, 64 insertions(+) create mode 100644 yarn-project/cli/src/cmds/bootstrap.ts diff --git a/yarn-project/cli/package.json b/yarn-project/cli/package.json index 74b2a0b2fd4..f6a4b35fe17 100644 --- a/yarn-project/cli/package.json +++ b/yarn-project/cli/package.json @@ -42,6 +42,7 @@ "@aztec/foundation": "workspace:^", "@aztec/l1-artifacts": "workspace:^", "@aztec/noir-contracts.js": "workspace:^", + "@aztec/protocol-contracts": "workspace:^", "@aztec/types": "workspace:^", "@iarna/toml": "^2.2.5", "@libp2p/peer-id-factory": "^3.0.4", diff --git a/yarn-project/cli/src/cmds/bootstrap.ts b/yarn-project/cli/src/cmds/bootstrap.ts new file mode 100644 index 00000000000..0dc833a1c83 --- /dev/null +++ b/yarn-project/cli/src/cmds/bootstrap.ts @@ -0,0 +1,53 @@ +import { AztecAddress, SignerlessWallet, createPXEClient } from '@aztec/aztec.js'; +import { DefaultMultiCallEntrypoint } from '@aztec/aztec.js/entrypoint'; +import { CANONICAL_KEY_REGISTRY_ADDRESS } from '@aztec/circuits.js'; +import { type LogFn } from '@aztec/foundation/log'; +import { GasTokenContract, KeyRegistryContract } from '@aztec/noir-contracts.js'; +import { getCanonicalGasToken } from '@aztec/protocol-contracts/gas-token'; +import { getCanonicalKeyRegistry } from '@aztec/protocol-contracts/key-registry'; + +export async function bootstrap(rpcUrl: string, log: LogFn) { + const pxe = createPXEClient(rpcUrl); + const canonicalKeyRegistry = getCanonicalKeyRegistry(); + const deployer = new SignerlessWallet(pxe, new DefaultMultiCallEntrypoint(31337, 1)); + + if ( + (await deployer.getContractInstance(canonicalKeyRegistry.address))?.contractClassId.equals( + canonicalKeyRegistry.contractClass.id, + ) && + (await deployer.isContractClassPubliclyRegistered(canonicalKeyRegistry.contractClass.id)) + ) { + log('Key Registry already deployed'); + return; + } + + const keyRegistry = await KeyRegistryContract.deploy(deployer) + .send({ contractAddressSalt: canonicalKeyRegistry.instance.salt, universalDeploy: true }) + .deployed(); + + if ( + !keyRegistry.address.equals(canonicalKeyRegistry.address) || + !keyRegistry.address.equals(AztecAddress.fromBigInt(CANONICAL_KEY_REGISTRY_ADDRESS)) + ) { + throw new Error( + `Deployed Key Registry address ${keyRegistry.address} does not match expected address ${canonicalKeyRegistry.address}, or they both do not equal CANONICAL_KEY_REGISTRY_ADDRESS`, + ); + } + + log(`Key Registry deployed at canonical address ${keyRegistry.address.toString()}`); + + const gasPortalAddress = (await deployer.getNodeInfo()).l1ContractAddresses.gasPortalAddress; + const canonicalGasToken = getCanonicalGasToken(); + + if (await deployer.isContractClassPubliclyRegistered(canonicalGasToken.contractClass.id)) { + log('Gas token already deployed'); + return; + } + + const gasToken = await GasTokenContract.deploy(deployer) + .send({ contractAddressSalt: canonicalGasToken.instance.salt, universalDeploy: true }) + .deployed(); + await gasToken.methods.set_portal(gasPortalAddress).send().wait(); + + log(`Gas token deployed at canonical address ${gasToken.address.toString()}`); +} diff --git a/yarn-project/cli/src/index.ts b/yarn-project/cli/src/index.ts index 40b52b8de9e..5a38ab73223 100644 --- a/yarn-project/cli/src/index.ts +++ b/yarn-project/cli/src/index.ts @@ -155,6 +155,15 @@ export function getProgram(log: LogFn, debugLogger: DebugLogger): Command { await registerAccount(rpcUrl, privateKey, partialAddress, debugLogger, log); }); + program + .command('bootstrap') + .description('Bootstrap the blockchain') + .addOption(pxeOption) + .action(async options => { + const { bootstrap } = await import('./cmds/bootstrap.js'); + await bootstrap(options.rpcUrl, log); + }); + program .command('deploy') .description('Deploys a compiled Aztec.nr contract to Aztec.') diff --git a/yarn-project/yarn.lock b/yarn-project/yarn.lock index 00454c22e0a..4f9f3211333 100644 --- a/yarn-project/yarn.lock +++ b/yarn-project/yarn.lock @@ -354,6 +354,7 @@ __metadata: "@aztec/foundation": "workspace:^" "@aztec/l1-artifacts": "workspace:^" "@aztec/noir-contracts.js": "workspace:^" + "@aztec/protocol-contracts": "workspace:^" "@aztec/types": "workspace:^" "@iarna/toml": ^2.2.5 "@jest/globals": ^29.5.0 From ee458948f15f72b1fc19f08e7f6e91afea99399d Mon Sep 17 00:00:00 2001 From: Alex Gherghisan Date: Thu, 23 May 2024 14:38:22 +0000 Subject: [PATCH 06/46] refactor: use deriveSigningKey --- yarn-project/cli/src/index.ts | 36 +++++++++++------------------- yarn-project/cli/src/parse_args.ts | 18 ++------------- 2 files changed, 15 insertions(+), 39 deletions(-) diff --git a/yarn-project/cli/src/index.ts b/yarn-project/cli/src/index.ts index 5a38ab73223..7e5ee5c1f28 100644 --- a/yarn-project/cli/src/index.ts +++ b/yarn-project/cli/src/index.ts @@ -1,5 +1,5 @@ -import { Fr } from '@aztec/circuits.js'; -import { DebugLogger, LogFn } from '@aztec/foundation/log'; +import { deriveSigningKey } from '@aztec/circuits.js'; +import { type DebugLogger, type LogFn } from '@aztec/foundation/log'; import { fileURLToPath } from '@aztec/foundation/url'; import { Command, Option } from 'commander'; @@ -9,7 +9,6 @@ import { dirname, resolve } from 'path'; import { parseAztecAddress, - parseEncryptionPrivateKey, parseEthereumAddress, parseField, parseFieldFromHexString, @@ -19,8 +18,8 @@ import { parseOptionalSelector, parseOptionalTxHash, parsePartialAddress, + parsePrivateKey, parsePublicKey, - parseSigningPrivateKey, parseTxHash, } from './parse_args.js'; @@ -56,16 +55,10 @@ export function getProgram(log: LogFn, debugLogger: DebugLogger): Command { .default(`http://${LOCALHOST}:8080`) .makeOptionMandatory(true); - const createSigningPrivateKeyOption = (description: string, mandatory: boolean) => - new Option('-k, --private-key ', description) + const createPrivateKeyOption = (description: string, mandatory: boolean) => + new Option('-e, --private-key ', description) .env('PRIVATE_KEY') - .argParser(parseSigningPrivateKey) - .makeOptionMandatory(mandatory); - - const createEncryptionPrivateKeyOption = (description: string, mandatory: boolean) => - new Option('-e, --enc-private-key ', description) - .env('ENC_PRIVATE_KEY') - .argParser(parseEncryptionPrivateKey) + .argParser(parsePrivateKey) .makeOptionMandatory(mandatory); program @@ -98,9 +91,7 @@ export function getProgram(log: LogFn, debugLogger: DebugLogger): Command { program .command('generate-keys') .summary('Generates encryption and signing private keys.') - .description( - 'Generates and encryption and signing private key pair.', - ) + .description('Generates and encryption and signing private key pair.') .option( '-m, --mnemonic', 'An optional mnemonic string used for the private key generation. If not provided, random private key will be generated.', @@ -126,15 +117,14 @@ export function getProgram(log: LogFn, debugLogger: DebugLogger): Command { 'Creates an aztec account that can be used for sending transactions. Registers the account on the PXE and deploys an account contract. Uses a Schnorr single-key account which uses the same key for encryption and authentication (not secure for production usage).', ) .summary('Creates an aztec account that can be used for sending transactions.') - .addOption(createSigningPrivateKeyOption('Private key for transaction signing. Uses random by default.', false)) - .addOption(createEncryptionPrivateKeyOption('Private key for note encryption. Uses random by default.', false)) + .addOption(createPrivateKeyOption('Private key for account. Uses random by default.', false)) .addOption(pxeOption) // `options.wait` is default true. Passing `--no-wait` will set it to false. // https://github.com/tj/commander.js#other-option-types-negatable-boolean-and-booleanvalue .option('--no-wait', 'Skip waiting for the contract to be deployed. Print the hash of deployment transaction') - .action(async ({ rpcUrl, privateKey, encPrivateKey, wait }) => { + .action(async ({ rpcUrl, privateKey, wait }) => { const { createAccount } = await import('./cmds/create_account.js'); - await createAccount(rpcUrl, encPrivateKey, privateKey, wait, debugLogger, log); + await createAccount(rpcUrl, privateKey, deriveSigningKey(privateKey), wait, debugLogger, log); }); program @@ -143,7 +133,7 @@ export function getProgram(log: LogFn, debugLogger: DebugLogger): Command { 'Registers an aztec account that can be used for sending transactions. Registers the account on the PXE. Uses a Schnorr single-key account which uses the same key for encryption and authentication (not secure for production usage).', ) .summary('Registers an aztec account that can be used for sending transactions.') - .addOption(createSigningPrivateKeyOption('Private key for note encryption and transaction signing.', true)) + .addOption(createPrivateKeyOption('Private key for account.', true)) .requiredOption( '-pa, --partial-address ', 'The partially computed address of the account contract.', @@ -189,7 +179,7 @@ export function getProgram(log: LogFn, debugLogger: DebugLogger): Command { 'Optional deployment salt as a hex string for generating the deployment address.', parseFieldFromHexString, ) - .addOption(createSigningPrivateKeyOption("The sender's private key.", true)) + .addOption(createPrivateKeyOption("The sender's private key.", true)) .option('--json', 'Emit output as json') // `options.wait` is default true. Passing `--no-wait` will set it to false. // https://github.com/tj/commander.js#other-option-types-negatable-boolean-and-booleanvalue @@ -370,7 +360,7 @@ export function getProgram(log: LogFn, debugLogger: DebugLogger): Command { "A compiled Aztec.nr contract's ABI in JSON format or name of a contract ABI exported by @aztec/noir-contracts.js", ) .requiredOption('-ca, --contract-address
', 'Aztec address of the contract.', parseAztecAddress) - .addOption(createSigningPrivateKeyOption("The sender's private key.", true)) + .addOption(createPrivateKeyOption("The sender's private key.", true)) .addOption(pxeOption) .option('--no-wait', 'Print transaction hash without waiting for it to be mined') .action(async (functionName, options) => { diff --git a/yarn-project/cli/src/parse_args.ts b/yarn-project/cli/src/parse_args.ts index 94b2bca4650..a019e1bbe34 100644 --- a/yarn-project/cli/src/parse_args.ts +++ b/yarn-project/cli/src/parse_args.ts @@ -1,7 +1,7 @@ import { FunctionSelector } from '@aztec/aztec.js/abi'; import { AztecAddress } from '@aztec/aztec.js/aztec_address'; import { EthAddress } from '@aztec/aztec.js/eth_address'; -import { Fr, GrumpkinScalar, Point } from '@aztec/aztec.js/fields'; +import { Fr, Point } from '@aztec/aztec.js/fields'; import { LogId } from '@aztec/aztec.js/log_id'; import { TxHash } from '@aztec/aztec.js/tx_hash'; @@ -191,21 +191,7 @@ export function parsePartialAddress(address: string): Fr { * @returns A private key * @throws InvalidArgumentError if the input string is not valid. */ -export function parseSigningPrivateKey(privateKey: string): GrumpkinScalar { - try { - const value = GrumpkinScalar.fromString(privateKey); - // most likely a badly formatted key was passed - if (value.isZero()) { - throw new Error('Private key must not be zero'); - } - - return value; - } catch (err) { - throw new InvalidArgumentError(`Invalid signing private key: ${privateKey}`); - } -} - -export function parseEncryptionPrivateKey(privateKey: string): Fr { +export function parsePrivateKey(privateKey: string): Fr { try { return Fr.fromString(privateKey); } catch (err) { From c84ab675a0eac48568c194f59222501274de20bf Mon Sep 17 00:00:00 2001 From: PhilWindle Date: Thu, 23 May 2024 15:38:53 +0100 Subject: [PATCH 07/46] Public keys to string --- yarn-project/circuits.js/src/types/public_keys.ts | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/yarn-project/circuits.js/src/types/public_keys.ts b/yarn-project/circuits.js/src/types/public_keys.ts index babb3c8ddbc..4086261b2e6 100644 --- a/yarn-project/circuits.js/src/types/public_keys.ts +++ b/yarn-project/circuits.js/src/types/public_keys.ts @@ -117,4 +117,12 @@ export class PublicKeys { reader.readObject(Point), ); } + + toString() { + return this.toBuffer().toString('hex'); + } + + static fromString(keys: string) { + return PublicKeys.fromBuffer(Buffer.from(keys, 'hex')); + } } From 4381e6f994054dd6d2c85fdd017815b0f800f1ad Mon Sep 17 00:00:00 2001 From: PhilWindle Date: Thu, 23 May 2024 15:42:30 +0100 Subject: [PATCH 08/46] Generate signing key from private key --- yarn-project/cli/src/cmds/generate_private_key.ts | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/yarn-project/cli/src/cmds/generate_private_key.ts b/yarn-project/cli/src/cmds/generate_private_key.ts index e2ff58f6767..5744658b383 100644 --- a/yarn-project/cli/src/cmds/generate_private_key.ts +++ b/yarn-project/cli/src/cmds/generate_private_key.ts @@ -1,8 +1,11 @@ -import { Fr, GrumpkinPrivateKey } from '@aztec/aztec.js'; +import { Fr } from '@aztec/aztec.js'; +import { deriveSigningKey } from '@aztec/circuits.js'; export function generateKeys() { + const privateKey = Fr.random(); + const signingKey = deriveSigningKey(privateKey); return { - privateEncryptionKey: Fr.random(), - privateSigningKey: GrumpkinPrivateKey.random(), + privateEncryptionKey: privateKey, + privateSigningKey: signingKey, }; } From 6318de0261e68d408b8a8c2e9542013046511778 Mon Sep 17 00:00:00 2001 From: Alex Gherghisan Date: Thu, 23 May 2024 15:01:27 +0000 Subject: [PATCH 09/46] feat: restore deployments --- yarn-project/cli/src/cmds/deploy.ts | 19 ++++----- yarn-project/cli/src/index.ts | 60 +++++++++++++++++------------ yarn-project/cli/src/utils.ts | 6 +-- 3 files changed, 49 insertions(+), 36 deletions(-) diff --git a/yarn-project/cli/src/cmds/deploy.ts b/yarn-project/cli/src/cmds/deploy.ts index 3def185b7a7..1b3d7dfef05 100644 --- a/yarn-project/cli/src/cmds/deploy.ts +++ b/yarn-project/cli/src/cmds/deploy.ts @@ -1,6 +1,6 @@ import { getSchnorrAccount } from '@aztec/accounts/schnorr'; -import { ContractDeployer, type EthAddress, type Fq, Fr, Point, PublicKey } from '@aztec/aztec.js'; -import { type PublicKeys } from '@aztec/circuits.js'; +import { ContractDeployer, Fr } from '@aztec/aztec.js'; +import { type PublicKeys, deriveSigningKey } from '@aztec/circuits.js'; import { getInitializer } from '@aztec/foundation/abi'; import { type DebugLogger, type LogFn } from '@aztec/foundation/log'; @@ -13,13 +13,14 @@ export async function deploy( artifactPath: string, json: boolean, rpcUrl: string, - publicKeys: PublicKeys, + publicKeys: PublicKeys | undefined, rawArgs: any[], - portalAddress: EthAddress, salt: Fr, privateKey: Fr, - signingPrivateKey: Fq, initializer: string | undefined, + skipPublicDeployment: boolean, + skipClassRegistration: boolean, + skipInitialization: boolean, wait: boolean, debugLogger: DebugLogger, log: LogFn, @@ -37,8 +38,8 @@ export async function deploy( ); } - const wallet = await getSchnorrAccount(client, privateKey, signingPrivateKey, Fr.ZERO).getWallet(); - const deployer = new ContractDeployer(contractArtifact, wallet, publicKeys.hash(), initializer); + const wallet = await getSchnorrAccount(client, privateKey, deriveSigningKey(privateKey), Fr.ZERO).getWallet(); + const deployer = new ContractDeployer(contractArtifact, wallet, publicKeys?.hash() ?? Fr.ZERO, initializer); let args = []; if (rawArgs.length > 0) { @@ -52,8 +53,8 @@ export async function deploy( const deploy = deployer.deploy(...args); - await deploy.create({ contractAddressSalt: salt }); - const tx = deploy.send({ contractAddressSalt: salt }); + await deploy.create({ contractAddressSalt: salt, skipClassRegistration, skipInitialization, skipPublicDeployment }); + const tx = deploy.send({ contractAddressSalt: salt, skipClassRegistration }); const txHash = await tx.getTxHash(); debugLogger.debug(`Deploy tx sent with hash ${txHash}`); if (wait) { diff --git a/yarn-project/cli/src/index.ts b/yarn-project/cli/src/index.ts index 7e5ee5c1f28..8c67f81384f 100644 --- a/yarn-project/cli/src/index.ts +++ b/yarn-project/cli/src/index.ts @@ -1,4 +1,4 @@ -import { deriveSigningKey } from '@aztec/circuits.js'; +import { PublicKeys, deriveSigningKey } from '@aztec/circuits.js'; import { type DebugLogger, type LogFn } from '@aztec/foundation/log'; import { fileURLToPath } from '@aztec/foundation/url'; @@ -161,7 +161,7 @@ export function getProgram(log: LogFn, debugLogger: DebugLogger): Command { '', "A compiled Aztec.nr contract's artifact in JSON format or name of a contract artifact exported by @aztec/noir-contracts.js", ) - .option('--initializer ', 'The contract initializer function to call') + .requiredOption('--initializer ', 'The contract initializer function to call', 'constructor') .option('-a, --args ', 'Contract constructor arguments', []) .addOption(pxeOption) .option( @@ -169,11 +169,6 @@ export function getProgram(log: LogFn, debugLogger: DebugLogger): Command { 'Optional encryption public key for this address. Set this value only if this contract is expected to receive private notes, which will be encrypted using this public key.', parsePublicKey, ) - .option( - '-p, --portal-address ', - 'Optional L1 portal address to link the contract to.', - parseEthereumAddress, - ) .option( '-s, --salt ', 'Optional deployment salt as a hex string for generating the deployment address.', @@ -184,27 +179,44 @@ export function getProgram(log: LogFn, debugLogger: DebugLogger): Command { // `options.wait` is default true. Passing `--no-wait` will set it to false. // https://github.com/tj/commander.js#other-option-types-negatable-boolean-and-booleanvalue .option('--no-wait', 'Skip waiting for the contract to be deployed. Print the hash of deployment transaction') + .option('--no-class-registration', 'Skip registering the contract class', true) + .option('--no-public-deployment', 'Skip deploying the contract publicly', false) + .option('--no-initialization', 'Skip initializing the contract', false) .action( async ( artifactPath, - { json, rpcUrl, publicKey, args: rawArgs, portalAddress, salt, wait, privateKey, initializer }, + { + json, + rpcUrl, + publicKey, + args: rawArgs, + salt, + wait, + privateKey, + initializer, + classRegistration, + initialization, + publicDeployment, + }, ) => { - // const { deploy } = await import('./cmds/deploy.js'); - // await deploy( - // artifactPath, - // json, - // rpcUrl, - // publicKey, - // rawArgs, - // portalAddress, - // salt, - // privateKey, - // initializer, - // wait, - // debugLogger, - // log, - // logJson, - // ); + const { deploy } = await import('./cmds/deploy.js'); + await deploy( + artifactPath, + json, + rpcUrl, + publicKey ? PublicKeys.fromString(publicKey) : undefined, + rawArgs, + salt, + privateKey, + initializer, + !publicDeployment, + !classRegistration, + !initialization, + wait, + debugLogger, + log, + logJson, + ); }, ); diff --git a/yarn-project/cli/src/utils.ts b/yarn-project/cli/src/utils.ts index 756b3193c7b..cdc470b1e96 100644 --- a/yarn-project/cli/src/utils.ts +++ b/yarn-project/cli/src/utils.ts @@ -2,8 +2,8 @@ import { type ContractArtifact, type FunctionArtifact, loadContractArtifact } fr import { AztecAddress } from '@aztec/aztec.js/aztec_address'; import { type L1ContractArtifactsForDeployment } from '@aztec/aztec.js/ethereum'; import { type PXE } from '@aztec/aztec.js/interfaces/pxe'; -import { DebugLogger, LogFn } from '@aztec/foundation/log'; -import { NoirPackageConfig } from '@aztec/foundation/noir'; +import { type DebugLogger, type LogFn } from '@aztec/foundation/log'; +import { type NoirPackageConfig } from '@aztec/foundation/noir'; import { AvailabilityOracleAbi, AvailabilityOracleBytecode, @@ -109,7 +109,7 @@ export async function deployAztecContracts( * @returns The contract ABIs. */ export async function getExampleContractArtifacts(): Promise { - const imports: any = await import('@aztec/noir-contracts.js'); + const imports = await import('@aztec/noir-contracts.js'); return Object.fromEntries(Object.entries(imports).filter(([key]) => key.endsWith('Artifact'))) as any; } From af943c5d375ac62cc2eba5a99aeee13d4b2d4464 Mon Sep 17 00:00:00 2001 From: Alex Gherghisan Date: Thu, 23 May 2024 15:32:38 +0000 Subject: [PATCH 10/46] feat: send --- yarn-project/cli/src/cmds/deploy.ts | 30 +++++++++++++++------ yarn-project/cli/src/cmds/send.ts | 11 +++++--- yarn-project/cli/src/index.ts | 41 +++++++++++++++-------------- 3 files changed, 51 insertions(+), 31 deletions(-) diff --git a/yarn-project/cli/src/cmds/deploy.ts b/yarn-project/cli/src/cmds/deploy.ts index 1b3d7dfef05..3bd7b1c97ac 100644 --- a/yarn-project/cli/src/cmds/deploy.ts +++ b/yarn-project/cli/src/cmds/deploy.ts @@ -20,7 +20,7 @@ export async function deploy( initializer: string | undefined, skipPublicDeployment: boolean, skipClassRegistration: boolean, - skipInitialization: boolean, + skipInitialization: boolean | undefined, wait: boolean, debugLogger: DebugLogger, log: LogFn, @@ -54,30 +54,44 @@ export async function deploy( const deploy = deployer.deploy(...args); await deploy.create({ contractAddressSalt: salt, skipClassRegistration, skipInitialization, skipPublicDeployment }); - const tx = deploy.send({ contractAddressSalt: salt, skipClassRegistration }); + const tx = deploy.send({ + contractAddressSalt: salt, + skipClassRegistration, + skipInitialization, + skipPublicDeployment, + }); + const txHash = await tx.getTxHash(); debugLogger.debug(`Deploy tx sent with hash ${txHash}`); if (wait) { const deployed = await tx.wait(); - const { address, partialAddress } = deployed.contract; + const { address, partialAddress, instance } = deployed.contract; if (json) { - logJson({ address: address.toString(), partialAddress: partialAddress.toString() }); + logJson({ + address: address.toString(), + partialAddress: partialAddress.toString(), + initializationHash: instance.initializationHash.toString(), + }); } else { - log(`\nContract deployed at ${address.toString()}\n`); - log(`Contract partial address ${partialAddress.toString()}\n`); + log(`Contract deployed at ${address.toString()}`); + log(`Contract partial address ${partialAddress.toString()}`); + log(`Contract init hash ${instance.initializationHash.toString()}`); } } else { const { address, partialAddress } = deploy; + const instance = deploy.getInstance(); if (json) { logJson({ address: address?.toString() ?? 'N/A', partialAddress: partialAddress?.toString() ?? 'N/A', txHash: txHash.toString(), + initializationHash: instance.initializationHash.toString(), }); } else { - log(`\nContract Address: ${address?.toString() ?? 'N/A'}`); + log(`Contract Address: ${address?.toString() ?? 'N/A'}`); log(`Contract Partial Address: ${partialAddress?.toString() ?? 'N/A'}`); - log(`Deployment transaction hash: ${txHash}\n`); + log(`Deployment transaction hash: ${txHash}`); + log(`Contract init hash ${instance.initializationHash.toString()}`); } } } diff --git a/yarn-project/cli/src/cmds/send.ts b/yarn-project/cli/src/cmds/send.ts index d9f9341f3bb..1e6edc436f6 100644 --- a/yarn-project/cli/src/cmds/send.ts +++ b/yarn-project/cli/src/cmds/send.ts @@ -1,5 +1,6 @@ import { getSchnorrAccount } from '@aztec/accounts/schnorr'; import { type AztecAddress, Contract, type Fq, Fr } from '@aztec/aztec.js'; +import { deriveSigningKey } from '@aztec/circuits.js'; import { type DebugLogger, type LogFn } from '@aztec/foundation/log'; import { createCompatibleClient } from '../client.js'; @@ -11,7 +12,6 @@ export async function send( contractArtifactPath: string, contractAddress: AztecAddress, encryptionPrivateKey: Fr, - signingPrivateKey: Fq, rpcUrl: string, wait: boolean, debugLogger: DebugLogger, @@ -20,7 +20,12 @@ export async function send( const { functionArgs, contractArtifact } = await prepTx(contractArtifactPath, functionName, functionArgsIn, log); const client = await createCompatibleClient(rpcUrl, debugLogger); - const wallet = await getSchnorrAccount(client, encryptionPrivateKey, signingPrivateKey, Fr.ZERO).getWallet(); + const wallet = await getSchnorrAccount( + client, + encryptionPrivateKey, + deriveSigningKey(encryptionPrivateKey), + Fr.ZERO, + ).getWallet(); const contract = await Contract.at(contractAddress, contractArtifact, wallet); const tx = contract.methods[functionName](...functionArgs).send(); log(`\nTransaction hash: ${(await tx.getTxHash()).toString()}`); @@ -30,7 +35,7 @@ export async function send( log('Transaction has been mined'); const receipt = await tx.getReceipt(); - log(`Status: ${receipt.status}\n`); + log(`Status: ${receipt.status}`); log(`Block number: ${receipt.blockNumber}`); log(`Block hash: ${receipt.blockHash?.toString('hex')}`); } else { diff --git a/yarn-project/cli/src/index.ts b/yarn-project/cli/src/index.ts index 8c67f81384f..9c7703b8210 100644 --- a/yarn-project/cli/src/index.ts +++ b/yarn-project/cli/src/index.ts @@ -161,7 +161,8 @@ export function getProgram(log: LogFn, debugLogger: DebugLogger): Command { '', "A compiled Aztec.nr contract's artifact in JSON format or name of a contract artifact exported by @aztec/noir-contracts.js", ) - .requiredOption('--initializer ', 'The contract initializer function to call', 'constructor') + .option('--initialize ', 'The contract initializer function to call', 'constructor') + .option('--no-initialize') .option('-a, --args ', 'Contract constructor arguments', []) .addOption(pxeOption) .option( @@ -179,9 +180,10 @@ export function getProgram(log: LogFn, debugLogger: DebugLogger): Command { // `options.wait` is default true. Passing `--no-wait` will set it to false. // https://github.com/tj/commander.js#other-option-types-negatable-boolean-and-booleanvalue .option('--no-wait', 'Skip waiting for the contract to be deployed. Print the hash of deployment transaction') - .option('--no-class-registration', 'Skip registering the contract class', true) - .option('--no-public-deployment', 'Skip deploying the contract publicly', false) - .option('--no-initialization', 'Skip initializing the contract', false) + .option('--class-registration', 'Register the contract class. Only has to be done once') + .option('--no-class-registration', 'Skip registering the contract class') + .option('--public-deployment', 'Deploy the public bytecode of contract') + .option('--no-public-deployment', "Skip deploying the contract's public bytecode") .action( async ( artifactPath, @@ -193,9 +195,8 @@ export function getProgram(log: LogFn, debugLogger: DebugLogger): Command { salt, wait, privateKey, - initializer, classRegistration, - initialization, + initialize, publicDeployment, }, ) => { @@ -208,10 +209,10 @@ export function getProgram(log: LogFn, debugLogger: DebugLogger): Command { rawArgs, salt, privateKey, - initializer, + typeof initialize === 'string' ? initialize : undefined, !publicDeployment, !classRegistration, - !initialization, + typeof initialize === 'string' ? false : initialize, wait, debugLogger, log, @@ -376,18 +377,18 @@ export function getProgram(log: LogFn, debugLogger: DebugLogger): Command { .addOption(pxeOption) .option('--no-wait', 'Print transaction hash without waiting for it to be mined') .action(async (functionName, options) => { - // const { send } = await import('./cmds/send.js'); - // await send( - // functionName, - // options.args, - // options.contractArtifact, - // options.contractAddress, - // options.privateKey, - // options.rpcUrl, - // !options.noWait, - // debugLogger, - // log, - // ); + const { send } = await import('./cmds/send.js'); + await send( + functionName, + options.args, + options.contractArtifact, + options.contractAddress, + options.privateKey, + options.rpcUrl, + !options.noWait, + debugLogger, + log, + ); }); program From 6267f52eaba3aa57fa5b5c9ec4b4e2ad94edd24f Mon Sep 17 00:00:00 2001 From: Alex Gherghisan Date: Thu, 23 May 2024 15:36:43 +0000 Subject: [PATCH 11/46] fix: return salt from contract deploy --- yarn-project/cli/src/cmds/deploy.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/yarn-project/cli/src/cmds/deploy.ts b/yarn-project/cli/src/cmds/deploy.ts index 3bd7b1c97ac..8996cce13fa 100644 --- a/yarn-project/cli/src/cmds/deploy.ts +++ b/yarn-project/cli/src/cmds/deploy.ts @@ -71,11 +71,13 @@ export async function deploy( address: address.toString(), partialAddress: partialAddress.toString(), initializationHash: instance.initializationHash.toString(), + salt: salt.toString(), }); } else { log(`Contract deployed at ${address.toString()}`); log(`Contract partial address ${partialAddress.toString()}`); log(`Contract init hash ${instance.initializationHash.toString()}`); + log(`Deployment salt: ${salt.toString()}`); } } else { const { address, partialAddress } = deploy; @@ -86,12 +88,14 @@ export async function deploy( partialAddress: partialAddress?.toString() ?? 'N/A', txHash: txHash.toString(), initializationHash: instance.initializationHash.toString(), + salt: salt.toString(), }); } else { log(`Contract Address: ${address?.toString() ?? 'N/A'}`); log(`Contract Partial Address: ${partialAddress?.toString() ?? 'N/A'}`); log(`Deployment transaction hash: ${txHash}`); log(`Contract init hash ${instance.initializationHash.toString()}`); + log(`Deployment salt: ${salt.toString()}`); } } } From 815b3abcb15c2aaa396d11257386f18f625cba8b Mon Sep 17 00:00:00 2001 From: PhilWindle Date: Thu, 23 May 2024 16:40:36 +0100 Subject: [PATCH 12/46] WIP --- yarn-project/cli/src/cmds/add_contract.ts | 4 ++-- yarn-project/cli/src/cmds/deploy.ts | 3 ++- yarn-project/cli/src/index.ts | 27 +++++++++++------------ yarn-project/cli/src/parse_args.ts | 12 ++++++---- 4 files changed, 25 insertions(+), 21 deletions(-) diff --git a/yarn-project/cli/src/cmds/add_contract.ts b/yarn-project/cli/src/cmds/add_contract.ts index 40a116422e7..5f13f5044a3 100644 --- a/yarn-project/cli/src/cmds/add_contract.ts +++ b/yarn-project/cli/src/cmds/add_contract.ts @@ -1,4 +1,4 @@ -import { AztecAddress, type ContractInstanceWithAddress, type Fr, getContractClassFromArtifact } from '@aztec/aztec.js'; +import { AztecAddress, type ContractInstanceWithAddress, Fr, getContractClassFromArtifact } from '@aztec/aztec.js'; import { type PublicKeys } from '@aztec/circuits.js'; import { computeContractAddressFromInstance } from '@aztec/circuits.js/contract'; import { type DebugLogger, type LogFn } from '@aztec/foundation/log'; @@ -23,7 +23,7 @@ export async function addContract( salt, initializationHash, contractClassId: getContractClassFromArtifact(artifact).id, - publicKeysHash: publicKeys.hash(), + publicKeysHash: publicKeys?.hash() ?? Fr.ZERO, address, deployer: deployer ?? AztecAddress.ZERO, }; diff --git a/yarn-project/cli/src/cmds/deploy.ts b/yarn-project/cli/src/cmds/deploy.ts index 1b3d7dfef05..ceec5c3ef58 100644 --- a/yarn-project/cli/src/cmds/deploy.ts +++ b/yarn-project/cli/src/cmds/deploy.ts @@ -1,6 +1,6 @@ import { getSchnorrAccount } from '@aztec/accounts/schnorr'; import { ContractDeployer, Fr } from '@aztec/aztec.js'; -import { type PublicKeys, deriveSigningKey } from '@aztec/circuits.js'; +import { type PublicKeys, deriveSigningKey, computeInitializationHash, getContractInstanceFromDeployParams } from '@aztec/circuits.js'; import { getInitializer } from '@aztec/foundation/abi'; import { type DebugLogger, type LogFn } from '@aztec/foundation/log'; @@ -49,6 +49,7 @@ export async function deploy( debugLogger.debug(`Input arguments: ${rawArgs.map((x: any) => `"${x}"`).join(', ')}`); args = encodeArgs(rawArgs, constructorArtifact!.parameters); debugLogger.debug(`Encoded arguments: ${args.join(', ')}`); + log(`\nInitialisation hash: ${computeInitializationHash(constructorArtifact, rawArgs)}`); } const deploy = deployer.deploy(...args); diff --git a/yarn-project/cli/src/index.ts b/yarn-project/cli/src/index.ts index 8c67f81384f..020ff0877c5 100644 --- a/yarn-project/cli/src/index.ts +++ b/yarn-project/cli/src/index.ts @@ -1,4 +1,4 @@ -import { PublicKeys, deriveSigningKey } from '@aztec/circuits.js'; +import { Fr, PublicKeys, deriveSigningKey } from '@aztec/circuits.js'; import { type DebugLogger, type LogFn } from '@aztec/foundation/log'; import { fileURLToPath } from '@aztec/foundation/url'; @@ -251,19 +251,18 @@ export function getProgram(log: LogFn, debugLogger: DebugLogger): Command { .option('--deployer-address
', 'Optional address of the contract deployer', parseAztecAddress) .addOption(pxeOption) .action(async options => { - // const { addContract } = await import('./cmds/add_contract.js'); - // await addContract( - // options.rpcUrl, - // options.contractArtifact, - // options.contractAddress, - // options.initHash, - // options.salt ?? Fr.ZERO, - // options.publicKey, - // options.portalContract, - // options.deployerAddress, - // debugLogger, - // log, - // ); + const { addContract } = await import('./cmds/add_contract.js'); + await addContract( + options.rpcUrl, + options.contractArtifact, + options.contractAddress, + options.initHash, + options.salt ?? Fr.ZERO, + options.publicKey, + options.deployerAddress, + debugLogger, + log, + ); }); program diff --git a/yarn-project/cli/src/parse_args.ts b/yarn-project/cli/src/parse_args.ts index a019e1bbe34..9161c72ef5e 100644 --- a/yarn-project/cli/src/parse_args.ts +++ b/yarn-project/cli/src/parse_args.ts @@ -4,6 +4,7 @@ import { EthAddress } from '@aztec/aztec.js/eth_address'; import { Fr, Point } from '@aztec/aztec.js/fields'; import { LogId } from '@aztec/aztec.js/log_id'; import { TxHash } from '@aztec/aztec.js/tx_hash'; +import { PublicKeys } from '@aztec/circuits.js'; import { InvalidArgumentError } from 'commander'; @@ -159,13 +160,16 @@ export function parseOptionalTxHash(txHash: string): TxHash | undefined { /** * Parses a public key from a string. - * @param publicKey - A public key - * @returns A Point instance + * @param publicKey - A public keys object serialised as a string + * @returns A PublicKeys instance * @throws InvalidArgumentError if the input string is not valid. */ -export function parsePublicKey(publicKey: string): Point { +export function parsePublicKey(publicKey: string): PublicKeys | undefined { + if (!publicKey) { + return undefined; + } try { - return Point.fromString(publicKey); + return PublicKeys.fromString(publicKey); } catch (err) { throw new InvalidArgumentError(`Invalid public key: ${publicKey}`); } From 474c43a07d738b87bc70db7766f4c414224e1ca6 Mon Sep 17 00:00:00 2001 From: Alex Gherghisan Date: Thu, 23 May 2024 16:44:30 +0000 Subject: [PATCH 13/46] fix: add extra pxe flags --- yarn-project/pxe/src/config/index.ts | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/yarn-project/pxe/src/config/index.ts b/yarn-project/pxe/src/config/index.ts index acd6cdae938..b9caa044d73 100644 --- a/yarn-project/pxe/src/config/index.ts +++ b/yarn-project/pxe/src/config/index.ts @@ -37,12 +37,22 @@ export type PXEServiceConfig = PXEConfig & KernelProverConfig & BBProverConfig; * Creates an instance of PXEServiceConfig out of environment variables using sensible defaults for integration testing if not set. */ export function getPXEServiceConfig(): PXEServiceConfig { - const { PXE_BLOCK_POLLING_INTERVAL_MS, PXE_L2_STARTING_BLOCK, PXE_DATA_DIRECTORY } = process.env; + const { + PXE_BLOCK_POLLING_INTERVAL_MS, + PXE_L2_STARTING_BLOCK, + PXE_DATA_DIRECTORY, + BB_BINARY_PATH, + BB_WORKING_DIRECTORY, + PXE_PROVER_ENABLED, + } = process.env; return { l2BlockPollingIntervalMS: PXE_BLOCK_POLLING_INTERVAL_MS ? +PXE_BLOCK_POLLING_INTERVAL_MS : 1000, l2StartingBlock: PXE_L2_STARTING_BLOCK ? +PXE_L2_STARTING_BLOCK : INITIAL_L2_BLOCK_NUM, dataDirectory: PXE_DATA_DIRECTORY, + bbBinaryPath: BB_BINARY_PATH, + bbWorkingDirectory: BB_WORKING_DIRECTORY, + proverEnabled: ['1', 'true'].includes(PXE_PROVER_ENABLED!), }; } From 41add27c619748c25d5d0b90aa883aae5c1e0578 Mon Sep 17 00:00:00 2001 From: Alex Gherghisan Date: Thu, 23 May 2024 17:35:15 +0000 Subject: [PATCH 14/46] fix: read prover options from env --- yarn-project/aztec/src/cli/cmds/start_prover.ts | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/yarn-project/aztec/src/cli/cmds/start_prover.ts b/yarn-project/aztec/src/cli/cmds/start_prover.ts index 7c39fe6e16a..97f4fa342ae 100644 --- a/yarn-project/aztec/src/cli/cmds/start_prover.ts +++ b/yarn-project/aztec/src/cli/cmds/start_prover.ts @@ -1,4 +1,5 @@ import { type ProvingJobSource } from '@aztec/circuit-types'; +import { getProverEnvVars } from '@aztec/prover-client'; import { ProverPool, createProvingJobSourceClient } from '@aztec/prover-client/prover-pool'; import { tmpdir } from 'node:os'; @@ -14,7 +15,11 @@ type ProverOptions = Partial<{ }>; export const startProver: ServiceStarter = async (options, signalHandlers, logger) => { - const proverOptions: ProverOptions = parseModuleOptions(options.prover); + const proverOptions: ProverOptions = { + proverUrl: process.env.PROVER_URL, + ...getProverEnvVars(), + ...parseModuleOptions(options.prover), + }; let source: ProvingJobSource; if (typeof proverOptions.proverUrl === 'string') { From f09bc3a5661988106beb3660c425bf79fe327f3b Mon Sep 17 00:00:00 2001 From: Alex Gherghisan Date: Thu, 23 May 2024 17:35:27 +0000 Subject: [PATCH 15/46] fix: serialize recursive proof --- .../circuits.js/src/structs/recursive_proof.ts | 17 +++++++++++++++++ .../prover-client/src/prover-pool/rpc.ts | 9 +++++++-- 2 files changed, 24 insertions(+), 2 deletions(-) diff --git a/yarn-project/circuits.js/src/structs/recursive_proof.ts b/yarn-project/circuits.js/src/structs/recursive_proof.ts index 12358a67f52..9cc23ca8a15 100644 --- a/yarn-project/circuits.js/src/structs/recursive_proof.ts +++ b/yarn-project/circuits.js/src/structs/recursive_proof.ts @@ -69,6 +69,23 @@ export class RecursiveProof { static fromString(str: string, size: N) { return RecursiveProof.fromBuffer(Buffer.from(str, 'hex'), size); } + + toJson() { + return { + length: this.proof.length, + proof: serializeToBuffer(this.proof).toString('hex'), + binaryProof: this.binaryProof.toString(), + fieldsValid: this.fieldsValid, + }; + } + + static fromJSON(json: any) { + return new RecursiveProof( + BufferReader.asReader(Buffer.from(json.proof, 'hex')).readArray(json.length, Fr), + Proof.fromString(json.binaryProof), + json.fieldsValid, + ); + } } /** diff --git a/yarn-project/prover-client/src/prover-pool/rpc.ts b/yarn-project/prover-client/src/prover-pool/rpc.ts index a7debf7bff2..4e2cb8ebb2d 100644 --- a/yarn-project/prover-client/src/prover-pool/rpc.ts +++ b/yarn-project/prover-client/src/prover-pool/rpc.ts @@ -10,6 +10,7 @@ import { PublicKernelCircuitPrivateInputs, PublicKernelCircuitPublicInputs, PublicKernelTailCircuitPrivateInputs, + RecursiveProof, RootParityInputs, RootRollupInputs, RootRollupPublicInputs, @@ -38,7 +39,9 @@ export function createProvingJobSourceServer(queue: ProvingJobSource): JsonRpcSe KernelCircuitPublicInputs, ProvingError, }, - {}, + { + RecursiveProof, + }, ); } @@ -65,7 +68,9 @@ export function createProvingJobSourceClient( KernelCircuitPublicInputs, ProvingError, }, - {}, + { + RecursiveProof, + }, false, namespace, fetch, From 52eebf17e033f164b2ac833dfb2e6a83ac0ab708 Mon Sep 17 00:00:00 2001 From: PhilWindle Date: Thu, 23 May 2024 17:35:39 +0000 Subject: [PATCH 16/46] WIP --- yarn-project/aztec/src/cli/cmds/start_prover.ts | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/yarn-project/aztec/src/cli/cmds/start_prover.ts b/yarn-project/aztec/src/cli/cmds/start_prover.ts index 7c39fe6e16a..c596f0785a7 100644 --- a/yarn-project/aztec/src/cli/cmds/start_prover.ts +++ b/yarn-project/aztec/src/cli/cmds/start_prover.ts @@ -1,4 +1,5 @@ import { type ProvingJobSource } from '@aztec/circuit-types'; +import { getProverEnvVars } from '@aztec/prover-client'; import { ProverPool, createProvingJobSourceClient } from '@aztec/prover-client/prover-pool'; import { tmpdir } from 'node:os'; @@ -14,7 +15,10 @@ type ProverOptions = Partial<{ }>; export const startProver: ServiceStarter = async (options, signalHandlers, logger) => { - const proverOptions: ProverOptions = parseModuleOptions(options.prover); + const proverOptions: ProverOptions = { + ...getProverEnvVars(), + ...parseModuleOptions(options.prover), + }; let source: ProvingJobSource; if (typeof proverOptions.proverUrl === 'string') { From caa9f037f791b65c49502b590c0e02c27f989cc8 Mon Sep 17 00:00:00 2001 From: Alex Gherghisan Date: Fri, 24 May 2024 09:05:23 +0000 Subject: [PATCH 17/46] fix: serialisation issues --- .../src/structs/parity/root_parity_input.ts | 18 ++++++++- .../src/structs/recursive_proof.ts | 37 ++++++++----------- .../src/structs/verification_key.ts | 11 ++++-- yarn-project/cli/src/client.ts | 4 +- yarn-project/cli/src/index.ts | 1 + .../prover-client/src/prover-pool/rpc.ts | 12 ++++-- 6 files changed, 50 insertions(+), 33 deletions(-) diff --git a/yarn-project/circuits.js/src/structs/parity/root_parity_input.ts b/yarn-project/circuits.js/src/structs/parity/root_parity_input.ts index bcdafe5aa2b..1496d19b179 100644 --- a/yarn-project/circuits.js/src/structs/parity/root_parity_input.ts +++ b/yarn-project/circuits.js/src/structs/parity/root_parity_input.ts @@ -19,6 +19,10 @@ export class RootParityInput { return serializeToBuffer(...RootParityInput.getFields(this)); } + toString() { + return this.toBuffer().toString('hex'); + } + static from( fields: FieldsOf>, ): RootParityInput { @@ -29,12 +33,22 @@ export class RootParityInput { return [fields.proof, fields.verificationKey, fields.publicInputs] as const; } - static fromBuffer(buffer: Buffer | BufferReader, size: PROOF_LENGTH) { + static fromBuffer( + buffer: Buffer | BufferReader, + expectedSize?: PROOF_LENGTH, + ): RootParityInput { const reader = BufferReader.asReader(buffer); return new RootParityInput( - RecursiveProof.fromBuffer(reader, size), + RecursiveProof.fromBuffer(reader, expectedSize), reader.readObject(VerificationKeyAsFields), reader.readObject(ParityPublicInputs), ); } + + static fromString( + str: string, + expectedSize?: PROOF_LENGTH, + ): RootParityInput { + return RootParityInput.fromBuffer(Buffer.from(str, 'hex'), expectedSize); + } } diff --git a/yarn-project/circuits.js/src/structs/recursive_proof.ts b/yarn-project/circuits.js/src/structs/recursive_proof.ts index 9cc23ca8a15..4f94adbe212 100644 --- a/yarn-project/circuits.js/src/structs/recursive_proof.ts +++ b/yarn-project/circuits.js/src/structs/recursive_proof.ts @@ -38,9 +38,16 @@ export class RecursiveProof { * @param buffer - A Buffer or BufferReader containing the length-encoded proof data. * @returns A Proof instance containing the decoded proof data. */ - static fromBuffer(buffer: Buffer | BufferReader, size: N): RecursiveProof { + static fromBuffer( + buffer: Buffer | BufferReader, + expectedSize?: N, + ): RecursiveProof { const reader = BufferReader.asReader(buffer); - return new RecursiveProof(reader.readArray(size, Fr), Proof.fromBuffer(reader), reader.readBoolean()); + const size = reader.readNumber(); + if (typeof expectedSize === 'number' && expectedSize !== size) { + throw new Error(`Expected proof length ${expectedSize}, got ${size}`); + } + return new RecursiveProof(reader.readArray(size, Fr) as any, Proof.fromBuffer(reader), reader.readBoolean()); } /** @@ -50,7 +57,7 @@ export class RecursiveProof { * @returns A Buffer containing the serialized proof data in custom format. */ public toBuffer() { - return serializeToBuffer(this.proof, this.binaryProof, this.fieldsValid); + return serializeToBuffer(this.proof.length, this.proof, this.binaryProof, this.fieldsValid); } /** @@ -66,25 +73,11 @@ export class RecursiveProof { * @param str - A hex string to deserialize from. * @returns - A new Proof instance. */ - static fromString(str: string, size: N) { - return RecursiveProof.fromBuffer(Buffer.from(str, 'hex'), size); - } - - toJson() { - return { - length: this.proof.length, - proof: serializeToBuffer(this.proof).toString('hex'), - binaryProof: this.binaryProof.toString(), - fieldsValid: this.fieldsValid, - }; - } - - static fromJSON(json: any) { - return new RecursiveProof( - BufferReader.asReader(Buffer.from(json.proof, 'hex')).readArray(json.length, Fr), - Proof.fromString(json.binaryProof), - json.fieldsValid, - ); + static fromString( + str: string, + expectedSize?: N, + ): RecursiveProof { + return RecursiveProof.fromBuffer(Buffer.from(str, 'hex'), expectedSize); } } diff --git a/yarn-project/circuits.js/src/structs/verification_key.ts b/yarn-project/circuits.js/src/structs/verification_key.ts index e469a7aee33..37f723be33d 100644 --- a/yarn-project/circuits.js/src/structs/verification_key.ts +++ b/yarn-project/circuits.js/src/structs/verification_key.ts @@ -235,9 +235,10 @@ export class VerificationKeyData { return serializeToBuffer(this.keyAsFields, this.keyAsBytes.length, this.keyAsBytes); } - /** - @@ -126,28 +97,14 @@ export class VerificationKeyAsFields { - */ + toString() { + return this.toBuffer().toString('hex'); + } + static fromBuffer(buffer: Buffer | BufferReader): VerificationKeyData { const reader = BufferReader.asReader(buffer); const verificationKeyAsFields = reader.readObject(VerificationKeyAsFields); @@ -246,6 +247,10 @@ export class VerificationKeyData { return new VerificationKeyData(verificationKeyAsFields, bytes); } + static fromString(str: string): VerificationKeyData { + return VerificationKeyData.fromBuffer(Buffer.from(str, 'hex')); + } + public clone() { return VerificationKeyData.fromBuffer(this.toBuffer()); } diff --git a/yarn-project/cli/src/client.ts b/yarn-project/cli/src/client.ts index 18ea8bd2d5e..8b3d55c37d9 100644 --- a/yarn-project/cli/src/client.ts +++ b/yarn-project/cli/src/client.ts @@ -1,5 +1,5 @@ -import { PXE, createPXEClient } from '@aztec/aztec.js'; -import { DebugLogger } from '@aztec/foundation/log'; +import { type PXE, createPXEClient } from '@aztec/aztec.js'; +import { type DebugLogger } from '@aztec/foundation/log'; import { fileURLToPath } from '@aztec/foundation/url'; import { readFileSync } from 'fs'; diff --git a/yarn-project/cli/src/index.ts b/yarn-project/cli/src/index.ts index 6d5d9a86ad1..9aeb759ea58 100644 --- a/yarn-project/cli/src/index.ts +++ b/yarn-project/cli/src/index.ts @@ -124,6 +124,7 @@ export function getProgram(log: LogFn, debugLogger: DebugLogger): Command { .option('--no-wait', 'Skip waiting for the contract to be deployed. Print the hash of deployment transaction') .action(async ({ rpcUrl, privateKey, wait }) => { const { createAccount } = await import('./cmds/create_account.js'); + privateKey ??= Fr.random(); await createAccount(rpcUrl, privateKey, deriveSigningKey(privateKey), wait, debugLogger, log); }); diff --git a/yarn-project/prover-client/src/prover-pool/rpc.ts b/yarn-project/prover-client/src/prover-pool/rpc.ts index 4e2cb8ebb2d..7e506658131 100644 --- a/yarn-project/prover-client/src/prover-pool/rpc.ts +++ b/yarn-project/prover-client/src/prover-pool/rpc.ts @@ -11,9 +11,11 @@ import { PublicKernelCircuitPublicInputs, PublicKernelTailCircuitPrivateInputs, RecursiveProof, + RootParityInput, RootParityInputs, RootRollupInputs, RootRollupPublicInputs, + VerificationKeyData, } from '@aztec/circuits.js'; import { createJsonRpcClient, makeFetch } from '@aztec/foundation/json-rpc/client'; import { JsonRpcServer } from '@aztec/foundation/json-rpc/server'; @@ -30,6 +32,7 @@ export function createProvingJobSourceServer(queue: ProvingJobSource): JsonRpcSe MergeRollupInputs, ParityPublicInputs, Proof, + RootParityInput, RootParityInputs, RootRollupInputs, RootRollupPublicInputs, @@ -38,10 +41,10 @@ export function createProvingJobSourceServer(queue: ProvingJobSource): JsonRpcSe PublicKernelTailCircuitPrivateInputs, KernelCircuitPublicInputs, ProvingError, - }, - { RecursiveProof, + VerificationKeyData, }, + {}, ); } @@ -59,6 +62,7 @@ export function createProvingJobSourceClient( MergeRollupInputs, ParityPublicInputs, Proof, + RootParityInput, RootParityInputs, RootRollupInputs, RootRollupPublicInputs, @@ -67,10 +71,10 @@ export function createProvingJobSourceClient( PublicKernelTailCircuitPrivateInputs, KernelCircuitPublicInputs, ProvingError, - }, - { RecursiveProof, + VerificationKeyData, }, + {}, false, namespace, fetch, From eef468ce7d8be63f4bc07ae6abb9827113494cbd Mon Sep 17 00:00:00 2001 From: Alex Gherghisan Date: Fri, 24 May 2024 10:43:35 +0000 Subject: [PATCH 18/46] feat: deploy_l1_verifier --- .../bb-prover/src/verifier/bb_verifier.ts | 10 +- yarn-project/cli/package.json | 2 + .../cli/src/cmds/deploy_l1_contracts.ts | 2 +- .../cli/src/cmds/deploy_l1_verifier.ts | 102 ++++++++++++++++++ yarn-project/cli/src/index.ts | 45 +++++++- .../integration_proof_verification.test.ts | 2 - .../src/e2e_prover/e2e_prover_test.ts | 5 +- .../ethereum/src/deploy_l1_contracts.ts | 2 +- .../scripts/generate-artifacts.sh | 1 + yarn-project/tsconfig.json | 3 +- yarn-project/yarn.lock | 19 ++++ 11 files changed, 180 insertions(+), 13 deletions(-) create mode 100644 yarn-project/cli/src/cmds/deploy_l1_verifier.ts diff --git a/yarn-project/bb-prover/src/verifier/bb_verifier.ts b/yarn-project/bb-prover/src/verifier/bb_verifier.ts index a27f6571a29..3b24313247c 100644 --- a/yarn-project/bb-prover/src/verifier/bb_verifier.ts +++ b/yarn-project/bb-prover/src/verifier/bb_verifier.ts @@ -6,18 +6,22 @@ import { type ProtocolArtifact, ProtocolCircuitArtifacts } from '@aztec/noir-pro import * as fs from 'fs/promises'; import { BB_RESULT, generateContractForCircuit, generateKeyForNoirCircuit, verifyProof } from '../bb/execute.js'; -import { type BBProverConfig } from '../prover/bb_prover.js'; import { extractVkData } from '../verification_key/verification_key_data.js'; +export type BBConfig = { + bbBinaryPath: string; + bbWorkingDirectory: string; +}; + export class BBCircuitVerifier { private constructor( - private config: BBProverConfig, + private config: BBConfig, private verificationKeys = new Map>(), private logger: DebugLogger, ) {} public static async new( - config: BBProverConfig, + config: BBConfig, initialCircuits: ProtocolArtifact[] = [], logger = createDebugLogger('aztec:bb-verifier'), ) { diff --git a/yarn-project/cli/package.json b/yarn-project/cli/package.json index f6a4b35fe17..df1ce9e2f9e 100644 --- a/yarn-project/cli/package.json +++ b/yarn-project/cli/package.json @@ -36,6 +36,7 @@ "dependencies": { "@aztec/accounts": "workspace:^", "@aztec/aztec.js": "workspace:^", + "@aztec/bb-prover": "workspace:^", "@aztec/circuit-types": "workspace:^", "@aztec/circuits.js": "workspace:^", "@aztec/ethereum": "workspace:^", @@ -51,6 +52,7 @@ "lodash.startcase": "^4.4.0", "node-fetch": "^3.3.2", "semver": "^7.5.4", + "solc": "^0.8.26", "source-map-support": "^0.5.21", "tslib": "^2.4.0", "viem": "^2.7.15" diff --git a/yarn-project/cli/src/cmds/deploy_l1_contracts.ts b/yarn-project/cli/src/cmds/deploy_l1_contracts.ts index a7a5af57fe1..a96b3e277a2 100644 --- a/yarn-project/cli/src/cmds/deploy_l1_contracts.ts +++ b/yarn-project/cli/src/cmds/deploy_l1_contracts.ts @@ -1,4 +1,4 @@ -import { DebugLogger, LogFn } from '@aztec/foundation/log'; +import { type DebugLogger, type LogFn } from '@aztec/foundation/log'; import { deployAztecContracts } from '../utils.js'; diff --git a/yarn-project/cli/src/cmds/deploy_l1_verifier.ts b/yarn-project/cli/src/cmds/deploy_l1_verifier.ts new file mode 100644 index 00000000000..5ca89e94837 --- /dev/null +++ b/yarn-project/cli/src/cmds/deploy_l1_verifier.ts @@ -0,0 +1,102 @@ +import { BBCircuitVerifier } from '@aztec/bb-prover'; +import { createL1Clients, deployL1Contract } from '@aztec/ethereum'; +import { type DebugLogger, type LogFn } from '@aztec/foundation/log'; +import { MockVerifierAbi, MockVerifierBytecode, RollupAbi } from '@aztec/l1-artifacts'; + +// @ts-expect-error solc-js doesn't publish its types https://github.com/ethereum/solc-js/issues/689 +import solc from 'solc'; +import { getContract } from 'viem'; +import { mnemonicToAccount, privateKeyToAccount } from 'viem/accounts'; + +import { createCompatibleClient } from '../client.js'; + +export async function deployUltraVerifier( + ethRpcUrl: string, + privateKey: string, + mnemonic: string, + pxeRpcUrl: string, + bbBinaryPath: string, + bbWorkingDirectory: string, + log: LogFn, + debugLogger: DebugLogger, +) { + const circuitVerifier = await BBCircuitVerifier.new({ bbBinaryPath, bbWorkingDirectory }); + const contractSrc = await circuitVerifier.generateSolidityContract('RootRollupArtifact', 'UltraVerifier.sol'); + log('Generated UltraVerifier contract'); + + const input = { + language: 'Solidity', + sources: { + 'UltraVerifier.sol': { + content: contractSrc, + }, + }, + settings: { + // we require the optimizer + optimizer: { + enabled: true, + runs: 200, + }, + outputSelection: { + '*': { + '*': ['evm.bytecode.object', 'abi'], + }, + }, + }, + }; + + const output = JSON.parse(solc.compile(JSON.stringify(input))); + log('Compiled UltraVerifier'); + + const abi = output.contracts['UltraVerifier.sol']['UltraVerifier'].abi; + const bytecode: string = output.contracts['UltraVerifier.sol']['UltraVerifier'].evm.bytecode.object; + + const account = !privateKey + ? mnemonicToAccount(mnemonic!) + : privateKeyToAccount(`${privateKey.startsWith('0x') ? '' : '0x'}${privateKey}` as `0x${string}`); + const { publicClient, walletClient } = createL1Clients(ethRpcUrl, account); + + const verifierAddress = await deployL1Contract(walletClient, publicClient, abi, `0x${bytecode}`); + log(`Deployed UltraVerifier at ${verifierAddress.toString()}`); + + const pxe = await createCompatibleClient(pxeRpcUrl, debugLogger); + const { l1ContractAddresses } = await pxe.getNodeInfo(); + + const rollup = getContract({ + abi: RollupAbi, + address: l1ContractAddresses.rollupAddress.toString(), + client: walletClient, + }); + + await rollup.write.setVerifier([verifierAddress.toString()]); + log(`Rollup accepts only real proofs now`); +} + +export async function deployMockVerifier( + ethRpcUrl: string, + privateKey: string, + mnemonic: string, + pxeRpcUrl: string, + log: LogFn, + debugLogger: DebugLogger, +) { + const account = !privateKey + ? mnemonicToAccount(mnemonic!) + : privateKeyToAccount(`${privateKey.startsWith('0x') ? '' : '0x'}${privateKey}` as `0x${string}`); + const { publicClient, walletClient } = createL1Clients(ethRpcUrl, account); + + const mockVerifierAddress = await deployL1Contract(walletClient, publicClient, MockVerifierAbi, MockVerifierBytecode); + log(`Deployed MockVerifier at ${mockVerifierAddress.toString()}`); + + const pxe = await createCompatibleClient(pxeRpcUrl, debugLogger); + const { l1ContractAddresses } = await pxe.getNodeInfo(); + + const rollup = getContract({ + abi: RollupAbi, + address: l1ContractAddresses.rollupAddress.toString(), + client: walletClient, + }); + + await rollup.write.setVerifier([mockVerifierAddress.toString()]); + log(`Rollup accepts only fake proofs now`); +} diff --git a/yarn-project/cli/src/index.ts b/yarn-project/cli/src/index.ts index 9aeb759ea58..0eb138ebfa4 100644 --- a/yarn-project/cli/src/index.ts +++ b/yarn-project/cli/src/index.ts @@ -88,6 +88,49 @@ export function getProgram(log: LogFn, debugLogger: DebugLogger): Command { ); }); + program + .command('deploy-l1-verifier') + .description('Deploys the rollup verifier contract') + .requiredOption( + '--eth-rpc-url ', + 'Url of the ethereum host. Chain identifiers localhost and testnet can be used', + ETHEREUM_HOST, + ) + .addOption(pxeOption) + .requiredOption('-p, --private-key ', 'The private key to use for deployment', PRIVATE_KEY) + .option( + '-m, --mnemonic ', + 'The mnemonic to use in deployment', + 'test test test test test test test test test test test junk', + ) + .requiredOption('--verifier ', 'Either mock or real', 'real') + .option('--bb ', 'Path to bb binary') + .option('--bb-working-dir ', 'Path to bb working directory') + .action(async options => { + const { deployMockVerifier, deployUltraVerifier } = await import('./cmds/deploy_l1_verifier.js'); + if (options.verifier === 'mock') { + await deployMockVerifier( + options.ethRpcUrl, + options.privateKey, + options.mnemonic, + options.rpcUrl, + log, + debugLogger, + ); + } else { + await deployUltraVerifier( + options.ethRpcUrl, + options.privateKey, + options.mnemonic, + options.rpcUrl, + options.bb, + options.bbWorkingDir, + log, + debugLogger, + ); + } + }); + program .command('generate-keys') .summary('Generates encryption and signing private keys.') @@ -96,7 +139,7 @@ export function getProgram(log: LogFn, debugLogger: DebugLogger): Command { '-m, --mnemonic', 'An optional mnemonic string used for the private key generation. If not provided, random private key will be generated.', ) - .action(async options => { + .action(async _options => { const { generateKeys } = await import('./cmds/generate_private_key.js'); const { privateEncryptionKey, privateSigningKey } = generateKeys(); log(`Encryption Private Key: ${privateEncryptionKey}\nSigning Private key: ${privateSigningKey}\n`); diff --git a/yarn-project/end-to-end/src/composed/integration_proof_verification.test.ts b/yarn-project/end-to-end/src/composed/integration_proof_verification.test.ts index 62e1f5553e9..9b9e178c717 100644 --- a/yarn-project/end-to-end/src/composed/integration_proof_verification.test.ts +++ b/yarn-project/end-to-end/src/composed/integration_proof_verification.test.ts @@ -63,8 +63,6 @@ describe('proof_verification', () => { const acvm = await getACVMConfig(logger); circuitVerifier = await BBCircuitVerifier.new({ - acvmBinaryPath: acvm!.acvmBinaryPath, - acvmWorkingDirectory: acvm!.acvmWorkingDirectory, bbBinaryPath: bb!.bbBinaryPath, bbWorkingDirectory: bb!.bbWorkingDirectory, }); diff --git a/yarn-project/end-to-end/src/e2e_prover/e2e_prover_test.ts b/yarn-project/end-to-end/src/e2e_prover/e2e_prover_test.ts index ed421629e07..27b90f29185 100644 --- a/yarn-project/end-to-end/src/e2e_prover/e2e_prover_test.ts +++ b/yarn-project/end-to-end/src/e2e_prover/e2e_prover_test.ts @@ -151,10 +151,7 @@ export class FullProverTest { throw new Error(`Test must be run with BB native configuration`); } - this.circuitProofVerifier = await BBCircuitVerifier.new({ - ...acvmConfig, - ...bbConfig, - }); + this.circuitProofVerifier = await BBCircuitVerifier.new(bbConfig); this.proverPool = ProverPool.nativePool( { diff --git a/yarn-project/ethereum/src/deploy_l1_contracts.ts b/yarn-project/ethereum/src/deploy_l1_contracts.ts index 8bb1f092b06..c842f6a6c5c 100644 --- a/yarn-project/ethereum/src/deploy_l1_contracts.ts +++ b/yarn-project/ethereum/src/deploy_l1_contracts.ts @@ -95,7 +95,7 @@ export interface L1ContractArtifactsForDeployment { */ export function createL1Clients( rpcUrl: string, - mnemonicOrHdAccount: string | HDAccount, + mnemonicOrHdAccount: string | HDAccount | PrivateKeyAccount, chain: Chain = foundry, ): { publicClient: PublicClient; walletClient: WalletClient } { const hdAccount = diff --git a/yarn-project/l1-artifacts/scripts/generate-artifacts.sh b/yarn-project/l1-artifacts/scripts/generate-artifacts.sh index 67ba59a51a1..af6dec6c058 100755 --- a/yarn-project/l1-artifacts/scripts/generate-artifacts.sh +++ b/yarn-project/l1-artifacts/scripts/generate-artifacts.sh @@ -20,6 +20,7 @@ CONTRACTS=( "l1-contracts:UniswapPortal" "l1-contracts:IERC20" "l1-contracts:GasPortal" + "l1-contracts:MockVerifier" ) diff --git a/yarn-project/tsconfig.json b/yarn-project/tsconfig.json index 8fa1304b81d..96d001f5ded 100644 --- a/yarn-project/tsconfig.json +++ b/yarn-project/tsconfig.json @@ -46,7 +46,8 @@ { "path": "types/tsconfig.json" }, { "path": "world-state/tsconfig.json" }, { "path": "scripts/tsconfig.json" }, - { "path": "entrypoints/tsconfig.json" } + { "path": "entrypoints/tsconfig.json" }, + { "path": "cli/tsconfig.json" } ], "files": ["./@types/jest/index.d.ts"] } diff --git a/yarn-project/yarn.lock b/yarn-project/yarn.lock index 4f9f3211333..f9651462d03 100644 --- a/yarn-project/yarn.lock +++ b/yarn-project/yarn.lock @@ -348,6 +348,7 @@ __metadata: dependencies: "@aztec/accounts": "workspace:^" "@aztec/aztec.js": "workspace:^" + "@aztec/bb-prover": "workspace:^" "@aztec/circuit-types": "workspace:^" "@aztec/circuits.js": "workspace:^" "@aztec/ethereum": "workspace:^" @@ -371,6 +372,7 @@ __metadata: lodash.startcase: ^4.4.0 node-fetch: ^3.3.2 semver: ^7.5.4 + solc: ^0.8.26 source-map-support: ^0.5.21 ts-jest: ^29.1.0 ts-node: ^10.9.1 @@ -12657,6 +12659,23 @@ __metadata: languageName: node linkType: hard +"solc@npm:^0.8.26": + version: 0.8.26 + resolution: "solc@npm:0.8.26" + dependencies: + command-exists: ^1.2.8 + commander: ^8.1.0 + follow-redirects: ^1.12.1 + js-sha3: 0.8.0 + memorystream: ^0.3.1 + semver: ^5.5.0 + tmp: 0.0.33 + bin: + solcjs: solc.js + checksum: e3eaeac76e60676377b357af8f3919d4c8c6a74b74112b49279fe8c74a3dfa1de8afe4788689fc307453bde336edc8572988d2cf9e909f84d870420eb640400c + languageName: node + linkType: hard + "sonic-forest@npm:^1.0.0": version: 1.0.3 resolution: "sonic-forest@npm:1.0.3" From 248548e408cfce83ff6a5ae85f7d286014b54812 Mon Sep 17 00:00:00 2001 From: Alex Gherghisan Date: Fri, 24 May 2024 11:09:44 +0000 Subject: [PATCH 19/46] fix: take prover agents from env --- .../aztec/src/cli/cmds/start_prover.ts | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/yarn-project/aztec/src/cli/cmds/start_prover.ts b/yarn-project/aztec/src/cli/cmds/start_prover.ts index 97f4fa342ae..d5bd721961a 100644 --- a/yarn-project/aztec/src/cli/cmds/start_prover.ts +++ b/yarn-project/aztec/src/cli/cmds/start_prover.ts @@ -1,18 +1,19 @@ import { type ProvingJobSource } from '@aztec/circuit-types'; -import { getProverEnvVars } from '@aztec/prover-client'; +import { type ProverClientConfig, getProverEnvVars } from '@aztec/prover-client'; import { ProverPool, createProvingJobSourceClient } from '@aztec/prover-client/prover-pool'; import { tmpdir } from 'node:os'; import { type ServiceStarter, parseModuleOptions } from '../util.js'; -type ProverOptions = Partial<{ - proverUrl: string; - agents: string; - acvmBinaryPath?: string; - bbBinaryPath?: string; - simulate?: string; -}>; +type ProverOptions = ProverClientConfig & + Partial<{ + proverUrl: string; + agents: string; + acvmBinaryPath?: string; + bbBinaryPath?: string; + simulate?: string; + }>; export const startProver: ServiceStarter = async (options, signalHandlers, logger) => { const proverOptions: ProverOptions = { @@ -29,7 +30,7 @@ export const startProver: ServiceStarter = async (options, signalHandlers, logge throw new Error('Starting prover without an orchestrator is not supported'); } - const agentCount = proverOptions.agents ? parseInt(proverOptions.agents, 10) : 1; + const agentCount = proverOptions.agents ? parseInt(proverOptions.agents, 10) : proverOptions.proverAgents; if (agentCount === 0 || !Number.isSafeInteger(agentCount)) { throw new Error('Cannot start prover without agents'); } From 87233103a8cd86d83253c8ed6386bef04eca47f0 Mon Sep 17 00:00:00 2001 From: Alex Gherghisan Date: Fri, 24 May 2024 11:19:31 +0000 Subject: [PATCH 20/46] fix: print new account private key --- yarn-project/cli/src/cmds/create_account.ts | 16 ++++++++-------- yarn-project/cli/src/index.ts | 3 +-- 2 files changed, 9 insertions(+), 10 deletions(-) diff --git a/yarn-project/cli/src/cmds/create_account.ts b/yarn-project/cli/src/cmds/create_account.ts index 09decfa220c..033de9ca036 100644 --- a/yarn-project/cli/src/cmds/create_account.ts +++ b/yarn-project/cli/src/cmds/create_account.ts @@ -1,22 +1,22 @@ import { getSchnorrAccount } from '@aztec/accounts/schnorr'; -import { Fq, Fr } from '@aztec/foundation/fields'; +import { deriveSigningKey } from '@aztec/circuits.js'; +import { Fr } from '@aztec/foundation/fields'; import { type DebugLogger, type LogFn } from '@aztec/foundation/log'; import { createCompatibleClient } from '../client.js'; export async function createAccount( rpcUrl: string, - encryptionPrivateKey: Fr, - signingPrivateKey: Fq, + privateKey: Fr | undefined, wait: boolean, debugLogger: DebugLogger, log: LogFn, ) { const client = await createCompatibleClient(rpcUrl, debugLogger); - const actualEncryptionPrivateKey = encryptionPrivateKey ?? Fr.random(); - const actualSigningPrivateKey = signingPrivateKey ?? Fq.random(); + const printPK = typeof privateKey === 'undefined'; + privateKey ??= Fr.random(); - const account = getSchnorrAccount(client, actualEncryptionPrivateKey, actualSigningPrivateKey, Fr.ZERO); + const account = getSchnorrAccount(client, privateKey, deriveSigningKey(privateKey), Fr.ZERO); const { address, publicKeys, partialAddress } = account.getCompleteAddress(); await account.register(); const tx = account.deploy(); @@ -32,8 +32,8 @@ export async function createAccount( log(`\nNew account:\n`); log(`Address: ${address.toString()}`); log(`Public key: ${publicKeys.toString()}`); - if (!signingPrivateKey) { - log(`Private key: ${actualSigningPrivateKey.toString()}`); + if (printPK) { + log(`Private key: ${privateKey.toString()}`); } log(`Partial address: ${partialAddress.toString()}`); } diff --git a/yarn-project/cli/src/index.ts b/yarn-project/cli/src/index.ts index 0eb138ebfa4..f5027b4d560 100644 --- a/yarn-project/cli/src/index.ts +++ b/yarn-project/cli/src/index.ts @@ -167,8 +167,7 @@ export function getProgram(log: LogFn, debugLogger: DebugLogger): Command { .option('--no-wait', 'Skip waiting for the contract to be deployed. Print the hash of deployment transaction') .action(async ({ rpcUrl, privateKey, wait }) => { const { createAccount } = await import('./cmds/create_account.js'); - privateKey ??= Fr.random(); - await createAccount(rpcUrl, privateKey, deriveSigningKey(privateKey), wait, debugLogger, log); + await createAccount(rpcUrl, privateKey, wait, debugLogger, log); }); program From 550194fbb4368483d339b0d112bdc0657cf56056 Mon Sep 17 00:00:00 2001 From: Alex Gherghisan Date: Fri, 24 May 2024 11:22:55 +0000 Subject: [PATCH 21/46] fix: sequencer builds full blocks --- yarn-project/sequencer-client/src/sequencer/sequencer.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/yarn-project/sequencer-client/src/sequencer/sequencer.ts b/yarn-project/sequencer-client/src/sequencer/sequencer.ts index 2d068660e7f..1fe547e127a 100644 --- a/yarn-project/sequencer-client/src/sequencer/sequencer.ts +++ b/yarn-project/sequencer-client/src/sequencer/sequencer.ts @@ -212,7 +212,8 @@ export class Sequencer { // We must initialise the block to be a power of 2 in size const numRealTxs = validTxs.length; const pow2 = Math.log2(numRealTxs); - const totalTxs = 2 ** Math.ceil(pow2); + // TODO turn this back into a Math.ceil once we can pad blocks to the next-power-of-2 with empty txs + const totalTxs = 2 ** Math.floor(pow2); const blockSize = Math.max(2, totalTxs); const blockTicket = await this.prover.startNewBlock(blockSize, newGlobalVariables, l1ToL2Messages, emptyTx); From 560c5289754b64629a5bdedc14b775ed03e64a67 Mon Sep 17 00:00:00 2001 From: Mitchell Tracy Date: Mon, 27 May 2024 11:14:39 +0200 Subject: [PATCH 22/46] log out gas token addresses. support encoding of field from string --- yarn-project/aztec/src/cli/texts.ts | 4 +++- yarn-project/cli/src/cmds/deploy_l1_contracts.ts | 4 +++- yarn-project/foundation/src/abi/encoder.ts | 9 ++++++++- 3 files changed, 14 insertions(+), 3 deletions(-) diff --git a/yarn-project/aztec/src/cli/texts.ts b/yarn-project/aztec/src/cli/texts.ts index 7d1edcb3970..f4e4dd81eb0 100644 --- a/yarn-project/aztec/src/cli/texts.ts +++ b/yarn-project/aztec/src/cli/texts.ts @@ -4,7 +4,9 @@ const contractAddresses = 'registryAddress:REGISTRY_CONTRACT_ADDRESS - string - The deployed L1 registry contract address.\n' + 'inboxAddress:INBOX_CONTRACT_ADDRESS - string - The deployed L1 inbox contract address.\n' + 'outboxAddress:OUTBOX_CONTRACT_ADDRESS - string - The deployed L1 outbox contract address.\n' + - 'availabilityOracleAddress:AVAILABILITY_ORACLE_CONTRACT_ADDRESS - string - The deployed L1 availability oracle contract address.\n'; + 'availabilityOracleAddress:AVAILABILITY_ORACLE_CONTRACT_ADDRESS - string - The deployed L1 availability oracle contract address.\n' + + 'gasTokenAddress:GAS_TOKEN_CONTRACT_ADDRESS - string - The deployed L1 gas token contract address.\n' + + 'gasPortalAddress:GAS_PORTAL_CONTRACT_ADDRESS - string - The deployed L1 gas portal contract address.\n'; const p2pOptions = 'p2pBlockCheckIntervalMS:P2P_BLOCK_CHECK_INTERVAL_MS - number - The frequency in which to check for blocks. Default: 100\n' + 'p2pPeerCheckIntervalMS:P2P_PEER_CHECK_INTERVAL_MS - number - The frequency in which to check for peers. Default: 1000\n' + diff --git a/yarn-project/cli/src/cmds/deploy_l1_contracts.ts b/yarn-project/cli/src/cmds/deploy_l1_contracts.ts index a96b3e277a2..629dcaec1c7 100644 --- a/yarn-project/cli/src/cmds/deploy_l1_contracts.ts +++ b/yarn-project/cli/src/cmds/deploy_l1_contracts.ts @@ -16,7 +16,9 @@ export async function deployL1Contracts( log(`Rollup Address: ${l1ContractAddresses.rollupAddress.toString()}`); log(`Registry Address: ${l1ContractAddresses.registryAddress.toString()}`); log(`L1 -> L2 Inbox Address: ${l1ContractAddresses.inboxAddress.toString()}`); - log(`L2 -> L1 Outbox address: ${l1ContractAddresses.outboxAddress.toString()}`); + log(`L2 -> L1 Outbox Address: ${l1ContractAddresses.outboxAddress.toString()}`); log(`Availability Oracle Address: ${l1ContractAddresses.availabilityOracleAddress.toString()}`); + log(`Gas Token Address: ${l1ContractAddresses.gasTokenAddress.toString()}`); + log(`Gas Portal Address: ${l1ContractAddresses.gasPortalAddress.toString()}`); log('\n'); } diff --git a/yarn-project/foundation/src/abi/encoder.ts b/yarn-project/foundation/src/abi/encoder.ts index 119554935af..22b31c63144 100644 --- a/yarn-project/foundation/src/abi/encoder.ts +++ b/yarn-project/foundation/src/abi/encoder.ts @@ -46,6 +46,8 @@ class ArgumentEncoder { this.flattened.push(new Fr(BigInt(arg))); } else if (typeof arg === 'bigint') { this.flattened.push(new Fr(arg)); + } else if (typeof arg === 'string') { + this.flattened.push(Fr.fromString(arg)); } else if (typeof arg === 'boolean') { this.flattened.push(new Fr(arg ? 1n : 0n)); } else if (typeof arg === 'object') { @@ -103,7 +105,12 @@ class ArgumentEncoder { break; } case 'integer': - this.flattened.push(new Fr(arg)); + if (typeof arg === 'string') { + const value = BigInt(arg); + this.flattened.push(new Fr(value)); + } else { + this.flattened.push(new Fr(arg)); + } break; default: throw new Error(`Unsupported type: ${abiType}`); From 6f7ac6f0e26b3714966b24934c01d53df8a1d95a Mon Sep 17 00:00:00 2001 From: PhilWindle Date: Mon, 27 May 2024 13:00:33 +0000 Subject: [PATCH 23/46] FIxes --- yarn-project/cli/tsconfig.json | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/yarn-project/cli/tsconfig.json b/yarn-project/cli/tsconfig.json index d686ac2a146..c691892f8f0 100644 --- a/yarn-project/cli/tsconfig.json +++ b/yarn-project/cli/tsconfig.json @@ -12,6 +12,9 @@ { "path": "../aztec.js" }, + { + "path": "../bb-prover" + }, { "path": "../circuit-types" }, @@ -30,6 +33,9 @@ { "path": "../noir-contracts.js" }, + { + "path": "../protocol-contracts" + }, { "path": "../types" } From 96ed8bd5c056efbdc001ae9237d5bed69a2aa633 Mon Sep 17 00:00:00 2001 From: Alex Gherghisan Date: Tue, 28 May 2024 08:32:49 +0000 Subject: [PATCH 24/46] fix: print salt --- yarn-project/cli/src/cmds/deploy.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/yarn-project/cli/src/cmds/deploy.ts b/yarn-project/cli/src/cmds/deploy.ts index 4ec8a31dde6..8ab3a52c775 100644 --- a/yarn-project/cli/src/cmds/deploy.ts +++ b/yarn-project/cli/src/cmds/deploy.ts @@ -15,7 +15,7 @@ export async function deploy( rpcUrl: string, publicKeys: PublicKeys | undefined, rawArgs: any[], - salt: Fr, + salt: Fr | undefined, privateKey: Fr, initializer: string | undefined, skipPublicDeployment: boolean, @@ -26,6 +26,7 @@ export async function deploy( log: LogFn, logJson: (output: any) => void, ) { + salt ??= Fr.random(); const contractArtifact = await getContractArtifact(artifactPath, log); const constructorArtifact = getInitializer(contractArtifact, initializer); From 0b56fbe54ac87060e08c42a62345b11fcb66ea33 Mon Sep 17 00:00:00 2001 From: spypsy Date: Tue, 28 May 2024 09:46:41 +0000 Subject: [PATCH 25/46] update node TFs for p2p comms --- yarn-project/aztec-node/terraform/main.tf | 113 +++++++++++++++--- .../aztec-node/terraform/variables.tf | 2 +- 2 files changed, 97 insertions(+), 18 deletions(-) diff --git a/yarn-project/aztec-node/terraform/main.tf b/yarn-project/aztec-node/terraform/main.tf index aa0f09624ed..06df5c74154 100644 --- a/yarn-project/aztec-node/terraform/main.tf +++ b/yarn-project/aztec-node/terraform/main.tf @@ -163,7 +163,12 @@ resource "aws_ecs_task_definition" "aztec-node" { "containerPort": 80 }, { - "containerPort": ${var.NODE_TCP_PORT + count.index} + "containerPort": ${var.NODE_P2P_PORT + count.index}, + "protocol": "tcp" + }, + { + "containerPort": ${var.NODE_P2P_PORT + count.index}, + "protocol": "udp" } ], "environment": [ @@ -254,7 +259,11 @@ resource "aws_ecs_task_definition" "aztec-node" { }, { "name": "P2P_TCP_LISTEN_PORT", - "value": "${var.NODE_TCP_PORT + count.index}" + "value": "${var.NODE_P2P_PORT + count.index}" + }, + { + "name": "P2P_UDP_LISTEN_PORT", + "value": "${var.NODE_P2P_PORT + count.index}" }, { "name": "P2P_TCP_LISTEN_IP", @@ -262,11 +271,11 @@ resource "aws_ecs_task_definition" "aztec-node" { }, { "name": "P2P_ANNOUNCE_HOSTNAME", - "value": "/dns4/${data.terraform_remote_state.aztec-network_iac.outputs.nlb_dns}" + "value": "/ip4/${data.terraform_remote_state.aztec-network_iac.outputs.p2p_eip}" }, { "name": "P2P_ANNOUNCE_PORT", - "value": "${var.NODE_TCP_PORT + count.index}" + "value": "${var.NODE_P2P_PORT + count.index}" }, { "name": "BOOTSTRAP_NODES", @@ -340,16 +349,22 @@ resource "aws_ecs_service" "aztec-node" { } load_balancer { - target_group_arn = aws_alb_target_group.aztec-node[count.index].arn + target_group_arn = aws_alb_target_group.aztec-node-http[count.index].arn container_name = "${var.DEPLOY_TAG}-aztec-node-${count.index + 1}" container_port = 80 } load_balancer { - target_group_arn = aws_lb_target_group.aztec-node-target-group[count.index].arn + target_group_arn = aws_lb_target_group.aztec-node-tcp[count.index].arn container_name = "${var.DEPLOY_TAG}-aztec-node-${count.index + 1}" - container_port = var.NODE_TCP_PORT + count.index + container_port = var.NODE_P2P_PORT + count.index + } + + load_balancer { + target_group_arn = aws_lb_target_group.aztec-node-udp[count.index].arn + container_name = "${var.DEPLOY_TAG}-aztec-node-${count.index + 1}" + container_port = var.NODE_P2P_PORT + count.index } service_registries { @@ -362,7 +377,7 @@ resource "aws_ecs_service" "aztec-node" { } # Configure ALB to route /aztec-node to server. -resource "aws_alb_target_group" "aztec-node" { +resource "aws_alb_target_group" "aztec-node-http" { count = local.node_count name = "${var.DEPLOY_TAG}-node-${count.index + 1}-http-target" port = 80 @@ -392,7 +407,7 @@ resource "aws_lb_listener_rule" "api" { action { type = "forward" - target_group_arn = aws_alb_target_group.aztec-node[count.index].arn + target_group_arn = aws_alb_target_group.aztec-node-http[count.index].arn } condition { @@ -402,10 +417,10 @@ resource "aws_lb_listener_rule" "api" { } } -resource "aws_lb_target_group" "aztec-node-target-group" { +resource "aws_lb_target_group" "aztec-node-tcp" { count = local.node_count name = "${var.DEPLOY_TAG}-node-${count.index + 1}-p2p-target" - port = var.NODE_TCP_PORT + count.index + port = var.NODE_P2P_PORT + count.index protocol = "TCP" target_type = "ip" vpc_id = data.terraform_remote_state.setup_iac.outputs.vpc_id @@ -415,15 +430,25 @@ resource "aws_lb_target_group" "aztec-node-target-group" { interval = 10 healthy_threshold = 2 unhealthy_threshold = 2 - port = var.NODE_TCP_PORT + count.index + port = var.NODE_P2P_PORT + count.index } } -resource "aws_security_group_rule" "allow-node-tcp" { +resource "aws_security_group_rule" "allow-node-tcp-in" { count = local.node_count type = "ingress" - from_port = var.NODE_TCP_PORT + count.index - to_port = var.NODE_TCP_PORT + count.index + from_port = var.NODE_P2P_PORT + count.index + to_port = var.NODE_P2P_PORT + count.index + protocol = "tcp" + cidr_blocks = ["0.0.0.0/0"] + security_group_id = data.terraform_remote_state.aztec-network_iac.outputs.p2p_security_group_id +} + +resource "aws_security_group_rule" "allow-node-tcp-out" { + count = local.node_count + type = "outgress" + from_port = var.NODE_P2P_PORT + count.index + to_port = var.NODE_P2P_PORT + count.index protocol = "tcp" cidr_blocks = ["0.0.0.0/0"] security_group_id = data.terraform_remote_state.aztec-network_iac.outputs.p2p_security_group_id @@ -432,7 +457,7 @@ resource "aws_security_group_rule" "allow-node-tcp" { resource "aws_lb_listener" "aztec-node-tcp-listener" { count = local.node_count load_balancer_arn = data.terraform_remote_state.aztec-network_iac.outputs.nlb_arn - port = var.NODE_TCP_PORT + count.index + port = var.NODE_P2P_PORT + count.index protocol = "TCP" tags = { @@ -441,6 +466,60 @@ resource "aws_lb_listener" "aztec-node-tcp-listener" { default_action { type = "forward" - target_group_arn = aws_lb_target_group.aztec-node-target-group[count.index].arn + target_group_arn = aws_lb_target_group.aztec-node-tcp[count.index].arn + } +} + + +resource "aws_lb_target_group" "aztec-node-udp" { + count = local.node_count + name = "${var.DEPLOY_TAG}-node-${count.index + 1}-p2p-target" + port = var.NODE_P2P_PORT + count.index + protocol = "UDP" + target_type = "ip" + vpc_id = data.terraform_remote_state.setup_iac.outputs.vpc_id + + health_check { + protocol = "TCP" + interval = 10 + healthy_threshold = 2 + unhealthy_threshold = 2 + port = var.NODE_P2P_PORT + count.index + } +} + +resource "aws_security_group_rule" "allow-node-udp-in" { + count = local.node_count + type = "ingress" + from_port = var.NODE_P2P_PORT + count.index + to_port = var.NODE_P2P_PORT + count.index + protocol = "udp" + cidr_blocks = ["0.0.0.0/0"] + security_group_id = data.terraform_remote_state.aztec-network_iac.outputs.p2p_security_group_id +} + +resource "aws_security_group_rule" "allow-node-udp-out" { + count = local.node_count + type = "outgress" + from_port = var.NODE_P2P_PORT + count.index + to_port = var.NODE_P2P_PORT + count.index + protocol = "udp" + cidr_blocks = ["0.0.0.0/0"] + security_group_id = data.terraform_remote_state.aztec-network_iac.outputs.p2p_security_group_id +} + +resource "aws_lb_listener" "aztec-node-udp-listener" { + count = local.node_count + load_balancer_arn = data.terraform_remote_state.aztec-network_iac.outputs.nlb_arn + port = var.NODE_P2P_PORT + count.index + protocol = "UDP" + + tags = { + name = "aztec-node-${count.index}-udp-listener" + } + + default_action { + type = "forward" + target_group_arn = aws_lb_target_group.aztec-node-tcp[count.index].arn } } diff --git a/yarn-project/aztec-node/terraform/variables.tf b/yarn-project/aztec-node/terraform/variables.tf index 30e0f59f597..ee956e30089 100644 --- a/yarn-project/aztec-node/terraform/variables.tf +++ b/yarn-project/aztec-node/terraform/variables.tf @@ -32,7 +32,7 @@ variable "BOOTNODE_2_PEER_ID" { type = string } -variable "NODE_TCP_PORT" { +variable "NODE_P2P_PORT" { type = number default = 40400 } From dbb14b1099828fd6f9e106c038f4afcb0fbccabf Mon Sep 17 00:00:00 2001 From: spypsy Date: Tue, 28 May 2024 09:54:24 +0000 Subject: [PATCH 26/46] remove old bootnode stuff --- yarn-project/aztec-node/terraform/main.tf | 9 ++------- yarn-project/aztec-node/terraform/variables.tf | 13 ------------- 2 files changed, 2 insertions(+), 20 deletions(-) diff --git a/yarn-project/aztec-node/terraform/main.tf b/yarn-project/aztec-node/terraform/main.tf index 06df5c74154..9bb1c3fc688 100644 --- a/yarn-project/aztec-node/terraform/main.tf +++ b/yarn-project/aztec-node/terraform/main.tf @@ -55,14 +55,9 @@ data "terraform_remote_state" "l1_contracts" { # Compute local variables locals { publisher_private_keys = [var.SEQ_1_PUBLISHER_PRIVATE_KEY, var.SEQ_2_PUBLISHER_PRIVATE_KEY] - bootnode_ids = [var.BOOTNODE_1_PEER_ID, var.BOOTNODE_2_PEER_ID] node_p2p_private_keys = [var.NODE_1_PRIVATE_KEY, var.NODE_2_PRIVATE_KEY] node_count = length(local.publisher_private_keys) - bootnodes = [for i in range(0, local.node_count) : - "/dns4/${var.DEPLOY_TAG}-p2p-bootstrap-${i + 1}.local/tcp/${var.BOOTNODE_LISTEN_PORT + i}/p2p/${local.bootnode_ids[i]}" - ] - combined_bootnodes = join(",", local.bootnodes) - data_dir = "/usr/src/yarn-project/aztec/data" + data_dir = "/usr/src/yarn-project/aztec/data" } resource "aws_cloudwatch_log_group" "aztec-node-log-group" { @@ -279,7 +274,7 @@ resource "aws_ecs_task_definition" "aztec-node" { }, { "name": "BOOTSTRAP_NODES", - "value": "${local.combined_bootnodes}" + "value": "enr:-JO4QNvVz7yYHQ4nzZQ7JCng9LOQkDnFqeLntDEfrAAGOS_eMFWOE4ZlyjYKb3J-yCGu8xoXXEUnUqI8iTJj1K43KH0EjWF6dGVjX25ldHdvcmsBgmlkgnY0gmlwhA0pYm6Jc2VjcDI1NmsxoQLzGvsxdzM9VhPjrMnxLmMxvrEcvSg-QZq7PWXDnnIy1YN1ZHCCnjQ" }, { "name": "P2P_ENABLED", diff --git a/yarn-project/aztec-node/terraform/variables.tf b/yarn-project/aztec-node/terraform/variables.tf index ee956e30089..9100bfe1d7a 100644 --- a/yarn-project/aztec-node/terraform/variables.tf +++ b/yarn-project/aztec-node/terraform/variables.tf @@ -19,19 +19,6 @@ variable "CHAIN_ID" { default = 31337 } -variable "BOOTNODE_LISTEN_PORT" { - type = number - default = 40500 -} - -variable "BOOTNODE_1_PEER_ID" { - type = string -} - -variable "BOOTNODE_2_PEER_ID" { - type = string -} - variable "NODE_P2P_PORT" { type = number default = 40400 From f1de35a01bbc226bec8bd0dfcb2c19f66dff2bf1 Mon Sep 17 00:00:00 2001 From: PhilWindle Date: Tue, 28 May 2024 10:01:20 +0000 Subject: [PATCH 27/46] WIP --- .../terraform/node}/main.tf | 241 +++++++++++++++--- .../terraform/node}/servicediscovery-drain.sh | 0 .../terraform/node}/variables.tf | 27 +- yarn-project/cli/package.json | 20 +- 4 files changed, 237 insertions(+), 51 deletions(-) rename yarn-project/{aztec-node/terraform => aztec/terraform/node}/main.tf (62%) rename yarn-project/{aztec-node/terraform => aztec/terraform/node}/servicediscovery-drain.sh (100%) rename yarn-project/{aztec-node/terraform => aztec/terraform/node}/variables.tf (62%) diff --git a/yarn-project/aztec-node/terraform/main.tf b/yarn-project/aztec/terraform/node/main.tf similarity index 62% rename from yarn-project/aztec-node/terraform/main.tf rename to yarn-project/aztec/terraform/node/main.tf index aa0f09624ed..fb43e513986 100644 --- a/yarn-project/aztec-node/terraform/main.tf +++ b/yarn-project/aztec/terraform/node/main.tf @@ -55,14 +55,9 @@ data "terraform_remote_state" "l1_contracts" { # Compute local variables locals { publisher_private_keys = [var.SEQ_1_PUBLISHER_PRIVATE_KEY, var.SEQ_2_PUBLISHER_PRIVATE_KEY] - bootnode_ids = [var.BOOTNODE_1_PEER_ID, var.BOOTNODE_2_PEER_ID] node_p2p_private_keys = [var.NODE_1_PRIVATE_KEY, var.NODE_2_PRIVATE_KEY] node_count = length(local.publisher_private_keys) - bootnodes = [for i in range(0, local.node_count) : - "/dns4/${var.DEPLOY_TAG}-p2p-bootstrap-${i + 1}.local/tcp/${var.BOOTNODE_LISTEN_PORT + i}/p2p/${local.bootnode_ids[i]}" - ] - combined_bootnodes = join(",", local.bootnodes) - data_dir = "/usr/src/yarn-project/aztec/data" + data_dir = "/usr/src/yarn-project/aztec/data" } resource "aws_cloudwatch_log_group" "aztec-node-log-group" { @@ -163,7 +158,7 @@ resource "aws_ecs_task_definition" "aztec-node" { "containerPort": 80 }, { - "containerPort": ${var.NODE_TCP_PORT + count.index} + "containerPort": ${var.P2P_TCP_LISTEN_PORT + count.index} } ], "environment": [ @@ -185,7 +180,7 @@ resource "aws_ecs_task_definition" "aztec-node" { }, { "name": "DEBUG", - "value": "aztec:*" + "value": "aztec:*,-json-rpc:json_proxy:*,-aztec:avm_simulator:*" }, { "name": "ETHEREUM_HOST", @@ -218,31 +213,31 @@ resource "aws_ecs_task_definition" "aztec-node" { { "name": "ROLLUP_CONTRACT_ADDRESS", - "value": "${data.terraform_remote_state.l1_contracts.outputs.rollup_contract_address}" + "value": "${var.ROLLUP_CONTRACT_ADDRESS}" }, { "name": "INBOX_CONTRACT_ADDRESS", - "value": "${data.terraform_remote_state.l1_contracts.outputs.inbox_contract_address}" + "value": "${var.INBOX_CONTRACT_ADDRESS}" }, { "name": "OUTBOX_CONTRACT_ADDRESS", - "value": "${data.terraform_remote_state.l1_contracts.outputs.outbox_contract_address}" + "value": "${var.OUTBOX_CONTRACT_ADDRESS}" }, { "name": "REGISTRY_CONTRACT_ADDRESS", - "value": "${data.terraform_remote_state.l1_contracts.outputs.registry_contract_address}" + "value": "${var.REGISTRY_CONTRACT_ADDRESS}" }, { "name": "AVAILABILITY_ORACLE_CONTRACT_ADDRESS", - "value": "${data.terraform_remote_state.l1_contracts.outputs.availability_oracle_contract_address}" + "value": "${var.AVAILABILITY_ORACLE_CONTRACT_ADDRESS}" }, { "name": "GAS_TOKEN_CONTRACT_ADDRESS", - "value": "${data.terraform_remote_state.l1_contracts.outputs.gas_token_contract_address}" + "value": "${var.GAS_TOKEN_CONTRACT_ADDRESS}" }, { "name": "GAS_PORTAL_CONTRACT_ADDRESS", - "value": "${data.terraform_remote_state.l1_contracts.outputs.gas_portal_contract_address}" + "value": "${var.GAS_PORTAL_CONTRACT_ADDRESS}" }, { "name": "API_KEY", @@ -254,7 +249,7 @@ resource "aws_ecs_task_definition" "aztec-node" { }, { "name": "P2P_TCP_LISTEN_PORT", - "value": "${var.NODE_TCP_PORT + count.index}" + "value": "${var.P2P_TCP_LISTEN_PORT + count.index}" }, { "name": "P2P_TCP_LISTEN_IP", @@ -266,15 +261,15 @@ resource "aws_ecs_task_definition" "aztec-node" { }, { "name": "P2P_ANNOUNCE_PORT", - "value": "${var.NODE_TCP_PORT + count.index}" + "value": "${var.P2P_TCP_LISTEN_PORT + count.index}" }, { "name": "BOOTSTRAP_NODES", - "value": "${local.combined_bootnodes}" + "value": "enr:-JO4QNvVz7yYHQ4nzZQ7JCng9LOQkDnFqeLntDEfrAAGOS_eMFWOE4ZlyjYKb3J-yCGu8xoXXEUnUqI8iTJj1K43KH0EjWF6dGVjX25ldHdvcmsBgmlkgnY0gmlwhA0pYm6Jc2VjcDI1NmsxoQLzGvsxdzM9VhPjrMnxLmMxvrEcvSg-QZq7PWXDnnIy1YN1ZHCCnjQ" }, { "name": "P2P_ENABLED", - "value": "true" + "value": "${var.P2P_ENABLED}" }, { "name": "CHAIN_ID", @@ -300,6 +295,30 @@ resource "aws_ecs_task_definition" "aztec-node" { "name": "P2P_PEER_CHECK_INTERVAL_MS", "value": "2000" }, + { + "name": "ACVM_WORKING_DIRECTORY", + "value": "/usr/src/acvm" + }, + { + "name": "BB_WORKING_DIRECTORY", + "value": "/usr/src/bb" + }, + { + "name": "ACVM_BINARY_PATH", + "value": "/usr/src/noir/noir-repo/target/release/acvm" + }, + { + "name": "BB_BINARY_PATH", + "value": "/usr/src/barretenberg/cpp/build/bin/bb" + }, + { + "name": "PROVER_AGENTS", + "value": "0" + }, + { + "name": "PROVER_REAL_PROOFS", + "value": "false" + } ], "mountPoints": [ { @@ -346,11 +365,11 @@ resource "aws_ecs_service" "aztec-node" { } - load_balancer { - target_group_arn = aws_lb_target_group.aztec-node-target-group[count.index].arn - container_name = "${var.DEPLOY_TAG}-aztec-node-${count.index + 1}" - container_port = var.NODE_TCP_PORT + count.index - } + # load_balancer { + # target_group_arn = aws_lb_target_group.aztec-node-target-group[count.index].arn + # container_name = "${var.DEPLOY_TAG}-aztec-node-${count.index + 1}" + # container_port = var.P2P_TCP_LISTEN_PORT + count.index + # } service_registries { registry_arn = aws_service_discovery_service.aztec-node[count.index].arn @@ -405,25 +424,26 @@ resource "aws_lb_listener_rule" "api" { resource "aws_lb_target_group" "aztec-node-target-group" { count = local.node_count name = "${var.DEPLOY_TAG}-node-${count.index + 1}-p2p-target" - port = var.NODE_TCP_PORT + count.index + port = var.P2P_TCP_LISTEN_PORT + count.index protocol = "TCP" target_type = "ip" vpc_id = data.terraform_remote_state.setup_iac.outputs.vpc_id - health_check { - protocol = "TCP" - interval = 10 - healthy_threshold = 2 - unhealthy_threshold = 2 - port = var.NODE_TCP_PORT + count.index - } + # health_check { + # protocol = "TCP" + # interval = 10 + # healthy_threshold = 2 + # unhealthy_threshold = 2 + # port = var.P2P_TCP_LISTEN_PORT + count.index + # enabled = var.P2P_ENABLED + # } } resource "aws_security_group_rule" "allow-node-tcp" { count = local.node_count type = "ingress" - from_port = var.NODE_TCP_PORT + count.index - to_port = var.NODE_TCP_PORT + count.index + from_port = var.P2P_TCP_LISTEN_PORT + count.index + to_port = var.P2P_TCP_LISTEN_PORT + count.index protocol = "tcp" cidr_blocks = ["0.0.0.0/0"] security_group_id = data.terraform_remote_state.aztec-network_iac.outputs.p2p_security_group_id @@ -432,7 +452,7 @@ resource "aws_security_group_rule" "allow-node-tcp" { resource "aws_lb_listener" "aztec-node-tcp-listener" { count = local.node_count load_balancer_arn = data.terraform_remote_state.aztec-network_iac.outputs.nlb_arn - port = var.NODE_TCP_PORT + count.index + port = var.P2P_TCP_LISTEN_PORT + count.index protocol = "TCP" tags = { @@ -444,3 +464,154 @@ resource "aws_lb_listener" "aztec-node-tcp-listener" { target_group_arn = aws_lb_target_group.aztec-node-target-group[count.index].arn } } + + + + + + +// Configuration for proving agents + +resource "aws_cloudwatch_log_group" "aztec-proving-agent-log-group" { + count = local.node_count + name = "/fargate/service/${var.DEPLOY_TAG}/aztec-proving-agent-${count.index + 1}" + retention_in_days = 14 +} + +resource "aws_service_discovery_service" "aztec-proving-agent" { + count = local.node_count + name = "${var.DEPLOY_TAG}-aztec-proving-agent-${count.index + 1}" + + health_check_custom_config { + failure_threshold = 1 + } + + dns_config { + namespace_id = data.terraform_remote_state.setup_iac.outputs.local_service_discovery_id + + dns_records { + ttl = 60 + type = "A" + } + + dns_records { + ttl = 60 + type = "SRV" + } + + routing_policy = "MULTIVALUE" + } + + # Terraform just fails if this resource changes and you have registered instances. + provisioner "local-exec" { + when = destroy + command = "${path.module}/servicediscovery-drain.sh ${self.id}" + } +} + +# Define task definitions for each node. +resource "aws_ecs_task_definition" "aztec-proving-agent" { + count = local.node_count + family = "${var.DEPLOY_TAG}-aztec-proving-agent-${count.index + 1}" + requires_compatibilities = ["FARGATE"] + network_mode = "awsvpc" + cpu = "16384" + memory = "65536" + execution_role_arn = data.terraform_remote_state.setup_iac.outputs.ecs_task_execution_role_arn + task_role_arn = data.terraform_remote_state.aztec2_iac.outputs.cloudwatch_logging_ecs_role_arn + + container_definitions = < Date: Tue, 28 May 2024 13:03:02 +0000 Subject: [PATCH 28/46] WIP --- yarn-project/aztec/terraform/node/main.tf | 68 +++++++++---------- .../aztec/terraform/node/variables.tf | 7 +- 2 files changed, 39 insertions(+), 36 deletions(-) diff --git a/yarn-project/aztec/terraform/node/main.tf b/yarn-project/aztec/terraform/node/main.tf index 645c63f472f..068139a50ef 100644 --- a/yarn-project/aztec/terraform/node/main.tf +++ b/yarn-project/aztec/terraform/node/main.tf @@ -158,11 +158,11 @@ resource "aws_ecs_task_definition" "aztec-node" { "containerPort": 80 }, { - "containerPort": ${var.NODE_P2P_PORT + count.index}, + "containerPort": ${var.NODE_P2P_TCP_PORT + count.index}, "protocol": "tcp" }, { - "containerPort": ${var.NODE_P2P_PORT + count.index}, + "containerPort": ${var.NODE_P2P_UDP_PORT + count.index}, "protocol": "udp" } ], @@ -185,7 +185,7 @@ resource "aws_ecs_task_definition" "aztec-node" { }, { "name": "DEBUG", - "value": "aztec:*,-json-rpc:json_proxy:*,-aztec:avm_simulator:*" + "value": "aztec:*,-json-rpc:json_proxy:*,-aztec:avm_simulator:*,discv5:*,libp2p:*" }, { "name": "ETHEREUM_HOST", @@ -254,11 +254,11 @@ resource "aws_ecs_task_definition" "aztec-node" { }, { "name": "P2P_TCP_LISTEN_PORT", - "value": "${var.NODE_P2P_PORT + count.index}" + "value": "${var.NODE_P2P_TCP_PORT + count.index}" }, { "name": "P2P_UDP_LISTEN_PORT", - "value": "${var.NODE_P2P_PORT + count.index}" + "value": "${var.NODE_P2P_UDP_PORT + count.index}" }, { "name": "P2P_TCP_LISTEN_IP", @@ -270,7 +270,7 @@ resource "aws_ecs_task_definition" "aztec-node" { }, { "name": "P2P_ANNOUNCE_PORT", - "value": "${var.NODE_P2P_PORT + count.index}" + "value": "${var.NODE_P2P_TCP_PORT + count.index}" }, { "name": "BOOTSTRAP_NODES", @@ -377,13 +377,13 @@ resource "aws_ecs_service" "aztec-node" { load_balancer { target_group_arn = aws_lb_target_group.aztec-node-tcp[count.index].arn container_name = "${var.DEPLOY_TAG}-aztec-node-${count.index + 1}" - container_port = var.NODE_P2P_PORT + count.index + container_port = var.NODE_P2P_TCP_PORT + count.index } load_balancer { target_group_arn = aws_lb_target_group.aztec-node-udp[count.index].arn container_name = "${var.DEPLOY_TAG}-aztec-node-${count.index + 1}" - container_port = var.NODE_P2P_PORT + count.index + container_port = var.NODE_P2P_UDP_PORT + count.index } service_registries { @@ -438,8 +438,8 @@ resource "aws_lb_listener_rule" "api" { resource "aws_lb_target_group" "aztec-node-tcp" { count = local.node_count - name = "${var.DEPLOY_TAG}-node-${count.index + 1}-p2p-target" - port = var.NODE_P2P_PORT + count.index + name = "${var.DEPLOY_TAG}-node-${count.index + 1}-p2p-tcp-target" + port = var.NODE_P2P_TCP_PORT + count.index protocol = "TCP" target_type = "ip" vpc_id = data.terraform_remote_state.setup_iac.outputs.vpc_id @@ -449,34 +449,34 @@ resource "aws_lb_target_group" "aztec-node-tcp" { interval = 10 healthy_threshold = 2 unhealthy_threshold = 2 - port = var.NODE_P2P_PORT + count.index + port = var.NODE_P2P_TCP_PORT + count.index } } resource "aws_security_group_rule" "allow-node-tcp-in" { count = local.node_count type = "ingress" - from_port = var.NODE_P2P_PORT + count.index - to_port = var.NODE_P2P_PORT + count.index + from_port = var.NODE_P2P_TCP_PORT + count.index + to_port = var.NODE_P2P_TCP_PORT + count.index protocol = "tcp" cidr_blocks = ["0.0.0.0/0"] security_group_id = data.terraform_remote_state.aztec-network_iac.outputs.p2p_security_group_id } -resource "aws_security_group_rule" "allow-node-tcp-out" { - count = local.node_count - type = "outgress" - from_port = var.NODE_P2P_PORT + count.index - to_port = var.NODE_P2P_PORT + count.index - protocol = "tcp" - cidr_blocks = ["0.0.0.0/0"] - security_group_id = data.terraform_remote_state.aztec-network_iac.outputs.p2p_security_group_id -} +# resource "aws_security_group_rule" "allow-node-tcp-out" { +# count = local.node_count +# type = "egress" +# from_port = var.NODE_P2P_TCP_PORT + count.index +# to_port = var.NODE_P2P_TCP_PORT + count.index +# protocol = "tcp" +# cidr_blocks = ["0.0.0.0/0"] +# security_group_id = data.terraform_remote_state.aztec-network_iac.outputs.p2p_security_group_id +# } resource "aws_lb_listener" "aztec-node-tcp-listener" { count = local.node_count load_balancer_arn = data.terraform_remote_state.aztec-network_iac.outputs.nlb_arn - port = var.NODE_P2P_PORT + count.index + port = var.NODE_P2P_TCP_PORT + count.index protocol = "TCP" tags = { @@ -492,8 +492,8 @@ resource "aws_lb_listener" "aztec-node-tcp-listener" { resource "aws_lb_target_group" "aztec-node-udp" { count = local.node_count - name = "${var.DEPLOY_TAG}-node-${count.index + 1}-p2p-target" - port = var.NODE_P2P_PORT + count.index + name = "${var.DEPLOY_TAG}-node-${count.index + 1}-p2p-udp-target" + port = var.NODE_P2P_UDP_PORT + count.index protocol = "UDP" target_type = "ip" vpc_id = data.terraform_remote_state.setup_iac.outputs.vpc_id @@ -503,25 +503,23 @@ resource "aws_lb_target_group" "aztec-node-udp" { interval = 10 healthy_threshold = 2 unhealthy_threshold = 2 - port = var.NODE_P2P_PORT + count.index + port = var.NODE_P2P_TCP_PORT + count.index } } resource "aws_security_group_rule" "allow-node-udp-in" { - count = local.node_count type = "ingress" - from_port = var.NODE_P2P_PORT + count.index - to_port = var.NODE_P2P_PORT + count.index + from_port = var.NODE_P2P_UDP_PORT + to_port = var.NODE_P2P_UDP_PORT + 100 protocol = "udp" cidr_blocks = ["0.0.0.0/0"] security_group_id = data.terraform_remote_state.aztec-network_iac.outputs.p2p_security_group_id } resource "aws_security_group_rule" "allow-node-udp-out" { - count = local.node_count - type = "outgress" - from_port = var.NODE_P2P_PORT + count.index - to_port = var.NODE_P2P_PORT + count.index + type = "egress" + from_port = var.NODE_P2P_UDP_PORT + to_port = var.NODE_P2P_UDP_PORT + 100 protocol = "udp" cidr_blocks = ["0.0.0.0/0"] security_group_id = data.terraform_remote_state.aztec-network_iac.outputs.p2p_security_group_id @@ -530,7 +528,7 @@ resource "aws_security_group_rule" "allow-node-udp-out" { resource "aws_lb_listener" "aztec-node-udp-listener" { count = local.node_count load_balancer_arn = data.terraform_remote_state.aztec-network_iac.outputs.nlb_arn - port = var.NODE_P2P_PORT + count.index + port = var.NODE_P2P_UDP_PORT + count.index protocol = "UDP" tags = { @@ -539,7 +537,7 @@ resource "aws_lb_listener" "aztec-node-udp-listener" { default_action { type = "forward" - target_group_arn = aws_lb_target_group.aztec-node-tcp[count.index].arn + target_group_arn = aws_lb_target_group.aztec-node-udp[count.index].arn } } diff --git a/yarn-project/aztec/terraform/node/variables.tf b/yarn-project/aztec/terraform/node/variables.tf index 10c5960f68c..36db982a38a 100644 --- a/yarn-project/aztec/terraform/node/variables.tf +++ b/yarn-project/aztec/terraform/node/variables.tf @@ -19,11 +19,16 @@ variable "CHAIN_ID" { default = 31337 } -variable "NODE_P2P_PORT" { +variable "NODE_P2P_TCP_PORT" { type = number default = 40400 } +variable "NODE_P2P_UDP_PORT" { + type = number + default = 40300 +} + variable "NODE_1_PRIVATE_KEY" { type = string default = "" From 1e19a9b0534960e010fcfa9175e9d92cfe857aa1 Mon Sep 17 00:00:00 2001 From: Santiago Palladino Date: Tue, 28 May 2024 16:53:32 +0200 Subject: [PATCH 29/46] feat: Add fees to CLI (#6713) --- yarn-project/aztec.js/src/account/index.ts | 4 +- yarn-project/aztec.js/src/api/abi.ts | 4 +- yarn-project/aztec.js/src/api/fee.ts | 1 + .../src/contract/base_contract_interaction.ts | 8 +- .../aztec.js/src/contract/batch_call.ts | 2 +- .../contract/contract_function_interaction.ts | 2 +- .../aztec.js/src/contract/deploy_method.ts | 29 ++- .../aztec.js/src/fee/no_fee_payment_method.ts | 23 +++ yarn-project/aztec.js/src/index.ts | 9 +- .../aztec.js/src/wallet/signerless_wallet.ts | 12 +- yarn-project/aztec/src/bin/index.ts | 2 +- yarn-project/circuit-types/src/tx/tx_hash.ts | 9 + .../circuit-types/src/tx/tx_receipt.test.ts | 23 +++ .../circuit-types/src/tx/tx_receipt.ts | 3 +- yarn-project/cli/package.json | 1 + yarn-project/cli/src/cmds/call.ts | 7 +- yarn-project/cli/src/cmds/create_account.ts | 29 ++- yarn-project/cli/src/cmds/deploy.ts | 30 ++- yarn-project/cli/src/cmds/get_balance.ts | 32 +++ yarn-project/cli/src/cmds/send.ts | 21 +- yarn-project/cli/src/fees.ts | 187 ++++++++++++++++++ yarn-project/cli/src/index.ts | 106 ++++++---- yarn-project/cli/src/parse_args.ts | 6 +- yarn-project/cli/src/utils.ts | 7 +- yarn-project/cli/tsconfig.json | 3 + .../src/e2e_fees/gas_estimation.test.ts | 50 ++++- .../foundation/src/serialize/serialize.ts | 2 + yarn-project/simulator/src/index.ts | 1 + yarn-project/yarn.lock | 1 + 29 files changed, 520 insertions(+), 94 deletions(-) create mode 100644 yarn-project/aztec.js/src/fee/no_fee_payment_method.ts create mode 100644 yarn-project/circuit-types/src/tx/tx_receipt.test.ts create mode 100644 yarn-project/cli/src/cmds/get_balance.ts create mode 100644 yarn-project/cli/src/fees.ts diff --git a/yarn-project/aztec.js/src/account/index.ts b/yarn-project/aztec.js/src/account/index.ts index 1e17feb770a..0f5dfffef3c 100644 --- a/yarn-project/aztec.js/src/account/index.ts +++ b/yarn-project/aztec.js/src/account/index.ts @@ -8,8 +8,8 @@ */ import { type Fr } from '@aztec/circuits.js'; -export { AccountContract } from './contract.js'; -export { AccountInterface, AuthWitnessProvider } from './interface.js'; +export { type AccountContract } from './contract.js'; +export { type AccountInterface, type AuthWitnessProvider } from './interface.js'; export * from './wallet.js'; /** A contract deployment salt. */ diff --git a/yarn-project/aztec.js/src/api/abi.ts b/yarn-project/aztec.js/src/api/abi.ts index 03a5b41d4f2..69f8b095aee 100644 --- a/yarn-project/aztec.js/src/api/abi.ts +++ b/yarn-project/aztec.js/src/api/abi.ts @@ -1,3 +1,3 @@ -export { ContractArtifact, FunctionArtifact, FunctionSelector } from '@aztec/foundation/abi'; +export { type ContractArtifact, type FunctionArtifact, FunctionSelector } from '@aztec/foundation/abi'; export { loadContractArtifact, contractArtifactToBuffer, contractArtifactFromBuffer } from '@aztec/types/abi'; -export { NoirCompiledContract } from '@aztec/types/noir'; +export { type NoirCompiledContract } from '@aztec/types/noir'; diff --git a/yarn-project/aztec.js/src/api/fee.ts b/yarn-project/aztec.js/src/api/fee.ts index c25727eb4d5..13dfffad38d 100644 --- a/yarn-project/aztec.js/src/api/fee.ts +++ b/yarn-project/aztec.js/src/api/fee.ts @@ -3,3 +3,4 @@ export { NativeFeePaymentMethod } from '../fee/native_fee_payment_method.js'; export { PrivateFeePaymentMethod } from '../fee/private_fee_payment_method.js'; export { PublicFeePaymentMethod } from '../fee/public_fee_payment_method.js'; export { NativeFeePaymentMethodWithClaim } from '../fee/native_fee_payment_method_with_claim.js'; +export { NoFeePaymentMethod } from '../fee/no_fee_payment_method.js'; diff --git a/yarn-project/aztec.js/src/contract/base_contract_interaction.ts b/yarn-project/aztec.js/src/contract/base_contract_interaction.ts index fc1dfdb44e8..1cdbaa00b4d 100644 --- a/yarn-project/aztec.js/src/contract/base_contract_interaction.ts +++ b/yarn-project/aztec.js/src/contract/base_contract_interaction.ts @@ -1,5 +1,6 @@ import { type Tx, type TxExecutionRequest } from '@aztec/circuit-types'; import { GasSettings } from '@aztec/circuits.js'; +import { createDebugLogger } from '@aztec/foundation/log'; import { type Wallet } from '../account/wallet.js'; import { type ExecutionRequestInit, type FeeOptions } from '../entrypoint/entrypoint.js'; @@ -27,6 +28,8 @@ export abstract class BaseContractInteraction { protected tx?: Tx; protected txRequest?: TxExecutionRequest; + protected log = createDebugLogger('aztec:js:contract_interaction'); + constructor(protected wallet: Wallet) {} /** @@ -95,7 +98,7 @@ export abstract class BaseContractInteraction { * @param request - Request to execute for this interaction. * @returns Fee options for the actual transaction. */ - protected async getFeeOptions(request: ExecutionRequestInit) { + protected async getFeeOptionsFromEstimatedGas(request: ExecutionRequestInit) { const fee = request.fee; if (fee) { const txRequest = await this.wallet.createTxExecutionRequest(request); @@ -104,6 +107,9 @@ export abstract class BaseContractInteraction { simulationResult, fee.gasSettings.teardownGasLimits, ); + this.log.debug( + `Estimated gas limits for tx: DA=${gasLimits.daGas} L2=${gasLimits.l2Gas} teardownDA=${teardownGasLimits.daGas} teardownL2=${teardownGasLimits.l2Gas}`, + ); const gasSettings = GasSettings.default({ ...fee.gasSettings, gasLimits, teardownGasLimits }); return { ...fee, gasSettings }; } diff --git a/yarn-project/aztec.js/src/contract/batch_call.ts b/yarn-project/aztec.js/src/contract/batch_call.ts index d2f1f6c9cd1..3a02a713901 100644 --- a/yarn-project/aztec.js/src/contract/batch_call.ts +++ b/yarn-project/aztec.js/src/contract/batch_call.ts @@ -20,7 +20,7 @@ export class BatchCall extends BaseContractInteraction { public async create(opts?: SendMethodOptions): Promise { if (!this.txRequest) { const calls = this.calls; - const fee = opts?.estimateGas ? await this.getFeeOptions({ calls, fee: opts?.fee }) : opts?.fee; + const fee = opts?.estimateGas ? await this.getFeeOptionsFromEstimatedGas({ calls, fee: opts?.fee }) : opts?.fee; this.txRequest = await this.wallet.createTxExecutionRequest({ calls, fee }); } return this.txRequest; diff --git a/yarn-project/aztec.js/src/contract/contract_function_interaction.ts b/yarn-project/aztec.js/src/contract/contract_function_interaction.ts index d74a282efff..7d31f4f74f4 100644 --- a/yarn-project/aztec.js/src/contract/contract_function_interaction.ts +++ b/yarn-project/aztec.js/src/contract/contract_function_interaction.ts @@ -54,7 +54,7 @@ export class ContractFunctionInteraction extends BaseContractInteraction { } if (!this.txRequest) { const calls = [this.request()]; - const fee = opts?.estimateGas ? await this.getFeeOptions({ calls, fee: opts?.fee }) : opts?.fee; + const fee = opts?.estimateGas ? await this.getFeeOptionsFromEstimatedGas({ calls, fee: opts?.fee }) : opts?.fee; this.txRequest = await this.wallet.createTxExecutionRequest({ calls, fee }); } return this.txRequest; diff --git a/yarn-project/aztec.js/src/contract/deploy_method.ts b/yarn-project/aztec.js/src/contract/deploy_method.ts index 5521901f943..c572d526f44 100644 --- a/yarn-project/aztec.js/src/contract/deploy_method.ts +++ b/yarn-project/aztec.js/src/contract/deploy_method.ts @@ -7,7 +7,6 @@ import { } from '@aztec/circuits.js'; import { type ContractArtifact, type FunctionArtifact, getInitializer } from '@aztec/foundation/abi'; import { type Fr } from '@aztec/foundation/fields'; -import { createDebugLogger } from '@aztec/foundation/log'; import { type ContractInstanceWithAddress } from '@aztec/types/contracts'; import { type Wallet } from '../account/index.js'; @@ -53,8 +52,6 @@ export class DeployMethod extends Bas /** Cached call to request() */ private functionCalls?: ExecutionRequestInit; - private log = createDebugLogger('aztec:js:deploy_method'); - constructor( private publicKeysHash: Fr, wallet: Wallet, @@ -79,8 +76,6 @@ export class DeployMethod extends Bas public async create(options: DeployOptions = {}): Promise { if (!this.txRequest) { this.txRequest = await this.wallet.createTxExecutionRequest(await this.request(options)); - // TODO: Should we add the contracts to the DB here, or once the tx has been sent or mined? - await this.wallet.registerContract({ artifact: this.artifact, instance: this.instance! }); } return this.txRequest; } @@ -98,6 +93,14 @@ export class DeployMethod extends Bas */ public async request(options: DeployOptions = {}): Promise { if (!this.functionCalls) { + // TODO: Should we add the contracts to the DB here, or once the tx has been sent or mined? + // Note that we need to run this registerContract here so it's available when computeFeeOptionsFromEstimatedGas + // runs, since it needs the contract to have been registered in order to estimate gas for its initialization, + // in case the initializer is public. This hints at the need of having "transient" contracts scoped to a + // simulation, so we can run the simulation with a set of contracts, but only "commit" them to the wallet + // once this tx has gone through. + await this.wallet.registerContract({ artifact: this.artifact, instance: this.getInstance(options) }); + const deployment = await this.getDeploymentFunctionCalls(options); const bootstrap = await this.getInitializeFunctionCalls(options); @@ -113,7 +116,13 @@ export class DeployMethod extends Bas }; if (options.estimateGas) { - request.fee = await this.getFeeOptions(request); + // Why do we call this seemingly idempotent getter method here, without using its return value? + // This call pushes a capsule required for contract class registration under the hood. And since + // capsules are a stack, when we run the simulation for estimating gas, we consume the capsule + // that was meant for the actual call. So we need to push it again here. Hopefully this design + // will go away soon. + await this.getDeploymentFunctionCalls(options); + request.fee = await this.getFeeOptionsFromEstimatedGas(request); } this.functionCalls = request; @@ -233,6 +242,14 @@ export class DeployMethod extends Bas return super.prove(options); } + /** + * Estimates gas cost for this deployment operation. + * @param options - Options. + */ + public override estimateGas(options?: Omit) { + return super.estimateGas(options); + } + /** Return this deployment address. */ public get address() { return this.instance?.address; diff --git a/yarn-project/aztec.js/src/fee/no_fee_payment_method.ts b/yarn-project/aztec.js/src/fee/no_fee_payment_method.ts new file mode 100644 index 00000000000..e9ed5809bc7 --- /dev/null +++ b/yarn-project/aztec.js/src/fee/no_fee_payment_method.ts @@ -0,0 +1,23 @@ +import { type FunctionCall } from '@aztec/circuit-types'; +import { AztecAddress } from '@aztec/circuits.js'; + +import { type FeePaymentMethod } from './fee_payment_method.js'; + +/** + * Does not pay fees. Will work until we enforce fee payment for all txs. + */ +export class NoFeePaymentMethod implements FeePaymentMethod { + constructor() {} + + getAsset() { + return AztecAddress.ZERO; + } + + getFunctionCalls(): Promise { + return Promise.resolve([]); + } + + getFeePayer(): Promise { + return Promise.resolve(AztecAddress.ZERO); + } +} diff --git a/yarn-project/aztec.js/src/index.ts b/yarn-project/aztec.js/src/index.ts index fd58326f366..920e20d51bf 100644 --- a/yarn-project/aztec.js/src/index.ts +++ b/yarn-project/aztec.js/src/index.ts @@ -24,13 +24,14 @@ export { Contract, ContractBase, ContractFunctionInteraction, - ContractMethod, - ContractNotes, - ContractStorageLayout, + type ContractMethod, + type ContractNotes, + type ContractStorageLayout, DeployMethod, DeploySentTx, + type SendMethodOptions, SentTx, - WaitOpts, + type WaitOpts, } from './contract/index.js'; export { ContractDeployer } from './deployment/index.js'; diff --git a/yarn-project/aztec.js/src/wallet/signerless_wallet.ts b/yarn-project/aztec.js/src/wallet/signerless_wallet.ts index f265107e4f1..bba8c3ec66e 100644 --- a/yarn-project/aztec.js/src/wallet/signerless_wallet.ts +++ b/yarn-project/aztec.js/src/wallet/signerless_wallet.ts @@ -24,26 +24,26 @@ export class SignerlessWallet extends BaseWallet { } getChainId(): Fr { - throw new Error('Method not implemented.'); + throw new Error('SignerlessWallet: Method getChainId not implemented.'); } getVersion(): Fr { - throw new Error('Method not implemented.'); + throw new Error('SignerlessWallet: Method getVersion not implemented.'); } getPublicKeysHash(): Fr { - throw new Error('Method not implemented.'); + throw new Error('SignerlessWallet: Method getPublicKeysHash not implemented.'); } getCompleteAddress(): CompleteAddress { - throw new Error('Method not implemented.'); + throw new Error('SignerlessWallet: Method getCompleteAddress not implemented.'); } createAuthWit(_messageHash: Fr): Promise { - throw new Error('Method not implemented.'); + throw new Error('SignerlessWallet: Method createAuthWit not implemented.'); } rotateNullifierKeys(_newNskM: Fq): Promise { - throw new Error('Method not implemented.'); + throw new Error('SignerlessWallet: Method rotateNullifierKeys not implemented.'); } } diff --git a/yarn-project/aztec/src/bin/index.ts b/yarn-project/aztec/src/bin/index.ts index 70a34456c74..4cdc27c1798 100644 --- a/yarn-project/aztec/src/bin/index.ts +++ b/yarn-project/aztec/src/bin/index.ts @@ -63,6 +63,6 @@ async function main() { main().catch(err => { debugLogger.error(`Error in command execution`); - debugLogger.error(err); + debugLogger.error(err + '\n' + err.stack); process.exit(1); }); diff --git a/yarn-project/circuit-types/src/tx/tx_hash.ts b/yarn-project/circuit-types/src/tx/tx_hash.ts index 514097b3d6b..c92340c1928 100644 --- a/yarn-project/circuit-types/src/tx/tx_hash.ts +++ b/yarn-project/circuit-types/src/tx/tx_hash.ts @@ -1,3 +1,4 @@ +import { randomBytes } from '@aztec/foundation/crypto'; import { BufferReader, deserializeBigInt, serializeBigInt } from '@aztec/foundation/serialize'; /** @@ -105,4 +106,12 @@ export class TxHash { public static fromString(str: string): TxHash { return new TxHash(Buffer.from(str, 'hex')); } + + /** + * Generates a random TxHash. + * @returns A new TxHash object. + */ + public static random(): TxHash { + return new TxHash(Buffer.from(randomBytes(TxHash.SIZE))); + } } diff --git a/yarn-project/circuit-types/src/tx/tx_receipt.test.ts b/yarn-project/circuit-types/src/tx/tx_receipt.test.ts new file mode 100644 index 00000000000..2eac60ece2e --- /dev/null +++ b/yarn-project/circuit-types/src/tx/tx_receipt.test.ts @@ -0,0 +1,23 @@ +import { TxHash } from './tx_hash.js'; +import { TxReceipt, TxStatus } from './tx_receipt.js'; + +describe('TxReceipt', () => { + it('serializes and deserializes from json', () => { + const receipt = new TxReceipt( + TxHash.random(), + TxStatus.SUCCESS, + 'error', + BigInt(1), + Buffer.from('blockHash'), + undefined, + ); + + expect(TxReceipt.fromJSON(receipt.toJSON())).toEqual(receipt); + }); + + it('serializes and deserializes from json with undefined fields', () => { + const receipt = new TxReceipt(TxHash.random(), TxStatus.DROPPED, 'error', undefined, undefined, undefined); + + expect(TxReceipt.fromJSON(receipt.toJSON())).toEqual(receipt); + }); +}); diff --git a/yarn-project/circuit-types/src/tx/tx_receipt.ts b/yarn-project/circuit-types/src/tx/tx_receipt.ts index 39c9f29f540..fc32f1b077f 100644 --- a/yarn-project/circuit-types/src/tx/tx_receipt.ts +++ b/yarn-project/circuit-types/src/tx/tx_receipt.ts @@ -66,6 +66,7 @@ export class TxReceipt { error: this.error, blockHash: this.blockHash?.toString('hex'), blockNumber: this.blockNumber, + transactionFee: this.transactionFee?.toString(), }; } @@ -78,7 +79,7 @@ export class TxReceipt { const txHash = TxHash.fromString(obj.txHash); const status = obj.status as TxStatus; const error = obj.error; - const transactionFee = obj.transactionFee; + const transactionFee = obj.transactionFee ? BigInt(obj.transactionFee) : undefined; const blockHash = obj.blockHash ? Buffer.from(obj.blockHash, 'hex') : undefined; const blockNumber = obj.blockNumber ? Number(obj.blockNumber) : undefined; return new TxReceipt(txHash, status, error, transactionFee, blockHash, blockNumber); diff --git a/yarn-project/cli/package.json b/yarn-project/cli/package.json index be0981f8b42..f1b0a6a81b0 100644 --- a/yarn-project/cli/package.json +++ b/yarn-project/cli/package.json @@ -60,6 +60,7 @@ "@aztec/l1-artifacts": "workspace:^", "@aztec/noir-contracts.js": "workspace:^", "@aztec/protocol-contracts": "workspace:^", + "@aztec/simulator": "workspace:^", "@aztec/types": "workspace:^", "@iarna/toml": "^2.2.5", "@libp2p/peer-id-factory": "^3.0.4", diff --git a/yarn-project/cli/src/cmds/call.ts b/yarn-project/cli/src/cmds/call.ts index 503a9a66d8c..aa71fe84a69 100644 --- a/yarn-project/cli/src/cmds/call.ts +++ b/yarn-project/cli/src/cmds/call.ts @@ -1,5 +1,5 @@ -import { AztecAddress } from '@aztec/aztec.js'; -import { DebugLogger, LogFn } from '@aztec/foundation/log'; +import { type AztecAddress, ContractFunctionInteraction, SignerlessWallet, Wallet } from '@aztec/aztec.js'; +import { type DebugLogger, type LogFn } from '@aztec/foundation/log'; import { format } from 'util'; @@ -26,7 +26,8 @@ export async function call( } const client = await createCompatibleClient(rpcUrl, debugLogger); + const call = new ContractFunctionInteraction(new SignerlessWallet(client), contractAddress, fnArtifact, functionArgs); const from = await getTxSender(client, fromAddress); - const result = await client.simulateUnconstrained(functionName, functionArgs, contractAddress, from); + const result = await call.simulate({ from }); log(format('\nView result: ', result, '\n')); } diff --git a/yarn-project/cli/src/cmds/create_account.ts b/yarn-project/cli/src/cmds/create_account.ts index 033de9ca036..3dbeb9d45b2 100644 --- a/yarn-project/cli/src/cmds/create_account.ts +++ b/yarn-project/cli/src/cmds/create_account.ts @@ -4,11 +4,14 @@ import { Fr } from '@aztec/foundation/fields'; import { type DebugLogger, type LogFn } from '@aztec/foundation/log'; import { createCompatibleClient } from '../client.js'; +import { type IFeeOpts, printGasEstimates } from '../fees.js'; export async function createAccount( rpcUrl: string, privateKey: Fr | undefined, + registerOnly: boolean, wait: boolean, + feeOpts: IFeeOpts, debugLogger: DebugLogger, log: LogFn, ) { @@ -19,14 +22,24 @@ export async function createAccount( const account = getSchnorrAccount(client, privateKey, deriveSigningKey(privateKey), Fr.ZERO); const { address, publicKeys, partialAddress } = account.getCompleteAddress(); await account.register(); - const tx = account.deploy(); - const txHash = tx.getTxHash(); - debugLogger.debug(`Account contract tx sent with hash ${txHash}`); - if (wait) { - log(`\nWaiting for account contract deployment...`); - await tx.wait(); - } else { - log(`\nAccount deployment transaction hash: ${txHash}\n`); + + if (!registerOnly) { + const wallet = await account.getWallet(); + const sendOpts = feeOpts.toSendOpts(wallet); + if (feeOpts.estimateOnly) { + const gas = await (await account.getDeployMethod()).estimateGas({ ...sendOpts }); + printGasEstimates(feeOpts, gas, log); + } else { + const tx = account.deploy({ ...sendOpts }); + const txHash = tx.getTxHash(); + debugLogger.debug(`Account contract tx sent with hash ${txHash}`); + if (wait) { + log(`\nWaiting for account contract deployment...`); + await tx.wait(); + } else { + log(`\nAccount deployment transaction hash: ${txHash}\n`); + } + } } log(`\nNew account:\n`); diff --git a/yarn-project/cli/src/cmds/deploy.ts b/yarn-project/cli/src/cmds/deploy.ts index 8ab3a52c775..82ee8312d16 100644 --- a/yarn-project/cli/src/cmds/deploy.ts +++ b/yarn-project/cli/src/cmds/deploy.ts @@ -1,11 +1,12 @@ import { getSchnorrAccount } from '@aztec/accounts/schnorr'; import { ContractDeployer, Fr } from '@aztec/aztec.js'; -import { type PublicKeys, deriveSigningKey, computeInitializationHash, getContractInstanceFromDeployParams } from '@aztec/circuits.js'; +import { type PublicKeys, deriveSigningKey } from '@aztec/circuits.js'; import { getInitializer } from '@aztec/foundation/abi'; import { type DebugLogger, type LogFn } from '@aztec/foundation/log'; import { createCompatibleClient } from '../client.js'; import { encodeArgs } from '../encoding.js'; +import { type IFeeOpts, printGasEstimates } from '../fees.js'; import { GITHUB_TAG_PREFIX } from '../github.js'; import { getContractArtifact } from '../utils.js'; @@ -22,6 +23,7 @@ export async function deploy( skipClassRegistration: boolean, skipInitialization: boolean | undefined, wait: boolean, + feeOpts: IFeeOpts, debugLogger: DebugLogger, log: LogFn, logJson: (output: any) => void, @@ -50,18 +52,25 @@ export async function deploy( debugLogger.debug(`Input arguments: ${rawArgs.map((x: any) => `"${x}"`).join(', ')}`); args = encodeArgs(rawArgs, constructorArtifact!.parameters); debugLogger.debug(`Encoded arguments: ${args.join(', ')}`); - log(`\nInitialisation hash: ${computeInitializationHash(constructorArtifact, rawArgs)}`); } const deploy = deployer.deploy(...args); - - await deploy.create({ contractAddressSalt: salt, skipClassRegistration, skipInitialization, skipPublicDeployment }); - const tx = deploy.send({ + const deployOpts = { + ...feeOpts.toSendOpts(wallet), contractAddressSalt: salt, skipClassRegistration, skipInitialization, skipPublicDeployment, - }); + }; + + if (feeOpts.estimateOnly) { + const gas = await deploy.estimateGas(deployOpts); + printGasEstimates(feeOpts, gas, log); + return; + } + + await deploy.create(deployOpts); + const tx = deploy.send(deployOpts); const txHash = await tx.getTxHash(); debugLogger.debug(`Deploy tx sent with hash ${txHash}`); @@ -74,12 +83,15 @@ export async function deploy( partialAddress: partialAddress.toString(), initializationHash: instance.initializationHash.toString(), salt: salt.toString(), + transactionFee: deployed.transactionFee, }); } else { log(`Contract deployed at ${address.toString()}`); log(`Contract partial address ${partialAddress.toString()}`); log(`Contract init hash ${instance.initializationHash.toString()}`); + log(`Deployment tx hash: ${txHash.toString()}`); log(`Deployment salt: ${salt.toString()}`); + log(`Deployment fee: ${deployed.transactionFee}`); } } else { const { address, partialAddress } = deploy; @@ -93,10 +105,10 @@ export async function deploy( salt: salt.toString(), }); } else { - log(`Contract Address: ${address?.toString() ?? 'N/A'}`); - log(`Contract Partial Address: ${partialAddress?.toString() ?? 'N/A'}`); - log(`Deployment transaction hash: ${txHash}`); + log(`Contract deployed at ${address?.toString()}`); + log(`Contract partial address ${partialAddress?.toString()}`); log(`Contract init hash ${instance.initializationHash.toString()}`); + log(`Deployment tx hash: ${txHash.toString()}`); log(`Deployment salt: ${salt.toString()}`); } } diff --git a/yarn-project/cli/src/cmds/get_balance.ts b/yarn-project/cli/src/cmds/get_balance.ts new file mode 100644 index 00000000000..4fa7d7f5cc3 --- /dev/null +++ b/yarn-project/cli/src/cmds/get_balance.ts @@ -0,0 +1,32 @@ +import { AztecAddress } from '@aztec/aztec.js'; +import { type DebugLogger, type LogFn } from '@aztec/foundation/log'; +import { TokenContractArtifact } from '@aztec/noir-contracts.js'; +import { GasTokenAddress, GasTokenArtifact } from '@aztec/protocol-contracts/gas-token'; +import { computeSlotForMapping } from '@aztec/simulator'; + +import { createCompatibleClient } from '../client.js'; + +export async function getBalance( + address: AztecAddress, + maybeTokenAddress: string | undefined, + rpcUrl: string, + debugLogger: DebugLogger, + log: LogFn, +) { + const client = await createCompatibleClient(rpcUrl, debugLogger); + const tokenAddress = maybeTokenAddress ? AztecAddress.fromString(maybeTokenAddress) : GasTokenAddress; + + // Get private balance + if (!tokenAddress.equals(GasTokenAddress)) { + const result = await client.simulateUnconstrained(`balance_of_private`, [address], tokenAddress); + log(`\nPrivate balance: ${result.toString()}`); + } + + // TODO(#6707): For public balance, we cannot directly simulate a public function call, so we read directly from storage as a workaround + const balancesStorageSlot = tokenAddress.equals(GasTokenAddress) + ? GasTokenArtifact.storageLayout.balances.slot + : TokenContractArtifact.storageLayout.public_balances.slot; + const slot = computeSlotForMapping(balancesStorageSlot, address); + const result = await client.getPublicStorageAt(tokenAddress, slot); + log(`Public balance: ${result.toBigInt()}`); +} diff --git a/yarn-project/cli/src/cmds/send.ts b/yarn-project/cli/src/cmds/send.ts index 1e6edc436f6..87e41d64698 100644 --- a/yarn-project/cli/src/cmds/send.ts +++ b/yarn-project/cli/src/cmds/send.ts @@ -1,9 +1,10 @@ import { getSchnorrAccount } from '@aztec/accounts/schnorr'; -import { type AztecAddress, Contract, type Fq, Fr } from '@aztec/aztec.js'; +import { type AztecAddress, Contract, Fr } from '@aztec/aztec.js'; import { deriveSigningKey } from '@aztec/circuits.js'; import { type DebugLogger, type LogFn } from '@aztec/foundation/log'; import { createCompatibleClient } from '../client.js'; +import { type IFeeOpts, printGasEstimates } from '../fees.js'; import { prepTx } from '../utils.js'; export async function send( @@ -14,6 +15,7 @@ export async function send( encryptionPrivateKey: Fr, rpcUrl: string, wait: boolean, + feeOpts: IFeeOpts, debugLogger: DebugLogger, log: LogFn, ) { @@ -27,7 +29,15 @@ export async function send( Fr.ZERO, ).getWallet(); const contract = await Contract.at(contractAddress, contractArtifact, wallet); - const tx = contract.methods[functionName](...functionArgs).send(); + const call = contract.methods[functionName](...functionArgs); + + if (feeOpts.estimateOnly) { + const gas = await call.estimateGas({ ...feeOpts.toSendOpts(wallet) }); + printGasEstimates(feeOpts, gas, log); + return; + } + + const tx = call.send({ ...feeOpts.toSendOpts(wallet) }); log(`\nTransaction hash: ${(await tx.getTxHash()).toString()}`); if (wait) { await tx.wait(); @@ -35,9 +45,10 @@ export async function send( log('Transaction has been mined'); const receipt = await tx.getReceipt(); - log(`Status: ${receipt.status}`); - log(`Block number: ${receipt.blockNumber}`); - log(`Block hash: ${receipt.blockHash?.toString('hex')}`); + log(` Tx fee: ${receipt.transactionFee}`); + log(` Status: ${receipt.status}`); + log(` Block number: ${receipt.blockNumber}`); + log(` Block hash: ${receipt.blockHash?.toString('hex')}`); } else { log('Transaction pending. Check status with get-tx-receipt'); } diff --git a/yarn-project/cli/src/fees.ts b/yarn-project/cli/src/fees.ts new file mode 100644 index 00000000000..c6800c66ead --- /dev/null +++ b/yarn-project/cli/src/fees.ts @@ -0,0 +1,187 @@ +import { + type AccountWallet, + type FeePaymentMethod, + NativeFeePaymentMethod, + NativeFeePaymentMethodWithClaim, + NoFeePaymentMethod, + PrivateFeePaymentMethod, + PublicFeePaymentMethod, + type SendMethodOptions, +} from '@aztec/aztec.js'; +import { AztecAddress, Fr, Gas, GasFees, GasSettings } from '@aztec/circuits.js'; +import { type LogFn } from '@aztec/foundation/log'; + +import { Option } from 'commander'; + +import { parseBigint } from './parse_args.js'; + +export type CliFeeArgs = { + estimateGasOnly: boolean; + inclusionFee?: bigint; + gasLimits?: string; + payment?: string; + estimateGas?: boolean; +}; + +export interface IFeeOpts { + estimateOnly: boolean; + gasSettings: GasSettings; + toSendOpts(sender: AccountWallet): SendMethodOptions; +} + +export function printGasEstimates( + feeOpts: IFeeOpts, + gasEstimates: Pick, + log: LogFn, +) { + const inclusionFee = feeOpts.gasSettings.inclusionFee; + log(`Maximum total tx fee: ${getEstimatedCost(gasEstimates, inclusionFee, GasSettings.default().maxFeesPerGas)}`); + log(`Estimated total tx fee: ${getEstimatedCost(gasEstimates, inclusionFee, GasFees.default())}`); + log(`Estimated gas usage: ${formatGasEstimate(gasEstimates)}`); +} + +function formatGasEstimate(estimate: Pick) { + return `da=${estimate.gasLimits.daGas},l2=${estimate.gasLimits.l2Gas},teardownDA=${estimate.teardownGasLimits.daGas},teardownL2=${estimate.teardownGasLimits.l2Gas}`; +} + +function getEstimatedCost( + estimate: Pick, + inclusionFee: Fr, + fees: GasFees, +) { + return GasSettings.from({ ...GasSettings.default(), ...estimate, inclusionFee, maxFeesPerGas: fees }) + .getFeeLimit() + .toBigInt(); +} + +export class FeeOpts implements IFeeOpts { + constructor( + public estimateOnly: boolean, + public gasSettings: GasSettings, + private paymentMethodFactory: (sender: AccountWallet) => FeePaymentMethod, + private estimateGas: boolean, + ) {} + + toSendOpts(sender: AccountWallet): SendMethodOptions { + return { + estimateGas: this.estimateGas, + fee: { gasSettings: this.gasSettings ?? GasSettings.default(), paymentMethod: this.paymentMethodFactory(sender) }, + }; + } + + static getOptions() { + return [ + new Option('--inclusion-fee ', 'Inclusion fee to pay for the tx.').argParser(parseBigint), + new Option('--gas-limits ', 'Gas limits for the tx.'), + new Option( + '--payment ', + 'Fee payment method and arguments. Valid methods are: none, native, fpc-public, fpc-private.', + ), + new Option('--no-estimate-gas', 'Whether to automatically estimate gas limits for the tx.'), + new Option('--estimate-gas-only', 'Only report gas estimation for the tx, do not send it.'), + ]; + } + + static fromCli(args: CliFeeArgs, log: LogFn) { + const estimateOnly = args.estimateGasOnly; + if (!args.inclusionFee && !args.gasLimits && !args.payment) { + return new NoFeeOpts(estimateOnly); + } + const gasSettings = GasSettings.from({ + ...GasSettings.default(), + ...(args.gasLimits ? parseGasLimits(args.gasLimits) : {}), + ...(args.inclusionFee ? { inclusionFee: new Fr(args.inclusionFee) } : {}), + }); + return new FeeOpts( + estimateOnly, + gasSettings, + args.payment ? parsePaymentMethod(args.payment, log) : () => new NoFeePaymentMethod(), + !!args.estimateGas, + ); + } +} + +class NoFeeOpts implements IFeeOpts { + constructor(public estimateOnly: boolean) {} + + get gasSettings(): GasSettings { + return GasSettings.default(); + } + + toSendOpts(): SendMethodOptions { + return {}; + } +} + +function parsePaymentMethod(payment: string, log: LogFn): (sender: AccountWallet) => FeePaymentMethod { + const parsed = payment.split(',').reduce((acc, item) => { + const [dimension, value] = item.split('='); + acc[dimension] = value; + return acc; + }, {} as Record); + + const getFpcOpts = (parsed: Record) => { + if (!parsed.fpc) { + throw new Error('Missing "fpc" in payment option'); + } + if (!parsed.asset) { + throw new Error('Missing "asset" in payment option'); + } + + return [AztecAddress.fromString(parsed.asset), AztecAddress.fromString(parsed.fpc)]; + }; + + return (sender: AccountWallet) => { + switch (parsed.method) { + case 'none': + log('Using no fee payment'); + return new NoFeePaymentMethod(); + case 'native': + if (parsed.claimSecret && parsed.claimAmount) { + log(`Using native fee payment method with claim for ${parsed.claimAmount} tokens`); + return new NativeFeePaymentMethodWithClaim( + sender.getAddress(), + BigInt(parsed.claimAmount), + Fr.fromString(parsed.claimSecret), + ); + } else { + log(`Using native fee payment`); + return new NativeFeePaymentMethod(sender.getAddress()); + } + case 'fpc-public': { + const [asset, fpc] = getFpcOpts(parsed); + log(`Using public fee payment with asset ${asset} via paymaster ${fpc}`); + return new PublicFeePaymentMethod(asset, fpc, sender); + } + case 'fpc-private': { + const [asset, fpc] = getFpcOpts(parsed); + log(`Using private fee payment with asset ${asset} via paymaster ${fpc}`); + return new PrivateFeePaymentMethod(asset, fpc, sender, Fr.fromString(parsed.rebateSecret)); + } + case undefined: + throw new Error('Missing "method" in payment option'); + default: + throw new Error(`Invalid fee payment method: ${payment}`); + } + }; +} + +function parseGasLimits(gasLimits: string): { gasLimits: Gas; teardownGasLimits: Gas } { + const parsed = gasLimits.split(',').reduce((acc, limit) => { + const [dimension, value] = limit.split('='); + acc[dimension] = parseInt(value, 10); + return acc; + }, {} as Record); + + const expected = ['da', 'l2', 'teardownDA', 'teardownL2']; + for (const dimension of expected) { + if (!(dimension in parsed)) { + throw new Error(`Missing gas limit for ${dimension}`); + } + } + + return { + gasLimits: new Gas(parsed.da, parsed.l2), + teardownGasLimits: new Gas(parsed.teardownDA, parsed.teardownL2), + }; +} diff --git a/yarn-project/cli/src/index.ts b/yarn-project/cli/src/index.ts index f5027b4d560..62b1b2c63c0 100644 --- a/yarn-project/cli/src/index.ts +++ b/yarn-project/cli/src/index.ts @@ -1,12 +1,13 @@ -import { Fr, PublicKeys, deriveSigningKey } from '@aztec/circuits.js'; +import { Fr, PublicKeys } from '@aztec/circuits.js'; import { type DebugLogger, type LogFn } from '@aztec/foundation/log'; import { fileURLToPath } from '@aztec/foundation/url'; -import { Command, Option } from 'commander'; +import { Command as CommanderCommand, Option } from 'commander'; import { lookup } from 'dns/promises'; import { readFileSync } from 'fs'; import { dirname, resolve } from 'path'; +import { FeeOpts } from './fees.js'; import { parseAztecAddress, parseEthereumAddress, @@ -35,6 +36,17 @@ const getLocalhost = () => const LOCALHOST = await getLocalhost(); const { ETHEREUM_HOST = `http://${LOCALHOST}:8545`, PRIVATE_KEY, API_KEY, CLI_VERSION } = process.env; +class Command extends CommanderCommand { + addOptions(options: Option[]) { + options.forEach(option => this.addOption(option)); + return this; + } + + override createCommand(name?: string): Command { + return new Command(name); + } +} + /** * Returns commander program that defines the CLI. * @param log - Console logger. @@ -162,12 +174,17 @@ export function getProgram(log: LogFn, debugLogger: DebugLogger): Command { .summary('Creates an aztec account that can be used for sending transactions.') .addOption(createPrivateKeyOption('Private key for account. Uses random by default.', false)) .addOption(pxeOption) + .option( + '--register-only', + 'Just register the account on the PXE. Do not deploy or initialize the account contract.', + ) // `options.wait` is default true. Passing `--no-wait` will set it to false. // https://github.com/tj/commander.js#other-option-types-negatable-boolean-and-booleanvalue .option('--no-wait', 'Skip waiting for the contract to be deployed. Print the hash of deployment transaction') - .action(async ({ rpcUrl, privateKey, wait }) => { + .action(async args => { const { createAccount } = await import('./cmds/create_account.js'); - await createAccount(rpcUrl, privateKey, wait, debugLogger, log); + const { rpcUrl, privateKey, wait, registerOnly } = args; + await createAccount(rpcUrl, privateKey, registerOnly, wait, FeeOpts.fromCli(args, log), debugLogger, log); }); program @@ -227,42 +244,40 @@ export function getProgram(log: LogFn, debugLogger: DebugLogger): Command { .option('--no-class-registration', 'Skip registering the contract class') .option('--public-deployment', 'Deploy the public bytecode of contract') .option('--no-public-deployment', "Skip deploying the contract's public bytecode") - .action( - async ( + .addOptions(FeeOpts.getOptions()) + .action(async (artifactPath, opts) => { + const { deploy } = await import('./cmds/deploy.js'); + const { + json, + rpcUrl, + publicKey, + args: rawArgs, + salt, + wait, + privateKey, + classRegistration, + initialize, + publicDeployment, + } = opts; + await deploy( artifactPath, - { - json, - rpcUrl, - publicKey, - args: rawArgs, - salt, - wait, - privateKey, - classRegistration, - initialize, - publicDeployment, - }, - ) => { - const { deploy } = await import('./cmds/deploy.js'); - await deploy( - artifactPath, - json, - rpcUrl, - publicKey ? PublicKeys.fromString(publicKey) : undefined, - rawArgs, - salt, - privateKey, - typeof initialize === 'string' ? initialize : undefined, - !publicDeployment, - !classRegistration, - typeof initialize === 'string' ? false : initialize, - wait, - debugLogger, - log, - logJson, - ); - }, - ); + json, + rpcUrl, + publicKey ? PublicKeys.fromString(publicKey) : undefined, + rawArgs, + salt, + privateKey, + typeof initialize === 'string' ? initialize : undefined, + !publicDeployment, + !classRegistration, + typeof initialize === 'string' ? false : initialize, + wait, + FeeOpts.fromCli(opts, log), + debugLogger, + log, + logJson, + ); + }); program .command('check-deploy') @@ -405,6 +420,17 @@ export function getProgram(log: LogFn, debugLogger: DebugLogger): Command { await getRecipient(address, options.rpcUrl, debugLogger, log); }); + program + .command('get-balance') + .description('Gets the token balance for an account. Does NOT format according to decimals.') + .argument('
', 'Aztec address to query balance for.', parseAztecAddress) + .option('-t, --token-address
', 'Token address to query balance for (defaults to gas token).') + .addOption(pxeOption) + .action(async (address, options) => { + const { getBalance } = await import('./cmds/get_balance.js'); + await getBalance(address, options.tokenAddress, options.rpcUrl, debugLogger, log); + }); + program .command('send') .description('Calls a function on an Aztec contract.') @@ -418,6 +444,7 @@ export function getProgram(log: LogFn, debugLogger: DebugLogger): Command { .addOption(createPrivateKeyOption("The sender's private key.", true)) .addOption(pxeOption) .option('--no-wait', 'Print transaction hash without waiting for it to be mined') + .addOptions(FeeOpts.getOptions()) .action(async (functionName, options) => { const { send } = await import('./cmds/send.js'); await send( @@ -428,6 +455,7 @@ export function getProgram(log: LogFn, debugLogger: DebugLogger): Command { options.privateKey, options.rpcUrl, !options.noWait, + FeeOpts.fromCli(options, log), debugLogger, log, ); diff --git a/yarn-project/cli/src/parse_args.ts b/yarn-project/cli/src/parse_args.ts index 9161c72ef5e..d4b49c8cc16 100644 --- a/yarn-project/cli/src/parse_args.ts +++ b/yarn-project/cli/src/parse_args.ts @@ -1,7 +1,7 @@ import { FunctionSelector } from '@aztec/aztec.js/abi'; import { AztecAddress } from '@aztec/aztec.js/aztec_address'; import { EthAddress } from '@aztec/aztec.js/eth_address'; -import { Fr, Point } from '@aztec/aztec.js/fields'; +import { Fr } from '@aztec/aztec.js/fields'; import { LogId } from '@aztec/aztec.js/log_id'; import { TxHash } from '@aztec/aztec.js/tx_hash'; import { PublicKeys } from '@aztec/circuits.js'; @@ -20,6 +20,10 @@ const stripLeadingHex = (hex: string) => { return hex; }; +export function parseBigint(bigint: string): bigint | undefined { + return bigint ? BigInt(bigint) : undefined; +} + /** * Parses a hex encoded string to an Fr integer * @param str - Hex encoded string diff --git a/yarn-project/cli/src/utils.ts b/yarn-project/cli/src/utils.ts index cdc470b1e96..b9779864099 100644 --- a/yarn-project/cli/src/utils.ts +++ b/yarn-project/cli/src/utils.ts @@ -121,8 +121,11 @@ export async function getExampleContractArtifacts(): Promise { export async function getContractArtifact(fileDir: string, log: LogFn) { // first check if it's a noir-contracts example const artifacts = await getExampleContractArtifacts(); - if (artifacts[fileDir]) { - return artifacts[fileDir] as ContractArtifact; + for (const key of [fileDir, fileDir + 'Artifact', fileDir + 'ContractArtifact']) { + if (artifacts[key]) { + return artifacts[key] as ContractArtifact; + + } } let contents: string; diff --git a/yarn-project/cli/tsconfig.json b/yarn-project/cli/tsconfig.json index c691892f8f0..80b20c28830 100644 --- a/yarn-project/cli/tsconfig.json +++ b/yarn-project/cli/tsconfig.json @@ -36,6 +36,9 @@ { "path": "../protocol-contracts" }, + { + "path": "../simulator" + }, { "path": "../types" } diff --git a/yarn-project/end-to-end/src/e2e_fees/gas_estimation.test.ts b/yarn-project/end-to-end/src/e2e_fees/gas_estimation.test.ts index e9e926fcfaa..d718794a5ad 100644 --- a/yarn-project/end-to-end/src/e2e_fees/gas_estimation.test.ts +++ b/yarn-project/end-to-end/src/e2e_fees/gas_estimation.test.ts @@ -6,7 +6,10 @@ import { PublicFeePaymentMethod, } from '@aztec/aztec.js'; import { GasFees, type GasSettings } from '@aztec/circuits.js'; -import { type TokenContract as BananaCoin, type FPCContract } from '@aztec/noir-contracts.js'; +import { type Logger } from '@aztec/foundation/log'; +import { TokenContract as BananaCoin, type FPCContract } from '@aztec/noir-contracts.js'; + +import { inspect } from 'util'; import { FeesTest } from './fees_test.js'; @@ -18,6 +21,7 @@ describe('e2e_fees gas_estimation', () => { let bananaFPC: FPCContract; let gasSettings: GasSettings; let teardownFixedFee: bigint; + let logger: Logger; const t = new FeesTest('gas_estimation'); @@ -26,7 +30,7 @@ describe('e2e_fees gas_estimation', () => { await t.applyFPCSetupSnapshot(); await t.applyFundAliceWithBananas(); await t.applyFundAliceWithGasToken(); - ({ aliceWallet, aliceAddress, bobAddress, bananaCoin, bananaFPC, gasSettings } = await t.setup()); + ({ aliceWallet, aliceAddress, bobAddress, bananaCoin, bananaFPC, gasSettings, logger } = await t.setup()); teardownFixedFee = gasSettings.teardownGasLimits.computeFee(GasFees.default()).toBigInt(); }); @@ -51,9 +55,17 @@ describe('e2e_fees gas_estimation', () => { .add(estimatedGas.teardownGasLimits.computeFee(GasFees.default())) .toBigInt(); + const logGasEstimate = (estimatedGas: Pick) => + logger.info(`Estimated gas at`, { + gasLimits: inspect(estimatedGas.gasLimits), + teardownGasLimits: inspect(estimatedGas.teardownGasLimits), + }); + it('estimates gas with native fee payment method', async () => { const paymentMethod = new NativeFeePaymentMethod(aliceAddress); const estimatedGas = await makeTransferRequest().estimateGas({ fee: { gasSettings, paymentMethod } }); + logGasEstimate(estimatedGas); + const [withEstimate, withoutEstimate] = await sendTransfers(paymentMethod); const actualFee = withEstimate.transactionFee!; @@ -75,6 +87,8 @@ describe('e2e_fees gas_estimation', () => { it('estimates gas with public payment method', async () => { const paymentMethod = new PublicFeePaymentMethod(bananaCoin.address, bananaFPC.address, aliceWallet); const estimatedGas = await makeTransferRequest().estimateGas({ fee: { gasSettings, paymentMethod } }); + logGasEstimate(estimatedGas); + const [withEstimate, withoutEstimate] = await sendTransfers(paymentMethod); const actualFee = withEstimate.transactionFee!; @@ -93,4 +107,36 @@ describe('e2e_fees gas_estimation', () => { expect(feeFromEstimatedGas).toBeLessThan(actualFee * 3n); expect(feeFromEstimatedGas).toBeGreaterThanOrEqual(actualFee); }); + + it('estimates gas for public contract initialization with native fee payment method', async () => { + const paymentMethod = new NativeFeePaymentMethod(aliceAddress); + const deployMethod = () => BananaCoin.deploy(aliceWallet, aliceAddress, 'TKN', 'TKN', 8); + const deployOpts = { fee: { gasSettings, paymentMethod }, skipClassRegistration: true }; + const estimatedGas = await deployMethod().estimateGas(deployOpts); + logGasEstimate(estimatedGas); + + const [withEstimate, withoutEstimate] = await Promise.all([ + deployMethod() + .send({ ...deployOpts, estimateGas: true }) + .wait(), + deployMethod() + .send({ ...deployOpts, estimateGas: false }) + .wait(), + ]); + + // Estimation should yield that teardown has no cost, so should send the tx with zero for teardown + const actualFee = withEstimate.transactionFee!; + expect(actualFee + teardownFixedFee).toEqual(withoutEstimate.transactionFee!); + + // Check that estimated gas for teardown are zero + expect(estimatedGas.teardownGasLimits.l2Gas).toEqual(0); + expect(estimatedGas.teardownGasLimits.daGas).toEqual(0); + + // Check that the estimate was close to the actual gas used by recomputing the tx fee from it + const feeFromEstimatedGas = getFeeFromEstimatedGas(estimatedGas); + + // The actual fee should be under the estimate, but not too much + expect(feeFromEstimatedGas).toBeLessThan(actualFee * 2n); + expect(feeFromEstimatedGas).toBeGreaterThanOrEqual(actualFee); + }); }); diff --git a/yarn-project/foundation/src/serialize/serialize.ts b/yarn-project/foundation/src/serialize/serialize.ts index eaa81c27c7f..ae305070714 100644 --- a/yarn-project/foundation/src/serialize/serialize.ts +++ b/yarn-project/foundation/src/serialize/serialize.ts @@ -242,6 +242,8 @@ export function toFriendlyJSON(obj: object): string { ).toFriendlyJSON ) { return value.toFriendlyJSON(); + } else if (value && value.type && ['Fr', 'Fq', 'AztecAddress'].includes(value.type)) { + return value.value; } else { return value; } diff --git a/yarn-project/simulator/src/index.ts b/yarn-project/simulator/src/index.ts index 22bfc120b57..9688f1fc383 100644 --- a/yarn-project/simulator/src/index.ts +++ b/yarn-project/simulator/src/index.ts @@ -6,3 +6,4 @@ export * from './public/index.js'; export * from './simulator/index.js'; export * from './mocks/index.js'; export * from './stats/index.js'; +export * from './utils.js'; diff --git a/yarn-project/yarn.lock b/yarn-project/yarn.lock index 1cc67e4e8a8..1f675e04c7c 100644 --- a/yarn-project/yarn.lock +++ b/yarn-project/yarn.lock @@ -357,6 +357,7 @@ __metadata: "@aztec/l1-artifacts": "workspace:^" "@aztec/noir-contracts.js": "workspace:^" "@aztec/protocol-contracts": "workspace:^" + "@aztec/simulator": "workspace:^" "@aztec/types": "workspace:^" "@iarna/toml": ^2.2.5 "@jest/globals": ^29.5.0 From 43a968fc95cb6906e8ed8531bc3f63bb176c5d40 Mon Sep 17 00:00:00 2001 From: Alex Gherghisan Date: Tue, 28 May 2024 15:35:51 +0000 Subject: [PATCH 30/46] fix: add extra deploy details --- yarn-project/cli/src/cmds/create_account.ts | 7 +++++-- yarn-project/cli/src/cmds/deploy.ts | 2 ++ 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/yarn-project/cli/src/cmds/create_account.ts b/yarn-project/cli/src/cmds/create_account.ts index 3dbeb9d45b2..6fb108bc249 100644 --- a/yarn-project/cli/src/cmds/create_account.ts +++ b/yarn-project/cli/src/cmds/create_account.ts @@ -18,10 +18,10 @@ export async function createAccount( const client = await createCompatibleClient(rpcUrl, debugLogger); const printPK = typeof privateKey === 'undefined'; privateKey ??= Fr.random(); + const salt = Fr.ZERO; - const account = getSchnorrAccount(client, privateKey, deriveSigningKey(privateKey), Fr.ZERO); + const account = getSchnorrAccount(client, privateKey, deriveSigningKey(privateKey), salt); const { address, publicKeys, partialAddress } = account.getCompleteAddress(); - await account.register(); if (!registerOnly) { const wallet = await account.getWallet(); @@ -49,4 +49,7 @@ export async function createAccount( log(`Private key: ${privateKey.toString()}`); } log(`Partial address: ${partialAddress.toString()}`); + log(`Salt: ${salt.toString()}`); + log(`Init hash: ${account.getInstance().initializationHash.toString()}`); + log(`Deployer: ${account.getInstance().deployer.toString()}`); } diff --git a/yarn-project/cli/src/cmds/deploy.ts b/yarn-project/cli/src/cmds/deploy.ts index 82ee8312d16..09d197f8385 100644 --- a/yarn-project/cli/src/cmds/deploy.ts +++ b/yarn-project/cli/src/cmds/deploy.ts @@ -103,6 +103,7 @@ export async function deploy( txHash: txHash.toString(), initializationHash: instance.initializationHash.toString(), salt: salt.toString(), + deployer: instance.deployer.toString(), }); } else { log(`Contract deployed at ${address?.toString()}`); @@ -110,6 +111,7 @@ export async function deploy( log(`Contract init hash ${instance.initializationHash.toString()}`); log(`Deployment tx hash: ${txHash.toString()}`); log(`Deployment salt: ${salt.toString()}`); + log(`Deployer: ${instance.deployer.toString()}`); } } } From 417751e63a6d026ed882eb2fb1dc8289caed70a9 Mon Sep 17 00:00:00 2001 From: Santiago Palladino Date: Tue, 28 May 2024 17:52:29 +0200 Subject: [PATCH 31/46] feat: Bridging from L1 --- yarn-project/cli/src/cmds/bridge_l1_gas.ts | 38 +++++ yarn-project/cli/src/gas_portal.ts | 154 ++++++++++++++++++ yarn-project/cli/src/index.ts | 33 ++++ .../src/crypto/random/randomness_singleton.ts | 4 +- 4 files changed, 227 insertions(+), 2 deletions(-) create mode 100644 yarn-project/cli/src/cmds/bridge_l1_gas.ts create mode 100644 yarn-project/cli/src/gas_portal.ts diff --git a/yarn-project/cli/src/cmds/bridge_l1_gas.ts b/yarn-project/cli/src/cmds/bridge_l1_gas.ts new file mode 100644 index 00000000000..08428f979bb --- /dev/null +++ b/yarn-project/cli/src/cmds/bridge_l1_gas.ts @@ -0,0 +1,38 @@ +import { type AztecAddress } from '@aztec/circuits.js'; +import { createEthereumChain, createL1Clients } from '@aztec/ethereum'; +import { type DebugLogger, type LogFn } from '@aztec/foundation/log'; + +import { createCompatibleClient } from '../client.js'; +import { GasPortalManagerFactory } from '../gas_portal.js'; + +export async function bridgeL1Gas( + amount: bigint, + recipient: AztecAddress, + rpcUrl: string, + l1RpcUrl: string, + apiKey: string, + mnemonic: string, + log: LogFn, + debugLogger: DebugLogger, +) { + // Prepare L1 client + const chain = createEthereumChain(l1RpcUrl, apiKey); + const { publicClient, walletClient } = createL1Clients(chain.rpcUrl, mnemonic, chain.chainInfo); + + // Prepare L2 client + const client = await createCompatibleClient(rpcUrl, debugLogger); + + // Setup portal manager + const manager = await GasPortalManagerFactory.create({ + pxeService: client, + publicClient: publicClient, + walletClient: walletClient, + logger: debugLogger, + }); + + const { secret } = await manager.prepareTokensOnL1(amount, amount, recipient); + + log(`Minted ${amount} gas tokens on L1 and pushed to L2 portal`); + log(`claimAmount=${amount},claimSecret=${secret}\n`); + log(`Note: You need to wait for two L2 blocks before pulling them from the L2 side`); +} diff --git a/yarn-project/cli/src/gas_portal.ts b/yarn-project/cli/src/gas_portal.ts new file mode 100644 index 00000000000..aa74367c5e1 --- /dev/null +++ b/yarn-project/cli/src/gas_portal.ts @@ -0,0 +1,154 @@ +// REFACTOR: This file has been shamelessly copied from yarn-project/end-to-end/src/shared/gas_portal_test_harness.ts +// We should make this a shared utility in the aztec.js package. +import { type AztecAddress, type DebugLogger, EthAddress, Fr, type PXE, computeSecretHash } from '@aztec/aztec.js'; +import { GasPortalAbi, OutboxAbi, PortalERC20Abi } from '@aztec/l1-artifacts'; + +import { + type Account, + type Chain, + type GetContractReturnType, + type HttpTransport, + type PublicClient, + type WalletClient, + getContract, +} from 'viem'; + +export interface GasPortalManagerFactoryConfig { + pxeService: PXE; + publicClient: PublicClient; + walletClient: WalletClient; + logger: DebugLogger; +} + +export class GasPortalManagerFactory { + private constructor(private config: GasPortalManagerFactoryConfig) {} + + private async createReal() { + const { pxeService, publicClient, walletClient, logger } = this.config; + + const ethAccount = EthAddress.fromString((await walletClient.getAddresses())[0]); + const l1ContractAddresses = (await pxeService.getNodeInfo()).l1ContractAddresses; + + const gasTokenAddress = l1ContractAddresses.gasTokenAddress; + const gasPortalAddress = l1ContractAddresses.gasPortalAddress; + + if (gasTokenAddress.isZero() || gasPortalAddress.isZero()) { + throw new Error('Gas portal not deployed on L1'); + } + + const outbox = getContract({ + address: l1ContractAddresses.outboxAddress.toString(), + abi: OutboxAbi, + client: walletClient, + }); + + const gasL1 = getContract({ + address: gasTokenAddress.toString(), + abi: PortalERC20Abi, + client: walletClient, + }); + + const gasPortal = getContract({ + address: gasPortalAddress.toString(), + abi: GasPortalAbi, + client: walletClient, + }); + + return new GasPortalManager( + pxeService, + logger, + ethAccount, + gasPortalAddress, + gasPortal, + gasL1, + outbox, + publicClient, + walletClient, + ); + } + + static create(config: GasPortalManagerFactoryConfig): Promise { + const factory = new GasPortalManagerFactory(config); + return factory.createReal(); + } +} + +/** + * A Class for testing cross chain interactions, contains common interactions + * shared between cross chain tests. + */ +class GasPortalManager { + constructor( + /** Private eXecution Environment (PXE). */ + public pxeService: PXE, + /** Logger. */ + public logger: DebugLogger, + /** Eth account to interact with. */ + public ethAccount: EthAddress, + /** Portal address. */ + public tokenPortalAddress: EthAddress, + /** Token portal instance. */ + public tokenPortal: GetContractReturnType>, + /** Underlying token for portal tests. */ + public underlyingERC20: GetContractReturnType>, + /** Message Bridge Outbox. */ + public outbox: GetContractReturnType>, + /** Viem Public client instance. */ + public publicClient: PublicClient, + /** Viem Wallet Client instance. */ + public walletClient: WalletClient, + ) {} + + get l1GasTokenAddress() { + return EthAddress.fromString(this.underlyingERC20.address); + } + + generateClaimSecret(): [Fr, Fr] { + this.logger.debug("Generating a claim secret using pedersen's hash function"); + const secret = Fr.random(); + const secretHash = computeSecretHash(secret); + this.logger.info('Generated claim secret: ' + secretHash.toString()); + return [secret, secretHash]; + } + + async mintTokensOnL1(amount: bigint) { + this.logger.info('Minting tokens on L1'); + await this.publicClient.waitForTransactionReceipt({ + hash: await this.underlyingERC20.write.mint([this.ethAccount.toString(), amount]), + }); + } + + async getL1GasTokenBalance(address: EthAddress) { + return await this.underlyingERC20.read.balanceOf([address.toString()]); + } + + async sendTokensToPortalPublic(bridgeAmount: bigint, l2Address: AztecAddress, secretHash: Fr) { + await this.publicClient.waitForTransactionReceipt({ + hash: await this.underlyingERC20.write.approve([this.tokenPortalAddress.toString(), bridgeAmount]), + }); + + // Deposit tokens to the TokenPortal + this.logger.info('Sending messages to L1 portal to be consumed publicly'); + const args = [l2Address.toString(), bridgeAmount, secretHash.toString()] as const; + const { result: messageHash } = await this.tokenPortal.simulate.depositToAztecPublic(args, { + account: this.ethAccount.toString(), + } as any); + await this.publicClient.waitForTransactionReceipt({ + hash: await this.tokenPortal.write.depositToAztecPublic(args), + }); + + return Fr.fromString(messageHash); + } + + async prepareTokensOnL1(l1TokenBalance: bigint, bridgeAmount: bigint, owner: AztecAddress) { + const [secret, secretHash] = this.generateClaimSecret(); + + // Mint tokens on L1 + await this.mintTokensOnL1(l1TokenBalance); + + // Deposit tokens to the TokenPortal + const msgHash = await this.sendTokensToPortalPublic(bridgeAmount, owner, secretHash); + + return { secret, msgHash, secretHash }; + } +} diff --git a/yarn-project/cli/src/index.ts b/yarn-project/cli/src/index.ts index 62b1b2c63c0..31904b42ebd 100644 --- a/yarn-project/cli/src/index.ts +++ b/yarn-project/cli/src/index.ts @@ -10,6 +10,7 @@ import { dirname, resolve } from 'path'; import { FeeOpts } from './fees.js'; import { parseAztecAddress, + parseBigint, parseEthereumAddress, parseField, parseFieldFromHexString, @@ -143,6 +144,37 @@ export function getProgram(log: LogFn, debugLogger: DebugLogger): Command { } }); + program + .command('bridge-l1-gas') + .description('Mints L1 gas tokens and pushes them to L2.') + .argument('', 'The amount of gas tokens to mint and bridge.', parseBigint) + .argument('', 'Aztec address of the recipient.', parseAztecAddress) + .requiredOption( + '--l1-rpc-url ', + 'Url of the ethereum host. Chain identifiers localhost and testnet can be used', + ETHEREUM_HOST, + ) + .option('-a, --api-key ', 'Api key for the ethereum host', API_KEY) + .option( + '-m, --mnemonic ', + 'The mnemonic to use for deriving the Ethereum address that will mint and bridge', + 'test test test test test test test test test test test junk', + ) + .addOption(pxeOption) + .action(async (amount, recipient, options) => { + const { bridgeL1Gas } = await import('./cmds/bridge_l1_gas.js'); + await bridgeL1Gas( + amount, + recipient, + options.rpcUrl, + options.l1RpcUrl, + options.apiKey ?? '', + options.mnemonic, + log, + debugLogger, + ); + }); + program .command('generate-keys') .summary('Generates encryption and signing private keys.') @@ -174,6 +206,7 @@ export function getProgram(log: LogFn, debugLogger: DebugLogger): Command { .summary('Creates an aztec account that can be used for sending transactions.') .addOption(createPrivateKeyOption('Private key for account. Uses random by default.', false)) .addOption(pxeOption) + .addOptions(FeeOpts.getOptions()) .option( '--register-only', 'Just register the account on the PXE. Do not deploy or initialize the account contract.', diff --git a/yarn-project/foundation/src/crypto/random/randomness_singleton.ts b/yarn-project/foundation/src/crypto/random/randomness_singleton.ts index 9e51f6da8ef..667db265df1 100644 --- a/yarn-project/foundation/src/crypto/random/randomness_singleton.ts +++ b/yarn-project/foundation/src/crypto/random/randomness_singleton.ts @@ -18,10 +18,10 @@ export class RandomnessSingleton { private readonly log = createDebugLogger('aztec:randomness_singleton'), ) { if (seed !== undefined) { - this.log.info(`Using pseudo-randomness with seed: ${seed}`); + this.log.verbose(`Using pseudo-randomness with seed: ${seed}`); this.counter = seed; } else { - this.log.info('Using true randomness'); + this.log.verbose('Using true randomness'); } } From 45b325739f1cf71e228e1f00edcf902416e6078f Mon Sep 17 00:00:00 2001 From: Santiago Palladino Date: Wed, 29 May 2024 09:47:00 +0200 Subject: [PATCH 32/46] Allow querying L1 gas token balance --- yarn-project/cli/src/cmds/get_l1_balance.ts | 33 +++++++++++++++++++++ yarn-project/cli/src/index.ts | 16 ++++++++++ yarn-project/cli/src/utils.ts | 1 - 3 files changed, 49 insertions(+), 1 deletion(-) create mode 100644 yarn-project/cli/src/cmds/get_l1_balance.ts diff --git a/yarn-project/cli/src/cmds/get_l1_balance.ts b/yarn-project/cli/src/cmds/get_l1_balance.ts new file mode 100644 index 00000000000..26163191c42 --- /dev/null +++ b/yarn-project/cli/src/cmds/get_l1_balance.ts @@ -0,0 +1,33 @@ +import { type EthAddress } from '@aztec/circuits.js'; +import { createEthereumChain } from '@aztec/ethereum'; +import { type DebugLogger, type LogFn } from '@aztec/foundation/log'; +import { PortalERC20Abi } from '@aztec/l1-artifacts'; + +import { createPublicClient, getContract, http } from 'viem'; + +import { createCompatibleClient } from '../client.js'; + +export async function getL1Balance( + who: EthAddress, + rpcUrl: string, + l1RpcUrl: string, + apiKey: string, + log: LogFn, + debugLogger: DebugLogger, +) { + const client = await createCompatibleClient(rpcUrl, debugLogger); + const { l1ContractAddresses } = await client.getNodeInfo(); + + const chain = createEthereumChain(l1RpcUrl, apiKey); + const publicClient = createPublicClient({ chain: chain.chainInfo, transport: http(chain.rpcUrl) }); + + const gasL1 = getContract({ + address: l1ContractAddresses.gasTokenAddress.toString(), + abi: PortalERC20Abi, + client: publicClient, + }); + + const balance = await gasL1.read.balanceOf([who.toString()]); + + log(`L1 gas token balance of ${who.toString()} is ${balance.toString()}`); +} diff --git a/yarn-project/cli/src/index.ts b/yarn-project/cli/src/index.ts index 31904b42ebd..b6146f2951a 100644 --- a/yarn-project/cli/src/index.ts +++ b/yarn-project/cli/src/index.ts @@ -175,6 +175,22 @@ export function getProgram(log: LogFn, debugLogger: DebugLogger): Command { ); }); + program + .command('get-l1-balance') + .description('Gets the balance of gas tokens in L1 for the given Ethereum address.') + .argument('', 'Ethereum address to check.', parseEthereumAddress) + .requiredOption( + '--l1-rpc-url ', + 'Url of the ethereum host. Chain identifiers localhost and testnet can be used', + ETHEREUM_HOST, + ) + .option('-a, --api-key ', 'Api key for the ethereum host', API_KEY) + .addOption(pxeOption) + .action(async (who, options) => { + const { getL1Balance } = await import('./cmds/get_l1_balance.js'); + await getL1Balance(who, options.rpcUrl, options.l1RpcUrl, options.apiKey ?? '', log, debugLogger); + }); + program .command('generate-keys') .summary('Generates encryption and signing private keys.') diff --git a/yarn-project/cli/src/utils.ts b/yarn-project/cli/src/utils.ts index b9779864099..7917d77156f 100644 --- a/yarn-project/cli/src/utils.ts +++ b/yarn-project/cli/src/utils.ts @@ -124,7 +124,6 @@ export async function getContractArtifact(fileDir: string, log: LogFn) { for (const key of [fileDir, fileDir + 'Artifact', fileDir + 'ContractArtifact']) { if (artifacts[key]) { return artifacts[key] as ContractArtifact; - } } From cbdd681c8be8539931a89ffe7473a3474bcaa925 Mon Sep 17 00:00:00 2001 From: PhilWindle Date: Wed, 29 May 2024 08:41:51 +0000 Subject: [PATCH 33/46] WIP --- yarn-project/aztec/terraform/node/main.tf | 46 ++++++++++--------- .../aztec/terraform/node/variables.tf | 1 + 2 files changed, 25 insertions(+), 22 deletions(-) diff --git a/yarn-project/aztec/terraform/node/main.tf b/yarn-project/aztec/terraform/node/main.tf index 068139a50ef..51881a108ea 100644 --- a/yarn-project/aztec/terraform/node/main.tf +++ b/yarn-project/aztec/terraform/node/main.tf @@ -58,6 +58,8 @@ locals { node_p2p_private_keys = [var.NODE_1_PRIVATE_KEY, var.NODE_2_PRIVATE_KEY] node_count = length(local.publisher_private_keys) data_dir = "/usr/src/yarn-project/aztec/data" + agents_per_sequencer = var.AGENTS_PER_SEQUENCER + total_agents = local.node_count * local.agents_per_sequencer } resource "aws_cloudwatch_log_group" "aztec-node-log-group" { @@ -374,17 +376,17 @@ resource "aws_ecs_service" "aztec-node" { } - load_balancer { - target_group_arn = aws_lb_target_group.aztec-node-tcp[count.index].arn - container_name = "${var.DEPLOY_TAG}-aztec-node-${count.index + 1}" - container_port = var.NODE_P2P_TCP_PORT + count.index - } + # load_balancer { + # target_group_arn = aws_lb_target_group.aztec-node-tcp[count.index].arn + # container_name = "${var.DEPLOY_TAG}-aztec-node-${count.index + 1}" + # container_port = var.NODE_P2P_TCP_PORT + count.index + # } - load_balancer { - target_group_arn = aws_lb_target_group.aztec-node-udp[count.index].arn - container_name = "${var.DEPLOY_TAG}-aztec-node-${count.index + 1}" - container_port = var.NODE_P2P_UDP_PORT + count.index - } + # load_balancer { + # target_group_arn = aws_lb_target_group.aztec-node-udp[count.index].arn + # container_name = "${var.DEPLOY_TAG}-aztec-node-${count.index + 1}" + # container_port = var.NODE_P2P_UDP_PORT + count.index + # } service_registries { registry_arn = aws_service_discovery_service.aztec-node[count.index].arn @@ -549,14 +551,14 @@ resource "aws_lb_listener" "aztec-node-udp-listener" { // Configuration for proving agents resource "aws_cloudwatch_log_group" "aztec-proving-agent-log-group" { - count = local.node_count - name = "/fargate/service/${var.DEPLOY_TAG}/aztec-proving-agent-${count.index + 1}" + count = local.total_agents + name = "/fargate/service/${var.DEPLOY_TAG}/aztec-proving-agent-${floor(count.index / local.agents_per_sequencer) + 1}-${(count.index % local.agents_per_sequencer) + 1}" retention_in_days = 14 } resource "aws_service_discovery_service" "aztec-proving-agent" { - count = local.node_count - name = "${var.DEPLOY_TAG}-aztec-proving-agent-${count.index + 1}" + count = local.total_agents + name = "${var.DEPLOY_TAG}-aztec-proving-agent-${floor(count.index / local.agents_per_sequencer) + 1}-${(count.index % local.agents_per_sequencer) + 1}" health_check_custom_config { failure_threshold = 1 @@ -587,8 +589,8 @@ resource "aws_service_discovery_service" "aztec-proving-agent" { # Define task definitions for each node. resource "aws_ecs_task_definition" "aztec-proving-agent" { - count = local.node_count - family = "${var.DEPLOY_TAG}-aztec-proving-agent-${count.index + 1}" + count = local.total_agents + family = "${var.DEPLOY_TAG}-aztec-proving-agent-${floor(count.index / local.agents_per_sequencer) + 1}-${(count.index % local.agents_per_sequencer) + 1}" requires_compatibilities = ["FARGATE"] network_mode = "awsvpc" cpu = "16384" @@ -599,7 +601,7 @@ resource "aws_ecs_task_definition" "aztec-proving-agent" { container_definitions = < Date: Wed, 29 May 2024 08:48:23 +0000 Subject: [PATCH 34/46] WIP --- yarn-project/aztec/terraform/node/main.tf | 6 +++--- yarn-project/aztec/terraform/node/variables.tf | 4 ++++ yarn-project/cli/src/cmds/create_account.ts | 2 +- 3 files changed, 8 insertions(+), 4 deletions(-) diff --git a/yarn-project/aztec/terraform/node/main.tf b/yarn-project/aztec/terraform/node/main.tf index 51881a108ea..86aae4066ce 100644 --- a/yarn-project/aztec/terraform/node/main.tf +++ b/yarn-project/aztec/terraform/node/main.tf @@ -328,7 +328,7 @@ resource "aws_ecs_task_definition" "aztec-node" { }, { "name": "PROVER_REAL_PROOFS", - "value": "false" + "value": "${var.PROVING_ENABLED}" } ], "mountPoints": [ @@ -603,7 +603,7 @@ resource "aws_ecs_task_definition" "aztec-proving-agent" { { "name": "${var.DEPLOY_TAG}-aztec-proving-agent-${floor(count.index / local.agents_per_sequencer) + 1}-${(count.index % local.agents_per_sequencer) + 1}", "image": "${var.DOCKERHUB_ACCOUNT}/aztec:${var.DEPLOY_TAG}", - "command": ["start", "--prover", "simulate=true"], + "command": ["start", "--prover"], "essential": true, "memoryReservation": 65536, "portMappings": [ @@ -634,7 +634,7 @@ resource "aws_ecs_task_definition" "aztec-proving-agent" { }, { "name": "PROVER_REAL_PROOFS", - "value": "false" + "value": "${var.PROVING_ENABLED}" }, { "name": "ACVM_WORKING_DIRECTORY", diff --git a/yarn-project/aztec/terraform/node/variables.tf b/yarn-project/aztec/terraform/node/variables.tf index e416318e88c..9cf51486bce 100644 --- a/yarn-project/aztec/terraform/node/variables.tf +++ b/yarn-project/aztec/terraform/node/variables.tf @@ -75,3 +75,7 @@ variable "OUTBOX_CONTRACT_ADDRESS" { type = string } variable "GAS_TOKEN_CONTRACT_ADDRESS" { type = string } variable "GAS_PORTAL_CONTRACT_ADDRESS" { type = string } variable "AGENTS_PER_SEQUENCER" { type = string } +variable "PROVING_ENABLED" { + type = bool + default = true +} diff --git a/yarn-project/cli/src/cmds/create_account.ts b/yarn-project/cli/src/cmds/create_account.ts index 6fb108bc249..0fa4a0e3aa8 100644 --- a/yarn-project/cli/src/cmds/create_account.ts +++ b/yarn-project/cli/src/cmds/create_account.ts @@ -31,7 +31,7 @@ export async function createAccount( printGasEstimates(feeOpts, gas, log); } else { const tx = account.deploy({ ...sendOpts }); - const txHash = tx.getTxHash(); + const txHash = await tx.getTxHash(); debugLogger.debug(`Account contract tx sent with hash ${txHash}`); if (wait) { log(`\nWaiting for account contract deployment...`); From 048cafbfcf9df41de07abe8e3dd9d1085b7a9774 Mon Sep 17 00:00:00 2001 From: Santiago Palladino Date: Wed, 29 May 2024 10:49:11 +0200 Subject: [PATCH 35/46] Fixes for CLI deploy and account registration --- yarn-project/cli/src/cmds/create_account.ts | 4 +++- yarn-project/cli/src/cmds/deploy.ts | 6 ++++-- yarn-project/cli/src/fees.ts | 8 ++++++-- yarn-project/cli/src/index.ts | 3 +++ 4 files changed, 16 insertions(+), 5 deletions(-) diff --git a/yarn-project/cli/src/cmds/create_account.ts b/yarn-project/cli/src/cmds/create_account.ts index 0fa4a0e3aa8..4ab2d0015c8 100644 --- a/yarn-project/cli/src/cmds/create_account.ts +++ b/yarn-project/cli/src/cmds/create_account.ts @@ -23,7 +23,9 @@ export async function createAccount( const account = getSchnorrAccount(client, privateKey, deriveSigningKey(privateKey), salt); const { address, publicKeys, partialAddress } = account.getCompleteAddress(); - if (!registerOnly) { + if (registerOnly) { + await account.register(); + } else { const wallet = await account.getWallet(); const sendOpts = feeOpts.toSendOpts(wallet); if (feeOpts.estimateOnly) { diff --git a/yarn-project/cli/src/cmds/deploy.ts b/yarn-project/cli/src/cmds/deploy.ts index 09d197f8385..c80c45dda4e 100644 --- a/yarn-project/cli/src/cmds/deploy.ts +++ b/yarn-project/cli/src/cmds/deploy.ts @@ -1,5 +1,5 @@ import { getSchnorrAccount } from '@aztec/accounts/schnorr'; -import { ContractDeployer, Fr } from '@aztec/aztec.js'; +import { ContractDeployer, type DeployMethod, Fr } from '@aztec/aztec.js'; import { type PublicKeys, deriveSigningKey } from '@aztec/circuits.js'; import { getInitializer } from '@aztec/foundation/abi'; import { type DebugLogger, type LogFn } from '@aztec/foundation/log'; @@ -22,6 +22,7 @@ export async function deploy( skipPublicDeployment: boolean, skipClassRegistration: boolean, skipInitialization: boolean | undefined, + universalDeploy: boolean | undefined, wait: boolean, feeOpts: IFeeOpts, debugLogger: DebugLogger, @@ -55,9 +56,10 @@ export async function deploy( } const deploy = deployer.deploy(...args); - const deployOpts = { + const deployOpts: Parameters[0] = { ...feeOpts.toSendOpts(wallet), contractAddressSalt: salt, + universalDeploy, skipClassRegistration, skipInitialization, skipPublicDeployment, diff --git a/yarn-project/cli/src/fees.ts b/yarn-project/cli/src/fees.ts index c6800c66ead..4b0a347197b 100644 --- a/yarn-project/cli/src/fees.ts +++ b/yarn-project/cli/src/fees.ts @@ -91,6 +91,7 @@ export class FeeOpts implements IFeeOpts { ...GasSettings.default(), ...(args.gasLimits ? parseGasLimits(args.gasLimits) : {}), ...(args.inclusionFee ? { inclusionFee: new Fr(args.inclusionFee) } : {}), + maxFeesPerGas: GasFees.default(), }); return new FeeOpts( estimateOnly, @@ -155,8 +156,11 @@ function parsePaymentMethod(payment: string, log: LogFn): (sender: AccountWallet } case 'fpc-private': { const [asset, fpc] = getFpcOpts(parsed); - log(`Using private fee payment with asset ${asset} via paymaster ${fpc}`); - return new PrivateFeePaymentMethod(asset, fpc, sender, Fr.fromString(parsed.rebateSecret)); + const rebateSecret = parsed.rebateSecret ? Fr.fromString(parsed.rebateSecret) : Fr.random(); + log( + `Using private fee payment with asset ${asset} via paymaster ${fpc} with rebate secret ${rebateSecret.toString()}`, + ); + return new PrivateFeePaymentMethod(asset, fpc, sender, rebateSecret); } case undefined: throw new Error('Missing "method" in payment option'); diff --git a/yarn-project/cli/src/index.ts b/yarn-project/cli/src/index.ts index b6146f2951a..74ed7406ae1 100644 --- a/yarn-project/cli/src/index.ts +++ b/yarn-project/cli/src/index.ts @@ -284,6 +284,7 @@ export function getProgram(log: LogFn, debugLogger: DebugLogger): Command { 'Optional deployment salt as a hex string for generating the deployment address.', parseFieldFromHexString, ) + .option('--universal', 'Do not mix the sender address into the deployment.') .addOption(createPrivateKeyOption("The sender's private key.", true)) .option('--json', 'Emit output as json') // `options.wait` is default true. Passing `--no-wait` will set it to false. @@ -307,6 +308,7 @@ export function getProgram(log: LogFn, debugLogger: DebugLogger): Command { classRegistration, initialize, publicDeployment, + universal, } = opts; await deploy( artifactPath, @@ -320,6 +322,7 @@ export function getProgram(log: LogFn, debugLogger: DebugLogger): Command { !publicDeployment, !classRegistration, typeof initialize === 'string' ? false : initialize, + universal, wait, FeeOpts.fromCli(opts, log), debugLogger, From 72f0664ace30c4e6bca147880dea0d3da98b28de Mon Sep 17 00:00:00 2001 From: Santiago Palladino Date: Wed, 29 May 2024 11:14:26 +0200 Subject: [PATCH 36/46] Estimate gas during account contract deployment --- yarn-project/aztec.js/src/account_manager/index.ts | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/yarn-project/aztec.js/src/account_manager/index.ts b/yarn-project/aztec.js/src/account_manager/index.ts index 9d947435caf..002f98169aa 100644 --- a/yarn-project/aztec.js/src/account_manager/index.ts +++ b/yarn-project/aztec.js/src/account_manager/index.ts @@ -17,7 +17,10 @@ import { DeployAccountSentTx } from './deploy_account_sent_tx.js'; /** * Options to deploy an account contract. */ -export type DeployAccountOptions = Pick; +export type DeployAccountOptions = Pick< + DeployOptions, + 'fee' | 'skipClassRegistration' | 'skipPublicDeployment' | 'estimateGas' +>; /** * Manages a user account. Provides methods for calculating the account's address, deploying the account contract, @@ -163,6 +166,7 @@ export class AccountManager { skipInitialization: false, universalDeploy: true, fee: opts?.fee, + estimateGas: opts?.estimateGas, }), ) .then(tx => tx.getTxHash()); From 48ad5a47cee8f96ea9c706029370214d8cc90b23 Mon Sep 17 00:00:00 2001 From: Santiago Palladino Date: Wed, 29 May 2024 11:14:39 +0200 Subject: [PATCH 37/46] Output more data on create-account --- yarn-project/cli/src/cmds/create_account.ts | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/yarn-project/cli/src/cmds/create_account.ts b/yarn-project/cli/src/cmds/create_account.ts index 4ab2d0015c8..e526682a779 100644 --- a/yarn-project/cli/src/cmds/create_account.ts +++ b/yarn-project/cli/src/cmds/create_account.ts @@ -23,6 +23,8 @@ export async function createAccount( const account = getSchnorrAccount(client, privateKey, deriveSigningKey(privateKey), salt); const { address, publicKeys, partialAddress } = account.getCompleteAddress(); + let tx; + let txReceipt; if (registerOnly) { await account.register(); } else { @@ -32,21 +34,19 @@ export async function createAccount( const gas = await (await account.getDeployMethod()).estimateGas({ ...sendOpts }); printGasEstimates(feeOpts, gas, log); } else { - const tx = account.deploy({ ...sendOpts }); + tx = account.deploy({ ...sendOpts }); const txHash = await tx.getTxHash(); debugLogger.debug(`Account contract tx sent with hash ${txHash}`); if (wait) { log(`\nWaiting for account contract deployment...`); - await tx.wait(); - } else { - log(`\nAccount deployment transaction hash: ${txHash}\n`); + txReceipt = await tx.wait(); } } } log(`\nNew account:\n`); log(`Address: ${address.toString()}`); - log(`Public key: ${publicKeys.toString()}`); + log(`Public key: 0x${publicKeys.toString()}`); if (printPK) { log(`Private key: ${privateKey.toString()}`); } @@ -54,4 +54,10 @@ export async function createAccount( log(`Salt: ${salt.toString()}`); log(`Init hash: ${account.getInstance().initializationHash.toString()}`); log(`Deployer: ${account.getInstance().deployer.toString()}`); + if (tx) { + log(`Deploy tx hash: ${await tx.getTxHash()}`); + } + if (txReceipt) { + log(`Deploy tx fee: ${txReceipt.transactionFee}`); + } } From 209e0cfc6ba74d7dea4047d1511f58c1ada6cdee Mon Sep 17 00:00:00 2001 From: Santiago Palladino Date: Wed, 29 May 2024 11:14:46 +0200 Subject: [PATCH 38/46] Command to add pending shield --- .../cli/src/cmds/add_pending_shield.ts | 33 +++++++++++++++++++ yarn-project/cli/src/index.ts | 23 +++++++++++++ 2 files changed, 56 insertions(+) create mode 100644 yarn-project/cli/src/cmds/add_pending_shield.ts diff --git a/yarn-project/cli/src/cmds/add_pending_shield.ts b/yarn-project/cli/src/cmds/add_pending_shield.ts new file mode 100644 index 00000000000..1ae6f4f9da1 --- /dev/null +++ b/yarn-project/cli/src/cmds/add_pending_shield.ts @@ -0,0 +1,33 @@ +import { type AztecAddress, Fr, computeSecretHash } from '@aztec/aztec.js'; +import { ExtendedNote, Note, type TxHash } from '@aztec/circuit-types'; +import { type DebugLogger, type LogFn } from '@aztec/foundation/log'; +import { TokenContract } from '@aztec/noir-contracts.js'; + +import { createCompatibleClient } from '../client.js'; + +export async function addPendingShield( + ownerAddress: AztecAddress, + tokenAddress: AztecAddress, + amount: bigint, + secret: Fr, + txHash: TxHash, + rpcUrl: string, + debugLogger: DebugLogger, + log: LogFn, +) { + const secretHash = computeSecretHash(secret); + const note = new Note([new Fr(amount), secretHash]); + const extendedNote = new ExtendedNote( + note, + ownerAddress, + tokenAddress, + TokenContract.storage.pending_shields.slot, + TokenContract.notes.TransparentNote.id, + txHash, + ); + const client = await createCompatibleClient(rpcUrl, debugLogger); + await client.addNote(extendedNote); + log(`Added pending shield note owned by ${ownerAddress.toString()} for ${amount}`); +} + +// await t.addPendingShieldNoteToPXE(bobsAddress, maxFee - actualFee, computeSecretHash(rebateSecret), tx.txHash); diff --git a/yarn-project/cli/src/index.ts b/yarn-project/cli/src/index.ts index 74ed7406ae1..b83f271a7a3 100644 --- a/yarn-project/cli/src/index.ts +++ b/yarn-project/cli/src/index.ts @@ -565,6 +565,29 @@ export function getProgram(log: LogFn, debugLogger: DebugLogger): Command { ); }); + program + .command('add-pending-shield') + .description('Adds a pending shield note to the database in the PXE.') + .argument('
', 'Aztec address of the note owner.', parseAztecAddress) + .argument('', 'Amount of the pending shield note.', parseBigint) + .requiredOption('-ca, --contract-address
', 'Aztec address of the token contract.', parseAztecAddress) + .requiredOption('-tx, --tx-hash ', 'Tx hash in which the note was created.', parseOptionalTxHash) + .requiredOption('--secret ', 'Secret used for shielding the note.', parseField) + .addOption(pxeOption) + .action(async (address, amount, options) => { + const { addPendingShield } = await import('./cmds/add_pending_shield.js'); + await addPendingShield( + address, + options.contractAddress, + amount, + options.secret, + options.txHash, + options.rpcUrl, + debugLogger, + log, + ); + }); + // Helper for users to decode hex strings into structs if needed. program .command('parse-parameter-struct') From 98f57bc8f8a85d2998735846cea669205d67c90b Mon Sep 17 00:00:00 2001 From: spypsy Date: Wed, 29 May 2024 09:32:20 +0000 Subject: [PATCH 39/46] use only subnet1 in node ecs service --- yarn-project/aztec/terraform/node/main.tf | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/yarn-project/aztec/terraform/node/main.tf b/yarn-project/aztec/terraform/node/main.tf index 86aae4066ce..5e1c6cc006b 100644 --- a/yarn-project/aztec/terraform/node/main.tf +++ b/yarn-project/aztec/terraform/node/main.tf @@ -363,8 +363,7 @@ resource "aws_ecs_service" "aztec-node" { network_configuration { subnets = [ - data.terraform_remote_state.setup_iac.outputs.subnet_az1_private_id, - data.terraform_remote_state.setup_iac.outputs.subnet_az2_private_id + data.terraform_remote_state.setup_iac.outputs.subnet_az1_private_id ] security_groups = [data.terraform_remote_state.aztec-network_iac.outputs.p2p_security_group_id, data.terraform_remote_state.setup_iac.outputs.security_group_private_id] } From 9f329a7eaa4546c8d88f7b70db0b642bf6a8fe3a Mon Sep 17 00:00:00 2001 From: spypsy Date: Wed, 29 May 2024 09:36:56 +0000 Subject: [PATCH 40/46] uncomment service load balancers --- yarn-project/aztec/terraform/node/main.tf | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/yarn-project/aztec/terraform/node/main.tf b/yarn-project/aztec/terraform/node/main.tf index 5e1c6cc006b..73503a7bcd5 100644 --- a/yarn-project/aztec/terraform/node/main.tf +++ b/yarn-project/aztec/terraform/node/main.tf @@ -375,17 +375,17 @@ resource "aws_ecs_service" "aztec-node" { } - # load_balancer { - # target_group_arn = aws_lb_target_group.aztec-node-tcp[count.index].arn - # container_name = "${var.DEPLOY_TAG}-aztec-node-${count.index + 1}" - # container_port = var.NODE_P2P_TCP_PORT + count.index - # } - - # load_balancer { - # target_group_arn = aws_lb_target_group.aztec-node-udp[count.index].arn - # container_name = "${var.DEPLOY_TAG}-aztec-node-${count.index + 1}" - # container_port = var.NODE_P2P_UDP_PORT + count.index - # } + load_balancer { + target_group_arn = aws_lb_target_group.aztec-node-tcp[count.index].arn + container_name = "${var.DEPLOY_TAG}-aztec-node-${count.index + 1}" + container_port = var.NODE_P2P_TCP_PORT + count.index + } + + load_balancer { + target_group_arn = aws_lb_target_group.aztec-node-udp[count.index].arn + container_name = "${var.DEPLOY_TAG}-aztec-node-${count.index + 1}" + container_port = var.NODE_P2P_UDP_PORT + count.index + } service_registries { registry_arn = aws_service_discovery_service.aztec-node[count.index].arn From 42514cb863e33d98c5aa1565164180f0bfe5497d Mon Sep 17 00:00:00 2001 From: spypsy Date: Wed, 29 May 2024 12:32:45 +0000 Subject: [PATCH 41/46] fix discv5 unverified sessions --- yarn-project/p2p-bootstrap/terraform/main.tf | 9 +++++---- yarn-project/p2p/src/bootstrap/bootstrap.ts | 1 + yarn-project/p2p/src/service/discV5_service.ts | 1 + 3 files changed, 7 insertions(+), 4 deletions(-) diff --git a/yarn-project/p2p-bootstrap/terraform/main.tf b/yarn-project/p2p-bootstrap/terraform/main.tf index aab81f1594f..ef4cd2e7e23 100644 --- a/yarn-project/p2p-bootstrap/terraform/main.tf +++ b/yarn-project/p2p-bootstrap/terraform/main.tf @@ -105,7 +105,7 @@ resource "aws_ecs_task_definition" "p2p-bootstrap" { [ { "name": "${var.DEPLOY_TAG}-p2p-bootstrap-${count.index + 1}", - "image": "${var.DOCKERHUB_ACCOUNT}/aztec:devnet", + "image": "${var.DOCKERHUB_ACCOUNT}/aztec:${var.DEPLOY_TAG}", "command": ["start", "--p2p-bootstrap"], "essential": true, "memoryReservation": 3776, @@ -145,7 +145,7 @@ resource "aws_ecs_task_definition" "p2p-bootstrap" { }, { "name": "DEBUG", - "value": "aztec:*" + "value": "aztec:*,discv5:*" }, { "name": "P2P_MIN_PEERS", @@ -251,9 +251,10 @@ resource "aws_security_group_rule" "allow-bootstrap-udp" { # Egress Rule for UDP Traffic resource "aws_security_group_rule" "allow-bootstrap-udp-egress" { + count = local.bootnode_count type = "egress" - from_port = var.BOOTNODE_LISTEN_PORT - to_port = var.BOOTNODE_LISTEN_PORT + from_port = var.BOOTNODE_LISTEN_PORT + count.index + to_port = var.BOOTNODE_LISTEN_PORT + count.index protocol = "udp" security_group_id = data.terraform_remote_state.aztec-network_iac.outputs.p2p_security_group_id cidr_blocks = ["0.0.0.0/0"] diff --git a/yarn-project/p2p/src/bootstrap/bootstrap.ts b/yarn-project/p2p/src/bootstrap/bootstrap.ts index d73a24937bf..7eab3aed482 100644 --- a/yarn-project/p2p/src/bootstrap/bootstrap.ts +++ b/yarn-project/p2p/src/bootstrap/bootstrap.ts @@ -49,6 +49,7 @@ export class BootstrapNode { bindAddrs: { ip4: listenAddrUdp }, config: { lookupTimeout: 2000, + allowUnverifiedSessions: true, }, }); diff --git a/yarn-project/p2p/src/service/discV5_service.ts b/yarn-project/p2p/src/service/discV5_service.ts index f86dc8f4c7c..05f81016cb4 100644 --- a/yarn-project/p2p/src/service/discV5_service.ts +++ b/yarn-project/p2p/src/service/discV5_service.ts @@ -61,6 +61,7 @@ export class DiscV5Service extends EventEmitter implements PeerDiscoveryService bindAddrs: { ip4: listenMultiAddrUdp }, config: { lookupTimeout: 2000, + allowUnverifiedSessions: true, }, }); From 4f02008e2499be569d829a9566ebd11fb8806762 Mon Sep 17 00:00:00 2001 From: PhilWindle Date: Wed, 29 May 2024 13:45:04 +0000 Subject: [PATCH 42/46] Disable P2P --- yarn-project/aztec/terraform/node/main.tf | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/yarn-project/aztec/terraform/node/main.tf b/yarn-project/aztec/terraform/node/main.tf index 73503a7bcd5..5e1c6cc006b 100644 --- a/yarn-project/aztec/terraform/node/main.tf +++ b/yarn-project/aztec/terraform/node/main.tf @@ -375,17 +375,17 @@ resource "aws_ecs_service" "aztec-node" { } - load_balancer { - target_group_arn = aws_lb_target_group.aztec-node-tcp[count.index].arn - container_name = "${var.DEPLOY_TAG}-aztec-node-${count.index + 1}" - container_port = var.NODE_P2P_TCP_PORT + count.index - } - - load_balancer { - target_group_arn = aws_lb_target_group.aztec-node-udp[count.index].arn - container_name = "${var.DEPLOY_TAG}-aztec-node-${count.index + 1}" - container_port = var.NODE_P2P_UDP_PORT + count.index - } + # load_balancer { + # target_group_arn = aws_lb_target_group.aztec-node-tcp[count.index].arn + # container_name = "${var.DEPLOY_TAG}-aztec-node-${count.index + 1}" + # container_port = var.NODE_P2P_TCP_PORT + count.index + # } + + # load_balancer { + # target_group_arn = aws_lb_target_group.aztec-node-udp[count.index].arn + # container_name = "${var.DEPLOY_TAG}-aztec-node-${count.index + 1}" + # container_port = var.NODE_P2P_UDP_PORT + count.index + # } service_registries { registry_arn = aws_service_discovery_service.aztec-node[count.index].arn From b711c859fc2d501061ad8c2e580a3339d8ae3cda Mon Sep 17 00:00:00 2001 From: PhilWindle Date: Wed, 29 May 2024 14:14:54 +0000 Subject: [PATCH 43/46] Formatting --- yarn-project/cli/src/client.test.ts | 6 +++--- yarn-project/cli/src/cmds/add_note.ts | 6 +++--- yarn-project/cli/src/cmds/block_number.ts | 2 +- yarn-project/cli/src/cmds/check_deploy.ts | 4 ++-- yarn-project/cli/src/cmds/compute_selector.ts | 2 +- yarn-project/cli/src/cmds/example_contracts.ts | 2 +- yarn-project/cli/src/cmds/generate_p2p_private_key.ts | 2 +- yarn-project/cli/src/cmds/get_account.ts | 4 ++-- yarn-project/cli/src/cmds/get_accounts.ts | 2 +- yarn-project/cli/src/cmds/get_contract_data.ts | 4 ++-- yarn-project/cli/src/cmds/get_logs.ts | 6 +++--- yarn-project/cli/src/cmds/get_node_info.ts | 2 +- yarn-project/cli/src/cmds/get_recipient.ts | 4 ++-- yarn-project/cli/src/cmds/get_recipients.ts | 2 +- yarn-project/cli/src/cmds/get_tx_receipt.ts | 4 ++-- yarn-project/cli/src/cmds/parse_parameter_struct.ts | 4 ++-- yarn-project/cli/src/encoding.ts | 2 +- yarn-project/cli/src/update/noir.ts | 4 ++-- yarn-project/cli/src/update/npm.ts | 6 +++--- yarn-project/cli/src/update/update.ts | 4 ++-- 20 files changed, 36 insertions(+), 36 deletions(-) diff --git a/yarn-project/cli/src/client.test.ts b/yarn-project/cli/src/client.test.ts index 8e879f762ba..ad21f399256 100644 --- a/yarn-project/cli/src/client.test.ts +++ b/yarn-project/cli/src/client.test.ts @@ -1,7 +1,7 @@ -import { NodeInfo } from '@aztec/aztec.js'; -import { PXE } from '@aztec/circuit-types'; +import { type NodeInfo } from '@aztec/aztec.js'; +import { type PXE } from '@aztec/circuit-types'; -import { MockProxy, mock } from 'jest-mock-extended'; +import { type MockProxy, mock } from 'jest-mock-extended'; import { checkServerVersion } from './client.js'; diff --git a/yarn-project/cli/src/cmds/add_note.ts b/yarn-project/cli/src/cmds/add_note.ts index 988c2a45c12..f6359bd5c1c 100644 --- a/yarn-project/cli/src/cmds/add_note.ts +++ b/yarn-project/cli/src/cmds/add_note.ts @@ -1,6 +1,6 @@ -import { AztecAddress, Fr } from '@aztec/aztec.js'; -import { ExtendedNote, Note, TxHash } from '@aztec/circuit-types'; -import { DebugLogger } from '@aztec/foundation/log'; +import { type AztecAddress, type Fr } from '@aztec/aztec.js'; +import { ExtendedNote, Note, type TxHash } from '@aztec/circuit-types'; +import { type DebugLogger } from '@aztec/foundation/log'; import { createCompatibleClient } from '../client.js'; import { parseFields } from '../parse_args.js'; diff --git a/yarn-project/cli/src/cmds/block_number.ts b/yarn-project/cli/src/cmds/block_number.ts index c5aed126443..5b6ca472d6b 100644 --- a/yarn-project/cli/src/cmds/block_number.ts +++ b/yarn-project/cli/src/cmds/block_number.ts @@ -1,4 +1,4 @@ -import { DebugLogger, LogFn } from '@aztec/foundation/log'; +import { type DebugLogger, type LogFn } from '@aztec/foundation/log'; import { createCompatibleClient } from '../client.js'; diff --git a/yarn-project/cli/src/cmds/check_deploy.ts b/yarn-project/cli/src/cmds/check_deploy.ts index c9777522e10..4a00c72a518 100644 --- a/yarn-project/cli/src/cmds/check_deploy.ts +++ b/yarn-project/cli/src/cmds/check_deploy.ts @@ -1,5 +1,5 @@ -import { AztecAddress } from '@aztec/aztec.js'; -import { DebugLogger, LogFn } from '@aztec/foundation/log'; +import { type AztecAddress } from '@aztec/aztec.js'; +import { type DebugLogger, type LogFn } from '@aztec/foundation/log'; import { createCompatibleClient } from '../client.js'; diff --git a/yarn-project/cli/src/cmds/compute_selector.ts b/yarn-project/cli/src/cmds/compute_selector.ts index 074be33597a..9d299a64eff 100644 --- a/yarn-project/cli/src/cmds/compute_selector.ts +++ b/yarn-project/cli/src/cmds/compute_selector.ts @@ -1,5 +1,5 @@ import { FunctionSelector } from '@aztec/foundation/abi'; -import { LogFn } from '@aztec/foundation/log'; +import { type LogFn } from '@aztec/foundation/log'; export function computeSelector(functionSignature: string, log: LogFn) { const selector = FunctionSelector.fromSignature(functionSignature); diff --git a/yarn-project/cli/src/cmds/example_contracts.ts b/yarn-project/cli/src/cmds/example_contracts.ts index c7ee019eccc..94c7437262b 100644 --- a/yarn-project/cli/src/cmds/example_contracts.ts +++ b/yarn-project/cli/src/cmds/example_contracts.ts @@ -1,4 +1,4 @@ -import { LogFn } from '@aztec/foundation/log'; +import { type LogFn } from '@aztec/foundation/log'; import { getExampleContractArtifacts } from '../utils.js'; diff --git a/yarn-project/cli/src/cmds/generate_p2p_private_key.ts b/yarn-project/cli/src/cmds/generate_p2p_private_key.ts index 928e61974ba..f62617ae6ea 100644 --- a/yarn-project/cli/src/cmds/generate_p2p_private_key.ts +++ b/yarn-project/cli/src/cmds/generate_p2p_private_key.ts @@ -1,4 +1,4 @@ -import { LogFn } from '@aztec/foundation/log'; +import { type LogFn } from '@aztec/foundation/log'; import { createSecp256k1PeerId } from '@libp2p/peer-id-factory'; diff --git a/yarn-project/cli/src/cmds/get_account.ts b/yarn-project/cli/src/cmds/get_account.ts index 8b3e9359d5f..c672c85fb85 100644 --- a/yarn-project/cli/src/cmds/get_account.ts +++ b/yarn-project/cli/src/cmds/get_account.ts @@ -1,5 +1,5 @@ -import { AztecAddress } from '@aztec/aztec.js'; -import { DebugLogger, LogFn } from '@aztec/foundation/log'; +import { type AztecAddress } from '@aztec/aztec.js'; +import { type DebugLogger, type LogFn } from '@aztec/foundation/log'; import { createCompatibleClient } from '../client.js'; diff --git a/yarn-project/cli/src/cmds/get_accounts.ts b/yarn-project/cli/src/cmds/get_accounts.ts index 15e62de7edb..204842f4387 100644 --- a/yarn-project/cli/src/cmds/get_accounts.ts +++ b/yarn-project/cli/src/cmds/get_accounts.ts @@ -1,4 +1,4 @@ -import { DebugLogger, LogFn } from '@aztec/foundation/log'; +import { type DebugLogger, type LogFn } from '@aztec/foundation/log'; import { createCompatibleClient } from '../client.js'; diff --git a/yarn-project/cli/src/cmds/get_contract_data.ts b/yarn-project/cli/src/cmds/get_contract_data.ts index d11180f7e4b..592358a9fd3 100644 --- a/yarn-project/cli/src/cmds/get_contract_data.ts +++ b/yarn-project/cli/src/cmds/get_contract_data.ts @@ -1,5 +1,5 @@ -import { AztecAddress } from '@aztec/aztec.js'; -import { DebugLogger, LogFn } from '@aztec/foundation/log'; +import { type AztecAddress } from '@aztec/aztec.js'; +import { type DebugLogger, type LogFn } from '@aztec/foundation/log'; import { createCompatibleClient } from '../client.js'; diff --git a/yarn-project/cli/src/cmds/get_logs.ts b/yarn-project/cli/src/cmds/get_logs.ts index 76c27c76726..29afb7bbc6f 100644 --- a/yarn-project/cli/src/cmds/get_logs.ts +++ b/yarn-project/cli/src/cmds/get_logs.ts @@ -1,6 +1,6 @@ -import { AztecAddress, LogFilter, LogId, TxHash } from '@aztec/aztec.js'; -import { EventSelector } from '@aztec/foundation/abi'; -import { DebugLogger, LogFn } from '@aztec/foundation/log'; +import { type AztecAddress, type LogFilter, type LogId, type TxHash } from '@aztec/aztec.js'; +import { type EventSelector } from '@aztec/foundation/abi'; +import { type DebugLogger, type LogFn } from '@aztec/foundation/log'; import { sleep } from '@aztec/foundation/sleep'; import { createCompatibleClient } from '../client.js'; diff --git a/yarn-project/cli/src/cmds/get_node_info.ts b/yarn-project/cli/src/cmds/get_node_info.ts index b775c08aa0d..b66df822532 100644 --- a/yarn-project/cli/src/cmds/get_node_info.ts +++ b/yarn-project/cli/src/cmds/get_node_info.ts @@ -1,4 +1,4 @@ -import { DebugLogger, LogFn } from '@aztec/foundation/log'; +import { type DebugLogger, type LogFn } from '@aztec/foundation/log'; import { createCompatibleClient } from '../client.js'; diff --git a/yarn-project/cli/src/cmds/get_recipient.ts b/yarn-project/cli/src/cmds/get_recipient.ts index 270bbad9ac2..4ae6baac896 100644 --- a/yarn-project/cli/src/cmds/get_recipient.ts +++ b/yarn-project/cli/src/cmds/get_recipient.ts @@ -1,5 +1,5 @@ -import { AztecAddress } from '@aztec/aztec.js'; -import { DebugLogger, LogFn } from '@aztec/foundation/log'; +import { type AztecAddress } from '@aztec/aztec.js'; +import { type DebugLogger, type LogFn } from '@aztec/foundation/log'; import { createCompatibleClient } from '../client.js'; diff --git a/yarn-project/cli/src/cmds/get_recipients.ts b/yarn-project/cli/src/cmds/get_recipients.ts index 875b84b6038..bc091bc5dd6 100644 --- a/yarn-project/cli/src/cmds/get_recipients.ts +++ b/yarn-project/cli/src/cmds/get_recipients.ts @@ -1,4 +1,4 @@ -import { DebugLogger, LogFn } from '@aztec/foundation/log'; +import { type DebugLogger, type LogFn } from '@aztec/foundation/log'; import { createCompatibleClient } from '../client.js'; diff --git a/yarn-project/cli/src/cmds/get_tx_receipt.ts b/yarn-project/cli/src/cmds/get_tx_receipt.ts index beaa53e2f9b..6119f035aac 100644 --- a/yarn-project/cli/src/cmds/get_tx_receipt.ts +++ b/yarn-project/cli/src/cmds/get_tx_receipt.ts @@ -1,6 +1,6 @@ -import { TxHash } from '@aztec/aztec.js'; +import { type TxHash } from '@aztec/aztec.js'; import { JsonStringify } from '@aztec/foundation/json-rpc'; -import { DebugLogger, LogFn } from '@aztec/foundation/log'; +import { type DebugLogger, type LogFn } from '@aztec/foundation/log'; import { createCompatibleClient } from '../client.js'; diff --git a/yarn-project/cli/src/cmds/parse_parameter_struct.ts b/yarn-project/cli/src/cmds/parse_parameter_struct.ts index d8b29211d3a..15bc057ede9 100644 --- a/yarn-project/cli/src/cmds/parse_parameter_struct.ts +++ b/yarn-project/cli/src/cmds/parse_parameter_struct.ts @@ -1,6 +1,6 @@ -import { StructType } from '@aztec/foundation/abi'; +import { type StructType } from '@aztec/foundation/abi'; import { JsonStringify } from '@aztec/foundation/json-rpc'; -import { LogFn } from '@aztec/foundation/log'; +import { type LogFn } from '@aztec/foundation/log'; import { parseStructString } from '../encoding.js'; import { getContractArtifact } from '../utils.js'; diff --git a/yarn-project/cli/src/encoding.ts b/yarn-project/cli/src/encoding.ts index d05c90b894f..e10f843814d 100644 --- a/yarn-project/cli/src/encoding.ts +++ b/yarn-project/cli/src/encoding.ts @@ -1,4 +1,4 @@ -import { ABIParameter, AbiType, StructType } from '@aztec/foundation/abi'; +import { type ABIParameter, type AbiType, type StructType } from '@aztec/foundation/abi'; import { Fr } from '@aztec/foundation/fields'; /** diff --git a/yarn-project/cli/src/update/noir.ts b/yarn-project/cli/src/update/noir.ts index 0864293f4da..b18c38284b6 100644 --- a/yarn-project/cli/src/update/noir.ts +++ b/yarn-project/cli/src/update/noir.ts @@ -1,4 +1,4 @@ -import { LogFn } from '@aztec/foundation/log'; +import { type LogFn } from '@aztec/foundation/log'; import { parseNoirPackageConfig } from '@aztec/foundation/noir'; import TOML from '@iarna/toml'; @@ -6,7 +6,7 @@ import { readFile } from 'fs/promises'; import { join, relative, resolve } from 'path'; import { atomicUpdateFile, prettyPrintNargoToml } from '../utils.js'; -import { DependencyChanges } from './common.js'; +import { type DependencyChanges } from './common.js'; /** * Updates Aztec.nr dependencies diff --git a/yarn-project/cli/src/update/npm.ts b/yarn-project/cli/src/update/npm.ts index ae085e6dfe7..c18716ca474 100644 --- a/yarn-project/cli/src/update/npm.ts +++ b/yarn-project/cli/src/update/npm.ts @@ -1,13 +1,13 @@ -import { LogFn } from '@aztec/foundation/log'; +import { type LogFn } from '@aztec/foundation/log'; import { spawnSync } from 'child_process'; import { existsSync } from 'fs'; import { readFile } from 'fs/promises'; import { join, relative, resolve } from 'path'; -import { SemVer, parse } from 'semver'; +import { type SemVer, parse } from 'semver'; import { atomicUpdateFile } from '../utils.js'; -import { DependencyChanges } from './common.js'; +import { type DependencyChanges } from './common.js'; const deprecatedNpmPackages = new Set(['@aztec/cli', '@aztec/aztec-sandbox']); const npmDeprecationMessage = ` diff --git a/yarn-project/cli/src/update/update.ts b/yarn-project/cli/src/update/update.ts index 5c946f9953d..e2bc90f7947 100644 --- a/yarn-project/cli/src/update/update.ts +++ b/yarn-project/cli/src/update/update.ts @@ -1,11 +1,11 @@ /* eslint-disable jsdoc/require-jsdoc */ -import { LogFn } from '@aztec/foundation/log'; +import { type LogFn } from '@aztec/foundation/log'; import { relative, resolve } from 'path'; import { parse } from 'semver'; import { GITHUB_TAG_PREFIX } from '../github.js'; -import { DependencyChanges } from './common.js'; +import { type DependencyChanges } from './common.js'; import { updateAztecNr } from './noir.js'; import { getNewestVersion, updateAztecDeps, updateLockfile } from './npm.js'; From 78cdca1a3b3dde1bb2698d07f55093adb66430a0 Mon Sep 17 00:00:00 2001 From: PhilWindle Date: Wed, 29 May 2024 14:18:37 +0000 Subject: [PATCH 44/46] Review fixes --- yarn-project/cli/src/index.ts | 13 --- yarn-project/cli/src/update/common.ts | 16 --- yarn-project/cli/src/update/noir.ts | 57 ---------- yarn-project/cli/src/update/npm.ts | 154 -------------------------- yarn-project/cli/src/update/update.ts | 79 ------------- 5 files changed, 319 deletions(-) delete mode 100644 yarn-project/cli/src/update/common.ts delete mode 100644 yarn-project/cli/src/update/noir.ts delete mode 100644 yarn-project/cli/src/update/npm.ts delete mode 100644 yarn-project/cli/src/update/update.ts diff --git a/yarn-project/cli/src/index.ts b/yarn-project/cli/src/index.ts index b83f271a7a3..489ee5c1c23 100644 --- a/yarn-project/cli/src/index.ts +++ b/yarn-project/cli/src/index.ts @@ -650,18 +650,5 @@ export function getProgram(log: LogFn, debugLogger: DebugLogger): Command { computeSelector(functionSignature, log); }); - program - .command('update') - .description('Updates Nodejs and Noir dependencies') - .argument('[projectPath]', 'Path to the project directory', process.cwd()) - .option('--contract [paths...]', 'Paths to contracts to update dependencies', []) - .option('--aztec-version ', 'The version to update Aztec packages to. Defaults to latest', 'latest') - .addOption(pxeOption) - .action(async (projectPath: string, options) => { - const { update } = await import('./update/update.js'); - const { contract, aztecVersion, rpcUrl } = options; - await update(projectPath, contract, rpcUrl, aztecVersion, log); - }); - return program; } diff --git a/yarn-project/cli/src/update/common.ts b/yarn-project/cli/src/update/common.ts deleted file mode 100644 index 6041c1266e4..00000000000 --- a/yarn-project/cli/src/update/common.ts +++ /dev/null @@ -1,16 +0,0 @@ -/** - * Tracks changes to dependencies - */ -export type DependencyChanges = { - /** Which file was changed */ - file: string; - /** changes done to the file */ - dependencies: Array<{ - /** Name of the dependency being changed */ - name: string; - /** Previous version of the dependency */ - from: string; - /** New version of the dependency (after the update) */ - to: string; - }>; -}; diff --git a/yarn-project/cli/src/update/noir.ts b/yarn-project/cli/src/update/noir.ts deleted file mode 100644 index b18c38284b6..00000000000 --- a/yarn-project/cli/src/update/noir.ts +++ /dev/null @@ -1,57 +0,0 @@ -import { type LogFn } from '@aztec/foundation/log'; -import { parseNoirPackageConfig } from '@aztec/foundation/noir'; - -import TOML from '@iarna/toml'; -import { readFile } from 'fs/promises'; -import { join, relative, resolve } from 'path'; - -import { atomicUpdateFile, prettyPrintNargoToml } from '../utils.js'; -import { type DependencyChanges } from './common.js'; - -/** - * Updates Aztec.nr dependencies - * @param contractPath - Path to the contract to be updated - * @param tag - The tag to update to - * @param log - Logging function - */ -export async function updateAztecNr(contractPath: string, tag: string, log: LogFn): Promise { - const configFilepath = resolve(join(contractPath, 'Nargo.toml')); - const packageConfig = parseNoirPackageConfig(TOML.parse(await readFile(configFilepath, 'utf-8'))); - const changes: DependencyChanges = { - dependencies: [], - file: configFilepath, - }; - - log(`Updating Aztec.nr libraries to ${tag} in ${relative(process.cwd(), changes.file)}`); - for (const dep of Object.values(packageConfig.dependencies)) { - if (!('git' in dep)) { - continue; - } - - // remove trailing slash - const gitUrl = dep.git.toLowerCase().replace(/\/$/, ''); - if (gitUrl !== 'https://github.com/aztecprotocol/aztec-packages') { - continue; - } - - if (dep.tag !== tag) { - // show the Aztec.nr package name rather than the lib name - const dirParts = dep.directory?.split('/') ?? []; - changes.dependencies.push({ - name: dirParts.slice(-2).join('/'), - from: dep.tag, - to: tag, - }); - - dep.tag = tag; - dep.directory = dep.directory?.replace('yarn-project/', 'noir-projects/'); - } - } - - if (changes.dependencies.length > 0) { - const contents = prettyPrintNargoToml(packageConfig); - await atomicUpdateFile(configFilepath, contents); - } - - return changes; -} diff --git a/yarn-project/cli/src/update/npm.ts b/yarn-project/cli/src/update/npm.ts deleted file mode 100644 index c18716ca474..00000000000 --- a/yarn-project/cli/src/update/npm.ts +++ /dev/null @@ -1,154 +0,0 @@ -import { type LogFn } from '@aztec/foundation/log'; - -import { spawnSync } from 'child_process'; -import { existsSync } from 'fs'; -import { readFile } from 'fs/promises'; -import { join, relative, resolve } from 'path'; -import { type SemVer, parse } from 'semver'; - -import { atomicUpdateFile } from '../utils.js'; -import { type DependencyChanges } from './common.js'; - -const deprecatedNpmPackages = new Set(['@aztec/cli', '@aztec/aztec-sandbox']); -const npmDeprecationMessage = ` -The following packages have been deprecated and will no longer be updated on the npm registry: -${Array.from(deprecatedNpmPackages) - .map(pkg => ` - ${pkg}`) - .join('\n')} -Remove them from package.json -`; - -/** - * Looks up a package.json file and returns its contents - * @param projectPath - Path to Nodejs project - * @returns The parsed package.json - */ -export async function readPackageJson(projectPath: string): Promise<{ - /** dependencies */ - dependencies?: Record; - /** devDependencies */ - devDependencies?: Record; -}> { - const configFilepath = resolve(join(projectPath, 'package.json')); - const pkg = JSON.parse(await readFile(configFilepath, 'utf-8')); - - return pkg; -} - -/** - * Queries the npm registry for the latest version of a package - * @param packageName - The package to query - * @param distTag - The distribution tag - * @returns The latest version of the package on that distribution tag - */ -export async function getNewestVersion(packageName: string, distTag = 'latest'): Promise { - const url = new URL(packageName, 'https://registry.npmjs.org/'); - const response = await fetch(url); - if (!response.ok) { - throw new Error(`Failed to fetch ${url}`); - } - - const body = await response.json(); - const latestVersion = parse(body['dist-tags'][distTag]); - if (!latestVersion) { - throw new Error(`Failed to get latest version from registry`); - } - - return latestVersion; -} - -/** - * Updates a project's \@aztec/* dependencies to the specific version - * @param projectPath - Path to Nodejs project - * @param aztecVersion - The version to update to - * @returns True if the project was updated - */ -export async function updateAztecDeps( - projectPath: string, - aztecVersion: SemVer, - log: LogFn, -): Promise { - const pkg = await readPackageJson(projectPath); - const changes: DependencyChanges = { - file: resolve(join(projectPath, 'package.json')), - dependencies: [], - }; - - log(`Updating @aztec packages to ${aztecVersion} in ${relative(process.cwd(), changes.file)}`); - const version = aztecVersion.version; - - let detectedDeprecatedPackages = false; - - for (const depType of ['dependencies', 'devDependencies'] as const) { - const dependencies = pkg[depType]; - if (!dependencies) { - continue; - } - - for (const name of Object.keys(dependencies)) { - if (!name.startsWith('@aztec/')) { - continue; - } - - // different release schedule - if (name === '@aztec/aztec-ui') { - continue; - } - - if (deprecatedNpmPackages.has(name)) { - detectedDeprecatedPackages = true; - continue; - } - - if (dependencies[name] !== version) { - changes.dependencies.push({ - name, - from: dependencies[name], - to: version, - }); - - dependencies[name] = version; - } - } - } - - if (detectedDeprecatedPackages) { - log(npmDeprecationMessage); - } - - if (changes.dependencies.length > 0) { - const contents = JSON.stringify(pkg, null, 2) + '\n'; - await atomicUpdateFile(resolve(join(projectPath, 'package.json')), contents); - } - - return changes; -} - -/** - * Updates a project's yarn.lock or package-lock.json - * @param projectPath - Path to Nodejs project - */ -export function updateLockfile(projectPath: string, log: LogFn): void { - const isNpm = existsSync(resolve(join(projectPath, 'package-lock.json'))); - const isYarn = existsSync(resolve(join(projectPath, 'yarn.lock'))); - const isPnpm = existsSync(resolve(join(projectPath, 'pnpm-lock.yaml'))); - - if (isPnpm) { - spawnSync('pnpm', ['install'], { - cwd: projectPath, - stdio: 'inherit', - }); - } else if (isYarn) { - spawnSync('yarn', ['install'], { - cwd: projectPath, - stdio: 'inherit', - }); - } else if (isNpm) { - spawnSync('npm', ['install'], { - cwd: projectPath, - stdio: 'inherit', - }); - } else { - log(`No lockfile found in ${projectPath}. Skipping lockfile update...`); - } -} diff --git a/yarn-project/cli/src/update/update.ts b/yarn-project/cli/src/update/update.ts deleted file mode 100644 index e2bc90f7947..00000000000 --- a/yarn-project/cli/src/update/update.ts +++ /dev/null @@ -1,79 +0,0 @@ -/* eslint-disable jsdoc/require-jsdoc */ -import { type LogFn } from '@aztec/foundation/log'; - -import { relative, resolve } from 'path'; -import { parse } from 'semver'; - -import { GITHUB_TAG_PREFIX } from '../github.js'; -import { type DependencyChanges } from './common.js'; -import { updateAztecNr } from './noir.js'; -import { getNewestVersion, updateAztecDeps, updateLockfile } from './npm.js'; - -const AZTECJS_PACKAGE = '@aztec/aztec.js'; -const UPDATE_DOCS_URL = 'https://docs.aztec.network/developers/updating'; - -export async function update( - projectPath: string, - contracts: string[], - pxeUrl: string, - aztecVersion: string, - log: LogFn, -): Promise { - const targetAztecVersion = - aztecVersion === 'latest' ? await getNewestVersion(AZTECJS_PACKAGE, 'latest') : parse(aztecVersion); - - if (!targetAztecVersion) { - throw new Error(`Invalid aztec version ${aztecVersion}`); - } - - const projectDependencyChanges: DependencyChanges[] = []; - try { - const npmChanges = await updateAztecDeps(resolve(process.cwd(), projectPath), targetAztecVersion, log); - if (npmChanges.dependencies.length > 0) { - updateLockfile(projectPath, log); - } - - projectDependencyChanges.push(npmChanges); - } catch (err) { - if (err instanceof Error && 'code' in err && err.code === 'ENOENT') { - log(`No package.json found in ${projectPath}. Skipping npm update...`); - } else { - throw err; - } - } - - for (const contract of contracts) { - try { - projectDependencyChanges.push( - await updateAztecNr( - resolve(process.cwd(), projectPath, contract), - `${GITHUB_TAG_PREFIX}-v${targetAztecVersion.version}`, - log, - ), - ); - } catch (err) { - if (err instanceof Error && 'code' in err && err.code === 'ENOENT') { - log(`No Nargo.toml found in ${relative(process.cwd(), contract)}. Skipping...`); - } else { - throw err; - } - } - } - - log(`To update Docker containers follow instructions at ${UPDATE_DOCS_URL}`); - - projectDependencyChanges.forEach(changes => { - printChanges(changes, log); - }); -} - -function printChanges(changes: DependencyChanges, log: LogFn): void { - log(`\nIn ${relative(process.cwd(), changes.file)}:`); - if (changes.dependencies.length === 0) { - log(' No changes'); - } else { - changes.dependencies.forEach(({ name, from, to }) => { - log(` Updated ${name} from ${from} to ${to}`); - }); - } -} From 7a560112d4acb6dac27cf6f7a1fb206f4f2c4259 Mon Sep 17 00:00:00 2001 From: PhilWindle Date: Wed, 29 May 2024 14:18:47 +0000 Subject: [PATCH 45/46] Fixes --- yarn-project/cli/src/cmds/call.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/yarn-project/cli/src/cmds/call.ts b/yarn-project/cli/src/cmds/call.ts index aa71fe84a69..63e19db0cb4 100644 --- a/yarn-project/cli/src/cmds/call.ts +++ b/yarn-project/cli/src/cmds/call.ts @@ -1,4 +1,4 @@ -import { type AztecAddress, ContractFunctionInteraction, SignerlessWallet, Wallet } from '@aztec/aztec.js'; +import { type AztecAddress, ContractFunctionInteraction, SignerlessWallet } from '@aztec/aztec.js'; import { type DebugLogger, type LogFn } from '@aztec/foundation/log'; import { format } from 'util'; From 3e07d02c8138acaac3ca1bf86021d3eb3ac42d65 Mon Sep 17 00:00:00 2001 From: Alex Gherghisan Date: Wed, 29 May 2024 15:01:25 +0000 Subject: [PATCH 46/46] fix: field encoding --- yarn-project/foundation/src/abi/encoder.test.ts | 6 ++---- yarn-project/foundation/src/fields/fields.ts | 14 ++++++++++---- 2 files changed, 12 insertions(+), 8 deletions(-) diff --git a/yarn-project/foundation/src/abi/encoder.test.ts b/yarn-project/foundation/src/abi/encoder.test.ts index 82c2d569ac7..b9a54c993fa 100644 --- a/yarn-project/foundation/src/abi/encoder.test.ts +++ b/yarn-project/foundation/src/abi/encoder.test.ts @@ -167,7 +167,7 @@ describe('abi/encoder', () => { }; const args = ['garbage']; - expect(() => encodeArguments(testFunctionAbi, args)).toThrow('Invalid argument "garbage" of type field'); + expect(() => encodeArguments(testFunctionAbi, args)).toThrow('Invalid hex-encoded string: "garbage"'); }); it('throws when passing string argument as integer', () => { @@ -191,9 +191,7 @@ describe('abi/encoder', () => { returnTypes: [], }; const args = ['garbage']; - expect(() => encodeArguments(testFunctionAbi, args)).toThrow( - `Type 'string' with value 'garbage' passed to BaseField ctor.`, - ); + expect(() => encodeArguments(testFunctionAbi, args)).toThrow(`Cannot convert garbage to a BigInt`); }); it('throws when passing object argument as field', () => { diff --git a/yarn-project/foundation/src/fields/fields.ts b/yarn-project/foundation/src/fields/fields.ts index f8d31429f58..9c6874ff648 100644 --- a/yarn-project/foundation/src/fields/fields.ts +++ b/yarn-project/foundation/src/fields/fields.ts @@ -174,8 +174,14 @@ function random(f: DerivedField): T { /** * Constructs a field from a 0x prefixed hex string. */ -function fromString(buf: string, f: DerivedField) { - const buffer = Buffer.from(buf.replace(/^0x/i, ''), 'hex'); +function fromHexString(buf: string, f: DerivedField) { + const withoutPrefix = buf.replace(/^0x/i, ''); + const buffer = Buffer.from(withoutPrefix, 'hex'); + + if (buffer.length === 0 && withoutPrefix.length > 0) { + throw new Error(`Invalid hex-encoded string: "${buf}"`); + } + return new f(buffer); } @@ -228,7 +234,7 @@ export class Fr extends BaseField { } static fromString(buf: string) { - return fromString(buf, Fr); + return fromHexString(buf, Fr); } /** Arithmetic */ @@ -329,7 +335,7 @@ export class Fq extends BaseField { } static fromString(buf: string) { - return fromString(buf, Fq); + return fromHexString(buf, Fq); } static fromHighLow(high: Fr, low: Fr): Fq {