diff --git a/build_manifest.json b/build_manifest.json index 2d55f73f907..c64447145c6 100644 --- a/build_manifest.json +++ b/build_manifest.json @@ -26,52 +26,72 @@ }, "bb.js": { "buildDir": "barretenberg/ts", - "dependencies": ["barretenberg-wasm-linux-clang"] + "dependencies": [ + "barretenberg-wasm-linux-clang" + ] }, "barretenberg-acir-tests-bb": { "buildDir": "barretenberg/acir_tests", "dockerfile": "Dockerfile.bb", - "dependencies": ["barretenberg-x86_64-linux-clang-assert"] + "dependencies": [ + "barretenberg-x86_64-linux-clang-assert" + ] }, "barretenberg-acir-tests-bb.js": { "buildDir": "barretenberg/acir_tests", "dockerfile": "Dockerfile.bb.js", - "dependencies": ["bb.js"] + "dependencies": [ + "bb.js" + ] }, "circuits-wasm-linux-clang": { "buildDir": "circuits/cpp", "dockerfile": "dockerfiles/Dockerfile.wasm-linux-clang", "rebuildPatterns": ".rebuild_patterns", - "dependencies": ["barretenberg-wasm-linux-clang"] + "dependencies": [ + "barretenberg-wasm-linux-clang" + ] }, "circuits-wasm-linux-clang-assert": { "buildDir": "circuits/cpp", "dockerfile": "dockerfiles/Dockerfile.wasm-linux-clang-assert", "rebuildPatterns": ".rebuild_patterns", - "dependencies": ["barretenberg-wasm-linux-clang"] + "dependencies": [ + "barretenberg-wasm-linux-clang" + ] }, "circuits-x86_64-linux-clang-tidy": { "buildDir": "circuits/cpp", "dockerfile": "dockerfiles/Dockerfile.x86_64-linux-clang-tidy", "rebuildPatterns": ".rebuild_patterns", - "dependencies": ["barretenberg-x86_64-linux-clang"] + "dependencies": [ + "barretenberg-x86_64-linux-clang" + ] }, "circuits-x86_64-linux-clang": { "buildDir": "circuits/cpp", "dockerfile": "dockerfiles/Dockerfile.x86_64-linux-clang", "rebuildPatterns": ".rebuild_patterns", - "dependencies": ["barretenberg-x86_64-linux-clang"] + "dependencies": [ + "barretenberg-x86_64-linux-clang" + ] }, "circuits-x86_64-linux-clang-assert": { "buildDir": "circuits/cpp", "dockerfile": "dockerfiles/Dockerfile.x86_64-linux-clang-assert", "rebuildPatterns": ".rebuild_patterns", - "dependencies": ["barretenberg-x86_64-linux-clang"] + "dependencies": [ + "barretenberg-x86_64-linux-clang" + ] }, "docs": { "buildDir": ".", "dockerfile": "docs/Dockerfile", - "rebuildPatterns": ["^docs/", "^.*.cpp$", "^.*.ts$"] + "rebuildPatterns": [ + "^docs/", + "^.*.cpp$", + "^.*.ts$" + ] }, "l1-contracts": { "buildDir": "l1-contracts" @@ -79,7 +99,10 @@ "l1-artifacts": { "buildDir": "yarn-project", "projectDir": "yarn-project/l1-artifacts", - "rebuildPatterns": ["^l1-contracts/", "^yarn-project/l1-artifacts/"], + "rebuildPatterns": [ + "^l1-contracts/", + "^yarn-project/l1-artifacts/" + ], "dependencies": [] }, "yarn-project-base": { @@ -94,7 +117,10 @@ "^yarn-project/yarn-project-base/", "^yarn-project/yarn.lock" ], - "dependencies": ["circuits-wasm-linux-clang", "l1-contracts"] + "dependencies": [ + "circuits-wasm-linux-clang", + "l1-contracts" + ] }, "acir-simulator": { "buildDir": "yarn-project", @@ -212,4 +238,4 @@ "buildDir": "yarn-project", "projectDir": "yarn-project/world-state" } -} +} \ No newline at end of file diff --git a/circuits/cpp/CMakePresets.json b/circuits/cpp/CMakePresets.json index 556a8444fcc..583fa90a9b2 100644 --- a/circuits/cpp/CMakePresets.json +++ b/circuits/cpp/CMakePresets.json @@ -308,4 +308,4 @@ "inherits": "wasm" } ] -} +} \ No newline at end of file diff --git a/yarn-project/canary/Dockerfile b/yarn-project/canary/Dockerfile index fa9498350af..3411ed2ca0a 100644 --- a/yarn-project/canary/Dockerfile +++ b/yarn-project/canary/Dockerfile @@ -1,16 +1,29 @@ -FROM node:18-alpine +FROM 278380418400.dkr.ecr.us-east-2.amazonaws.com/yarn-project-base AS builder + RUN apk update && apk add --no-cache udev ttf-freefont chromium curl jq bash ENV CHROME_BIN="/usr/bin/chromium-browser" PUPPETEER_SKIP_CHROMIUM_DOWNLOAD="true" -ARG COMMIT_TAG="" +ARG COMMIT_TAG="0.7.5" + +COPY . . + +# Setup & Build end-to-end package +WORKDIR /usr/src/yarn-project/end-to-end + +# Extract canary @aztec dependencies +RUN ../canary/scripts/extract_packages.sh ../canary/package.json > ./target_pkgs.txt +# Update end-to-end dependencies with target version +RUN ./scripts/setup_canary.sh $COMMIT_TAG ./target_pkgs.txt +RUN rm ./target_pkgs.txt +RUN yarn && yarn build -#Build canary -WORKDIR /usr/src/ -COPY ./canary . +# Build canary package +WORKDIR /usr/src/yarn-project/canary RUN ./scripts/update_packages.sh $COMMIT_TAG RUN yarn && yarn build -RUN cp node_modules/@aztec/aztec.js/dest/main.js src/web/ -RUN cp node_modules/@aztec/circuits.js/resources/aztec3-circuits.wasm src/web/ +# Copy web artifacts for browser test +RUN cp ./node_modules/@aztec/aztec.js/dest/main.js src/web/ +RUN cp ./node_modules/@aztec/circuits.js/resources/aztec3-circuits.wasm src/web/ ENTRYPOINT ["yarn", "test"] \ No newline at end of file diff --git a/yarn-project/canary/package.json b/yarn-project/canary/package.json index adf9534ab60..0a792977fb4 100644 --- a/yarn-project/canary/package.json +++ b/yarn-project/canary/package.json @@ -23,7 +23,7 @@ "dependencies": { "@aztec/aztec.js": "workspace:^", "@aztec/cli": "workspace:^", - "@aztec/foundation": "workspace:^", + "@aztec/end-to-end": "workspace:^", "@aztec/l1-artifacts": "workspace:^", "@aztec/noir-contracts": "workspace:^", "@jest/globals": "^29.5.0", @@ -33,8 +33,6 @@ "jest": "^29.5.0", "koa": "^2.14.2", "koa-static": "^5.0.0", - "puppeteer": "^21.1.0", - "string-argv": "^0.3.2", "ts-jest": "^29.1.0", "ts-node": "^10.9.1", "tslib": "^2.4.0", diff --git a/yarn-project/canary/scripts/extract_packages.sh b/yarn-project/canary/scripts/extract_packages.sh new file mode 100755 index 00000000000..24499e23375 --- /dev/null +++ b/yarn-project/canary/scripts/extract_packages.sh @@ -0,0 +1,11 @@ +#!/bin/bash + +FILE=$1 + +# Capture the output of the jq command in a Bash array +mapfile -t TARGET_PKGS < <(jq -r '.dependencies | keys[] | select(startswith("@aztec/") and . != "@aztec/end-to-end")' $FILE) + +# Loop through the array and print each element on a new line +for pkg in "${TARGET_PKGS[@]}"; do + echo "$pkg" +done \ No newline at end of file diff --git a/yarn-project/canary/scripts/update_packages.sh b/yarn-project/canary/scripts/update_packages.sh index 0b5a3c59b43..dac457c7248 100755 --- a/yarn-project/canary/scripts/update_packages.sh +++ b/yarn-project/canary/scripts/update_packages.sh @@ -2,21 +2,21 @@ set -eu COMMIT_TAG=$1 -if [ -z "$COMMIT_TAG" ]; then - echo "No commit tag provided." +if [ -z "$COMMIT_TAG" ]; then + echo "No commit tag provided." exit 0 fi VERSION=$(npx semver $COMMIT_TAG) -if [ -z "$VERSION" ]; then - echo "$COMMIT_TAG is not a semantic version." +if [ -z "$VERSION" ]; then + echo "$COMMIT_TAG is not a semantic version." exit 1 fi echo "Updating Aztec dependencies to version $VERSION" TMP=$(mktemp) -for PKG in $(jq --raw-output ".dependencies | keys[] | select(contains(\"@aztec/\"))" package.json); do +for PKG in $(jq --raw-output ".dependencies | keys[] | select(contains(\"@aztec/\") and (. != \"@aztec/end-to-end\"))" package.json); do jq --arg v $VERSION ".dependencies[\"$PKG\"] = \$v" package.json > $TMP && mv $TMP package.json done diff --git a/yarn-project/canary/src/aztec_js_browser.test.ts b/yarn-project/canary/src/aztec_js_browser.test.ts index e3a001651d1..3db3febdb67 100644 --- a/yarn-project/canary/src/aztec_js_browser.test.ts +++ b/yarn-project/canary/src/aztec_js_browser.test.ts @@ -1,219 +1,25 @@ -/* eslint-disable no-console */ -import * as AztecJs from '@aztec/aztec.js'; -import { PrivateTokenContractAbi } from '@aztec/noir-contracts/artifacts'; +import { createDebugLogger, fileURLToPath } from '@aztec/aztec.js'; +import { browserTestSuite } from '@aztec/end-to-end'; -import { Server } from 'http'; import Koa from 'koa'; import serve from 'koa-static'; import path, { dirname } from 'path'; -import { Browser, Page, launch } from 'puppeteer'; -declare global { - interface Window { - AztecJs: typeof AztecJs; - } -} - -const __filename = AztecJs.fileURLToPath(import.meta.url); +const __filename = fileURLToPath(import.meta.url); const __dirname = dirname(__filename); +const PORT = 3000; -const PORT = 3033; - -const { SANDBOX_URL } = process.env; - -const conditionalDescribe = () => (SANDBOX_URL ? describe : describe.skip); -const privKey = AztecJs.GrumpkinScalar.random(); - -/** - * This test is a bit of a special case as it's relying on sandbox and web browser and not only on anvil and node.js. - * To run the test, do the following: - * 1) Build the whole repository, - * 2) go to `yarn-project/aztec.js` and build the web packed package with `yarn build:web`, - * 3) start anvil: `anvil`, - * 4) open new terminal and optionally set the more verbose debug level: `DEBUG=aztec:*`, - * 5) go to the sandbox dir `yarn-project/aztec-sandbox` and run `yarn start`, - * 6) open new terminal and export the sandbox URL: `export SANDBOX_URL='http://localhost:8080'`, - * 7) go to `yarn-project/end-to-end` and run the test: `yarn test aztec_js_browser` - * - * NOTE: If you see aztec-sandbox logs spammed with unexpected logs there is probably a chrome process with a webpage - * unexpectedly running in the background. Kill it with `killall chrome` - */ -conditionalDescribe()('e2e_aztec.js_browser', () => { - const initialBalance = 33n; - const transferAmount = 3n; - - let contractAddress: AztecJs.AztecAddress; - - let logger: AztecJs.DebugLogger; - let pageLogger: AztecJs.DebugLogger; - let app: Koa; - let testClient: AztecJs.AztecRPC; - let server: Server; - - let browser: Browser; - let page: Page; - - beforeAll(async () => { - testClient = AztecJs.createAztecRpcClient(SANDBOX_URL!); - await AztecJs.waitForSandbox(testClient); - const pathRes = path.resolve(__dirname, './web'); - app = new Koa(); - app.use(serve(pathRes)); - server = app.listen(PORT, () => { - logger(`Server started at http://localhost:${PORT}`); - }); - - logger = AztecJs.createDebugLogger('aztec:aztec.js:web'); - pageLogger = AztecJs.createDebugLogger('aztec:aztec.js:web:page'); +const logger = createDebugLogger('aztec:canary_aztec.js:web'); +const pageLogger = createDebugLogger('aztec:canary_aztec.js:web:page'); - browser = await launch({ - executablePath: process.env.CHROME_BIN, - headless: 'new', - args: [ - '--allow-file-access-from-files', - '--no-sandbox', - '--headless', - '--disable-web-security', - '--disable-features=IsolateOrigins', - '--disable-site-isolation-trials', - '--disable-gpu', - '--disable-dev-shm-usage', - '--disk-cache-dir=/dev/null', - ], - }); - page = await browser.newPage(); - page.on('console', msg => { - pageLogger('PAGE MSG', msg.text()); - }); - page.on('pageerror', err => { - pageLogger.error('PAGE ERROR', err.toString()); - }); - await page.goto(`http://localhost:${PORT}`); - }, 120_000); - - afterAll(async () => { - await browser.close(); - server.close(); - }); - - it('Loads Aztec.js in the browser', async () => { - const generatePublicKeyExists = await page.evaluate(() => { - const { generatePublicKey } = window.AztecJs; - return typeof generatePublicKey === 'function'; - }); - expect(generatePublicKeyExists).toBe(true); +const setupApp = () => { + const app = new Koa(); + app.use(serve(path.resolve(__dirname, './web'))); + const server = app.listen(PORT, () => { + logger(`Server started at http://localhost:${PORT}`); }); - it('Creates an account', async () => { - const result = await page.evaluate( - async (rpcUrl, privateKeyString) => { - const { GrumpkinScalar, createAztecRpcClient, makeFetch, getUnsafeSchnorrAccount } = window.AztecJs; - const client = createAztecRpcClient(rpcUrl!); - const privateKey = GrumpkinScalar.fromString(privateKeyString); - const account = getUnsafeSchnorrAccount(client, privateKey); - await account.waitDeploy(); - const completeAddress = await account.getCompleteAddress(); - const addressString = completeAddress.address.toString(); - console.log(`Created Account: ${addressString}`); - return addressString; - }, - SANDBOX_URL, - privKey.toString(), - ); - const accounts = await testClient.getRegisteredAccounts(); - const stringAccounts = accounts.map(acc => acc.address.toString()); - expect(stringAccounts.includes(result)).toBeTruthy(); - }, 15_000); - - it('Deploys Private Token contract', async () => { - await deployPrivateTokenContract(); - }, 30_000); - - it("Gets the owner's balance", async () => { - const result = await page.evaluate( - async (rpcUrl, contractAddress, PrivateTokenContractAbi) => { - const { Contract, AztecAddress, createAztecRpcClient, makeFetch } = window.AztecJs; - const client = createAztecRpcClient(rpcUrl!); - const owner = (await client.getRegisteredAccounts())[0].address; - const [wallet] = await AztecJs.getSandboxAccountsWallets(client); - const contract = await Contract.at(AztecAddress.fromString(contractAddress), PrivateTokenContractAbi, wallet); - const balance = await contract.methods.getBalance(owner).view({ from: owner }); - console.log(`Owner's balance: ${balance}`); - return balance; - }, - SANDBOX_URL, - (await getPrivateTokenAddress()).toString(), - PrivateTokenContractAbi, - ); - logger('Owner balance:', result); - expect(result).toEqual(initialBalance); - }); - - it('Sends a transfer TX', async () => { - const result = await page.evaluate( - async (rpcUrl, contractAddress, transferAmount, PrivateTokenContractAbi) => { - console.log(`Starting transfer tx`); - const { AztecAddress, Contract, createAztecRpcClient, makeFetch } = window.AztecJs; - const client = createAztecRpcClient(rpcUrl!); - const accounts = await client.getRegisteredAccounts(); - const owner = accounts[0].address; - const receiver = accounts[1].address; - const [wallet] = await AztecJs.getSandboxAccountsWallets(client); - const contract = await Contract.at(AztecAddress.fromString(contractAddress), PrivateTokenContractAbi, wallet); - await contract.methods.transfer(transferAmount, receiver).send().wait(); - console.log(`Transferred ${transferAmount} tokens to new Account`); - const receiverBalance = await contract.methods.getBalance(receiver).view({ from: receiver }); - console.log(`Receiver's balance is now: ${receiverBalance}`); - const senderBalance = await contract.methods.getBalance(owner).view({ from: owner }); - console.log(`Updated sender balance: ${senderBalance}`); - return receiverBalance; - }, - SANDBOX_URL, - (await getPrivateTokenAddress()).toString(), - transferAmount, - PrivateTokenContractAbi, - ); - expect(result).toEqual(transferAmount); - }, 60_000); - - const deployPrivateTokenContract = async () => { - const txHash = await page.evaluate( - async (rpcUrl, privateKeyString, initialBalance, PrivateTokenContractAbi) => { - const { GrumpkinScalar, DeployMethod, createAztecRpcClient, makeFetch, getUnsafeSchnorrAccount } = - window.AztecJs; - const client = createAztecRpcClient(rpcUrl!); - let accounts = await client.getRegisteredAccounts(); - if (accounts.length === 0) { - // This test needs an account for deployment. We create one in case there is none available in the RPC server. - const privateKey = GrumpkinScalar.fromString(privateKeyString); - await getUnsafeSchnorrAccount(client, privateKey).waitDeploy(); - accounts = await client.getRegisteredAccounts(); - } - const owner = accounts[0]; - const tx = new DeployMethod(owner.publicKey, client, PrivateTokenContractAbi, [ - initialBalance, - owner.address, - ]).send(); - await tx.wait(); - const receipt = await tx.getReceipt(); - console.log(`Contract Deployed: ${receipt.contractAddress}`); - return receipt.txHash.toString(); - }, - SANDBOX_URL, - privKey.toString(), - initialBalance, - PrivateTokenContractAbi, - ); - - const txResult = await testClient.getTxReceipt(AztecJs.TxHash.fromString(txHash)); - expect(txResult.status).toEqual(AztecJs.TxStatus.MINED); - contractAddress = txResult.contractAddress!; - }; + return server; +}; - const getPrivateTokenAddress = async () => { - if (!contractAddress) { - await deployPrivateTokenContract(); - } - return contractAddress; - }; -}); +browserTestSuite(setupApp, pageLogger); diff --git a/yarn-project/canary/src/cli.test.ts b/yarn-project/canary/src/cli.test.ts index e5d73e2a51d..309dddca662 100644 --- a/yarn-project/canary/src/cli.test.ts +++ b/yarn-project/canary/src/cli.test.ts @@ -1,23 +1,9 @@ -import { - AztecAddress, - AztecRPC, - CompleteAddress, - createAztecRpcClient, - createDebugLogger, - makeFetch, - waitForSandbox, -} from '@aztec/aztec.js'; -import { getProgram } from '@aztec/cli'; - -import stringArgv from 'string-argv'; -import { format } from 'util'; +import { createAztecRpcClient, createDebugLogger, makeFetch, waitForSandbox } from '@aztec/aztec.js'; +import { cliTestSuite } from '@aztec/end-to-end'; const { SANDBOX_URL = 'http://localhost:8080' } = process.env; -const debug = createDebugLogger('aztec:e2e_cli'); - -const INITIAL_BALANCE = 33000; -const TRANSFER_BALANCE = 3000; +const debug = createDebugLogger('aztec:canary_cli'); const setupRPC = async () => { const aztecRpcClient = createAztecRpcClient(SANDBOX_URL, makeFetch([1, 2, 3, 4, 5], true)); @@ -25,154 +11,4 @@ const setupRPC = async () => { return aztecRpcClient; }; -describe('CLI canary', () => { - let cli: ReturnType; - let aztecRpcClient: AztecRPC; - let existingAccounts: CompleteAddress[]; - let contractAddress: AztecAddress; - let log: (...args: any[]) => void; - - // All logs emitted by the cli will be collected here, and reset between tests - const logs: string[] = []; - - beforeAll(async () => { - aztecRpcClient = await setupRPC(); - log = (...args: any[]) => { - logs.push(format(...args)); - debug(...args); - }; - }, 60_000); - - // in order to run the same command twice, we need to create a new CLI instance - const resetCli = () => { - cli = getProgram(log, debug); - }; - - beforeEach(() => { - logs.splice(0); - resetCli(); - }); - - // Run a command on the CLI - const run = (cmd: string, addRpcUrl = true) => { - const args = stringArgv(cmd, 'node', 'dest/bin/index.js'); - if (addRpcUrl) { - args.push('--rpc-url', SANDBOX_URL); - } - return cli.parseAsync(args); - }; - - // Returns first match across all logs collected so far - const findInLogs = (regex: RegExp) => { - for (const log of logs) { - const match = regex.exec(log); - if (match) return match; - } - }; - - const findMultipleInLogs = (regex: RegExp) => { - const matches = []; - for (const log of logs) { - const match = regex.exec(log); - if (match) matches.push(match); - } - return matches; - }; - - const clearLogs = () => { - logs.splice(0); - }; - - it('creates & retrieves an account', async () => { - existingAccounts = await aztecRpcClient.getRegisteredAccounts(); - debug('Create an account'); - await run(`create-account`); - const foundAddress = findInLogs(/Address:\s+(?
0x[a-fA-F0-9]+)/)?.groups?.address; - expect(foundAddress).toBeDefined(); - const newAddress = AztecAddress.fromString(foundAddress!); - - const accountsAfter = await aztecRpcClient.getRegisteredAccounts(); - const expectedAccounts = [...existingAccounts.map(a => a.address), newAddress]; - expect(accountsAfter.map(a => a.address)).toEqual(expectedAccounts); - const newCompleteAddress = accountsAfter[accountsAfter.length - 1]; - - // Test get-accounts - debug('Check that account was added to the list of accs in RPC'); - await run('get-accounts'); - const fetchedAddresses = findMultipleInLogs(/Address:\s+(?
0x[a-fA-F0-9]+)/); - const foundFetchedAddress = fetchedAddresses.find(match => match.groups?.address === newAddress.toString()); - expect(foundFetchedAddress).toBeDefined(); - - // Test get-account - debug('Check we can retrieve the specific account'); - clearLogs(); - await run(`get-account ${newAddress.toString()}`); - const fetchedAddress = findInLogs(/Public Key:\s+(?
0x[a-fA-F0-9]+)/)?.groups?.address; - expect(fetchedAddress).toEqual(newCompleteAddress.publicKey.toString()); - }, 30_000); - - it('deploys a contract & sends transactions', async () => { - // generate a private key - debug('Create an account using a private key'); - await run('generate-private-key', false); - const privKey = findInLogs(/Private\sKey:\s+0x(?[a-fA-F0-9]+)/)?.groups?.privKey; - expect(privKey).toHaveLength(64); - await run(`create-account --private-key ${privKey}`); - const foundAddress = findInLogs(/Address:\s+(?
0x[a-fA-F0-9]+)/)?.groups?.address; - expect(foundAddress).toBeDefined(); - const ownerAddress = AztecAddress.fromString(foundAddress!); - - debug('Deploy Private Token Contract using created account.'); - await run(`deploy PrivateTokenContractAbi --args ${INITIAL_BALANCE} ${ownerAddress} --salt 0`); - const loggedAddress = findInLogs(/Contract\sdeployed\sat\s+(?
0x[a-fA-F0-9]+)/)?.groups?.address; - expect(loggedAddress).toBeDefined(); - contractAddress = AztecAddress.fromString(loggedAddress!); - - const deployedContract = await aztecRpcClient.getContractData(contractAddress); - expect(deployedContract?.contractAddress).toEqual(contractAddress); - - debug('Check contract can be found in returned address'); - await run(`check-deploy -ca ${loggedAddress}`); - const checkResult = findInLogs(/Contract\sfound\sat\s+(?
0x[a-fA-F0-9]+)/)?.groups?.address; - expect(checkResult).toEqual(deployedContract?.contractAddress.toString()); - - // clear logs - clearLogs(); - await run(`get-contract-data ${loggedAddress}`); - const contractDataAddress = findInLogs(/Address:\s+(?
0x[a-fA-F0-9]+)/)?.groups?.address; - expect(contractDataAddress).toEqual(deployedContract?.contractAddress.toString()); - - debug("Check owner's balance"); - await run( - `call getBalance --args ${ownerAddress} --contract-abi PrivateTokenContractAbi --contract-address ${contractAddress.toString()}`, - ); - const balance = findInLogs(/View\sresult:\s+(?\S+)/)?.groups?.data; - expect(balance!).toEqual(`${BigInt(INITIAL_BALANCE).toString()}n`); - - debug('Transfer some tokens'); - const existingAccounts = await aztecRpcClient.getRegisteredAccounts(); - // ensure we pick a different acc - const receiver = existingAccounts.find(acc => acc.address.toString() !== ownerAddress.toString()); - - await run( - `send transfer --args ${TRANSFER_BALANCE} ${receiver?.address.toString()} --contract-address ${contractAddress.toString()} --contract-abi PrivateTokenContractAbi --private-key ${privKey}`, - ); - const txHash = findInLogs(/Transaction\shash:\s+(?\S+)/)?.groups?.txHash; - - debug('Check the transfer receipt'); - await run(`get-tx-receipt ${txHash}`); - const txResult = findInLogs(/Transaction receipt:\s*(?[\s\S]*?\})/)?.groups?.txHash; - const parsedResult = JSON.parse(txResult!); - expect(parsedResult.txHash).toEqual(txHash); - expect(parsedResult.status).toEqual('mined'); - debug("Check Receiver's balance"); - // Reset CLI as we're calling getBalance again - resetCli(); - clearLogs(); - await run( - `call getBalance --args ${receiver?.address.toString()} --contract-abi PrivateTokenContractAbi --contract-address ${contractAddress.toString()}`, - ); - const receiverBalance = findInLogs(/View\sresult:\s+(?\S+)/)?.groups?.data; - expect(receiverBalance).toEqual(`${BigInt(TRANSFER_BALANCE).toString()}n`); - }, 60_000); -}); +cliTestSuite('CLI Canary', setupRPC, () => Promise.resolve(), debug, SANDBOX_URL); diff --git a/yarn-project/canary/tsconfig.json b/yarn-project/canary/tsconfig.json index 3e6bccafeb4..36f2fd7fa21 100644 --- a/yarn-project/canary/tsconfig.json +++ b/yarn-project/canary/tsconfig.json @@ -26,7 +26,7 @@ "path": "../cli" }, { - "path": "../foundation" + "path": "../end-to-end" }, { "path": "../l1-artifacts" diff --git a/yarn-project/end-to-end/scripts/setup_canary.sh b/yarn-project/end-to-end/scripts/setup_canary.sh new file mode 100755 index 00000000000..2f519f6405f --- /dev/null +++ b/yarn-project/end-to-end/scripts/setup_canary.sh @@ -0,0 +1,52 @@ +#!/bin/bash +set -eu + +COMMIT_TAG=$1 +TARGET_PKGS_FILE=$2 + +# Check if file exists and read it into an array +if [ -f "$TARGET_PKGS_FILE" ]; then + mapfile -t TARGET_PKGS < <(cat "$TARGET_PKGS_FILE") + echo "Loaded array:" + for i in "${TARGET_PKGS[@]}"; do + echo "$i" + done +else + echo "File $TARGET_PKGS_FILE does not exist." +fi + +if [ -z "$COMMIT_TAG" ]; then + echo "No commit tag provided." + exit 0 +fi + +VERSION=$(npx semver $COMMIT_TAG) +if [ -z "$VERSION" ]; then + echo "$COMMIT_TAG is not a semantic version." + exit 1 +fi + +echo "Removing all files & folders that aren't needed for canary tests" +TARGET_DIR="./src" +cd "$TARGET_DIR" + +# Loop through all files and folders in the directory +for item in $(ls -A); do + if [[ "$item" != "index.ts" && "$item" != "canary" ]]; then + # Remove the item (either file or folder) + rm -rf "$item" + fi +done +cd .. + +echo "Updating external Aztec dependencies to version $VERSION" + +# Packages that are publically available in npm +# TARGET_PKGS=("@aztec/aztec.js" "@aztec/cli" "@aztec/l1-artifacts" "@aztec/noir-contracts") + +TMP=$(mktemp) +for PKG in "${TARGET_PKGS[@]}"; do + jq --arg v $VERSION ".dependencies[\"$PKG\"] = \$v" package.json > $TMP && mv $TMP package.json +done + +jq ".references = []" tsconfig.json > $TMP && mv $TMP tsconfig.json diff --git a/yarn-project/end-to-end/src/canary/browser.ts b/yarn-project/end-to-end/src/canary/browser.ts new file mode 100644 index 00000000000..c455d9e95ea --- /dev/null +++ b/yarn-project/end-to-end/src/canary/browser.ts @@ -0,0 +1,203 @@ +/* eslint-disable no-console */ +import * as AztecJs from '@aztec/aztec.js'; +import { PrivateTokenContractAbi } from '@aztec/noir-contracts/artifacts'; + +import { Server } from 'http'; +import Koa from 'koa'; +import serve from 'koa-static'; +import path, { dirname } from 'path'; +import { Browser, Page, launch } from 'puppeteer'; + +declare global { + /** + * Helper interface to declare aztec.js within browser context. + */ + interface Window { + /** + * The aztec.js library. + */ + AztecJs: typeof AztecJs; + } +} + +const __filename = AztecJs.fileURLToPath(import.meta.url); +const __dirname = dirname(__filename); + +const PORT = 3000; + +const { SANDBOX_URL } = process.env; + +const conditionalDescribe = () => (SANDBOX_URL ? describe : describe.skip); +const privKey = AztecJs.GrumpkinScalar.random(); + +export const browserTestSuite = (setup: () => Server, pageLogger: AztecJs.DebugLogger) => + conditionalDescribe()('e2e_aztec.js_browser', () => { + const initialBalance = 33n; + const transferAmount = 3n; + + let contractAddress: AztecJs.AztecAddress; + + let app: Koa; + let testClient: AztecJs.AztecRPC; + let server: Server; + + let browser: Browser; + let page: Page; + + beforeAll(async () => { + server = setup(); + testClient = AztecJs.createAztecRpcClient(SANDBOX_URL!); + await AztecJs.waitForSandbox(testClient); + + app = new Koa(); + app.use(serve(path.resolve(__dirname, './web'))); + + browser = await launch({ + executablePath: process.env.CHROME_BIN, + headless: 'new', + args: [ + '--allow-file-access-from-files', + '--no-sandbox', + '--headless', + '--disable-web-security', + '--disable-features=IsolateOrigins', + '--disable-site-isolation-trials', + '--disable-gpu', + '--disable-dev-shm-usage', + '--disk-cache-dir=/dev/null', + ], + }); + page = await browser.newPage(); + page.on('console', msg => { + pageLogger(msg.text()); + }); + page.on('pageerror', err => { + pageLogger.error(err.toString()); + }); + await page.goto(`http://localhost:${PORT}/index.html`); + }, 120_000); + + afterAll(async () => { + await browser.close(); + server.close(); + }); + + it('Loads Aztec.js in the browser', async () => { + const generatePublicKeyExists = await page.evaluate(() => { + const { generatePublicKey } = window.AztecJs; + return typeof generatePublicKey === 'function'; + }); + expect(generatePublicKeyExists).toBe(true); + }); + + it('Creates an account', async () => { + const result = await page.evaluate( + async (rpcUrl, privateKeyString) => { + const { GrumpkinScalar, createAztecRpcClient, getUnsafeSchnorrAccount } = window.AztecJs; + const client = createAztecRpcClient(rpcUrl!); + const privateKey = GrumpkinScalar.fromString(privateKeyString); + const account = getUnsafeSchnorrAccount(client, privateKey); + await account.waitDeploy(); + const completeAddress = await account.getCompleteAddress(); + const addressString = completeAddress.address.toString(); + console.log(`Created Account: ${addressString}`); + return addressString; + }, + SANDBOX_URL, + privKey.toString(), + ); + const accounts = await testClient.getRegisteredAccounts(); + const stringAccounts = accounts.map(acc => acc.address.toString()); + expect(stringAccounts.includes(result)).toBeTruthy(); + }, 15_000); + + it('Deploys Private Token contract', async () => { + await deployPrivateTokenContract(); + }, 30_000); + + it("Gets the owner's balance", async () => { + const result = await page.evaluate( + async (rpcUrl, contractAddress, PrivateTokenContractAbi) => { + const { Contract, AztecAddress, createAztecRpcClient } = window.AztecJs; + const client = createAztecRpcClient(rpcUrl!); + const owner = (await client.getRegisteredAccounts())[0].address; + const [wallet] = await AztecJs.getSandboxAccountsWallets(client); + const contract = await Contract.at(AztecAddress.fromString(contractAddress), PrivateTokenContractAbi, wallet); + const balance = await contract.methods.getBalance(owner).view({ from: owner }); + console.log(`Owner's balance: ${balance}`); + return balance; + }, + SANDBOX_URL, + (await getPrivateTokenAddress()).toString(), + PrivateTokenContractAbi, + ); + expect(result).toEqual(initialBalance); + }); + + it('Sends a transfer TX', async () => { + const result = await page.evaluate( + async (rpcUrl, contractAddress, transferAmount, PrivateTokenContractAbi) => { + console.log(`Starting transfer tx`); + const { AztecAddress, Contract, createAztecRpcClient } = window.AztecJs; + const client = createAztecRpcClient(rpcUrl!); + const accounts = await client.getRegisteredAccounts(); + const owner = accounts[0].address; + const receiver = accounts[1].address; + const [wallet] = await AztecJs.getSandboxAccountsWallets(client); + const contract = await Contract.at(AztecAddress.fromString(contractAddress), PrivateTokenContractAbi, wallet); + await contract.methods.transfer(transferAmount, receiver).send().wait(); + console.log(`Transferred ${transferAmount} tokens to new Account`); + const receiverBalance = await contract.methods.getBalance(receiver).view({ from: receiver }); + console.log(`Receiver's balance is now: ${receiverBalance}`); + const senderBalance = await contract.methods.getBalance(owner).view({ from: owner }); + console.log(`Updated sender balance: ${senderBalance}`); + return receiverBalance; + }, + SANDBOX_URL, + (await getPrivateTokenAddress()).toString(), + transferAmount, + PrivateTokenContractAbi, + ); + expect(result).toEqual(transferAmount); + }, 60_000); + + const deployPrivateTokenContract = async () => { + const txHash = await page.evaluate( + async (rpcUrl, privateKeyString, initialBalance, PrivateTokenContractAbi) => { + const { GrumpkinScalar, DeployMethod, createAztecRpcClient, getUnsafeSchnorrAccount } = window.AztecJs; + const client = createAztecRpcClient(rpcUrl!); + let accounts = await client.getRegisteredAccounts(); + if (accounts.length === 0) { + // This test needs an account for deployment. We create one in case there is none available in the RPC server. + const privateKey = GrumpkinScalar.fromString(privateKeyString); + await getUnsafeSchnorrAccount(client, privateKey).waitDeploy(); + accounts = await client.getRegisteredAccounts(); + } + const owner = accounts[0]; + const tx = new DeployMethod(owner.publicKey, client, PrivateTokenContractAbi, [ + initialBalance, + owner.address, + ]).send(); + await tx.wait(); + const receipt = await tx.getReceipt(); + console.log(`Contract Deployed: ${receipt.contractAddress}`); + return receipt.txHash.toString(); + }, + SANDBOX_URL, + privKey.toString(), + initialBalance, + PrivateTokenContractAbi, + ); + + const txResult = await testClient.getTxReceipt(AztecJs.TxHash.fromString(txHash)); + expect(txResult.status).toEqual(AztecJs.TxStatus.MINED); + contractAddress = txResult.contractAddress!; + }; + + const getPrivateTokenAddress = async () => { + if (!contractAddress) { + await deployPrivateTokenContract(); + } + return contractAddress; + }; + }); diff --git a/yarn-project/end-to-end/src/canary/cli.ts b/yarn-project/end-to-end/src/canary/cli.ts new file mode 100644 index 00000000000..7cda9b1c3bd --- /dev/null +++ b/yarn-project/end-to-end/src/canary/cli.ts @@ -0,0 +1,171 @@ +import { AztecAddress, AztecRPC, CompleteAddress, DebugLogger } from '@aztec/aztec.js'; +import { getProgram } from '@aztec/cli'; + +import stringArgv from 'string-argv'; +import { format } from 'util'; + +const INITIAL_BALANCE = 33000; +const TRANSFER_BALANCE = 3000; + +export const cliTestSuite = ( + name: string, + setup: () => Promise, + cleanup: () => Promise, + debug: DebugLogger, + rpcUrl = 'http://localhost:8080', +) => + describe(name, () => { + let cli: ReturnType; + let aztecRpcClient: AztecRPC; + let existingAccounts: CompleteAddress[]; + let contractAddress: AztecAddress; + let log: (...args: any[]) => void; + + // All logs emitted by the cli will be collected here, and reset between tests + const logs: string[] = []; + + beforeAll(async () => { + aztecRpcClient = await setup(); + log = (...args: any[]) => { + logs.push(format(...args)); + debug(...args); + }; + }, 30_000); + + afterAll(async () => { + await cleanup(); + }); + + // in order to run the same command twice, we need to create a new CLI instance + const resetCli = () => { + cli = getProgram(log, debug); + }; + + beforeEach(() => { + logs.splice(0); + resetCli(); + }); + + // Run a command on the CLI + const run = (cmd: string, addRpcUrl = true) => { + const args = stringArgv(cmd, 'node', 'dest/bin/index.js'); + if (addRpcUrl) { + args.push('--rpc-url', rpcUrl); + } + return cli.parseAsync(args); + }; + + // Returns first match across all logs collected so far + const findInLogs = (regex: RegExp) => { + for (const log of logs) { + const match = regex.exec(log); + if (match) return match; + } + }; + + const findMultipleInLogs = (regex: RegExp) => { + const matches = []; + for (const log of logs) { + const match = regex.exec(log); + if (match) matches.push(match); + } + return matches; + }; + + const clearLogs = () => { + logs.splice(0); + }; + + it('creates & retrieves an account', async () => { + existingAccounts = await aztecRpcClient.getRegisteredAccounts(); + debug('Create an account'); + await run(`create-account`); + const foundAddress = findInLogs(/Address:\s+(?
0x[a-fA-F0-9]+)/)?.groups?.address; + expect(foundAddress).toBeDefined(); + const newAddress = AztecAddress.fromString(foundAddress!); + + const accountsAfter = await aztecRpcClient.getRegisteredAccounts(); + const expectedAccounts = [...existingAccounts.map(a => a.address), newAddress]; + expect(accountsAfter.map(a => a.address)).toEqual(expectedAccounts); + const newCompleteAddress = accountsAfter[accountsAfter.length - 1]; + + // Test get-accounts + debug('Check that account was added to the list of accs in RPC'); + await run('get-accounts'); + const fetchedAddresses = findMultipleInLogs(/Address:\s+(?
0x[a-fA-F0-9]+)/); + const foundFetchedAddress = fetchedAddresses.find(match => match.groups?.address === newAddress.toString()); + expect(foundFetchedAddress).toBeDefined(); + + // Test get-account + debug('Check we can retrieve the specific account'); + clearLogs(); + await run(`get-account ${newAddress.toString()}`); + const fetchedAddress = findInLogs(/Public Key:\s+(?
0x[a-fA-F0-9]+)/)?.groups?.address; + expect(fetchedAddress).toEqual(newCompleteAddress.publicKey.toString()); + }); + + it('deploys a contract & sends transactions', async () => { + // generate a private key + debug('Create an account using a private key'); + await run('generate-private-key', false); + const privKey = findInLogs(/Private\sKey:\s+0x(?[a-fA-F0-9]+)/)?.groups?.privKey; + expect(privKey).toHaveLength(64); + await run(`create-account --private-key ${privKey}`); + const foundAddress = findInLogs(/Address:\s+(?
0x[a-fA-F0-9]+)/)?.groups?.address; + expect(foundAddress).toBeDefined(); + const ownerAddress = AztecAddress.fromString(foundAddress!); + + debug('Deploy Private Token Contract using created account.'); + await run(`deploy PrivateTokenContractAbi --args ${INITIAL_BALANCE} ${ownerAddress} --salt 0`); + const loggedAddress = findInLogs(/Contract\sdeployed\sat\s+(?
0x[a-fA-F0-9]+)/)?.groups?.address; + expect(loggedAddress).toBeDefined(); + contractAddress = AztecAddress.fromString(loggedAddress!); + + const deployedContract = await aztecRpcClient.getContractData(contractAddress); + expect(deployedContract?.contractAddress).toEqual(contractAddress); + + debug('Check contract can be found in returned address'); + await run(`check-deploy -ca ${loggedAddress}`); + const checkResult = findInLogs(/Contract\sfound\sat\s+(?
0x[a-fA-F0-9]+)/)?.groups?.address; + expect(checkResult).toEqual(deployedContract?.contractAddress.toString()); + + // clear logs + clearLogs(); + await run(`get-contract-data ${loggedAddress}`); + const contractDataAddress = findInLogs(/Address:\s+(?
0x[a-fA-F0-9]+)/)?.groups?.address; + expect(contractDataAddress).toEqual(deployedContract?.contractAddress.toString()); + + debug("Check owner's balance"); + await run( + `call getBalance --args ${ownerAddress} --contract-abi PrivateTokenContractAbi --contract-address ${contractAddress.toString()}`, + ); + const balance = findInLogs(/View\sresult:\s+(?\S+)/)?.groups?.data; + expect(balance!).toEqual(`${BigInt(INITIAL_BALANCE).toString()}n`); + + debug('Transfer some tokens'); + const existingAccounts = await aztecRpcClient.getRegisteredAccounts(); + // ensure we pick a different acc + const receiver = existingAccounts.find(acc => acc.address.toString() !== ownerAddress.toString()); + + await run( + `send transfer --args ${TRANSFER_BALANCE} ${receiver?.address.toString()} --contract-address ${contractAddress.toString()} --contract-abi PrivateTokenContractAbi --private-key ${privKey}`, + ); + const txHash = findInLogs(/Transaction\shash:\s+(?\S+)/)?.groups?.txHash; + + debug('Check the transfer receipt'); + await run(`get-tx-receipt ${txHash}`); + const txResult = findInLogs(/Transaction receipt:\s*(?[\s\S]*?\})/)?.groups?.txHash; + const parsedResult = JSON.parse(txResult!); + expect(parsedResult.txHash).toEqual(txHash); + expect(parsedResult.status).toEqual('mined'); + debug("Check Receiver's balance"); + // Reset CLI as we're calling getBalance again + resetCli(); + clearLogs(); + await run( + `call getBalance --args ${receiver?.address.toString()} --contract-abi PrivateTokenContractAbi --contract-address ${contractAddress.toString()}`, + ); + const receiverBalance = findInLogs(/View\sresult:\s+(?\S+)/)?.groups?.data; + expect(receiverBalance).toEqual(`${BigInt(TRANSFER_BALANCE).toString()}n`); + }, 30_000); + }); diff --git a/yarn-project/end-to-end/src/e2e_aztec_js_browser.test.ts b/yarn-project/end-to-end/src/e2e_aztec_js_browser.test.ts index c770579fc65..bb4a246251e 100644 --- a/yarn-project/end-to-end/src/e2e_aztec_js_browser.test.ts +++ b/yarn-project/end-to-end/src/e2e_aztec_js_browser.test.ts @@ -1,32 +1,17 @@ -/* eslint-disable no-console */ -import * as AztecJs from '@aztec/aztec.js'; -import { AztecAddress, GrumpkinScalar } from '@aztec/circuits.js'; -import { DebugLogger, createDebugLogger } from '@aztec/foundation/log'; -import { fileURLToPath } from '@aztec/foundation/url'; -import { PrivateTokenContractAbi } from '@aztec/noir-contracts/artifacts'; +import { createDebugLogger, fileURLToPath } from '@aztec/aztec.js'; -import { Server } from 'http'; import Koa from 'koa'; import serve from 'koa-static'; import path, { dirname } from 'path'; -import { Browser, Page, launch } from 'puppeteer'; -declare global { - interface Window { - AztecJs: typeof AztecJs; - } -} +import { browserTestSuite } from './canary/browser.js'; const __filename = fileURLToPath(import.meta.url); const __dirname = dirname(__filename); - const PORT = 3000; -const { SANDBOX_URL } = process.env; - -const conditionalDescribe = () => (SANDBOX_URL ? describe : describe.skip); -const privKey = GrumpkinScalar.random(); - +const logger = createDebugLogger('aztec:canary_aztec.js:web'); +const pageLogger = createDebugLogger('aztec:canary_aztec.js:web:page'); /** * This test is a bit of a special case as it's relying on sandbox and web browser and not only on anvil and node.js. * To run the test, do the following: @@ -41,175 +26,14 @@ const privKey = GrumpkinScalar.random(); * NOTE: If you see aztec-sandbox logs spammed with unexpected logs there is probably a chrome process with a webpage * unexpectedly running in the background. Kill it with `killall chrome` */ -conditionalDescribe()('e2e_aztec.js_browser', () => { - const initialBalance = 33n; - const transferAmount = 3n; - - let contractAddress: AztecAddress; - - let logger: DebugLogger; - let pageLogger: DebugLogger; - let app: Koa; - let testClient: AztecJs.AztecRPC; - let server: Server; - - let browser: Browser; - let page: Page; - - beforeAll(async () => { - testClient = AztecJs.createAztecRpcClient(SANDBOX_URL!); - await AztecJs.waitForSandbox(testClient); - - app = new Koa(); - app.use(serve(path.resolve(__dirname, './web'))); - server = app.listen(PORT, () => { - logger(`Server started at http://localhost:${PORT}`); - }); - - logger = createDebugLogger('aztec:aztec.js:web'); - pageLogger = createDebugLogger('aztec:aztec.js:web:page'); - - browser = await launch({ - executablePath: process.env.CHROME_BIN, - headless: 'new', - args: [ - '--allow-file-access-from-files', - '--no-sandbox', - '--headless', - '--disable-web-security', - '--disable-features=IsolateOrigins', - '--disable-site-isolation-trials', - '--disable-gpu', - '--disable-dev-shm-usage', - '--disk-cache-dir=/dev/null', - ], - }); - page = await browser.newPage(); - page.on('console', msg => { - pageLogger(msg.text()); - }); - page.on('pageerror', err => { - pageLogger.error(err.toString()); - }); - await page.goto(`http://localhost:${PORT}/index.html`); - }, 120_000); - - afterAll(async () => { - await browser.close(); - server.close(); - }); - - it('Loads Aztec.js in the browser', async () => { - const generatePublicKeyExists = await page.evaluate(() => { - const { generatePublicKey } = window.AztecJs; - return typeof generatePublicKey === 'function'; - }); - expect(generatePublicKeyExists).toBe(true); +const setupApp = () => { + const app = new Koa(); + app.use(serve(path.resolve(__dirname, './web'))); + const server = app.listen(PORT, () => { + logger(`Server started at http://localhost:${PORT}`); }); - it('Creates an account', async () => { - const result = await page.evaluate( - async (rpcUrl, privateKeyString) => { - const { GrumpkinScalar, createAztecRpcClient, getUnsafeSchnorrAccount } = window.AztecJs; - const client = createAztecRpcClient(rpcUrl!); - const privateKey = GrumpkinScalar.fromString(privateKeyString); - const account = getUnsafeSchnorrAccount(client, privateKey); - await account.waitDeploy(); - const completeAddress = await account.getCompleteAddress(); - const addressString = completeAddress.address.toString(); - console.log(`Created Account: ${addressString}`); - return addressString; - }, - SANDBOX_URL, - privKey.toString(), - ); - const accounts = await testClient.getRegisteredAccounts(); - const stringAccounts = accounts.map(acc => acc.address.toString()); - expect(stringAccounts.includes(result)).toBeTruthy(); - }, 15_000); - - it('Deploys Private Token contract', async () => { - await deployPrivateTokenContract(); - }, 30_000); - - it("Gets the owner's balance", async () => { - const result = await page.evaluate( - async (rpcUrl, contractAddress, PrivateTokenContractAbi) => { - const { Contract, AztecAddress, createAztecRpcClient } = window.AztecJs; - const client = createAztecRpcClient(rpcUrl!); - const owner = (await client.getRegisteredAccounts())[0].address; - const [wallet] = await AztecJs.getSandboxAccountsWallets(client); - const contract = await Contract.at(AztecAddress.fromString(contractAddress), PrivateTokenContractAbi, wallet); - const balance = await contract.methods.getBalance(owner).view({ from: owner }); - return balance; - }, - SANDBOX_URL, - (await getPrivateTokenAddress()).toString(), - PrivateTokenContractAbi, - ); - logger('Owner balance:', result); - expect(result).toEqual(initialBalance); - }); - - it('Sends a transfer TX', async () => { - const result = await page.evaluate( - async (rpcUrl, contractAddress, transferAmount, PrivateTokenContractAbi) => { - console.log(`Starting transfer tx`); - const { AztecAddress, Contract, createAztecRpcClient } = window.AztecJs; - const client = createAztecRpcClient(rpcUrl!); - const accounts = await client.getRegisteredAccounts(); - const receiver = accounts[1].address; - const [wallet] = await AztecJs.getSandboxAccountsWallets(client); - const contract = await Contract.at(AztecAddress.fromString(contractAddress), PrivateTokenContractAbi, wallet); - await contract.methods.transfer(transferAmount, receiver).send().wait(); - console.log(`Transferred ${transferAmount} tokens to new Account`); - return await contract.methods.getBalance(receiver).view({ from: receiver }); - }, - SANDBOX_URL, - (await getPrivateTokenAddress()).toString(), - transferAmount, - PrivateTokenContractAbi, - ); - expect(result).toEqual(transferAmount); - }, 60_000); - - const deployPrivateTokenContract = async () => { - const txHash = await page.evaluate( - async (rpcUrl, privateKeyString, initialBalance, PrivateTokenContractAbi) => { - const { GrumpkinScalar, DeployMethod, createAztecRpcClient, getUnsafeSchnorrAccount } = window.AztecJs; - const client = createAztecRpcClient(rpcUrl!); - let accounts = await client.getRegisteredAccounts(); - if (accounts.length === 0) { - // This test needs an account for deployment. We create one in case there is none available in the RPC server. - const privateKey = GrumpkinScalar.fromString(privateKeyString); - await getUnsafeSchnorrAccount(client, privateKey).waitDeploy(); - accounts = await client.getRegisteredAccounts(); - } - const owner = accounts[0]; - const tx = new DeployMethod(owner.publicKey, client, PrivateTokenContractAbi, [ - initialBalance, - owner.address, - ]).send(); - await tx.wait(); - const receipt = await tx.getReceipt(); - console.log(`Contract Deployed: ${receipt.contractAddress}`); - return receipt.txHash.toString(); - }, - SANDBOX_URL, - privKey.toString(), - initialBalance, - PrivateTokenContractAbi, - ); - - const txResult = await testClient.getTxReceipt(AztecJs.TxHash.fromString(txHash)); - expect(txResult.status).toEqual(AztecJs.TxStatus.MINED); - contractAddress = txResult.contractAddress!; - }; + return server; +}; - const getPrivateTokenAddress = async () => { - if (!contractAddress) { - await deployPrivateTokenContract(); - } - return contractAddress; - }; -}); +browserTestSuite(setupApp, pageLogger); diff --git a/yarn-project/end-to-end/src/e2e_cli.test.ts b/yarn-project/end-to-end/src/e2e_cli.test.ts index a93defcec93..112748fe75b 100644 --- a/yarn-project/end-to-end/src/e2e_cli.test.ts +++ b/yarn-project/end-to-end/src/e2e_cli.test.ts @@ -1,14 +1,10 @@ import { AztecNodeService } from '@aztec/aztec-node'; -import { AztecAddress, AztecRPCServer } from '@aztec/aztec-rpc'; +import { AztecRPCServer } from '@aztec/aztec-rpc'; import { startHttpRpcServer } from '@aztec/aztec-sandbox'; -import { createDebugLogger } from '@aztec/aztec.js'; -import { getProgram } from '@aztec/cli'; -import { AztecRPC, CompleteAddress } from '@aztec/types'; +import { AztecRPC, createDebugLogger } from '@aztec/aztec.js'; -import stringArgv from 'string-argv'; -import { format } from 'util'; - -import { setup } from './fixtures/utils.js'; +import { cliTestSuite } from './canary/cli.js'; +import { setup as e2eSetup } from './fixtures/utils.js'; const HTTP_PORT = 9009; const RPC_URL = `http://localhost:${HTTP_PORT}`; @@ -19,7 +15,7 @@ let aztecNode: AztecNodeService | undefined; let aztecRpcServer: AztecRPC; const testSetup = async () => { - const context = await setup(2); + const context = await e2eSetup(2); debug(`Environment set up`); const { deployL1ContractsValues } = context; ({ aztecNode, aztecRpcServer } = context); @@ -28,167 +24,10 @@ const testSetup = async () => { return aztecRpcServer; }; -const cleanup = async () => { +const testCleanup = async () => { http.close(); await aztecNode?.stop(); await (aztecRpcServer as AztecRPCServer).stop(); }; -const INITIAL_BALANCE = 33000; -const TRANSFER_BALANCE = 3000; - -describe('CLI e2e test', () => { - let cli: ReturnType; - let aztecRpcClient: AztecRPC; - let existingAccounts: CompleteAddress[]; - let contractAddress: AztecAddress; - let log: (...args: any[]) => void; - - // All logs emitted by the cli will be collected here, and reset between tests - const logs: string[] = []; - - beforeAll(async () => { - aztecRpcClient = await testSetup(); - log = (...args: any[]) => { - logs.push(format(...args)); - debug(...args); - }; - }, 30_000); - - afterAll(async () => { - await cleanup(); - }); - - // in order to run the same command twice, we need to create a new CLI instance - const resetCli = () => { - cli = getProgram(log, debug); - }; - - beforeEach(() => { - logs.splice(0); - resetCli(); - }); - - // Run a command on the CLI - const run = (cmd: string, addRpcUrl = true) => { - const args = stringArgv(cmd, 'node', 'dest/bin/index.js'); - if (addRpcUrl) { - args.push('--rpc-url', RPC_URL); - } - return cli.parseAsync(args); - }; - - // Returns first match across all logs collected so far - const findInLogs = (regex: RegExp) => { - for (const log of logs) { - const match = regex.exec(log); - if (match) return match; - } - }; - - const findMultipleInLogs = (regex: RegExp) => { - const matches = []; - for (const log of logs) { - const match = regex.exec(log); - if (match) matches.push(match); - } - return matches; - }; - - const clearLogs = () => { - logs.splice(0); - }; - - it('creates & retrieves an account', async () => { - existingAccounts = await aztecRpcClient.getRegisteredAccounts(); - debug('Create an account'); - await run(`create-account`); - const foundAddress = findInLogs(/Address:\s+(?
0x[a-fA-F0-9]+)/)?.groups?.address; - expect(foundAddress).toBeDefined(); - const newAddress = AztecAddress.fromString(foundAddress!); - - const accountsAfter = await aztecRpcClient.getRegisteredAccounts(); - const expectedAccounts = [...existingAccounts.map(a => a.address), newAddress]; - expect(accountsAfter.map(a => a.address)).toEqual(expectedAccounts); - const newCompleteAddress = accountsAfter[accountsAfter.length - 1]; - - // Test get-accounts - debug('Check that account was added to the list of accs in RPC'); - await run('get-accounts'); - const fetchedAddresses = findMultipleInLogs(/Address:\s+(?
0x[a-fA-F0-9]+)/); - const foundFetchedAddress = fetchedAddresses.find(match => match.groups?.address === newAddress.toString()); - expect(foundFetchedAddress).toBeDefined(); - - // Test get-account - debug('Check we can retrieve the specific account'); - clearLogs(); - await run(`get-account ${newAddress.toString()}`); - const fetchedAddress = findInLogs(/Public Key:\s+(?
0x[a-fA-F0-9]+)/)?.groups?.address; - expect(fetchedAddress).toEqual(newCompleteAddress.publicKey.toString()); - }); - - it('deploys a contract & sends transactions', async () => { - // generate a private key - debug('Create an account using a private key'); - await run('generate-private-key', false); - const privKey = findInLogs(/Private\sKey:\s+0x(?[a-fA-F0-9]+)/)?.groups?.privKey; - expect(privKey).toHaveLength(64); - await run(`create-account --private-key ${privKey}`); - const foundAddress = findInLogs(/Address:\s+(?
0x[a-fA-F0-9]+)/)?.groups?.address; - expect(foundAddress).toBeDefined(); - const ownerAddress = AztecAddress.fromString(foundAddress!); - - debug('Deploy Private Token Contract using created account.'); - await run(`deploy PrivateTokenContractAbi --args ${INITIAL_BALANCE} ${ownerAddress} --salt 0`); - const loggedAddress = findInLogs(/Contract\sdeployed\sat\s+(?
0x[a-fA-F0-9]+)/)?.groups?.address; - expect(loggedAddress).toBeDefined(); - contractAddress = AztecAddress.fromString(loggedAddress!); - - const deployedContract = await aztecRpcClient.getContractData(contractAddress); - expect(deployedContract?.contractAddress).toEqual(contractAddress); - - debug('Check contract can be found in returned address'); - await run(`check-deploy -ca ${loggedAddress}`); - const checkResult = findInLogs(/Contract\sfound\sat\s+(?
0x[a-fA-F0-9]+)/)?.groups?.address; - expect(checkResult).toEqual(deployedContract?.contractAddress.toString()); - - // clear logs - clearLogs(); - await run(`get-contract-data ${loggedAddress}`); - const contractDataAddress = findInLogs(/Address:\s+(?
0x[a-fA-F0-9]+)/)?.groups?.address; - expect(contractDataAddress).toEqual(deployedContract?.contractAddress.toString()); - - debug("Check owner's balance"); - await run( - `call getBalance --args ${ownerAddress} --contract-abi PrivateTokenContractAbi --contract-address ${contractAddress.toString()}`, - ); - const balance = findInLogs(/View\sresult:\s+(?\S+)/)?.groups?.data; - expect(balance!).toEqual(`${BigInt(INITIAL_BALANCE).toString()}n`); - - debug('Transfer some tokens'); - const existingAccounts = await aztecRpcClient.getRegisteredAccounts(); - // ensure we pick a different acc - const receiver = existingAccounts.find(acc => acc.address.toString() !== ownerAddress.toString()); - - await run( - `send transfer --args ${TRANSFER_BALANCE} ${receiver?.address.toString()} --contract-address ${contractAddress.toString()} --contract-abi PrivateTokenContractAbi --private-key ${privKey}`, - ); - const txHash = findInLogs(/Transaction\shash:\s+(?\S+)/)?.groups?.txHash; - - debug('Check the transfer receipt'); - await run(`get-tx-receipt ${txHash}`); - const txResult = findInLogs(/Transaction receipt:\s*(?[\s\S]*?\})/)?.groups?.txHash; - const parsedResult = JSON.parse(txResult!); - expect(parsedResult.txHash).toEqual(txHash); - expect(parsedResult.status).toEqual('mined'); - debug("Check Receiver's balance"); - // Reset CLI as we're calling getBalance again - resetCli(); - clearLogs(); - await run( - `call getBalance --args ${receiver?.address.toString()} --contract-abi PrivateTokenContractAbi --contract-address ${contractAddress.toString()}`, - ); - const receiverBalance = findInLogs(/View\sresult:\s+(?\S+)/)?.groups?.data; - expect(receiverBalance).toEqual(`${BigInt(TRANSFER_BALANCE).toString()}n`); - }, 30_000); -}); +cliTestSuite('E2E CLI Test', testSetup, testCleanup, createDebugLogger('aztec:e2e_cli'), RPC_URL); diff --git a/yarn-project/end-to-end/src/fixtures/utils.ts b/yarn-project/end-to-end/src/fixtures/utils.ts index 9fe43a88f70..d55774b1911 100644 --- a/yarn-project/end-to-end/src/fixtures/utils.ts +++ b/yarn-project/end-to-end/src/fixtures/utils.ts @@ -4,6 +4,7 @@ import { AccountWallet, AztecAddress, CheatCodes, + CompleteAddress, EthAddress, EthCheatCodes, Wallet, @@ -12,7 +13,6 @@ import { getL1ContractAddresses, getSandboxAccountsWallets, } from '@aztec/aztec.js'; -import { CompleteAddress } from '@aztec/circuits.js'; import { DeployL1Contracts, deployL1Contract, deployL1Contracts } from '@aztec/ethereum'; import { Fr } from '@aztec/foundation/fields'; import { DebugLogger, createDebugLogger } from '@aztec/foundation/log'; diff --git a/yarn-project/end-to-end/src/index.ts b/yarn-project/end-to-end/src/index.ts new file mode 100644 index 00000000000..dd9005c49c9 --- /dev/null +++ b/yarn-project/end-to-end/src/index.ts @@ -0,0 +1,4 @@ +// Should only export tests from the canary directory + +export { cliTestSuite } from './canary/cli.js'; +export { browserTestSuite } from './canary/browser.js'; diff --git a/yarn-project/yarn.lock b/yarn-project/yarn.lock index cb627d24226..9edddedd644 100644 --- a/yarn-project/yarn.lock +++ b/yarn-project/yarn.lock @@ -267,7 +267,7 @@ __metadata: dependencies: "@aztec/aztec.js": "workspace:^" "@aztec/cli": "workspace:^" - "@aztec/foundation": "workspace:^" + "@aztec/end-to-end": "workspace:^" "@aztec/l1-artifacts": "workspace:^" "@aztec/noir-contracts": "workspace:^" "@jest/globals": ^29.5.0 @@ -278,8 +278,6 @@ __metadata: jest: ^29.5.0 koa: ^2.14.2 koa-static: ^5.0.0 - puppeteer: ^21.1.0 - string-argv: ^0.3.2 ts-jest: ^29.1.0 ts-node: ^10.9.1 tslib: ^2.4.0 @@ -361,7 +359,7 @@ __metadata: languageName: unknown linkType: soft -"@aztec/end-to-end@workspace:end-to-end": +"@aztec/end-to-end@workspace:^, @aztec/end-to-end@workspace:end-to-end": version: 0.0.0-use.local resolution: "@aztec/end-to-end@workspace:end-to-end" dependencies: @@ -2912,23 +2910,6 @@ __metadata: languageName: node linkType: hard -"@puppeteer/browsers@npm:1.7.0": - version: 1.7.0 - resolution: "@puppeteer/browsers@npm:1.7.0" - dependencies: - debug: 4.3.4 - extract-zip: 2.0.1 - progress: 2.0.3 - proxy-agent: 6.3.0 - tar-fs: 3.0.4 - unbzip2-stream: 1.4.3 - yargs: 17.7.1 - bin: - browsers: lib/cjs/main-cli.js - checksum: 0a2aecc72fb94a8d94246188f94cfaad730d1d372b34df94ca51ff8a94596bf475a0fee162c317a768fa4b2a707bfa8afd582d594958f49e1019effadfe744b6 - languageName: node - linkType: hard - "@rushstack/eslint-patch@npm:^1.1.4, @rushstack/eslint-patch@npm:^1.2.0": version: 1.3.2 resolution: "@rushstack/eslint-patch@npm:1.3.2" @@ -5580,17 +5561,6 @@ __metadata: languageName: node linkType: hard -"chromium-bidi@npm:0.4.20": - version: 0.4.20 - resolution: "chromium-bidi@npm:0.4.20" - dependencies: - mitt: 3.0.1 - peerDependencies: - devtools-protocol: "*" - checksum: 397145b3728948d403dbf087af97b7112988ed3c4cf43286754452a4b88f087f2088e1b3f18fa5974ceecc8200ca004ed258e18b784ecb4c6ab1ed78c1b280b0 - languageName: node - linkType: hard - "ci-info@npm:^3.2.0": version: 3.8.0 resolution: "ci-info@npm:3.8.0" @@ -6482,13 +6452,6 @@ __metadata: languageName: node linkType: hard -"devtools-protocol@npm:0.0.1159816": - version: 0.0.1159816 - resolution: "devtools-protocol@npm:0.0.1159816" - checksum: 24aa985a34a093a7418c955d0398e8a0829bc1f482eb1db55eca0cf09789de8344d0f36177d3e394fc63ec9bf08cf4114fadf3a7f2ab64f95a17ccc53bcf1aea - languageName: node - linkType: hard - "dezalgo@npm:^1.0.4": version: 1.0.4 resolution: "dezalgo@npm:1.0.4" @@ -11412,13 +11375,6 @@ __metadata: languageName: node linkType: hard -"mitt@npm:3.0.1": - version: 3.0.1 - resolution: "mitt@npm:3.0.1" - checksum: b55a489ac9c2949ab166b7f060601d3b6d893a852515ae9eca4e11df01c013876df777ea109317622b5c1c60e8aae252558e33c8c94e14124db38f64a39614b1 - languageName: node - linkType: hard - "mkdirp-classic@npm:^0.5.2": version: 0.5.3 resolution: "mkdirp-classic@npm:0.5.3" @@ -12730,20 +12686,6 @@ __metadata: languageName: node linkType: hard -"puppeteer-core@npm:21.1.0": - version: 21.1.0 - resolution: "puppeteer-core@npm:21.1.0" - dependencies: - "@puppeteer/browsers": 1.7.0 - chromium-bidi: 0.4.20 - cross-fetch: 4.0.0 - debug: 4.3.4 - devtools-protocol: 0.0.1159816 - ws: 8.13.0 - checksum: 1c3f2a2bb6de3ec90808f06f0c3ef769f0854e7df610c16080c1dd5434cedf865f48d5059c5406fa84227aab8bfae7dec789e5701adb800895c7a7ec888cfb04 - languageName: node - linkType: hard - "puppeteer@npm:^20.9.0": version: 20.9.0 resolution: "puppeteer@npm:20.9.0" @@ -12755,17 +12697,6 @@ __metadata: languageName: node linkType: hard -"puppeteer@npm:^21.1.0": - version: 21.1.0 - resolution: "puppeteer@npm:21.1.0" - dependencies: - "@puppeteer/browsers": 1.7.0 - cosmiconfig: 8.2.0 - puppeteer-core: 21.1.0 - checksum: 15e343dd1c048a0ef228aa52a2df2240dc76d543c379fc9246c41a87f226a2e820c957a911d5fed9e0394c5dd56da5fc163b6626e1219a857268e88cc2544163 - languageName: node - linkType: hard - "pure-rand@npm:^6.0.0": version: 6.0.2 resolution: "pure-rand@npm:6.0.2"