diff --git a/barretenberg/ts/src/barretenberg/blake2s.test.ts b/barretenberg/ts/src/barretenberg/blake2s.test.ts index 49ae654fe74..2e5a07883c4 100644 --- a/barretenberg/ts/src/barretenberg/blake2s.test.ts +++ b/barretenberg/ts/src/barretenberg/blake2s.test.ts @@ -41,7 +41,7 @@ describe('blake2s sync', () => { let api: BarretenbergSync; beforeAll(async () => { - api = await BarretenbergSync.new(); + api = await BarretenbergSync.initSingleton(); }); it('blake2s', () => { diff --git a/barretenberg/ts/src/barretenberg/index.ts b/barretenberg/ts/src/barretenberg/index.ts index b4403b748fa..ed2c0884062 100644 --- a/barretenberg/ts/src/barretenberg/index.ts +++ b/barretenberg/ts/src/barretenberg/index.ts @@ -89,26 +89,28 @@ export class Barretenberg extends BarretenbergApi { } } +let barrentenbergSyncSingletonPromise: Promise; let barretenbergSyncSingleton: BarretenbergSync; -let barretenbergSyncSingletonPromise: Promise; export class BarretenbergSync extends BarretenbergApiSync { private constructor(wasm: BarretenbergWasmMain) { super(wasm); } - static async new() { + private static async new() { const wasm = new BarretenbergWasmMain(); const { module, threads } = await fetchModuleAndThreads(1); await wasm.init(module, threads); return new BarretenbergSync(wasm); } - static initSingleton() { - if (!barretenbergSyncSingletonPromise) { - barretenbergSyncSingletonPromise = BarretenbergSync.new().then(s => (barretenbergSyncSingleton = s)); + static async initSingleton() { + if (!barrentenbergSyncSingletonPromise) { + barrentenbergSyncSingletonPromise = BarretenbergSync.new(); } - return barretenbergSyncSingletonPromise; + + barretenbergSyncSingleton = await barrentenbergSyncSingletonPromise; + return barretenbergSyncSingleton; } static getSingleton() { @@ -122,35 +124,3 @@ export class BarretenbergSync extends BarretenbergApiSync { return this.wasm; } } - -let barrentenbergLazySingleton: BarretenbergLazy; - -export class BarretenbergLazy extends BarretenbergApi { - private constructor(wasm: BarretenbergWasmMain) { - super(wasm); - } - - private static async new() { - const wasm = new BarretenbergWasmMain(); - const { module, threads } = await fetchModuleAndThreads(1); - await wasm.init(module, threads); - return new BarretenbergLazy(wasm); - } - - static async getSingleton() { - if (!barrentenbergLazySingleton) { - barrentenbergLazySingleton = await BarretenbergLazy.new(); - } - return barrentenbergLazySingleton; - } - - getWasm() { - return this.wasm; - } -} - -// If we're in ESM environment, use top level await. CJS users need to call it manually. -// Need to ignore for cjs build. -// eslint-disable-next-line @typescript-eslint/ban-ts-comment -// @ts-ignore -await BarretenbergSync.initSingleton(); // POSTPROCESS ESM ONLY diff --git a/barretenberg/ts/src/barretenberg/pedersen.test.ts b/barretenberg/ts/src/barretenberg/pedersen.test.ts index ef532411512..7c9c81091ac 100644 --- a/barretenberg/ts/src/barretenberg/pedersen.test.ts +++ b/barretenberg/ts/src/barretenberg/pedersen.test.ts @@ -6,7 +6,7 @@ describe('pedersen sync', () => { let api: BarretenbergSync; beforeAll(async () => { - api = await BarretenbergSync.new(); + api = await BarretenbergSync.initSingleton(); }); it('pedersenHash', () => { diff --git a/barretenberg/ts/src/barretenberg/poseidon.test.ts b/barretenberg/ts/src/barretenberg/poseidon.test.ts index dd27f70a018..b38cfd5b024 100644 --- a/barretenberg/ts/src/barretenberg/poseidon.test.ts +++ b/barretenberg/ts/src/barretenberg/poseidon.test.ts @@ -6,7 +6,7 @@ describe('poseidon sync', () => { let api: BarretenbergSync; beforeAll(async () => { - api = await BarretenbergSync.new(); + api = await BarretenbergSync.initSingleton(); }); it('poseidonHash', () => { diff --git a/barretenberg/ts/src/barretenberg_api/index.ts b/barretenberg/ts/src/barretenberg_api/index.ts index c0f4e50050a..90d9952ab7b 100644 --- a/barretenberg/ts/src/barretenberg_api/index.ts +++ b/barretenberg/ts/src/barretenberg_api/index.ts @@ -76,6 +76,18 @@ export class BarretenbergApi { return out[0]; } + async poseidon2HashAccumulate(inputsBuffer: Fr[]): Promise { + const inArgs = [inputsBuffer].map(serializeBufferable); + const outTypes: OutputType[] = [Fr]; + const result = await this.wasm.callWasmExport( + 'poseidon2_hash_accumulate', + inArgs, + outTypes.map(t => t.SIZE_IN_BYTES), + ); + const out = result.map((r, i) => outTypes[i].fromBuffer(r)); + return out[0]; + } + async poseidon2Hashes(inputsBuffer: Fr[]): Promise { const inArgs = [inputsBuffer].map(serializeBufferable); const outTypes: OutputType[] = [Fr]; diff --git a/barretenberg/ts/src/barretenberg_wasm/fetch_code/browser/index.ts b/barretenberg/ts/src/barretenberg_wasm/fetch_code/browser/index.ts index 3defd348760..b3542f679a0 100644 --- a/barretenberg/ts/src/barretenberg_wasm/fetch_code/browser/index.ts +++ b/barretenberg/ts/src/barretenberg_wasm/fetch_code/browser/index.ts @@ -1,12 +1,9 @@ import barretenbergModule from '../../barretenberg.wasm.gz'; import barretenbergThreadsModule from '../../barretenberg-threads.wasm.gz'; -import pako from 'pako'; // Annoyingly the wasm declares if it's memory is shared or not. So now we need two wasms if we want to be // able to fallback on "non shared memory" situations. export async function fetchCode(multithreaded: boolean) { const res = await fetch(multithreaded ? barretenbergThreadsModule : barretenbergModule); - const compressedData = await res.arrayBuffer(); - const decompressedData = pako.ungzip(new Uint8Array(compressedData)); - return decompressedData.buffer; + return res.arrayBuffer(); } diff --git a/barretenberg/ts/src/index.ts b/barretenberg/ts/src/index.ts index 0129d2799fe..23159ba7ff9 100644 --- a/barretenberg/ts/src/index.ts +++ b/barretenberg/ts/src/index.ts @@ -3,11 +3,11 @@ export { BackendOptions, Barretenberg, BarretenbergSync, - BarretenbergLazy, BarretenbergVerifier, UltraPlonkBackend, UltraHonkBackend, AztecClientBackend, } from './barretenberg/index.js'; + export { RawBuffer, Fr } from './types/index.js'; export { splitHonkProof, reconstructHonkProof, ProofData } from './proof/index.js'; diff --git a/barretenberg/ts/tsconfig.browser.json b/barretenberg/ts/tsconfig.browser.json index 8d906587ac5..40af611485c 100644 --- a/barretenberg/ts/tsconfig.browser.json +++ b/barretenberg/ts/tsconfig.browser.json @@ -4,5 +4,5 @@ "outDir": "dest/browser", "tsBuildInfoFile": ".tsbuildinfo.browser" }, - "include": ["src", "src/barretenberg_wasm/barretenberg_wasm_thread/factory/browser/thread.worker.ts"] + "include": ["!src/main.ts", "src", "src/barretenberg_wasm/barretenberg_wasm_thread/factory/browser/thread.worker.ts"] } diff --git a/barretenberg/ts/webpack.config.js b/barretenberg/ts/webpack.config.js index 757b3db7212..f524c000401 100644 --- a/barretenberg/ts/webpack.config.js +++ b/barretenberg/ts/webpack.config.js @@ -17,7 +17,7 @@ export default { rules: [ { test: /\.wasm\.gz$/, - type: 'asset/inline', + type: 'asset/resource' }, { test: /\.worker\.ts$/, diff --git a/gaztec/package.json b/gaztec/package.json index 107fff54b09..5d79c28bf80 100644 --- a/gaztec/package.json +++ b/gaztec/package.json @@ -43,6 +43,7 @@ "typescript": "~5.7.3", "typescript-eslint": "^8.11.0", "vite": "^6.0.7", - "vite-plugin-node-polyfills": "^0.22.0" + "vite-plugin-node-polyfills": "^0.22.0", + "vite-plugin-static-copy": "^2.2.0" } } diff --git a/gaztec/src/App.tsx b/gaztec/src/App.tsx index abc9bf85996..40760dfa1d8 100644 --- a/gaztec/src/App.tsx +++ b/gaztec/src/App.tsx @@ -1,13 +1,18 @@ -import { Home } from "./components/home/home"; import { Global } from "@emotion/react"; import { ThemeProvider } from "@mui/material/styles"; import { globalStyle, theme } from "./common.styles"; +import { Suspense, lazy } from "react"; +import { LinearProgress } from "@mui/material"; + +const Home = lazy(() => import("./components/home/home")); function App() { return ( - + }> + + ); } diff --git a/gaztec/src/config.ts b/gaztec/src/aztecEnv.ts similarity index 53% rename from gaztec/src/config.ts rename to gaztec/src/aztecEnv.ts index f2d2d822453..000152e577f 100644 --- a/gaztec/src/config.ts +++ b/gaztec/src/aztecEnv.ts @@ -3,6 +3,9 @@ import { createAztecNodeClient, type PXE, AztecNode, + AccountWalletWithSecretKey, + AztecAddress, + Contract, } from "@aztec/aztec.js"; import { PXEService } from "@aztec/pxe/service"; import { PXEServiceConfig, getPXEServiceConfig } from "@aztec/pxe/config"; @@ -13,6 +16,9 @@ import { createStore } from "@aztec/kv-store/indexeddb"; import { BBWASMLazyPrivateKernelProver } from "@aztec/bb-prover/wasm/lazy"; import { WASMSimulator } from "@aztec/simulator/client"; import { debug } from "debug"; +import { createContext } from "react"; +import { WalletDB } from "./utils/storage"; +import { ContractFunctionInteractionTx } from "./utils/txs"; process.env = Object.keys(import.meta.env).reduce((acc, key) => { acc[key.replace("VITE_", "")] = import.meta.env[key]; @@ -21,6 +27,46 @@ process.env = Object.keys(import.meta.env).reduce((acc, key) => { debug.enable("*"); +export const AztecContext = createContext<{ + pxe: PXE | null; + nodeURL: string; + node: AztecNode; + wallet: AccountWalletWithSecretKey | null; + isPXEInitialized: boolean; + walletDB: WalletDB | null; + currentContractAddress: AztecAddress; + currentContract: Contract; + currentTx: ContractFunctionInteractionTx; + setWalletDB: (walletDB: WalletDB) => void; + setPXEInitialized: (isPXEInitialized: boolean) => void; + setWallet: (wallet: AccountWalletWithSecretKey) => void; + setAztecNode: (node: AztecNode) => void; + setPXE: (pxe: PXE) => void; + setNodeURL: (nodeURL: string) => void; + setCurrentTx: (currentTx: ContractFunctionInteractionTx) => void; + setCurrentContract: (currentContract: Contract) => void; + setCurrentContractAddress: (currentContractAddress: AztecAddress) => void; +}>({ + pxe: null, + nodeURL: "", + node: null, + wallet: null, + isPXEInitialized: false, + walletDB: null, + currentContract: null, + currentContractAddress: null, + currentTx: null, + setWalletDB: (walletDB: WalletDB) => {}, + setPXEInitialized: (isPXEInitialized: boolean) => {}, + setWallet: (wallet: AccountWalletWithSecretKey) => {}, + setNodeURL: (nodeURL: string) => {}, + setPXE: (pxe: PXE) => {}, + setAztecNode: (node: AztecNode) => {}, + setCurrentTx: (currentTx: ContractFunctionInteractionTx) => {}, + setCurrentContract: (currentContract: Contract) => {}, + setCurrentContractAddress: (currentContractAddress: AztecAddress) => {}, +}); + export class AztecEnv { static async connectToNode(nodeURL: string): Promise { const aztecNode = await createAztecNodeClient(nodeURL); diff --git a/gaztec/src/components/common/fnParameter.tsx b/gaztec/src/components/common/fnParameter.tsx index 29f185beffc..20c3acb2a4e 100644 --- a/gaztec/src/components/common/fnParameter.tsx +++ b/gaztec/src/components/common/fnParameter.tsx @@ -16,9 +16,9 @@ import { formatFrAsString, parseAliasedBuffersAsString, } from "../../utils/conversion"; -import { Fragment, useContext, useState } from "react"; +import { useContext, useState } from "react"; import EditIcon from "@mui/icons-material/Edit"; -import { AztecContext } from "../home/home"; +import { AztecContext } from "../../aztecEnv"; const container = css({ display: "flex", diff --git a/gaztec/src/components/contract/components/createAuthwitDialog.tsx b/gaztec/src/components/contract/components/createAuthwitDialog.tsx index 518f065d2f1..62fdd94e550 100644 --- a/gaztec/src/components/contract/components/createAuthwitDialog.tsx +++ b/gaztec/src/components/contract/components/createAuthwitDialog.tsx @@ -11,7 +11,7 @@ import { css, } from "@mui/material"; import { useContext, useState } from "react"; -import { AztecContext } from "../../home/home"; +import { AztecContext } from "../../../aztecEnv"; import { FunctionParameter } from "../../common/fnParameter"; const creationForm = css({ @@ -61,13 +61,11 @@ export function CreateAuthwitDialog({ action, }); } else { - await wallet - .setPublicAuthWit( - { caller: AztecAddress.fromString(caller), action }, - true - ) - .send() - .wait(); + const validateActionInteraction = await wallet.setPublicAuthWit( + { caller: AztecAddress.fromString(caller), action }, + true + ); + await validateActionInteraction.send().wait(); } setAlias(""); setCreating(false); diff --git a/gaztec/src/components/contract/components/deployContractDialog.tsx b/gaztec/src/components/contract/components/deployContractDialog.tsx index 75d07cf8268..11ff31a7ab1 100644 --- a/gaztec/src/components/contract/components/deployContractDialog.tsx +++ b/gaztec/src/components/contract/components/deployContractDialog.tsx @@ -25,7 +25,7 @@ import { getDefaultInitializer, getInitializer, } from "@aztec/foundation/abi"; -import { AztecContext } from "../../home/home"; +import { AztecContext } from "../../../aztecEnv"; import { parseAliasedBuffersAsString } from "../../../utils/conversion"; import { FunctionParameter } from "../../common/fnParameter"; import { GITHUB_TAG_PREFIX } from "../../../utils/constants"; diff --git a/gaztec/src/components/contract/components/registerContractDialog.tsx b/gaztec/src/components/contract/components/registerContractDialog.tsx index 77bd6a56e1f..b8f5c94386e 100644 --- a/gaztec/src/components/contract/components/registerContractDialog.tsx +++ b/gaztec/src/components/contract/components/registerContractDialog.tsx @@ -16,7 +16,7 @@ import { css, } from "@mui/material"; import { useContext, useState } from "react"; -import { AztecContext } from "../../home/home"; +import { AztecContext } from "../../../aztecEnv"; import { GITHUB_TAG_PREFIX } from "../../../utils/constants"; const creationForm = css({ diff --git a/gaztec/src/components/contract/contract.tsx b/gaztec/src/components/contract/contract.tsx index e22d6c977cc..8bd50b578a2 100644 --- a/gaztec/src/components/contract/contract.tsx +++ b/gaztec/src/components/contract/contract.tsx @@ -9,7 +9,7 @@ import { ContractInstanceWithAddress, loadContractArtifact, } from "@aztec/aztec.js"; -import { AztecContext } from "../home/home"; +import { AztecContext } from "../../aztecEnv"; import { Button, Card, @@ -241,7 +241,7 @@ export function ContractComponent() { const call = currentContract.methods[fnName](...parameters[fnName]); const provenCall = await call.prove(); - txHash = provenCall.getTxHash(); + txHash = await provenCall.getTxHash(); setCurrentTx({ ...currentTx, ...{ txHash, status: "sending" }, diff --git a/gaztec/src/components/home/home.tsx b/gaztec/src/components/home/home.tsx index 9f5e7c7edd5..40eeb01507e 100644 --- a/gaztec/src/components/home/home.tsx +++ b/gaztec/src/components/home/home.tsx @@ -1,16 +1,8 @@ import { css } from "@emotion/react"; import { ContractComponent } from "../contract/contract"; import { SidebarComponent } from "../sidebar/sidebar"; -import { createContext, useState } from "react"; -import { - type PXE, - type AccountWalletWithSecretKey, - Contract, - AztecNode, - AztecAddress, -} from "@aztec/aztec.js"; -import { type WalletDB } from "../../utils/storage"; -import { ContractFunctionInteractionTx } from "../../utils/txs"; +import { useState } from "react"; +import { AztecContext } from "../../aztecEnv"; const layout = css({ display: "flex", @@ -18,47 +10,7 @@ const layout = css({ height: "100%", }); -export const AztecContext = createContext<{ - pxe: PXE | null; - nodeURL: string; - node: AztecNode; - wallet: AccountWalletWithSecretKey | null; - isPXEInitialized: boolean; - walletDB: WalletDB | null; - currentContractAddress: AztecAddress; - currentContract: Contract; - currentTx: ContractFunctionInteractionTx; - setWalletDB: (walletDB: WalletDB) => void; - setPXEInitialized: (isPXEInitialized: boolean) => void; - setWallet: (wallet: AccountWalletWithSecretKey) => void; - setAztecNode: (node: AztecNode) => void; - setPXE: (pxe: PXE) => void; - setNodeURL: (nodeURL: string) => void; - setCurrentTx: (currentTx: ContractFunctionInteractionTx) => void; - setCurrentContract: (currentContract: Contract) => void; - setCurrentContractAddress: (currentContractAddress: AztecAddress) => void; -}>({ - pxe: null, - nodeURL: "", - node: null, - wallet: null, - isPXEInitialized: false, - walletDB: null, - currentContract: null, - currentContractAddress: null, - currentTx: null, - setWalletDB: (walletDB: WalletDB) => {}, - setPXEInitialized: (isPXEInitialized: boolean) => {}, - setWallet: (wallet: AccountWalletWithSecretKey) => {}, - setNodeURL: (nodeURL: string) => {}, - setPXE: (pxe: PXE) => {}, - setAztecNode: (node: AztecNode) => {}, - setCurrentTx: (currentTx: ContractFunctionInteractionTx) => {}, - setCurrentContract: (currentContract: Contract) => {}, - setCurrentContractAddress: (currentContractAddress: AztecAddress) => {}, -}); - -export function Home() { +export default function Home() { const [pxe, setPXE] = useState(null); const [wallet, setWallet] = useState(null); const [nodeURL, setNodeURL] = useState(""); diff --git a/gaztec/src/components/sidebar/components/createAccountDialog.tsx b/gaztec/src/components/sidebar/components/createAccountDialog.tsx index 4d85686b9e5..6ac7c983251 100644 --- a/gaztec/src/components/sidebar/components/createAccountDialog.tsx +++ b/gaztec/src/components/sidebar/components/createAccountDialog.tsx @@ -11,7 +11,7 @@ import { } from "@mui/material"; import { useContext, useState } from "react"; import { deriveSigningKey } from "@aztec/circuits.js/keys"; -import { AztecContext } from "../../home/home"; +import { AztecContext } from "../../../aztecEnv"; const creationForm = css({ display: "flex", diff --git a/gaztec/src/components/sidebar/sidebar.tsx b/gaztec/src/components/sidebar/sidebar.tsx index 7b4b6d24b7d..e63a2326648 100644 --- a/gaztec/src/components/sidebar/sidebar.tsx +++ b/gaztec/src/components/sidebar/sidebar.tsx @@ -3,16 +3,13 @@ import InputLabel from "@mui/material/InputLabel"; import MenuItem from "@mui/material/MenuItem"; import FormControl from "@mui/material/FormControl"; import Select, { SelectChangeEvent } from "@mui/material/Select"; -import { AztecContext } from "../home/home"; -import { AztecEnv } from "../../config"; +import { AztecEnv, AztecContext } from "../../aztecEnv"; import { createStore } from "@aztec/kv-store/indexeddb"; import { AccountWalletWithSecretKey, - Contract, Fr, TxHash, createLogger, - loadContractArtifact, AztecAddress, } from "@aztec/aztec.js"; import { WalletDB } from "../../utils/storage"; diff --git a/gaztec/vite.config.ts b/gaztec/vite.config.ts index 11c6c7f4a52..b10a427eba7 100644 --- a/gaztec/vite.config.ts +++ b/gaztec/vite.config.ts @@ -1,6 +1,7 @@ import { defineConfig, searchForWorkspaceRoot } from "vite"; import react from "@vitejs/plugin-react-swc"; import { PolyfillOptions, nodePolyfills } from "vite-plugin-node-polyfills"; +import { viteStaticCopy } from "vite-plugin-static-copy"; // Unfortunate, but needed due to https://github.com/davidmyersdev/vite-plugin-node-polyfills/issues/81 // Suspected to be because of the yarn workspace setup, but not sure @@ -36,26 +37,20 @@ export default defineConfig({ "../yarn-project/noir-protocol-circuits-types/artifacts", "../noir/packages/noirc_abi/web", "../noir/packages/acvm_js/web", + "../barretenberg/ts/dest/browser", ], }, }, plugins: [ react({ jsxImportSource: "@emotion/react" }), nodePolyfillsFix({ include: ["buffer", "process", "path"] }), - ], - build: { - // Needed to support bb.js top level await until - // https://github.com/Menci/vite-plugin-top-level-await/pull/63 is merged - // and we can use the plugin again (or we get rid of TLA) - target: "esnext", - rollupOptions: { - output: { - manualChunks(id: string) { - if (id.includes("bb-prover")) { - return "@aztec/bb-prover"; - } + viteStaticCopy({ + targets: [ + { + src: "../barretenberg/ts/dest/browser/*.gz", + dest: "assets/", }, - }, - }, - }, + ], + }), + ], }); diff --git a/gaztec/yarn.lock b/gaztec/yarn.lock index 378af4c791b..ae2f6fddac3 100644 --- a/gaztec/yarn.lock +++ b/gaztec/yarn.lock @@ -1484,6 +1484,16 @@ __metadata: languageName: node linkType: hard +"anymatch@npm:~3.1.2": + version: 3.1.3 + resolution: "anymatch@npm:3.1.3" + dependencies: + normalize-path: "npm:^3.0.0" + picomatch: "npm:^2.0.4" + checksum: 10c0/57b06ae984bc32a0d22592c87384cd88fe4511b1dd7581497831c56d41939c8a001b28e7b853e1450f2bf61992dfcaa8ae2d0d161a0a90c4fb631ef07098fbac + languageName: node + linkType: hard + "argparse@npm:^2.0.1": version: 2.0.1 resolution: "argparse@npm:2.0.1" @@ -1556,6 +1566,13 @@ __metadata: languageName: node linkType: hard +"binary-extensions@npm:^2.0.0": + version: 2.3.0 + resolution: "binary-extensions@npm:2.3.0" + checksum: 10c0/75a59cafc10fb12a11d510e77110c6c7ae3f4ca22463d52487709ca7f18f69d886aa387557cc9864fbdb10153d0bdb4caacabf11541f55e89ed6e18d12ece2b5 + languageName: node + linkType: hard + "bn.js@npm:^4.0.0, bn.js@npm:^4.1.0, bn.js@npm:^4.11.9": version: 4.12.1 resolution: "bn.js@npm:4.12.1" @@ -1589,7 +1606,7 @@ __metadata: languageName: node linkType: hard -"braces@npm:^3.0.3": +"braces@npm:^3.0.3, braces@npm:~3.0.2": version: 3.0.3 resolution: "braces@npm:3.0.3" dependencies: @@ -1782,6 +1799,25 @@ __metadata: languageName: node linkType: hard +"chokidar@npm:^3.5.3": + version: 3.6.0 + resolution: "chokidar@npm:3.6.0" + dependencies: + anymatch: "npm:~3.1.2" + braces: "npm:~3.0.2" + fsevents: "npm:~2.3.2" + glob-parent: "npm:~5.1.2" + is-binary-path: "npm:~2.1.0" + is-glob: "npm:~4.0.1" + normalize-path: "npm:~3.0.0" + readdirp: "npm:~3.6.0" + dependenciesMeta: + fsevents: + optional: true + checksum: 10c0/8361dcd013f2ddbe260eacb1f3cb2f2c6f2b0ad118708a343a5ed8158941a39cb8fb1d272e0f389712e74ee90ce8ba864eece9e0e62b9705cb468a2f6d917462 + languageName: node + linkType: hard + "chownr@npm:^3.0.0": version: 3.0.0 resolution: "chownr@npm:3.0.0" @@ -2409,7 +2445,7 @@ __metadata: languageName: node linkType: hard -"fast-glob@npm:^3.3.2": +"fast-glob@npm:^3.2.11, fast-glob@npm:^3.3.2": version: 3.3.3 resolution: "fast-glob@npm:3.3.3" dependencies: @@ -2525,6 +2561,17 @@ __metadata: languageName: node linkType: hard +"fs-extra@npm:^11.1.0": + version: 11.3.0 + resolution: "fs-extra@npm:11.3.0" + dependencies: + graceful-fs: "npm:^4.2.0" + jsonfile: "npm:^6.0.1" + universalify: "npm:^2.0.0" + checksum: 10c0/5f95e996186ff45463059feb115a22fb048bdaf7e487ecee8a8646c78ed8fdca63630e3077d4c16ce677051f5e60d3355a06f3cd61f3ca43f48cc58822a44d0a + languageName: node + linkType: hard + "fs-minipass@npm:^3.0.0": version: 3.0.3 resolution: "fs-minipass@npm:3.0.3" @@ -2588,7 +2635,7 @@ __metadata: languageName: node linkType: hard -"glob-parent@npm:^5.1.2": +"glob-parent@npm:^5.1.2, glob-parent@npm:~5.1.2": version: 5.1.2 resolution: "glob-parent@npm:5.1.2" dependencies: @@ -2650,7 +2697,7 @@ __metadata: languageName: node linkType: hard -"graceful-fs@npm:^4.2.6": +"graceful-fs@npm:^4.1.6, graceful-fs@npm:^4.2.0, graceful-fs@npm:^4.2.6": version: 4.2.11 resolution: "graceful-fs@npm:4.2.11" checksum: 10c0/386d011a553e02bc594ac2ca0bd6d9e4c22d7fa8cfbfc448a6d148c59ea881b092db9dbe3547ae4b88e55f1b01f7c4a2ecc53b310c042793e63aa44cf6c257f2 @@ -2871,6 +2918,15 @@ __metadata: languageName: node linkType: hard +"is-binary-path@npm:~2.1.0": + version: 2.1.0 + resolution: "is-binary-path@npm:2.1.0" + dependencies: + binary-extensions: "npm:^2.0.0" + checksum: 10c0/a16eaee59ae2b315ba36fad5c5dcaf8e49c3e27318f8ab8fa3cdb8772bf559c8d1ba750a589c2ccb096113bb64497084361a25960899cb6172a6925ab6123d38 + languageName: node + linkType: hard + "is-callable@npm:^1.1.3": version: 1.2.7 resolution: "is-callable@npm:1.2.7" @@ -2913,7 +2969,7 @@ __metadata: languageName: node linkType: hard -"is-glob@npm:^4.0.0, is-glob@npm:^4.0.1, is-glob@npm:^4.0.3": +"is-glob@npm:^4.0.0, is-glob@npm:^4.0.1, is-glob@npm:^4.0.3, is-glob@npm:~4.0.1": version: 4.0.3 resolution: "is-glob@npm:4.0.3" dependencies: @@ -3070,6 +3126,19 @@ __metadata: languageName: node linkType: hard +"jsonfile@npm:^6.0.1": + version: 6.1.0 + resolution: "jsonfile@npm:6.1.0" + dependencies: + graceful-fs: "npm:^4.1.6" + universalify: "npm:^2.0.0" + dependenciesMeta: + graceful-fs: + optional: true + checksum: 10c0/4f95b5e8a5622b1e9e8f33c96b7ef3158122f595998114d1e7f03985649ea99cb3cd99ce1ed1831ae94c8c8543ab45ebd044207612f31a56fd08462140e46865 + languageName: node + linkType: hard + "jss-plugin-camel-case@npm:^10.10.0": version: 10.10.0 resolution: "jss-plugin-camel-case@npm:10.10.0" @@ -3505,6 +3574,13 @@ __metadata: languageName: node linkType: hard +"normalize-path@npm:^3.0.0, normalize-path@npm:~3.0.0": + version: 3.0.0 + resolution: "normalize-path@npm:3.0.0" + checksum: 10c0/e008c8142bcc335b5e38cf0d63cfd39d6cf2d97480af9abdbe9a439221fd4d749763bab492a8ee708ce7a194bb00c9da6d0a115018672310850489137b3da046 + languageName: node + linkType: hard + "object-assign@npm:^4.1.1": version: 4.1.1 resolution: "object-assign@npm:4.1.1" @@ -3710,7 +3786,7 @@ __metadata: languageName: node linkType: hard -"picomatch@npm:^2.3.1": +"picomatch@npm:^2.0.4, picomatch@npm:^2.2.1, picomatch@npm:^2.3.1": version: 2.3.1 resolution: "picomatch@npm:2.3.1" checksum: 10c0/26c02b8d06f03206fc2ab8d16f19960f2ff9e81a658f831ecb656d8f17d9edc799e8364b1f4a7873e89d9702dff96204be0fa26fe4181f6843f040f819dac4be @@ -3959,6 +4035,15 @@ __metadata: languageName: node linkType: hard +"readdirp@npm:~3.6.0": + version: 3.6.0 + resolution: "readdirp@npm:3.6.0" + dependencies: + picomatch: "npm:^2.2.1" + checksum: 10c0/6fa848cf63d1b82ab4e985f4cf72bd55b7dcfd8e0a376905804e48c3634b7e749170940ba77b32804d5fe93b3cc521aa95a8d7e7d725f830da6d93f3669ce66b + languageName: node + linkType: hard + "regenerator-runtime@npm:^0.14.0": version: 0.14.1 resolution: "regenerator-runtime@npm:0.14.1" @@ -4567,6 +4652,13 @@ __metadata: languageName: node linkType: hard +"universalify@npm:^2.0.0": + version: 2.0.1 + resolution: "universalify@npm:2.0.1" + checksum: 10c0/73e8ee3809041ca8b818efb141801a1004e3fc0002727f1531f4de613ea281b494a40909596dae4a042a4fb6cd385af5d4db2e137b1362e0e91384b828effd3a + languageName: node + linkType: hard + "uri-js@npm:^4.2.2": version: 4.4.1 resolution: "uri-js@npm:4.4.1" @@ -4618,6 +4710,20 @@ __metadata: languageName: node linkType: hard +"vite-plugin-static-copy@npm:^2.2.0": + version: 2.2.0 + resolution: "vite-plugin-static-copy@npm:2.2.0" + dependencies: + chokidar: "npm:^3.5.3" + fast-glob: "npm:^3.2.11" + fs-extra: "npm:^11.1.0" + picocolors: "npm:^1.0.0" + peerDependencies: + vite: ^5.0.0 || ^6.0.0 + checksum: 10c0/c5174926d66776697bfe8aa3013bfea62a48868c683784973b9b329c43b57a915685031047d397a9c0ae8dd1fd734bde37438af3939d395f9b82ada341b4fff7 + languageName: node + linkType: hard + "vite@npm:^6.0.7": version: 6.0.7 resolution: "vite@npm:6.0.7" @@ -4705,6 +4811,7 @@ __metadata: typescript-eslint: "npm:^8.11.0" vite: "npm:^6.0.7" vite-plugin-node-polyfills: "npm:^0.22.0" + vite-plugin-static-copy: "npm:^2.2.0" languageName: unknown linkType: soft diff --git a/yarn-project/.gitignore b/yarn-project/.gitignore index 131191bc25f..40d4d571bbb 100644 --- a/yarn-project/.gitignore +++ b/yarn-project/.gitignore @@ -23,7 +23,8 @@ end-to-end/addresses.json end-to-end/flame_graph end-to-end/log end-to-end/data -end-to-end/src/web/main.js +end-to-end/src/web/*.gz +end-to-end/src/web/*main.js end-to-end/src/web/main.js.LICENSE.txt l1-artifacts/generated l1-contracts/generated diff --git a/yarn-project/archiver/src/archiver/archiver.test.ts b/yarn-project/archiver/src/archiver/archiver.test.ts index 0eeca5e43ad..5ea4eedb6e6 100644 --- a/yarn-project/archiver/src/archiver/archiver.test.ts +++ b/yarn-project/archiver/src/archiver/archiver.test.ts @@ -180,7 +180,7 @@ describe('Archiver', () => { (b, i) => (b.header.globalVariables.timestamp = new Fr(now + DefaultL1ContractsConfig.ethereumSlotDuration * (i + 1))), ); - const rollupTxs = blocks.map(makeRollupTx); + const rollupTxs = await Promise.all(blocks.map(makeRollupTx)); publicClient.getBlockNumber.mockResolvedValueOnce(2500n).mockResolvedValueOnce(2600n).mockResolvedValueOnce(2700n); @@ -196,7 +196,8 @@ describe('Archiver', () => { mockInbox.read.totalMessagesInserted.mockResolvedValueOnce(2n).mockResolvedValueOnce(6n); - blocks.forEach(b => blobSinkClient.getBlobSidecar.mockResolvedValueOnce([makeBlobFromBlock(b)])); + const blobsFromBlocks = await Promise.all(blocks.map(b => makeBlobFromBlock(b))); + blobsFromBlocks.forEach(blob => blobSinkClient.getBlobSidecar.mockResolvedValueOnce([blob])); makeMessageSentEvent(98n, 1n, 0n); makeMessageSentEvent(99n, 1n, 1n); @@ -276,7 +277,7 @@ describe('Archiver', () => { const numL2BlocksInTest = 2; - const rollupTxs = blocks.map(makeRollupTx); + const rollupTxs = await Promise.all(blocks.map(makeRollupTx)); // Here we set the current L1 block number to 102. L1 to L2 messages after this should not be read. publicClient.getBlockNumber.mockResolvedValue(102n); @@ -294,7 +295,8 @@ describe('Archiver', () => { makeL2BlockProposedEvent(90n, 3n, badArchive); rollupTxs.forEach(tx => publicClient.getTransaction.mockResolvedValueOnce(tx)); - blocks.forEach(b => blobSinkClient.getBlobSidecar.mockResolvedValueOnce([makeBlobFromBlock(b)])); + const blobsFromBlocks = await Promise.all(blocks.map(b => makeBlobFromBlock(b))); + blobsFromBlocks.forEach(blob => blobSinkClient.getBlobSidecar.mockResolvedValueOnce([blob])); await archiver.start(false); @@ -318,7 +320,7 @@ describe('Archiver', () => { const numL2BlocksInTest = 2; - const rollupTxs = blocks.map(makeRollupTx); + const rollupTxs = await Promise.all(blocks.map(makeRollupTx)); publicClient.getBlockNumber.mockResolvedValueOnce(50n).mockResolvedValueOnce(100n); mockRollup.read.status @@ -333,7 +335,8 @@ describe('Archiver', () => { makeL2BlockProposedEvent(80n, 2n, blocks[1].archive.root.toString()); rollupTxs.forEach(tx => publicClient.getTransaction.mockResolvedValueOnce(tx)); - blocks.forEach(b => blobSinkClient.getBlobSidecar.mockResolvedValueOnce([makeBlobFromBlock(b)])); + const blobsFromBlocks = await Promise.all(blocks.map(b => makeBlobFromBlock(b))); + blobsFromBlocks.forEach(blob => blobSinkClient.getBlobSidecar.mockResolvedValueOnce([blob])); await archiver.start(false); @@ -354,7 +357,7 @@ describe('Archiver', () => { const numL2BlocksInTest = 2; - const rollupTxs = blocks.map(makeRollupTx); + const rollupTxs = await Promise.all(blocks.map(makeRollupTx)); publicClient.getBlockNumber.mockResolvedValueOnce(50n).mockResolvedValueOnce(100n).mockResolvedValueOnce(150n); @@ -382,7 +385,8 @@ describe('Archiver', () => { makeL2BlockProposedEvent(80n, 2n, blocks[1].archive.root.toString()); rollupTxs.forEach(tx => publicClient.getTransaction.mockResolvedValueOnce(tx)); - blocks.forEach(b => blobSinkClient.getBlobSidecar.mockResolvedValueOnce([makeBlobFromBlock(b)])); + const blobsFromBlocks = await Promise.all(blocks.map(b => makeBlobFromBlock(b))); + blobsFromBlocks.forEach(blob => blobSinkClient.getBlobSidecar.mockResolvedValueOnce([blob])); await archiver.start(false); @@ -424,12 +428,13 @@ describe('Archiver', () => { l2Block.header.globalVariables.slotNumber = new Fr(notLastL2SlotInEpoch); blocks = [l2Block]; - const rollupTxs = blocks.map(makeRollupTx); + const rollupTxs = await Promise.all(blocks.map(makeRollupTx)); publicClient.getBlockNumber.mockResolvedValueOnce(l1BlockForL2Block); mockRollup.read.status.mockResolvedValueOnce([0n, GENESIS_ROOT, 1n, l2Block.archive.root.toString(), GENESIS_ROOT]); makeL2BlockProposedEvent(l1BlockForL2Block, 1n, l2Block.archive.root.toString()); rollupTxs.forEach(tx => publicClient.getTransaction.mockResolvedValueOnce(tx)); - blocks.forEach(b => blobSinkClient.getBlobSidecar.mockResolvedValueOnce([makeBlobFromBlock(b)])); + const blobsFromBlocks = await Promise.all(blocks.map(b => makeBlobFromBlock(b))); + blobsFromBlocks.forEach(blob => blobSinkClient.getBlobSidecar.mockResolvedValueOnce([blob])); await archiver.start(false); @@ -456,13 +461,14 @@ describe('Archiver', () => { l2Block.header.globalVariables.slotNumber = new Fr(lastL2SlotInEpoch); blocks = [l2Block]; - const rollupTxs = blocks.map(makeRollupTx); + const rollupTxs = await Promise.all(blocks.map(makeRollupTx)); publicClient.getBlockNumber.mockResolvedValueOnce(l1BlockForL2Block); mockRollup.read.status.mockResolvedValueOnce([0n, GENESIS_ROOT, 1n, l2Block.archive.root.toString(), GENESIS_ROOT]); makeL2BlockProposedEvent(l1BlockForL2Block, 1n, l2Block.archive.root.toString()); rollupTxs.forEach(tx => publicClient.getTransaction.mockResolvedValueOnce(tx)); - blocks.forEach(b => blobSinkClient.getBlobSidecar.mockResolvedValueOnce([makeBlobFromBlock(b)])); + const blobsFromBlocks = await Promise.all(blocks.map(b => makeBlobFromBlock(b))); + blobsFromBlocks.forEach(blob => blobSinkClient.getBlobSidecar.mockResolvedValueOnce([blob])); await archiver.start(false); @@ -547,12 +553,12 @@ describe('Archiver', () => { * @param block - The L2Block. * @returns A fake tx with calldata that corresponds to calling process in the Rollup contract. */ -function makeRollupTx(l2Block: L2Block) { +async function makeRollupTx(l2Block: L2Block) { const header = toHex(l2Block.header.toBuffer()); const body = toHex(l2Block.body.toBuffer()); - const blobInput = Blob.getEthBlobEvaluationInputs(Blob.getBlobs(l2Block.body.toBlobFields())); + const blobInput = Blob.getEthBlobEvaluationInputs(await Blob.getBlobs(l2Block.body.toBlobFields())); const archive = toHex(l2Block.archive.root.toBuffer()); - const blockHash = toHex(l2Block.header.hash().toBuffer()); + const blockHash = toHex((await l2Block.header.hash()).toBuffer()); const input = encodeFunctionData({ abi: RollupAbi, functionName: 'propose', diff --git a/yarn-project/archiver/src/archiver/archiver.ts b/yarn-project/archiver/src/archiver/archiver.ts index 88269d6b863..870e3f39a06 100644 --- a/yarn-project/archiver/src/archiver/archiver.ts +++ b/yarn-project/archiver/src/archiver/archiver.ts @@ -803,7 +803,7 @@ export class Archiver implements ArchiveSource, Traceable { async addContractClass(contractClass: ContractClassPublic): Promise { await this.store.addContractClasses( [contractClass], - [computePublicBytecodeCommitment(contractClass.packedBytecode)], + [await computePublicBytecodeCommitment(contractClass.packedBytecode)], 0, ); return; @@ -838,10 +838,13 @@ export class Archiver implements ArchiveSource, Traceable { ); } + const latestBlockHeaderHash = await latestBlockHeader?.hash(); + const provenBlockHeaderHash = await provenBlockHeader?.hash(); + const finalizedBlockHeaderHash = await provenBlockHeader?.hash(); return { - latest: { number: latestBlockNumber, hash: latestBlockHeader?.hash().toString() } as L2BlockId, - proven: { number: provenBlockNumber, hash: provenBlockHeader?.hash().toString() } as L2BlockId, - finalized: { number: provenBlockNumber, hash: provenBlockHeader?.hash().toString() } as L2BlockId, + latest: { number: latestBlockNumber, hash: latestBlockHeaderHash?.toString() } as L2BlockId, + proven: { number: provenBlockNumber, hash: provenBlockHeaderHash?.toString() } as L2BlockId, + finalized: { number: provenBlockNumber, hash: finalizedBlockHeaderHash?.toString() } as L2BlockId, }; } } @@ -890,19 +893,19 @@ class ArchiverStoreHelper * @param allLogs - All logs emitted in a bunch of blocks. */ async #updateRegisteredContractClasses(allLogs: UnencryptedL2Log[], blockNum: number, operation: Operation) { - const contractClasses = allLogs + const contractClassRegisteredEvents = allLogs .filter(log => ContractClassRegisteredEvent.isContractClassRegisteredEvent(log.data)) - .map(log => ContractClassRegisteredEvent.fromLog(log.data)) - .map(e => e.toContractClassPublic()); + .map(log => ContractClassRegisteredEvent.fromLog(log.data)); + + const contractClasses = await Promise.all(contractClassRegisteredEvents.map(e => e.toContractClassPublic())); if (contractClasses.length > 0) { contractClasses.forEach(c => this.#log.verbose(`${Operation[operation]} contract class ${c.id.toString()}`)); if (operation == Operation.Store) { // TODO: Will probably want to create some worker threads to compute these bytecode commitments as they are expensive - return await this.store.addContractClasses( - contractClasses, - contractClasses.map(x => computePublicBytecodeCommitment(x.packedBytecode)), - blockNum, + const commitments = await Promise.all( + contractClasses.map(c => computePublicBytecodeCommitment(c.packedBytecode)), ); + return await this.store.addContractClasses(contractClasses, commitments, blockNum); } else if (operation == Operation.Delete) { return await this.store.deleteContractClasses(contractClasses, blockNum); } @@ -970,10 +973,18 @@ class ArchiverStoreHelper const unconstrainedFns = allFns.filter( (fn): fn is UnconstrainedFunctionWithMembershipProof => 'privateFunctionsArtifactTreeRoot' in fn, ); - const validPrivateFns = privateFns.filter(fn => isValidPrivateFunctionMembershipProof(fn, contractClass)); - const validUnconstrainedFns = unconstrainedFns.filter(fn => - isValidUnconstrainedFunctionMembershipProof(fn, contractClass), + + const privateFunctionsWithValidity = await Promise.all( + privateFns.map(async fn => ({ fn, valid: await isValidPrivateFunctionMembershipProof(fn, contractClass) })), + ); + const validPrivateFns = privateFunctionsWithValidity.filter(({ valid }) => valid).map(({ fn }) => fn); + const unconstrainedFunctionsWithValidity = await Promise.all( + unconstrainedFns.map(async fn => ({ + fn, + valid: await isValidUnconstrainedFunctionMembershipProof(fn, contractClass), + })), ); + const validUnconstrainedFns = unconstrainedFunctionsWithValidity.filter(({ valid }) => valid).map(({ fn }) => fn); const validFnCount = validPrivateFns.length + validUnconstrainedFns.length; if (validFnCount !== allFns.length) { this.#log.warn(`Skipping ${allFns.length - validFnCount} invalid functions`); diff --git a/yarn-project/archiver/src/archiver/archiver_store_test_suite.ts b/yarn-project/archiver/src/archiver/archiver_store_test_suite.ts index a5b0acd4670..a702fb50b9b 100644 --- a/yarn-project/archiver/src/archiver/archiver_store_test_suite.ts +++ b/yarn-project/archiver/src/archiver/archiver_store_test_suite.ts @@ -209,7 +209,7 @@ export function describeArchiverDataStore(testName: string, getStore: () => Arch () => wrapInBlock(blocks[5].data.body.txEffects[2], blocks[5].data), () => wrapInBlock(blocks[1].data.body.txEffects[0], blocks[1].data), ])('retrieves a previously stored transaction', async getExpectedTx => { - const expectedTx = getExpectedTx(); + const expectedTx = await getExpectedTx(); const actualTx = await store.getTxEffect(expectedTx.data.txHash); expect(actualTx).toEqual(expectedTx); }); @@ -227,7 +227,7 @@ export function describeArchiverDataStore(testName: string, getStore: () => Arch ])('tries to retrieves a previously stored transaction after deleted', async getExpectedTx => { await store.unwindBlocks(blocks.length, blocks.length); - const expectedTx = getExpectedTx(); + const expectedTx = await getExpectedTx(); const actualTx = await store.getTxEffect(expectedTx.data.txHash); expect(actualTx).toEqual(undefined); }); @@ -300,10 +300,10 @@ export function describeArchiverDataStore(testName: string, getStore: () => Arch const blockNum = 10; beforeEach(async () => { - contractClass = makeContractClassPublic(); + contractClass = await makeContractClassPublic(); await store.addContractClasses( [contractClass], - [computePublicBytecodeCommitment(contractClass.packedBytecode)], + [await computePublicBytecodeCommitment(contractClass.packedBytecode)], blockNum, ); }); @@ -320,7 +320,7 @@ export function describeArchiverDataStore(testName: string, getStore: () => Arch it('returns contract class if later "deployment" class was deleted', async () => { await store.addContractClasses( [contractClass], - [computePublicBytecodeCommitment(contractClass.packedBytecode)], + [await computePublicBytecodeCommitment(contractClass.packedBytecode)], blockNum + 1, ); await store.deleteContractClasses([contractClass], blockNum + 1); diff --git a/yarn-project/archiver/src/archiver/data_retrieval.ts b/yarn-project/archiver/src/archiver/data_retrieval.ts index 366240c326f..31905ade40c 100644 --- a/yarn-project/archiver/src/archiver/data_retrieval.ts +++ b/yarn-project/archiver/src/archiver/data_retrieval.ts @@ -191,7 +191,7 @@ async function getBlockFromRollupTx( } // TODO(#9101): Once we stop publishing calldata, we will still need the blobCheck below to ensure that the block we are building does correspond to the blob fields - const blobCheck = Blob.getBlobs(blockFields); + const blobCheck = await Blob.getBlobs(blockFields); if (Blob.getEthBlobEvaluationInputs(blobCheck) !== blobInputs) { // NB: We can just check the blobhash here, which is the first 32 bytes of blobInputs // A mismatch means that the fields published in the blob in propose() do NOT match those in the extracted block. diff --git a/yarn-project/archiver/src/archiver/kv_archiver_store/block_store.ts b/yarn-project/archiver/src/archiver/kv_archiver_store/block_store.ts index 41c235ba524..b6574b5f8fa 100644 --- a/yarn-project/archiver/src/archiver/kv_archiver_store/block_store.ts +++ b/yarn-project/archiver/src/archiver/kv_archiver_store/block_store.ts @@ -55,12 +55,12 @@ export class BlockStore { * @param blocks - The L2 blocks to be added to the store. * @returns True if the operation is successful. */ - addBlocks(blocks: L1Published[]): Promise { + async addBlocks(blocks: L1Published[]): Promise { if (blocks.length === 0) { - return Promise.resolve(true); + return true; } - return this.db.transaction(() => { + return await this.db.transaction(async () => { for (const block of blocks) { void this.#blocks.set(block.data.number, { header: block.data.header.toBuffer(), @@ -72,7 +72,7 @@ export class BlockStore { void this.#txIndex.set(tx.txHash.toString(), [block.data.number, i]); }); - void this.#blockBodies.set(block.data.hash().toString(), block.data.body.toBuffer()); + void this.#blockBodies.set((await block.data.hash()).toString(), block.data.body.toBuffer()); } void this.#lastSynchedL1Block.set(blocks[blocks.length - 1].l1.blockNumber); @@ -88,8 +88,8 @@ export class BlockStore { * @param blocksToUnwind - The number of blocks we are to unwind * @returns True if the operation is successful */ - unwindBlocks(from: number, blocksToUnwind: number) { - return this.db.transaction(() => { + async unwindBlocks(from: number, blocksToUnwind: number) { + return await this.db.transaction(async () => { const last = this.getSynchedL2BlockNumber(); if (from != last) { throw new Error(`Can only unwind blocks from the tip (requested ${from} but current tip is ${last})`); @@ -97,7 +97,7 @@ export class BlockStore { for (let i = 0; i < blocksToUnwind; i++) { const blockNumber = from - i; - const block = this.getBlock(blockNumber); + const block = await this.getBlock(blockNumber); if (block === undefined) { throw new Error(`Cannot remove block ${blockNumber} from the store, we don't have it`); @@ -106,7 +106,7 @@ export class BlockStore { block.data.body.txEffects.forEach(tx => { void this.#txIndex.delete(tx.txHash.toString()); }); - const blockHash = block.data.hash().toString(); + const blockHash = (await block.data.hash()).toString(); void this.#blockBodies.delete(blockHash); this.#log.debug(`Unwound block ${blockNumber} ${blockHash}`); } @@ -121,9 +121,10 @@ export class BlockStore { * @param limit - The number of blocks to return. * @returns The requested L2 blocks */ - *getBlocks(start: number, limit: number): IterableIterator> { + async *getBlocks(start: number, limit: number): AsyncIterableIterator> { for (const blockStorage of this.#blocks.values(this.#computeBlockRange(start, limit))) { - yield this.getBlockFromBlockStorage(blockStorage); + const block = await this.getBlockFromBlockStorage(blockStorage); + yield block; } } @@ -132,10 +133,10 @@ export class BlockStore { * @param blockNumber - The number of the block to return. * @returns The requested L2 block. */ - getBlock(blockNumber: number): L1Published | undefined { + getBlock(blockNumber: number): Promise | undefined> { const blockStorage = this.#blocks.get(blockNumber); if (!blockStorage || !blockStorage.header) { - return undefined; + return Promise.resolve(undefined); } return this.getBlockFromBlockStorage(blockStorage); @@ -153,10 +154,10 @@ export class BlockStore { } } - private getBlockFromBlockStorage(blockStorage: BlockStorage) { + private async getBlockFromBlockStorage(blockStorage: BlockStorage) { const header = BlockHeader.fromBuffer(blockStorage.header); const archive = AppendOnlyTreeSnapshot.fromBuffer(blockStorage.archive); - const blockHash = header.hash().toString(); + const blockHash = (await header.hash()).toString(); const blockBodyBuffer = this.#blockBodies.get(blockHash); if (blockBodyBuffer === undefined) { throw new Error( @@ -174,13 +175,13 @@ export class BlockStore { * @param txHash - The txHash of the tx corresponding to the tx effect. * @returns The requested tx effect (or undefined if not found). */ - getTxEffect(txHash: TxHash): InBlock | undefined { + async getTxEffect(txHash: TxHash): Promise | undefined> { const [blockNumber, txIndex] = this.getTxLocation(txHash) ?? []; if (typeof blockNumber !== 'number' || typeof txIndex !== 'number') { return undefined; } - const block = this.getBlock(blockNumber); + const block = await this.getBlock(blockNumber); if (!block) { return undefined; } @@ -188,7 +189,7 @@ export class BlockStore { return { data: block.data.body.txEffects[txIndex], l2BlockNumber: block.data.number, - l2BlockHash: block.data.hash().toString(), + l2BlockHash: (await block.data.hash()).toString(), }; } @@ -197,13 +198,13 @@ export class BlockStore { * @param txHash - The hash of a tx we try to get the receipt for. * @returns The requested tx receipt (or undefined if not found). */ - getSettledTxReceipt(txHash: TxHash): TxReceipt | undefined { + async getSettledTxReceipt(txHash: TxHash): Promise { const [blockNumber, txIndex] = this.getTxLocation(txHash) ?? []; if (typeof blockNumber !== 'number' || typeof txIndex !== 'number') { return undefined; } - const block = this.getBlock(blockNumber)!; + const block = (await this.getBlock(blockNumber))!; const tx = block.data.body.txEffects[txIndex]; return new TxReceipt( @@ -211,7 +212,7 @@ export class BlockStore { TxReceipt.statusFromRevertCode(tx.revertCode), '', tx.transactionFee.toBigInt(), - L2BlockHash.fromField(block.data.hash()), + L2BlockHash.fromField(await block.data.hash()), block.data.number, ); } diff --git a/yarn-project/archiver/src/archiver/kv_archiver_store/kv_archiver_store.ts b/yarn-project/archiver/src/archiver/kv_archiver_store/kv_archiver_store.ts index d14260a5957..91c310d4459 100644 --- a/yarn-project/archiver/src/archiver/kv_archiver_store/kv_archiver_store.ts +++ b/yarn-project/archiver/src/archiver/kv_archiver_store/kv_archiver_store.ts @@ -20,6 +20,7 @@ import { } from '@aztec/circuits.js'; import { FunctionSelector } from '@aztec/foundation/abi'; import { type AztecAddress } from '@aztec/foundation/aztec-address'; +import { toArray } from '@aztec/foundation/iterable'; import { createLogger } from '@aztec/foundation/log'; import { type AztecKVStore } from '@aztec/kv-store'; @@ -63,17 +64,15 @@ export class KVArchiverDataStore implements ArchiverDataStore { return Promise.resolve(this.functionNames.get(selector.toString())); } - registerContractFunctionSignatures(_address: AztecAddress, signatures: string[]): Promise { + async registerContractFunctionSignatures(_address: AztecAddress, signatures: string[]): Promise { for (const sig of signatures) { try { - const selector = FunctionSelector.fromSignature(sig); + const selector = await FunctionSelector.fromSignature(sig); this.functionNames.set(selector.toString(), sig.slice(0, sig.indexOf('('))); } catch { this.#log.warn(`Failed to parse signature: ${sig}. Ignoring`); } } - - return Promise.resolve(); } getContractClass(id: Fr): Promise { @@ -155,12 +154,7 @@ export class KVArchiverDataStore implements ArchiverDataStore { * @returns The requested L2 blocks */ getBlocks(start: number, limit: number): Promise[]> { - try { - return Promise.resolve(Array.from(this.#blockStore.getBlocks(start, limit))); - } catch (err) { - // this function is sync so if any errors are thrown we need to make sure they're passed on as rejected Promises - return Promise.reject(err); - } + return toArray(this.#blockStore.getBlocks(start, limit)); } /** diff --git a/yarn-project/archiver/src/archiver/kv_archiver_store/nullifier_store.ts b/yarn-project/archiver/src/archiver/kv_archiver_store/nullifier_store.ts index 7193f86c5b0..3a300cb907f 100644 --- a/yarn-project/archiver/src/archiver/kv_archiver_store/nullifier_store.ts +++ b/yarn-project/archiver/src/archiver/kv_archiver_store/nullifier_store.ts @@ -16,8 +16,9 @@ export class NullifierStore { } async addNullifiers(blocks: L2Block[]): Promise { + const blockHashes = await Promise.all(blocks.map(block => block.hash())); await this.db.transaction(() => { - blocks.forEach(block => { + blocks.forEach((block, i) => { const dataStartIndexForBlock = block.header.state.partial.nullifierTree.nextAvailableLeafIndex - block.body.txEffects.length * MAX_NULLIFIERS_PER_TX; @@ -25,7 +26,7 @@ export class NullifierStore { const dataStartIndexForTx = dataStartIndexForBlock + txIndex * MAX_NULLIFIERS_PER_TX; txEffects.nullifiers.forEach((nullifier, nullifierIndex) => { void this.#nullifiersToBlockNumber.set(nullifier.toString(), block.number); - void this.#nullifiersToBlockHash.set(nullifier.toString(), block.hash().toString()); + void this.#nullifiersToBlockHash.set(nullifier.toString(), blockHashes[i].toString()); void this.#nullifiersToIndex.set(nullifier.toString(), dataStartIndexForTx + nullifierIndex); }); }); diff --git a/yarn-project/archiver/src/archiver/memory_archiver_store/memory_archiver_store.ts b/yarn-project/archiver/src/archiver/memory_archiver_store/memory_archiver_store.ts index 55b426cce3f..4c5e8d154e3 100644 --- a/yarn-project/archiver/src/archiver/memory_archiver_store/memory_archiver_store.ts +++ b/yarn-project/archiver/src/archiver/memory_archiver_store/memory_archiver_store.ts @@ -191,14 +191,18 @@ export class MemoryArchiverStore implements ArchiverDataStore { * @param blocks - The L2 blocks to be added to the store and the last processed L1 block. * @returns True if the operation is successful. */ - public addBlocks(blocks: L1Published[]): Promise { + public async addBlocks(blocks: L1Published[]): Promise { if (blocks.length === 0) { return Promise.resolve(true); } this.lastL1BlockNewBlocks = blocks[blocks.length - 1].l1.blockNumber; this.l2Blocks.push(...blocks); - this.txEffects.push(...blocks.flatMap(b => b.data.body.txEffects.map(txEffect => wrapInBlock(txEffect, b.data)))); + const flatTxEffects = blocks.flatMap(b => b.data.body.txEffects.map(txEffect => ({ block: b, txEffect }))); + const wrappedTxEffects = await Promise.all( + flatTxEffects.map(flatTxEffect => wrapInBlock(flatTxEffect.txEffect, flatTxEffect.block.data)), + ); + this.txEffects.push(...wrappedTxEffects); return Promise.resolve(true); } @@ -325,22 +329,25 @@ export class MemoryArchiverStore implements ArchiverDataStore { return Promise.resolve(true); } - addNullifiers(blocks: L2Block[]): Promise { - blocks.forEach(block => { - const dataStartIndexForBlock = - block.header.state.partial.nullifierTree.nextAvailableLeafIndex - - block.body.txEffects.length * MAX_NULLIFIERS_PER_TX; - block.body.txEffects.forEach((txEffects, txIndex) => { - const dataStartIndexForTx = dataStartIndexForBlock + txIndex * MAX_NULLIFIERS_PER_TX; - txEffects.nullifiers.forEach((nullifier, nullifierIndex) => { - this.blockScopedNullifiers.set(nullifier.toString(), { - index: BigInt(dataStartIndexForTx + nullifierIndex), - blockNumber: block.number, - blockHash: block.hash().toString(), + async addNullifiers(blocks: L2Block[]): Promise { + await Promise.all( + blocks.map(async block => { + const dataStartIndexForBlock = + block.header.state.partial.nullifierTree.nextAvailableLeafIndex - + block.body.txEffects.length * MAX_NULLIFIERS_PER_TX; + const blockHash = await block.hash(); + block.body.txEffects.forEach((txEffects, txIndex) => { + const dataStartIndexForTx = dataStartIndexForBlock + txIndex * MAX_NULLIFIERS_PER_TX; + txEffects.nullifiers.forEach((nullifier, nullifierIndex) => { + this.blockScopedNullifiers.set(nullifier.toString(), { + index: BigInt(dataStartIndexForTx + nullifierIndex), + blockNumber: block.number, + blockHash: blockHash.toString(), + }); }); }); - }); - }); + }), + ); return Promise.resolve(true); } @@ -448,24 +455,22 @@ export class MemoryArchiverStore implements ArchiverDataStore { * @param txHash - The hash of a tx we try to get the receipt for. * @returns The requested tx receipt (or undefined if not found). */ - public getSettledTxReceipt(txHash: TxHash): Promise { + public async getSettledTxReceipt(txHash: TxHash): Promise { for (const block of this.l2Blocks) { for (const txEffect of block.data.body.txEffects) { if (txEffect.txHash.equals(txHash)) { - return Promise.resolve( - new TxReceipt( - txHash, - TxReceipt.statusFromRevertCode(txEffect.revertCode), - '', - txEffect.transactionFee.toBigInt(), - L2BlockHash.fromField(block.data.hash()), - block.data.number, - ), + return new TxReceipt( + txHash, + TxReceipt.statusFromRevertCode(txEffect.revertCode), + '', + txEffect.transactionFee.toBigInt(), + L2BlockHash.fromField(await block.data.hash()), + block.data.number, ); } } } - return Promise.resolve(undefined); + return undefined; } /** @@ -735,17 +740,15 @@ export class MemoryArchiverStore implements ArchiverDataStore { return Promise.resolve(this.functionNames.get(selector.toString())); } - public registerContractFunctionSignatures(_address: AztecAddress, signatures: string[]): Promise { + public async registerContractFunctionSignatures(_address: AztecAddress, signatures: string[]): Promise { for (const sig of signatures) { try { - const selector = FunctionSelector.fromSignature(sig); + const selector = await FunctionSelector.fromSignature(sig); this.functionNames.set(selector.toString(), sig.slice(0, sig.indexOf('('))); } catch { this.#log.warn(`Failed to parse signature: ${sig}. Ignoring`); } } - - return Promise.resolve(); } public estimateSize(): { mappingSize: number; actualSize: number; numItems: number } { diff --git a/yarn-project/archiver/src/factory.ts b/yarn-project/archiver/src/factory.ts index 9863b4a57ca..7db0968af91 100644 --- a/yarn-project/archiver/src/factory.ts +++ b/yarn-project/archiver/src/factory.ts @@ -53,7 +53,7 @@ async function registerProtocolContracts(store: KVArchiverDataStore) { .map(fn => decodeFunctionSignature(fn.name, fn.parameters)); await store.registerContractFunctionSignatures(contract.address, publicFunctionSignatures); - const bytecodeCommitment = computePublicBytecodeCommitment(contractClassPublic.packedBytecode); + const bytecodeCommitment = await computePublicBytecodeCommitment(contractClassPublic.packedBytecode); await store.addContractClasses([contractClassPublic], [bytecodeCommitment], blockNumber); await store.addContractInstances([contract.instance], blockNumber); } @@ -67,11 +67,13 @@ async function registerProtocolContracts(store: KVArchiverDataStore) { async function registerCommonContracts(store: KVArchiverDataStore) { const blockNumber = 0; const artifacts = [TokenBridgeContractArtifact, TokenContractArtifact]; - const classes = artifacts.map(artifact => ({ - ...getContractClassFromArtifact(artifact), - privateFunctions: [], - unconstrainedFunctions: [], - })); - const bytecodeCommitments = classes.map(x => computePublicBytecodeCommitment(x.packedBytecode)); + const classes = await Promise.all( + artifacts.map(async artifact => ({ + ...(await getContractClassFromArtifact(artifact)), + privateFunctions: [], + unconstrainedFunctions: [], + })), + ); + const bytecodeCommitments = await Promise.all(classes.map(x => computePublicBytecodeCommitment(x.packedBytecode))); await store.addContractClasses(classes, bytecodeCommitments, blockNumber); } diff --git a/yarn-project/archiver/src/test/mock_l2_block_source.ts b/yarn-project/archiver/src/test/mock_l2_block_source.ts index 96f89f959d4..8f6676f59ea 100644 --- a/yarn-project/archiver/src/test/mock_l2_block_source.ts +++ b/yarn-project/archiver/src/test/mock_l2_block_source.ts @@ -126,7 +126,7 @@ export class MockL2BlockSource implements L2BlockSource { * @param txHash - The hash of a transaction which resulted in the returned tx effect. * @returns The requested tx effect. */ - public getTxEffect(txHash: TxHash) { + public async getTxEffect(txHash: TxHash) { const match = this.l2Blocks .flatMap(b => b.body.txEffects.map(tx => [tx, b] as const)) .find(([tx]) => tx.txHash.equals(txHash)); @@ -134,7 +134,7 @@ export class MockL2BlockSource implements L2BlockSource { return Promise.resolve(undefined); } const [txEffect, block] = match; - return Promise.resolve({ data: txEffect, l2BlockNumber: block.number, l2BlockHash: block.hash().toString() }); + return { data: txEffect, l2BlockNumber: block.number, l2BlockHash: (await block.hash()).toString() }; } /** @@ -142,24 +142,22 @@ export class MockL2BlockSource implements L2BlockSource { * @param txHash - The hash of a tx we try to get the receipt for. * @returns The requested tx receipt (or undefined if not found). */ - public getSettledTxReceipt(txHash: TxHash): Promise { + public async getSettledTxReceipt(txHash: TxHash): Promise { for (const block of this.l2Blocks) { for (const txEffect of block.body.txEffects) { if (txEffect.txHash.equals(txHash)) { - return Promise.resolve( - new TxReceipt( - txHash, - TxStatus.SUCCESS, - '', - txEffect.transactionFee.toBigInt(), - L2BlockHash.fromField(block.hash()), - block.number, - ), + return new TxReceipt( + txHash, + TxStatus.SUCCESS, + '', + txEffect.transactionFee.toBigInt(), + L2BlockHash.fromField(await block.hash()), + block.number, ); } } } - return Promise.resolve(undefined); + return undefined; } async getL2Tips(): Promise { @@ -169,10 +167,14 @@ export class MockL2BlockSource implements L2BlockSource { await this.getProvenBlockNumber(), ] as const; + const latestBlock = this.l2Blocks[latest - 1]; + const provenBlock = this.l2Blocks[proven - 1]; + const finalizedBlock = this.l2Blocks[finalized - 1]; + return { - latest: { number: latest, hash: this.l2Blocks[latest - 1]?.hash().toString() }, - proven: { number: proven, hash: this.l2Blocks[proven - 1]?.hash().toString() }, - finalized: { number: finalized, hash: this.l2Blocks[finalized - 1]?.hash().toString() }, + latest: { number: latest, hash: (await latestBlock?.hash())?.toString() }, + proven: { number: proven, hash: (await provenBlock?.hash())?.toString() }, + finalized: { number: finalized, hash: (await finalizedBlock?.hash())?.toString() }, }; } diff --git a/yarn-project/aztec-node/src/aztec-node/server.test.ts b/yarn-project/aztec-node/src/aztec-node/server.test.ts index df982353430..7c905a6bc77 100644 --- a/yarn-project/aztec-node/src/aztec-node/server.test.ts +++ b/yarn-project/aztec-node/src/aztec-node/server.test.ts @@ -101,7 +101,7 @@ describe('aztec node', () => { describe('tx validation', () => { it('tests that the node correctly validates double spends', async () => { - const txs = [mockTxForRollup(0x10000), mockTxForRollup(0x20000)]; + const txs = await Promise.all([mockTxForRollup(0x10000), mockTxForRollup(0x20000)]); txs.forEach(tx => { tx.data.constants.txContext.chainId = chainId; }); @@ -138,7 +138,7 @@ describe('aztec node', () => { }); it('tests that the node correctly validates chain id', async () => { - const tx = mockTxForRollup(0x10000); + const tx = await mockTxForRollup(0x10000); tx.data.constants.txContext.chainId = chainId; expect(await node.isValidTx(tx)).toEqual({ result: 'valid' }); @@ -150,7 +150,7 @@ describe('aztec node', () => { }); it('tests that the node correctly validates max block numbers', async () => { - const txs = [mockTxForRollup(0x10000), mockTxForRollup(0x20000), mockTxForRollup(0x30000)]; + const txs = await Promise.all([mockTxForRollup(0x10000), mockTxForRollup(0x20000), mockTxForRollup(0x30000)]); txs.forEach(tx => { tx.data.constants.txContext.chainId = chainId; }); diff --git a/yarn-project/aztec-node/src/aztec-node/server.ts b/yarn-project/aztec-node/src/aztec-node/server.ts index 333a2bc59cc..48731a15c7e 100644 --- a/yarn-project/aztec-node/src/aztec-node/server.ts +++ b/yarn-project/aztec-node/src/aztec-node/server.ts @@ -366,7 +366,7 @@ export class AztecNodeService implements AztecNode, Traceable { // to emit the corresponding nullifier, which is now being checked. Note that this method // is only called by the PXE to check if a contract is publicly registered. if (klazz) { - const classNullifier = siloNullifier(AztecAddress.fromNumber(REGISTERER_CONTRACT_ADDRESS), id); + const classNullifier = await siloNullifier(AztecAddress.fromNumber(REGISTERER_CONTRACT_ADDRESS), id); const worldState = await this.#getWorldState('latest'); const [index] = await worldState.findLeafIndices(MerkleTreeId.NULLIFIER_TREE, [classNullifier.toBuffer()]); this.log.debug(`Registration nullifier ${classNullifier} for contract class ${id} found at index ${index}`); @@ -426,7 +426,7 @@ export class AztecNodeService implements AztecNode, Traceable { */ public async sendTx(tx: Tx) { const timer = new Timer(); - const txHash = tx.getTxHash().toString(); + const txHash = (await tx.getTxHash()).toString(); const valid = await this.isValidTx(tx); if (valid.result !== 'valid') { @@ -440,7 +440,7 @@ export class AztecNodeService implements AztecNode, Traceable { await this.p2pClient!.sendTx(tx); this.metrics.receivedTx(timer.ms(), true); - this.log.info(`Received tx ${tx.getTxHash()}`, { txHash }); + this.log.info(`Received tx ${txHash}`, { txHash }); } public async getTxReceipt(txHash: TxHash): Promise { @@ -812,7 +812,7 @@ export class AztecNodeService implements AztecNode, Traceable { */ public async getPublicStorageAt(contract: AztecAddress, slot: Fr, blockNumber: L2BlockNumber): Promise { const committedDb = await this.#getWorldState(blockNumber); - const leafSlot = computePublicDataTreeLeafSlot(contract, slot); + const leafSlot = await computePublicDataTreeLeafSlot(contract, slot); const lowLeafResult = await committedDb.getPreviousValueIndex(MerkleTreeId.PUBLIC_DATA_TREE, leafSlot.toBigInt()); if (!lowLeafResult || !lowLeafResult.alreadyPresent) { @@ -840,11 +840,11 @@ export class AztecNodeService implements AztecNode, Traceable { * Simulates the public part of a transaction with the current state. * @param tx - The transaction to simulate. **/ - @trackSpan('AztecNodeService.simulatePublicCalls', (tx: Tx) => ({ - [Attributes.TX_HASH]: tx.getTxHash().toString(), + @trackSpan('AztecNodeService.simulatePublicCalls', async (tx: Tx) => ({ + [Attributes.TX_HASH]: (await tx.getTxHash()).toString(), })) public async simulatePublicCalls(tx: Tx, enforceFeePayment = true): Promise { - const txHash = tx.getTxHash(); + const txHash = await tx.getTxHash(); const blockNumber = (await this.blockSource.getBlockNumber()) + 1; // If sequencer is not initialized, we just set these values to zero for simulation. @@ -901,7 +901,7 @@ export class AztecNodeService implements AztecNode, Traceable { blockNumber, l1ChainId: this.l1ChainId, enforceFees: !!this.config.enforceFees, - setupAllowList: this.config.allowedInSetup ?? getDefaultAllowedSetupFunctions(), + setupAllowList: this.config.allowedInSetup ?? (await getDefaultAllowedSetupFunctions()), gasFees: await this.getCurrentBaseFees(), }); @@ -910,7 +910,7 @@ export class AztecNodeService implements AztecNode, Traceable { public async setConfig(config: Partial): Promise { const newConfig = { ...this.config, ...config }; - this.sequencer?.updateSequencerConfig(config); + await this.sequencer?.updateSequencerConfig(config); if (newConfig.realProofs !== this.config.realProofs) { this.proofVerifier = config.realProofs ? await BBCircuitVerifier.new(newConfig) : new TestCircuitVerifier(); diff --git a/yarn-project/aztec.js/package.json b/yarn-project/aztec.js/package.json index 82ff6558047..a34ebb907cc 100644 --- a/yarn-project/aztec.js/package.json +++ b/yarn-project/aztec.js/package.json @@ -94,6 +94,7 @@ "@types/jest": "^29.5.0", "@types/node": "^18.7.23", "buffer": "^6.0.3", + "copy-webpack-plugin": "^12.0.2", "crypto-browserify": "^3.12.0", "jest": "^29.5.0", "jest-mock-extended": "^3.0.3", diff --git a/yarn-project/aztec.js/src/account_manager/deploy_account_method.ts b/yarn-project/aztec.js/src/account_manager/deploy_account_method.ts index 6a1b1db1f42..3e80ae20258 100644 --- a/yarn-project/aztec.js/src/account_manager/deploy_account_method.ts +++ b/yarn-project/aztec.js/src/account_manager/deploy_account_method.ts @@ -4,7 +4,7 @@ import { type FunctionArtifact, FunctionSelector, encodeArguments, - getFunctionArtifact, + getFunctionArtifactByName, } from '@aztec/foundation/abi'; import { type AuthWitnessProvider } from '../account/interface.js'; @@ -42,7 +42,7 @@ export class DeployAccountMethod extends DeployMethod { this.#authWitnessProvider = authWitnessProvider; this.#feePaymentArtifact = typeof feePaymentNameOrArtifact === 'string' - ? getFunctionArtifact(artifact, feePaymentNameOrArtifact) + ? getFunctionArtifactByName(artifact, feePaymentNameOrArtifact) : feePaymentNameOrArtifact; } @@ -53,7 +53,7 @@ export class DeployAccountMethod extends DeployMethod { if (options.fee && this.#feePaymentArtifact) { const { address } = await this.getInstance(); - const emptyAppPayload = EntrypointPayload.fromAppExecution([]); + const emptyAppPayload = await EntrypointPayload.fromAppExecution([]); const fee = await this.getDefaultFeeOptions(options.fee); const feePayload = await EntrypointPayload.fromFeeOptions(address, fee); @@ -61,7 +61,7 @@ export class DeployAccountMethod extends DeployMethod { name: this.#feePaymentArtifact.name, to: address, args: encodeArguments(this.#feePaymentArtifact, [emptyAppPayload, feePayload, false]), - selector: FunctionSelector.fromNameAndParameters( + selector: await FunctionSelector.fromNameAndParameters( this.#feePaymentArtifact.name, this.#feePaymentArtifact.parameters, ), @@ -74,7 +74,7 @@ export class DeployAccountMethod extends DeployMethod { exec.hashedArguments ??= []; exec.authWitnesses.push( - await this.#authWitnessProvider.createAuthWit(computeCombinedPayloadHash(emptyAppPayload, feePayload)), + await this.#authWitnessProvider.createAuthWit(await computeCombinedPayloadHash(emptyAppPayload, feePayload)), ); exec.hashedArguments.push(...emptyAppPayload.hashedArguments); diff --git a/yarn-project/aztec.js/src/api/init.ts b/yarn-project/aztec.js/src/api/init.ts deleted file mode 100644 index 9654b9c8042..00000000000 --- a/yarn-project/aztec.js/src/api/init.ts +++ /dev/null @@ -1,10 +0,0 @@ -import { init } from '@aztec/foundation/crypto'; - -/** - * This should only be needed to be called in CJS environments that don't have top level await. - * Initializes any asynchronous subsystems required to use the library. - * At time of writing, this is just our foundation crypto lib. - */ -export async function initAztecJs() { - await init(); -} diff --git a/yarn-project/aztec.js/src/contract/contract_base.ts b/yarn-project/aztec.js/src/contract/contract_base.ts index 94efbb63a9e..5d0ef204b58 100644 --- a/yarn-project/aztec.js/src/contract/contract_base.ts +++ b/yarn-project/aztec.js/src/contract/contract_base.ts @@ -18,7 +18,7 @@ export type ContractMethod = ((...args: any[]) => ContractFunctionInteraction) & /** * The unique identifier for a contract function in bytecode. */ - readonly selector: FunctionSelector; + selector: () => Promise; }; /** @@ -62,7 +62,7 @@ export class ContractBase { * A getter for users to fetch the function selector. * @returns Selector of the function. */ - get selector() { + selector() { return FunctionSelector.fromNameAndParameters(f.name, f.parameters); }, }); 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 332206badc2..0949dc78083 100644 --- a/yarn-project/aztec.js/src/contract/contract_function_interaction.ts +++ b/yarn-project/aztec.js/src/contract/contract_function_interaction.ts @@ -64,7 +64,7 @@ export class ContractFunctionInteraction extends BaseContractInteraction { if (this.functionDao.functionType === FunctionType.UNCONSTRAINED) { throw new Error("Can't call `create` on an unconstrained function."); } - const calls = [this.request()]; + const calls = [await this.request()]; const fee = await this.getFeeOptions({ calls, ...opts }); const { nonce, cancellable } = opts; return await this.wallet.createTxExecutionRequest({ calls, fee, nonce, cancellable }); @@ -76,13 +76,13 @@ export class ContractFunctionInteraction extends BaseContractInteraction { * block for constructing batch requests. * @returns An execution request wrapped in promise. */ - public request(): FunctionCall { + public async request(): Promise { // docs:end:request const args = encodeArguments(this.functionDao, this.args); return { name: this.functionDao.name, args, - selector: FunctionSelector.fromNameAndParameters(this.functionDao.name, this.functionDao.parameters), + selector: await FunctionSelector.fromNameAndParameters(this.functionDao.name, this.functionDao.parameters), type: this.functionDao.functionType, to: this.contractAddress, isStatic: this.functionDao.isStatic, diff --git a/yarn-project/aztec.js/src/contract/deploy_method.ts b/yarn-project/aztec.js/src/contract/deploy_method.ts index 136be11473c..55eb4500de9 100644 --- a/yarn-project/aztec.js/src/contract/deploy_method.ts +++ b/yarn-project/aztec.js/src/contract/deploy_method.ts @@ -144,7 +144,7 @@ export class DeployMethod extends Bas // Obtain contract class from artifact and check it matches the reported one by the instance. // TODO(@spalladino): We're unnecessarily calculating the contract class multiple times here. - const contractClass = getContractClassFromArtifact(this.artifact); + const contractClass = await getContractClassFromArtifact(this.artifact); if (!instance.contractClassId.equals(contractClass.id)) { throw new Error( `Contract class mismatch when deploying contract: got ${instance.contractClassId.toString()} from instance and ${contractClass.id.toString()} from artifact`, @@ -161,14 +161,15 @@ export class DeployMethod extends Bas this.log.info( `Creating request for registering contract class ${contractClass.id.toString()} as part of deployment for ${instance.address.toString()}`, ); - calls.push((await registerContractClass(this.wallet, this.artifact)).request()); + const registerContractClassInteraction = await registerContractClass(this.wallet, this.artifact); + calls.push(await registerContractClassInteraction.request()); } } // Deploy the contract via the instance deployer. if (!options.skipPublicDeployment) { const deploymentInteraction = await deployInstance(this.wallet, instance); - calls.push(deploymentInteraction.request()); + calls.push(await deploymentInteraction.request()); } return { calls }; @@ -191,7 +192,7 @@ export class DeployMethod extends Bas this.constructorArtifact, this.args, ); - calls.push(constructorCall.request()); + calls.push(await constructorCall.request()); } return { calls }; } diff --git a/yarn-project/aztec.js/src/contract/get_gas_limits.test.ts b/yarn-project/aztec.js/src/contract/get_gas_limits.test.ts index a49c639fb59..8a25b836e37 100644 --- a/yarn-project/aztec.js/src/contract/get_gas_limits.test.ts +++ b/yarn-project/aztec.js/src/contract/get_gas_limits.test.ts @@ -9,7 +9,7 @@ describe('getGasLimits', () => { beforeEach(async () => { txSimulationResult = await mockSimulatedTx(); - const tx = mockTxForRollup(); + const tx = await mockTxForRollup(); tx.data.gasUsed = Gas.from({ daGas: 100, l2Gas: 200 }); txSimulationResult.publicInputs = tx.data; diff --git a/yarn-project/aztec.js/src/deployment/broadcast_function.ts b/yarn-project/aztec.js/src/deployment/broadcast_function.ts index a0367788a4a..67398836735 100644 --- a/yarn-project/aztec.js/src/deployment/broadcast_function.ts +++ b/yarn-project/aztec.js/src/deployment/broadcast_function.ts @@ -6,7 +6,7 @@ import { createUnconstrainedFunctionMembershipProof, getContractClassFromArtifact, } from '@aztec/circuits.js'; -import { type ContractArtifact, type FunctionSelector, FunctionType, bufferAsFields } from '@aztec/foundation/abi'; +import { type ContractArtifact, FunctionSelector, FunctionType, bufferAsFields } from '@aztec/foundation/abi'; import { padArrayEnd } from '@aztec/foundation/collection'; import { Fr } from '@aztec/foundation/fields'; @@ -28,8 +28,15 @@ export async function broadcastPrivateFunction( artifact: ContractArtifact, selector: FunctionSelector, ): Promise { - const contractClass = getContractClassFromArtifact(artifact); - const privateFunctionArtifact = artifact.functions.find(fn => selector.equals(fn)); + const contractClass = await getContractClassFromArtifact(artifact); + const privateFunctions = artifact.functions.filter(fn => fn.functionType === FunctionType.PRIVATE); + const functionsAndSelectors = await Promise.all( + privateFunctions.map(async fn => ({ + f: fn, + selector: await FunctionSelector.fromNameAndParameters(fn.name, fn.parameters), + })), + ); + const privateFunctionArtifact = functionsAndSelectors.find(fn => selector.equals(fn.selector))?.f; if (!privateFunctionArtifact) { throw new Error(`Private function with selector ${selector.toString()} not found`); } @@ -42,9 +49,9 @@ export async function broadcastPrivateFunction( unconstrainedFunctionsArtifactTreeRoot, privateFunctionTreeSiblingPath, privateFunctionTreeLeafIndex, - } = createPrivateFunctionMembershipProof(selector, artifact); + } = await createPrivateFunctionMembershipProof(selector, artifact); - const vkHash = computeVerificationKeyHash(privateFunctionArtifact); + const vkHash = await computeVerificationKeyHash(privateFunctionArtifact); const bytecode = bufferAsFields( privateFunctionArtifact.bytecode, MAX_PACKED_BYTECODE_SIZE_PER_PRIVATE_FUNCTION_IN_FIELDS, @@ -82,14 +89,18 @@ export async function broadcastUnconstrainedFunction( artifact: ContractArtifact, selector: FunctionSelector, ): Promise { - const contractClass = getContractClassFromArtifact(artifact); - const functionArtifactIndex = artifact.functions.findIndex( - fn => fn.functionType === FunctionType.UNCONSTRAINED && selector.equals(fn), + const contractClass = await getContractClassFromArtifact(artifact); + const unconstrainedFunctions = artifact.functions.filter(fn => fn.functionType === FunctionType.UNCONSTRAINED); + const unconstrainedFunctionsAndSelectors = await Promise.all( + unconstrainedFunctions.map(async fn => ({ + f: fn, + selector: await FunctionSelector.fromNameAndParameters(fn.name, fn.parameters), + })), ); - if (functionArtifactIndex < 0) { + const unconstrainedFunctionArtifact = unconstrainedFunctionsAndSelectors.find(fn => selector.equals(fn.selector))?.f; + if (!unconstrainedFunctionArtifact) { throw new Error(`Unconstrained function with selector ${selector.toString()} not found`); } - const functionArtifact = artifact.functions[functionArtifactIndex]; const { artifactMetadataHash, @@ -97,9 +108,12 @@ export async function broadcastUnconstrainedFunction( artifactTreeSiblingPath, functionMetadataHash, privateFunctionsArtifactTreeRoot, - } = createUnconstrainedFunctionMembershipProof(selector, artifact); + } = await createUnconstrainedFunctionMembershipProof(selector, artifact); - const bytecode = bufferAsFields(functionArtifact.bytecode, MAX_PACKED_BYTECODE_SIZE_PER_PRIVATE_FUNCTION_IN_FIELDS); + const bytecode = bufferAsFields( + unconstrainedFunctionArtifact.bytecode, + MAX_PACKED_BYTECODE_SIZE_PER_PRIVATE_FUNCTION_IN_FIELDS, + ); await wallet.addCapsule(bytecode); diff --git a/yarn-project/aztec.js/src/deployment/register_class.ts b/yarn-project/aztec.js/src/deployment/register_class.ts index 2a708cf6589..ed178b91cd8 100644 --- a/yarn-project/aztec.js/src/deployment/register_class.ts +++ b/yarn-project/aztec.js/src/deployment/register_class.ts @@ -18,7 +18,7 @@ export async function registerContractClass( emitPublicBytecode = defaultEmitPublicBytecode, ): Promise { const { artifactHash, privateFunctionsRoot, publicBytecodeCommitment, packedBytecode } = - getContractClassFromArtifact(artifact); + await getContractClassFromArtifact(artifact); const encodedBytecode = bufferAsFields(packedBytecode, MAX_PACKED_PUBLIC_BYTECODE_SIZE_IN_FIELDS); const registerer = await getRegistererContract(wallet); await wallet.addCapsule(encodedBytecode); diff --git a/yarn-project/aztec.js/src/entrypoint/default_entrypoint.ts b/yarn-project/aztec.js/src/entrypoint/default_entrypoint.ts index 82c9fd3c758..b20ac3b4312 100644 --- a/yarn-project/aztec.js/src/entrypoint/default_entrypoint.ts +++ b/yarn-project/aztec.js/src/entrypoint/default_entrypoint.ts @@ -10,7 +10,7 @@ import { type EntrypointInterface, type ExecutionRequestInit } from './entrypoin export class DefaultEntrypoint implements EntrypointInterface { constructor(private chainId: number, private protocolVersion: number) {} - createTxExecutionRequest(exec: ExecutionRequestInit): Promise { + async createTxExecutionRequest(exec: ExecutionRequestInit): Promise { const { fee, calls, authWitnesses = [], hashedArguments = [] } = exec; if (calls.length > 1) { @@ -23,7 +23,7 @@ export class DefaultEntrypoint implements EntrypointInterface { throw new Error('Public entrypoints are not allowed'); } - const entrypointHashedValues = HashedValues.fromValues(call.args); + const entrypointHashedValues = await HashedValues.fromValues(call.args); const txContext = new TxContext(this.chainId, this.protocolVersion, fee.gasSettings); return Promise.resolve( new TxExecutionRequest( diff --git a/yarn-project/aztec.js/src/entrypoint/default_multi_call_entrypoint.ts b/yarn-project/aztec.js/src/entrypoint/default_multi_call_entrypoint.ts index df27e73f2c0..8f234e37acb 100644 --- a/yarn-project/aztec.js/src/entrypoint/default_multi_call_entrypoint.ts +++ b/yarn-project/aztec.js/src/entrypoint/default_multi_call_entrypoint.ts @@ -14,16 +14,16 @@ export class DefaultMultiCallEntrypoint implements EntrypointInterface { private address: AztecAddress = ProtocolContractAddress.MultiCallEntrypoint, ) {} - createTxExecutionRequest(executions: ExecutionRequestInit): Promise { + async createTxExecutionRequest(executions: ExecutionRequestInit): Promise { const { fee, calls, authWitnesses = [], hashedArguments = [] } = executions; - const payload = EntrypointPayload.fromAppExecution(calls); + const payload = await EntrypointPayload.fromAppExecution(calls); const abi = this.getEntrypointAbi(); - const entrypointHashedArgs = HashedValues.fromValues(encodeArguments(abi, [payload])); + const entrypointHashedArgs = await HashedValues.fromValues(encodeArguments(abi, [payload])); const txRequest = TxExecutionRequest.from({ firstCallArgsHash: entrypointHashedArgs.hash, origin: this.address, - functionSelector: FunctionSelector.fromNameAndParameters(abi.name, abi.parameters), + functionSelector: await FunctionSelector.fromNameAndParameters(abi.name, abi.parameters), txContext: new TxContext(this.chainId, this.version, fee.gasSettings), argsOfCalls: [...payload.hashedArguments, ...hashedArguments, entrypointHashedArgs], authWitnesses, diff --git a/yarn-project/aztec.js/src/entrypoint/payload.ts b/yarn-project/aztec.js/src/entrypoint/payload.ts index c6f3593c1e5..ca64245092c 100644 --- a/yarn-project/aztec.js/src/entrypoint/payload.ts +++ b/yarn-project/aztec.js/src/entrypoint/payload.ts @@ -56,19 +56,22 @@ type EncodedFunctionCall = { /** Assembles an entrypoint payload */ export abstract class EntrypointPayload { - #hashedArguments: HashedValues[] = []; - #functionCalls: EncodedFunctionCall[] = []; - #nonce: Fr; - #generatorIndex: number; - - protected constructor(functionCalls: FunctionCall[], generatorIndex: number, nonce = Fr.random()) { + protected constructor( + private functionCalls: EncodedFunctionCall[], + private _hashedArguments: HashedValues[], + private generatorIndex: number, + private _nonce: Fr, + ) {} + + protected static async create(functionCalls: FunctionCall[]) { + const hashedArguments: HashedValues[] = []; for (const call of functionCalls) { - this.#hashedArguments.push(HashedValues.fromValues(call.args)); + hashedArguments.push(await HashedValues.fromValues(call.args)); } /* eslint-disable camelcase */ - this.#functionCalls = functionCalls.map((call, index) => ({ - args_hash: this.#hashedArguments[index].hash, + const encodedFunctionCalls = functionCalls.map((call, index) => ({ + args_hash: hashedArguments[index].hash, function_selector: call.selector.toField(), target_address: call.to.toField(), is_public: call.type == FunctionType.PUBLIC, @@ -76,8 +79,10 @@ export abstract class EntrypointPayload { })); /* eslint-enable camelcase */ - this.#generatorIndex = generatorIndex; - this.#nonce = nonce; + return { + encodedFunctionCalls, + hashedArguments, + }; } /* eslint-disable camelcase */ @@ -86,7 +91,7 @@ export abstract class EntrypointPayload { * @internal */ get function_calls() { - return this.#functionCalls; + return this.functionCalls; } /* eslint-enable camelcase */ @@ -95,14 +100,14 @@ export abstract class EntrypointPayload { * @internal */ get nonce() { - return this.#nonce; + return this._nonce; } /** * The hashed arguments for the function calls */ get hashedArguments() { - return this.#hashedArguments; + return this._hashedArguments; } /** @@ -116,12 +121,12 @@ export abstract class EntrypointPayload { * @returns The hash of the payload */ hash() { - return poseidon2HashWithSeparator(this.toFields(), this.#generatorIndex); + return poseidon2HashWithSeparator(this.toFields(), this.generatorIndex); } /** Serializes the function calls to an array of fields. */ protected functionCallsToFields() { - return this.#functionCalls.flatMap(call => [ + return this.functionCalls.flatMap(call => [ call.args_hash, call.function_selector, call.target_address, @@ -135,8 +140,9 @@ export abstract class EntrypointPayload { * @param functionCalls - The function calls to execute * @returns The execution payload */ - static fromFunctionCalls(functionCalls: FunctionCall[]) { - return new AppEntrypointPayload(functionCalls, 0); + static async fromFunctionCalls(functionCalls: FunctionCall[]) { + const { encodedFunctionCalls, hashedArguments } = await this.create(functionCalls); + return new AppEntrypointPayload(encodedFunctionCalls, hashedArguments, 0, Fr.random()); } /** @@ -145,12 +151,13 @@ export abstract class EntrypointPayload { * @param nonce - The nonce for the payload, used to emit a nullifier identifying the call * @returns The execution payload */ - static fromAppExecution(functionCalls: FunctionCall[] | Tuple, nonce = Fr.random()) { + static async fromAppExecution(functionCalls: FunctionCall[] | Tuple, nonce = Fr.random()) { if (functionCalls.length > APP_MAX_CALLS) { throw new Error(`Expected at most ${APP_MAX_CALLS} function calls, got ${functionCalls.length}`); } const paddedCalls = padArrayEnd(functionCalls, FunctionCall.empty(), APP_MAX_CALLS); - return new AppEntrypointPayload(paddedCalls, GeneratorIndex.SIGNATURE_PAYLOAD, nonce); + const { encodedFunctionCalls, hashedArguments } = await this.create(paddedCalls); + return new AppEntrypointPayload(encodedFunctionCalls, hashedArguments, GeneratorIndex.SIGNATURE_PAYLOAD, nonce); } /** @@ -164,7 +171,14 @@ export abstract class EntrypointPayload { const feePayer = await feeOpts?.paymentMethod.getFeePayer(feeOpts?.gasSettings); const isFeePayer = !!feePayer && feePayer.equals(sender); const paddedCalls = padArrayEnd(calls, FunctionCall.empty(), FEE_MAX_CALLS); - return new FeeEntrypointPayload(paddedCalls, GeneratorIndex.FEE_PAYLOAD, isFeePayer); + const { encodedFunctionCalls, hashedArguments } = await this.create(paddedCalls); + return new FeeEntrypointPayload( + encodedFunctionCalls, + hashedArguments, + GeneratorIndex.FEE_PAYLOAD, + Fr.random(), + isFeePayer, + ); } } @@ -179,8 +193,14 @@ class AppEntrypointPayload extends EntrypointPayload { class FeeEntrypointPayload extends EntrypointPayload { #isFeePayer: boolean; - constructor(functionCalls: FunctionCall[], generatorIndex: number, isFeePayer: boolean) { - super(functionCalls, generatorIndex); + constructor( + functionCalls: EncodedFunctionCall[], + hashedArguments: HashedValues[], + generatorIndex: number, + nonce: Fr, + isFeePayer: boolean, + ) { + super(functionCalls, hashedArguments, generatorIndex, nonce); this.#isFeePayer = isFeePayer; } @@ -202,6 +222,12 @@ class FeeEntrypointPayload extends EntrypointPayload { * @param feePayload - A fee payload. * @returns A hash of a combined payload. */ -export function computeCombinedPayloadHash(appPayload: AppEntrypointPayload, feePayload: FeeEntrypointPayload): Fr { - return poseidon2HashWithSeparator([appPayload.hash(), feePayload.hash()], GeneratorIndex.COMBINED_PAYLOAD); +export async function computeCombinedPayloadHash( + appPayload: AppEntrypointPayload, + feePayload: FeeEntrypointPayload, +): Promise { + return poseidon2HashWithSeparator( + [await appPayload.hash(), await feePayload.hash()], + GeneratorIndex.COMBINED_PAYLOAD, + ); } diff --git a/yarn-project/aztec.js/src/fee/fee_juice_payment_method_with_claim.ts b/yarn-project/aztec.js/src/fee/fee_juice_payment_method_with_claim.ts index a63aafd7e0e..2ede963265a 100644 --- a/yarn-project/aztec.js/src/fee/fee_juice_payment_method_with_claim.ts +++ b/yarn-project/aztec.js/src/fee/fee_juice_payment_method_with_claim.ts @@ -24,7 +24,7 @@ export class FeeJuicePaymentMethodWithClaim extends FeeJuicePaymentMethod { */ override async getFunctionCalls(): Promise { const canonicalFeeJuice = await getCanonicalFeeJuice(); - const selector = FunctionSelector.fromNameAndParameters( + const selector = await FunctionSelector.fromNameAndParameters( canonicalFeeJuice.artifact.functions.find(f => f.name === 'claim')!, ); diff --git a/yarn-project/aztec.js/src/fee/private_fee_payment_method.ts b/yarn-project/aztec.js/src/fee/private_fee_payment_method.ts index 4151eb648a7..ab032e9c226 100644 --- a/yarn-project/aztec.js/src/fee/private_fee_payment_method.ts +++ b/yarn-project/aztec.js/src/fee/private_fee_payment_method.ts @@ -96,7 +96,7 @@ export class PrivateFeePaymentMethod implements FeePaymentMethod { action: { name: 'setup_refund', args: [this.wallet.getAddress().toField(), ...maxFee.toFields(), nonce], - selector: FunctionSelector.fromSignature('setup_refund((Field),(Field,Field),Field)'), + selector: await FunctionSelector.fromSignature('setup_refund((Field),(Field,Field),Field)'), type: FunctionType.PRIVATE, isStatic: false, to: await this.getAsset(), @@ -108,7 +108,7 @@ export class PrivateFeePaymentMethod implements FeePaymentMethod { { name: 'fee_entrypoint_private', to: this.paymentContract, - selector: FunctionSelector.fromSignature('fee_entrypoint_private((Field,Field),Field)'), + selector: await FunctionSelector.fromSignature('fee_entrypoint_private((Field,Field),Field)'), type: FunctionType.PRIVATE, isStatic: false, args: [...maxFee.toFields(), nonce], diff --git a/yarn-project/aztec.js/src/fee/public_fee_payment_method.ts b/yarn-project/aztec.js/src/fee/public_fee_payment_method.ts index 529783e5ffb..8d725d3e0ce 100644 --- a/yarn-project/aztec.js/src/fee/public_fee_payment_method.ts +++ b/yarn-project/aztec.js/src/fee/public_fee_payment_method.ts @@ -82,33 +82,33 @@ export class PublicFeePaymentMethod implements FeePaymentMethod { const nonce = Fr.random(); const maxFee = new U128(gasSettings.getFeeLimit().toBigInt()); - return Promise.resolve([ - this.wallet - .setPublicAuthWit( - { - caller: this.paymentContract, - action: { - name: 'transfer_in_public', - args: [this.wallet.getAddress().toField(), this.paymentContract.toField(), ...maxFee.toFields(), nonce], - selector: FunctionSelector.fromSignature('transfer_in_public((Field),(Field),(Field,Field),Field)'), - type: FunctionType.PUBLIC, - isStatic: false, - to: await this.getAsset(), - returnTypes: [], - }, - }, - true, - ) - .request(), + const setPublicAuthWitInteraction = await this.wallet.setPublicAuthWit( + { + caller: this.paymentContract, + action: { + name: 'transfer_in_public', + args: [this.wallet.getAddress().toField(), this.paymentContract.toField(), ...maxFee.toFields(), nonce], + selector: await FunctionSelector.fromSignature('transfer_in_public((Field),(Field),(Field,Field),Field)'), + type: FunctionType.PUBLIC, + isStatic: false, + to: await this.getAsset(), + returnTypes: [], + }, + }, + true, + ); + + return [ + await setPublicAuthWitInteraction.request(), { name: 'fee_entrypoint_public', to: this.paymentContract, - selector: FunctionSelector.fromSignature('fee_entrypoint_public((Field,Field),Field)'), + selector: await FunctionSelector.fromSignature('fee_entrypoint_public((Field,Field),Field)'), type: FunctionType.PRIVATE, isStatic: false, args: [...maxFee.toFields(), nonce], returnTypes: [], }, - ]); + ]; } } diff --git a/yarn-project/aztec.js/src/index.ts b/yarn-project/aztec.js/src/index.ts index a380678e19e..9185a71784d 100644 --- a/yarn-project/aztec.js/src/index.ts +++ b/yarn-project/aztec.js/src/index.ts @@ -154,7 +154,6 @@ export { EthCheatCodes, deployL1Contract, deployL1Contracts, type DeployL1Contra // This entire index file will be deprecated at some point after we're satisfied. export * from './api/abi.js'; export * from './api/fee.js'; -export * from './api/init.js'; // Granular export, even if not in the api folder export * from './contract/index.js'; export * from './api/addresses.js'; diff --git a/yarn-project/aztec.js/src/utils/authwit.ts b/yarn-project/aztec.js/src/utils/authwit.ts index 11d640ba749..0f2f17f563e 100644 --- a/yarn-project/aztec.js/src/utils/authwit.ts +++ b/yarn-project/aztec.js/src/utils/authwit.ts @@ -50,17 +50,17 @@ export type IntentAction = { * @param metadata - The metadata for the intent (chainId, version) * @returns The message hash for the action */ -export const computeAuthWitMessageHash = (intent: IntentInnerHash | IntentAction, metadata: IntentMetadata) => { +export const computeAuthWitMessageHash = async (intent: IntentInnerHash | IntentAction, metadata: IntentMetadata) => { const chainId = metadata.chainId; const version = metadata.version; if ('caller' in intent) { - const action = intent.action instanceof ContractFunctionInteraction ? intent.action.request() : intent.action; + const action = intent.action instanceof ContractFunctionInteraction ? await intent.action.request() : intent.action; return computeOuterAuthWitHash( action.to, chainId, version, - computeInnerAuthWitHashFromAction(intent.caller, action), + await computeInnerAuthWitHashFromAction(intent.caller, action), ); } else { const inner = Buffer.isBuffer(intent.innerHash) ? Fr.fromBuffer(intent.innerHash) : intent.innerHash; @@ -69,8 +69,12 @@ export const computeAuthWitMessageHash = (intent: IntentInnerHash | IntentAction }; // docs:end:authwit_computeAuthWitMessageHash -export const computeInnerAuthWitHashFromAction = (caller: AztecAddress, action: FunctionCall) => - computeInnerAuthWitHash([caller.toField(), action.selector.toField(), HashedValues.fromValues(action.args).hash]); +export const computeInnerAuthWitHashFromAction = async (caller: AztecAddress, action: FunctionCall) => + computeInnerAuthWitHash([ + caller.toField(), + action.selector.toField(), + (await HashedValues.fromValues(action.args)).hash, + ]); /** * Compute the inner hash for an authentication witness. diff --git a/yarn-project/aztec.js/src/utils/cheat_codes.ts b/yarn-project/aztec.js/src/utils/cheat_codes.ts index c51139f2e90..ac8992308f2 100644 --- a/yarn-project/aztec.js/src/utils/cheat_codes.ts +++ b/yarn-project/aztec.js/src/utils/cheat_codes.ts @@ -223,7 +223,7 @@ export class AztecCheatCodes { * @param key - The key to lookup in the map * @returns The storage slot of the value in the map */ - public computeSlotInMap(mapSlot: Fr | bigint, key: Fr | bigint | AztecAddress): Fr { + public computeSlotInMap(mapSlot: Fr | bigint, key: Fr | bigint | AztecAddress): Promise { const keyFr = typeof key === 'bigint' ? new Fr(key) : key.toField(); return deriveStorageSlotInMap(mapSlot, keyFr); } diff --git a/yarn-project/aztec.js/src/utils/portal_manager.ts b/yarn-project/aztec.js/src/utils/portal_manager.ts index 76b4ce8fc36..d86a30f862f 100644 --- a/yarn-project/aztec.js/src/utils/portal_manager.ts +++ b/yarn-project/aztec.js/src/utils/portal_manager.ts @@ -49,9 +49,9 @@ function stringifyEthAddress(address: EthAddress | Hex, name?: string) { } /** Generates a pair secret and secret hash */ -export function generateClaimSecret(logger?: Logger): [Fr, Fr] { +export async function generateClaimSecret(logger?: Logger): Promise<[Fr, Fr]> { const secret = Fr.random(); - const secretHash = computeSecretHash(secret); + const secretHash = await computeSecretHash(secret); logger?.verbose(`Generated claim secret=${secret.toString()} hash=${secretHash.toString()}`); return [secret, secretHash]; } @@ -144,7 +144,7 @@ export class L1FeeJuicePortalManager { * @param mint - Whether to mint the tokens before sending (only during testing). */ public async bridgeTokensPublic(to: AztecAddress, amount: bigint, mint = false): Promise { - const [claimSecret, claimSecretHash] = generateClaimSecret(); + const [claimSecret, claimSecretHash] = await generateClaimSecret(); if (mint) { await this.tokenManager.mint(amount, this.walletClient.account.address); } diff --git a/yarn-project/aztec.js/src/wallet/account_wallet.ts b/yarn-project/aztec.js/src/wallet/account_wallet.ts index 781b8ab454e..15136fb10d1 100644 --- a/yarn-project/aztec.js/src/wallet/account_wallet.ts +++ b/yarn-project/aztec.js/src/wallet/account_wallet.ts @@ -55,7 +55,7 @@ export class AccountWallet extends BaseWallet { } else if (messageHashOrIntent instanceof Fr) { messageHash = messageHashOrIntent; } else { - messageHash = this.getMessageHash(messageHashOrIntent); + messageHash = await this.getMessageHash(messageHashOrIntent); } const witness = await this.account.createAuthWit(messageHash); @@ -72,17 +72,17 @@ export class AccountWallet extends BaseWallet { * @param authorized - True to authorize, false to revoke authorization. * @returns - A function interaction. */ - public setPublicAuthWit( + public async setPublicAuthWit( messageHashOrIntent: Fr | Buffer | IntentInnerHash | IntentAction, authorized: boolean, - ): ContractFunctionInteraction { + ): Promise { let messageHash: Fr; if (Buffer.isBuffer(messageHashOrIntent)) { messageHash = Fr.fromBuffer(messageHashOrIntent); } else if (messageHashOrIntent instanceof Fr) { messageHash = messageHashOrIntent; } else { - messageHash = this.getMessageHash(messageHashOrIntent); + messageHash = await this.getMessageHash(messageHashOrIntent); } return new ContractFunctionInteraction(this, ProtocolContractAddress.AuthRegistry, this.getSetAuthorizedAbi(), [ @@ -91,16 +91,17 @@ export class AccountWallet extends BaseWallet { ]); } - private getInnerHashAndConsumer(intent: IntentInnerHash | IntentAction): { + private async getInnerHashAndConsumer(intent: IntentInnerHash | IntentAction): Promise<{ /** The inner hash */ innerHash: Fr; /** The consumer of the authwit */ consumer: AztecAddress; - } { + }> { if ('caller' in intent && 'action' in intent) { - const action = intent.action instanceof ContractFunctionInteraction ? intent.action.request() : intent.action; + const action = + intent.action instanceof ContractFunctionInteraction ? await intent.action.request() : intent.action; return { - innerHash: computeInnerAuthWitHashFromAction(intent.caller, action), + innerHash: await computeInnerAuthWitHashFromAction(intent.caller, action), consumer: action.to, }; } else if (Buffer.isBuffer(intent.innerHash)) { @@ -115,7 +116,7 @@ export class AccountWallet extends BaseWallet { * @param intent - A tuple of (consumer and inner hash) or (caller and action) * @returns The message hash */ - private getMessageHash(intent: IntentInnerHash | IntentAction): Fr { + private getMessageHash(intent: IntentInnerHash | IntentAction): Promise { const chainId = this.getChainId(); const version = this.getVersion(); return computeAuthWitMessageHash(intent, { chainId, version }); @@ -140,9 +141,9 @@ export class AccountWallet extends BaseWallet { /** boolean flag indicating if the authwit is valid in public context */ isValidInPublic: boolean; }> { - const { innerHash, consumer } = this.getInnerHashAndConsumer(intent); + const { innerHash, consumer } = await this.getInnerHashAndConsumer(intent); - const messageHash = this.getMessageHash(intent); + const messageHash = await this.getMessageHash(intent); const results = { isValidInPrivate: false, isValidInPublic: false }; // Check private diff --git a/yarn-project/aztec.js/src/wallet/account_wallet_with_private_key.ts b/yarn-project/aztec.js/src/wallet/account_wallet_with_private_key.ts index 135d06ce339..1d72dfac5a3 100644 --- a/yarn-project/aztec.js/src/wallet/account_wallet_with_private_key.ts +++ b/yarn-project/aztec.js/src/wallet/account_wallet_with_private_key.ts @@ -30,9 +30,9 @@ export class AccountWalletWithSecretKey extends AccountWallet { * note - this ensures that the address secret always corresponds to an address point with y being positive * dev - this is also referred to as the address secret, which decrypts payloads encrypted to an address point */ - public getEncryptionSecret() { + public async getEncryptionSecret() { return computeAddressSecret( - this.getCompleteAddress().getPreaddress(), + await this.getCompleteAddress().getPreaddress(), deriveMasterIncomingViewingSecretKey(this.getSecretKey()), ); } diff --git a/yarn-project/aztec.js/webpack.config.js b/yarn-project/aztec.js/webpack.config.js index 3ba9561af4e..277f7a997d9 100644 --- a/yarn-project/aztec.js/webpack.config.js +++ b/yarn-project/aztec.js/webpack.config.js @@ -1,4 +1,4 @@ -// import CopyWebpackPlugin from 'copy-webpack-plugin'; +import CopyPlugin from 'copy-webpack-plugin'; import { createRequire } from 'module'; import { dirname, resolve } from 'path'; import ResolveTypeScriptPlugin from 'resolve-typescript-plugin'; @@ -15,7 +15,14 @@ export default { main: './src/index.ts', }, module: { + parser: { + javascript: { importMeta: false }, + }, rules: [ + { + test: /\.gz$/, + type: 'asset/resource', + }, { test: /\.tsx?$/, use: [ @@ -41,6 +48,14 @@ export default { outputModule: true, }, plugins: [ + new CopyPlugin({ + patterns: [ + { + context: '../../barretenberg/ts/dest/browser', + from: '*.gz', + }, + ], + }), new webpack.DefinePlugin({ 'process.env': { NODE_ENV: JSON.stringify('production'), diff --git a/yarn-project/aztec/src/cli/cmds/start_pxe.ts b/yarn-project/aztec/src/cli/cmds/start_pxe.ts index 278892571a1..6254e2ee194 100644 --- a/yarn-project/aztec/src/cli/cmds/start_pxe.ts +++ b/yarn-project/aztec/src/cli/cmds/start_pxe.ts @@ -100,19 +100,21 @@ export async function addPXE( }; } - Object.values(l2Contracts).forEach(async ({ name, address, artifact, initHash, salt }) => { - const instance: ContractInstanceWithAddress = { - version: 1, - salt, - initializationHash: initHash, - address, - deployer: AztecAddress.ZERO, - contractClassId: getContractClassFromArtifact(artifact!).id, - publicKeys: PublicKeys.default(), - }; - userLog(`Registering ${name} at ${address.toString()}`); - await pxe.registerContract({ artifact, instance }); - }); + await Promise.all( + Object.values(l2Contracts).map(async ({ name, address, artifact, initHash, salt }) => { + const instance: ContractInstanceWithAddress = { + version: 1, + salt, + initializationHash: initHash, + address, + deployer: AztecAddress.ZERO, + contractClassId: (await getContractClassFromArtifact(artifact!)).id, + publicKeys: PublicKeys.default(), + }; + userLog(`Registering ${name} at ${address.toString()}`); + await pxe.registerContract({ artifact, instance }); + }), + ); } // Add PXE to services list diff --git a/yarn-project/aztec/src/sandbox.ts b/yarn-project/aztec/src/sandbox.ts index 6134d0fc1a8..c6720fda7ed 100644 --- a/yarn-project/aztec/src/sandbox.ts +++ b/yarn-project/aztec/src/sandbox.ts @@ -80,10 +80,10 @@ export async function deployContractsToL1( ? createEthereumChain(aztecNodeConfig.l1RpcUrl, aztecNodeConfig.l1ChainId) : { chainInfo: localAnvil }; - const l1Contracts = await waitThenDeploy(aztecNodeConfig, () => + const l1Contracts = await waitThenDeploy(aztecNodeConfig, async () => deployL1Contracts(aztecNodeConfig.l1RpcUrl, hdAccount, chain.chainInfo, contractDeployLogger, { l2FeeJuiceAddress: ProtocolContractAddress.FeeJuice, - vkTreeRoot: getVKTreeRoot(), + vkTreeRoot: await getVKTreeRoot(), protocolContractTreeRoot, assumeProvenThrough: opts.assumeProvenThroughBlockNumber, salt: opts.salt, diff --git a/yarn-project/bb-prover/src/test/test_avm.ts b/yarn-project/bb-prover/src/test/test_avm.ts index a45710afb87..577f724a103 100644 --- a/yarn-project/bb-prover/src/test/test_avm.ts +++ b/yarn-project/bb-prover/src/test/test_avm.ts @@ -31,17 +31,17 @@ import { padArrayEnd } from '@aztec/foundation/collection'; import { type PublicFunctionCallResult } from '@aztec/simulator/server'; // TODO: pub somewhere more usable - copied from abstract phase manager -export function getPublicInputs(result: PublicFunctionCallResult): PublicCircuitPublicInputs { +export async function getPublicInputs(result: PublicFunctionCallResult): Promise { return PublicCircuitPublicInputs.from({ callContext: result.executionRequest.callContext, proverAddress: AztecAddress.ZERO, - argsHash: computeVarArgsHash(result.executionRequest.args), + argsHash: await computeVarArgsHash(result.executionRequest.args), noteHashes: padArrayEnd(result.noteHashes, NoteHash.empty(), MAX_NOTE_HASHES_PER_CALL), nullifiers: padArrayEnd(result.nullifiers, Nullifier.empty(), MAX_NULLIFIERS_PER_CALL), l2ToL1Msgs: padArrayEnd(result.l2ToL1Messages, L2ToL1Message.empty(), MAX_L2_TO_L1_MSGS_PER_CALL), startSideEffectCounter: result.startSideEffectCounter, endSideEffectCounter: result.endSideEffectCounter, - returnsHash: computeVarArgsHash(result.returnValues), + returnsHash: await computeVarArgsHash(result.returnValues), noteHashReadRequests: padArrayEnd( result.noteHashReadRequests, TreeLeafReadRequest.empty(), diff --git a/yarn-project/bb-prover/src/verification_key/verification_key_data.ts b/yarn-project/bb-prover/src/verification_key/verification_key_data.ts index 3a3f975802e..7cad7b86401 100644 --- a/yarn-project/bb-prover/src/verification_key/verification_key_data.ts +++ b/yarn-project/bb-prover/src/verification_key/verification_key_data.ts @@ -25,7 +25,7 @@ export async function extractVkData(vkDirectoryPath: string): Promise BlobStore) { it('should store and retrieve a blob', async () => { // Create a test blob with random fields const testFields = [Fr.random(), Fr.random(), Fr.random()]; - const blob = Blob.fromFields(testFields); + const blob = await Blob.fromFields(testFields); const blockId = '0x12345'; const blobWithIndex = new BlobWithIndex(blob, 0); @@ -33,7 +33,7 @@ export function describeBlobStore(getBlobStore: () => BlobStore) { it('Should allow requesting a specific index of blob', async () => { const testFields = [Fr.random(), Fr.random(), Fr.random()]; - const blob = Blob.fromFields(testFields); + const blob = await Blob.fromFields(testFields); const blockId = '0x12345'; const blobWithIndex = new BlobWithIndex(blob, 0); const blobWithIndex2 = new BlobWithIndex(blob, 1); @@ -56,8 +56,8 @@ export function describeBlobStore(getBlobStore: () => BlobStore) { it('Differentiate between blockHash and slot', async () => { const testFields = [Fr.random(), Fr.random(), Fr.random()]; const testFieldsSlot = [Fr.random(), Fr.random(), Fr.random()]; - const blob = Blob.fromFields(testFields); - const blobSlot = Blob.fromFields(testFieldsSlot); + const blob = await Blob.fromFields(testFields); + const blobSlot = await Blob.fromFields(testFieldsSlot); const blockId = '0x12345'; const slot = '12345'; const blobWithIndex = new BlobWithIndex(blob, 0); @@ -86,8 +86,8 @@ export function describeBlobStore(getBlobStore: () => BlobStore) { it('should handle multiple blobs with different block IDs', async () => { // Create two different blobs - const blob1 = Blob.fromFields([Fr.random(), Fr.random()]); - const blob2 = Blob.fromFields([Fr.random(), Fr.random(), Fr.random()]); + const blob1 = await Blob.fromFields([Fr.random(), Fr.random()]); + const blob2 = await Blob.fromFields([Fr.random(), Fr.random(), Fr.random()]); const blobWithIndex1 = new BlobWithIndex(blob1, 0); const blobWithIndex2 = new BlobWithIndex(blob2, 0); @@ -107,8 +107,8 @@ export function describeBlobStore(getBlobStore: () => BlobStore) { it('should overwrite blob when using same block ID', async () => { // Create two different blobs - const originalBlob = Blob.fromFields([Fr.random()]); - const newBlob = Blob.fromFields([Fr.random(), Fr.random()]); + const originalBlob = await Blob.fromFields([Fr.random()]); + const newBlob = await Blob.fromFields([Fr.random(), Fr.random()]); const blockId = '1'; const originalBlobWithIndex = new BlobWithIndex(originalBlob, 0); const newBlobWithIndex = new BlobWithIndex(newBlob, 0); @@ -127,8 +127,8 @@ export function describeBlobStore(getBlobStore: () => BlobStore) { }); it('should handle multiple blobs with the same block ID', async () => { - const blob1 = Blob.fromFields([Fr.random()]); - const blob2 = Blob.fromFields([Fr.random()]); + const blob1 = await Blob.fromFields([Fr.random()]); + const blob2 = await Blob.fromFields([Fr.random()]); const blobWithIndex1 = new BlobWithIndex(blob1, 0); const blobWithIndex2 = new BlobWithIndex(blob2, 0); diff --git a/yarn-project/blob-sink/src/client/blob-sink-client-tests.ts b/yarn-project/blob-sink/src/client/blob-sink-client-tests.ts index bea94533769..411b1436ffe 100644 --- a/yarn-project/blob-sink/src/client/blob-sink-client-tests.ts +++ b/yarn-project/blob-sink/src/client/blob-sink-client-tests.ts @@ -24,7 +24,7 @@ export function runBlobSinkClientTests( }); it('should send and retrieve blobs', async () => { - const blob = makeEncodedBlob(3); + const blob = await makeEncodedBlob(3); const blockId = '0x1234'; const success = await client.sendBlobsToBlobSink(blockId, [blob]); @@ -37,7 +37,7 @@ export function runBlobSinkClientTests( }); it('should handle multiple blobs', async () => { - const blobs = [makeEncodedBlob(2), makeEncodedBlob(2), makeEncodedBlob(2)]; + const blobs = await Promise.all([makeEncodedBlob(2), makeEncodedBlob(2), makeEncodedBlob(2)]); const blockId = '0x5678'; const success = await client.sendBlobsToBlobSink(blockId, blobs); diff --git a/yarn-project/blob-sink/src/client/http.test.ts b/yarn-project/blob-sink/src/client/http.test.ts index f652c719535..5da17a7a631 100644 --- a/yarn-project/blob-sink/src/client/http.test.ts +++ b/yarn-project/blob-sink/src/client/http.test.ts @@ -30,7 +30,7 @@ describe('HttpBlobSinkClient', () => { it('should handle server connection errors gracefully', async () => { const client = new HttpBlobSinkClient({ blobSinkUrl: 'http://localhost:12345' }); // Invalid port - const blob = Blob.fromFields([Fr.random()]); + const blob = await Blob.fromFields([Fr.random()]); const success = await client.sendBlobsToBlobSink('0x1234', [blob]); expect(success).toBe(false); @@ -52,8 +52,8 @@ describe('HttpBlobSinkClient', () => { const MOCK_SLOT_NUMBER = 1; - beforeEach(() => { - testBlob = makeEncodedBlob(3); + beforeEach(async () => { + testBlob = await makeEncodedBlob(3); }); const startExecutionHostServer = (): Promise => { diff --git a/yarn-project/blob-sink/src/client/http.ts b/yarn-project/blob-sink/src/client/http.ts index d49f13a4647..7434fc5f1af 100644 --- a/yarn-project/blob-sink/src/client/http.ts +++ b/yarn-project/blob-sink/src/client/http.ts @@ -108,7 +108,7 @@ export class HttpBlobSinkClient implements BlobSinkClientInterface { if (res.ok) { const body = await res.json(); - const blobs = body.data.map((b: BlobJson) => Blob.fromJson(b)); + const blobs = await Promise.all(body.data.map((b: BlobJson) => Blob.fromJson(b))); return blobs; } diff --git a/yarn-project/blob-sink/src/server/server.test.ts b/yarn-project/blob-sink/src/server/server.test.ts index cc0571e9500..3668db2dc25 100644 --- a/yarn-project/blob-sink/src/server/server.test.ts +++ b/yarn-project/blob-sink/src/server/server.test.ts @@ -20,11 +20,13 @@ describe('BlobSinkService', () => { }); describe('should store and retrieve a blob sidecar', () => { - const blob = makeEncodedBlob(3); - const blob2 = makeEncodedBlob(3); const blockId = '0x1234'; + let blob: Blob; + let blob2: Blob; beforeEach(async () => { + blob = await makeEncodedBlob(3); + blob2 = await makeEncodedBlob(3); // Post the blob const postResponse = await request(service.getApp()) .post('/blob_sidecar') @@ -54,8 +56,8 @@ describe('BlobSinkService', () => { // Convert the response blob back to a Blob object and verify it matches const retrievedBlobs = getResponse.body.data; - const retrievedBlob = Blob.fromEncodedBlobBuffer(Buffer.from(retrievedBlobs[0].blob.slice(2), 'hex')); - const retrievedBlob2 = Blob.fromEncodedBlobBuffer(Buffer.from(retrievedBlobs[1].blob.slice(2), 'hex')); + const retrievedBlob = await Blob.fromEncodedBlobBuffer(Buffer.from(retrievedBlobs[0].blob.slice(2), 'hex')); + const retrievedBlob2 = await Blob.fromEncodedBlobBuffer(Buffer.from(retrievedBlobs[1].blob.slice(2), 'hex')); expect(retrievedBlob.fieldsHash.toString()).toBe(blob.fieldsHash.toString()); expect(retrievedBlob.commitment.toString('hex')).toBe(blob.commitment.toString('hex')); @@ -76,8 +78,8 @@ describe('BlobSinkService', () => { expect(getWithIndicies.body.data.length).toBe(2); const retrievedBlobs = getWithIndicies.body.data; - const retrievedBlob = Blob.fromEncodedBlobBuffer(Buffer.from(retrievedBlobs[0].blob.slice(2), 'hex')); - const retrievedBlob2 = Blob.fromEncodedBlobBuffer(Buffer.from(retrievedBlobs[1].blob.slice(2), 'hex')); + const retrievedBlob = await Blob.fromEncodedBlobBuffer(Buffer.from(retrievedBlobs[0].blob.slice(2), 'hex')); + const retrievedBlob2 = await Blob.fromEncodedBlobBuffer(Buffer.from(retrievedBlobs[1].blob.slice(2), 'hex')); expect(retrievedBlob.fieldsHash.toString()).toBe(blob.fieldsHash.toString()); expect(retrievedBlob.commitment.toString('hex')).toBe(blob.commitment.toString('hex')); expect(retrievedBlob.proof.toString('hex')).toBe(blob.proof.toString('hex')); @@ -94,7 +96,7 @@ describe('BlobSinkService', () => { expect(getWithIndicies.body.data.length).toBe(1); const retrievedBlobs = getWithIndicies.body.data; - const retrievedBlob = Blob.fromEncodedBlobBuffer(Buffer.from(retrievedBlobs[0].blob.slice(2), 'hex')); + const retrievedBlob = await Blob.fromEncodedBlobBuffer(Buffer.from(retrievedBlobs[0].blob.slice(2), 'hex')); expect(retrievedBlob.fieldsHash.toString()).toBe(blob2.fieldsHash.toString()); expect(retrievedBlob.commitment.toString('hex')).toBe(blob2.commitment.toString('hex')); expect(retrievedBlob.proof.toString('hex')).toBe(blob2.proof.toString('hex')); diff --git a/yarn-project/blob-sink/src/types/blob_with_index.test.ts b/yarn-project/blob-sink/src/types/blob_with_index.test.ts index d29c6b98b88..26c77b20f42 100644 --- a/yarn-project/blob-sink/src/types/blob_with_index.test.ts +++ b/yarn-project/blob-sink/src/types/blob_with_index.test.ts @@ -4,8 +4,8 @@ import { Fr } from '@aztec/foundation/fields'; import { BlobWithIndex, BlobsWithIndexes } from './blob_with_index.js'; describe('BlobWithIndex Serde', () => { - it('should serialize and deserialize', () => { - const blob = Blob.fromFields([Fr.random(), Fr.random(), Fr.random()]); + it('should serialize and deserialize', async () => { + const blob = await Blob.fromFields([Fr.random(), Fr.random(), Fr.random()]); const blobWithIndex = new BlobWithIndex(blob, 0); const serialized = blobWithIndex.toBuffer(); @@ -16,11 +16,11 @@ describe('BlobWithIndex Serde', () => { }); describe('BlobsWithIndexes Serde', () => { - it('should serialize and deserialize', () => { - const blobs = [ - new BlobWithIndex(Blob.fromFields([Fr.random(), Fr.random(), Fr.random()]), 0), - new BlobWithIndex(Blob.fromFields([Fr.random(), Fr.random(), Fr.random()]), 1), - ]; + it('should serialize and deserialize', async () => { + const blobs = await Promise.all([ + new BlobWithIndex(await Blob.fromFields([Fr.random(), Fr.random(), Fr.random()]), 0), + new BlobWithIndex(await Blob.fromFields([Fr.random(), Fr.random(), Fr.random()]), 1), + ]); const blobsWithIndexes = new BlobsWithIndexes(blobs); const serialized = blobsWithIndexes.toBuffer(); diff --git a/yarn-project/bot/src/bot.ts b/yarn-project/bot/src/bot.ts index e41fb6797e0..4d1d9589125 100644 --- a/yarn-project/bot/src/bot.ts +++ b/yarn-project/bot/src/bot.ts @@ -9,7 +9,7 @@ import { } from '@aztec/aztec.js'; import { type AztecNode, type FunctionCall, type PXE } from '@aztec/circuit-types'; import { Gas } from '@aztec/circuits.js'; -import { times } from '@aztec/foundation/collection'; +import { timesParallel } from '@aztec/foundation/collection'; import { type EasyPrivateTokenContract } from '@aztec/noir-contracts.js/EasyPrivateToken'; import { type TokenContract } from '@aztec/noir-contracts.js/Token'; @@ -57,15 +57,21 @@ export class Bot { const calls: FunctionCall[] = []; if (isStandardTokenContract(token)) { - calls.push(...times(privateTransfersPerTx, () => token.methods.transfer(recipient, TRANSFER_AMOUNT).request())); calls.push( - ...times(publicTransfersPerTx, () => + ...(await timesParallel(privateTransfersPerTx, () => + token.methods.transfer(recipient, TRANSFER_AMOUNT).request(), + )), + ); + calls.push( + ...(await timesParallel(publicTransfersPerTx, () => token.methods.transfer_in_public(sender, recipient, TRANSFER_AMOUNT, 0).request(), - ), + )), ); } else { calls.push( - ...times(privateTransfersPerTx, () => token.methods.transfer(TRANSFER_AMOUNT, sender, recipient).request()), + ...(await timesParallel(privateTransfersPerTx, () => + token.methods.transfer(TRANSFER_AMOUNT, sender, recipient).request(), + )), ); } diff --git a/yarn-project/bot/src/factory.ts b/yarn-project/bot/src/factory.ts index 69495fbefcf..9b872740536 100644 --- a/yarn-project/bot/src/factory.ts +++ b/yarn-project/bot/src/factory.ts @@ -152,13 +152,13 @@ export class BotFactory { const from = sender; // we are setting from to sender here because of TODO(#9887) calls.push( isStandardToken - ? token.methods.mint_to_private(from, sender, MINT_BALANCE).request() - : token.methods.mint(MINT_BALANCE, sender).request(), + ? await token.methods.mint_to_private(from, sender, MINT_BALANCE).request() + : await token.methods.mint(MINT_BALANCE, sender).request(), ); } if (isStandardToken && publicBalance < MIN_BALANCE) { this.log.info(`Minting public tokens for ${sender.toString()}`); - calls.push(token.methods.mint_to_public(sender, MINT_BALANCE).request()); + calls.push(await token.methods.mint_to_public(sender, MINT_BALANCE).request()); } if (calls.length === 0) { this.log.info(`Skipping minting as ${sender.toString()} has enough tokens`); diff --git a/yarn-project/builder/src/contract-interface-gen/codegen.ts b/yarn-project/builder/src/contract-interface-gen/codegen.ts index dea5cb8417a..dee4a1e5add 100644 --- a/yarn-project/builder/src/contract-interface-gen/codegen.ts +++ b/yarn-project/builder/src/contract-interface-gen/codegen.ts @@ -60,7 +60,7 @@ async function generateFromNoirAbi(outputPath: string, noirAbiPath: string, opts relativeArtifactPath = `./${relativeArtifactPath}`; } - const tsWrapper = generateTypescriptContractInterface(aztecAbi, relativeArtifactPath); + const tsWrapper = await generateTypescriptContractInterface(aztecAbi, relativeArtifactPath); const outputFilePath = `${outputPath}/${aztecAbi.name}.ts`; await writeFile(outputFilePath, tsWrapper); diff --git a/yarn-project/builder/src/contract-interface-gen/typescript.ts b/yarn-project/builder/src/contract-interface-gen/typescript.ts index 81bcdb7bc6a..c27d4550010 100644 --- a/yarn-project/builder/src/contract-interface-gen/typescript.ts +++ b/yarn-project/builder/src/contract-interface-gen/typescript.ts @@ -2,6 +2,7 @@ import { type ABIParameter, type ABIVariable, type ContractArtifact, + EventSelector, type FunctionArtifact, decodeFunctionSignature, getDefaultInitializer, @@ -245,38 +246,42 @@ function generateNotesGetter(input: ContractArtifact) { } // events is of type AbiType -function generateEvents(events: any[] | undefined) { +async function generateEvents(events: any[] | undefined) { if (events === undefined) { return { events: '', eventDefs: '' }; } - const eventsMetadata = events.map(event => { - const eventName = event.path.split('::').at(-1); + const eventsMetadata = await Promise.all( + events.map(async event => { + const eventName = event.path.split('::').at(-1); - const eventDefProps = event.fields.map((field: ABIVariable) => `${field.name}: ${abiTypeToTypescript(field.type)}`); - const eventDef = ` + const eventDefProps = event.fields.map( + (field: ABIVariable) => `${field.name}: ${abiTypeToTypescript(field.type)}`, + ); + const eventDef = ` export type ${eventName} = { ${eventDefProps.join('\n')} } `; - const fieldNames = event.fields.map((field: any) => `"${field.name}"`); - const eventType = `${eventName}: {abiType: AbiType, eventSelector: EventSelector, fieldNames: string[] }`; - // Reusing the decodeFunctionSignature - const eventSignature = decodeFunctionSignature(eventName, event.fields); - const eventSelector = `EventSelector.fromSignature('${eventSignature}')`; - const eventImpl = `${eventName}: { + const fieldNames = event.fields.map((field: any) => `"${field.name}"`); + const eventType = `${eventName}: {abiType: AbiType, eventSelector: EventSelector, fieldNames: string[] }`; + // Reusing the decodeFunctionSignature + const eventSignature = decodeFunctionSignature(eventName, event.fields); + const eventSelector = await EventSelector.fromSignature(eventSignature); + const eventImpl = `${eventName}: { abiType: ${JSON.stringify(event, null, 4)}, - eventSelector: ${eventSelector}, + eventSelector: EventSelector.fromString("${eventSelector}"), fieldNames: [${fieldNames}], }`; - return { - eventDef, - eventType, - eventImpl, - }; - }); + return { + eventDef, + eventType, + eventImpl, + }; + }), + ); return { eventDefs: eventsMetadata.map(({ eventDef }) => eventDef).join('\n'), @@ -296,7 +301,7 @@ function generateEvents(events: any[] | undefined) { * @param artifactImportPath - Optional path to import the artifact (if not set, will be required in the constructor). * @returns The corresponding ts code. */ -export function generateTypescriptContractInterface(input: ContractArtifact, artifactImportPath?: string) { +export async function generateTypescriptContractInterface(input: ContractArtifact, artifactImportPath?: string) { const methods = input.functions .filter(f => !f.isInternal) .sort((a, b) => a.name.localeCompare(b.name)) @@ -308,7 +313,7 @@ export function generateTypescriptContractInterface(input: ContractArtifact, art const artifactGetter = artifactImportPath && generateArtifactGetter(input.name); const storageLayoutGetter = artifactImportPath && generateStorageLayoutGetter(input); const notesGetter = artifactImportPath && generateNotesGetter(input); - const { eventDefs, events } = generateEvents(input.outputs.structs?.events); + const { eventDefs, events } = await generateEvents(input.outputs.structs?.events); return ` /* Autogenerated file, do not edit! */ diff --git a/yarn-project/circuit-types/src/hashed_values.test.ts b/yarn-project/circuit-types/src/hashed_values.test.ts index a7cbd07094b..e3766aecae7 100644 --- a/yarn-project/circuit-types/src/hashed_values.test.ts +++ b/yarn-project/circuit-types/src/hashed_values.test.ts @@ -3,9 +3,9 @@ import { jsonStringify } from '@aztec/foundation/json-rpc'; import { HashedValues } from './hashed_values.js'; describe('HashedValues', () => { - it('serializes and deserializes', () => { - const values = HashedValues.random(); + it('serializes and deserializes', async () => { + const values = await HashedValues.random(); const json = jsonStringify(values); - expect(HashedValues.schema.parse(JSON.parse(json))).toEqual(values); + await expect(HashedValues.schema.parseAsync(JSON.parse(json))).resolves.toEqual(values); }); }); diff --git a/yarn-project/circuit-types/src/hashed_values.ts b/yarn-project/circuit-types/src/hashed_values.ts index 5fd26da5fb5..6fcf3ca19ba 100644 --- a/yarn-project/circuit-types/src/hashed_values.ts +++ b/yarn-project/circuit-types/src/hashed_values.ts @@ -32,8 +32,8 @@ export class HashedValues { return HashedValues.fromValues([Fr.random(), Fr.random()]); } - static fromValues(values: Fr[]) { - return new HashedValues(values, computeVarArgsHash(values)); + static async fromValues(values: Fr[]) { + return new HashedValues(values, await computeVarArgsHash(values)); } toBuffer() { diff --git a/yarn-project/circuit-types/src/in_block.ts b/yarn-project/circuit-types/src/in_block.ts index 5e5205b4237..ffbfdfd9115 100644 --- a/yarn-project/circuit-types/src/in_block.ts +++ b/yarn-project/circuit-types/src/in_block.ts @@ -19,11 +19,11 @@ export function randomInBlock(data: T): InBlock { }; } -export function wrapInBlock(data: T, block: L2Block): InBlock { +export async function wrapInBlock(data: T, block: L2Block): Promise> { return { data, l2BlockNumber: block.number, - l2BlockHash: block.hash().toString(), + l2BlockHash: (await block.hash()).toString(), }; } diff --git a/yarn-project/circuit-types/src/interfaces/archiver.test.ts b/yarn-project/circuit-types/src/interfaces/archiver.test.ts index aac93f40cde..7146dd87722 100644 --- a/yarn-project/circuit-types/src/interfaces/archiver.test.ts +++ b/yarn-project/circuit-types/src/interfaces/archiver.test.ts @@ -189,7 +189,7 @@ describe('ArchiverApiSchema', () => { }); it('getContractClass', async () => { - const contractClass = getContractClassFromArtifact(artifact); + const contractClass = await getContractClassFromArtifact(artifact); const result = await context.client.getContractClass(Fr.random()); expect(result).toEqual({ ...omit(contractClass, 'publicBytecodeCommitment'), @@ -199,7 +199,7 @@ describe('ArchiverApiSchema', () => { }); it('getContractFunctionName', async () => { - const selector = FunctionSelector.fromNameAndParameters( + const selector = await FunctionSelector.fromNameAndParameters( artifact.functions[0].name, artifact.functions[0].parameters, ); @@ -208,9 +208,9 @@ describe('ArchiverApiSchema', () => { }); it('getBytecodeCommitment', async () => { - const contractClass = getContractClassFromArtifact(artifact); + const contractClass = await getContractClassFromArtifact(artifact); const result = await context.client.getBytecodeCommitment(Fr.random()); - expect(result).toEqual(computePublicBytecodeCommitment(contractClass.packedBytecode)); + expect(result).toEqual(await computePublicBytecodeCommitment(contractClass.packedBytecode)); }); it('getContractClassIds', async () => { @@ -247,7 +247,7 @@ describe('ArchiverApiSchema', () => { }); it('addContractClass', async () => { - const contractClass = getContractClassFromArtifact(artifact); + const contractClass = await getContractClassFromArtifact(artifact); await context.client.addContractClass({ ...omit(contractClass, 'publicBytecodeCommitment'), unconstrainedFunctions: [], @@ -346,24 +346,26 @@ class MockArchiver implements ArchiverApi { expect(selector).toBeInstanceOf(FunctionSelector); return Promise.resolve({ selector, bytecode: Buffer.alloc(10, 10) }); } - getContractClass(id: Fr): Promise { + async getContractClass(id: Fr): Promise { expect(id).toBeInstanceOf(Fr); - const contractClass = getContractClassFromArtifact(this.artifact); + const contractClass = await getContractClassFromArtifact(this.artifact); return Promise.resolve({ ...contractClass, unconstrainedFunctions: [], privateFunctions: [] }); } - getBytecodeCommitment(id: Fr): Promise { + async getBytecodeCommitment(id: Fr): Promise { expect(id).toBeInstanceOf(Fr); - const contractClass = getContractClassFromArtifact(this.artifact); - return Promise.resolve(computePublicBytecodeCommitment(contractClass.packedBytecode)); + const contractClass = await getContractClassFromArtifact(this.artifact); + return computePublicBytecodeCommitment(contractClass.packedBytecode); } - getContractFunctionName(address: AztecAddress, selector: FunctionSelector): Promise { + async getContractFunctionName(address: AztecAddress, selector: FunctionSelector): Promise { expect(address).toBeInstanceOf(AztecAddress); expect(selector).toBeInstanceOf(FunctionSelector); - return Promise.resolve( - this.artifact.functions.find(f => - FunctionSelector.fromNameAndParameters({ name: f.name, parameters: f.parameters }).equals(selector), - )?.name, + const functionsAndSelectors = await Promise.all( + this.artifact.functions.map(async f => ({ + name: f.name, + selector: await FunctionSelector.fromNameAndParameters({ name: f.name, parameters: f.parameters }), + })), ); + return functionsAndSelectors.find(f => f.selector.equals(selector))?.name; } async getContract(address: AztecAddress): Promise { return { diff --git a/yarn-project/circuit-types/src/interfaces/aztec-node.test.ts b/yarn-project/circuit-types/src/interfaces/aztec-node.test.ts index c3d98c74ead..c864bcd63a3 100644 --- a/yarn-project/circuit-types/src/interfaces/aztec-node.test.ts +++ b/yarn-project/circuit-types/src/interfaces/aztec-node.test.ts @@ -311,7 +311,7 @@ describe('AztecNodeApiSchema', () => { }); it('getContractClass', async () => { - const contractClass = getContractClassFromArtifact(artifact); + const contractClass = await getContractClassFromArtifact(artifact); const response = await context.client.getContractClass(Fr.random()); expect(response).toEqual({ ...omit(contractClass, 'publicBytecodeCommitment'), @@ -352,7 +352,7 @@ describe('AztecNodeApiSchema', () => { }); it('addContractClass', async () => { - const contractClass = getContractClassFromArtifact(artifact); + const contractClass = await getContractClassFromArtifact(artifact); await context.client.addContractClass({ ...contractClass, unconstrainedFunctions: [], privateFunctions: [] }); }); }); @@ -579,10 +579,10 @@ class MockAztecNode implements AztecNode { expect(config.coinbase).toBeInstanceOf(EthAddress); return Promise.resolve(); } - getContractClass(id: Fr): Promise { + async getContractClass(id: Fr): Promise { expect(id).toBeInstanceOf(Fr); - const contractClass = getContractClassFromArtifact(this.artifact); - return Promise.resolve({ ...contractClass, unconstrainedFunctions: [], privateFunctions: [] }); + const contractClass = await getContractClassFromArtifact(this.artifact); + return { ...contractClass, unconstrainedFunctions: [], privateFunctions: [] }; } async getContract(address: AztecAddress): Promise { expect(address).toBeInstanceOf(AztecAddress); diff --git a/yarn-project/circuit-types/src/interfaces/epoch-prover.ts b/yarn-project/circuit-types/src/interfaces/epoch-prover.ts index 02f76b95a9c..095a96a01e9 100644 --- a/yarn-project/circuit-types/src/interfaces/epoch-prover.ts +++ b/yarn-project/circuit-types/src/interfaces/epoch-prover.ts @@ -19,7 +19,7 @@ export interface EpochProver extends Omit { * Kickstarts tube circuits for the specified txs. These will be used during epoch proving. * Note that if the tube circuits are not started this way, they will be started nontheless after processing. */ - startTubeCircuits(txs: Tx[]): void; + startTubeCircuits(txs: Tx[]): Promise; /** Returns the block. */ setBlockCompleted(blockNumber: number, expectedBlockHeader?: BlockHeader): Promise; diff --git a/yarn-project/circuit-types/src/interfaces/pxe.test.ts b/yarn-project/circuit-types/src/interfaces/pxe.test.ts index 9745659762c..56d34e8a67f 100644 --- a/yarn-project/circuit-types/src/interfaces/pxe.test.ts +++ b/yarn-project/circuit-types/src/interfaces/pxe.test.ts @@ -282,7 +282,11 @@ describe('PXESchema', () => { it('getContractClass', async () => { const result = await context.client.getContractClass(Fr.random()); - const expected = omit(getContractClassFromArtifact(artifact), 'privateFunctionsRoot', 'publicBytecodeCommitment'); + const expected = omit( + await getContractClassFromArtifact(artifact), + 'privateFunctionsRoot', + 'publicBytecodeCommitment', + ); expect(result).toEqual(expected); }); @@ -520,10 +524,10 @@ class MockPXE implements PXE { expect(address).toEqual(this.address); return Promise.resolve(this.instance); } - getContractClass(id: Fr): Promise { + async getContractClass(id: Fr): Promise { expect(id).toBeInstanceOf(Fr); - const contractClass = getContractClassFromArtifact(this.artifact); - return Promise.resolve(contractClass); + const contractClass = await getContractClassFromArtifact(this.artifact); + return contractClass; } getContractArtifact(id: Fr): Promise { expect(id).toBeInstanceOf(Fr); diff --git a/yarn-project/circuit-types/src/l2_block.ts b/yarn-project/circuit-types/src/l2_block.ts index 037bc95027b..805a204042f 100644 --- a/yarn-project/circuit-types/src/l2_block.ts +++ b/yarn-project/circuit-types/src/l2_block.ts @@ -112,7 +112,7 @@ export class L2Block { * Returns the block's hash (hash of block header). * @returns The block's hash. */ - public hash(): Fr { + public hash(): Promise { return this.header.hash(); } diff --git a/yarn-project/circuit-types/src/l2_block_downloader/l2_block_stream.test.ts b/yarn-project/circuit-types/src/l2_block_downloader/l2_block_stream.test.ts index a7d84ea67fa..b9881f934f6 100644 --- a/yarn-project/circuit-types/src/l2_block_downloader/l2_block_stream.test.ts +++ b/yarn-project/circuit-types/src/l2_block_downloader/l2_block_stream.test.ts @@ -42,7 +42,8 @@ describe('L2BlockStream', () => { const makeBlock = (number: number) => ({ number } as L2Block); - const makeHeader = (number: number) => mock({ hash: () => new Fr(number) } as BlockHeader); + const makeHeader = (number: number) => + mock({ hash: () => Promise.resolve(new Fr(number)) } as BlockHeader); const setRemoteTips = (latest_: number, proven?: number, finalized?: number) => { proven = proven ?? 0; diff --git a/yarn-project/circuit-types/src/l2_block_downloader/l2_block_stream.ts b/yarn-project/circuit-types/src/l2_block_downloader/l2_block_stream.ts index 7597f27c284..3ffc741ea47 100644 --- a/yarn-project/circuit-types/src/l2_block_downloader/l2_block_stream.ts +++ b/yarn-project/circuit-types/src/l2_block_downloader/l2_block_stream.ts @@ -120,7 +120,10 @@ export class L2BlockStream { const localBlockHash = await this.localData.getL2BlockHash(blockNumber); const sourceBlockHash = args.sourceCache.find(id => id.number === blockNumber && id.hash)?.hash ?? - (await this.l2BlockSource.getBlockHeader(blockNumber).then(h => h?.hash().toString())); + (await this.l2BlockSource + .getBlockHeader(blockNumber) + .then(h => h?.hash()) + .then(hash => hash?.toString())); this.log.trace(`Comparing block hashes for block ${blockNumber}`, { localBlockHash, sourceBlockHash, diff --git a/yarn-project/circuit-types/src/logs/l1_payload/encrypted_log_payload.test.ts b/yarn-project/circuit-types/src/logs/l1_payload/encrypted_log_payload.test.ts index e08100b0883..c53700cf976 100644 --- a/yarn-project/circuit-types/src/logs/l1_payload/encrypted_log_payload.test.ts +++ b/yarn-project/circuit-types/src/logs/l1_payload/encrypted_log_payload.test.ts @@ -41,7 +41,7 @@ describe('EncryptedLogPayload', () => { }); it('decrypt a log as incoming', async () => { - const addressSecret = await computeAddressSecret(completeAddress.getPreaddress(), ivskM); + const addressSecret = await computeAddressSecret(await completeAddress.getPreaddress(), ivskM); const recreated = await EncryptedLogPayload.decryptAsIncoming(payload.fields, addressSecret); @@ -65,14 +65,14 @@ describe('EncryptedLogPayload', () => { ); // We set a random secret, as it is simply the result of an oracle call, and we are not actually computing this in nr. - const logTag = new IndexedTaggingSecret(new Fr(69420), 1337).computeTag( + const logTag = await new IndexedTaggingSecret(new Fr(69420), 1337).computeTag( AztecAddress.fromBigInt(0x25afb798ea6d0b8c1618e50fdeafa463059415013d3b7c75d46abf5e242be70cn), ); const log = new EncryptedLogPayload(logTag, contractAddress, plaintext); const ephSk = new GrumpkinScalar(0x1358d15019d4639393d62b97e1588c095957ce74a1c32d6ec7d62fe6705d9538n); - const recipientCompleteAddress = CompleteAddress.fromString( + const recipientCompleteAddress = await CompleteAddress.fromString( '0x25afb798ea6d0b8c1618e50fdeafa463059415013d3b7c75d46abf5e242be70c138af8799f2fba962549802469e12e3b7ba4c5f9c999c6421e05c73f45ec68481970dd8ce0250b677759dfc040f6edaf77c5827a7bcd425e66bcdec3fa7e59bc18dd22d6a4032eefe3a7a55703f583396596235f7c186e450c92981186ee74042e49e00996565114016a1a478309842ecbaf930fb716c3f498e7e10370631d7507f696b8b233de2c1935e43c793399586f532da5ff7c0356636a75acb862e964156e8a3e42bfca3663936ba98c7fd26386a14657c23b5f5146f1a94b6c4651542685ea16f17c580a7cc7c8ff2688dce9bde8bf1f50475f4c3281e1c33404ee0025f50db0733f719462b22eff03cec746bb9e3829ae3636c84fbccd2754b5a5a92087a5f41ccf94a03a2671cd341ba3264c45147e75d4ea96e3b1a58498550b89', ); @@ -97,7 +97,7 @@ describe('EncryptedLogPayload', () => { const ivskM = new GrumpkinScalar(0x0d6e27b21c89a7632f7766e35cc280d43f75bea3898d7328400a5fefc804d462n); - const addressSecret = await computeAddressSecret(recipientCompleteAddress.getPreaddress(), ivskM); + const addressSecret = await computeAddressSecret(await recipientCompleteAddress.getPreaddress(), ivskM); const recreated = await EncryptedLogPayload.decryptAsIncoming(payload.fields, addressSecret); expect(recreated?.toBuffer()).toEqual(log.toBuffer()); }); diff --git a/yarn-project/circuit-types/src/logs/l1_payload/payload.test.ts b/yarn-project/circuit-types/src/logs/l1_payload/payload.test.ts index 6dc3a358683..af100f5a494 100644 --- a/yarn-project/circuit-types/src/logs/l1_payload/payload.test.ts +++ b/yarn-project/circuit-types/src/logs/l1_payload/payload.test.ts @@ -16,8 +16,8 @@ describe('note', () => { expect(Note.fromBuffer(note.toBuffer())).toEqual(note); }); - it('converts to and from json', () => { - expect(jsonParseWithSchema(jsonStringify(note), Note.schema)).toEqual(note); + it('converts to and from json', async () => { + expect(await jsonParseWithSchema(jsonStringify(note), Note.schema)).toEqual(note); }); }); diff --git a/yarn-project/circuit-types/src/messaging/l1_to_l2_message.ts b/yarn-project/circuit-types/src/messaging/l1_to_l2_message.ts index 582a386feb4..36a3ee7c040 100644 --- a/yarn-project/circuit-types/src/messaging/l1_to_l2_message.ts +++ b/yarn-project/circuit-types/src/messaging/l1_to_l2_message.ts @@ -86,7 +86,7 @@ export async function getNonNullifiedL1ToL2MessageWitness( } const [messageIndex, siblingPath] = response; - const messageNullifier = computeL1ToL2MessageNullifier(contractAddress, messageHash, secret); + const messageNullifier = await computeL1ToL2MessageNullifier(contractAddress, messageHash, secret); const [nullifierIndex] = await node.findLeavesIndexes('latest', MerkleTreeId.NULLIFIER_TREE, [messageNullifier]); if (nullifierIndex !== undefined) { diff --git a/yarn-project/circuit-types/src/mocks.ts b/yarn-project/circuit-types/src/mocks.ts index 03cdda0fb79..79646dbd34b 100644 --- a/yarn-project/circuit-types/src/mocks.ts +++ b/yarn-project/circuit-types/src/mocks.ts @@ -40,7 +40,7 @@ import { TxEffect } from './tx_effect.js'; export const randomTxHash = (): TxHash => TxHash.random(); -export const mockPrivateCallExecutionResult = ( +export const mockPrivateCallExecutionResult = async ( seed = 1, numberOfNonRevertiblePublicCallRequests = MAX_ENQUEUED_CALLS_PER_TX / 2, numberOfRevertiblePublicCallRequests = MAX_ENQUEUED_CALLS_PER_TX / 2, @@ -56,7 +56,11 @@ export const mockPrivateCallExecutionResult = ( if (isForPublic) { const publicCallRequests = times(totalPublicCallRequests, i => makePublicCallRequest(seed + 0x102 + i)).reverse(); // Reverse it so that they are sorted by counters in descending order. const publicFunctionArgs = times(totalPublicCallRequests, i => [new Fr(seed + i * 100), new Fr(seed + i * 101)]); - publicCallRequests.forEach((r, i) => (r.argsHash = computeVarArgsHash(publicFunctionArgs[i]))); + for (let i = 0; i < publicCallRequests.length; i++) { + const r = publicCallRequests[i]; + r.argsHash = await computeVarArgsHash(publicFunctionArgs[i]); + i++; + } if (hasPublicTeardownCallRequest) { const request = publicCallRequests.shift()!; @@ -84,11 +88,11 @@ export const mockPrivateCallExecutionResult = ( ); }; -export const mockPrivateExecutionResult = (seed = 1) => { - return new PrivateExecutionResult(mockPrivateCallExecutionResult(seed), Fr.zero()); +export const mockPrivateExecutionResult = async (seed = 1) => { + return new PrivateExecutionResult(await mockPrivateCallExecutionResult(seed), Fr.zero()); }; -export const mockTx = ( +export const mockTx = async ( seed = 1, { numberOfNonRevertiblePublicCallRequests = MAX_ENQUEUED_CALLS_PER_TX / 2, @@ -125,7 +129,10 @@ export const mockTx = ( const publicCallRequests = times(totalPublicCallRequests, i => makePublicCallRequest(seed + 0x102 + i)).reverse(); // Reverse it so that they are sorted by counters in descending order. const publicFunctionArgs = times(totalPublicCallRequests, i => [new Fr(seed + i * 100), new Fr(seed + i * 101)]); - publicCallRequests.forEach((r, i) => (r.argsHash = computeVarArgsHash(publicFunctionArgs[i]))); + for (let i = 0; i < publicCallRequests.length; i++) { + const r = publicCallRequests[i]; + r.argsHash = await computeVarArgsHash(publicFunctionArgs[i]); + } if (hasPublicTeardownCallRequest) { const request = publicCallRequests.shift()!; @@ -163,8 +170,8 @@ export const mockTxForRollup = (seed = 1) => mockTx(seed, { numberOfNonRevertiblePublicCallRequests: 0, numberOfRevertiblePublicCallRequests: 0 }); export const mockSimulatedTx = async (seed = 1) => { - const privateExecutionResult = mockPrivateExecutionResult(seed); - const tx = mockTx(seed); + const privateExecutionResult = await mockPrivateExecutionResult(seed); + const tx = await mockTx(seed); const output = new PublicSimulationOutput( undefined, makeCombinedConstantData(), @@ -219,7 +226,7 @@ export const randomContractInstanceWithAddress = async ( export const randomDeployedContract = async () => { const artifact = randomContractArtifact(); - const contractClassId = computeContractClassId(getContractClassFromArtifact(artifact)); + const contractClassId = await computeContractClassId(await getContractClassFromArtifact(artifact)); return { artifact, instance: await randomContractInstanceWithAddress({ contractClassId }) }; }; diff --git a/yarn-project/circuit-types/src/p2p/block_attestation.test.ts b/yarn-project/circuit-types/src/p2p/block_attestation.test.ts index fc32fa7a704..7050d6b3743 100644 --- a/yarn-project/circuit-types/src/p2p/block_attestation.test.ts +++ b/yarn-project/circuit-types/src/p2p/block_attestation.test.ts @@ -10,25 +10,25 @@ describe('Block Attestation serialization / deserialization', () => { expect(deserialized).toEqual(serialized); }; - it('Should serialize / deserialize', () => { - const attestation = makeBlockAttestation(); + it('Should serialize / deserialize', async () => { + const attestation = await makeBlockAttestation(); const serialized = attestation.toBuffer(); const deserialized = BlockAttestation.fromBuffer(serialized); checkEquivalence(attestation, deserialized); }); - it('Should serialize / deserialize + recover sender', () => { + it('Should serialize / deserialize + recover sender', async () => { const account = Secp256k1Signer.random(); - const attestation = makeBlockAttestation({ signer: account }); + const attestation = await makeBlockAttestation({ signer: account }); const serialized = attestation.toBuffer(); const deserialized = BlockAttestation.fromBuffer(serialized); checkEquivalence(attestation, deserialized); // Recover signature - const sender = deserialized.getSender(); + const sender = await deserialized.getSender(); expect(sender).toEqual(account.address); }); }); diff --git a/yarn-project/circuit-types/src/p2p/block_attestation.ts b/yarn-project/circuit-types/src/p2p/block_attestation.ts index d4fea987a54..decd8d70320 100644 --- a/yarn-project/circuit-types/src/p2p/block_attestation.ts +++ b/yarn-project/circuit-types/src/p2p/block_attestation.ts @@ -49,8 +49,8 @@ export class BlockAttestation extends Gossipable { .transform(obj => new BlockAttestation(obj.payload, obj.signature)); } - override p2pMessageIdentifier(): Buffer32 { - return new BlockAttestationHash(keccak256(this.signature.toBuffer())); + override p2pMessageIdentifier(): Promise { + return Promise.resolve(new BlockAttestationHash(keccak256(this.signature.toBuffer()))); } get archive(): Fr { @@ -62,10 +62,13 @@ export class BlockAttestation extends Gossipable { * Lazily evaluate and cache the sender of the attestation * @returns The sender of the attestation */ - getSender() { + async getSender() { if (!this.sender) { // Recover the sender from the attestation - const hashed = getHashedSignaturePayloadEthSignedMessage(this.payload, SignatureDomainSeparator.blockAttestation); + const hashed = await getHashedSignaturePayloadEthSignedMessage( + this.payload, + SignatureDomainSeparator.blockAttestation, + ); // Cache the sender for later use this.sender = recoverAddress(hashed, this.signature); } @@ -73,7 +76,7 @@ export class BlockAttestation extends Gossipable { return this.sender; } - getPayload(): Buffer { + getPayload(): Promise { return this.payload.getPayloadToSign(SignatureDomainSeparator.blockAttestation); } diff --git a/yarn-project/circuit-types/src/p2p/block_proposal.test.ts b/yarn-project/circuit-types/src/p2p/block_proposal.test.ts index f1ea41ece79..3db49eabade 100644 --- a/yarn-project/circuit-types/src/p2p/block_proposal.test.ts +++ b/yarn-project/circuit-types/src/p2p/block_proposal.test.ts @@ -10,25 +10,25 @@ describe('Block Proposal serialization / deserialization', () => { expect(deserialized).toEqual(serialized); }; - it('Should serialize / deserialize', () => { - const proposal = makeBlockProposal(); + it('Should serialize / deserialize', async () => { + const proposal = await makeBlockProposal(); const serialized = proposal.toBuffer(); const deserialized = BlockProposal.fromBuffer(serialized); checkEquivalence(proposal, deserialized); }); - it('Should serialize / deserialize + recover sender', () => { + it('Should serialize / deserialize + recover sender', async () => { const account = Secp256k1Signer.random(); - const proposal = makeBlockProposal({ signer: account }); + const proposal = await makeBlockProposal({ signer: account }); const serialized = proposal.toBuffer(); const deserialized = BlockProposal.fromBuffer(serialized); checkEquivalence(proposal, deserialized); // Recover signature - const sender = deserialized.getSender(); + const sender = await deserialized.getSender(); expect(sender).toEqual(account.address); }); }); diff --git a/yarn-project/circuit-types/src/p2p/block_proposal.ts b/yarn-project/circuit-types/src/p2p/block_proposal.ts index b65b0870833..4b698e9cff8 100644 --- a/yarn-project/circuit-types/src/p2p/block_proposal.ts +++ b/yarn-project/circuit-types/src/p2p/block_proposal.ts @@ -41,8 +41,8 @@ export class BlockProposal extends Gossipable { super(); } - override p2pMessageIdentifier(): Buffer32 { - return new BlockProposalHash(keccak256(this.signature.toBuffer())); + override p2pMessageIdentifier(): Promise { + return Promise.resolve(new BlockProposalHash(keccak256(this.signature.toBuffer()))); } get archive(): Fr { @@ -57,7 +57,7 @@ export class BlockProposal extends Gossipable { payload: ConsensusPayload, payloadSigner: (payload: Buffer32) => Promise, ) { - const hashed = getHashedSignaturePayload(payload, SignatureDomainSeparator.blockProposal); + const hashed = await getHashedSignaturePayload(payload, SignatureDomainSeparator.blockProposal); const sig = await payloadSigner(hashed); return new BlockProposal(payload, sig); @@ -66,9 +66,12 @@ export class BlockProposal extends Gossipable { /**Get Sender * Lazily evaluate the sender of the proposal; result is cached */ - getSender() { + async getSender() { if (!this.sender) { - const hashed = getHashedSignaturePayloadEthSignedMessage(this.payload, SignatureDomainSeparator.blockProposal); + const hashed = await getHashedSignaturePayloadEthSignedMessage( + this.payload, + SignatureDomainSeparator.blockProposal, + ); // Cache the sender for later use this.sender = recoverAddress(hashed, this.signature); } diff --git a/yarn-project/circuit-types/src/p2p/consensus_payload.ts b/yarn-project/circuit-types/src/p2p/consensus_payload.ts index b1e72369013..efe0bad8b25 100644 --- a/yarn-project/circuit-types/src/p2p/consensus_payload.ts +++ b/yarn-project/circuit-types/src/p2p/consensus_payload.ts @@ -36,14 +36,14 @@ export class ConsensusPayload implements Signable { return [fields.header, fields.archive, fields.txHashes] as const; } - getPayloadToSign(domainSeparator: SignatureDomainSeparator): Buffer { + async getPayloadToSign(domainSeparator: SignatureDomainSeparator): Promise { const abi = parseAbiParameters('uint8, (bytes32, bytes32, (uint256, uint256), bytes, bytes32[])'); const txArray = this.txHashes.map(tx => tx.toString()); const encodedData = encodeAbiParameters(abi, [ domainSeparator, [ this.archive.toString(), - this.header.hash().toString(), + (await this.header.hash()).toString(), [0n, 0n] /* @todo See #9963 */, this.header.toString(), txArray, diff --git a/yarn-project/circuit-types/src/p2p/gossipable.ts b/yarn-project/circuit-types/src/p2p/gossipable.ts index 4de52bce26f..a54968f2475 100644 --- a/yarn-project/circuit-types/src/p2p/gossipable.ts +++ b/yarn-project/circuit-types/src/p2p/gossipable.ts @@ -16,7 +16,7 @@ export abstract class Gossipable { * * - A digest of the message information, this key is used for deduplication */ - abstract p2pMessageIdentifier(): Buffer32; + abstract p2pMessageIdentifier(): Promise; /** To Buffer * diff --git a/yarn-project/circuit-types/src/p2p/mocks.ts b/yarn-project/circuit-types/src/p2p/mocks.ts index 8896980843b..ff9fd3f6a0f 100644 --- a/yarn-project/circuit-types/src/p2p/mocks.ts +++ b/yarn-project/circuit-types/src/p2p/mocks.ts @@ -16,7 +16,7 @@ export interface MakeConsensusPayloadOptions { txHashes?: TxHash[]; } -const makeAndSignConsensusPayload = ( +const makeAndSignConsensusPayload = async ( domainSeparator: SignatureDomainSeparator, options?: MakeConsensusPayloadOptions, ) => { @@ -33,19 +33,19 @@ const makeAndSignConsensusPayload = ( txHashes, }); - const hash = getHashedSignaturePayloadEthSignedMessage(payload, domainSeparator); + const hash = await getHashedSignaturePayloadEthSignedMessage(payload, domainSeparator); const signature = signer.sign(hash); return { payload, signature }; }; -export const makeBlockProposal = (options?: MakeConsensusPayloadOptions): BlockProposal => { - const { payload, signature } = makeAndSignConsensusPayload(SignatureDomainSeparator.blockProposal, options); +export const makeBlockProposal = async (options?: MakeConsensusPayloadOptions): Promise => { + const { payload, signature } = await makeAndSignConsensusPayload(SignatureDomainSeparator.blockProposal, options); return new BlockProposal(payload, signature); }; // TODO(https://github.com/AztecProtocol/aztec-packages/issues/8028) -export const makeBlockAttestation = (options?: MakeConsensusPayloadOptions): BlockAttestation => { - const { payload, signature } = makeAndSignConsensusPayload(SignatureDomainSeparator.blockAttestation, options); +export const makeBlockAttestation = async (options?: MakeConsensusPayloadOptions): Promise => { + const { payload, signature } = await makeAndSignConsensusPayload(SignatureDomainSeparator.blockAttestation, options); return new BlockAttestation(payload, signature); }; diff --git a/yarn-project/circuit-types/src/p2p/signature_utils.ts b/yarn-project/circuit-types/src/p2p/signature_utils.ts index 4c05ccb3823..49355ebe7e0 100644 --- a/yarn-project/circuit-types/src/p2p/signature_utils.ts +++ b/yarn-project/circuit-types/src/p2p/signature_utils.ts @@ -7,7 +7,7 @@ export enum SignatureDomainSeparator { } export interface Signable { - getPayloadToSign(domainSeparator: SignatureDomainSeparator): Buffer; + getPayloadToSign(domainSeparator: SignatureDomainSeparator): Promise; } /** @@ -15,8 +15,11 @@ export interface Signable { * @param s - The `Signable` to sign * @returns The hashed payload for the signature of the `Signable` */ -export function getHashedSignaturePayload(s: Signable, domainSeparator: SignatureDomainSeparator): Buffer32 { - return Buffer32.fromBuffer(keccak256(s.getPayloadToSign(domainSeparator))); +export async function getHashedSignaturePayload( + s: Signable, + domainSeparator: SignatureDomainSeparator, +): Promise { + return Buffer32.fromBuffer(keccak256(await s.getPayloadToSign(domainSeparator))); } /** @@ -24,10 +27,10 @@ export function getHashedSignaturePayload(s: Signable, domainSeparator: Signatur * @param s - the `Signable` to sign * @returns The hashed payload for the signature of the `Signable` as an Ethereum signed message */ -export function getHashedSignaturePayloadEthSignedMessage( +export async function getHashedSignaturePayloadEthSignedMessage( s: Signable, domainSeparator: SignatureDomainSeparator, -): Buffer32 { - const payload = getHashedSignaturePayload(s, domainSeparator); +): Promise { + const payload = await getHashedSignaturePayload(s, domainSeparator); return makeEthSignDigest(payload); } diff --git a/yarn-project/circuit-types/src/private_execution_result.test.ts b/yarn-project/circuit-types/src/private_execution_result.test.ts index 237b9c9086d..4a4d67f8c53 100644 --- a/yarn-project/circuit-types/src/private_execution_result.test.ts +++ b/yarn-project/circuit-types/src/private_execution_result.test.ts @@ -41,8 +41,7 @@ describe('execution_result', () => { describe('serialization', () => { it('serializes and deserializes correctly', async () => { const instance = await PrivateExecutionResult.random(); - jsonParseWithSchema; - expect(jsonParseWithSchema(jsonStringify(instance), PrivateExecutionResult.schema)).toEqual(instance); + expect(await jsonParseWithSchema(jsonStringify(instance), PrivateExecutionResult.schema)).toEqual(instance); }); }); diff --git a/yarn-project/circuit-types/src/prover_coordination/epoch_proof_quote.test.ts b/yarn-project/circuit-types/src/prover_coordination/epoch_proof_quote.test.ts index fe29fcb941d..889aa030a19 100644 --- a/yarn-project/circuit-types/src/prover_coordination/epoch_proof_quote.test.ts +++ b/yarn-project/circuit-types/src/prover_coordination/epoch_proof_quote.test.ts @@ -30,8 +30,8 @@ describe('epoch proof quote', () => { checkEquivalence(quote, deserialised); }); - it('should serialize and deserialize from JSON', () => { - const deserialised = jsonParseWithSchema(jsonStringify(quote), EpochProofQuote.schema); + it('should serialize and deserialize from JSON', async () => { + const deserialised = await jsonParseWithSchema(jsonStringify(quote), EpochProofQuote.schema); checkEquivalence(quote, deserialised); }); }); diff --git a/yarn-project/circuit-types/src/prover_coordination/epoch_proof_quote.ts b/yarn-project/circuit-types/src/prover_coordination/epoch_proof_quote.ts index 26ed60e0d35..a7a816d48a3 100644 --- a/yarn-project/circuit-types/src/prover_coordination/epoch_proof_quote.ts +++ b/yarn-project/circuit-types/src/prover_coordination/epoch_proof_quote.ts @@ -29,9 +29,9 @@ export class EpochProofQuote extends Gossipable { return [fields.payload, fields.signature] as const; } - override p2pMessageIdentifier(): Buffer32 { + override p2pMessageIdentifier(): Promise { // TODO: https://github.com/AztecProtocol/aztec-packages/issues/8911 - return new Buffer32(keccak256(this.signature.toBuffer())); + return Promise.resolve(new Buffer32(keccak256(this.signature.toBuffer()))); } override toBuffer(): Buffer { diff --git a/yarn-project/circuit-types/src/public_execution_request.ts b/yarn-project/circuit-types/src/public_execution_request.ts index 6e386c3d888..4fb19d2f96c 100644 --- a/yarn-project/circuit-types/src/public_execution_request.ts +++ b/yarn-project/circuit-types/src/public_execution_request.ts @@ -65,23 +65,23 @@ export class PublicExecutionRequest { return this.callContext.isEmpty() && this.args.length === 0; } - isForCallRequest(callRequest: PublicCallRequest) { + async isForCallRequest(callRequest: PublicCallRequest) { return ( this.callContext.msgSender.equals(callRequest.msgSender) && this.callContext.contractAddress.equals(callRequest.contractAddress) && this.callContext.functionSelector.equals(callRequest.functionSelector) && this.callContext.isStaticCall == callRequest.isStaticCall && - computeVarArgsHash(this.args).equals(callRequest.argsHash) + (await computeVarArgsHash(this.args)).equals(callRequest.argsHash) ); } - toCallRequest(): PublicCallRequest { + async toCallRequest(): Promise { return new PublicCallRequest( this.callContext.msgSender, this.callContext.contractAddress, this.callContext.functionSelector, this.callContext.isStaticCall, - computeVarArgsHash(this.args), + await computeVarArgsHash(this.args), ); } diff --git a/yarn-project/circuit-types/src/test/factories.ts b/yarn-project/circuit-types/src/test/factories.ts index bfd6e6e2dbf..2c71acb7e2a 100644 --- a/yarn-project/circuit-types/src/test/factories.ts +++ b/yarn-project/circuit-types/src/test/factories.ts @@ -31,7 +31,7 @@ import { type GasUsed } from '../tx/gas_used.js'; import { makeProcessedTxFromPrivateOnlyTx, makeProcessedTxFromTxWithPublicCalls } from '../tx/processed_tx.js'; /** Makes a bloated processed tx for testing purposes. */ -export function makeBloatedProcessedTx({ +export async function makeBloatedProcessedTx({ seed = 1, header, db, @@ -66,8 +66,8 @@ export function makeBloatedProcessedTx({ txConstantData.protocolContractTreeRoot = protocolContractTreeRoot; const tx = !privateOnly - ? mockTx(seed) - : mockTx(seed, { numberOfNonRevertiblePublicCallRequests: 0, numberOfRevertiblePublicCallRequests: 0 }); + ? await mockTx(seed) + : await mockTx(seed, { numberOfNonRevertiblePublicCallRequests: 0, numberOfRevertiblePublicCallRequests: 0 }); tx.data.constants = txConstantData; // No side effects were created in mockTx. The default gasUsed is the tx overhead. diff --git a/yarn-project/circuit-types/src/tx/processed_tx.ts b/yarn-project/circuit-types/src/tx/processed_tx.ts index 85f0640c099..1d471ca0c2b 100644 --- a/yarn-project/circuit-types/src/tx/processed_tx.ts +++ b/yarn-project/circuit-types/src/tx/processed_tx.ts @@ -76,12 +76,12 @@ export type FailedTx = { error: Error; }; -export function makeProcessedTxFromPrivateOnlyTx( +export async function makeProcessedTxFromPrivateOnlyTx( tx: Tx, transactionFee: Fr, feePaymentPublicDataWrite: PublicDataWrite | undefined, globalVariables: GlobalVariables, -): ProcessedTx { +): Promise { const constants = CombinedConstantData.combine(tx.data.constants, globalVariables); const publicDataWrites = feePaymentPublicDataWrite ? [feePaymentPublicDataWrite] : []; @@ -89,7 +89,7 @@ export function makeProcessedTxFromPrivateOnlyTx( const data = tx.data.forRollup!; const txEffect = new TxEffect( RevertCode.OK, - tx.getTxHash(), + await tx.getTxHash(), transactionFee, data.end.noteHashes.filter(h => !h.isZero()), data.end.nullifiers.filter(h => !h.isZero()), @@ -127,13 +127,13 @@ export function toNumBlobFields(txs: ProcessedTx[]): number { }, 0); } -export function makeProcessedTxFromTxWithPublicCalls( +export async function makeProcessedTxFromTxWithPublicCalls( tx: Tx, avmProvingRequest: AvmProvingRequest, gasUsed: GasUsed, revertCode: RevertCode, revertReason: SimulationError | undefined, -): ProcessedTx { +): Promise { const avmOutput = avmProvingRequest.inputs.output; const constants = CombinedConstantData.combine(tx.data.constants, avmOutput.globalVariables); @@ -149,7 +149,7 @@ export function makeProcessedTxFromTxWithPublicCalls( const txEffect = new TxEffect( revertCode, - tx.getTxHash(), + await tx.getTxHash(), avmOutput.transactionFee, avmOutput.accumulatedData.noteHashes.filter(h => !h.isZero()), avmOutput.accumulatedData.nullifiers.filter(h => !h.isZero()), diff --git a/yarn-project/circuit-types/src/tx/tx.test.ts b/yarn-project/circuit-types/src/tx/tx.test.ts index 0710303b16b..8a272a1c4bd 100644 --- a/yarn-project/circuit-types/src/tx/tx.test.ts +++ b/yarn-project/circuit-types/src/tx/tx.test.ts @@ -4,14 +4,14 @@ import { mockTx } from '../mocks.js'; import { Tx } from './tx.js'; describe('Tx', () => { - it('convert to and from buffer', () => { - const tx = mockTx(); + it('convert to and from buffer', async () => { + const tx = await mockTx(); const buf = tx.toBuffer(); expect(Tx.fromBuffer(buf)).toEqual(tx); }); - it('convert to and from json', () => { - const tx = mockTx(); + it('convert to and from json', async () => { + const tx = await mockTx(); const json = jsonStringify(tx); expect(Tx.schema.parse(JSON.parse(json))).toEqual(tx); }); diff --git a/yarn-project/circuit-types/src/tx/tx.ts b/yarn-project/circuit-types/src/tx/tx.ts index 53d7ee59da5..35d424f9cc9 100644 --- a/yarn-project/circuit-types/src/tx/tx.ts +++ b/yarn-project/circuit-types/src/tx/tx.ts @@ -63,8 +63,8 @@ export class Tx extends Gossipable { } // Gossipable method - override p2pMessageIdentifier(): Buffer32 { - return new Buffer32(this.getTxHash().toBuffer()); + override async p2pMessageIdentifier(): Promise { + return new Buffer32((await this.getTxHash()).toBuffer()); } hasPublicCalls() { @@ -159,19 +159,19 @@ export class Tx extends Gossipable { * @param logsSource - An instance of `L2LogsSource` which can be used to obtain the logs. * @returns The requested logs. */ - public getPublicLogs(logsSource: L2LogsSource): Promise { - return logsSource.getPublicLogs({ txHash: this.getTxHash() }); + public async getPublicLogs(logsSource: L2LogsSource): Promise { + return logsSource.getPublicLogs({ txHash: await this.getTxHash() }); } /** * Computes (if necessary) & return transaction hash. * @returns The hash of the public inputs of the private kernel tail circuit. */ - getTxHash(forceRecompute = false): TxHash { + async getTxHash(forceRecompute = false): Promise { if (!this.txHash || forceRecompute) { const hash = this.data.forPublic - ? this.data.toPrivateToPublicKernelCircuitPublicInputs().hash() - : this.data.toPrivateToRollupKernelCircuitPublicInputs().hash(); + ? await this.data.toPrivateToPublicKernelCircuitPublicInputs().hash() + : await this.data.toPrivateToRollupKernelCircuitPublicInputs().hash(); this.txHash = new TxHash(hash); } return this.txHash!; @@ -188,9 +188,9 @@ export class Tx extends Gossipable { } /** Returns stats about this tx. */ - getStats(): TxStats { + async getStats(): Promise { return { - txHash: this.getTxHash().toString(), + txHash: (await this.getTxHash()).toString(), noteHashCount: this.data.getNonEmptyNoteHashes().length, nullifierCount: this.data.getNonEmptyNullifiers().length, @@ -244,8 +244,8 @@ export class Tx extends Gossipable { * @param tx - Tx-like object. * @returns - The hash. */ - static getHash(tx: Tx | HasHash): TxHash { - return hasHash(tx) ? tx.hash : tx.getTxHash(); + static async getHash(tx: Tx | HasHash): Promise { + return hasHash(tx) ? tx.hash : await tx.getTxHash(); } /** @@ -253,8 +253,8 @@ export class Tx extends Gossipable { * @param txs - The txs to get the hashes from. * @returns The corresponding array of hashes. */ - static getHashes(txs: (Tx | HasHash)[]): TxHash[] { - return txs.map(Tx.getHash); + static async getHashes(txs: (Tx | HasHash)[]): Promise { + return await Promise.all(txs.map(Tx.getHash)); } /** diff --git a/yarn-project/circuit-types/src/tx_effect.ts b/yarn-project/circuit-types/src/tx_effect.ts index 6255d13c274..5ad06039585 100644 --- a/yarn-project/circuit-types/src/tx_effect.ts +++ b/yarn-project/circuit-types/src/tx_effect.ts @@ -23,7 +23,6 @@ import { } from '@aztec/circuits.js'; import { type FieldsOf, makeTuple, makeTupleAsync } from '@aztec/foundation/array'; import { toBufferBE } from '@aztec/foundation/bigint-buffer'; -import { TX_EFFECT_PREFIX_BYTE_LENGTH, TX_START_PREFIX, TX_START_PREFIX_BYTES_LENGTH } from '@aztec/foundation/blob'; import { padArrayEnd } from '@aztec/foundation/collection'; import { sha256Trunc } from '@aztec/foundation/crypto'; import { jsonStringify } from '@aztec/foundation/json-rpc'; @@ -44,6 +43,13 @@ import { TxHash } from './tx/tx_hash.js'; export { RevertCodeEnum } from '@aztec/circuits.js'; +// This will appear as 0x74785f7374617274 in logs +export const TX_START_PREFIX = 8392562855083340404n; +// These are helper constants to decode tx effects from blob encoded fields +export const TX_START_PREFIX_BYTES_LENGTH = TX_START_PREFIX.toString(16).length / 2; +// 7 bytes for: | 0 | txlen[0] | txlen[1] | 0 | REVERT_CODE_PREFIX | 0 | revertCode | +export const TX_EFFECT_PREFIX_BYTE_LENGTH = TX_START_PREFIX_BYTES_LENGTH + 7; + export class TxEffect { constructor( /** diff --git a/yarn-project/circuit-types/src/tx_execution_request.test.ts b/yarn-project/circuit-types/src/tx_execution_request.test.ts index 4fbe718f476..902193a6e82 100644 --- a/yarn-project/circuit-types/src/tx_execution_request.test.ts +++ b/yarn-project/circuit-types/src/tx_execution_request.test.ts @@ -6,6 +6,6 @@ describe('TxExecutionRequest', () => { it('serializes and deserializes', async () => { const request = await TxExecutionRequest.random(); const json = jsonStringify(request); - expect(jsonParseWithSchema(json, TxExecutionRequest.schema)).toEqual(request); + expect(await jsonParseWithSchema(json, TxExecutionRequest.schema)).toEqual(request); }); }); diff --git a/yarn-project/circuit-types/src/tx_execution_request.ts b/yarn-project/circuit-types/src/tx_execution_request.ts index c40b0ddbde2..efca36f8a1d 100644 --- a/yarn-project/circuit-types/src/tx_execution_request.ts +++ b/yarn-project/circuit-types/src/tx_execution_request.ts @@ -138,7 +138,7 @@ export class TxExecutionRequest { FunctionSelector.random(), Fr.random(), TxContext.empty(), - [HashedValues.random()], + [await HashedValues.random()], [AuthWitness.random()], ); } diff --git a/yarn-project/circuits.js/src/barretenberg/crypto/aes128/index.ts b/yarn-project/circuits.js/src/barretenberg/crypto/aes128/index.ts index 01cc276166e..83e17d17c70 100644 --- a/yarn-project/circuits.js/src/barretenberg/crypto/aes128/index.ts +++ b/yarn-project/circuits.js/src/barretenberg/crypto/aes128/index.ts @@ -1,4 +1,4 @@ -import { BarretenbergLazy, RawBuffer } from '@aztec/bb.js'; +import { BarretenbergSync, RawBuffer } from '@aztec/bb.js'; import { Buffer } from 'buffer'; @@ -22,9 +22,9 @@ export class Aes128 { paddingBuffer.fill(numPaddingBytes); const input = Buffer.concat([data, paddingBuffer]); - const api = await BarretenbergLazy.getSingleton(); + const api = await BarretenbergSync.initSingleton(); return Buffer.from( - await api.aesEncryptBufferCbc(new RawBuffer(input), new RawBuffer(iv), new RawBuffer(key), input.length), + api.aesEncryptBufferCbc(new RawBuffer(input), new RawBuffer(iv), new RawBuffer(key), input.length), ); } @@ -36,9 +36,9 @@ export class Aes128 { * @returns Decrypted data. */ public async decryptBufferCBC(data: Uint8Array, iv: Uint8Array, key: Uint8Array) { - const api = await BarretenbergLazy.getSingleton(); + const api = await BarretenbergSync.initSingleton(); const paddedBuffer = Buffer.from( - await api.aesDecryptBufferCbc(new RawBuffer(data), new RawBuffer(iv), new RawBuffer(key), data.length), + api.aesDecryptBufferCbc(new RawBuffer(data), new RawBuffer(iv), new RawBuffer(key), data.length), ); const paddingToRemove = paddedBuffer[paddedBuffer.length - 1]; return paddedBuffer.subarray(0, paddedBuffer.length - paddingToRemove); diff --git a/yarn-project/circuits.js/src/barretenberg/crypto/ecdsa/index.ts b/yarn-project/circuits.js/src/barretenberg/crypto/ecdsa/index.ts index 1c9d7c54e60..b545f0e0d0d 100644 --- a/yarn-project/circuits.js/src/barretenberg/crypto/ecdsa/index.ts +++ b/yarn-project/circuits.js/src/barretenberg/crypto/ecdsa/index.ts @@ -1,4 +1,4 @@ -import { BarretenbergLazy } from '@aztec/bb.js'; +import { BarretenbergSync } from '@aztec/bb.js'; import { numToInt32BE } from '@aztec/foundation/serialize'; import { concatenateUint8Arrays } from '../../serialize.js'; @@ -17,8 +17,8 @@ export class Ecdsa { * @returns A secp256k1 public key. */ public async computePublicKey(privateKey: Buffer): Promise { - const api = await BarretenbergLazy.getSingleton(); - const [result] = await api.getWasm().callWasmExport('ecdsa__compute_public_key', [privateKey], [64]); + const api = await BarretenbergSync.initSingleton(); + const [result] = api.getWasm().callWasmExport('ecdsa__compute_public_key', [privateKey], [64]); return Buffer.from(result); } @@ -29,9 +29,9 @@ export class Ecdsa { * @returns An ECDSA signature of the form (r, s, v). */ public async constructSignature(msg: Uint8Array, privateKey: Buffer) { - const api = await BarretenbergLazy.getSingleton(); + const api = await BarretenbergSync.initSingleton(); const messageArray = concatenateUint8Arrays([numToInt32BE(msg.length), msg]); - const [r, s, v] = await api + const [r, s, v] = api .getWasm() .callWasmExport('ecdsa__construct_signature_', [messageArray, privateKey], [32, 32, 1]); return new EcdsaSignature(Buffer.from(r), Buffer.from(s), Buffer.from(v)); @@ -44,9 +44,9 @@ export class Ecdsa { * @returns The secp256k1 public key of the signer. */ public async recoverPublicKey(msg: Uint8Array, sig: EcdsaSignature): Promise { - const api = await BarretenbergLazy.getSingleton(); + const api = await BarretenbergSync.initSingleton(); const messageArray = concatenateUint8Arrays([numToInt32BE(msg.length), msg]); - const [result] = await api + const [result] = api .getWasm() .callWasmExport('ecdsa__recover_public_key_from_signature_', [messageArray, sig.r, sig.s, sig.v], [64]); return Buffer.from(result); @@ -60,9 +60,9 @@ export class Ecdsa { * @returns True or false. */ public async verifySignature(msg: Uint8Array, pubKey: Buffer, sig: EcdsaSignature) { - const api = await BarretenbergLazy.getSingleton(); + const api = await BarretenbergSync.initSingleton(); const messageArray = concatenateUint8Arrays([numToInt32BE(msg.length), msg]); - const [result] = await api + const [result] = api .getWasm() .callWasmExport('ecdsa__verify_signature_', [messageArray, pubKey, sig.r, sig.s, sig.v], [1]); return result[0] === 1; diff --git a/yarn-project/circuits.js/src/barretenberg/crypto/grumpkin/index.ts b/yarn-project/circuits.js/src/barretenberg/crypto/grumpkin/index.ts index ecda999647b..241a582ab6e 100644 --- a/yarn-project/circuits.js/src/barretenberg/crypto/grumpkin/index.ts +++ b/yarn-project/circuits.js/src/barretenberg/crypto/grumpkin/index.ts @@ -1,4 +1,4 @@ -import { BarretenbergLazy } from '@aztec/bb.js'; +import { BarretenbergSync } from '@aztec/bb.js'; import { Fr, type GrumpkinScalar, Point } from '@aztec/foundation/fields'; /** @@ -28,10 +28,8 @@ export class Grumpkin { * @returns Result of the multiplication. */ public async mul(point: Point, scalar: GrumpkinScalar): Promise { - const api = await BarretenbergLazy.getSingleton(); - const [result] = await api - .getWasm() - .callWasmExport('ecc_grumpkin__mul', [point.toBuffer(), scalar.toBuffer()], [64]); + const api = await BarretenbergSync.initSingleton(); + const [result] = api.getWasm().callWasmExport('ecc_grumpkin__mul', [point.toBuffer(), scalar.toBuffer()], [64]); return Point.fromBuffer(Buffer.from(result)); } @@ -42,8 +40,8 @@ export class Grumpkin { * @returns Result of the addition. */ public async add(a: Point, b: Point): Promise { - const api = await BarretenbergLazy.getSingleton(); - const [result] = await api.getWasm().callWasmExport('ecc_grumpkin__add', [a.toBuffer(), b.toBuffer()], [64]); + const api = await BarretenbergSync.initSingleton(); + const [result] = api.getWasm().callWasmExport('ecc_grumpkin__add', [a.toBuffer(), b.toBuffer()], [64]); return Point.fromBuffer(Buffer.from(result)); } @@ -58,8 +56,8 @@ export class Grumpkin { const pointsByteLength = points.length * Point.SIZE_IN_BYTES; - const api = await BarretenbergLazy.getSingleton(); - const [result] = await api + const api = await BarretenbergSync.initSingleton(); + const [result] = api .getWasm() .callWasmExport( 'ecc_grumpkin__batch_mul', @@ -79,10 +77,8 @@ export class Grumpkin { * @returns Random field element. */ public async getRandomFr(): Promise { - const api = await BarretenbergLazy.getSingleton(); - const [result] = await api - .getWasm() - .callWasmExport('ecc_grumpkin__get_random_scalar_mod_circuit_modulus', [], [32]); + const api = await BarretenbergSync.initSingleton(); + const [result] = api.getWasm().callWasmExport('ecc_grumpkin__get_random_scalar_mod_circuit_modulus', [], [32]); return Fr.fromBuffer(Buffer.from(result)); } @@ -92,8 +88,8 @@ export class Grumpkin { * @returns Buffer representation of the field element. */ public async reduce512BufferToFr(uint512Buf: Buffer): Promise { - const api = await BarretenbergLazy.getSingleton(); - const [result] = await api + const api = await BarretenbergSync.initSingleton(); + const [result] = api .getWasm() .callWasmExport('ecc_grumpkin__reduce512_buffer_mod_circuit_modulus', [uint512Buf], [32]); return Fr.fromBuffer(Buffer.from(result)); diff --git a/yarn-project/circuits.js/src/barretenberg/crypto/schnorr/index.ts b/yarn-project/circuits.js/src/barretenberg/crypto/schnorr/index.ts index 8688e6380c9..43f09559564 100644 --- a/yarn-project/circuits.js/src/barretenberg/crypto/schnorr/index.ts +++ b/yarn-project/circuits.js/src/barretenberg/crypto/schnorr/index.ts @@ -1,4 +1,4 @@ -import { BarretenbergLazy } from '@aztec/bb.js'; +import { BarretenbergSync } from '@aztec/bb.js'; import { type GrumpkinScalar, Point } from '@aztec/foundation/fields'; import { numToInt32BE } from '@aztec/foundation/serialize'; @@ -18,8 +18,8 @@ export class Schnorr { * @returns A grumpkin public key. */ public async computePublicKey(privateKey: GrumpkinScalar): Promise { - const api = await BarretenbergLazy.getSingleton(); - const [result] = await api.getWasm().callWasmExport('schnorr_compute_public_key', [privateKey.toBuffer()], [64]); + const api = await BarretenbergSync.initSingleton(); + const [result] = api.getWasm().callWasmExport('schnorr_compute_public_key', [privateKey.toBuffer()], [64]); return Point.fromBuffer(Buffer.from(result)); } @@ -30,9 +30,9 @@ export class Schnorr { * @returns A Schnorr signature of the form (s, e). */ public async constructSignature(msg: Uint8Array, privateKey: GrumpkinScalar) { - const api = await BarretenbergLazy.getSingleton(); + const api = await BarretenbergSync.initSingleton(); const messageArray = concatenateUint8Arrays([numToInt32BE(msg.length), msg]); - const [s, e] = await api + const [s, e] = api .getWasm() .callWasmExport('schnorr_construct_signature', [messageArray, privateKey.toBuffer()], [32, 32]); return new SchnorrSignature(Buffer.from([...s, ...e])); @@ -46,9 +46,9 @@ export class Schnorr { * @returns True or false. */ public async verifySignature(msg: Uint8Array, pubKey: PublicKey, sig: SchnorrSignature) { - const api = await BarretenbergLazy.getSingleton(); + const api = await BarretenbergSync.initSingleton(); const messageArray = concatenateUint8Arrays([numToInt32BE(msg.length), msg]); - const [result] = await api + const [result] = api .getWasm() .callWasmExport('schnorr_verify_signature', [messageArray, pubKey.toBuffer(), sig.s, sig.e], [1]); return result[0] === 1; diff --git a/yarn-project/circuits.js/src/barretenberg/crypto/secp256k1/index.ts b/yarn-project/circuits.js/src/barretenberg/crypto/secp256k1/index.ts index 1b0088020f5..58d5d6be475 100644 --- a/yarn-project/circuits.js/src/barretenberg/crypto/secp256k1/index.ts +++ b/yarn-project/circuits.js/src/barretenberg/crypto/secp256k1/index.ts @@ -1,4 +1,4 @@ -import { BarretenbergLazy } from '@aztec/bb.js'; +import { BarretenbergSync } from '@aztec/bb.js'; /** * Secp256k1 elliptic curve operations. @@ -27,8 +27,8 @@ export class Secp256k1 { * @returns Result of the multiplication. */ public async mul(point: Uint8Array, scalar: Uint8Array) { - const api = await BarretenbergLazy.getSingleton(); - const [result] = await api.getWasm().callWasmExport('ecc_secp256k1__mul', [point, scalar], [64]); + const api = await BarretenbergSync.initSingleton(); + const [result] = api.getWasm().callWasmExport('ecc_secp256k1__mul', [point, scalar], [64]); return Buffer.from(result); } @@ -37,10 +37,8 @@ export class Secp256k1 { * @returns Random field element. */ public async getRandomFr() { - const api = await BarretenbergLazy.getSingleton(); - const [result] = await api - .getWasm() - .callWasmExport('ecc_secp256k1__get_random_scalar_mod_circuit_modulus', [], [32]); + const api = await BarretenbergSync.initSingleton(); + const [result] = api.getWasm().callWasmExport('ecc_secp256k1__get_random_scalar_mod_circuit_modulus', [], [32]); return Buffer.from(result); } @@ -50,8 +48,8 @@ export class Secp256k1 { * @returns Buffer representation of the field element. */ public async reduce512BufferToFr(uint512Buf: Buffer) { - const api = await BarretenbergLazy.getSingleton(); - const [result] = await api + const api = await BarretenbergSync.initSingleton(); + const [result] = api .getWasm() .callWasmExport('ecc_secp256k1__reduce512_buffer_mod_circuit_modulus', [uint512Buf], [32]); return Buffer.from(result); diff --git a/yarn-project/circuits.js/src/contract/artifact_hash.test.ts b/yarn-project/circuits.js/src/contract/artifact_hash.test.ts index 45cc32d5138..453f058785d 100644 --- a/yarn-project/circuits.js/src/contract/artifact_hash.test.ts +++ b/yarn-project/circuits.js/src/contract/artifact_hash.test.ts @@ -4,7 +4,7 @@ import { getTestContractArtifact } from '../tests/fixtures.js'; import { computeArtifactHash } from './artifact_hash.js'; describe('ArtifactHash', () => { - it('calculates the artifact hash', () => { + it('calculates the artifact hash', async () => { const emptyArtifact: ContractArtifact = { fileMap: [], functions: [], @@ -16,17 +16,19 @@ describe('ArtifactHash', () => { storageLayout: {}, notes: {}, }; - expect(computeArtifactHash(emptyArtifact).toString()).toMatchInlineSnapshot( + const hash = await computeArtifactHash(emptyArtifact); + expect(hash.toString()).toMatchInlineSnapshot( `"0x0dea64e7fa0688017f77bcb7075485485afb4a5f1f8508483398869439f82fdf"`, ); }); - it('calculates the test contract artifact hash multiple times to ensure deterministic hashing', () => { + it('calculates the test contract artifact hash multiple times to ensure deterministic hashing', async () => { const testArtifact = getTestContractArtifact(); - const calculatedArtifactHash = computeArtifactHash(testArtifact).toString(); + const calculatedArtifactHash = (await computeArtifactHash(testArtifact)).toString(); for (let i = 0; i < 1000; i++) { - expect(computeArtifactHash(testArtifact).toString()).toBe(calculatedArtifactHash); + const testArtifactHash = await computeArtifactHash(testArtifact); + expect(testArtifactHash.toString()).toBe(calculatedArtifactHash); } }); }); diff --git a/yarn-project/circuits.js/src/contract/artifact_hash.ts b/yarn-project/circuits.js/src/contract/artifact_hash.ts index cbfa334609c..a69151ee988 100644 --- a/yarn-project/circuits.js/src/contract/artifact_hash.ts +++ b/yarn-project/circuits.js/src/contract/artifact_hash.ts @@ -36,24 +36,24 @@ const sha256Fr = reduceFn(sha256, Fr); * ``` * @param artifact - Artifact to calculate the hash for. */ -export function computeArtifactHash( +export async function computeArtifactHash( artifact: ContractArtifact | { privateFunctionRoot: Fr; unconstrainedFunctionRoot: Fr; metadataHash: Fr }, -): Fr { +): Promise { if ('privateFunctionRoot' in artifact && 'unconstrainedFunctionRoot' in artifact && 'metadataHash' in artifact) { const { privateFunctionRoot, unconstrainedFunctionRoot, metadataHash } = artifact; const preimage = [privateFunctionRoot, unconstrainedFunctionRoot, metadataHash].map(x => x.toBuffer()); return sha256Fr(Buffer.concat([numToUInt8(VERSION), ...preimage])); } - const preimage = computeArtifactHashPreimage(artifact); - const artifactHash = computeArtifactHash(computeArtifactHashPreimage(artifact)); + const preimage = await computeArtifactHashPreimage(artifact); + const artifactHash = computeArtifactHash(preimage); getLogger().trace('Computed artifact hash', { artifactHash, ...preimage }); return artifactHash; } -export function computeArtifactHashPreimage(artifact: ContractArtifact) { - const privateFunctionRoot = computeArtifactFunctionTreeRoot(artifact, FunctionType.PRIVATE); - const unconstrainedFunctionRoot = computeArtifactFunctionTreeRoot(artifact, FunctionType.UNCONSTRAINED); +export async function computeArtifactHashPreimage(artifact: ContractArtifact) { + const privateFunctionRoot = await computeArtifactFunctionTreeRoot(artifact, FunctionType.PRIVATE); + const unconstrainedFunctionRoot = await computeArtifactFunctionTreeRoot(artifact, FunctionType.UNCONSTRAINED); const metadataHash = computeArtifactMetadataHash(artifact); return { privateFunctionRoot, unconstrainedFunctionRoot, metadataHash }; } @@ -62,36 +62,41 @@ export function computeArtifactMetadataHash(artifact: ContractArtifact) { return sha256Fr(Buffer.from(JSON.stringify({ name: artifact.name, outputs: artifact.outputs }), 'utf-8')); } -export function computeArtifactFunctionTreeRoot(artifact: ContractArtifact, fnType: FunctionType) { - const root = computeArtifactFunctionTree(artifact, fnType)?.root; - return root ? Fr.fromBuffer(root) : Fr.ZERO; +export async function computeArtifactFunctionTreeRoot(artifact: ContractArtifact, fnType: FunctionType) { + const tree = await computeArtifactFunctionTree(artifact, fnType); + return tree?.root ? Fr.fromBuffer(tree.root) : Fr.ZERO; } -export function computeArtifactFunctionTree(artifact: ContractArtifact, fnType: FunctionType): MerkleTree | undefined { - const leaves = computeFunctionLeaves(artifact, fnType); +export async function computeArtifactFunctionTree( + artifact: ContractArtifact, + fnType: FunctionType, +): Promise { + const leaves = await computeFunctionLeaves(artifact, fnType); // TODO(@spalladino) Consider implementing a null-object for empty trees if (leaves.length === 0) { return undefined; } const height = Math.ceil(Math.log2(leaves.length)); - const calculator = new MerkleTreeCalculator(height, Buffer.alloc(32), getArtifactMerkleTreeHasher()); + const calculator = await MerkleTreeCalculator.create(height, Buffer.alloc(32), getArtifactMerkleTreeHasher()); return calculator.computeTree(leaves.map(x => x.toBuffer())); } -function computeFunctionLeaves(artifact: ContractArtifact, fnType: FunctionType) { - return artifact.functions - .filter(f => f.functionType === fnType) - .map(f => ({ ...f, selector: FunctionSelector.fromNameAndParameters(f.name, f.parameters) })) - .sort((a, b) => a.selector.value - b.selector.value) - .map(computeFunctionArtifactHash); +async function computeFunctionLeaves(artifact: ContractArtifact, fnType: FunctionType) { + const selectors = await Promise.all( + artifact.functions + .filter(f => f.functionType === fnType) + .map(async f => ({ ...f, selector: await FunctionSelector.fromNameAndParameters(f.name, f.parameters) })), + ); + selectors.sort((a, b) => a.selector.value - b.selector.value); + return await Promise.all(selectors.map(computeFunctionArtifactHash)); } -export function computeFunctionArtifactHash( +export async function computeFunctionArtifactHash( fn: | FunctionArtifact | (Pick & { functionMetadataHash: Fr; selector: FunctionSelector }), ) { - const selector = 'selector' in fn ? fn.selector : FunctionSelector.fromNameAndParameters(fn); + const selector = 'selector' in fn ? fn.selector : await FunctionSelector.fromNameAndParameters(fn); const bytecodeHash = sha256Fr(fn.bytecode).toBuffer(); const metadataHash = 'functionMetadataHash' in fn ? fn.functionMetadataHash : computeFunctionMetadataHash(fn); @@ -107,5 +112,5 @@ function getLogger() { } export function getArtifactMerkleTreeHasher() { - return (l: Buffer, r: Buffer) => sha256Fr(Buffer.concat([l, r])).toBuffer(); + return (l: Buffer, r: Buffer) => Promise.resolve(sha256Fr(Buffer.concat([l, r])).toBuffer()); } diff --git a/yarn-project/circuits.js/src/contract/contract_address.test.ts b/yarn-project/circuits.js/src/contract/contract_address.test.ts index d33427746d9..6eb036136df 100644 --- a/yarn-project/circuits.js/src/contract/contract_address.test.ts +++ b/yarn-project/circuits.js/src/contract/contract_address.test.ts @@ -12,26 +12,26 @@ import { describe('ContractAddress', () => { setupCustomSnapshotSerializers(expect); - it('computePartialAddress', () => { + it('computePartialAddress', async () => { const mockInstance = { contractClassId: new Fr(1), saltedInitializationHash: new Fr(2), }; - const result = computePartialAddress(mockInstance); + const result = await computePartialAddress(mockInstance); expect(result).toMatchSnapshot(); }); - it('computeSaltedInitializationHash', () => { + it('computeSaltedInitializationHash', async () => { const mockInstance = { initializationHash: new Fr(1), salt: new Fr(2), deployer: AztecAddress.fromField(new Fr(4)), }; - const result = computeSaltedInitializationHash(mockInstance); + const result = await computeSaltedInitializationHash(mockInstance); expect(result).toMatchSnapshot(); }); - it('computeInitializationHash', () => { + it('computeInitializationHash', async () => { const mockInitFn: FunctionAbi = { functionType: FunctionType.PRIVATE, isInitializer: false, @@ -43,12 +43,12 @@ describe('ContractAddress', () => { errorTypes: {}, }; const mockArgs: any[] = [true]; - const result = computeInitializationHash(mockInitFn, mockArgs); + const result = await computeInitializationHash(mockInitFn, mockArgs); expect(result).toMatchSnapshot(); }); - it('computeInitializationHash empty', () => { - const result = computeInitializationHash(undefined, []); + it('computeInitializationHash empty', async () => { + const result = await computeInitializationHash(undefined, []); expect(result).toEqual(Fr.ZERO); }); diff --git a/yarn-project/circuits.js/src/contract/contract_address.ts b/yarn-project/circuits.js/src/contract/contract_address.ts index bf931d52ba7..ebaeadb2d68 100644 --- a/yarn-project/circuits.js/src/contract/contract_address.ts +++ b/yarn-project/circuits.js/src/contract/contract_address.ts @@ -19,12 +19,12 @@ import { type ContractInstance } from './interfaces/contract_instance.js'; * ``` * @param instance - A contract instance for which to calculate the deployment address. */ -export function computeContractAddressFromInstance( +export async function computeContractAddressFromInstance( instance: | ContractInstance | ({ contractClassId: Fr; saltedInitializationHash: Fr } & Pick), ): Promise { - const partialAddress = computePartialAddress(instance); + const partialAddress = await computePartialAddress(instance); return computeAddress(instance.publicKeys, partialAddress); } @@ -32,15 +32,15 @@ export function computeContractAddressFromInstance( * Computes the partial address defined as the hash of the contract class id and salted initialization hash. * @param instance - Contract instance for which to calculate the partial address. */ -export function computePartialAddress( +export async function computePartialAddress( instance: | Pick | { contractClassId: Fr; saltedInitializationHash: Fr }, -): Fr { +): Promise { const saltedInitializationHash = 'saltedInitializationHash' in instance ? instance.saltedInitializationHash - : computeSaltedInitializationHash(instance); + : await computeSaltedInitializationHash(instance); return poseidon2HashWithSeparator( [instance.contractClassId, saltedInitializationHash], @@ -54,7 +54,7 @@ export function computePartialAddress( */ export function computeSaltedInitializationHash( instance: Pick, -): Fr { +): Promise { return poseidon2HashWithSeparator( [instance.salt, instance.initializationHash, instance.deployer], GeneratorIndex.PARTIAL_ADDRESS, @@ -67,11 +67,11 @@ export function computeSaltedInitializationHash( * @param args - Unencoded arguments, will be encoded as fields according to the constructor function abi. * @returns The hash, or zero if no initialization function is provided. */ -export function computeInitializationHash(initFn: FunctionAbi | undefined, args: any[]): Fr { +export async function computeInitializationHash(initFn: FunctionAbi | undefined, args: any[]): Promise { if (!initFn) { return Fr.ZERO; } - const selector = FunctionSelector.fromNameAndParameters(initFn.name, initFn.parameters); + const selector = await FunctionSelector.fromNameAndParameters(initFn.name, initFn.parameters); const flatArgs = encodeArguments(initFn, args); return computeInitializationHashFromEncodedArgs(selector, flatArgs); } @@ -82,7 +82,10 @@ export function computeInitializationHash(initFn: FunctionAbi | undefined, args: * @param args - Encoded arguments. * @returns The hash. */ -export function computeInitializationHashFromEncodedArgs(initFn: FunctionSelector, encodedArgs: Fr[]): Fr { - const argsHash = computeVarArgsHash(encodedArgs); +export async function computeInitializationHashFromEncodedArgs( + initFn: FunctionSelector, + encodedArgs: Fr[], +): Promise { + const argsHash = await computeVarArgsHash(encodedArgs); return poseidon2HashWithSeparator([initFn, argsHash], GeneratorIndex.CONSTRUCTOR); } diff --git a/yarn-project/circuits.js/src/contract/contract_class.test.ts b/yarn-project/circuits.js/src/contract/contract_class.test.ts index 096db939be4..c64b940f9a1 100644 --- a/yarn-project/circuits.js/src/contract/contract_class.test.ts +++ b/yarn-project/circuits.js/src/contract/contract_class.test.ts @@ -6,9 +6,9 @@ import { getBenchmarkContractArtifact } from '../tests/fixtures.js'; import { getContractClassFromArtifact } from './contract_class.js'; describe('ContractClass', () => { - it('creates a contract class from a contract compilation artifact', () => { + it('creates a contract class from a contract compilation artifact', async () => { const artifact = getBenchmarkContractArtifact(); - const contractClass = getContractClassFromArtifact({ + const contractClass = await getContractClassFromArtifact({ ...artifact, artifactHash: Fr.fromHexString('0x1234'), }); @@ -21,9 +21,11 @@ describe('ContractClass', () => { // Check function selectors match const publicFunctionSelectors = [FunctionSelector.fromField(new Fr(PUBLIC_DISPATCH_SELECTOR))]; - const privateFunctionSelectors = artifact.functions - .filter(fn => fn.functionType === FunctionType.PRIVATE) - .map(fn => FunctionSelector.fromNameAndParameters(fn)); + const privateFunctions = artifact.functions.filter(fn => fn.functionType === FunctionType.PRIVATE); + + const privateFunctionSelectors = await Promise.all( + privateFunctions.map(fn => FunctionSelector.fromNameAndParameters(fn)), + ); expect(new Set(contractClass.publicFunctions.map(fn => fn.selector))).toEqual(new Set(publicFunctionSelectors)); expect(new Set(contractClass.privateFunctions.map(fn => fn.selector))).toEqual(new Set(privateFunctionSelectors)); diff --git a/yarn-project/circuits.js/src/contract/contract_class.ts b/yarn-project/circuits.js/src/contract/contract_class.ts index 78c49020d92..7e973dde13d 100644 --- a/yarn-project/circuits.js/src/contract/contract_class.ts +++ b/yarn-project/circuits.js/src/contract/contract_class.ts @@ -15,17 +15,19 @@ const cmpFunctionArtifacts = (a: T, b: a.selector.toField().cmp(b.selector.toField()); /** Creates a ContractClass from a contract compilation artifact. */ -export function getContractClassFromArtifact( +export async function getContractClassFromArtifact( artifact: ContractArtifact | ContractArtifactWithHash, -): ContractClassWithId & ContractClassIdPreimage { - const artifactHash = 'artifactHash' in artifact ? artifact.artifactHash : computeArtifactHash(artifact); - const artifactPublicFunctions: ContractClass['publicFunctions'] = artifact.functions - .filter(f => f.functionType === FunctionType.PUBLIC) - .map(f => ({ - selector: FunctionSelector.fromNameAndParameters(f.name, f.parameters), +): Promise { + const artifactHash = 'artifactHash' in artifact ? artifact.artifactHash : await computeArtifactHash(artifact); + const publicFunctions = artifact.functions.filter(f => f.functionType === FunctionType.PUBLIC); + const artifactPublicFunctions: ContractClass['publicFunctions'] = await Promise.all( + publicFunctions.map(async f => ({ + selector: await FunctionSelector.fromNameAndParameters(f.name, f.parameters), bytecode: f.bytecode, - })) - .sort(cmpFunctionArtifacts); + })), + ); + + artifactPublicFunctions.sort(cmpFunctionArtifacts); let packedBytecode = Buffer.alloc(0); let dispatchFunction: PublicFunction | undefined = undefined; @@ -41,10 +43,12 @@ export function getContractClassFromArtifact( packedBytecode = dispatchFunction.bytecode; } - const privateFunctions: ContractClass['privateFunctions'] = artifact.functions - .filter(f => f.functionType === FunctionType.PRIVATE) - .map(getContractClassPrivateFunctionFromArtifact) - .sort(cmpFunctionArtifacts); + const privateFunctions = artifact.functions.filter(f => f.functionType === FunctionType.PRIVATE); + const privateArtifactFunctions: ContractClass['privateFunctions'] = await Promise.all( + privateFunctions.map(getContractClassPrivateFunctionFromArtifact), + ); + + privateArtifactFunctions.sort(cmpFunctionArtifacts); const contractClass: ContractClass = { version: 1, @@ -52,26 +56,26 @@ export function getContractClassFromArtifact( // TODO(https://github.com/AztecProtocol/aztec-packages/issues/8985): Remove public functions. publicFunctions: dispatchFunction ? [dispatchFunction] : [], packedBytecode, - privateFunctions, + privateFunctions: privateArtifactFunctions, }; - return { ...contractClass, ...computeContractClassIdWithPreimage(contractClass) }; + return { ...contractClass, ...(await computeContractClassIdWithPreimage(contractClass)) }; } -export function getContractClassPrivateFunctionFromArtifact( +export async function getContractClassPrivateFunctionFromArtifact( f: FunctionArtifact, -): ContractClass['privateFunctions'][number] { +): Promise { return { - selector: FunctionSelector.fromNameAndParameters(f.name, f.parameters), - vkHash: computeVerificationKeyHash(f), + selector: await FunctionSelector.fromNameAndParameters(f.name, f.parameters), + vkHash: await computeVerificationKeyHash(f), }; } /** * For a given private function, computes the hash of its vk. */ -export function computeVerificationKeyHash(f: FunctionArtifact) { +export async function computeVerificationKeyHash(f: FunctionArtifact) { if (!f.verificationKey) { throw new Error(`Private function ${f.name} must have a verification key`); } - return hashVK(vkAsFieldsMegaHonk(Buffer.from(f.verificationKey, 'base64'))); + return hashVK(await vkAsFieldsMegaHonk(Buffer.from(f.verificationKey, 'base64'))); } diff --git a/yarn-project/circuits.js/src/contract/contract_class_id.test.ts b/yarn-project/circuits.js/src/contract/contract_class_id.test.ts index 2d1296854ca..1c38a0ab817 100644 --- a/yarn-project/circuits.js/src/contract/contract_class_id.test.ts +++ b/yarn-project/circuits.js/src/contract/contract_class_id.test.ts @@ -5,7 +5,7 @@ import { type ContractClass } from './interfaces/contract_class.js'; describe('ContractClass', () => { describe('getContractClassId', () => { - it('calculates the contract class id', () => { + it('calculates the contract class id', async () => { const contractClass: ContractClass = { version: 1, artifactHash: Fr.fromHexString('0x1234'), @@ -23,8 +23,9 @@ describe('ContractClass', () => { }, ], }; + const contractClassId = await computeContractClassId(contractClass); - expect(computeContractClassId(contractClass).toString()).toMatchInlineSnapshot( + expect(contractClassId.toString()).toMatchInlineSnapshot( `"0x2c3a8b2ad29dd4000cb827e973737bcf57fc072aeaf93ceeef4b4b9eb086cf67"`, ); }); diff --git a/yarn-project/circuits.js/src/contract/contract_class_id.ts b/yarn-project/circuits.js/src/contract/contract_class_id.ts index 181b9a70dca..01134756414 100644 --- a/yarn-project/circuits.js/src/contract/contract_class_id.ts +++ b/yarn-project/circuits.js/src/contract/contract_class_id.ts @@ -21,24 +21,24 @@ import { computePrivateFunctionsRoot } from './private_function.js'; * @param contractClass - Contract class. * @returns The identifier. */ -export function computeContractClassId(contractClass: ContractClass | ContractClassIdPreimage): Fr { - return computeContractClassIdWithPreimage(contractClass).id; +export async function computeContractClassId(contractClass: ContractClass | ContractClassIdPreimage): Promise { + return (await computeContractClassIdWithPreimage(contractClass)).id; } /** Computes a contract class id and returns it along with its preimage. */ -export function computeContractClassIdWithPreimage( +export async function computeContractClassIdWithPreimage( contractClass: ContractClass | ContractClassIdPreimage, -): ContractClassIdPreimage & { id: Fr } { +): Promise { const artifactHash = contractClass.artifactHash; const privateFunctionsRoot = 'privateFunctionsRoot' in contractClass ? contractClass.privateFunctionsRoot - : computePrivateFunctionsRoot(contractClass.privateFunctions); + : await computePrivateFunctionsRoot(contractClass.privateFunctions); const publicBytecodeCommitment = 'publicBytecodeCommitment' in contractClass ? contractClass.publicBytecodeCommitment - : computePublicBytecodeCommitment(contractClass.packedBytecode); - const id = poseidon2HashWithSeparator( + : await computePublicBytecodeCommitment(contractClass.packedBytecode); + const id = await poseidon2HashWithSeparator( [artifactHash, privateFunctionsRoot, publicBytecodeCommitment], GeneratorIndex.CONTRACT_LEAF, // TODO(@spalladino): Review all generator indices in this file ); @@ -46,9 +46,9 @@ export function computeContractClassIdWithPreimage( } /** Returns the preimage of a contract class id given a contract class. */ -export function computeContractClassIdPreimage(contractClass: ContractClass): ContractClassIdPreimage { - const privateFunctionsRoot = computePrivateFunctionsRoot(contractClass.privateFunctions); - const publicBytecodeCommitment = computePublicBytecodeCommitment(contractClass.packedBytecode); +export async function computeContractClassIdPreimage(contractClass: ContractClass): Promise { + const privateFunctionsRoot = await computePrivateFunctionsRoot(contractClass.privateFunctions); + const publicBytecodeCommitment = await computePublicBytecodeCommitment(contractClass.packedBytecode); return { artifactHash: contractClass.artifactHash, privateFunctionsRoot, publicBytecodeCommitment }; } @@ -59,12 +59,12 @@ export type ContractClassIdPreimage = { publicBytecodeCommitment: Fr; }; -export function computePublicBytecodeCommitment(packedBytecode: Buffer) { +export async function computePublicBytecodeCommitment(packedBytecode: Buffer) { // Encode the buffer into field elements (chunked into 32 bytes each) const encodedBytecode: Fr[] = bufferAsFields(packedBytecode, MAX_PACKED_PUBLIC_BYTECODE_SIZE_IN_FIELDS); // The first element is the length of the bytecode (in bytes) const bytecodeLength = Math.ceil(encodedBytecode[0].toNumber() / (Fr.SIZE_IN_BYTES - 1)); assert(bytecodeLength < MAX_PACKED_PUBLIC_BYTECODE_SIZE_IN_FIELDS, 'Bytecode exceeds maximum deployable size'); - return bytecodeLength == 0 ? new Fr(0) : poseidon2HashAccumulate(encodedBytecode.slice(0, bytecodeLength + 1)); + return bytecodeLength == 0 ? new Fr(0) : await poseidon2HashAccumulate(encodedBytecode.slice(0, bytecodeLength + 1)); } diff --git a/yarn-project/circuits.js/src/contract/contract_instance.ts b/yarn-project/circuits.js/src/contract/contract_instance.ts index 96cf400315a..a64e7e12fa6 100644 --- a/yarn-project/circuits.js/src/contract/contract_instance.ts +++ b/yarn-project/circuits.js/src/contract/contract_instance.ts @@ -113,15 +113,15 @@ export async function getContractInstanceFromDeployParams( const salt = opts.salt ?? Fr.random(); const constructorArtifact = getConstructorArtifact(artifact, opts.constructorArtifact); const deployer = opts.deployer ?? AztecAddress.ZERO; - const contractClass = getContractClassFromArtifact(artifact); - const contractClassId = computeContractClassId(contractClass); + const contractClass = await getContractClassFromArtifact(artifact); + const contractClassId = await computeContractClassId(contractClass); const initializationHash = constructorArtifact && opts?.skipArgsDecoding - ? computeInitializationHashFromEncodedArgs( - FunctionSelector.fromNameAndParameters(constructorArtifact?.name, constructorArtifact?.parameters), + ? await computeInitializationHashFromEncodedArgs( + await FunctionSelector.fromNameAndParameters(constructorArtifact?.name, constructorArtifact?.parameters), args, ) - : computeInitializationHash(constructorArtifact, args); + : await computeInitializationHash(constructorArtifact, args); const publicKeys = opts.publicKeys ?? PublicKeys.default(); const instance: ContractInstance = { diff --git a/yarn-project/circuits.js/src/contract/private_function.test.ts b/yarn-project/circuits.js/src/contract/private_function.test.ts index e3c72db9bfc..7eab1eb4d4e 100644 --- a/yarn-project/circuits.js/src/contract/private_function.test.ts +++ b/yarn-project/circuits.js/src/contract/private_function.test.ts @@ -12,25 +12,25 @@ describe('PrivateFunction', () => { { selector: makeSelector(3), vkHash: fr(4) }, ]; - it('computes merkle tree', () => { - const tree = computePrivateFunctionsTree(privateFunctions); + it('computes merkle tree', async () => { + const tree = await computePrivateFunctionsTree(privateFunctions); expect(tree.nodes.map(node => node.toString())).toMatchSnapshot(); }); - it('computes merkle tree root', () => { - const root = computePrivateFunctionsRoot(privateFunctions); + it('computes merkle tree root', async () => { + const root = await computePrivateFunctionsRoot(privateFunctions); expect(root.toString()).toMatchSnapshot(); }); - it('tree and root methods agree', () => { - const tree = computePrivateFunctionsTree(privateFunctions); - const root = computePrivateFunctionsRoot(privateFunctions); + it('tree and root methods agree', async () => { + const tree = await computePrivateFunctionsTree(privateFunctions); + const root = await computePrivateFunctionsRoot(privateFunctions); expect(Fr.fromBuffer(tree.root).equals(root)).toBe(true); }); - it('sorts functions before computing tree', () => { - const root = computePrivateFunctionsRoot(privateFunctions); - const rootReversed = computePrivateFunctionsRoot([...privateFunctions].reverse()); + it('sorts functions before computing tree', async () => { + const root = await computePrivateFunctionsRoot(privateFunctions); + const rootReversed = await computePrivateFunctionsRoot([...privateFunctions].reverse()); expect(root.equals(rootReversed)).toBe(true); }); }); diff --git a/yarn-project/circuits.js/src/contract/private_function.ts b/yarn-project/circuits.js/src/contract/private_function.ts index 16fa61a36b9..046ce0a3f19 100644 --- a/yarn-project/circuits.js/src/contract/private_function.ts +++ b/yarn-project/circuits.js/src/contract/private_function.ts @@ -11,32 +11,36 @@ let privateFunctionTreeCalculator: MerkleTreeCalculator | undefined; const PRIVATE_FUNCTION_SIZE = 2; /** Returns a Merkle tree for the set of private functions in a contract. */ -export function computePrivateFunctionsTree(fns: PrivateFunction[]): MerkleTree { - return getPrivateFunctionTreeCalculator().computeTree(computePrivateFunctionLeaves(fns)); +export async function computePrivateFunctionsTree(fns: PrivateFunction[]): Promise { + const calculator = await getPrivateFunctionTreeCalculator(); + const leaves = await computePrivateFunctionLeaves(fns); + return calculator.computeTree(leaves); } /** Returns the Merkle tree root for the set of private functions in a contract. */ -export function computePrivateFunctionsRoot(fns: PrivateFunction[]): Fr { - return Fr.fromBuffer(getPrivateFunctionTreeCalculator().computeTreeRoot(computePrivateFunctionLeaves(fns))); +export async function computePrivateFunctionsRoot(fns: PrivateFunction[]): Promise { + const calculator = await getPrivateFunctionTreeCalculator(); + const leaves = await computePrivateFunctionLeaves(fns); + return Fr.fromBuffer(await calculator.computeTreeRoot(leaves)); } -function computePrivateFunctionLeaves(fns: PrivateFunction[]): Buffer[] { +function computePrivateFunctionLeaves(fns: PrivateFunction[]): Promise { const leaves = [...fns].sort((a, b) => a.selector.value - b.selector.value); - return leaves.map(computePrivateFunctionLeaf); + return Promise.all(leaves.map(computePrivateFunctionLeaf)); } /** Returns the leaf for a given private function. */ -export function computePrivateFunctionLeaf(fn: PrivateFunction): Buffer { - return poseidon2HashWithSeparator([fn.selector, fn.vkHash], GeneratorIndex.FUNCTION_LEAF).toBuffer(); +export async function computePrivateFunctionLeaf(fn: PrivateFunction): Promise { + return (await poseidon2HashWithSeparator([fn.selector, fn.vkHash], GeneratorIndex.FUNCTION_LEAF)).toBuffer(); } -function getPrivateFunctionTreeCalculator(): MerkleTreeCalculator { +async function getPrivateFunctionTreeCalculator(): Promise { if (!privateFunctionTreeCalculator) { - const functionTreeZeroLeaf = pedersenHash(new Array(PRIVATE_FUNCTION_SIZE).fill(0)).toBuffer(); - privateFunctionTreeCalculator = new MerkleTreeCalculator( + const functionTreeZeroLeaf = (await pedersenHash(new Array(PRIVATE_FUNCTION_SIZE).fill(0))).toBuffer(); + privateFunctionTreeCalculator = await MerkleTreeCalculator.create( FUNCTION_TREE_HEIGHT, functionTreeZeroLeaf, - (left, right) => poseidon2Hash([left, right]).toBuffer(), + async (left, right) => (await poseidon2Hash([left, right])).toBuffer(), ); } return privateFunctionTreeCalculator; diff --git a/yarn-project/circuits.js/src/contract/private_function_membership_proof.test.ts b/yarn-project/circuits.js/src/contract/private_function_membership_proof.test.ts index 3a57e9787b2..07336a9f309 100644 --- a/yarn-project/circuits.js/src/contract/private_function_membership_proof.test.ts +++ b/yarn-project/circuits.js/src/contract/private_function_membership_proof.test.ts @@ -17,18 +17,18 @@ describe('private_function_membership_proof', () => { let vkHash: Fr; let selector: FunctionSelector; - beforeAll(() => { + beforeAll(async () => { artifact = getBenchmarkContractArtifact(); - contractClass = getContractClassFromArtifact(artifact); + contractClass = await getContractClassFromArtifact(artifact); privateFunction = artifact.functions.findLast(fn => fn.functionType === FunctionType.PRIVATE)!; - vkHash = computeVerificationKeyHash(privateFunction); - selector = FunctionSelector.fromNameAndParameters(privateFunction); + vkHash = await computeVerificationKeyHash(privateFunction); + selector = await FunctionSelector.fromNameAndParameters(privateFunction); }); - it('computes and verifies a proof', () => { - const proof = createPrivateFunctionMembershipProof(selector, artifact); + it('computes and verifies a proof', async () => { + const proof = await createPrivateFunctionMembershipProof(selector, artifact); const fn = { ...privateFunction, ...proof, selector, vkHash }; - expect(isValidPrivateFunctionMembershipProof(fn, contractClass)).toBeTruthy(); + await expect(isValidPrivateFunctionMembershipProof(fn, contractClass)).resolves.toBeTruthy(); }); test.each([ @@ -37,12 +37,12 @@ describe('private_function_membership_proof', () => { 'functionMetadataHash', 'unconstrainedFunctionsArtifactTreeRoot', 'privateFunctionTreeSiblingPath', - ] as const)('fails proof if %s is mangled', field => { - const proof = createPrivateFunctionMembershipProof(selector, artifact); + ] as const)('fails proof if %s is mangled', async field => { + const proof = await createPrivateFunctionMembershipProof(selector, artifact); const original = proof[field]; const mangled = Array.isArray(original) ? [Fr.random(), ...original.slice(1)] : Fr.random(); const wrong = { ...proof, [field]: mangled }; const fn = { ...privateFunction, ...wrong, selector, vkHash }; - expect(isValidPrivateFunctionMembershipProof(fn, contractClass)).toBeFalsy(); + await expect(isValidPrivateFunctionMembershipProof(fn, contractClass)).resolves.toBeFalsy(); }); }); diff --git a/yarn-project/circuits.js/src/contract/private_function_membership_proof.ts b/yarn-project/circuits.js/src/contract/private_function_membership_proof.ts index 9d1e28212ae..0c05011f4f7 100644 --- a/yarn-project/circuits.js/src/contract/private_function_membership_proof.ts +++ b/yarn-project/circuits.js/src/contract/private_function_membership_proof.ts @@ -1,4 +1,4 @@ -import { type ContractArtifact, type FunctionSelector, FunctionType } from '@aztec/foundation/abi'; +import { type ContractArtifact, FunctionSelector, FunctionType } from '@aztec/foundation/abi'; import { poseidon2Hash } from '@aztec/foundation/crypto'; import { Fr } from '@aztec/foundation/fields'; import { createLogger } from '@aztec/foundation/log'; @@ -25,37 +25,46 @@ import { computePrivateFunctionLeaf, computePrivateFunctionsTree } from './priva * @param selector - Selector of the function to create the proof for. * @param artifact - Artifact of the contract class where the function is defined. */ -export function createPrivateFunctionMembershipProof( +export async function createPrivateFunctionMembershipProof( selector: FunctionSelector, artifact: ContractArtifact, -): PrivateFunctionMembershipProof { +): Promise { const log = createLogger('circuits:function_membership_proof'); // Locate private function definition and artifact - const privateFunctions = artifact.functions - .filter(fn => fn.functionType === FunctionType.PRIVATE) - .map(getContractClassPrivateFunctionFromArtifact); - const privateFunction = privateFunctions.find(fn => fn.selector.equals(selector)); - const privateFunctionArtifact = artifact.functions.find(fn => selector.equals(fn)); + const privateFunctions = artifact.functions.filter(fn => fn.functionType === FunctionType.PRIVATE); + const privateFunctionsFromArtifact = await Promise.all( + privateFunctions.map(getContractClassPrivateFunctionFromArtifact), + ); + const privateFunction = privateFunctionsFromArtifact.find(fn => fn.selector.equals(selector)); + const privateFunctionsAndSelectors = await Promise.all( + privateFunctions.map(async fn => ({ + fn, + selector: await FunctionSelector.fromNameAndParameters(fn.name, fn.parameters), + })), + ); + const privateFunctionArtifact = privateFunctionsAndSelectors.find(fnAndSelector => + selector.equals(fnAndSelector.selector), + )?.fn; if (!privateFunction || !privateFunctionArtifact) { throw new Error(`Private function with selector ${selector.toString()} not found`); } // Compute preimage for the artifact hash const { unconstrainedFunctionRoot: unconstrainedFunctionsArtifactTreeRoot, metadataHash: artifactMetadataHash } = - computeArtifactHashPreimage(artifact); + await computeArtifactHashPreimage(artifact); // We need two sibling paths because private function information is split across two trees: // The "private function tree" captures the selectors and verification keys, and is used in the kernel circuit for verifying the proof generated by the app circuit. - const functionLeaf = computePrivateFunctionLeaf(privateFunction); - const functionsTree = computePrivateFunctionsTree(privateFunctions); + const functionLeaf = await computePrivateFunctionLeaf(privateFunction); + const functionsTree = await computePrivateFunctionsTree(privateFunctionsFromArtifact); const functionsTreeLeafIndex = functionsTree.getIndex(functionLeaf); const functionsTreeSiblingPath = functionsTree.getSiblingPath(functionsTreeLeafIndex).map(Fr.fromBuffer); // And the "artifact tree" captures function bytecode and metadata, and is used by the pxe to check that its executing the code it's supposed to be executing, but it never goes into circuits. const functionMetadataHash = computeFunctionMetadataHash(privateFunctionArtifact); - const functionArtifactHash = computeFunctionArtifactHash({ ...privateFunctionArtifact, functionMetadataHash }); - const artifactTree = computeArtifactFunctionTree(artifact, FunctionType.PRIVATE)!; + const functionArtifactHash = await computeFunctionArtifactHash({ ...privateFunctionArtifact, functionMetadataHash }); + const artifactTree = (await computeArtifactFunctionTree(artifact, FunctionType.PRIVATE))!; const artifactTreeLeafIndex = artifactTree.getIndex(functionArtifactHash.toBuffer()); const artifactTreeSiblingPath = artifactTree.getSiblingPath(artifactTreeLeafIndex).map(Fr.fromBuffer); @@ -103,22 +112,21 @@ export function createPrivateFunctionMembershipProof( * @param fn - Function to check membership proof for. * @param contractClass - In which contract class the function is expected to be. */ -export function isValidPrivateFunctionMembershipProof( +export async function isValidPrivateFunctionMembershipProof( fn: ExecutablePrivateFunctionWithMembershipProof, contractClass: Pick, ) { const log = createLogger('circuits:function_membership_proof'); // Check private function tree membership - const functionLeaf = computePrivateFunctionLeaf(fn); - const computedPrivateFunctionTreeRoot = Fr.fromBuffer( - computeRootFromSiblingPath( - functionLeaf, - fn.privateFunctionTreeSiblingPath.map(fr => fr.toBuffer()), - fn.privateFunctionTreeLeafIndex, - (left, right) => poseidon2Hash([left, right]).toBuffer(), - ), + const functionLeaf = await computePrivateFunctionLeaf(fn); + const rootBuffer = await computeRootFromSiblingPath( + functionLeaf, + fn.privateFunctionTreeSiblingPath.map(fr => fr.toBuffer()), + fn.privateFunctionTreeLeafIndex, + async (left, right) => (await poseidon2Hash([left, right])).toBuffer(), ); + const computedPrivateFunctionTreeRoot = Fr.fromBuffer(rootBuffer); if (!contractClass.privateFunctionsRoot.equals(computedPrivateFunctionTreeRoot)) { log.debug(`Private function tree root mismatch`, { expectedPrivateFunctionTreeRoot: contractClass.privateFunctionsRoot, @@ -129,16 +137,15 @@ export function isValidPrivateFunctionMembershipProof( } // Check artifact hash - const functionArtifactHash = computeFunctionArtifactHash(fn); - const computedArtifactPrivateFunctionTreeRoot = Fr.fromBuffer( - computeRootFromSiblingPath( - functionArtifactHash.toBuffer(), - fn.artifactTreeSiblingPath.map(fr => fr.toBuffer()), - fn.artifactTreeLeafIndex, - getArtifactMerkleTreeHasher(), - ), + const functionArtifactHash = await computeFunctionArtifactHash(fn); + const computedArtifactPrivateFunctionTreeRootBuffer = await computeRootFromSiblingPath( + functionArtifactHash.toBuffer(), + fn.artifactTreeSiblingPath.map(fr => fr.toBuffer()), + fn.artifactTreeLeafIndex, + getArtifactMerkleTreeHasher(), ); - const computedArtifactHash = computeArtifactHash({ + const computedArtifactPrivateFunctionTreeRoot = Fr.fromBuffer(computedArtifactPrivateFunctionTreeRootBuffer); + const computedArtifactHash = await computeArtifactHash({ privateFunctionRoot: computedArtifactPrivateFunctionTreeRoot, unconstrainedFunctionRoot: fn.unconstrainedFunctionsArtifactTreeRoot, metadataHash: fn.artifactMetadataHash, diff --git a/yarn-project/circuits.js/src/contract/unconstrained_function_membership_proof.test.ts b/yarn-project/circuits.js/src/contract/unconstrained_function_membership_proof.test.ts index f795c6f29b6..ccf19ec5e8f 100644 --- a/yarn-project/circuits.js/src/contract/unconstrained_function_membership_proof.test.ts +++ b/yarn-project/circuits.js/src/contract/unconstrained_function_membership_proof.test.ts @@ -17,48 +17,48 @@ describe('unconstrained_function_membership_proof', () => { let vkHash: Fr; let selector: FunctionSelector; - beforeEach(() => { + beforeEach(async () => { artifact = getTestContractArtifact(); - contractClass = getContractClassFromArtifact(artifact); + contractClass = await getContractClassFromArtifact(artifact); unconstrainedFunction = artifact.functions.findLast(fn => fn.functionType === FunctionType.UNCONSTRAINED)!; - selector = FunctionSelector.fromNameAndParameters(unconstrainedFunction); + selector = await FunctionSelector.fromNameAndParameters(unconstrainedFunction); }); const isUnconstrained = (fn: { functionType: FunctionType }) => fn.functionType === FunctionType.UNCONSTRAINED; - it('computes and verifies a proof', () => { + it('computes and verifies a proof', async () => { expect(unconstrainedFunction).toBeDefined(); - const proof = createUnconstrainedFunctionMembershipProof(selector, artifact); + const proof = await createUnconstrainedFunctionMembershipProof(selector, artifact); const fn = { ...unconstrainedFunction, ...proof, selector }; - expect(isValidUnconstrainedFunctionMembershipProof(fn, contractClass)).toBeTruthy(); + await expect(isValidUnconstrainedFunctionMembershipProof(fn, contractClass)).resolves.toBeTruthy(); }); - it('handles a contract with a single function', () => { + it('handles a contract with a single function', async () => { // Remove all unconstrained functions from the contract but one const unconstrainedFns = artifact.functions.filter(isUnconstrained); artifact.functions = artifact.functions.filter(fn => !isUnconstrained(fn) || fn === unconstrainedFns[0]); expect(artifact.functions.filter(isUnconstrained).length).toBe(1); const unconstrainedFunction = unconstrainedFns[0]; - const selector = FunctionSelector.fromNameAndParameters(unconstrainedFunction); + const selector = await FunctionSelector.fromNameAndParameters(unconstrainedFunction); - const proof = createUnconstrainedFunctionMembershipProof(selector, artifact); + const proof = await createUnconstrainedFunctionMembershipProof(selector, artifact); expect(proof.artifactTreeSiblingPath.length).toBe(0); const fn = { ...unconstrainedFunction, ...proof, selector }; - const contractClass = getContractClassFromArtifact(artifact); - expect(isValidUnconstrainedFunctionMembershipProof(fn, contractClass)).toBeTruthy(); + const contractClass = await getContractClassFromArtifact(artifact); + await expect(isValidUnconstrainedFunctionMembershipProof(fn, contractClass)).resolves.toBeTruthy(); }); test.each(['artifactTreeSiblingPath', 'artifactMetadataHash', 'functionMetadataHash'] as const)( 'fails proof if %s is mangled', - field => { - const proof = createUnconstrainedFunctionMembershipProof(selector, artifact); + async field => { + const proof = await createUnconstrainedFunctionMembershipProof(selector, artifact); const original = proof[field]; const mangled = Array.isArray(original) ? [Fr.random(), ...original.slice(1)] : Fr.random(); const wrong = { ...proof, [field]: mangled }; const fn = { ...unconstrainedFunction, ...wrong, selector, vkHash }; - expect(isValidUnconstrainedFunctionMembershipProof(fn, contractClass)).toBeFalsy(); + await expect(isValidUnconstrainedFunctionMembershipProof(fn, contractClass)).resolves.toBeFalsy(); }, ); }); diff --git a/yarn-project/circuits.js/src/contract/unconstrained_function_membership_proof.ts b/yarn-project/circuits.js/src/contract/unconstrained_function_membership_proof.ts index fb36b03110e..5957bce625d 100644 --- a/yarn-project/circuits.js/src/contract/unconstrained_function_membership_proof.ts +++ b/yarn-project/circuits.js/src/contract/unconstrained_function_membership_proof.ts @@ -1,4 +1,4 @@ -import { type ContractArtifact, type FunctionSelector, FunctionType } from '@aztec/foundation/abi'; +import { type ContractArtifact, FunctionSelector, FunctionType } from '@aztec/foundation/abi'; import { Fr } from '@aztec/foundation/fields'; import { createLogger } from '@aztec/foundation/log'; @@ -22,28 +22,29 @@ import { * @param selector - Selector of the function to create the proof for. * @param artifact - Artifact of the contract class where the function is defined. */ -export function createUnconstrainedFunctionMembershipProof( +export async function createUnconstrainedFunctionMembershipProof( selector: FunctionSelector, artifact: ContractArtifact, -): UnconstrainedFunctionMembershipProof { +): Promise { const log = createLogger('circuits:function_membership_proof'); // Locate function artifact - const fn = artifact.functions.find(fn => selector.equals(fn)); + const uncontrainedFunctions = artifact.functions.filter(fn => fn.functionType === FunctionType.UNCONSTRAINED); + const unconstrainedFunctionsAndSelectors = await Promise.all( + uncontrainedFunctions.map(async fn => ({ fn, selector: await FunctionSelector.fromNameAndParameters(fn) })), + ); + const fn = unconstrainedFunctionsAndSelectors.find(fnAndSelector => selector.equals(fnAndSelector.selector))?.fn; if (!fn) { - throw new Error(`Function with selector ${selector.toString()} not found`); - } else if (fn.functionType !== FunctionType.UNCONSTRAINED) { - throw new Error(`Function ${fn.name} with selector ${selector.toString()} is not unconstrained`); + throw new Error(`Unconstrained function with selector ${selector.toString()} not found`); } - // Compute preimage for the artifact hash const { privateFunctionRoot: privateFunctionsArtifactTreeRoot, metadataHash: artifactMetadataHash } = - computeArtifactHashPreimage(artifact); + await computeArtifactHashPreimage(artifact); // Compute the sibling path for the "artifact tree" const functionMetadataHash = computeFunctionMetadataHash(fn); - const functionArtifactHash = computeFunctionArtifactHash({ ...fn, functionMetadataHash }); - const artifactTree = computeArtifactFunctionTree(artifact, FunctionType.UNCONSTRAINED)!; + const functionArtifactHash = await computeFunctionArtifactHash({ ...fn, functionMetadataHash }); + const artifactTree = (await computeArtifactFunctionTree(artifact, FunctionType.UNCONSTRAINED))!; const artifactTreeLeafIndex = artifactTree.getIndex(functionArtifactHash.toBuffer()); const artifactTreeSiblingPath = artifactTree.getSiblingPath(artifactTreeLeafIndex).map(Fr.fromBuffer); @@ -81,22 +82,21 @@ export function createUnconstrainedFunctionMembershipProof( * @param fn - Function to check membership proof for. * @param contractClass - In which contract class the function is expected to be. */ -export function isValidUnconstrainedFunctionMembershipProof( +export async function isValidUnconstrainedFunctionMembershipProof( fn: UnconstrainedFunctionWithMembershipProof, contractClass: Pick, ) { const log = createLogger('circuits:function_membership_proof'); - const functionArtifactHash = computeFunctionArtifactHash(fn); - const computedArtifactFunctionTreeRoot = Fr.fromBuffer( - computeRootFromSiblingPath( - functionArtifactHash.toBuffer(), - fn.artifactTreeSiblingPath.map(fr => fr.toBuffer()), - fn.artifactTreeLeafIndex, - getArtifactMerkleTreeHasher(), - ), + const functionArtifactHash = await computeFunctionArtifactHash(fn); + const computedArtifactFunctionTreeRootBuffer = await computeRootFromSiblingPath( + functionArtifactHash.toBuffer(), + fn.artifactTreeSiblingPath.map(fr => fr.toBuffer()), + fn.artifactTreeLeafIndex, + getArtifactMerkleTreeHasher(), ); - const computedArtifactHash = computeArtifactHash({ + const computedArtifactFunctionTreeRoot = Fr.fromBuffer(computedArtifactFunctionTreeRootBuffer); + const computedArtifactHash = await computeArtifactHash({ privateFunctionRoot: fn.privateFunctionsArtifactTreeRoot, unconstrainedFunctionRoot: computedArtifactFunctionTreeRoot, metadataHash: fn.artifactMetadataHash, diff --git a/yarn-project/circuits.js/src/hash/hash.test.ts b/yarn-project/circuits.js/src/hash/hash.test.ts index 39fa210a6f3..a0b4352c820 100644 --- a/yarn-project/circuits.js/src/hash/hash.test.ts +++ b/yarn-project/circuits.js/src/hash/hash.test.ts @@ -19,31 +19,31 @@ import { describe('hash', () => { setupCustomSnapshotSerializers(expect); - it('computes note hash nonce', () => { + it('computes note hash nonce', async () => { const nullifierZero = new Fr(123n); const noteHashIndex = 456; - const res = computeNoteHashNonce(nullifierZero, noteHashIndex); + const res = await computeNoteHashNonce(nullifierZero, noteHashIndex); expect(res).toMatchSnapshot(); }); - it('computes unique note hash', () => { + it('computes unique note hash', async () => { const nonce = new Fr(123n); const noteHash = new Fr(456); - const res = computeUniqueNoteHash(nonce, noteHash); + const res = await computeUniqueNoteHash(nonce, noteHash); expect(res).toMatchSnapshot(); }); - it('computes siloed note hash', () => { + it('computes siloed note hash', async () => { const contractAddress = new AztecAddress(new Fr(123n).toBuffer()); const uniqueNoteHash = new Fr(456); - const res = siloNoteHash(contractAddress, uniqueNoteHash); + const res = await siloNoteHash(contractAddress, uniqueNoteHash); expect(res).toMatchSnapshot(); }); - it('computes siloed nullifier', () => { + it('computes siloed nullifier', async () => { const contractAddress = new AztecAddress(new Fr(123n).toBuffer()); const innerNullifier = new Fr(456); - const res = siloNullifier(contractAddress, innerNullifier); + const res = await siloNullifier(contractAddress, innerNullifier); expect(res).toMatchSnapshot(); }); @@ -53,39 +53,39 @@ describe('hash', () => { expect(res).toMatchSnapshot(); }); - it('computes public data tree leaf slot', () => { + it('computes public data tree leaf slot', async () => { const contractAddress = makeAztecAddress(); const value = new Fr(3n); - const res = computePublicDataTreeLeafSlot(contractAddress, value); + const res = await computePublicDataTreeLeafSlot(contractAddress, value); expect(res).toMatchSnapshot(); }); - it('hashes empty function args', () => { - const res = computeVarArgsHash([]); + it('hashes empty function args', async () => { + const res = await computeVarArgsHash([]); expect(res).toMatchSnapshot(); }); - it('hashes function args', () => { + it('hashes function args', async () => { const args = times(8, i => new Fr(i)); - const res = computeVarArgsHash(args); + const res = await computeVarArgsHash(args); expect(res).toMatchSnapshot(); }); - it('hashes many function args', () => { + it('hashes many function args', async () => { const args = times(200, i => new Fr(i)); - const res = computeVarArgsHash(args); + const res = await computeVarArgsHash(args); expect(res).toMatchSnapshot(); }); - it('compute secret message hash', () => { + it('compute secret message hash', async () => { const value = new Fr(8n); - const hash = computeSecretHash(value); + const hash = await computeSecretHash(value); expect(hash).toMatchSnapshot(); }); - it('Var args hash matches noir', () => { + it('Var args hash matches noir', async () => { const args = times(100, i => new Fr(i)); - const res = computeVarArgsHash(args); + const res = await computeVarArgsHash(args); expect(res).toMatchSnapshot(); // Value used in "compute_var_args_hash" test in hash.nr diff --git a/yarn-project/circuits.js/src/hash/hash.ts b/yarn-project/circuits.js/src/hash/hash.ts index 79fd9d4d98e..4489e00f87a 100644 --- a/yarn-project/circuits.js/src/hash/hash.ts +++ b/yarn-project/circuits.js/src/hash/hash.ts @@ -10,7 +10,7 @@ import { type ScopedL2ToL1Message } from '../structs/l2_to_l1_message.js'; * @param vkBuf - The verification key as fields. * @returns The hash of the verification key. */ -export function hashVK(keyAsFields: Fr[]): Fr { +export function hashVK(keyAsFields: Fr[]): Promise { return poseidon2Hash(keyAsFields); } @@ -20,7 +20,7 @@ export function hashVK(keyAsFields: Fr[]): Fr { * @param noteHashIndex - The index of the note hash. * @returns A note hash nonce. */ -export function computeNoteHashNonce(nullifierZero: Fr, noteHashIndex: number): Fr { +export function computeNoteHashNonce(nullifierZero: Fr, noteHashIndex: number): Promise { return poseidon2HashWithSeparator([nullifierZero, noteHashIndex], GeneratorIndex.NOTE_HASH_NONCE); } @@ -31,7 +31,7 @@ export function computeNoteHashNonce(nullifierZero: Fr, noteHashIndex: number): * @param uniqueNoteHash - The unique note hash to silo. * @returns A siloed note hash. */ -export function siloNoteHash(contract: AztecAddress, uniqueNoteHash: Fr): Fr { +export function siloNoteHash(contract: AztecAddress, uniqueNoteHash: Fr): Promise { return poseidon2HashWithSeparator([contract, uniqueNoteHash], GeneratorIndex.SILOED_NOTE_HASH); } @@ -42,7 +42,7 @@ export function siloNoteHash(contract: AztecAddress, uniqueNoteHash: Fr): Fr { * @param siloedNoteHash - A note hash. * @returns A unique note hash. */ -export function computeUniqueNoteHash(nonce: Fr, siloedNoteHash: Fr): Fr { +export function computeUniqueNoteHash(nonce: Fr, siloedNoteHash: Fr): Promise { return poseidon2HashWithSeparator([nonce, siloedNoteHash], GeneratorIndex.UNIQUE_NOTE_HASH); } @@ -53,7 +53,7 @@ export function computeUniqueNoteHash(nonce: Fr, siloedNoteHash: Fr): Fr { * @param innerNullifier - The nullifier to silo. * @returns A siloed nullifier. */ -export function siloNullifier(contract: AztecAddress, innerNullifier: Fr): Fr { +export function siloNullifier(contract: AztecAddress, innerNullifier: Fr): Promise { return poseidon2HashWithSeparator([contract, innerNullifier], GeneratorIndex.OUTER_NULLIFIER); } @@ -74,7 +74,7 @@ export function computePublicDataTreeValue(value: Fr): Fr { * @returns Public data tree index computed from contract address and storage slot. */ -export function computePublicDataTreeLeafSlot(contractAddress: AztecAddress, storageSlot: Fr): Fr { +export function computePublicDataTreeLeafSlot(contractAddress: AztecAddress, storageSlot: Fr): Promise { return poseidon2HashWithSeparator([contractAddress, storageSlot], GeneratorIndex.PUBLIC_LEAF_INDEX); } @@ -83,9 +83,9 @@ export function computePublicDataTreeLeafSlot(contractAddress: AztecAddress, sto * @param args - Arguments to hash. * @returns Pedersen hash of the arguments. */ -export function computeVarArgsHash(args: Fr[]) { +export function computeVarArgsHash(args: Fr[]): Promise { if (args.length === 0) { - return Fr.ZERO; + return Promise.resolve(Fr.ZERO); } return poseidon2HashWithSeparator(args, GeneratorIndex.FUNCTION_ARGS); @@ -97,12 +97,15 @@ export function computeVarArgsHash(args: Fr[]) { * @param secret - The secret to hash (could be generated however you want e.g. `Fr.random()`) * @returns The hash */ -export function computeSecretHash(secret: Fr) { +export function computeSecretHash(secret: Fr): Promise { return poseidon2HashWithSeparator([secret], GeneratorIndex.SECRET_HASH); } -export function computeL1ToL2MessageNullifier(contract: AztecAddress, messageHash: Fr, secret: Fr) { - const innerMessageNullifier = poseidon2HashWithSeparator([messageHash, secret], GeneratorIndex.MESSAGE_NULLIFIER); +export async function computeL1ToL2MessageNullifier(contract: AztecAddress, messageHash: Fr, secret: Fr) { + const innerMessageNullifier = await poseidon2HashWithSeparator( + [messageHash, secret], + GeneratorIndex.MESSAGE_NULLIFIER, + ); return siloNullifier(contract, innerMessageNullifier); } diff --git a/yarn-project/circuits.js/src/hash/map_slot.test.ts b/yarn-project/circuits.js/src/hash/map_slot.test.ts index b120c496ea5..a0974bef74a 100644 --- a/yarn-project/circuits.js/src/hash/map_slot.test.ts +++ b/yarn-project/circuits.js/src/hash/map_slot.test.ts @@ -5,11 +5,11 @@ import { updateInlineTestData } from '@aztec/foundation/testing/files'; import { deriveStorageSlotInMap } from './index.js'; describe('Map slot', () => { - it('derived map slot matches Noir', () => { + it('derived map slot matches Noir', async () => { const mapSlot = new Fr(0x132258fb6962c4387ba659d9556521102d227549a386d39f0b22d1890d59c2b5n); const key = AztecAddress.fromString('0x302dbc2f9b50a73283d5fb2f35bc01eae8935615817a0b4219a057b2ba8a5a3f'); - const slot = deriveStorageSlotInMap(mapSlot, key); + const slot = await deriveStorageSlotInMap(mapSlot, key); expect(slot.toString()).toMatchInlineSnapshot( `"0x15b9fe39449affd8b377461263e9d2b610b9ad40580553500b4e41d9cbd887ac"`, diff --git a/yarn-project/circuits.js/src/hash/map_slot.ts b/yarn-project/circuits.js/src/hash/map_slot.ts index 8ffed794039..60a9cacc62d 100644 --- a/yarn-project/circuits.js/src/hash/map_slot.ts +++ b/yarn-project/circuits.js/src/hash/map_slot.ts @@ -13,6 +13,6 @@ export function deriveStorageSlotInMap( /** Convert key to a field. */ toField: () => Fr; }, -): Fr { +): Promise { return poseidon2Hash([mapSlot, key.toField()]); } diff --git a/yarn-project/circuits.js/src/hints/build_nullifier_read_request_hints.ts b/yarn-project/circuits.js/src/hints/build_nullifier_read_request_hints.ts index 751eac3d9b7..8110d72df7c 100644 --- a/yarn-project/circuits.js/src/hints/build_nullifier_read_request_hints.ts +++ b/yarn-project/circuits.js/src/hints/build_nullifier_read_request_hints.ts @@ -96,7 +96,9 @@ export async function buildNullifierReadRequestHintsFromResetStates( +export async function buildSiloedNullifierReadRequestHints( oracle: { getNullifierMembershipWitness(nullifier: Fr): Promise; }, @@ -142,13 +144,13 @@ export function buildSiloedNullifierReadRequestHints - new ReadRequest(siloNullifier(r.contractAddress, r.value), r.counter).scope(AztecAddress.ZERO), + const nonEmptyNullifierReadRequests = getNonEmptyItems(nullifierReadRequests); + const readRequests = await Promise.all( + nonEmptyNullifierReadRequests.map(async r => + new ReadRequest(await siloNullifier(r.contractAddress, r.value), r.counter).scope(AztecAddress.ZERO), ), - ScopedReadRequest.empty(), - MAX_NULLIFIER_READ_REQUESTS_PER_TX, ); + const siloedReadRequests = padArrayEnd(readRequests, ScopedReadRequest.empty(), MAX_NULLIFIER_READ_REQUESTS_PER_TX); const scopedNullifiers = nullifiers.map(n => new Nullifier(n.value, n.counter, n.noteHash).scope(AztecAddress.ZERO), diff --git a/yarn-project/circuits.js/src/keys/derivation.test.ts b/yarn-project/circuits.js/src/keys/derivation.test.ts index dab2a79891b..6e2ee87332a 100644 --- a/yarn-project/circuits.js/src/keys/derivation.test.ts +++ b/yarn-project/circuits.js/src/keys/derivation.test.ts @@ -5,21 +5,20 @@ import { PublicKeys } from '../types/public_keys.js'; import { computeAddress, computePreaddress } from './derivation.js'; describe('🔑', () => { - it('computing public keys hash matches Noir', () => { + it('computing public keys hash matches Noir', async () => { const masterNullifierPublicKey = new Point(new Fr(1), new Fr(2), false); const masterIncomingViewingPublicKey = new Point(new Fr(3), new Fr(4), false); const masterOutgoingViewingPublicKey = new Point(new Fr(5), new Fr(6), false); const masterTaggingPublicKey = new Point(new Fr(7), new Fr(8), false); const expected = Fr.fromHexString('0x0fecd9a32db731fec1fded1b9ff957a1625c069245a3613a2538bd527068b0ad'); - expect( - new PublicKeys( - masterNullifierPublicKey, - masterIncomingViewingPublicKey, - masterOutgoingViewingPublicKey, - masterTaggingPublicKey, - ).hash(), - ).toEqual(expected); + const publicKeysHash = await new PublicKeys( + masterNullifierPublicKey, + masterIncomingViewingPublicKey, + masterOutgoingViewingPublicKey, + masterTaggingPublicKey, + ).hash(); + expect(publicKeysHash).toEqual(expected); // Run with AZTEC_GENERATE_TEST_DATA=1 to update noir test data updateInlineTestData( @@ -29,11 +28,11 @@ describe('🔑', () => { ); }); - it('Pre address from partial matches Noir', () => { + it('Pre address from partial matches Noir', async () => { const publicKeysHash = new Fr(1n); const partialAddress = new Fr(2n); - const address = computePreaddress(publicKeysHash, partialAddress).toString(); - expect(address).toMatchSnapshot(); + const address = await computePreaddress(publicKeysHash, partialAddress); + expect(address.toString()).toMatchSnapshot(); // Run with AZTEC_GENERATE_TEST_DATA=1 to update noir test data updateInlineTestData( diff --git a/yarn-project/circuits.js/src/keys/derivation.ts b/yarn-project/circuits.js/src/keys/derivation.ts index 681a028c5ee..1fd89f3b68f 100644 --- a/yarn-project/circuits.js/src/keys/derivation.ts +++ b/yarn-project/circuits.js/src/keys/derivation.ts @@ -9,17 +9,17 @@ import { PublicKeys } from '../types/public_keys.js'; import { type KeyPrefix } from './key_types.js'; import { getKeyGenerator } from './utils.js'; -export function computeAppNullifierSecretKey(masterNullifierSecretKey: GrumpkinScalar, app: AztecAddress): Fr { +export function computeAppNullifierSecretKey(masterNullifierSecretKey: GrumpkinScalar, app: AztecAddress): Promise { return computeAppSecretKey(masterNullifierSecretKey, app, 'n'); // 'n' is the key prefix for nullifier secret key } -export function computeAppSecretKey(skM: GrumpkinScalar, app: AztecAddress, keyPrefix: KeyPrefix): Fr { +export function computeAppSecretKey(skM: GrumpkinScalar, app: AztecAddress, keyPrefix: KeyPrefix): Promise { const generator = getKeyGenerator(keyPrefix); return poseidon2HashWithSeparator([skM.hi, skM.lo, app], generator); } -export function computeOvskApp(ovsk: GrumpkinScalar, app: AztecAddress) { - const ovskAppFr = computeAppSecretKey(ovsk, app, 'ov'); // 'ov' is the key prefix for outgoing viewing key +export async function computeOvskApp(ovsk: GrumpkinScalar, app: AztecAddress): Promise { + const ovskAppFr = await computeAppSecretKey(ovsk, app, 'ov'); // 'ov' is the key prefix for outgoing viewing key // Here we are intentionally converting Fr (output of poseidon) to Fq. This is fine even though a distribution of // P = s * G will not be uniform because 2 * (q - r) / q is small. return GrumpkinScalar.fromBuffer(ovskAppFr.toBuffer()); @@ -51,7 +51,7 @@ export async function computeAddress(publicKeys: PublicKeys, partialAddress: Fr) // 1. preaddress = poseidon2([publicKeysHash, partialAddress], GeneratorIndex.CONTRACT_ADDRESS_V1); // 2. addressPoint = (preaddress * G) + ivpk_m // 3. address = addressPoint.x - const preaddress = computePreaddress(publicKeys.hash(), partialAddress); + const preaddress = await computePreaddress(await publicKeys.hash(), partialAddress); const address = await new Grumpkin().add( await derivePublicKeyFromSecretKey(new Fq(preaddress.toBigInt())), publicKeys.masterIncomingViewingPublicKey, @@ -129,7 +129,7 @@ export async function computeTaggingSecretPoint( ivsk: Fq, externalAddress: AztecAddress, ) { - const knownPreaddress = computePreaddress(knownAddress.publicKeys.hash(), knownAddress.partialAddress); + const knownPreaddress = await computePreaddress(await knownAddress.publicKeys.hash(), knownAddress.partialAddress); // TODO: #8970 - Computation of address point from x coordinate might fail const externalAddressPoint = await externalAddress.toAddressPoint(); const curve = new Grumpkin(); diff --git a/yarn-project/circuits.js/src/merkle/merkle_tree_calculator.test.ts b/yarn-project/circuits.js/src/merkle/merkle_tree_calculator.test.ts index 1fc122b4f07..d8ffb839c0c 100644 --- a/yarn-project/circuits.js/src/merkle/merkle_tree_calculator.test.ts +++ b/yarn-project/circuits.js/src/merkle/merkle_tree_calculator.test.ts @@ -3,44 +3,44 @@ import { Fr } from '@aztec/foundation/fields'; import { MerkleTreeCalculator } from './merkle_tree_calculator.js'; describe('merkle tree root calculator', () => { - it('should correctly handle no leaves', () => { + it('should correctly handle no leaves', async () => { // Height of 3 is 8 leaves. - const calculator = new MerkleTreeCalculator(4); - const expected = calculator.computeTreeRoot(new Array(8).fill(new Fr(0)).map(fr => fr.toBuffer())); - expect(calculator.computeTreeRoot()).toEqual(expected); + const calculator = await MerkleTreeCalculator.create(4); + const expected = await calculator.computeTreeRoot(new Array(8).fill(new Fr(0)).map(fr => fr.toBuffer())); + await expect(calculator.computeTreeRoot()).resolves.toEqual(expected); }); - it('should correctly leverage zero hashes', () => { - const calculator = new MerkleTreeCalculator(4); + it('should correctly leverage zero hashes', async () => { + const calculator = await MerkleTreeCalculator.create(4); const leaves = Array.from({ length: 5 }).map((_, i) => new Fr(i).toBuffer()); const padded = [...leaves, ...new Array(3).fill(Buffer.alloc(32))]; - const expected = calculator.computeTreeRoot(padded); - const result = calculator.computeTreeRoot(leaves); + const expected = await calculator.computeTreeRoot(padded); + const result = await calculator.computeTreeRoot(leaves); expect(result).not.toBeUndefined(); expect(result).toEqual(expected); }); - it('should correctly handle non default zero leaf', () => { + it('should correctly handle non default zero leaf', async () => { const zeroLeaf = new Fr(666).toBuffer(); - const calculator = new MerkleTreeCalculator(4, zeroLeaf); + const calculator = await MerkleTreeCalculator.create(4, zeroLeaf); const leaves = Array.from({ length: 5 }).map((_, i) => new Fr(i).toBuffer()); const padded = [...leaves, ...new Array(3).fill(zeroLeaf)]; - const expected = calculator.computeTreeRoot(padded); - expect(calculator.computeTreeRoot(leaves)).toEqual(expected); + const expected = await calculator.computeTreeRoot(padded); + await expect(calculator.computeTreeRoot(leaves)).resolves.toEqual(expected); }); - it('should compute entire tree', () => { - const calculator = new MerkleTreeCalculator(4); + it('should compute entire tree', async () => { + const calculator = await MerkleTreeCalculator.create(4); const leaves = Array.from({ length: 5 }).map((_, i) => new Fr(i).toBuffer()); - const expectedRoot = calculator.computeTreeRoot(leaves); - const result = calculator.computeTree(leaves); + const expectedRoot = await calculator.computeTreeRoot(leaves); + const result = await calculator.computeTree(leaves); expect(result.nodes.length).toEqual(31); expect(result.root).toEqual(expectedRoot); }); - it('should correctly handle empty leaf array', () => { - const calculator = new MerkleTreeCalculator(4); - const expected = calculator.computeTree(); - expect(expected.root).toEqual(calculator.computeTreeRoot()); + it('should correctly handle empty leaf array', async () => { + const calculator = await MerkleTreeCalculator.create(4); + const expected = await calculator.computeTree(); + expect(expected.root).toEqual(await calculator.computeTreeRoot()); }); }); diff --git a/yarn-project/circuits.js/src/merkle/merkle_tree_calculator.ts b/yarn-project/circuits.js/src/merkle/merkle_tree_calculator.ts index 9244bb7ecb2..2613989879d 100644 --- a/yarn-project/circuits.js/src/merkle/merkle_tree_calculator.ts +++ b/yarn-project/circuits.js/src/merkle/merkle_tree_calculator.ts @@ -6,22 +6,27 @@ import { MerkleTree } from './merkle_tree.js'; * Merkle tree calculator. */ export class MerkleTreeCalculator { - private zeroHashes: Buffer[]; - private hasher: (left: Buffer, right: Buffer) => Buffer; - - constructor( + private constructor( private height: number, - zeroLeaf = Buffer.alloc(32), - hasher = (left: Buffer, right: Buffer) => pedersenHash([left, right]).toBuffer(), + private zeroHashes: Buffer[], + private hasher: (left: Buffer, right: Buffer) => Promise, ) { this.hasher = hasher; - this.zeroHashes = Array.from({ length: height }).reduce( - (acc: Buffer[], _, i) => [...acc, this.hasher(acc[i], acc[i])], - [zeroLeaf], - ); } - computeTree(leaves: Buffer[] = []): MerkleTree { + static async create( + height: number, + zeroLeaf = Buffer.alloc(32), + hasher = async (left: Buffer, right: Buffer) => (await pedersenHash([left, right])).toBuffer(), + ) { + const zeroHashes = [zeroLeaf]; + for (let i = 0; i < height; i++) { + zeroHashes.push(await hasher(zeroHashes[i], zeroHashes[i])); + } + return new MerkleTreeCalculator(height, zeroHashes, hasher); + } + + async computeTree(leaves: Buffer[] = []): Promise { if (leaves.length === 0) { leaves = new Array(2 ** this.height).fill(this.zeroHashes[0]); } @@ -34,7 +39,7 @@ export class MerkleTreeCalculator { for (let j = 0; j < leaves.length / 2; ++j) { const l = leaves[j * 2]; const r = leaves[j * 2 + 1] || this.zeroHashes[i]; - newLeaves[j] = this.hasher(l, r); + newLeaves[j] = await this.hasher(l, r); } result = result.concat(new Array(numLeaves - leaves.length).fill(this.zeroHashes[i]), newLeaves); leaves = newLeaves; @@ -43,7 +48,7 @@ export class MerkleTreeCalculator { return new MerkleTree(this.height, result); } - computeTreeRoot(leaves: Buffer[] = []): Buffer { + async computeTreeRoot(leaves: Buffer[] = []): Promise { if (leaves.length === 0) { return this.zeroHashes[this.zeroHashes.length - 1]; } @@ -55,7 +60,7 @@ export class MerkleTreeCalculator { for (; j < leaves.length / 2; ++j) { const l = leaves[j * 2]; const r = leaves[j * 2 + 1] || this.zeroHashes[i]; - leaves[j] = this.hasher(l, r); + leaves[j] = await this.hasher(l, r); } leaves = leaves.slice(0, j); } diff --git a/yarn-project/circuits.js/src/merkle/sibling_path.test.ts b/yarn-project/circuits.js/src/merkle/sibling_path.test.ts index 7b2852570d0..af574650c82 100644 --- a/yarn-project/circuits.js/src/merkle/sibling_path.test.ts +++ b/yarn-project/circuits.js/src/merkle/sibling_path.test.ts @@ -7,15 +7,15 @@ import { computeRootFromSiblingPath } from './sibling_path.js'; describe('sibling path', () => { let tree: MerkleTree; - beforeAll(() => { - const calculator = new MerkleTreeCalculator(4); + beforeAll(async () => { + const calculator = await MerkleTreeCalculator.create(4); const leaves = Array.from({ length: 5 }).map((_, i) => new Fr(i).toBuffer()); - tree = calculator.computeTree(leaves); + tree = await calculator.computeTree(leaves); }); - test.each([0, 1, 2, 3, 4, 5, 6, 7])('recovers the root from a leaf at index %s and its sibling path', index => { + test.each([0, 1, 2, 3, 4, 5, 6, 7])('recovers the root from a leaf at index %s and its sibling path', async index => { const leaf = tree.leaves[index]; const siblingPath = tree.getSiblingPath(index); - expect(computeRootFromSiblingPath(leaf, siblingPath, index)).toEqual(tree.root); + expect(await computeRootFromSiblingPath(leaf, siblingPath, index)).toEqual(tree.root); }); }); diff --git a/yarn-project/circuits.js/src/merkle/sibling_path.ts b/yarn-project/circuits.js/src/merkle/sibling_path.ts index 52383dae87c..870bb4a966a 100644 --- a/yarn-project/circuits.js/src/merkle/sibling_path.ts +++ b/yarn-project/circuits.js/src/merkle/sibling_path.ts @@ -1,15 +1,15 @@ import { pedersenHash } from '@aztec/foundation/crypto'; /** Computes the expected root of a merkle tree given a leaf and its sibling path. */ -export function computeRootFromSiblingPath( +export async function computeRootFromSiblingPath( leaf: Buffer, siblingPath: Buffer[], index: number, - hasher = (left: Buffer, right: Buffer) => pedersenHash([left, right]).toBuffer(), + hasher = async (left: Buffer, right: Buffer) => (await pedersenHash([left, right])).toBuffer(), ) { let result = leaf; for (const sibling of siblingPath) { - result = index & 1 ? hasher(sibling, result) : hasher(result, sibling); + result = index & 1 ? await hasher(sibling, result) : await hasher(result, sibling); index >>= 1; } return result; diff --git a/yarn-project/circuits.js/src/structs/blobs/blob_public_inputs.test.ts b/yarn-project/circuits.js/src/structs/blobs/blob_public_inputs.test.ts index aa996cae5a9..d7893596b66 100644 --- a/yarn-project/circuits.js/src/structs/blobs/blob_public_inputs.test.ts +++ b/yarn-project/circuits.js/src/structs/blobs/blob_public_inputs.test.ts @@ -1,4 +1,5 @@ import { Blob } from '@aztec/foundation/blob'; +import { timesParallel } from '@aztec/foundation/collection'; import { randomInt } from '@aztec/foundation/crypto'; import { BLOBS_PER_BLOCK, BLOB_PUBLIC_INPUTS } from '../../constants.gen.js'; @@ -19,8 +20,8 @@ describe('BlobPublicInputs', () => { expect(res).toEqual(blobPI); }); - it('converts correctly from Blob class', () => { - const blob = Blob.fromFields(Array(400).fill(new Fr(3))); + it('converts correctly from Blob class', async () => { + const blob = await Blob.fromFields(Array(400).fill(new Fr(3))); const converted = BlobPublicInputs.fromBlob(blob); expect(converted.z).toEqual(blob.challengeZ); expect(Buffer.from(converted.y.toString(16), 'hex')).toEqual(blob.evaluationY); @@ -54,8 +55,8 @@ describe('BlockBlobPublicInputs', () => { expect(res).toEqual(blobPI); }); - it('converts correctly from Blob class', () => { - const blobs = Array.from({ length: BLOBS_PER_BLOCK }, (_, i) => Blob.fromFields(Array(400).fill(new Fr(i + 1)))); + it('converts correctly from Blob class', async () => { + const blobs = await timesParallel(BLOBS_PER_BLOCK, i => Blob.fromFields(Array(400).fill(new Fr(i + 1)))); const converted = BlockBlobPublicInputs.fromBlobs(blobs); converted.inner.forEach((blobPI, i) => { expect(blobPI.z).toEqual(blobs[i].challengeZ); diff --git a/yarn-project/circuits.js/src/structs/blobs/sponge_blob.test.ts b/yarn-project/circuits.js/src/structs/blobs/sponge_blob.test.ts index 573e0e7f7a3..55a7b59990c 100644 --- a/yarn-project/circuits.js/src/structs/blobs/sponge_blob.test.ts +++ b/yarn-project/circuits.js/src/structs/blobs/sponge_blob.test.ts @@ -35,21 +35,21 @@ describe('SpongeBlob', () => { expect(fields.length).toBe(SPONGE_BLOB_LENGTH); }); - it('matches an ordinary short poseidon2 hash', () => { + it('matches an ordinary short poseidon2 hash', async () => { spongeBlob = SpongeBlob.init(4); const input = [Fr.ONE, new Fr(2), new Fr(3), new Fr(4)]; - spongeBlob.absorb(input); - const expectedHash = poseidon2Hash(input); - const res = spongeBlob.squeeze(); + await spongeBlob.absorb(input); + const expectedHash = await poseidon2Hash(input); + const res = await spongeBlob.squeeze(); expect(res).toEqual(expectedHash); }); - it('matches an ordinary long poseidon2 hash', () => { + it('matches an ordinary long poseidon2 hash', async () => { spongeBlob = SpongeBlob.init(4096); const input = Array(4096).fill(new Fr(3)); - spongeBlob.absorb(input); - const expectedHash = poseidon2Hash(input); - const res = spongeBlob.squeeze(); + await spongeBlob.absorb(input); + const expectedHash = await poseidon2Hash(input); + const res = await spongeBlob.squeeze(); expect(res).toEqual(expectedHash); }); }); diff --git a/yarn-project/circuits.js/src/structs/blobs/sponge_blob.ts b/yarn-project/circuits.js/src/structs/blobs/sponge_blob.ts index c17bdee3915..7eb79c95bcc 100644 --- a/yarn-project/circuits.js/src/structs/blobs/sponge_blob.ts +++ b/yarn-project/circuits.js/src/structs/blobs/sponge_blob.ts @@ -53,21 +53,21 @@ export class SpongeBlob { return SpongeBlob.fromBuffer(this.toBuffer()); } - absorb(fields: Fr[]) { + async absorb(fields: Fr[]) { if (this.fields + fields.length > this.expectedFields) { throw new Error( `Attempted to fill spongeblob with ${this.fields + fields.length}, but it has a max of ${this.expectedFields}`, ); } - this.sponge.absorb(fields); + await this.sponge.absorb(fields); this.fields += fields.length; } - squeeze(): Fr { + async squeeze(): Promise { // If the blob sponge is not 'full', we append 1 to match Poseidon2::hash_internal() // NB: There is currently no use case in which we don't 'fill' a blob sponge, but adding for completeness if (this.fields != this.expectedFields) { - this.sponge.absorb([Fr.ONE]); + await this.sponge.absorb([Fr.ONE]); } return this.sponge.squeeze(); } @@ -141,37 +141,37 @@ export class Poseidon2Sponge { // Note: there isn't currently an impl in ts that allows for a custom aborption via an // existing sponge. // A custom blob-based impl of noir/noir-repo/noir_stdlib/src/hash/poseidon2.nr - performDuplex() { + async performDuplex() { for (let i = 0; i < this.cache.length; i++) { if (i < this.cacheSize) { this.state[i] = this.state[i].add(this.cache[i]); } } - const perm = poseidon2Permutation(this.state); + const perm = await poseidon2Permutation(this.state); // ts doesn't understand that the above always gives 4 this.state = [perm[0], perm[1], perm[2], perm[3]]; } - absorb(fields: Fr[]) { + async absorb(fields: Fr[]) { if (this.squeezeMode) { throw new Error(`Poseidon sponge is not able to absorb more inputs.`); } - fields.forEach(field => { + for (const field of fields) { if (this.cacheSize == this.cache.length) { - this.performDuplex(); + await this.performDuplex(); this.cache[0] = field; this.cacheSize = 1; } else { this.cache[this.cacheSize++] = field; } - }); + } } - squeeze(): Fr { + async squeeze(): Promise { if (this.squeezeMode) { throw new Error(`Poseidon sponge has already been squeezed.`); } - this.performDuplex(); + await this.performDuplex(); this.squeezeMode = true; return this.state[0]; } diff --git a/yarn-project/circuits.js/src/structs/block_header.test.ts b/yarn-project/circuits.js/src/structs/block_header.test.ts index 40c7e3eec96..ccffd90b1e4 100644 --- a/yarn-project/circuits.js/src/structs/block_header.test.ts +++ b/yarn-project/circuits.js/src/structs/block_header.test.ts @@ -26,10 +26,10 @@ describe('BlockHeader', () => { expect(res).toEqual(header); }); - it('computes hash', () => { + it('computes hash', async () => { const seed = 9870243; const header = makeHeader(seed, undefined); - const hash = header.hash(); + const hash = await header.hash(); expect(hash).toMatchSnapshot(); }); @@ -38,9 +38,9 @@ describe('BlockHeader', () => { expect(fields.length).toBe(BLOCK_HEADER_LENGTH); }); - it('computes empty hash', () => { + it('computes empty hash', async () => { const header = BlockHeader.empty(); - const hash = header.hash(); + const hash = await header.hash(); expect(hash).toMatchSnapshot(); // Run with AZTEC_GENERATE_TEST_DATA=1 to update noir test data diff --git a/yarn-project/circuits.js/src/structs/block_header.ts b/yarn-project/circuits.js/src/structs/block_header.ts index dcb57c0b04d..fa4f170ea84 100644 --- a/yarn-project/circuits.js/src/structs/block_header.ts +++ b/yarn-project/circuits.js/src/structs/block_header.ts @@ -148,7 +148,7 @@ export class BlockHeader { return BlockHeader.fromBuffer(hexToBuffer(str)); } - hash(): Fr { + hash(): Promise { return poseidon2HashWithSeparator(this.toFields(), GeneratorIndex.BLOCK_HASH); } diff --git a/yarn-project/circuits.js/src/structs/complete_address.test.ts b/yarn-project/circuits.js/src/structs/complete_address.test.ts index 481e52f4ae5..064ebe460a3 100644 --- a/yarn-project/circuits.js/src/structs/complete_address.test.ts +++ b/yarn-project/circuits.js/src/structs/complete_address.test.ts @@ -25,12 +25,12 @@ describe('CompleteAddress', () => { it('serializes / deserializes correctly', async () => { const expectedAddress = await CompleteAddress.random(); - const address = CompleteAddress.fromBuffer(expectedAddress.toBuffer()); + const address = await CompleteAddress.fromBuffer(expectedAddress.toBuffer()); expect(address.equals(expectedAddress)).toBe(true); }); it('instantiates from string and individual components', async () => { - const completeAddressFromString = CompleteAddress.fromString( + const completeAddressFromString = await CompleteAddress.fromString( '0x24e4646f58b9fbe7d38e317db8d5636c423fbbdfbe119fc190fe9c64747e0c6222f7fcddfa3ce3e8f0cc8e82d7b94cdd740afa3e77f8e4a63ea78a239432dcab0471657de2b6216ade6c506d28fbc22ba8b8ed95c871ad9f3e3984e90d9723a7111223493147f6785514b1c195bb37a2589f22a6596d30bb2bb145fdc9ca8f1e273bbffd678edce8fe30e0deafc4f66d58357c06fd4a820285294b9746c3be9509115c96e962322ffed6522f57194627136b8d03ac7469109707f5e44190c4840c49773308a13d740a7f0d4f0e6163b02c5a408b6f965856b6a491002d073d5b00d3d81beb009873eb7116327cf47c612d5758ef083d4fda78e9b63980b2a7622f567d22d2b02fe1f4ad42db9d58a36afd1983e7e2909d1cab61cafedad6193a0a7c585381b10f4666044266a02405bf6e01fa564c8517d4ad5823493abd31de', ); diff --git a/yarn-project/circuits.js/src/structs/complete_address.ts b/yarn-project/circuits.js/src/structs/complete_address.ts index 2c1e1ba23e8..e8ee455dd08 100644 --- a/yarn-project/circuits.js/src/structs/complete_address.ts +++ b/yarn-project/circuits.js/src/structs/complete_address.ts @@ -59,15 +59,15 @@ export class CompleteAddress { return new CompleteAddress(address, publicKeys, partialAddress); } - getPreaddress() { - return computePreaddress(this.publicKeys.hash(), this.partialAddress); + async getPreaddress() { + return computePreaddress(await this.publicKeys.hash(), this.partialAddress); } - static fromSecretKeyAndInstance( + static async fromSecretKeyAndInstance( secretKey: Fr, instance: Parameters[0], ): Promise { - const partialAddress = computePartialAddress(instance); + const partialAddress = await computePartialAddress(instance); return CompleteAddress.fromSecretKeyAndPartialAddress(secretKey, partialAddress); } @@ -123,12 +123,12 @@ export class CompleteAddress { * @param buffer - The input buffer or BufferReader containing the address data. * @returns - A new CompleteAddress instance with the extracted address data. */ - static fromBuffer(buffer: Buffer | BufferReader): CompleteAddress { + static fromBuffer(buffer: Buffer | BufferReader): Promise { const reader = BufferReader.asReader(buffer); const address = reader.readObject(AztecAddress); const publicKeys = reader.readObject(PublicKeys); const partialAddress = reader.readObject(Fr); - return new CompleteAddress(address, publicKeys, partialAddress); + return CompleteAddress.create(address, publicKeys, partialAddress); } /** @@ -139,7 +139,7 @@ export class CompleteAddress { * @param address - The hex-encoded string representing the complete address. * @returns A Point instance. */ - static fromString(address: string): CompleteAddress { + static fromString(address: string): Promise { return CompleteAddress.fromBuffer(Buffer.from(address.replace(/^0x/i, ''), 'hex')); } diff --git a/yarn-project/circuits.js/src/structs/function_data.ts b/yarn-project/circuits.js/src/structs/function_data.ts index afc63a96d21..eb813bbf44c 100644 --- a/yarn-project/circuits.js/src/structs/function_data.ts +++ b/yarn-project/circuits.js/src/structs/function_data.ts @@ -17,9 +17,9 @@ export class FunctionData { public isPrivate: boolean, ) {} - static fromAbi(abi: FunctionAbi | ContractFunctionDao): FunctionData { + static async fromAbi(abi: FunctionAbi | ContractFunctionDao): Promise { return new FunctionData( - FunctionSelector.fromNameAndParameters(abi.name, abi.parameters), + await FunctionSelector.fromNameAndParameters(abi.name, abi.parameters), abi.functionType === FunctionType.PRIVATE, ); } diff --git a/yarn-project/circuits.js/src/structs/indexed_tagging_secret.ts b/yarn-project/circuits.js/src/structs/indexed_tagging_secret.ts index 0fd04cda28c..57b7995c1f4 100644 --- a/yarn-project/circuits.js/src/structs/indexed_tagging_secret.ts +++ b/yarn-project/circuits.js/src/structs/indexed_tagging_secret.ts @@ -37,8 +37,8 @@ export class IndexedTaggingSecret { * @param app The app address * @returns The siloed tag. */ - computeSiloedTag(recipient: AztecAddress, app: AztecAddress) { - const tag = this.computeTag(recipient); + async computeSiloedTag(recipient: AztecAddress, app: AztecAddress) { + const tag = await this.computeTag(recipient); return poseidon2Hash([app, tag]); } } diff --git a/yarn-project/circuits.js/src/structs/public_data_update_request.ts b/yarn-project/circuits.js/src/structs/public_data_update_request.ts index ab1973fb081..1ef117bfbd8 100644 --- a/yarn-project/circuits.js/src/structs/public_data_update_request.ts +++ b/yarn-project/circuits.js/src/structs/public_data_update_request.ts @@ -79,8 +79,11 @@ export class PublicDataUpdateRequest { return new PublicDataUpdateRequest(Fr.fromBuffer(reader), Fr.fromBuffer(reader), reader.readNumber()); } - static fromContractStorageUpdateRequest(contractAddress: AztecAddress, updateRequest: ContractStorageUpdateRequest) { - const leafSlot = computePublicDataTreeLeafSlot(contractAddress, updateRequest.storageSlot); + static async fromContractStorageUpdateRequest( + contractAddress: AztecAddress, + updateRequest: ContractStorageUpdateRequest, + ) { + const leafSlot = await computePublicDataTreeLeafSlot(contractAddress, updateRequest.storageSlot); return new PublicDataUpdateRequest(leafSlot, updateRequest.newValue, updateRequest.counter); } diff --git a/yarn-project/circuits.js/src/structs/tx_request.test.ts b/yarn-project/circuits.js/src/structs/tx_request.test.ts index fec6fab23b7..a3040407b13 100644 --- a/yarn-project/circuits.js/src/structs/tx_request.test.ts +++ b/yarn-project/circuits.js/src/structs/tx_request.test.ts @@ -34,7 +34,7 @@ describe('TxRequest', () => { expect(fields.length).toBe(TX_REQUEST_LENGTH); }); - it('compute hash', () => { + it('compute hash', async () => { const gasSettings = new GasSettings(new Gas(2, 2), new Gas(1, 1), new GasFees(4, 4), new GasFees(3, 3)); const txRequest = TxRequest.from({ origin: AztecAddress.fromBigInt(1n), @@ -43,15 +43,15 @@ describe('TxRequest', () => { txContext: new TxContext(Fr.ZERO, Fr.ZERO, gasSettings), }); - const hash = txRequest.hash().toString(); + const hash = await txRequest.hash(); - expect(hash).toMatchSnapshot(); + expect(hash.toString()).toMatchSnapshot(); // Run with AZTEC_GENERATE_TEST_DATA=1 to update noir test data updateInlineTestData( 'noir-projects/noir-protocol-circuits/crates/types/src/transaction/tx_request.nr', 'test_data_tx_request_hash', - hash, + hash.toString(), ); }); }); diff --git a/yarn-project/circuits.js/src/tests/factories.ts b/yarn-project/circuits.js/src/tests/factories.ts index e4297628120..5e84a47a468 100644 --- a/yarn-project/circuits.js/src/tests/factories.ts +++ b/yarn-project/circuits.js/src/tests/factories.ts @@ -1256,15 +1256,18 @@ export function makeUnconstrainedFunctionWithMembershipProof(seed = 0): Unconstr }; } -export function makeContractClassPublic(seed = 0, publicDispatchFunction?: PublicFunction): ContractClassPublic { +export async function makeContractClassPublic( + seed = 0, + publicDispatchFunction?: PublicFunction, +): Promise { const artifactHash = fr(seed + 1); const publicFunctions = publicDispatchFunction ? [publicDispatchFunction] : makeTuple(1, makeContractClassPublicFunction, seed + 2); const privateFunctionsRoot = fr(seed + 3); const packedBytecode = publicDispatchFunction?.bytecode ?? makeBytes(100, seed + 4); - const publicBytecodeCommitment = computePublicBytecodeCommitment(packedBytecode); - const id = computeContractClassId({ artifactHash, privateFunctionsRoot, publicBytecodeCommitment }); + const publicBytecodeCommitment = await computePublicBytecodeCommitment(packedBytecode); + const id = await computeContractClassId({ artifactHash, privateFunctionsRoot, publicBytecodeCommitment }); return { id, artifactHash, @@ -1330,11 +1333,11 @@ export async function makeContractInstanceFromClassId(classId: Fr, seed = 0): Pr const deployer = new AztecAddress(new Fr(seed + 2)); const publicKeys = await PublicKeys.random(); - const saltedInitializationHash = poseidon2HashWithSeparator( + const saltedInitializationHash = await poseidon2HashWithSeparator( [salt, initializationHash, deployer], GeneratorIndex.PARTIAL_ADDRESS, ); - const partialAddress = poseidon2HashWithSeparator( + const partialAddress = await poseidon2HashWithSeparator( [classId, saltedInitializationHash], GeneratorIndex.PARTIAL_ADDRESS, ); @@ -1350,7 +1353,7 @@ export async function makeContractInstanceFromClassId(classId: Fr, seed = 0): Pr } export async function makeAvmBytecodeHints(seed = 0): Promise { - const { artifactHash, privateFunctionsRoot, packedBytecode, id } = makeContractClassPublic(seed); + const { artifactHash, privateFunctionsRoot, packedBytecode, id } = await makeContractClassPublic(seed); const instance = await makeContractInstanceFromClassId(id, seed + 0x1000); const avmHintInstance = new AvmContractInstanceHint( @@ -1364,7 +1367,7 @@ export async function makeAvmBytecodeHints(seed = 0): Promise { expect(pk).toEqual(deserializedWithoutPrefix); }); - it('computes public keys hash', () => { + it('computes public keys hash', async () => { const keys = new PublicKeys( new Point(new Fr(1n), new Fr(2n), false), new Point(new Fr(3n), new Fr(4n), false), @@ -25,28 +25,32 @@ describe('PublicKeys', () => { new Point(new Fr(7n), new Fr(8n), false), ); - const hash = keys.hash().toString(); - expect(hash).toMatchInlineSnapshot(`"0x0fecd9a32db731fec1fded1b9ff957a1625c069245a3613a2538bd527068b0ad"`); + const hash = await keys.hash(); + expect(hash.toString()).toMatchInlineSnapshot( + `"0x0fecd9a32db731fec1fded1b9ff957a1625c069245a3613a2538bd527068b0ad"`, + ); // Run with AZTEC_GENERATE_TEST_DATA=1 to update noir test data updateInlineTestData( 'noir-projects/noir-protocol-circuits/crates/types/src/public_keys.nr', 'expected_public_keys_hash', - hash, + hash.toString(), ); }); - it('computes default keys hash', () => { + it('computes default keys hash', async () => { const keys = PublicKeys.default(); - const hash = keys.hash().toString(); - expect(hash).toMatchInlineSnapshot(`"0x1d3bf1fb93ae0e9cda83b203dd91c3bfb492a9aecf30ec90e1057eced0f0e62d"`); + const hash = await keys.hash(); + expect(hash.toString()).toMatchInlineSnapshot( + `"0x1d3bf1fb93ae0e9cda83b203dd91c3bfb492a9aecf30ec90e1057eced0f0e62d"`, + ); // Run with AZTEC_GENERATE_TEST_DATA=1 to update noir test data updateInlineTestData( 'noir-projects/noir-protocol-circuits/crates/types/src/public_keys.nr', 'test_data_default_hash', - hash, + hash.toString(), ); }); }); diff --git a/yarn-project/cli-wallet/src/cmds/authorize_action.ts b/yarn-project/cli-wallet/src/cmds/authorize_action.ts index 88c552efd14..06abf2a8d56 100644 --- a/yarn-project/cli-wallet/src/cmds/authorize_action.ts +++ b/yarn-project/cli-wallet/src/cmds/authorize_action.ts @@ -27,7 +27,8 @@ export async function authorizeAction( const contract = await Contract.at(contractAddress, contractArtifact, wallet); const action = contract.methods[functionName](...functionArgs); - const witness = await wallet.setPublicAuthWit({ caller, action }, true).send().wait(); + const setAuthwitnessInteraction = await wallet.setPublicAuthWit({ caller, action }, true); + const witness = await setAuthwitnessInteraction.send().wait(); log(`Authorized action ${functionName} on contract ${contractAddress} for caller ${caller}`); diff --git a/yarn-project/cli/src/cmds/contracts/inspect_contract.ts b/yarn-project/cli/src/cmds/contracts/inspect_contract.ts index 0c79238ae00..c85fce5d294 100644 --- a/yarn-project/cli/src/cmds/contracts/inspect_contract.ts +++ b/yarn-project/cli/src/cmds/contracts/inspect_contract.ts @@ -16,7 +16,7 @@ export async function inspectContract(contractArtifactFile: string, debugLogger: if (contractFns.length === 0) { log(`No functions found for contract ${contractArtifact.name}`); } - const contractClass = getContractClassFromArtifact(contractArtifact); + const contractClass = await getContractClassFromArtifact(contractArtifact); const bytecodeLengthInFields = 1 + Math.ceil(contractClass.packedBytecode.length / 31); log(`Contract class details:`); @@ -35,14 +35,14 @@ export async function inspectContract(contractArtifactFile: string, debugLogger: const internalFunctions = contractFns.filter(f => f.isInternal); if (internalFunctions.length > 0) { log(`\nInternal functions:`); - internalFunctions.forEach(f => logFunction(f, log)); + await Promise.all(internalFunctions.map(f => logFunction(f, log))); } } -function logFunction(fn: FunctionArtifact, log: LogFn) { +async 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 selector = await FunctionSelector.fromSignature(signature); const bytecodeSize = fn.bytecode.length; const bytecodeHash = sha256(fn.bytecode).toString('hex'); log( diff --git a/yarn-project/cli/src/cmds/devnet/bootstrap_network.ts b/yarn-project/cli/src/cmds/devnet/bootstrap_network.ts index 99362f430bf..388e173c3a5 100644 --- a/yarn-project/cli/src/cmds/devnet/bootstrap_network.ts +++ b/yarn-project/cli/src/cmds/devnet/bootstrap_network.ts @@ -173,8 +173,8 @@ async function deployToken( .deployed(waitOpts); await new BatchCall(wallet, [ - devCoin.methods.set_minter(bridge.address, true).request(), - devCoin.methods.set_admin(bridge.address).request(), + await devCoin.methods.set_minter(bridge.address, true).request(), + await devCoin.methods.set_admin(bridge.address).request(), ]) .send() .wait(waitOpts); diff --git a/yarn-project/cli/src/cmds/misc/compute_selector.ts b/yarn-project/cli/src/cmds/misc/compute_selector.ts index 9d299a64eff..e14f9285bf7 100644 --- a/yarn-project/cli/src/cmds/misc/compute_selector.ts +++ b/yarn-project/cli/src/cmds/misc/compute_selector.ts @@ -1,7 +1,7 @@ import { FunctionSelector } from '@aztec/foundation/abi'; import { type LogFn } from '@aztec/foundation/log'; -export function computeSelector(functionSignature: string, log: LogFn) { - const selector = FunctionSelector.fromSignature(functionSignature); +export async function computeSelector(functionSignature: string, log: LogFn) { + const selector = await FunctionSelector.fromSignature(functionSignature); log(`${selector}`); } diff --git a/yarn-project/cli/src/cmds/misc/index.ts b/yarn-project/cli/src/cmds/misc/index.ts index b716dd3ad65..fe6caa16212 100644 --- a/yarn-project/cli/src/cmds/misc/index.ts +++ b/yarn-project/cli/src/cmds/misc/index.ts @@ -43,7 +43,7 @@ export function injectCommands(program: Command, log: LogFn) { .argument('', 'Function signature to compute selector for e.g. foo(Field)') .action(async (functionSignature: string) => { const { computeSelector } = await import('./compute_selector.js'); - computeSelector(functionSignature, log); + await computeSelector(functionSignature, log); }); program diff --git a/yarn-project/cli/src/cmds/pxe/add_contract.ts b/yarn-project/cli/src/cmds/pxe/add_contract.ts index 62ee9176a40..d43ba9c586d 100644 --- a/yarn-project/cli/src/cmds/pxe/add_contract.ts +++ b/yarn-project/cli/src/cmds/pxe/add_contract.ts @@ -22,7 +22,7 @@ export async function addContract( version: 1, salt, initializationHash, - contractClassId: getContractClassFromArtifact(artifact).id, + contractClassId: (await getContractClassFromArtifact(artifact)).id, publicKeys: publicKeys ?? PublicKeys.default(), address, deployer: deployer ?? AztecAddress.ZERO, diff --git a/yarn-project/cli/src/utils/aztec.ts b/yarn-project/cli/src/utils/aztec.ts index d409f009eca..3facdb91796 100644 --- a/yarn-project/cli/src/utils/aztec.ts +++ b/yarn-project/cli/src/utils/aztec.ts @@ -66,7 +66,7 @@ export async function deployAztecContracts( return await deployL1Contracts(chain.rpcUrl, account, chain.chainInfo, debugLogger, { l2FeeJuiceAddress: ProtocolContractAddress.FeeJuice, - vkTreeRoot: getVKTreeRoot(), + vkTreeRoot: await getVKTreeRoot(), protocolContractTreeRoot, salt, initialValidators, diff --git a/yarn-project/cli/src/utils/inspect.ts b/yarn-project/cli/src/utils/inspect.ts index 7ed8e2b00b6..41438823a52 100644 --- a/yarn-project/cli/src/utils/inspect.ts +++ b/yarn-project/cli/src/utils/inspect.ts @@ -12,7 +12,8 @@ export async function inspectBlock(pxe: PXE, blockNumber: number, log: LogFn, op return; } - log(`Block ${blockNumber} (${block.hash().toString()})`); + const blockHash = await block.hash(); + log(`Block ${blockNumber} (${blockHash.toString()})`); log(` Total fees: ${block.header.totalFees.toBigInt()}`); log(` Total mana used: ${block.header.totalManaUsed.toBigInt()}`); log( diff --git a/yarn-project/end-to-end/package.json b/yarn-project/end-to-end/package.json index 966518ac665..77a1d18380f 100644 --- a/yarn-project/end-to-end/package.json +++ b/yarn-project/end-to-end/package.json @@ -107,6 +107,7 @@ "@types/js-yaml": "^4.0.9", "@types/lodash.chunk": "^4.2.9", "concurrently": "^7.6.0", + "copy-webpack-plugin": "^12.0.2", "jest": "^29.5.0", "jest-extended": "^4.0.2", "js-yaml": "^4.1.0", diff --git a/yarn-project/end-to-end/src/bench/bench_build_block.test.ts b/yarn-project/end-to-end/src/bench/bench_build_block.test.ts index 2955d2956ee..a850c51463e 100644 --- a/yarn-project/end-to-end/src/bench/bench_build_block.test.ts +++ b/yarn-project/end-to-end/src/bench/bench_build_block.test.ts @@ -35,7 +35,7 @@ describe('benchmarks/build_block', () => { const TX_COUNT = 32; it(`builds a block with ${TX_COUNT} txs`, async () => { - sequencer.updateSequencerConfig({ minTxsPerBlock: TX_COUNT }); + await sequencer.updateSequencerConfig({ minTxsPerBlock: TX_COUNT }); const sentTxs = await sendTxs(TX_COUNT, context, contract); await waitTxs(sentTxs, context); }); diff --git a/yarn-project/end-to-end/src/bench/utils.ts b/yarn-project/end-to-end/src/bench/utils.ts index 8003534c240..a1685128045 100644 --- a/yarn-project/end-to-end/src/bench/utils.ts +++ b/yarn-project/end-to-end/src/bench/utils.ts @@ -1,6 +1,6 @@ import { type AztecNodeService } from '@aztec/aztec-node'; import { type AztecNode, BatchCall, INITIAL_L2_BLOCK_NUM, type SentTx, type WaitOpts } from '@aztec/aztec.js'; -import { mean, stdDev, times } from '@aztec/foundation/collection'; +import { mean, stdDev, timesParallel } from '@aztec/foundation/collection'; import { randomInt } from '@aztec/foundation/crypto'; import { BenchmarkingContract } from '@aztec/noir-contracts.js/Benchmarking'; import { type PXEService, type PXEServiceConfig, createPXEService } from '@aztec/pxe'; @@ -129,12 +129,12 @@ export function getFolderSize(path: string): number { * @param contract - Benchmarking contract. * @returns A BatchCall instance. */ -export function makeCall(index: number, context: EndToEndContext, contract: BenchmarkingContract) { +export async function makeCall(index: number, context: EndToEndContext, contract: BenchmarkingContract) { const owner = context.wallet.getAddress(); const sender = owner; return new BatchCall(context.wallet, [ - contract.methods.create_note(owner, sender, index + 1).request(), - contract.methods.increment_balance(owner, index + 1).request(), + await contract.methods.create_note(owner, sender, index + 1).request(), + await contract.methods.increment_balance(owner, index + 1).request(), ]); } @@ -151,7 +151,7 @@ export async function sendTxs( context: EndToEndContext, contract: BenchmarkingContract, ): Promise { - const calls = times(txCount, index => makeCall(index, context, contract)); + const calls = await timesParallel(txCount, index => makeCall(index, context, contract)); context.logger.info(`Creating ${txCount} txs`); const provenTxs = await Promise.all(calls.map(call => call.prove({ skipPublicSimulation: true }))); context.logger.info(`Sending ${txCount} txs`); diff --git a/yarn-project/end-to-end/src/composed/e2e_aztec_js_browser.test.ts b/yarn-project/end-to-end/src/composed/e2e_aztec_js_browser.test.ts index 03ff98223ca..16c0e635a35 100644 --- a/yarn-project/end-to-end/src/composed/e2e_aztec_js_browser.test.ts +++ b/yarn-project/end-to-end/src/composed/e2e_aztec_js_browser.test.ts @@ -46,6 +46,13 @@ const setupApp = async () => { } const app = new Koa(); + app.use(async (ctx, next) => { + if (ctx.url.endsWith('.gz')) { + ctx.set('Content-Encoding', 'gzip'); + ctx.res.removeHeader('Content-Length'); + } + await next(); + }); app.use(serve(path.resolve(__dirname, '../web'))); const server = app.listen(PORT, () => { logger.info(`Web Server started at http://localhost:${PORT}`); diff --git a/yarn-project/end-to-end/src/composed/e2e_persistence.test.ts b/yarn-project/end-to-end/src/composed/e2e_persistence.test.ts index d96a40e0fc4..3f84d8b2d18 100644 --- a/yarn-project/end-to-end/src/composed/e2e_persistence.test.ts +++ b/yarn-project/end-to-end/src/composed/e2e_persistence.test.ts @@ -80,13 +80,16 @@ describe('Aztec persistence', () => { const secret = Fr.random(); - const mintTxReceipt = await contract.methods.mint_private(1000n, computeSecretHash(secret)).send().wait(); + const mintTxReceipt = await contract.methods + .mint_private(1000n, await computeSecretHash(secret)) + .send() + .wait(); await addPendingShieldNoteToPXE( ownerWallet, contractAddress, 1000n, - computeSecretHash(secret), + await computeSecretHash(secret), mintTxReceipt.txHash, ); @@ -146,12 +149,15 @@ describe('Aztec persistence', () => { const balance = await contract.methods.balance_of_private(ownerWallet.getAddress()).simulate(); const secret = Fr.random(); - const mintTxReceipt = await contract.methods.mint_private(1000n, computeSecretHash(secret)).send().wait(); + const mintTxReceipt = await contract.methods + .mint_private(1000n, await computeSecretHash(secret)) + .send() + .wait(); await addPendingShieldNoteToPXE( ownerWallet, contractAddress, 1000n, - computeSecretHash(secret), + await computeSecretHash(secret), mintTxReceipt.txHash, ); @@ -280,7 +286,10 @@ describe('Aztec persistence', () => { // mint some tokens with a secret we know and redeem later on a separate PXE secret = Fr.random(); mintAmount = 1000n; - const mintTxReceipt = await contract.methods.mint_private(mintAmount, computeSecretHash(secret)).send().wait(); + const mintTxReceipt = await contract.methods + .mint_private(mintAmount, await computeSecretHash(secret)) + .send() + .wait(); mintTxHash = mintTxReceipt.txHash; // publicly reveal that I have 1000 tokens @@ -313,7 +322,13 @@ describe('Aztec persistence', () => { it('allows consuming transparent note created on another PXE', async () => { // this was created in the temporary PXE in `beforeAll` - await addPendingShieldNoteToPXE(ownerWallet, contractAddress, mintAmount, computeSecretHash(secret), mintTxHash); + await addPendingShieldNoteToPXE( + ownerWallet, + contractAddress, + mintAmount, + await computeSecretHash(secret), + mintTxHash, + ); const balanceBeforeRedeem = await contract.methods.balance_of_private(ownerWallet.getAddress()).simulate(); diff --git a/yarn-project/end-to-end/src/composed/integration_l1_publisher.test.ts b/yarn-project/end-to-end/src/composed/integration_l1_publisher.test.ts index 9408edd30be..0793b1aaf45 100644 --- a/yarn-project/end-to-end/src/composed/integration_l1_publisher.test.ts +++ b/yarn-project/end-to-end/src/composed/integration_l1_publisher.test.ts @@ -19,6 +19,7 @@ import { type L1ContractAddresses, createEthereumChain } from '@aztec/ethereum'; import { EthCheatCodesWithState } from '@aztec/ethereum/test'; import { range } from '@aztec/foundation/array'; import { Blob } from '@aztec/foundation/blob'; +import { timesParallel } from '@aztec/foundation/collection'; import { sha256, sha256ToField } from '@aztec/foundation/crypto'; import { openTmpStore } from '@aztec/kv-store/lmdb'; import { OutboxAbi, RollupAbi } from '@aztec/l1-artifacts'; @@ -35,7 +36,7 @@ import { } from '@aztec/world-state'; import { beforeEach, describe, expect, it, jest } from '@jest/globals'; -import * as fs from 'fs'; +import { writeFile } from 'fs/promises'; import { type MockProxy, mock } from 'jest-mock-extended'; import { type Account, @@ -202,12 +203,12 @@ describe('L1Publisher integration', () => { await worldStateSynchronizer.stop(); }); - const makeProcessedTx = (seed = 0x1): ProcessedTx => + const makeProcessedTx = async (seed = 0x1): Promise => makeBloatedProcessedTx({ header: prevHeader, chainId: fr(chainId), version: fr(config.version), - vkTreeRoot: getVKTreeRoot(), + vkTreeRoot: await getVKTreeRoot(), gasSettings: GasSettings.default({ maxFeesPerGas: baseFee }), protocolContractTreeRoot, seed, @@ -224,14 +225,14 @@ describe('L1Publisher integration', () => { * Creates a json object that can be used to test the solidity contract. * The json object must be put into */ - const writeJson = ( + const writeJson = async ( fileName: string, block: L2Block, l1ToL2Content: Fr[], blobs: Blob[], recipientAddress: AztecAddress, deployerAddress: `0x${string}`, - ): void => { + ): Promise => { if (!AZTEC_GENERATE_TEST_DATA) { return; } @@ -253,7 +254,7 @@ describe('L1Publisher integration', () => { // The json formatting in forge is a bit brittle, so we convert Fr to a number in the few values below. // This should not be a problem for testing as long as the values are not larger than u32. archive: `0x${block.archive.root.toBuffer().toString('hex').padStart(64, '0')}`, - blockHash: `0x${block.hash().toBuffer().toString('hex').padStart(64, '0')}`, + blockHash: `0x${(await block.hash()).toBuffer().toString('hex').padStart(64, '0')}`, body: `0x${block.body.toBuffer().toString('hex')}`, decodedHeader: { contentCommitment: { @@ -313,7 +314,7 @@ describe('L1Publisher integration', () => { }; const output = JSON.stringify(jsonObject, null, 2); - fs.writeFileSync(path, output, 'utf8'); + await writeFile(path, output, 'utf8'); }; const buildBlock = async (globalVariables: GlobalVariables, txs: ProcessedTx[], l1ToL2Messages: Fr[]) => { @@ -365,9 +366,9 @@ describe('L1Publisher integration', () => { // Ensure that each transaction has unique (non-intersecting nullifier values) const totalNullifiersPerBlock = 4 * MAX_NULLIFIERS_PER_TX; - const txs = Array(numTxs) - .fill(0) - .map((_, txIndex) => makeProcessedTx(totalNullifiersPerBlock * i + MAX_NULLIFIERS_PER_TX * (txIndex + 1))); + const txs = await timesParallel(numTxs, txIndex => + makeProcessedTx(totalNullifiersPerBlock * i + MAX_NULLIFIERS_PER_TX * (txIndex + 1)), + ); const ts = (await publicClient.getBlock()).timestamp; const slot = await rollup.read.getSlotAt([ts + BigInt(config.ethereumSlotDuration)]); @@ -398,12 +399,12 @@ describe('L1Publisher integration', () => { // Check that we have not yet written a root to this blocknumber expect(BigInt(emptyRoot)).toStrictEqual(0n); - const blobs = Blob.getBlobs(block.body.toBlobFields()); + const blobs = await Blob.getBlobs(block.body.toBlobFields()); expect(block.header.contentCommitment.blobsHash).toEqual( sha256ToField(blobs.map(b => b.getEthVersionedBlobHash())).toBuffer(), ); - writeJson( + await writeJson( `${jsonFileNamePrefix}_${block.number}`, block, l1ToL2Content, @@ -441,7 +442,7 @@ describe('L1Publisher integration', () => { { header: `0x${block.header.toBuffer().toString('hex')}`, archive: `0x${block.archive.root.toBuffer().toString('hex')}`, - blockHash: `0x${block.header.hash().toBuffer().toString('hex')}`, + blockHash: `0x${(await block.header.hash()).toBuffer().toString('hex')}`, oracleInput: { provingCostModifier: 0n, feeAssetPriceModifier: 0n, @@ -503,7 +504,7 @@ describe('L1Publisher integration', () => { // a Rollup__InvalidInHash that is not caught by validateHeader before. const l1ToL2Messages = new Array(NUMBER_OF_L1_L2_MESSAGES_PER_ROLLUP).fill(new Fr(1n)); - const txs = [makeProcessedTx(0x1000), makeProcessedTx(0x2000)]; + const txs = await Promise.all([makeProcessedTx(0x1000), makeProcessedTx(0x2000)]); const ts = (await publicClient.getBlock()).timestamp; const slot = await rollup.read.getSlotAt([ts + BigInt(config.ethereumSlotDuration)]); const timestamp = await rollup.read.getTimestampForSlot([slot]); diff --git a/yarn-project/end-to-end/src/e2e_authwit.test.ts b/yarn-project/end-to-end/src/e2e_authwit.test.ts index 71d4331f054..18b907e0af8 100644 --- a/yarn-project/end-to-end/src/e2e_authwit.test.ts +++ b/yarn-project/end-to-end/src/e2e_authwit.test.ts @@ -44,7 +44,7 @@ describe('e2e_authwit_tests', () => { // 6. We check that the authwit is NOT valid in private for wallet[1] (check that it is not signed by 1) // docs:start:compute_inner_authwit_hash - const innerHash = computeInnerAuthWitHash([Fr.fromHexString('0xdead')]); + const innerHash = await computeInnerAuthWitHash([Fr.fromHexString('0xdead')]); // docs:end:compute_inner_authwit_hash // docs:start:compute_arbitrary_authwit_hash @@ -88,11 +88,11 @@ describe('e2e_authwit_tests', () => { }); describe('failure case', () => { it('invalid chain id', async () => { - const innerHash = computeInnerAuthWitHash([Fr.fromHexString('0xdead'), Fr.fromHexString('0xbeef')]); + const innerHash = await computeInnerAuthWitHash([Fr.fromHexString('0xdead'), Fr.fromHexString('0xbeef')]); const intent = { consumer: auth.address, innerHash }; - const messageHash = computeAuthWitMessageHash(intent, { chainId: Fr.random(), version }); - const expectedMessageHash = computeAuthWitMessageHash(intent, { chainId, version }); + const messageHash = await computeAuthWitMessageHash(intent, { chainId: Fr.random(), version }); + const expectedMessageHash = await computeAuthWitMessageHash(intent, { chainId, version }); expect(await wallets[0].lookupValidity(wallets[0].getAddress(), intent)).toEqual({ isValidInPrivate: false, @@ -120,12 +120,12 @@ describe('e2e_authwit_tests', () => { }); it('invalid version', async () => { - const innerHash = computeInnerAuthWitHash([Fr.fromHexString('0xdead'), Fr.fromHexString('0xbeef')]); + const innerHash = await computeInnerAuthWitHash([Fr.fromHexString('0xdead'), Fr.fromHexString('0xbeef')]); const intent = { consumer: auth.address, innerHash }; - const messageHash = computeAuthWitMessageHash(intent, { chainId, version: Fr.random() }); + const messageHash = await computeAuthWitMessageHash(intent, { chainId, version: Fr.random() }); - const expectedMessageHash = computeAuthWitMessageHash(intent, { chainId, version }); + const expectedMessageHash = await computeAuthWitMessageHash(intent, { chainId, version }); expect(await wallets[0].lookupValidity(wallets[0].getAddress(), intent)).toEqual({ isValidInPrivate: false, @@ -158,7 +158,7 @@ describe('e2e_authwit_tests', () => { describe('Public', () => { describe('arbitrary data', () => { it('happy path', async () => { - const innerHash = computeInnerAuthWitHash([Fr.fromHexString('0xdead'), Fr.fromHexString('0x01')]); + const innerHash = await computeInnerAuthWitHash([Fr.fromHexString('0xdead'), Fr.fromHexString('0x01')]); const intent = { consumer: wallets[1].getAddress(), innerHash }; @@ -168,7 +168,8 @@ describe('e2e_authwit_tests', () => { }); // docs:start:set_public_authwit - await wallets[0].setPublicAuthWit(intent, true).send().wait(); + const validateActionInteraction = await wallets[0].setPublicAuthWit(intent, true); + await validateActionInteraction.send().wait(); // docs:end:set_public_authwit expect(await wallets[0].lookupValidity(wallets[0].getAddress(), intent)).toEqual({ isValidInPrivate: false, @@ -186,7 +187,7 @@ describe('e2e_authwit_tests', () => { describe('failure case', () => { it('cancel before usage', async () => { - const innerHash = computeInnerAuthWitHash([Fr.fromHexString('0xdead'), Fr.fromHexString('0x02')]); + const innerHash = await computeInnerAuthWitHash([Fr.fromHexString('0xdead'), Fr.fromHexString('0x02')]); const intent = { consumer: auth.address, innerHash }; expect(await wallets[0].lookupValidity(wallets[0].getAddress(), intent)).toEqual({ @@ -194,14 +195,16 @@ describe('e2e_authwit_tests', () => { isValidInPublic: false, }); - await wallets[0].setPublicAuthWit(intent, true).send().wait(); + const validateActionInteraction = await wallets[0].setPublicAuthWit(intent, true); + await validateActionInteraction.send().wait(); expect(await wallets[0].lookupValidity(wallets[0].getAddress(), intent)).toEqual({ isValidInPrivate: false, isValidInPublic: true, }); - await wallets[0].setPublicAuthWit(intent, false).send().wait(); + const cancelActionInteraction = await wallets[0].setPublicAuthWit(intent, false); + await cancelActionInteraction.send().wait(); expect(await wallets[0].lookupValidity(wallets[0].getAddress(), intent)).toEqual({ isValidInPrivate: false, diff --git a/yarn-project/end-to-end/src/e2e_avm_simulator.test.ts b/yarn-project/end-to-end/src/e2e_avm_simulator.test.ts index b8e26d7f97e..fc40776e275 100644 --- a/yarn-project/end-to-end/src/e2e_avm_simulator.test.ts +++ b/yarn-project/end-to-end/src/e2e_avm_simulator.test.ts @@ -102,8 +102,8 @@ describe('e2e_avm_simulator', () => { const address = AztecAddress.fromBigInt(9090n); // This will create 1 tx with 2 public calls in it. await new BatchCall(wallet, [ - avmContract.methods.set_storage_map(address, 100).request(), - avmContract.methods.add_storage_map(address, 100).request(), + await avmContract.methods.set_storage_map(address, 100).request(), + await avmContract.methods.add_storage_map(address, 100).request(), ]) .send() .wait(); @@ -149,8 +149,8 @@ describe('e2e_avm_simulator', () => { // This will create 1 tx with 2 public calls in it. await new BatchCall(wallet, [ - avmContract.methods.new_nullifier(nullifier).request(), - avmContract.methods.assert_nullifier_exists(nullifier).request(), + await avmContract.methods.new_nullifier(nullifier).request(), + await avmContract.methods.assert_nullifier_exists(nullifier).request(), ]) .send() .wait(); diff --git a/yarn-project/end-to-end/src/e2e_blacklist_token_contract/blacklist_token_contract_test.ts b/yarn-project/end-to-end/src/e2e_blacklist_token_contract/blacklist_token_contract_test.ts index 68e19afdba0..e597eb47f9a 100644 --- a/yarn-project/end-to-end/src/e2e_blacklist_token_contract/blacklist_token_contract_test.ts +++ b/yarn-project/end-to-end/src/e2e_blacklist_token_contract/blacklist_token_contract_test.ts @@ -205,7 +205,7 @@ export class BlacklistTokenContractTest { this.logger.verbose(`Minting ${amount} privately...`); const secret = Fr.random(); - const secretHash = computeSecretHash(secret); + const secretHash = await computeSecretHash(secret); const receipt = await asset.methods.mint_private(amount, secretHash).send().wait(); await this.addPendingShieldNoteToPXE(0, amount, secretHash, receipt.txHash); diff --git a/yarn-project/end-to-end/src/e2e_blacklist_token_contract/burn.test.ts b/yarn-project/end-to-end/src/e2e_blacklist_token_contract/burn.test.ts index 4d9da34679d..26082c95837 100644 --- a/yarn-project/end-to-end/src/e2e_blacklist_token_contract/burn.test.ts +++ b/yarn-project/end-to-end/src/e2e_blacklist_token_contract/burn.test.ts @@ -42,7 +42,11 @@ describe('e2e_blacklist_token_contract burn', () => { // We need to compute the message we want to sign and add it to the wallet as approved const action = asset.withWallet(wallets[1]).methods.burn_public(wallets[0].getAddress(), amount, nonce); - await wallets[0].setPublicAuthWit({ caller: wallets[1].getAddress(), action }, true).send().wait(); + const validateActionInteraction = await wallets[0].setPublicAuthWit( + { caller: wallets[1].getAddress(), action }, + true, + ); + await validateActionInteraction.send().wait(); await action.send().wait(); @@ -90,7 +94,11 @@ describe('e2e_blacklist_token_contract burn', () => { // We need to compute the message we want to sign and add it to the wallet as approved const action = asset.withWallet(wallets[1]).methods.burn_public(wallets[0].getAddress(), amount, nonce); - await wallets[0].setPublicAuthWit({ caller: wallets[1].getAddress(), action }, true).send().wait(); + const validateActionInteraction = await wallets[0].setPublicAuthWit( + { caller: wallets[1].getAddress(), action }, + true, + ); + await validateActionInteraction.send().wait(); await expect(action.prove()).rejects.toThrow(U128_UNDERFLOW_ERROR); }); @@ -103,7 +111,11 @@ describe('e2e_blacklist_token_contract burn', () => { // We need to compute the message we want to sign and add it to the wallet as approved const action = asset.withWallet(wallets[1]).methods.burn_public(wallets[0].getAddress(), amount, nonce); - await wallets[0].setPublicAuthWit({ caller: wallets[0].getAddress(), action }, true).send().wait(); + const validateActionInteraction = await wallets[0].setPublicAuthWit( + { caller: wallets[0].getAddress(), action }, + true, + ); + await validateActionInteraction.send().wait(); await expect( asset.withWallet(wallets[1]).methods.burn_public(wallets[0].getAddress(), amount, nonce).simulate(), @@ -196,8 +208,8 @@ describe('e2e_blacklist_token_contract burn', () => { // We need to compute the message we want to sign and add it to the wallet as approved const action = asset.withWallet(wallets[1]).methods.burn(wallets[0].getAddress(), amount, nonce); - const messageHash = computeAuthWitMessageHash( - { caller: wallets[1].getAddress(), action: action.request() }, + const messageHash = await computeAuthWitMessageHash( + { caller: wallets[1].getAddress(), action }, { chainId: wallets[0].getChainId(), version: wallets[0].getVersion() }, ); @@ -215,8 +227,8 @@ describe('e2e_blacklist_token_contract burn', () => { // We need to compute the message we want to sign and add it to the wallet as approved const action = asset.withWallet(wallets[2]).methods.burn(wallets[0].getAddress(), amount, nonce); - const expectedMessageHash = computeAuthWitMessageHash( - { caller: wallets[2].getAddress(), action: action.request() }, + const expectedMessageHash = await computeAuthWitMessageHash( + { caller: wallets[2].getAddress(), action }, { chainId: wallets[0].getChainId(), version: wallets[0].getVersion() }, ); diff --git a/yarn-project/end-to-end/src/e2e_blacklist_token_contract/minting.test.ts b/yarn-project/end-to-end/src/e2e_blacklist_token_contract/minting.test.ts index 010eb706ef1..8775f4c493a 100644 --- a/yarn-project/end-to-end/src/e2e_blacklist_token_contract/minting.test.ts +++ b/yarn-project/end-to-end/src/e2e_blacklist_token_contract/minting.test.ts @@ -78,8 +78,8 @@ describe('e2e_blacklist_token_contract mint', () => { let secretHash: Fr; let txHash: TxHash; - beforeAll(() => { - secretHash = computeSecretHash(secret); + beforeAll(async () => { + secretHash = await computeSecretHash(secret); }); describe('Mint flow', () => { diff --git a/yarn-project/end-to-end/src/e2e_blacklist_token_contract/shielding.test.ts b/yarn-project/end-to-end/src/e2e_blacklist_token_contract/shielding.test.ts index c2955daf770..810d74255b2 100644 --- a/yarn-project/end-to-end/src/e2e_blacklist_token_contract/shielding.test.ts +++ b/yarn-project/end-to-end/src/e2e_blacklist_token_contract/shielding.test.ts @@ -26,8 +26,8 @@ describe('e2e_blacklist_token_contract shield + redeem_shield', () => { const secret = Fr.random(); let secretHash: Fr; - beforeAll(() => { - secretHash = computeSecretHash(secret); + beforeAll(async () => { + secretHash = await computeSecretHash(secret); }); it('on behalf of self', async () => { @@ -54,7 +54,11 @@ describe('e2e_blacklist_token_contract shield + redeem_shield', () => { // We need to compute the message we want to sign and add it to the wallet as approved const action = asset.withWallet(wallets[1]).methods.shield(wallets[0].getAddress(), amount, secretHash, nonce); - await wallets[0].setPublicAuthWit({ caller: wallets[1].getAddress(), action }, true).send().wait(); + const validateActionInteraction = await wallets[0].setPublicAuthWit( + { caller: wallets[1].getAddress(), action }, + true, + ); + await validateActionInteraction.send().wait(); const receipt = await action.send().wait(); @@ -101,7 +105,11 @@ describe('e2e_blacklist_token_contract shield + redeem_shield', () => { // We need to compute the message we want to sign and add it to the wallet as approved const action = asset.withWallet(wallets[1]).methods.shield(wallets[0].getAddress(), amount, secretHash, nonce); - await wallets[0].setPublicAuthWit({ caller: wallets[1].getAddress(), action }, true).send().wait(); + const validateActionInteraction = await wallets[0].setPublicAuthWit( + { caller: wallets[1].getAddress(), action }, + true, + ); + await validateActionInteraction.send().wait(); await expect(action.prove()).rejects.toThrow(U128_UNDERFLOW_ERROR); }); @@ -114,7 +122,11 @@ describe('e2e_blacklist_token_contract shield + redeem_shield', () => { // We need to compute the message we want to sign and add it to the wallet as approved const action = asset.withWallet(wallets[2]).methods.shield(wallets[0].getAddress(), amount, secretHash, nonce); - await wallets[0].setPublicAuthWit({ caller: wallets[1].getAddress(), action }, true).send().wait(); + const validateActionInteraction = await wallets[0].setPublicAuthWit( + { caller: wallets[1].getAddress(), action }, + true, + ); + await validateActionInteraction.send().wait(); await expect(action.prove()).rejects.toThrow(/unauthorized/); }); diff --git a/yarn-project/end-to-end/src/e2e_blacklist_token_contract/transfer_private.test.ts b/yarn-project/end-to-end/src/e2e_blacklist_token_contract/transfer_private.test.ts index c35625d3450..ba524f3a41a 100644 --- a/yarn-project/end-to-end/src/e2e_blacklist_token_contract/transfer_private.test.ts +++ b/yarn-project/end-to-end/src/e2e_blacklist_token_contract/transfer_private.test.ts @@ -144,8 +144,8 @@ describe('e2e_blacklist_token_contract transfer private', () => { const action = asset .withWallet(wallets[1]) .methods.transfer(wallets[0].getAddress(), wallets[1].getAddress(), amount, nonce); - const messageHash = computeAuthWitMessageHash( - { caller: wallets[1].getAddress(), action: action.request() }, + const messageHash = await computeAuthWitMessageHash( + { caller: wallets[1].getAddress(), action }, { chainId: wallets[0].getChainId(), version: wallets[0].getVersion() }, ); @@ -162,8 +162,8 @@ describe('e2e_blacklist_token_contract transfer private', () => { const action = asset .withWallet(wallets[2]) .methods.transfer(wallets[0].getAddress(), wallets[1].getAddress(), amount, nonce); - const expectedMessageHash = computeAuthWitMessageHash( - { caller: wallets[2].getAddress(), action: action.request() }, + const expectedMessageHash = await computeAuthWitMessageHash( + { caller: wallets[2].getAddress(), action }, { chainId: wallets[0].getChainId(), version: wallets[0].getVersion() }, ); diff --git a/yarn-project/end-to-end/src/e2e_blacklist_token_contract/transfer_public.test.ts b/yarn-project/end-to-end/src/e2e_blacklist_token_contract/transfer_public.test.ts index 58d2d762fa2..c54439bd755 100644 --- a/yarn-project/end-to-end/src/e2e_blacklist_token_contract/transfer_public.test.ts +++ b/yarn-project/end-to-end/src/e2e_blacklist_token_contract/transfer_public.test.ts @@ -53,7 +53,11 @@ describe('e2e_blacklist_token_contract transfer public', () => { .withWallet(wallets[1]) .methods.transfer_public(wallets[0].getAddress(), wallets[1].getAddress(), amount, nonce); - await wallets[0].setPublicAuthWit({ caller: wallets[1].getAddress(), action }, true).send().wait(); + const validateActionInteraction = await wallets[0].setPublicAuthWit( + { caller: wallets[1].getAddress(), action }, + true, + ); + await validateActionInteraction.send().wait(); // docs:end:authwit_public_transfer_example // Perform the transfer @@ -113,7 +117,11 @@ describe('e2e_blacklist_token_contract transfer public', () => { // We need to compute the message we want to sign and add it to the wallet as approved // docs:start:set_public_authwit - await wallets[0].setPublicAuthWit({ caller: wallets[1].getAddress(), action }, true).send().wait(); + const validateActionInteraction = await wallets[0].setPublicAuthWit( + { caller: wallets[1].getAddress(), action }, + true, + ); + await validateActionInteraction.send().wait(); // docs:end:set_public_authwit // Perform the transfer await expect(action.prove()).rejects.toThrow(U128_UNDERFLOW_ERROR); @@ -134,7 +142,11 @@ describe('e2e_blacklist_token_contract transfer public', () => { .withWallet(wallets[1]) .methods.transfer_public(wallets[0].getAddress(), wallets[1].getAddress(), amount, nonce); - await wallets[0].setPublicAuthWit({ caller: wallets[0].getAddress(), action }, true).send().wait(); + const validateActionInteraction = await wallets[0].setPublicAuthWit( + { caller: wallets[0].getAddress(), action }, + true, + ); + await validateActionInteraction.send().wait(); // Perform the transfer await expect(action.simulate()).rejects.toThrow(/unauthorized/); diff --git a/yarn-project/end-to-end/src/e2e_blacklist_token_contract/unshielding.test.ts b/yarn-project/end-to-end/src/e2e_blacklist_token_contract/unshielding.test.ts index e23372a34c0..be3c4ce7af7 100644 --- a/yarn-project/end-to-end/src/e2e_blacklist_token_contract/unshielding.test.ts +++ b/yarn-project/end-to-end/src/e2e_blacklist_token_contract/unshielding.test.ts @@ -115,8 +115,8 @@ describe('e2e_blacklist_token_contract unshielding', () => { const action = asset .withWallet(wallets[2]) .methods.unshield(wallets[0].getAddress(), wallets[1].getAddress(), amount, nonce); - const expectedMessageHash = computeAuthWitMessageHash( - { caller: wallets[2].getAddress(), action: action.request() }, + const expectedMessageHash = await computeAuthWitMessageHash( + { caller: wallets[2].getAddress(), action }, { chainId: wallets[0].getChainId(), version: wallets[0].getVersion() }, ); diff --git a/yarn-project/end-to-end/src/e2e_block_building.test.ts b/yarn-project/end-to-end/src/e2e_block_building.test.ts index 484b056b079..cc51a3b9d82 100644 --- a/yarn-project/end-to-end/src/e2e_block_building.test.ts +++ b/yarn-project/end-to-end/src/e2e_block_building.test.ts @@ -463,7 +463,7 @@ describe('e2e_block_building', () => { // The last log is not encrypted. // The first field is the first value and is siloed with contract address by the kernel circuit. - const expectedFirstField = poseidon2Hash([testContract.address, values[0]]); + const expectedFirstField = await poseidon2Hash([testContract.address, values[0]]); expect(privateLogs[2].fields.slice(0, 5)).toEqual([expectedFirstField, ...values.slice(1)]); }, 60_000); }); diff --git a/yarn-project/end-to-end/src/e2e_card_game.test.ts b/yarn-project/end-to-end/src/e2e_card_game.test.ts index 72ee46c0c0f..1038df08578 100644 --- a/yarn-project/end-to-end/src/e2e_card_game.test.ts +++ b/yarn-project/end-to-end/src/e2e_card_game.test.ts @@ -85,10 +85,10 @@ describe('e2e_card_game', () => { let contractAsSecondPlayer: CardGameContract; let contractAsThirdPlayer: CardGameContract; - const getPackedCards = (accountIndex: number, seed: bigint): Card[] => { + const getPackedCards = async (accountIndex: number, seed: bigint): Promise => { // First we get the app nullifier secret key for the account const masterNullifierSecretKey = masterNullifierSecretKeys[accountIndex]; - const appNullifierSecretKey = computeAppNullifierSecretKey(masterNullifierSecretKey, contract.address); + const appNullifierSecretKey = await computeAppNullifierSecretKey(masterNullifierSecretKey, contract.address); // Then we compute the mix from it and hash it to get the random bytes the same way as in the contract const mix = appNullifierSecretKey.toBigInt() + seed; const randomBytes = sha256(toBufferLE(mix, 32)); @@ -159,7 +159,7 @@ describe('e2e_card_game', () => { await contract.methods.buy_pack(seed).send().wait(); // docs:end:send_tx const collection = await contract.methods.view_collection_cards(firstPlayer, 0).simulate({ from: firstPlayer }); - const expected = getPackedCards(0, seed); + const expected = await getPackedCards(0, seed); expect(boundedVecToArray(collection)).toMatchObject(expected); }); diff --git a/yarn-project/end-to-end/src/e2e_cheat_codes.test.ts b/yarn-project/end-to-end/src/e2e_cheat_codes.test.ts index 42e58a350a3..2fc4e27728e 100644 --- a/yarn-project/end-to-end/src/e2e_cheat_codes.test.ts +++ b/yarn-project/end-to-end/src/e2e_cheat_codes.test.ts @@ -183,7 +183,7 @@ describe('e2e_cheat_codes', () => { await mintTokensToPrivate(token, wallet, admin, mintAmount); await token.methods.sync_notes().simulate(); - const balancesAdminSlot = cc.aztec.computeSlotInMap(TokenContract.storage.balances.slot, admin); + const balancesAdminSlot = await cc.aztec.computeSlotInMap(TokenContract.storage.balances.slot, admin); // check if note was added to pending shield: const notes = await cc.aztec.loadPrivate(admin, token.address, balancesAdminSlot); diff --git a/yarn-project/end-to-end/src/e2e_cross_chain_messaging/l1_to_l2.test.ts b/yarn-project/end-to-end/src/e2e_cross_chain_messaging/l1_to_l2.test.ts index 8c67dd634ba..d43befd0325 100644 --- a/yarn-project/end-to-end/src/e2e_cross_chain_messaging/l1_to_l2.test.ts +++ b/yarn-project/end-to-end/src/e2e_cross_chain_messaging/l1_to_l2.test.ts @@ -33,7 +33,7 @@ describe('e2e_cross_chain_messaging l1_to_l2', () => { ? testContract.methods.consume_message_from_arbitrary_sender_private : testContract.methods.consume_message_from_arbitrary_sender_public; - const [secret, secretHash] = generateClaimSecret(); + const [secret, secretHash] = await generateClaimSecret(); const message = { recipient: testContract.address, content: Fr.random(), secretHash }; const [message1Hash, actualMessage1Index] = await sendL2Message(message); diff --git a/yarn-project/end-to-end/src/e2e_cross_chain_messaging/token_bridge_public.test.ts b/yarn-project/end-to-end/src/e2e_cross_chain_messaging/token_bridge_public.test.ts index 6f09c0bf374..925b6e71eaf 100644 --- a/yarn-project/end-to-end/src/e2e_cross_chain_messaging/token_bridge_public.test.ts +++ b/yarn-project/end-to-end/src/e2e_cross_chain_messaging/token_bridge_public.test.ts @@ -73,16 +73,14 @@ describe('e2e_cross_chain_messaging token_bridge_public', () => { // 4. Give approval to bridge to burn owner's funds: const withdrawAmount = 9n; const nonce = Fr.random(); - await user1Wallet - .setPublicAuthWit( - { - caller: l2Bridge.address, - action: l2Token.methods.burn_public(ownerAddress, withdrawAmount, nonce).request(), - }, - true, - ) - .send() - .wait(); + const validateActionInteraction = await user1Wallet.setPublicAuthWit( + { + caller: l2Bridge.address, + action: l2Token.methods.burn_public(ownerAddress, withdrawAmount, nonce), + }, + true, + ); + await validateActionInteraction.send().wait(); // 5. Withdraw owner's funds from L2 to L1 logger.verbose('5. Withdraw owner funds from L2 to L1'); diff --git a/yarn-project/end-to-end/src/e2e_crowdfunding_and_claim.test.ts b/yarn-project/end-to-end/src/e2e_crowdfunding_and_claim.test.ts index e3027690bb1..d9cfb864482 100644 --- a/yarn-project/end-to-end/src/e2e_crowdfunding_and_claim.test.ts +++ b/yarn-project/end-to-end/src/e2e_crowdfunding_and_claim.test.ts @@ -102,7 +102,7 @@ describe('e2e_crowdfunding_and_claim', () => { deadline, ); const crowdfundingInstance = await crowdfundingDeployment.getInstance(); - await pxe.registerAccount(crowdfundingSecretKey, computePartialAddress(crowdfundingInstance)); + await pxe.registerAccount(crowdfundingSecretKey, await computePartialAddress(crowdfundingInstance)); crowdfundingContract = await crowdfundingDeployment.send().deployed(); logger.info(`Crowdfunding contract deployed at ${crowdfundingContract.address}`); @@ -335,9 +335,9 @@ describe('e2e_crowdfunding_and_claim', () => { ).rejects.toThrow('Assertion failed: Not an operator'); // Instead, we construct a call and impersonate operator by skipping the usual account contract entrypoint... - const call = crowdfundingContract.withWallet(donorWallets[1]).methods.withdraw(donationAmount).request(); + const call = await crowdfundingContract.withWallet(donorWallets[1]).methods.withdraw(donationAmount).request(); // ...using the withdraw fn as our entrypoint - const entrypointHashedValues = HashedValues.fromValues(call.args); + const entrypointHashedValues = await HashedValues.fromValues(call.args); const maxFeesPerGas = await pxe.getCurrentBaseFees(); const request = new TxExecutionRequest( call.to, diff --git a/yarn-project/end-to-end/src/e2e_deploy_contract/contract_class_registration.test.ts b/yarn-project/end-to-end/src/e2e_deploy_contract/contract_class_registration.test.ts index e1518bf45b8..3798d571b7e 100644 --- a/yarn-project/end-to-end/src/e2e_deploy_contract/contract_class_registration.test.ts +++ b/yarn-project/end-to-end/src/e2e_deploy_contract/contract_class_registration.test.ts @@ -52,7 +52,7 @@ describe('e2e_deploy_contract contract class registration', () => { beforeAll(async () => { artifact = StatefulTestContract.artifact; registrationTxReceipt = await registerContractClass(wallet, artifact, false).then(c => c.send().wait()); - contractClass = getContractClassFromArtifact(artifact); + contractClass = await getContractClassFromArtifact(artifact); // TODO(#10007) Remove this call. Node should get the bytecode from the event broadcast. expect(await aztecNode.getContractClass(contractClass.id)).toBeUndefined(); @@ -76,7 +76,7 @@ describe('e2e_deploy_contract contract class registration', () => { // TODO(#10007) Remove this test as well. it('starts archiver with pre-registered common contracts', async () => { - const classId = computeContractClassId(getContractClassFromArtifact(TokenContractArtifact)); + const classId = await computeContractClassId(await getContractClassFromArtifact(TokenContractArtifact)); // The node checks the registration nullifier expect(await aztecNode.getContractClass(classId)).toBeUndefined(); // But the archiver does not @@ -107,7 +107,10 @@ describe('e2e_deploy_contract contract class registration', () => { // If that's the case you should update this test to use a private function which fits into the bytecode size limit. throw new Error('No constructor found in the StatefulTestContract artifact. Does it still exist?'); } - const selector = FunctionSelector.fromNameAndParameters(constructorArtifact.name, constructorArtifact.parameters); + const selector = await FunctionSelector.fromNameAndParameters( + constructorArtifact.name, + constructorArtifact.parameters, + ); const tx = await (await broadcastPrivateFunction(wallet, artifact, selector)).send().wait(); const logs = await pxe.getContractClassLogs({ txHash: tx.txHash }); @@ -122,7 +125,7 @@ describe('e2e_deploy_contract contract class registration', () => { it('broadcasts an unconstrained function', async () => { const functionArtifact = artifact.functions.find(fn => fn.functionType === FunctionType.UNCONSTRAINED)!; - const selector = FunctionSelector.fromNameAndParameters(functionArtifact); + const selector = await FunctionSelector.fromNameAndParameters(functionArtifact); const tx = await (await broadcastUnconstrainedFunction(wallet, artifact, selector)).send().wait(); const logs = await pxe.getContractClassLogs({ txHash: tx.txHash }); const logData = logs.logs[0].log.data; diff --git a/yarn-project/end-to-end/src/e2e_deploy_contract/deploy_method.test.ts b/yarn-project/end-to-end/src/e2e_deploy_contract/deploy_method.test.ts index ba73c129e80..59b156d3c2e 100644 --- a/yarn-project/end-to-end/src/e2e_deploy_contract/deploy_method.test.ts +++ b/yarn-project/end-to-end/src/e2e_deploy_contract/deploy_method.test.ts @@ -56,7 +56,7 @@ describe('e2e_deploy_contract deploy method', () => { // TODO(#10007): Remove this test. Common contracts (ie token contracts) are only distinguished // because we're manually adding them to the archiver to support provernet. it('registers a contract class for a common contract', async () => { - const { id: tokenContractClass } = getContractClassFromArtifact(TokenContract.artifact); + const { id: tokenContractClass } = await getContractClassFromArtifact(TokenContract.artifact); expect(await pxe.isContractClassPubliclyRegistered(tokenContractClass)).toBeFalse(); await TokenContract.deploy(wallet, wallet.getAddress(), 'TOKEN', 'TKN', 18n).send().deployed(); expect(await pxe.isContractClassPubliclyRegistered(tokenContractClass)).toBeTrue(); @@ -132,7 +132,7 @@ describe('e2e_deploy_contract deploy method', () => { // Batch registration, deployment, and public call into same TX logger.debug(`Creating public calls to run in same batch as deployment`); - const init = contract.methods.increment_public_value(owner, 84).request(); + const init = await contract.methods.increment_public_value(owner, 84).request(); logger.debug(`Deploying a contract and calling a public function in the same batched call`); await new BatchCall(wallet, [...deploy.calls, init]).send().wait(); }, 300_000); diff --git a/yarn-project/end-to-end/src/e2e_deploy_contract/private_initialization.test.ts b/yarn-project/end-to-end/src/e2e_deploy_contract/private_initialization.test.ts index 33a38c0ecb1..1feec35ac7e 100644 --- a/yarn-project/end-to-end/src/e2e_deploy_contract/private_initialization.test.ts +++ b/yarn-project/end-to-end/src/e2e_deploy_contract/private_initialization.test.ts @@ -26,7 +26,7 @@ describe('e2e_deploy_contract private initialization', () => { const testWallet = kind === 'as entrypoint' ? new SignerlessWallet(pxe) : wallet; const contract = await t.registerContract(testWallet, TestContract); const receipt = await contract.methods.emit_nullifier(10).send().wait({ debug: true }); - const expected = siloNullifier(contract.address, new Fr(10)); + const expected = await siloNullifier(contract.address, new Fr(10)); expect(receipt.debugInfo?.nullifiers).toContainEqual(expected); }, ); @@ -60,7 +60,7 @@ describe('e2e_deploy_contract private initialization', () => { const contracts = await Promise.all( initArgs.map(initArgs => t.registerContract(wallet, StatefulTestContract, { initArgs })), ); - const calls = contracts.map((c, i) => c.methods.constructor(...initArgs[i]).request()); + const calls = await Promise.all(contracts.map((c, i) => c.methods.constructor(...initArgs[i]).request())); await new BatchCall(wallet, calls).send().wait(); expect(await contracts[0].methods.summed_values(owner).simulate()).toEqual(42n); expect(await contracts[1].methods.summed_values(owner).simulate()).toEqual(52n); @@ -72,8 +72,8 @@ describe('e2e_deploy_contract private initialization', () => { const contract = await t.registerContract(wallet, StatefulTestContract, { initArgs }); const sender = owner; const batch = new BatchCall(wallet, [ - contract.methods.constructor(...initArgs).request(), - contract.methods.create_note(owner, sender, 10).request(), + await contract.methods.constructor(...initArgs).request(), + await contract.methods.create_note(owner, sender, 10).request(), ]); logger.info(`Executing constructor and private function in batch at ${contract.address}`); await batch.send().wait(); diff --git a/yarn-project/end-to-end/src/e2e_epochs.test.ts b/yarn-project/end-to-end/src/e2e_epochs.test.ts index d3c32a3f4d0..62f47dfbaca 100644 --- a/yarn-project/end-to-end/src/e2e_epochs.test.ts +++ b/yarn-project/end-to-end/src/e2e_epochs.test.ts @@ -134,7 +134,7 @@ describe('e2e_epochs', () => { }); it('submits proof claim alone if there are no txs to build a block', async () => { - context.sequencer?.updateSequencerConfig({ minTxsPerBlock: 1 }); + await context.sequencer?.updateSequencerConfig({ minTxsPerBlock: 1 }); await waitUntilEpochStarts(1); const blockNumberAtEndOfEpoch0 = Number(await rollup.getBlockNumber()); logger.info(`Starting epoch 1 after L2 block ${blockNumberAtEndOfEpoch0}`); diff --git a/yarn-project/end-to-end/src/e2e_escrow_contract.test.ts b/yarn-project/end-to-end/src/e2e_escrow_contract.test.ts index cb0550efe53..55dc5a96568 100644 --- a/yarn-project/end-to-end/src/e2e_escrow_contract.test.ts +++ b/yarn-project/end-to-end/src/e2e_escrow_contract.test.ts @@ -47,7 +47,7 @@ describe('e2e_escrow_contract', () => { escrowPublicKeys = (await deriveKeys(escrowSecretKey)).publicKeys; const escrowDeployment = EscrowContract.deployWithPublicKeys(escrowPublicKeys, wallet, owner); const escrowInstance = await escrowDeployment.getInstance(); - await pxe.registerAccount(escrowSecretKey, computePartialAddress(escrowInstance)); + await pxe.registerAccount(escrowSecretKey, await computePartialAddress(escrowInstance)); escrowContract = await escrowDeployment.send().deployed(); logger.info(`Escrow contract deployed at ${escrowContract.address}`); @@ -92,8 +92,8 @@ describe('e2e_escrow_contract', () => { await expectTokenBalance(wallet, token, owner, 50n, logger); await new BatchCall(wallet, [ - token.methods.transfer(recipient, 10).request(), - escrowContract.methods.withdraw(token.address, 20, recipient).request(), + await token.methods.transfer(recipient, 10).request(), + await escrowContract.methods.withdraw(token.address, 20, recipient).request(), ]) .send() .wait(); diff --git a/yarn-project/end-to-end/src/e2e_event_logs.test.ts b/yarn-project/end-to-end/src/e2e_event_logs.test.ts index b106afa689f..c0db5b7d1e6 100644 --- a/yarn-project/end-to-end/src/e2e_event_logs.test.ts +++ b/yarn-project/end-to-end/src/e2e_event_logs.test.ts @@ -54,7 +54,9 @@ describe('Logs', () => { ))!; expect(decryptedEvent0.contractAddress).toStrictEqual(testLogContract.address); - expect(decryptedEvent0.eventTypeId).toStrictEqual(EventSelector.fromSignature('ExampleEvent0(Field,Field)')); + expect(decryptedEvent0.eventTypeId).toStrictEqual( + await EventSelector.fromSignature('ExampleEvent0(Field,Field)'), + ); // We decode our event into the event type const event0Metadata = new EventMetadata(TestLogContract.events.ExampleEvent0); @@ -79,7 +81,7 @@ describe('Logs', () => { expect(badEvent0).toBe(undefined); expect(decryptedEvent1.contractAddress).toStrictEqual(testLogContract.address); - expect(decryptedEvent1.eventTypeId).toStrictEqual(EventSelector.fromSignature('ExampleEvent1((Field),u8)')); + expect(decryptedEvent1.eventTypeId).toStrictEqual(await EventSelector.fromSignature('ExampleEvent1((Field),u8)')); // We expect the fields to have been populated correctly expect(event1?.value2).toStrictEqual(new AztecAddress(preimage[2])); diff --git a/yarn-project/end-to-end/src/e2e_fees/failures.test.ts b/yarn-project/end-to-end/src/e2e_fees/failures.test.ts index 1be010b2f2c..19f8619adaa 100644 --- a/yarn-project/end-to-end/src/e2e_fees/failures.test.ts +++ b/yarn-project/end-to-end/src/e2e_fees/failures.test.ts @@ -317,33 +317,33 @@ class BuggedSetupFeePaymentMethod extends PublicFeePaymentMethod { const asset = await this.getAsset(); - return Promise.resolve([ - this.wallet - .setPublicAuthWit( - { - caller: this.paymentContract, - action: { - name: 'transfer_in_public', - args: [this.wallet.getAddress().toField(), this.paymentContract.toField(), ...maxFee.toFields(), nonce], - selector: FunctionSelector.fromSignature('transfer_in_public((Field),(Field),(Field,Field),Field)'), - type: FunctionType.PUBLIC, - isStatic: false, - to: asset, - returnTypes: [], - }, - }, - true, - ) - .request(), + const setPublicAuthWitInteraction = await this.wallet.setPublicAuthWit( + { + caller: this.paymentContract, + action: { + name: 'transfer_in_public', + args: [this.wallet.getAddress().toField(), this.paymentContract.toField(), ...maxFee.toFields(), nonce], + selector: await FunctionSelector.fromSignature('transfer_in_public((Field),(Field),(Field,Field),Field)'), + type: FunctionType.PUBLIC, + isStatic: false, + to: asset, + returnTypes: [], + }, + }, + true, + ); + + return [ + await setPublicAuthWitInteraction.request(), { name: 'fee_entrypoint_public', to: this.paymentContract, - selector: FunctionSelector.fromSignature('fee_entrypoint_public((Field,Field),Field)'), + selector: await FunctionSelector.fromSignature('fee_entrypoint_public((Field,Field),Field)'), type: FunctionType.PRIVATE, isStatic: false, args: [...tooMuchFee.toFields(), nonce], returnTypes: [], }, - ]); + ]; } } diff --git a/yarn-project/end-to-end/src/e2e_fees/fees_test.ts b/yarn-project/end-to-end/src/e2e_fees/fees_test.ts index aa544329bda..77bb6ca11bc 100644 --- a/yarn-project/end-to-end/src/e2e_fees/fees_test.ts +++ b/yarn-project/end-to-end/src/e2e_fees/fees_test.ts @@ -151,7 +151,7 @@ export class FeesTest { if (!bobInstance) { throw new Error('Bob instance not found'); } - await this.aliceWallet.registerAccount(accountKeys[1][0], computePartialAddress(bobInstance)); + await this.aliceWallet.registerAccount(accountKeys[1][0], await computePartialAddress(bobInstance)); this.coinbase = EthAddress.random(); const { publicClient, walletClient } = createL1Clients(aztecNodeConfig.l1RpcUrl, MNEMONIC); 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 d0fb0c3b644..95295cc155f 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 @@ -75,7 +75,9 @@ describe('e2e_fees gas_estimation', () => { }); logGasEstimate(estimatedGas); - (t.aztecNode as AztecNodeService).getSequencer()!.updateSequencerConfig({ minTxsPerBlock: 2, maxTxsPerBlock: 2 }); + await (t.aztecNode as AztecNodeService) + .getSequencer()! + .updateSequencerConfig({ minTxsPerBlock: 2, maxTxsPerBlock: 2 }); const [withEstimate, withoutEstimate] = await sendTransfers(paymentMethod, estimatedGasPadding); diff --git a/yarn-project/end-to-end/src/e2e_fees/private_payments.test.ts b/yarn-project/end-to-end/src/e2e_fees/private_payments.test.ts index 39420fd44b8..0db699cef4e 100644 --- a/yarn-project/end-to-end/src/e2e_fees/private_payments.test.ts +++ b/yarn-project/end-to-end/src/e2e_fees/private_payments.test.ts @@ -269,8 +269,8 @@ describe('e2e_fees private_payment', () => { * increase Alice's private banana balance by feeAmount by finalizing partial note */ const tx = await new BatchCall(aliceWallet, [ - bananaCoin.methods.transfer(bobAddress, amountTransferredInPrivate).request(), - bananaCoin.methods.transfer_to_private(aliceAddress, amountTransferredToPrivate).request(), + await bananaCoin.methods.transfer(bobAddress, amountTransferredInPrivate).request(), + await bananaCoin.methods.transfer_to_private(aliceAddress, amountTransferredToPrivate).request(), ]) .send({ fee: { diff --git a/yarn-project/end-to-end/src/e2e_keys.test.ts b/yarn-project/end-to-end/src/e2e_keys.test.ts index 8d854a07271..2603d0a3f9d 100644 --- a/yarn-project/end-to-end/src/e2e_keys.test.ts +++ b/yarn-project/end-to-end/src/e2e_keys.test.ts @@ -67,7 +67,7 @@ describe('Keys', () => { // need nsk_app and the contract address of the DeFi contract to detect the nullification of the initial note. it('nsk_app and contract address are enough to detect note nullification', async () => { const masterNullifierSecretKey = deriveMasterNullifierSecretKey(secret); - const nskApp = computeAppNullifierSecretKey(masterNullifierSecretKey, testContract.address); + const nskApp = await computeAppNullifierSecretKey(masterNullifierSecretKey, testContract.address); const noteValue = 5; const noteOwner = account.getAddress(); @@ -94,10 +94,12 @@ describe('Keys', () => { block.body.txEffects.flatMap(txEffect => txEffect.nullifiers), ); // 3. Derive all the possible nullifiers using nskApp - const derivedNullifiers = noteHashes.map(noteHash => { - const innerNullifier = poseidon2HashWithSeparator([noteHash, nskApp], GeneratorIndex.NOTE_NULLIFIER); - return siloNullifier(contractAddress, innerNullifier); - }); + const derivedNullifiers = await Promise.all( + noteHashes.map(async noteHash => { + const innerNullifier = await poseidon2HashWithSeparator([noteHash, nskApp], GeneratorIndex.NOTE_NULLIFIER); + return siloNullifier(contractAddress, innerNullifier); + }), + ); // 4. Count the number of derived nullifiers that are in the nullifiers array return derivedNullifiers.reduce((count, derived) => { if (nullifiers.some(nullifier => nullifier.equals(derived))) { @@ -112,10 +114,10 @@ describe('Keys', () => { it('gets ovsk_app', async () => { // Derive the ovpk_m_hash from the account secret const ovskM = deriveMasterOutgoingViewingSecretKey(secret); - const ovpkMHash = (await derivePublicKeyFromSecretKey(ovskM)).hash(); + const ovpkMHash = await (await derivePublicKeyFromSecretKey(ovskM)).hash(); // Compute the expected ovsk_app - const expectedOvskApp = computeAppSecretKey(ovskM, testContract.address, 'ov'); + const expectedOvskApp = await computeAppSecretKey(ovskM, testContract.address, 'ov'); // Get the ovsk_app via the test contract const ovskAppBigInt = await testContract.methods.get_ovsk_app(ovpkMHash).simulate(); diff --git a/yarn-project/end-to-end/src/e2e_lending_contract.test.ts b/yarn-project/end-to-end/src/e2e_lending_contract.test.ts index 0b64d5aac47..8e96bff8269 100644 --- a/yarn-project/end-to-end/src/e2e_lending_contract.test.ts +++ b/yarn-project/end-to-end/src/e2e_lending_contract.test.ts @@ -138,7 +138,7 @@ describe('e2e_lending_contract', () => { ), }); await lendingSim.progressSlots(SLOT_JUMP); - lendingSim.depositPrivate(lendingAccount.address, lendingAccount.key(), depositAmount); + lendingSim.depositPrivate(lendingAccount.address, await lendingAccount.key(), depositAmount); // Make a private deposit of funds into own account. // This should: @@ -199,21 +199,19 @@ describe('e2e_lending_contract', () => { const nonce = Fr.random(); // Add it to the wallet as approved - await wallet - .setPublicAuthWit( - { - caller: lendingContract.address, - action: collateralAsset.methods.transfer_in_public( - lendingAccount.address, - lendingContract.address, - depositAmount, - nonce, - ), - }, - true, - ) - .send() - .wait(); + const validateAction = await wallet.setPublicAuthWit( + { + caller: lendingContract.address, + action: collateralAsset.methods.transfer_in_public( + lendingAccount.address, + lendingContract.address, + depositAmount, + nonce, + ), + }, + true, + ); + await validateAction.send().wait(); await lendingSim.progressSlots(SLOT_JUMP); lendingSim.depositPublic(lendingAccount.address, lendingAccount.address.toField(), depositAmount); @@ -236,7 +234,7 @@ describe('e2e_lending_contract', () => { it('Borrow 🥸 : 🏦 -> 🍌', async () => { const borrowAmount = 69n; await lendingSim.progressSlots(SLOT_JUMP); - lendingSim.borrow(lendingAccount.key(), lendingAccount.address, borrowAmount); + lendingSim.borrow(await lendingAccount.key(), lendingAccount.address, borrowAmount); // Make a private borrow using the private account // This should: @@ -277,7 +275,7 @@ describe('e2e_lending_contract', () => { }); await lendingSim.progressSlots(SLOT_JUMP); - lendingSim.repayPrivate(lendingAccount.address, lendingAccount.key(), repayAmount); + lendingSim.repayPrivate(lendingAccount.address, await lendingAccount.key(), repayAmount); // Make a private repay of the debt in the private account // This should: @@ -321,16 +319,14 @@ describe('e2e_lending_contract', () => { const nonce = Fr.random(); // Add it to the wallet as approved - await wallet - .setPublicAuthWit( - { - caller: lendingContract.address, - action: stableCoin.methods.burn_public(lendingAccount.address, repayAmount, nonce).request(), - }, - true, - ) - .send() - .wait(); + const validateAction = await wallet.setPublicAuthWit( + { + caller: lendingContract.address, + action: stableCoin.methods.burn_public(lendingAccount.address, repayAmount, nonce), + }, + true, + ); + await validateAction.send().wait(); await lendingSim.progressSlots(SLOT_JUMP); lendingSim.repayPublic(lendingAccount.address, lendingAccount.address.toField(), repayAmount); @@ -368,7 +364,7 @@ describe('e2e_lending_contract', () => { it('Withdraw 🥸 : 🏦 -> 💰', async () => { const withdrawAmount = 42n; await lendingSim.progressSlots(SLOT_JUMP); - lendingSim.withdraw(lendingAccount.key(), lendingAccount.address, withdrawAmount); + lendingSim.withdraw(await lendingAccount.key(), lendingAccount.address, withdrawAmount); // Withdraw funds from the private account // This should: diff --git a/yarn-project/end-to-end/src/e2e_nested_contract/manual_private_call.test.ts b/yarn-project/end-to-end/src/e2e_nested_contract/manual_private_call.test.ts index 640345447d0..69fccd0d1f7 100644 --- a/yarn-project/end-to-end/src/e2e_nested_contract/manual_private_call.test.ts +++ b/yarn-project/end-to-end/src/e2e_nested_contract/manual_private_call.test.ts @@ -16,13 +16,16 @@ describe('e2e_nested_contract manual', () => { }); it('performs nested calls', async () => { - await parentContract.methods.entry_point(childContract.address, childContract.methods.value.selector).send().wait(); + await parentContract.methods + .entry_point(childContract.address, await childContract.methods.value.selector()) + .send() + .wait(); }); it('fails simulation if calling a function not allowed to be called externally', async () => { await expect( parentContract.methods - .entry_point(childContract.address, (childContract.methods as any).value_internal.selector) + .entry_point(childContract.address, await (childContract.methods as any).value_internal.selector()) .prove(), ).rejects.toThrow(/Assertion failed: Function value_internal can only be called internally/); }); diff --git a/yarn-project/end-to-end/src/e2e_nested_contract/manual_private_enqueue.test.ts b/yarn-project/end-to-end/src/e2e_nested_contract/manual_private_enqueue.test.ts index 57cd7354c6a..474ba12608e 100644 --- a/yarn-project/end-to-end/src/e2e_nested_contract/manual_private_enqueue.test.ts +++ b/yarn-project/end-to-end/src/e2e_nested_contract/manual_private_enqueue.test.ts @@ -28,7 +28,7 @@ describe('e2e_nested_contract manual_enqueue', () => { it('enqueues a single public call', async () => { await parentContract.methods - .enqueue_call_to_child(childContract.address, childContract.methods.pub_inc_value.selector, 42n) + .enqueue_call_to_child(childContract.address, await childContract.methods.pub_inc_value.selector(), 42n) .send() .wait(); expect(await getChildStoredValue(childContract)).toEqual(new Fr(42n)); @@ -39,7 +39,7 @@ describe('e2e_nested_contract manual_enqueue', () => { parentContract.methods .enqueue_call_to_child( childContract.address, - (childContract.methods as any).pub_inc_value_internal.selector, + await (childContract.methods as any).pub_inc_value_internal.selector(), 42n, ) .prove(), @@ -48,7 +48,7 @@ describe('e2e_nested_contract manual_enqueue', () => { it('enqueues multiple public calls', async () => { await parentContract.methods - .enqueue_call_to_child_twice(childContract.address, childContract.methods.pub_inc_value.selector, 42n) + .enqueue_call_to_child_twice(childContract.address, await childContract.methods.pub_inc_value.selector(), 42n) .send() .wait(); expect(await getChildStoredValue(childContract)).toEqual(new Fr(85n)); @@ -56,7 +56,7 @@ describe('e2e_nested_contract manual_enqueue', () => { it('enqueues a public call with nested public calls', async () => { await parentContract.methods - .enqueue_call_to_pub_entry_point(childContract.address, childContract.methods.pub_inc_value.selector, 42n) + .enqueue_call_to_pub_entry_point(childContract.address, await childContract.methods.pub_inc_value.selector(), 42n) .send() .wait(); expect(await getChildStoredValue(childContract)).toEqual(new Fr(42n)); @@ -64,7 +64,11 @@ describe('e2e_nested_contract manual_enqueue', () => { it('enqueues multiple public calls with nested public calls', async () => { await parentContract.methods - .enqueue_calls_to_pub_entry_point(childContract.address, childContract.methods.pub_inc_value.selector, 42n) + .enqueue_calls_to_pub_entry_point( + childContract.address, + await childContract.methods.pub_inc_value.selector(), + 42n, + ) .send() .wait(); expect(await getChildStoredValue(childContract)).toEqual(new Fr(85n)); diff --git a/yarn-project/end-to-end/src/e2e_nested_contract/manual_public.test.ts b/yarn-project/end-to-end/src/e2e_nested_contract/manual_public.test.ts index 69d71fb17fb..1ae18b2d21c 100644 --- a/yarn-project/end-to-end/src/e2e_nested_contract/manual_public.test.ts +++ b/yarn-project/end-to-end/src/e2e_nested_contract/manual_public.test.ts @@ -24,7 +24,7 @@ describe('e2e_nested_contract manual', () => { it('performs public nested calls', async () => { await parentContract.methods - .pub_entry_point(childContract.address, childContract.methods.pub_get_value.selector, 42n) + .pub_entry_point(childContract.address, await childContract.methods.pub_get_value.selector(), 42n) .send() .wait(); }); @@ -32,7 +32,7 @@ describe('e2e_nested_contract manual', () => { // Regression for https://github.com/AztecProtocol/aztec-packages/issues/640 it('reads fresh value after write within the same tx', async () => { await parentContract.methods - .pub_entry_point_twice(childContract.address, childContract.methods.pub_inc_value.selector, 42n) + .pub_entry_point_twice(childContract.address, await childContract.methods.pub_inc_value.selector(), 42n) .send() .wait(); expect(await getChildStoredValue(childContract)).toEqual(new Fr(84n)); @@ -43,10 +43,10 @@ describe('e2e_nested_contract manual', () => { // through the account contract, if the account entrypoint behaves properly, it will honor // this order and not run the private call first which results in the public calls being inverted. it('executes public calls in expected order', async () => { - const pubSetValueSelector = childContract.methods.pub_set_value.selector; + const pubSetValueSelector = await childContract.methods.pub_set_value.selector(); const actions = [ - childContract.methods.pub_set_value(20n).request(), - parentContract.methods.enqueue_call_to_child(childContract.address, pubSetValueSelector, 40n).request(), + await childContract.methods.pub_set_value(20n).request(), + await parentContract.methods.enqueue_call_to_child(childContract.address, pubSetValueSelector, 40n).request(), ]; const tx = await new BatchCall(wallet, actions).send().wait(); diff --git a/yarn-project/end-to-end/src/e2e_non_contract_account.test.ts b/yarn-project/end-to-end/src/e2e_non_contract_account.test.ts index db26a2cea68..bd03f77f0fb 100644 --- a/yarn-project/end-to-end/src/e2e_non_contract_account.test.ts +++ b/yarn-project/end-to-end/src/e2e_non_contract_account.test.ts @@ -35,7 +35,7 @@ describe('e2e_non_contract_account', () => { .send() .wait({ interval: 0.1, debug: true }); - const expectedSiloedNullifier = siloNullifier(contract.address, nullifier); + const expectedSiloedNullifier = await siloNullifier(contract.address, nullifier); expect(debugInfo?.nullifiers).toContainEqual(expectedSiloedNullifier); }); diff --git a/yarn-project/end-to-end/src/e2e_ordering.test.ts b/yarn-project/end-to-end/src/e2e_ordering.test.ts index da87ad57ddf..844ad2678cb 100644 --- a/yarn-project/end-to-end/src/e2e_ordering.test.ts +++ b/yarn-project/end-to-end/src/e2e_ordering.test.ts @@ -49,7 +49,7 @@ describe('e2e_ordering', () => { beforeEach(async () => { parent = await ParentContract.deploy(wallet).send().deployed(); child = await ChildContract.deploy(wallet).send().deployed(); - pubSetValueSelector = child.methods.pub_set_value.selector; + pubSetValueSelector = await child.methods.pub_set_value.selector(); }, TIMEOUT); describe('enqueued public calls ordering', () => { @@ -75,8 +75,13 @@ describe('e2e_ordering', () => { expect(enqueuedPublicCalls.length).toEqual(2); // The call stack items in the output of the kernel proof match the tx enqueuedPublicFunctionCalls - enqueuedPublicCalls.forEach((c, i) => { - expect(c.isForCallRequest(tx.data.forPublic!.revertibleAccumulatedData.publicCallRequests[i])).toBe(true); + const areForCallRequests = await Promise.all( + enqueuedPublicCalls.map((c, i) => + c.isForCallRequest(tx.data.forPublic!.revertibleAccumulatedData.publicCallRequests[i]), + ), + ); + areForCallRequests.forEach(isForCallRequest => { + expect(isForCallRequest).toBe(true); }); // The enqueued public calls are in the expected order based on the argument they set (stack is reversed!) diff --git a/yarn-project/end-to-end/src/e2e_outbox.test.ts b/yarn-project/end-to-end/src/e2e_outbox.test.ts index cf910a1b8a3..e00ad668124 100644 --- a/yarn-project/end-to-end/src/e2e_outbox.test.ts +++ b/yarn-project/end-to-end/src/e2e_outbox.test.ts @@ -56,8 +56,8 @@ describe('E2E Outbox Tests', () => { ]; const call = new BatchCall(wallets[0], [ - contract.methods.create_l2_to_l1_message_arbitrary_recipient_private(content1, recipient1).request(), - contract.methods.create_l2_to_l1_message_arbitrary_recipient_private(content2, recipient2).request(), + await contract.methods.create_l2_to_l1_message_arbitrary_recipient_private(content1, recipient1).request(), + await contract.methods.create_l2_to_l1_message_arbitrary_recipient_private(content2, recipient2).request(), ]); // TODO (#5104): When able to guarantee multiple txs in a single block, make this populate a full tree. Right now we are @@ -176,9 +176,9 @@ describe('E2E Outbox Tests', () => { ]; const call0 = new BatchCall(wallets[0], [ - contract.methods.create_l2_to_l1_message_arbitrary_recipient_private(content1, recipient1).request(), - contract.methods.create_l2_to_l1_message_arbitrary_recipient_private(content2, recipient2).request(), - contract.methods.create_l2_to_l1_message_arbitrary_recipient_private(content3, recipient3).request(), + await contract.methods.create_l2_to_l1_message_arbitrary_recipient_private(content1, recipient1).request(), + await contract.methods.create_l2_to_l1_message_arbitrary_recipient_private(content2, recipient2).request(), + await contract.methods.create_l2_to_l1_message_arbitrary_recipient_private(content3, recipient3).request(), ]); const call1 = contract.methods.create_l2_to_l1_message_arbitrary_recipient_private(content4, recipient4); diff --git a/yarn-project/end-to-end/src/e2e_p2p/reex.test.ts b/yarn-project/end-to-end/src/e2e_p2p/reex.test.ts index d9bf0b8a8ce..04520e3fa71 100644 --- a/yarn-project/end-to-end/src/e2e_p2p/reex.test.ts +++ b/yarn-project/end-to-end/src/e2e_p2p/reex.test.ts @@ -130,7 +130,9 @@ describe('e2e_p2p_reex', () => { const signer = (node as any).sequencer.sequencer.validatorClient.validationService.keyStore; const newProposal = new BlockProposal( proposal.payload, - await signer.signMessage(getHashedSignaturePayload(proposal.payload, SignatureDomainSeparator.blockProposal)), + await signer.signMessage( + await getHashedSignaturePayload(proposal.payload, SignatureDomainSeparator.blockProposal), + ), ); return (node as any).p2pClient.p2pService.propagate(newProposal); @@ -155,8 +157,8 @@ describe('e2e_p2p_reex', () => { const originalSimulate = simulator.simulate.bind(simulator); // We only stub the simulate method if it's NOT the first time we see the tx // so the proposer works fine, but we cause the failure in the validators. - jest.spyOn(simulator, 'simulate').mockImplementation((tx: Tx) => { - const txHash = tx.getTxHash().toString(); + jest.spyOn(simulator, 'simulate').mockImplementation(async (tx: Tx) => { + const txHash = (await tx.getTxHash()).toString(); if (seenTxs.has(txHash)) { t.logger.warn('Calling stubbed simulate for tx', { txHash }); return stub(tx, originalSimulate); diff --git a/yarn-project/end-to-end/src/e2e_pending_note_hashes_contract.test.ts b/yarn-project/end-to-end/src/e2e_pending_note_hashes_contract.test.ts index ebda8f38f1f..639bc7a2475 100644 --- a/yarn-project/end-to-end/src/e2e_pending_note_hashes_contract.test.ts +++ b/yarn-project/end-to-end/src/e2e_pending_note_hashes_contract.test.ts @@ -93,8 +93,8 @@ describe('e2e_pending_note_hashes_contract', () => { mintAmount, owner, sender, - deployedContract.methods.insert_note.selector, - deployedContract.methods.get_then_nullify_note.selector, + await deployedContract.methods.insert_note.selector(), + await deployedContract.methods.get_then_nullify_note.selector(), ) .send() .wait(); @@ -118,8 +118,8 @@ describe('e2e_pending_note_hashes_contract', () => { mintAmount, owner, sender, - deployedContract.methods.insert_note_extra_emit.selector, - deployedContract.methods.get_then_nullify_note.selector, + await deployedContract.methods.insert_note_extra_emit.selector(), + await deployedContract.methods.get_then_nullify_note.selector(), ) .send() .wait(); @@ -142,8 +142,8 @@ describe('e2e_pending_note_hashes_contract', () => { mintAmount, owner, sender, - deployedContract.methods.insert_note.selector, - deployedContract.methods.get_then_nullify_note.selector, + await deployedContract.methods.insert_note.selector(), + await deployedContract.methods.get_then_nullify_note.selector(), ) .send() .wait(); @@ -167,8 +167,8 @@ describe('e2e_pending_note_hashes_contract', () => { mintAmount, owner, sender, - deployedContract.methods.insert_note.selector, - deployedContract.methods.get_then_nullify_note.selector, + await deployedContract.methods.insert_note.selector(), + await deployedContract.methods.get_then_nullify_note.selector(), ) .send() .wait(); @@ -192,8 +192,8 @@ describe('e2e_pending_note_hashes_contract', () => { mintAmount, owner, sender, - deployedContract.methods.insert_note_static_randomness.selector, - deployedContract.methods.get_then_nullify_note.selector, + await deployedContract.methods.insert_note_static_randomness.selector(), + await deployedContract.methods.get_then_nullify_note.selector(), ) .send() .wait(); @@ -227,8 +227,8 @@ describe('e2e_pending_note_hashes_contract', () => { mintAmount, owner, sender, - deployedContract.methods.insert_note.selector, - deployedContract.methods.get_then_nullify_note.selector, + await deployedContract.methods.insert_note.selector(), + await deployedContract.methods.get_then_nullify_note.selector(), ) .send() .wait(); @@ -264,8 +264,8 @@ describe('e2e_pending_note_hashes_contract', () => { mintAmount, owner, sender, - deployedContract.methods.dummy.selector, - deployedContract.methods.get_then_nullify_note.selector, + await deployedContract.methods.dummy.selector(), + await deployedContract.methods.get_then_nullify_note.selector(), ) .send() .wait(); diff --git a/yarn-project/end-to-end/src/e2e_state_vars.test.ts b/yarn-project/end-to-end/src/e2e_state_vars.test.ts index a32d0985945..6dd4d178ff3 100644 --- a/yarn-project/end-to-end/src/e2e_state_vars.test.ts +++ b/yarn-project/end-to-end/src/e2e_state_vars.test.ts @@ -53,9 +53,9 @@ describe('e2e_state_vars', () => { // The indirect, adds 1 to the point to ensure that we are returning the correct value. const [a, b, c] = await new BatchCall(wallet, [ - contract.methods.get_public_immutable_constrained_private().request(), - contract.methods.get_public_immutable_constrained_private_indirect().request(), - contract.methods.get_public_immutable().request(), + await contract.methods.get_public_immutable_constrained_private().request(), + await contract.methods.get_public_immutable_constrained_private_indirect().request(), + await contract.methods.get_public_immutable().request(), ]).simulate(); expect(a).toEqual(c); @@ -70,9 +70,9 @@ describe('e2e_state_vars', () => { // The indirect, adds 1 to the point to ensure that we are returning the correct value. const [a, b, c] = await new BatchCall(wallet, [ - contract.methods.get_public_immutable_constrained_public().request(), - contract.methods.get_public_immutable_constrained_public_indirect().request(), - contract.methods.get_public_immutable().request(), + await contract.methods.get_public_immutable_constrained_public().request(), + await contract.methods.get_public_immutable_constrained_public_indirect().request(), + await contract.methods.get_public_immutable().request(), ]).simulate(); expect(a).toEqual(c); diff --git a/yarn-project/end-to-end/src/e2e_static_calls.test.ts b/yarn-project/end-to-end/src/e2e_static_calls.test.ts index b8d806de1c0..1934479fac6 100644 --- a/yarn-project/end-to-end/src/e2e_static_calls.test.ts +++ b/yarn-project/end-to-end/src/e2e_static_calls.test.ts @@ -51,7 +51,10 @@ describe('e2e_static_calls', () => { it('performs legal private to private static calls', async () => { // Using low level calls await parentContract.methods - .private_static_call(childContract.address, childContract.methods.private_get_value.selector, [42n, owner]) + .private_static_call(childContract.address, await childContract.methods.private_get_value.selector(), [ + 42n, + owner, + ]) .send() .wait(); @@ -61,7 +64,7 @@ describe('e2e_static_calls', () => { it('performs legal (nested) private to private static calls', async () => { await parentContract.methods - .private_nested_static_call(childContract.address, childContract.methods.private_get_value.selector, [ + .private_nested_static_call(childContract.address, await childContract.methods.private_get_value.selector(), [ 42n, owner, ]) @@ -72,7 +75,7 @@ describe('e2e_static_calls', () => { it('performs legal public to public static calls', async () => { // Using low level calls await parentContract.methods - .public_static_call(childContract.address, childContract.methods.pub_get_value.selector, [42n]) + .public_static_call(childContract.address, await childContract.methods.pub_get_value.selector(), [42n]) .send() .wait(); @@ -82,7 +85,7 @@ describe('e2e_static_calls', () => { it('performs legal (nested) public to public static calls', async () => { await parentContract.methods - .public_nested_static_call(childContract.address, childContract.methods.pub_get_value.selector, [42n]) + .public_nested_static_call(childContract.address, await childContract.methods.pub_get_value.selector(), [42n]) .send() .wait(); }); @@ -90,7 +93,11 @@ describe('e2e_static_calls', () => { it('performs legal enqueued public static calls', async () => { // Using low level calls await parentContract.methods - .enqueue_static_call_to_pub_function(childContract.address, childContract.methods.pub_get_value.selector, [42n]) + .enqueue_static_call_to_pub_function( + childContract.address, + await childContract.methods.pub_get_value.selector(), + [42n], + ) .send() .wait(); @@ -102,7 +109,7 @@ describe('e2e_static_calls', () => { await parentContract.methods .enqueue_static_nested_call_to_pub_function( childContract.address, - childContract.methods.pub_get_value.selector, + await childContract.methods.pub_get_value.selector(), [42n], ) .send() @@ -112,7 +119,7 @@ describe('e2e_static_calls', () => { it('fails when performing illegal private to private static calls', async () => { await expect( parentContract.methods - .private_static_call_3_args(childContract.address, childContract.methods.private_set_value.selector, [ + .private_static_call_3_args(childContract.address, await childContract.methods.private_set_value.selector(), [ 42n, owner, sender, @@ -125,7 +132,10 @@ describe('e2e_static_calls', () => { it('fails when performing non-static calls to poorly written private static functions', async () => { await expect( parentContract.methods - .private_call(childContract.address, childContract.methods.private_illegal_set_value.selector, [42n, owner]) + .private_call(childContract.address, await childContract.methods.private_illegal_set_value.selector(), [ + 42n, + owner, + ]) .send() .wait(), ).rejects.toThrow(STATIC_CONTEXT_ASSERTION_ERROR); @@ -134,11 +144,11 @@ describe('e2e_static_calls', () => { it('fails when performing illegal (nested) private to private static calls', async () => { await expect( parentContract.methods - .private_nested_static_call_3_args(childContract.address, childContract.methods.private_set_value.selector, [ - 42n, - owner, - sender, - ]) + .private_nested_static_call_3_args( + childContract.address, + await childContract.methods.private_set_value.selector(), + [42n, owner, sender], + ) .send() .wait(), ).rejects.toThrow(STATIC_CALL_STATE_MODIFICATION_ERROR); @@ -147,7 +157,7 @@ describe('e2e_static_calls', () => { it('fails when performing illegal public to public static calls', async () => { await expect( parentContract.methods - .public_static_call(childContract.address, childContract.methods.pub_set_value.selector, [42n]) + .public_static_call(childContract.address, await childContract.methods.pub_set_value.selector(), [42n]) .send() .wait(), ).rejects.toThrow(STATIC_CALL_STATE_MODIFICATION_ERROR); @@ -156,7 +166,7 @@ describe('e2e_static_calls', () => { it('fails when performing illegal (nested) public to public static calls', async () => { await expect( parentContract.methods - .public_nested_static_call(childContract.address, childContract.methods.pub_set_value.selector, [42n]) + .public_nested_static_call(childContract.address, await childContract.methods.pub_set_value.selector(), [42n]) .send() .wait(), ).rejects.toThrow(STATIC_CALL_STATE_MODIFICATION_ERROR); @@ -165,9 +175,11 @@ describe('e2e_static_calls', () => { it('fails when performing illegal enqueued public static calls', async () => { await expect( parentContract.methods - .enqueue_static_call_to_pub_function(childContract.address, childContract.methods.pub_set_value.selector, [ - 42n, - ]) + .enqueue_static_call_to_pub_function( + childContract.address, + await childContract.methods.pub_set_value.selector(), + [42n], + ) .send() .wait(), ).rejects.toThrow(STATIC_CALL_STATE_MODIFICATION_ERROR); @@ -178,7 +190,7 @@ describe('e2e_static_calls', () => { parentContract.methods .enqueue_static_nested_call_to_pub_function( childContract.address, - childContract.methods.pub_set_value.selector, + await childContract.methods.pub_set_value.selector(), [42n], ) .send() @@ -189,7 +201,10 @@ describe('e2e_static_calls', () => { it('fails when performing non-static enqueue calls to poorly written public static functions', async () => { await expect( parentContract.methods - .enqueue_call(childContract.address, childContract.methods.pub_illegal_inc_value.selector, [42n, owner]) + .enqueue_call(childContract.address, await childContract.methods.pub_illegal_inc_value.selector(), [ + 42n, + owner, + ]) .send() .wait(), ).rejects.toThrow(STATIC_CONTEXT_ASSERTION_ERROR); diff --git a/yarn-project/end-to-end/src/e2e_synching.test.ts b/yarn-project/end-to-end/src/e2e_synching.test.ts index 4eaa8430317..25714248b05 100644 --- a/yarn-project/end-to-end/src/e2e_synching.test.ts +++ b/yarn-project/end-to-end/src/e2e_synching.test.ts @@ -231,10 +231,10 @@ class TestVariant { const txs = []; for (let i = 0; i < this.txCount; i++) { const batch = new BatchCall(this.wallets[i], [ - this.spam.methods.spam(this.seed, 16, false).request(), - this.spam.methods.spam(this.seed + 16n, 16, false).request(), - this.spam.methods.spam(this.seed + 32n, 16, false).request(), - this.spam.methods.spam(this.seed + 48n, 15, true).request(), + await this.spam.methods.spam(this.seed, 16, false).request(), + await this.spam.methods.spam(this.seed + 16n, 16, false).request(), + await this.spam.methods.spam(this.seed + 32n, 16, false).request(), + await this.spam.methods.spam(this.seed + 48n, 15, true).request(), ]); this.seed += 100n; @@ -332,7 +332,7 @@ describe('e2e_synching', () => { // Now we create all of our interesting blocks. // Alter the block requirements for the sequencer such that we ensure blocks sizes as desired. - sequencer?.updateSequencerConfig({ minTxsPerBlock: variant.txCount, maxTxsPerBlock: variant.txCount }); + await sequencer?.updateSequencerConfig({ minTxsPerBlock: variant.txCount, maxTxsPerBlock: variant.txCount }); // The setup will mint tokens (private and public) await variant.setup(); @@ -631,7 +631,7 @@ describe('e2e_synching', () => { const blockBefore = await aztecNode.getBlock(await aztecNode.getBlockNumber()); - sequencer?.updateSequencerConfig({ minTxsPerBlock: variant.txCount, maxTxsPerBlock: variant.txCount }); + await sequencer?.updateSequencerConfig({ minTxsPerBlock: variant.txCount, maxTxsPerBlock: variant.txCount }); const txs = await variant.createAndSendTxs(); await Promise.all(txs.map(tx => tx.wait({ timeout: 1200 }))); @@ -690,7 +690,7 @@ describe('e2e_synching', () => { const blockBefore = await aztecNode.getBlock(await aztecNode.getBlockNumber()); - sequencer?.updateSequencerConfig({ minTxsPerBlock: variant.txCount, maxTxsPerBlock: variant.txCount }); + await sequencer?.updateSequencerConfig({ minTxsPerBlock: variant.txCount, maxTxsPerBlock: variant.txCount }); const txs = await variant.createAndSendTxs(); await Promise.all(txs.map(tx => tx.wait({ timeout: 1200 }))); diff --git a/yarn-project/end-to-end/src/e2e_token_contract/burn.test.ts b/yarn-project/end-to-end/src/e2e_token_contract/burn.test.ts index 0b7b10306bf..54dd8586907 100644 --- a/yarn-project/end-to-end/src/e2e_token_contract/burn.test.ts +++ b/yarn-project/end-to-end/src/e2e_token_contract/burn.test.ts @@ -41,7 +41,11 @@ describe('e2e_token_contract burn', () => { // We need to compute the message we want to sign and add it to the wallet as approved const action = asset.withWallet(wallets[1]).methods.burn_public(accounts[0].address, amount, nonce); - await wallets[0].setPublicAuthWit({ caller: accounts[1].address, action }, true).send().wait(); + const validateActionInteraction = await wallets[0].setPublicAuthWit( + { caller: accounts[1].address, action }, + true, + ); + await validateActionInteraction.send().wait(); await action.send().wait(); @@ -89,7 +93,11 @@ describe('e2e_token_contract burn', () => { // We need to compute the message we want to sign and add it to the wallet as approved const action = asset.withWallet(wallets[1]).methods.burn_public(accounts[0].address, amount, nonce); - await wallets[0].setPublicAuthWit({ caller: accounts[1].address, action }, true).send().wait(); + const validateActionInteraction = await wallets[0].setPublicAuthWit( + { caller: accounts[1].address, action }, + true, + ); + await validateActionInteraction.send().wait(); await expect(action.simulate()).rejects.toThrow(U128_UNDERFLOW_ERROR); }); @@ -102,7 +110,11 @@ describe('e2e_token_contract burn', () => { // We need to compute the message we want to sign and add it to the wallet as approved const action = asset.withWallet(wallets[1]).methods.burn_public(accounts[0].address, amount, nonce); - await wallets[0].setPublicAuthWit({ caller: accounts[0].address, action }, true).send().wait(); + const validateActionInteraction = await wallets[0].setPublicAuthWit( + { caller: accounts[0].address, action }, + true, + ); + await validateActionInteraction.send().wait(); await expect( asset.withWallet(wallets[1]).methods.burn_public(accounts[0].address, amount, nonce).simulate(), @@ -189,8 +201,8 @@ describe('e2e_token_contract burn', () => { // We need to compute the message we want to sign and add it to the wallet as approved const action = asset.withWallet(wallets[1]).methods.burn_private(accounts[0].address, amount, nonce); - const messageHash = computeAuthWitMessageHash( - { caller: accounts[1].address, action: action.request() }, + const messageHash = await computeAuthWitMessageHash( + { caller: accounts[1].address, action }, { chainId: wallets[0].getChainId(), version: wallets[0].getVersion() }, ); @@ -210,8 +222,8 @@ describe('e2e_token_contract burn', () => { // We need to compute the message we want to sign and add it to the wallet as approved const action = asset.withWallet(wallets[2]).methods.burn_private(accounts[0].address, amount, nonce); - const expectedMessageHash = computeAuthWitMessageHash( - { caller: accounts[2].address, action: action.request() }, + const expectedMessageHash = await computeAuthWitMessageHash( + { caller: accounts[2].address, action }, { chainId: wallets[0].getChainId(), version: wallets[0].getVersion() }, ); diff --git a/yarn-project/end-to-end/src/e2e_token_contract/private_transfer_recursion.test.ts b/yarn-project/end-to-end/src/e2e_token_contract/private_transfer_recursion.test.ts index b23c219d915..100cf7e414c 100644 --- a/yarn-project/end-to-end/src/e2e_token_contract/private_transfer_recursion.test.ts +++ b/yarn-project/end-to-end/src/e2e_token_contract/private_transfer_recursion.test.ts @@ -24,7 +24,9 @@ describe('e2e_token_contract private transfer recursion', () => { for (let mintedNotes = 0; mintedNotes < noteAmounts.length; mintedNotes += notesPerIteration) { const toMint = noteAmounts.slice(mintedNotes, mintedNotes + notesPerIteration); const from = wallets[0].getAddress(); // we are setting from to sender here because of TODO(#9887) - const actions = toMint.map(amt => asset.methods.mint_to_private(from, wallets[0].getAddress(), amt).request()); + const actions = await Promise.all( + toMint.map(amt => asset.methods.mint_to_private(from, wallets[0].getAddress(), amt).request()), + ); await new BatchCall(wallets[0], actions).send().wait(); } diff --git a/yarn-project/end-to-end/src/e2e_token_contract/transfer_in_private.test.ts b/yarn-project/end-to-end/src/e2e_token_contract/transfer_in_private.test.ts index 7b1aedfef2f..79c7cb04148 100644 --- a/yarn-project/end-to-end/src/e2e_token_contract/transfer_in_private.test.ts +++ b/yarn-project/end-to-end/src/e2e_token_contract/transfer_in_private.test.ts @@ -108,8 +108,8 @@ describe('e2e_token_contract transfer private', () => { const action = asset .withWallet(wallets[1]) .methods.transfer_in_private(accounts[0].address, accounts[1].address, amount, nonce); - const messageHash = computeAuthWitMessageHash( - { caller: accounts[1].address, action: action.request() }, + const messageHash = await computeAuthWitMessageHash( + { caller: accounts[1].address, action }, { chainId: wallets[0].getChainId(), version: wallets[0].getVersion(), @@ -131,8 +131,8 @@ describe('e2e_token_contract transfer private', () => { const action = asset .withWallet(wallets[2]) .methods.transfer_in_private(accounts[0].address, accounts[1].address, amount, nonce); - const expectedMessageHash = computeAuthWitMessageHash( - { caller: accounts[2].address, action: action.request() }, + const expectedMessageHash = await computeAuthWitMessageHash( + { caller: accounts[2].address, action }, { chainId: wallets[0].getChainId(), version: wallets[0].getVersion(), @@ -172,7 +172,7 @@ describe('e2e_token_contract transfer private', () => { isValidInPublic: false, }); - const innerHash = computeInnerAuthWitHashFromAction(accounts[1].address, action.request()); + const innerHash = await computeInnerAuthWitHashFromAction(accounts[1].address, await action.request()); await asset.withWallet(wallets[0]).methods.cancel_authwit(innerHash).send().wait(); expect(await wallets[0].lookupValidity(wallets[0].getAddress(), intent)).toEqual({ diff --git a/yarn-project/end-to-end/src/e2e_token_contract/transfer_in_public.test.ts b/yarn-project/end-to-end/src/e2e_token_contract/transfer_in_public.test.ts index b0440880ecb..82d6586490e 100644 --- a/yarn-project/end-to-end/src/e2e_token_contract/transfer_in_public.test.ts +++ b/yarn-project/end-to-end/src/e2e_token_contract/transfer_in_public.test.ts @@ -71,7 +71,8 @@ describe('e2e_token_contract transfer public', () => { .withWallet(wallets[1]) .methods.transfer_in_public(accounts[0].address, accounts[1].address, amount, nonce); - await wallets[0].setPublicAuthWit({ caller: accounts[1].address, action }, true).send().wait(); + const validateActionInteraction = await wallets[0].setPublicAuthWit({ caller: accounts[1].address, action }, true); + await validateActionInteraction.send().wait(); // docs:end:authwit_public_transfer_example // Perform the transfer @@ -138,7 +139,11 @@ describe('e2e_token_contract transfer public', () => { ); // We need to compute the message we want to sign and add it to the wallet as approved - await wallets[0].setPublicAuthWit({ caller: accounts[1].address, action }, true).send().wait(); + const validateActionInteraction = await wallets[0].setPublicAuthWit( + { caller: accounts[1].address, action }, + true, + ); + await validateActionInteraction.send().wait(); expect(await wallets[0].lookupValidity(wallets[0].getAddress(), { caller: accounts[1].address, action })).toEqual( { @@ -166,7 +171,11 @@ describe('e2e_token_contract transfer public', () => { .withWallet(wallets[1]) .methods.transfer_in_public(accounts[0].address, accounts[1].address, amount, nonce); - await wallets[0].setPublicAuthWit({ caller: accounts[0].address, action }, true).send().wait(); + const validateActionInteraction = await wallets[0].setPublicAuthWit( + { caller: accounts[0].address, action }, + true, + ); + await validateActionInteraction.send().wait(); // Perform the transfer await expect(action.simulate()).rejects.toThrow(/unauthorized/); @@ -186,7 +195,11 @@ describe('e2e_token_contract transfer public', () => { const action = asset .withWallet(wallets[1]) .methods.transfer_in_public(accounts[0].address, accounts[1].address, amount, nonce); - await wallets[0].setPublicAuthWit({ caller: accounts[0].address, action }, true).send().wait(); + const validateActionInteraction = await wallets[0].setPublicAuthWit( + { caller: accounts[0].address, action }, + true, + ); + await validateActionInteraction.send().wait(); // Perform the transfer await expect(action.simulate()).rejects.toThrow(/unauthorized/); @@ -205,9 +218,14 @@ describe('e2e_token_contract transfer public', () => { .withWallet(wallets[1]) .methods.transfer_in_public(accounts[0].address, accounts[1].address, amount, nonce); - await wallets[0].setPublicAuthWit({ caller: accounts[1].address, action }, true).send().wait(); + const validateActionInteraction = await wallets[0].setPublicAuthWit( + { caller: accounts[1].address, action }, + true, + ); + await validateActionInteraction.send().wait(); - await wallets[0].setPublicAuthWit({ caller: accounts[1].address, action }, false).send().wait(); + const cancelActionInteraction = await wallets[0].setPublicAuthWit({ caller: accounts[1].address, action }, false); + await cancelActionInteraction.send().wait(); await expect( asset @@ -227,9 +245,14 @@ describe('e2e_token_contract transfer public', () => { .withWallet(wallets[1]) .methods.transfer_in_public(accounts[0].address, accounts[1].address, amount, nonce); - await wallets[0].setPublicAuthWit({ caller: accounts[1].address, action }, true).send().wait(); + const validateActionInteraction = await wallets[0].setPublicAuthWit( + { caller: accounts[1].address, action }, + true, + ); + await validateActionInteraction.send().wait(); - await wallets[0].setPublicAuthWit({ caller: accounts[1].address, action }, false).send().wait(); + const cancelActionInteraction = await wallets[0].setPublicAuthWit({ caller: accounts[1].address, action }, false); + await cancelActionInteraction.send().wait(); await expect(action.simulate()).rejects.toThrow(/unauthorized/); }); diff --git a/yarn-project/end-to-end/src/e2e_token_contract/transfer_to_public.test.ts b/yarn-project/end-to-end/src/e2e_token_contract/transfer_to_public.test.ts index dbecfab9212..4525d502534 100644 --- a/yarn-project/end-to-end/src/e2e_token_contract/transfer_to_public.test.ts +++ b/yarn-project/end-to-end/src/e2e_token_contract/transfer_to_public.test.ts @@ -113,7 +113,7 @@ describe('e2e_token_contract transfer_to_public', () => { const action = asset .withWallet(wallets[2]) .methods.transfer_to_public(accounts[0].address, accounts[1].address, amount, nonce); - const expectedMessageHash = computeAuthWitMessageHash( + const expectedMessageHash = await computeAuthWitMessageHash( { caller: accounts[2].address, action }, { chainId: wallets[0].getChainId(), version: wallets[0].getVersion() }, ); diff --git a/yarn-project/end-to-end/src/fixtures/setup_l1_contracts.ts b/yarn-project/end-to-end/src/fixtures/setup_l1_contracts.ts index 0a90dbcc2e1..e918fae40a3 100644 --- a/yarn-project/end-to-end/src/fixtures/setup_l1_contracts.ts +++ b/yarn-project/end-to-end/src/fixtures/setup_l1_contracts.ts @@ -16,7 +16,7 @@ export const setupL1Contracts = async ( ) => { const l1Data = await deployL1Contracts(l1RpcUrl, account, foundry, logger, { l2FeeJuiceAddress: ProtocolContractAddress.FeeJuice, - vkTreeRoot: getVKTreeRoot(), + vkTreeRoot: await getVKTreeRoot(), protocolContractTreeRoot, salt: undefined, ...args, diff --git a/yarn-project/end-to-end/src/fixtures/snapshot_manager.ts b/yarn-project/end-to-end/src/fixtures/snapshot_manager.ts index d6fc0635f03..257e4184580 100644 --- a/yarn-project/end-to-end/src/fixtures/snapshot_manager.ts +++ b/yarn-project/end-to-end/src/fixtures/snapshot_manager.ts @@ -577,7 +577,7 @@ export const addAccounts = logger.verbose('Account deployment tx hashes:'); for (const provenTx of provenTxs) { - logger.verbose(provenTx.getTxHash().toString()); + logger.verbose((await provenTx.getTxHash()).toString()); } logger.verbose('Deploying accounts...'); @@ -601,12 +601,13 @@ export async function publicDeployAccounts( const accountAddressesToDeploy = accountsToDeploy.map(a => ('address' in a ? a.address : a)); const instances = await Promise.all(accountAddressesToDeploy.map(account => sender.getContractInstance(account))); - const contractClass = getContractClassFromArtifact(SchnorrAccountContractArtifact); + const contractClass = await getContractClassFromArtifact(SchnorrAccountContractArtifact); const alreadyRegistered = await sender.isContractClassPubliclyRegistered(contractClass.id); const calls: FunctionCall[] = []; if (!alreadyRegistered) { - calls.push((await registerContractClass(sender, SchnorrAccountContractArtifact)).request()); + const registerContractCall = await registerContractClass(sender, SchnorrAccountContractArtifact); + calls.push(await registerContractCall.request()); } const requests = await Promise.all( instances.map(async instance => (await deployInstance(sender, instance!)).request()), diff --git a/yarn-project/end-to-end/src/fixtures/utils.ts b/yarn-project/end-to-end/src/fixtures/utils.ts index 26af6f38cb5..4205abeaa22 100644 --- a/yarn-project/end-to-end/src/fixtures/utils.ts +++ b/yarn-project/end-to-end/src/fixtures/utils.ts @@ -116,7 +116,7 @@ export const setupL1Contracts = async ( ) => { const l1Data = await deployL1Contracts(l1RpcUrl, account, chain, logger, { l2FeeJuiceAddress: ProtocolContractAddress.FeeJuice, - vkTreeRoot: getVKTreeRoot(), + vkTreeRoot: await getVKTreeRoot(), protocolContractTreeRoot, salt: args.salt, initialValidators: args.initialValidators, @@ -586,7 +586,7 @@ export async function ensureAccountsPubliclyDeployed(sender: Wallet, accountsToD const instances = await Promise.all( accountsAndAddresses.filter(({ deployed }) => !deployed).map(({ address }) => sender.getContractInstance(address)), ); - const contractClass = getContractClassFromArtifact(SchnorrAccountContractArtifact); + const contractClass = await getContractClassFromArtifact(SchnorrAccountContractArtifact); if (!(await sender.isContractClassPubliclyRegistered(contractClass.id))) { await (await registerContractClass(sender, SchnorrAccountContractArtifact)).send().wait(); } diff --git a/yarn-project/end-to-end/src/guides/dapp_testing.test.ts b/yarn-project/end-to-end/src/guides/dapp_testing.test.ts index dec7ebd5d22..e4869366d13 100644 --- a/yarn-project/end-to-end/src/guides/dapp_testing.test.ts +++ b/yarn-project/end-to-end/src/guides/dapp_testing.test.ts @@ -100,7 +100,7 @@ describe('guides/dapp/testing', () => { // docs:start:calc-slot cheats = await CheatCodes.create(ETHEREUM_HOST, pxe); // The balances mapping is indexed by user address - ownerSlot = cheats.aztec.computeSlotInMap(TokenContract.storage.balances.slot, ownerAddress); + ownerSlot = await cheats.aztec.computeSlotInMap(TokenContract.storage.balances.slot, ownerAddress); // docs:end:calc-slot }); @@ -122,7 +122,7 @@ describe('guides/dapp/testing', () => { it('checks public storage', async () => { // docs:start:public-storage await token.methods.mint_to_public(owner.getAddress(), 100n).send().wait(); - const ownerPublicBalanceSlot = cheats.aztec.computeSlotInMap( + const ownerPublicBalanceSlot = await cheats.aztec.computeSlotInMap( TokenContract.storage.public_balances.slot, owner.getAddress(), ); @@ -183,7 +183,7 @@ describe('guides/dapp/testing', () => { const call = token.methods.transfer_in_public(owner.getAddress(), recipient.getAddress(), 1000n, 0); const receipt = await call.send({ skipPublicSimulation: true }).wait({ dontThrowOnRevert: true }); expect(receipt.status).toEqual(TxStatus.APP_LOGIC_REVERTED); - const ownerPublicBalanceSlot = cheats.aztec.computeSlotInMap( + const ownerPublicBalanceSlot = await cheats.aztec.computeSlotInMap( TokenContract.storage.public_balances.slot, owner.getAddress(), ); diff --git a/yarn-project/end-to-end/src/shared/browser.ts b/yarn-project/end-to-end/src/shared/browser.ts index 3f3be722344..070e8bc1d77 100644 --- a/yarn-project/end-to-end/src/shared/browser.ts +++ b/yarn-project/end-to-end/src/shared/browser.ts @@ -8,9 +8,6 @@ import { contractArtifactToBuffer } from '@aztec/types/abi'; import getPort from 'get-port'; import { type Server } from 'http'; -import Koa from 'koa'; -import serve from 'koa-static'; -import path, { dirname } from 'path'; import { type Browser, type Page, launch } from 'puppeteer-core'; declare global { @@ -28,9 +25,6 @@ declare global { } } -const __filename = AztecJs.fileURLToPath(import.meta.url); -const __dirname = dirname(__filename); - const privKey = AztecJs.GrumpkinScalar.random(); export const browserTestSuite = ( @@ -60,7 +54,6 @@ export const browserTestSuite = ( let contractAddress: AztecJs.AztecAddress; - let app: Koa; let testClient: AztecJs.PXE; let server: Server; let webServerURL: string; @@ -75,9 +68,6 @@ export const browserTestSuite = ( testClient = AztecJs.createPXEClient(pxeURL); await AztecJs.waitForPXE(testClient); - app = new Koa(); - app.use(serve(path.resolve(__dirname, './web'))); - const debuggingPort = await getPort({ port: 9222 }); browser = await launch({ executablePath: process.env.CHROME_BIN, @@ -99,6 +89,7 @@ export const browserTestSuite = ( page.on('pageerror', err => { pageLogger.error(`Error on web page`, err); }); + await page.goto(`${webServerURL}/index.html`); while (!(await page.evaluate(() => !!window.AztecJs))) { pageLogger.verbose('Waiting for window.AztecJs...'); @@ -146,8 +137,8 @@ export const browserTestSuite = ( }); it('Can access CompleteAddress class in browser', async () => { - const result: string = await page.evaluate(() => { - const completeAddress = window.AztecJs.CompleteAddress.fromString( + const result: string = await page.evaluate(async () => { + const completeAddress = await window.AztecJs.CompleteAddress.fromString( '0x2401bfdad7ac9282bd612e8a6bb0f6ce125b08e317e24dc04ddbba24694ac2e7261249d8b3ad8ad9ed075257eede1dcd8356bfd55e1518f07717c47609194b6500c926582f07fda6a53e3600251f2aa1401c0cd377cef064f3f59045222194541acc5f62d8907a6dc98b85e32f8097a152c3c795bb3981c64e576b014f23005e0891d109aa087560cf8720ae94098827aa009a0bcee09f98fd2a05a7cbc6185402a53516a379d7856d26e3bb5542f1fe57f1ee5a0e4c60f7a463205aa19e2f8e00bce110b9a89857b79e3f70777e38a262b04cf80c56bd833a3c4b58dde7dbdc25c807c4012229e08651fd0d48cf9d966d9ab18826692f48a4cf934bef78614023e9cb95711f532786c7c78e72c3752f03f2a4cafc1846ad9df47324e2b7683f0faaa2e6fe44f3ff68646ce7d8538cb6935ce25472c4c75a244ab0c5d2e3b74d', ); // NOTE: browser does not know how to serialize CompleteAddress for return, so return a string @@ -227,7 +218,7 @@ export const browserTestSuite = ( contractArtifactFromBuffer, } = window.AztecJs; // We serialize the artifact since buffers (used for bytecode) do not cross well from one realm to another - const TokenContractArtifact = contractArtifactFromBuffer( + const TokenContractArtifact = await contractArtifactFromBuffer( Buffer.from(serializedTokenContractArtifact, 'base64'), ); const pxe = createPXEClient(rpcUrl!); diff --git a/yarn-project/end-to-end/src/shared/uniswap_l1_l2.ts b/yarn-project/end-to-end/src/shared/uniswap_l1_l2.ts index 3af521e9a99..16c5a86fe24 100644 --- a/yarn-project/end-to-end/src/shared/uniswap_l1_l2.ts +++ b/yarn-project/end-to-end/src/shared/uniswap_l1_l2.ts @@ -221,7 +221,7 @@ export const uniswapL1L2TestSuite = ( // 4. Swap on L1 - sends L2 to L1 message to withdraw WETH to L1 and another message to swap assets. logger.info('Withdrawing weth to L1 and sending message to swap to dai'); - const [secretForDepositingSwappedDai, secretHashForDepositingSwappedDai] = generateClaimSecret(); + const [secretForDepositingSwappedDai, secretHashForDepositingSwappedDai] = await generateClaimSecret(); const l2UniswapInteractionReceipt = await uniswapL2Contract.methods .swap_private( @@ -608,17 +608,15 @@ export const uniswapL1L2TestSuite = ( // swap should fail since no withdraw approval to uniswap: const nonceForWETHTransferToPublicApproval = new Fr(2n); - const expectedMessageHash = computeAuthWitMessageHash( + const expectedMessageHash = await computeAuthWitMessageHash( { caller: uniswapL2Contract.address, - action: wethCrossChainHarness.l2Token.methods - .transfer_to_public( - ownerAddress, - uniswapL2Contract.address, - wethAmountToBridge, - nonceForWETHTransferToPublicApproval, - ) - .request(), + action: wethCrossChainHarness.l2Token.methods.transfer_to_public( + ownerAddress, + uniswapL2Contract.address, + wethAmountToBridge, + nonceForWETHTransferToPublicApproval, + ), }, { chainId: ownerWallet.getChainId(), version: ownerWallet.getVersion() }, ); @@ -685,26 +683,22 @@ export const uniswapL1L2TestSuite = ( // 2. Give approval to uniswap to transfer funds to itself const nonceForWETHTransferApproval = new Fr(2n); - await ownerWallet - .setPublicAuthWit( - { - caller: uniswapL2Contract.address, - action: wethCrossChainHarness.l2Token.methods - .transfer_in_public( - ownerAddress, - uniswapL2Contract.address, - wethAmountToBridge, - nonceForWETHTransferApproval, - ) - .request(), - }, - true, - ) - .send() - .wait(); + const validateActionInteraction = await ownerWallet.setPublicAuthWit( + { + caller: uniswapL2Contract.address, + action: wethCrossChainHarness.l2Token.methods.transfer_in_public( + ownerAddress, + uniswapL2Contract.address, + wethAmountToBridge, + nonceForWETHTransferApproval, + ), + }, + true, + ); + await validateActionInteraction.send().wait(); // No approval to call `swap` but should work even without it: - const [_, secretHashForDepositingSwappedDai] = generateClaimSecret(); + const [_, secretHashForDepositingSwappedDai] = await generateClaimSecret(); await uniswapL2Contract.methods .swap_public( @@ -748,7 +742,8 @@ export const uniswapL1L2TestSuite = ( ownerEthAddress, nonceForSwap, ); - await ownerWallet.setPublicAuthWit({ caller: approvedUser, action }, true).send().wait(); + const validateActionInteraction = await ownerWallet.setPublicAuthWit({ caller: approvedUser, action }, true); + await validateActionInteraction.send().wait(); await expect(action.simulate()).rejects.toThrow(/unauthorized/); }); @@ -757,23 +752,19 @@ export const uniswapL1L2TestSuite = ( // swap should fail since no transfer approval to uniswap: const nonceForWETHTransferApproval = new Fr(4n); - await ownerWallet - .setPublicAuthWit( - { - caller: uniswapL2Contract.address, - action: wethCrossChainHarness.l2Token.methods - .transfer_in_public( - ownerAddress, - uniswapL2Contract.address, - wethAmountToBridge, - nonceForWETHTransferApproval, - ) - .request(), - }, - true, - ) - .send() - .wait(); + const validateActionInteraction = await ownerWallet.setPublicAuthWit( + { + caller: uniswapL2Contract.address, + action: wethCrossChainHarness.l2Token.methods.transfer_in_public( + ownerAddress, + uniswapL2Contract.address, + wethAmountToBridge, + nonceForWETHTransferApproval, + ), + }, + true, + ); + await validateActionInteraction.send().wait(); await expect( uniswapL2Contract.methods @@ -817,7 +808,7 @@ export const uniswapL1L2TestSuite = ( // Swap logger.info('Withdrawing weth to L1 and sending message to swap to dai'); - const [, secretHashForDepositingSwappedDai] = generateClaimSecret(); + const [, secretHashForDepositingSwappedDai] = await generateClaimSecret(); const withdrawReceipt = await uniswapL2Contract.methods .swap_private( wethCrossChainHarness.l2Token.address, @@ -927,23 +918,19 @@ export const uniswapL1L2TestSuite = ( // Owner gives uniswap approval to transfer funds on its behalf const nonceForWETHTransferApproval = new Fr(5n); - await ownerWallet - .setPublicAuthWit( - { - caller: uniswapL2Contract.address, - action: wethCrossChainHarness.l2Token.methods - .transfer_in_public( - ownerAddress, - uniswapL2Contract.address, - wethAmountToBridge, - nonceForWETHTransferApproval, - ) - .request(), - }, - true, - ) - .send() - .wait(); + const validateActionInteraction = await ownerWallet.setPublicAuthWit( + { + caller: uniswapL2Contract.address, + action: wethCrossChainHarness.l2Token.methods.transfer_in_public( + ownerAddress, + uniswapL2Contract.address, + wethAmountToBridge, + nonceForWETHTransferApproval, + ), + }, + true, + ); + await validateActionInteraction.send().wait(); // Call swap_public on L2 const secretHashForDepositingSwappedDai = Fr.random(); diff --git a/yarn-project/end-to-end/src/simulators/lending_simulator.ts b/yarn-project/end-to-end/src/simulators/lending_simulator.ts index 70c72d0d478..56351cddad9 100644 --- a/yarn-project/end-to-end/src/simulators/lending_simulator.ts +++ b/yarn-project/end-to-end/src/simulators/lending_simulator.ts @@ -190,7 +190,7 @@ export class LendingSimulator { expect(interestAccumulator).toEqual(this.accumulator); expect(asset['last_updated_ts']).toEqual(BigInt(this.time)); - for (const key of [this.account.address, AztecAddress.fromField(this.account.key())]) { + for (const key of [this.account.address, AztecAddress.fromField(await this.account.key())]) { const privatePos = await this.lendingContract.methods.get_position(key).simulate(); expect(new Fr(privatePos['collateral'])).toEqual(this.collateral[key.toString()] ?? Fr.ZERO); expect(new Fr(privatePos['static_debt'])).toEqual(this.staticDebt[key.toString()] ?? Fr.ZERO); diff --git a/yarn-project/end-to-end/src/simulators/token_simulator.ts b/yarn-project/end-to-end/src/simulators/token_simulator.ts index ae259539bec..34233f68254 100644 --- a/yarn-project/end-to-end/src/simulators/token_simulator.ts +++ b/yarn-project/end-to-end/src/simulators/token_simulator.ts @@ -97,9 +97,9 @@ export class TokenSimulator { async checkPublic() { // public calls - const calls = [this.token.methods.total_supply().request()]; + const calls = [await this.token.methods.total_supply().request()]; for (const address of this.accounts) { - calls.push(this.token.methods.balance_of_public(address).request()); + calls.push(await this.token.methods.balance_of_public(address).request()); } const results = ( @@ -128,7 +128,7 @@ export class TokenSimulator { const defaultCalls = []; for (const address of defaultLookups) { - defaultCalls.push(this.token.methods.balance_of_private(address).request()); + defaultCalls.push(await this.token.methods.balance_of_private(address).request()); } const results = ( await Promise.all(chunk(defaultCalls, 4).map(batch => new BatchCall(this.defaultWallet, batch).simulate())) diff --git a/yarn-project/end-to-end/webpack.config.js b/yarn-project/end-to-end/webpack.config.js index 3ae5808f82f..0b8c47c8d00 100644 --- a/yarn-project/end-to-end/webpack.config.js +++ b/yarn-project/end-to-end/webpack.config.js @@ -1,3 +1,4 @@ +import CopyPlugin from 'copy-webpack-plugin'; import { createRequire } from 'module'; import { dirname, resolve } from 'path'; import ResolveTypeScriptPlugin from 'resolve-typescript-plugin'; @@ -14,7 +15,14 @@ export default { main: './src/web/main.ts', }, module: { + parser: { + javascript: { importMeta: false }, + }, rules: [ + { + test: /\.gz$/, + type: 'asset/resource', + }, { test: /\.tsx?$/, use: [ @@ -40,6 +48,14 @@ export default { outputModule: true, }, plugins: [ + new CopyPlugin({ + patterns: [ + { + context: '../../barretenberg/ts/dest/browser', + from: '*.gz', + }, + ], + }), new webpack.DefinePlugin({ 'process.env': { NODE_ENV: JSON.stringify('production'), diff --git a/yarn-project/entrypoints/src/account_entrypoint.ts b/yarn-project/entrypoints/src/account_entrypoint.ts index 83c96533a90..93126754780 100644 --- a/yarn-project/entrypoints/src/account_entrypoint.ts +++ b/yarn-project/entrypoints/src/account_entrypoint.ts @@ -25,20 +25,22 @@ export class DefaultAccountEntrypoint implements EntrypointInterface { async createTxExecutionRequest(exec: ExecutionRequestInit): Promise { const { calls, fee, nonce, cancellable } = exec; - const appPayload = EntrypointPayload.fromAppExecution(calls, nonce); + const appPayload = await EntrypointPayload.fromAppExecution(calls, nonce); const feePayload = await EntrypointPayload.fromFeeOptions(this.address, fee); const abi = this.getEntrypointAbi(); - const entrypointHashedArgs = HashedValues.fromValues(encodeArguments(abi, [appPayload, feePayload, !!cancellable])); + const entrypointHashedArgs = await HashedValues.fromValues( + encodeArguments(abi, [appPayload, feePayload, !!cancellable]), + ); const combinedPayloadAuthWitness = await this.auth.createAuthWit( - computeCombinedPayloadHash(appPayload, feePayload), + await computeCombinedPayloadHash(appPayload, feePayload), ); const txRequest = TxExecutionRequest.from({ firstCallArgsHash: entrypointHashedArgs.hash, origin: this.address, - functionSelector: FunctionSelector.fromNameAndParameters(abi.name, abi.parameters), + functionSelector: await FunctionSelector.fromNameAndParameters(abi.name, abi.parameters), txContext: new TxContext(this.chainId, this.version, fee.gasSettings), argsOfCalls: [...appPayload.hashedArguments, ...feePayload.hashedArguments, entrypointHashedArgs], authWitnesses: [combinedPayloadAuthWitness], diff --git a/yarn-project/entrypoints/src/dapp_entrypoint.ts b/yarn-project/entrypoints/src/dapp_entrypoint.ts index d093ff495e9..6d61dfdf656 100644 --- a/yarn-project/entrypoints/src/dapp_entrypoint.ts +++ b/yarn-project/entrypoints/src/dapp_entrypoint.ts @@ -26,18 +26,18 @@ export class DefaultDappEntrypoint implements EntrypointInterface { throw new Error(`Expected exactly 1 function call, got ${calls.length}`); } - const payload = EntrypointPayload.fromFunctionCalls(calls); + const payload = await EntrypointPayload.fromFunctionCalls(calls); const abi = this.getEntrypointAbi(); - const entrypointHashedArgs = HashedValues.fromValues(encodeArguments(abi, [payload, this.userAddress])); - const functionSelector = FunctionSelector.fromNameAndParameters(abi.name, abi.parameters); + const entrypointHashedArgs = await HashedValues.fromValues(encodeArguments(abi, [payload, this.userAddress])); + const functionSelector = await FunctionSelector.fromNameAndParameters(abi.name, abi.parameters); // Default msg_sender for entrypoints is now Fr.max_value rather than 0 addr (see #7190 & #7404) - const innerHash = computeInnerAuthWitHash([ + const innerHash = await computeInnerAuthWitHash([ Fr.MAX_FIELD_VALUE, functionSelector.toField(), entrypointHashedArgs.hash, ]); - const outerHash = computeAuthWitMessageHash( + const outerHash = await computeAuthWitMessageHash( { consumer: this.dappEntrypointAddress, innerHash }, { chainId: new Fr(this.chainId), version: new Fr(this.version) }, ); diff --git a/yarn-project/foundation/package.json b/yarn-project/foundation/package.json index 156ed4b1779..86200f0c3e6 100644 --- a/yarn-project/foundation/package.json +++ b/yarn-project/foundation/package.json @@ -15,6 +15,7 @@ "./collection": "./dest/collection/index.js", "./config": "./dest/config/index.js", "./crypto": "./dest/crypto/index.js", + "./crypto/sync": "./dest/crypto/sync/index.js", "./decorators": "./dest/decorators/index.js", "./error": "./dest/error/index.js", "./eth-address": "./dest/eth-address/index.js", diff --git a/yarn-project/foundation/src/abi/abi.ts b/yarn-project/foundation/src/abi/abi.ts index 9423dde8dca..c4b555ba73b 100644 --- a/yarn-project/foundation/src/abi/abi.ts +++ b/yarn-project/foundation/src/abi/abi.ts @@ -6,7 +6,7 @@ import { type Fr } from '../fields/fields.js'; import { createLogger } from '../log/index.js'; import { schemas } from '../schemas/schemas.js'; import { type ZodFor } from '../schemas/types.js'; -import { type FunctionSelector } from './function_selector.js'; +import { FunctionSelector } from './function_selector.js'; import { type NoteSelector } from './note_selector.js'; /** A basic value. */ @@ -380,16 +380,36 @@ export const ContractArtifactSchema: ZodFor = z.object({ fileMap: z.record(z.coerce.number(), z.object({ source: z.string(), path: z.string() })), }); +export function getFunctionArtifactByName(artifact: ContractArtifact, functionName: string): FunctionArtifact { + const functionArtifact = artifact.functions.find(f => f.name === functionName); + + if (!functionArtifact) { + throw new Error(`Unknown function ${functionName}`); + } + + const debugMetadata = getFunctionDebugMetadata(artifact, functionArtifact); + return { ...functionArtifact, debug: debugMetadata }; +} + /** Gets a function artifact including debug metadata given its name or selector. */ -export function getFunctionArtifact( +export async function getFunctionArtifact( artifact: ContractArtifact, functionNameOrSelector: string | FunctionSelector, -): FunctionArtifact { - const functionArtifact = artifact.functions.find(f => - typeof functionNameOrSelector === 'string' - ? f.name === functionNameOrSelector - : functionNameOrSelector.equals(f.name, f.parameters), - ); +): Promise { + let functionArtifact; + if (typeof functionNameOrSelector === 'string') { + functionArtifact = artifact.functions.find(f => f.name === functionNameOrSelector); + } else { + const functionsAndSelectors = await Promise.all( + artifact.functions.map(async fn => ({ + fn, + selector: await FunctionSelector.fromNameAndParameters(fn.name, fn.parameters), + })), + ); + functionArtifact = functionsAndSelectors.find(fnAndSelector => + functionNameOrSelector.equals(fnAndSelector.selector), + )?.fn; + } if (!functionArtifact) { throw new Error(`Unknown function ${functionNameOrSelector}`); } diff --git a/yarn-project/foundation/src/abi/encoder.test.ts b/yarn-project/foundation/src/abi/encoder.test.ts index 65ca6e66eb6..f5665a7d2dd 100644 --- a/yarn-project/foundation/src/abi/encoder.test.ts +++ b/yarn-project/foundation/src/abi/encoder.test.ts @@ -7,7 +7,7 @@ import { type FunctionAbi, FunctionType } from './abi.js'; import { encodeArguments } from './encoder.js'; describe('abi/encoder', () => { - it('serializes fields as fields', () => { + it('serializes fields as fields', async () => { const abi: FunctionAbi = { name: 'constructor', functionType: FunctionType.PRIVATE, @@ -30,7 +30,7 @@ describe('abi/encoder', () => { const field = Fr.random(); expect(encodeArguments(abi, [field])).toEqual([field]); - const serializedField = jsonParseWithSchema(jsonStringify(field), schemas.Fr); + const serializedField = await jsonParseWithSchema(jsonStringify(field), schemas.Fr); expect(encodeArguments(abi, [serializedField])).toEqual([field]); }); @@ -122,7 +122,7 @@ describe('abi/encoder', () => { const completeAddressLike = { address, publicKey: await Point.random(), partialAddress: Fr.random() }; expect(encodeArguments(abi, [completeAddressLike])).toEqual([address.toField()]); - const serializedAddress = jsonParseWithSchema(jsonStringify(address), schemas.AztecAddress); + const serializedAddress = await jsonParseWithSchema(jsonStringify(address), schemas.AztecAddress); expect(encodeArguments(abi, [serializedAddress])).toEqual([address.toField()]); }); diff --git a/yarn-project/foundation/src/abi/event_selector.ts b/yarn-project/foundation/src/abi/event_selector.ts index 4ce61b4c5d7..80036e77282 100644 --- a/yarn-project/foundation/src/abi/event_selector.ts +++ b/yarn-project/foundation/src/abi/event_selector.ts @@ -40,12 +40,12 @@ export class EventSelector extends Selector { * @param signature - Signature to generate the selector for (e.g. "transfer(field,field)"). * @returns selector. */ - static fromSignature(signature: string) { + static async fromSignature(signature: string) { // throw if signature contains whitespace if (/\s/.test(signature)) { throw new Error('Signature cannot contain whitespace'); } - const hash = poseidon2HashBytes(Buffer.from(signature)); + const hash = await poseidon2HashBytes(Buffer.from(signature)); // We take the last Selector.SIZE big endian bytes const bytes = hash.toBuffer().slice(-Selector.SIZE); return EventSelector.fromBuffer(bytes); diff --git a/yarn-project/foundation/src/abi/function_selector.test.ts b/yarn-project/foundation/src/abi/function_selector.test.ts index a3f6d055354..6ebdf1ff6f7 100644 --- a/yarn-project/foundation/src/abi/function_selector.test.ts +++ b/yarn-project/foundation/src/abi/function_selector.test.ts @@ -22,13 +22,13 @@ describe('FunctionSelector', () => { expect(res).toEqual(selector); }); - it('computes a function selector from signature', () => { - const res = FunctionSelector.fromSignature('IS_VALID()'); + it('computes a function selector from signature', async () => { + const res = await FunctionSelector.fromSignature('IS_VALID()'); expect(res.toBuffer().toString('hex')).toMatchSnapshot(); }); - it('computes a function selector from a long string', () => { - const res = FunctionSelector.fromSignature('foo_and_bar_and_baz_and_foo_bar_baz_and_bar_foo'); + it('computes a function selector from a long string', async () => { + const res = await FunctionSelector.fromSignature('foo_and_bar_and_baz_and_foo_bar_baz_and_bar_foo'); expect(res.toBuffer().toString('hex')).toMatchSnapshot(); }); }); diff --git a/yarn-project/foundation/src/abi/function_selector.ts b/yarn-project/foundation/src/abi/function_selector.ts index 32658b460cd..909e1d95d60 100644 --- a/yarn-project/foundation/src/abi/function_selector.ts +++ b/yarn-project/foundation/src/abi/function_selector.ts @@ -19,26 +19,6 @@ export interface FunctionSelector { /** A function selector is the first 4 bytes of the hash of a function signature. */ export class FunctionSelector extends Selector { - /** - * Checks if this function selector is equal to another. - * @returns True if the function selectors are equal. - */ - override equals(fn: { name: string; parameters: ABIParameter[] }): boolean; - override equals(otherName: string, otherParams: ABIParameter[]): boolean; - override equals(other: FunctionSelector): boolean; - override equals( - other: FunctionSelector | string | { name: string; parameters: ABIParameter[] }, - otherParams?: ABIParameter[], - ): boolean { - if (typeof other === 'string') { - return this.equals(FunctionSelector.fromNameAndParameters(other, otherParams!)); - } else if (typeof other === 'object' && 'name' in other) { - return this.equals(FunctionSelector.fromNameAndParameters(other.name, other.parameters)); - } else { - return this.value === other.value; - } - } - /** * Deserializes from a buffer or reader, corresponding to a write in cpp. * @param buffer - Buffer or BufferReader to read from. @@ -69,12 +49,12 @@ export class FunctionSelector extends Selector { * @param signature - Signature to generate the selector for (e.g. "transfer(field,field)"). * @returns selector. */ - static fromSignature(signature: string) { + static async fromSignature(signature: string) { // throw if signature contains whitespace if (/\s/.test(signature)) { throw new Error('Signature cannot contain whitespace'); } - const hash = poseidon2HashBytes(Buffer.from(signature)); + const hash = await poseidon2HashBytes(Buffer.from(signature)); // We take the last Selector.SIZE big endian bytes const bytes = hash.toBuffer().slice(-Selector.SIZE); return FunctionSelector.fromBuffer(bytes); @@ -109,16 +89,16 @@ export class FunctionSelector extends Selector { * @param parameters - An array of ABIParameter objects, each containing the type information of a function parameter. * @returns A Buffer containing the 4-byte selector. */ - static fromNameAndParameters(args: { name: string; parameters: ABIParameter[] }): FunctionSelector; - static fromNameAndParameters(name: string, parameters: ABIParameter[]): FunctionSelector; - static fromNameAndParameters( + static fromNameAndParameters(args: { name: string; parameters: ABIParameter[] }): Promise; + static fromNameAndParameters(name: string, parameters: ABIParameter[]): Promise; + static async fromNameAndParameters( nameOrArgs: string | { name: string; parameters: ABIParameter[] }, maybeParameters?: ABIParameter[], - ): FunctionSelector { + ): Promise { const { name, parameters } = typeof nameOrArgs === 'string' ? { name: nameOrArgs, parameters: maybeParameters! } : nameOrArgs; const signature = decodeFunctionSignature(name, parameters); - const selector = this.fromSignature(signature); + const selector = await this.fromSignature(signature); // If using the debug logger here it kill the typing in the `server_world_state_synchronizer` and jest tests. // console.log(`selector for ${signature} is ${selector}`); return selector; diff --git a/yarn-project/foundation/src/blob/blob.test.ts b/yarn-project/foundation/src/blob/blob.test.ts index c730277e355..0163a4cad0b 100644 --- a/yarn-project/foundation/src/blob/blob.test.ts +++ b/yarn-project/foundation/src/blob/blob.test.ts @@ -74,12 +74,12 @@ describe('blob', () => { expect(isValid).toBe(true); }); - it('should evaluate a blob of 400 items', () => { + it('should evaluate a blob of 400 items', async () => { // This test ensures that the Blob class correctly matches the c-kzg lib // The values here are used to test Noir's blob evaluation in noir-projects/noir-protocol-circuits/crates/blob/src/blob.nr -> test_400 const blobItems = Array(400).fill(new Fr(3)); - const ourBlob = Blob.fromFields(blobItems); - const blobItemsHash = poseidon2Hash(Array(400).fill(new Fr(3))); + const ourBlob = await Blob.fromFields(blobItems); + const blobItemsHash = await poseidon2Hash(Array(400).fill(new Fr(3))); expect(blobItemsHash).toEqual(ourBlob.fieldsHash); // We add zeros before getting commitment as we do not store the blob along with @@ -87,7 +87,7 @@ describe('blob', () => { const dataWithZeros = Buffer.concat([ourBlob.data], BYTES_PER_BLOB); expect(blobToKzgCommitment(dataWithZeros)).toEqual(ourBlob.commitment); - const z = poseidon2Hash([blobItemsHash, ...ourBlob.commitmentToFields()]); + const z = await poseidon2Hash([blobItemsHash, ...ourBlob.commitmentToFields()]); expect(z).toEqual(ourBlob.challengeZ); const res = computeKzgProof(dataWithZeros, ourBlob.challengeZ.toBuffer()); @@ -103,7 +103,7 @@ describe('blob', () => { expect(isValid).toBe(true); }); - it('should evaluate full blobs', () => { + it('should evaluate full blobs', async () => { // This test ensures that the Blob class correctly matches the c-kzg lib // The values here are used to test Noir's blob evaluation in noir-projects/noir-protocol-circuits/crates/blob/src/blob.nr -> test_full_blobs @@ -113,15 +113,15 @@ describe('blob', () => { blobItems[j * FIELD_ELEMENTS_PER_BLOB + i] = new Fr(i + 2); } } - const blobItemsHash = poseidon2Hash(blobItems); - const blobs = Blob.getBlobs(blobItems); - blobs.forEach(ourBlob => { + const blobItemsHash = await poseidon2Hash(blobItems); + const blobs = await Blob.getBlobs(blobItems); + for (const ourBlob of blobs) { // const ourBlob = Blob.fromFields(blobItems.slice(j * FIELD_ELEMENTS_PER_BLOB, (j + 1) * FIELD_ELEMENTS_PER_BLOB), blobItemsHash); expect(blobItemsHash).toEqual(ourBlob.fieldsHash); expect(blobToKzgCommitment(ourBlob.data)).toEqual(ourBlob.commitment); - const z = poseidon2Hash([blobItemsHash, ...ourBlob.commitmentToFields()]); + const z = await poseidon2Hash([blobItemsHash, ...ourBlob.commitmentToFields()]); expect(z).toEqual(ourBlob.challengeZ); const res = computeKzgProof(ourBlob.data, ourBlob.challengeZ.toBuffer()); @@ -135,20 +135,20 @@ describe('blob', () => { ourBlob.proof, ); expect(isValid).toBe(true); - }); + } }); - it('Should serialise and deserialise a blob', () => { - const blob = Blob.fromFields([Fr.random(), Fr.random(), Fr.random()]); + it('Should serialise and deserialise a blob', async () => { + const blob = await Blob.fromFields([Fr.random(), Fr.random(), Fr.random()]); const blobBuffer = blob.toBuffer(); const deserialisedBlob = Blob.fromBuffer(blobBuffer); expect(blob.fieldsHash.equals(deserialisedBlob.fieldsHash)).toBe(true); }); - it('Should create a blob from a JSON object', () => { - const blob = makeEncodedBlob(3); + it('Should create a blob from a JSON object', async () => { + const blob = await makeEncodedBlob(3); const blobJson = blob.toJson(); - const deserialisedBlob = Blob.fromJson(blobJson); + const deserialisedBlob = await Blob.fromJson(blobJson); expect(blob.fieldsHash.equals(deserialisedBlob.fieldsHash)).toBe(true); }); }); diff --git a/yarn-project/foundation/src/blob/blob.ts b/yarn-project/foundation/src/blob/blob.ts index 36446de4602..3c363b3b2db 100644 --- a/yarn-project/foundation/src/blob/blob.ts +++ b/yarn-project/foundation/src/blob/blob.ts @@ -44,7 +44,7 @@ export class Blob { * @param multiBlobFieldsHash - The fields hash to use for the Blob. * @returns A Blob created from the buffer. */ - static fromEncodedBlobBuffer(blob: BlobBuffer, multiBlobFieldsHash?: Fr): Blob { + static fromEncodedBlobBuffer(blob: BlobBuffer, multiBlobFieldsHash?: Fr): Promise { const fields: Fr[] = deserializeEncodedBlobFields(blob); return Blob.fromFields(fields, multiBlobFieldsHash); } @@ -56,7 +56,7 @@ export class Blob { * @param multiBlobFieldsHash - The fields hash to use for the Blob. * @returns A Blob created from the array of fields. */ - static fromFields(fields: Fr[], multiBlobFieldsHash?: Fr): Blob { + static async fromFields(fields: Fr[], multiBlobFieldsHash?: Fr): Promise { if (fields.length > FIELD_ELEMENTS_PER_BLOB) { throw new Error( `Attempted to overfill blob with ${fields.length} elements. The maximum is ${FIELD_ELEMENTS_PER_BLOB}`, @@ -66,9 +66,9 @@ export class Blob { const data = Buffer.concat([serializeToBuffer(fields)], BYTES_PER_BLOB); // This matches the output of SpongeBlob.squeeze() in the blob circuit - const fieldsHash = multiBlobFieldsHash ? multiBlobFieldsHash : poseidon2Hash(fields); + const fieldsHash = multiBlobFieldsHash ? multiBlobFieldsHash : await poseidon2Hash(fields); const commitment = Buffer.from(blobToKzgCommitment(data)); - const challengeZ = poseidon2Hash([fieldsHash, ...commitmentToFields(commitment)]); + const challengeZ = await poseidon2Hash([fieldsHash, ...commitmentToFields(commitment)]); const res = computeKzgProof(data, challengeZ.toBuffer()); if (!verifyKzgProof(commitment, challengeZ.toBuffer(), res[1], res[0])) { throw new Error(`KZG proof did not verify.`); @@ -91,10 +91,10 @@ export class Blob { * @param json - The JSON object to create the Blob from. * @returns A Blob created from the JSON object. */ - static fromJson(json: BlobJson): Blob { + static async fromJson(json: BlobJson): Promise { const blobBuffer = Buffer.from(json.blob.slice(2), 'hex'); - const blob = Blob.fromEncodedBlobBuffer(blobBuffer); + const blob = await Blob.fromEncodedBlobBuffer(blobBuffer); if (blob.commitment.toString('hex') !== json.kzg_commitment.slice(2)) { throw new Error('KZG commitment does not match'); @@ -276,13 +276,13 @@ export class Blob { // Returns as many blobs as we require to broadcast the given fields // Assumes we share the fields hash between all blobs - static getBlobs(fields: Fr[]): Blob[] { + static async getBlobs(fields: Fr[]): Promise { const numBlobs = Math.max(Math.ceil(fields.length / FIELD_ELEMENTS_PER_BLOB), 1); - const multiBlobFieldsHash = poseidon2Hash(fields); + const multiBlobFieldsHash = await poseidon2Hash(fields); const res = []; for (let i = 0; i < numBlobs; i++) { const end = fields.length < (i + 1) * FIELD_ELEMENTS_PER_BLOB ? fields.length : (i + 1) * FIELD_ELEMENTS_PER_BLOB; - res.push(Blob.fromFields(fields.slice(i * FIELD_ELEMENTS_PER_BLOB, end), multiBlobFieldsHash)); + res.push(await Blob.fromFields(fields.slice(i * FIELD_ELEMENTS_PER_BLOB, end), multiBlobFieldsHash)); } return res; } diff --git a/yarn-project/foundation/src/blob/mocks.ts b/yarn-project/foundation/src/blob/mocks.ts index fd7890eebd9..7d4464b8339 100644 --- a/yarn-project/foundation/src/blob/mocks.ts +++ b/yarn-project/foundation/src/blob/mocks.ts @@ -21,10 +21,10 @@ function encodeFirstField(length: number): Fr { ); } -export function makeEncodedBlob(length: number): Blob { +export function makeEncodedBlob(length: number): Promise { return Blob.fromFields([encodeFirstField(length + 1), ...Array.from({ length: length }, () => Fr.random())]); } -export function makeEncodedBlobFields(fields: Fr[]): Blob { +export function makeEncodedBlobFields(fields: Fr[]): Promise { return Blob.fromFields([encodeFirstField(fields.length + 1), ...fields]); } diff --git a/yarn-project/foundation/src/crypto/index.ts b/yarn-project/foundation/src/crypto/index.ts index 9385f359563..8df25e761be 100644 --- a/yarn-project/foundation/src/crypto/index.ts +++ b/yarn-project/foundation/src/crypto/index.ts @@ -1,5 +1,3 @@ -import { BarretenbergSync } from '@aztec/bb.js'; - export * from './keccak/index.js'; export * from './random/index.js'; export * from './sha256/index.js'; @@ -8,13 +6,3 @@ export * from './pedersen/index.js'; export * from './poseidon/index.js'; export * from './secp256k1-signer/index.js'; export * from './keys/index.js'; - -/** - * Init the bb singleton. This constructs (if not already) the barretenberg sync api within bb.js itself. - * It takes about 100-200ms to initialize. It may not seem like much, but when in conjunction with many other things - * initializing, developers may want to pick precisely when to incur this cost. - * If in a test environment, we'll just do it on module load. - */ -export async function init() { - await BarretenbergSync.initSingleton(); -} diff --git a/yarn-project/foundation/src/crypto/keys/index.ts b/yarn-project/foundation/src/crypto/keys/index.ts index 7b2066717a2..92899072d34 100644 --- a/yarn-project/foundation/src/crypto/keys/index.ts +++ b/yarn-project/foundation/src/crypto/keys/index.ts @@ -2,8 +2,9 @@ import { BarretenbergSync, RawBuffer } from '@aztec/bb.js'; import { Fr } from '../../fields/fields.js'; -export function vkAsFieldsMegaHonk(input: Buffer): Fr[] { - return BarretenbergSync.getSingleton() - .acirVkAsFieldsMegaHonk(new RawBuffer(input)) - .map(bbFr => Fr.fromBuffer(Buffer.from(bbFr.toBuffer()))); // TODO(#4189): remove this conversion +export async function vkAsFieldsMegaHonk(input: Buffer): Promise { + const api = await BarretenbergSync.initSingleton(); + const result = api.acirVkAsFieldsMegaHonk(new RawBuffer(input)); + + return result.map(bbFr => Fr.fromBuffer(Buffer.from(bbFr.toBuffer()))); // TODO(#4189): remove this conversion } diff --git a/yarn-project/foundation/src/crypto/pedersen/index.test.ts b/yarn-project/foundation/src/crypto/pedersen/index.test.ts index 0e9e1c1c92a..b2e0e6273e0 100644 --- a/yarn-project/foundation/src/crypto/pedersen/index.test.ts +++ b/yarn-project/foundation/src/crypto/pedersen/index.test.ts @@ -1,46 +1,43 @@ -import { BarretenbergSync } from '@aztec/bb.js'; - import { toBufferBE } from '../../bigint-buffer/index.js'; import { setupCustomSnapshotSerializers } from '../../testing/index.js'; import { pedersenCommit, pedersenHash, pedersenHashBuffer } from './index.js'; describe('pedersen', () => { - beforeAll(async () => { + beforeAll(() => { setupCustomSnapshotSerializers(expect); - await BarretenbergSync.initSingleton(); }); - it('pedersen commit', () => { - const r = pedersenCommit([toBufferBE(1n, 32), toBufferBE(1n, 32)]); + it('pedersen commit', async () => { + const r = await pedersenCommit([toBufferBE(1n, 32), toBufferBE(1n, 32)]); expect(r).toEqual([ Buffer.from('2f7a8f9a6c96926682205fb73ee43215bf13523c19d7afe36f12760266cdfe15', 'hex'), Buffer.from('01916b316adbbf0e10e39b18c1d24b33ec84b46daddf72f43878bcc92b6057e6', 'hex'), ]); }); - it('pedersen commit with zero', () => { - const r = pedersenCommit([toBufferBE(0n, 32), toBufferBE(1n, 32)]); + it('pedersen commit with zero', async () => { + const r = await pedersenCommit([toBufferBE(0n, 32), toBufferBE(1n, 32)]); expect(r).toEqual([ Buffer.from('054aa86a73cb8a34525e5bbed6e43ba1198e860f5f3950268f71df4591bde402', 'hex'), Buffer.from('209dcfbf2cfb57f9f6046f44d71ac6faf87254afc7407c04eb621a6287cac126', 'hex'), ]); }); - it('pedersen hash', () => { - const r = pedersenHash([toBufferBE(1n, 32), toBufferBE(1n, 32)]); + it('pedersen hash', async () => { + const r = await pedersenHash([toBufferBE(1n, 32), toBufferBE(1n, 32)]); expect(r.toString()).toEqual('0x07ebfbf4df29888c6cd6dca13d4bb9d1a923013ddbbcbdc3378ab8845463297b'); }); - it('pedersen hash with index', () => { - const r = pedersenHash([toBufferBE(1n, 32), toBufferBE(1n, 32)], 5); + it('pedersen hash with index', async () => { + const r = await pedersenHash([toBufferBE(1n, 32), toBufferBE(1n, 32)], 5); expect(r.toString()).toEqual('0x1c446df60816b897cda124524e6b03f36df0cec333fad87617aab70d7861daa6'); }); - it('pedersen hash buffer', () => { + it('pedersen hash buffer', async () => { const input = Buffer.alloc(123); input.writeUint32BE(321, 0); input.writeUint32BE(456, 119); - const r = pedersenHashBuffer(input); + const r = await pedersenHashBuffer(input); expect(r).toMatchSnapshot(); }); }); diff --git a/yarn-project/foundation/src/crypto/pedersen/pedersen.wasm.ts b/yarn-project/foundation/src/crypto/pedersen/pedersen.wasm.ts index fcbceafbfb6..b2698f6989b 100644 --- a/yarn-project/foundation/src/crypto/pedersen/pedersen.wasm.ts +++ b/yarn-project/foundation/src/crypto/pedersen/pedersen.wasm.ts @@ -7,12 +7,13 @@ import { type Fieldable, serializeToFields } from '../../serialize/serialize.js' * Create a pedersen commitment (point) from an array of input fields. * Left pads any inputs less than 32 bytes. */ -export function pedersenCommit(input: Buffer[], offset = 0) { +export async function pedersenCommit(input: Buffer[], offset = 0) { if (!input.every(i => i.length <= 32)) { throw new Error('All Pedersen Commit input buffers must be <= 32 bytes.'); } input = input.map(i => (i.length < 32 ? Buffer.concat([Buffer.alloc(32 - i.length, 0), i]) : i)); - const point = BarretenbergSync.getSingleton().pedersenCommit( + const api = await BarretenbergSync.initSingleton(); + const point = api.pedersenCommit( input.map(i => new FrBarretenberg(i)), offset, ); @@ -27,23 +28,21 @@ export function pedersenCommit(input: Buffer[], offset = 0) { * @param index - The separator index to use for the hash. * @returns The pedersen hash. */ -export function pedersenHash(input: Fieldable[], index = 0): Fr { +export async function pedersenHash(input: Fieldable[], index = 0): Promise { const inputFields = serializeToFields(input); - return Fr.fromBuffer( - Buffer.from( - BarretenbergSync.getSingleton() - .pedersenHash( - inputFields.map(i => new FrBarretenberg(i.toBuffer())), // TODO(#4189): remove this stupid conversion - index, - ) - .toBuffer(), - ), + const api = await BarretenbergSync.initSingleton(); + const hash = api.pedersenHash( + inputFields.map(i => new FrBarretenberg(i.toBuffer())), // TODO(#4189): remove this stupid conversion + index, ); + return Fr.fromBuffer(Buffer.from(hash.toBuffer())); } /** * Create a pedersen hash from an arbitrary length buffer. */ -export function pedersenHashBuffer(input: Buffer, index = 0) { - return Buffer.from(BarretenbergSync.getSingleton().pedersenHashBuffer(input, index).toBuffer()); +export async function pedersenHashBuffer(input: Buffer, index = 0) { + const api = await BarretenbergSync.initSingleton(); + const result = api.pedersenHashBuffer(input, index); + return Buffer.from(result.toBuffer()); } diff --git a/yarn-project/foundation/src/crypto/poseidon/index.test.ts b/yarn-project/foundation/src/crypto/poseidon/index.test.ts index a12998a1213..7adf6092158 100644 --- a/yarn-project/foundation/src/crypto/poseidon/index.test.ts +++ b/yarn-project/foundation/src/crypto/poseidon/index.test.ts @@ -2,9 +2,9 @@ import { Fr } from '../../fields/fields.js'; import { poseidon2Permutation } from './index.js'; describe('poseidon2Permutation', () => { - it('test vectors from cpp should match', () => { + it('test vectors from cpp should match', async () => { const init = [0, 1, 2, 3]; - expect(poseidon2Permutation(init)).toEqual([ + await expect(poseidon2Permutation(init)).resolves.toEqual([ new Fr(0x01bd538c2ee014ed5141b29e9ae240bf8db3fe5b9a38629a9647cf8d76c01737n), new Fr(0x239b62e7db98aa3a2a8f6a0d2fa1709e7a35959aa6c7034814d9daa90cbac662n), new Fr(0x04cbb44c61d928ed06808456bf758cbf0c18d1e15a7b6dbc8245fa7515d5e3cbn), @@ -12,9 +12,9 @@ describe('poseidon2Permutation', () => { ]); }); - it('test vectors from Noir should match', () => { + it('test vectors from Noir should match', async () => { const init = [1n, 2n, 3n, 0x0a0000000000000000n]; - expect(poseidon2Permutation(init)).toEqual([ + await expect(poseidon2Permutation(init)).resolves.toEqual([ new Fr(0x0369007aa630f5dfa386641b15416ecb16fb1a6f45b1acb903cb986b221a891cn), new Fr(0x1919fd474b4e2e0f8e0cf8ca98ef285675781cbd31aa4807435385d28e4c02a5n), new Fr(0x0810e7e9a1c236aae4ebff7d3751d9f7346dc443d1de863977d2b81fe8c557f4n), diff --git a/yarn-project/foundation/src/crypto/poseidon/index.ts b/yarn-project/foundation/src/crypto/poseidon/index.ts index aad83209f2f..8756a310425 100644 --- a/yarn-project/foundation/src/crypto/poseidon/index.ts +++ b/yarn-project/foundation/src/crypto/poseidon/index.ts @@ -8,17 +8,13 @@ import { type Fieldable, serializeToFields } from '../../serialize/serialize.js' * @param input - The input fields to hash. * @returns The poseidon hash. */ -export function poseidon2Hash(input: Fieldable[]): Fr { +export async function poseidon2Hash(input: Fieldable[]): Promise { const inputFields = serializeToFields(input); - return Fr.fromBuffer( - Buffer.from( - BarretenbergSync.getSingleton() - .poseidon2Hash( - inputFields.map(i => new FrBarretenberg(i.toBuffer())), // TODO(#4189): remove this stupid conversion - ) - .toBuffer(), - ), + const api = await BarretenbergSync.initSingleton(); + const hash = api.poseidon2Hash( + inputFields.map(i => new FrBarretenberg(i.toBuffer())), // TODO(#4189): remove this stupid conversion ); + return Fr.fromBuffer(Buffer.from(hash.toBuffer())); } /** @@ -27,29 +23,22 @@ export function poseidon2Hash(input: Fieldable[]): Fr { * @param separator - The domain separator. * @returns The poseidon hash. */ -export function poseidon2HashWithSeparator(input: Fieldable[], separator: number): Fr { +export async function poseidon2HashWithSeparator(input: Fieldable[], separator: number): Promise { const inputFields = serializeToFields(input); inputFields.unshift(new Fr(separator)); - return Fr.fromBuffer( - Buffer.from( - BarretenbergSync.getSingleton() - .poseidon2Hash( - inputFields.map(i => new FrBarretenberg(i.toBuffer())), // TODO(#4189): remove this stupid conversion - ) - .toBuffer(), - ), + const api = await BarretenbergSync.initSingleton(); + + const hash = api.poseidon2Hash( + inputFields.map(i => new FrBarretenberg(i.toBuffer())), // TODO(#4189): remove this stupid conversion ); + return Fr.fromBuffer(Buffer.from(hash.toBuffer())); } -export function poseidon2HashAccumulate(input: Fieldable[]): Fr { +export async function poseidon2HashAccumulate(input: Fieldable[]): Promise { const inputFields = serializeToFields(input); - return Fr.fromBuffer( - Buffer.from( - BarretenbergSync.getSingleton() - .poseidon2HashAccumulate(inputFields.map(i => new FrBarretenberg(i.toBuffer()))) - .toBuffer(), - ), - ); + const api = await BarretenbergSync.initSingleton(); + const result = api.poseidon2HashAccumulate(inputFields.map(i => new FrBarretenberg(i.toBuffer()))); + return Fr.fromBuffer(Buffer.from(result.toBuffer())); } /** @@ -57,19 +46,18 @@ export function poseidon2HashAccumulate(input: Fieldable[]): Fr { * @param input the input state. Expected to be of size 4. * @returns the output state, size 4. */ -export function poseidon2Permutation(input: Fieldable[]): Fr[] { +export async function poseidon2Permutation(input: Fieldable[]): Promise { const inputFields = serializeToFields(input); // We'd like this assertion but it's not possible to use it in the browser. // assert(input.length === 4, 'Input state must be of size 4'); - const res = BarretenbergSync.getSingleton().poseidon2Permutation( - inputFields.map(i => new FrBarretenberg(i.toBuffer())), - ); + const api = await BarretenbergSync.initSingleton(); + const res = api.poseidon2Permutation(inputFields.map(i => new FrBarretenberg(i.toBuffer()))); // We'd like this assertion but it's not possible to use it in the browser. // assert(res.length === 4, 'Output state must be of size 4'); return res.map(o => Fr.fromBuffer(Buffer.from(o.toBuffer()))); } -export function poseidon2HashBytes(input: Buffer): Fr { +export async function poseidon2HashBytes(input: Buffer): Promise { const inputFields = []; for (let i = 0; i < input.length; i += 31) { const fieldBytes = Buffer.alloc(32, 0); @@ -80,13 +68,10 @@ export function poseidon2HashBytes(input: Buffer): Fr { inputFields.push(Fr.fromBuffer(fieldBytes)); } - return Fr.fromBuffer( - Buffer.from( - BarretenbergSync.getSingleton() - .poseidon2Hash( - inputFields.map(i => new FrBarretenberg(i.toBuffer())), // TODO(#4189): remove this stupid conversion - ) - .toBuffer(), - ), + const api = await BarretenbergSync.initSingleton(); + const res = api.poseidon2Hash( + inputFields.map(i => new FrBarretenberg(i.toBuffer())), // TODO(#4189): remove this stupid conversion ); + + return Fr.fromBuffer(Buffer.from(res.toBuffer())); } diff --git a/yarn-project/foundation/src/crypto/sync/index.ts b/yarn-project/foundation/src/crypto/sync/index.ts new file mode 100644 index 00000000000..65a9546b408 --- /dev/null +++ b/yarn-project/foundation/src/crypto/sync/index.ts @@ -0,0 +1,6 @@ +import { BarretenbergSync } from '@aztec/bb.js'; + +export * from './poseidon/index.js'; +export * from './pedersen/index.js'; + +await BarretenbergSync.initSingleton(); diff --git a/yarn-project/foundation/src/crypto/sync/pedersen/__snapshots__/index.test.ts.snap b/yarn-project/foundation/src/crypto/sync/pedersen/__snapshots__/index.test.ts.snap new file mode 100644 index 00000000000..b8842b33c77 --- /dev/null +++ b/yarn-project/foundation/src/crypto/sync/pedersen/__snapshots__/index.test.ts.snap @@ -0,0 +1,3 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`pedersen pedersen hash buffer 1`] = `Buffer<0x2bd5c452a0c97162294fc9dfd0f1e09d0e09c95fa5ed3ff149fbdef366cb51f9>`; diff --git a/yarn-project/foundation/src/crypto/sync/pedersen/index.test.ts b/yarn-project/foundation/src/crypto/sync/pedersen/index.test.ts new file mode 100644 index 00000000000..80864fc874f --- /dev/null +++ b/yarn-project/foundation/src/crypto/sync/pedersen/index.test.ts @@ -0,0 +1,46 @@ +import { BarretenbergSync } from '@aztec/bb.js'; + +import { toBufferBE } from '../../../bigint-buffer/index.js'; +import { setupCustomSnapshotSerializers } from '../../../testing/index.js'; +import { pedersenCommit, pedersenHash, pedersenHashBuffer } from './index.js'; + +describe('pedersen', () => { + beforeAll(async () => { + await BarretenbergSync.initSingleton(); + setupCustomSnapshotSerializers(expect); + }); + + it('pedersen commit', () => { + const r = pedersenCommit([toBufferBE(1n, 32), toBufferBE(1n, 32)]); + expect(r).toEqual([ + Buffer.from('2f7a8f9a6c96926682205fb73ee43215bf13523c19d7afe36f12760266cdfe15', 'hex'), + Buffer.from('01916b316adbbf0e10e39b18c1d24b33ec84b46daddf72f43878bcc92b6057e6', 'hex'), + ]); + }); + + it('pedersen commit with zero', () => { + const r = pedersenCommit([toBufferBE(0n, 32), toBufferBE(1n, 32)]); + expect(r).toEqual([ + Buffer.from('054aa86a73cb8a34525e5bbed6e43ba1198e860f5f3950268f71df4591bde402', 'hex'), + Buffer.from('209dcfbf2cfb57f9f6046f44d71ac6faf87254afc7407c04eb621a6287cac126', 'hex'), + ]); + }); + + it('pedersen hash', () => { + const r = pedersenHash([toBufferBE(1n, 32), toBufferBE(1n, 32)]); + expect(r.toString()).toEqual('0x07ebfbf4df29888c6cd6dca13d4bb9d1a923013ddbbcbdc3378ab8845463297b'); + }); + + it('pedersen hash with index', () => { + const r = pedersenHash([toBufferBE(1n, 32), toBufferBE(1n, 32)], 5); + expect(r.toString()).toEqual('0x1c446df60816b897cda124524e6b03f36df0cec333fad87617aab70d7861daa6'); + }); + + it('pedersen hash buffer', () => { + const input = Buffer.alloc(123); + input.writeUint32BE(321, 0); + input.writeUint32BE(456, 119); + const r = pedersenHashBuffer(input); + expect(r).toMatchSnapshot(); + }); +}); diff --git a/yarn-project/foundation/src/crypto/sync/pedersen/index.ts b/yarn-project/foundation/src/crypto/sync/pedersen/index.ts new file mode 100644 index 00000000000..8fb73c59ca7 --- /dev/null +++ b/yarn-project/foundation/src/crypto/sync/pedersen/index.ts @@ -0,0 +1,45 @@ +import { BarretenbergSync, Fr as FrBarretenberg } from '@aztec/bb.js'; + +import { Fr } from '../../../fields/fields.js'; +import { type Fieldable, serializeToFields } from '../../../serialize/serialize.js'; + +/** + * Create a pedersen commitment (point) from an array of input fields. + * Left pads any inputs less than 32 bytes. + */ +export function pedersenCommit(input: Buffer[], offset = 0) { + if (!input.every(i => i.length <= 32)) { + throw new Error('All Pedersen Commit input buffers must be <= 32 bytes.'); + } + input = input.map(i => (i.length < 32 ? Buffer.concat([Buffer.alloc(32 - i.length, 0), i]) : i)); + const point = BarretenbergSync.getSingleton().pedersenCommit( + input.map(i => new FrBarretenberg(i)), + offset, + ); + // toBuffer returns Uint8Arrays (browser/worker-boundary friendly). + // TODO: rename toTypedArray()? + return [Buffer.from(point.x.toBuffer()), Buffer.from(point.y.toBuffer())]; +} + +/** + * Create a pedersen hash (field) from an array of input fields. + * @param input - The input fieldables to hash. + * @param index - The separator index to use for the hash. + * @returns The pedersen hash. + */ +export function pedersenHash(input: Fieldable[], index = 0): Fr { + const inputFields = serializeToFields(input); + const hash = BarretenbergSync.getSingleton().pedersenHash( + inputFields.map(i => new FrBarretenberg(i.toBuffer())), // TODO(#4189): remove this stupid conversion + index, + ); + return Fr.fromBuffer(Buffer.from(hash.toBuffer())); +} + +/** + * Create a pedersen hash from an arbitrary length buffer. + */ +export function pedersenHashBuffer(input: Buffer, index = 0) { + const result = BarretenbergSync.getSingleton().pedersenHashBuffer(input, index); + return Buffer.from(result.toBuffer()); +} diff --git a/yarn-project/foundation/src/crypto/sync/poseidon/index.test.ts b/yarn-project/foundation/src/crypto/sync/poseidon/index.test.ts new file mode 100644 index 00000000000..40873d1e90a --- /dev/null +++ b/yarn-project/foundation/src/crypto/sync/poseidon/index.test.ts @@ -0,0 +1,29 @@ +import { BarretenbergSync } from '@aztec/bb.js'; + +import { Fr } from '../../../fields/fields.js'; +import { poseidon2Permutation } from './index.js'; + +describe('poseidon2Permutation', () => { + beforeAll(async () => { + await BarretenbergSync.initSingleton(); + }); + it('test vectors from cpp should match', () => { + const init = [0, 1, 2, 3]; + expect(poseidon2Permutation(init)).toEqual([ + new Fr(0x01bd538c2ee014ed5141b29e9ae240bf8db3fe5b9a38629a9647cf8d76c01737n), + new Fr(0x239b62e7db98aa3a2a8f6a0d2fa1709e7a35959aa6c7034814d9daa90cbac662n), + new Fr(0x04cbb44c61d928ed06808456bf758cbf0c18d1e15a7b6dbc8245fa7515d5e3cbn), + new Fr(0x2e11c5cff2a22c64d01304b778d78f6998eff1ab73163a35603f54794c30847an), + ]); + }); + + it('test vectors from Noir should match', () => { + const init = [1n, 2n, 3n, 0x0a0000000000000000n]; + expect(poseidon2Permutation(init)).toEqual([ + new Fr(0x0369007aa630f5dfa386641b15416ecb16fb1a6f45b1acb903cb986b221a891cn), + new Fr(0x1919fd474b4e2e0f8e0cf8ca98ef285675781cbd31aa4807435385d28e4c02a5n), + new Fr(0x0810e7e9a1c236aae4ebff7d3751d9f7346dc443d1de863977d2b81fe8c557f4n), + new Fr(0x1f4a188575e29985b6f8ad03afc1f0759488f8835aafb6e19e06160fb64d3d4an), + ]); + }); +}); diff --git a/yarn-project/foundation/src/crypto/sync/poseidon/index.ts b/yarn-project/foundation/src/crypto/sync/poseidon/index.ts new file mode 100644 index 00000000000..86040b130d0 --- /dev/null +++ b/yarn-project/foundation/src/crypto/sync/poseidon/index.ts @@ -0,0 +1,76 @@ +import { BarretenbergSync, Fr as FrBarretenberg } from '@aztec/bb.js'; + +import { Fr } from '../../../fields/fields.js'; +import { type Fieldable, serializeToFields } from '../../../serialize/serialize.js'; + +/** + * Create a poseidon hash (field) from an array of input fields. + * @param input - The input fields to hash. + * @returns The poseidon hash. + */ +export function poseidon2Hash(input: Fieldable[]): Fr { + const inputFields = serializeToFields(input); + const hash = BarretenbergSync.getSingleton().poseidon2Hash( + inputFields.map(i => new FrBarretenberg(i.toBuffer())), // TODO(#4189): remove this stupid conversion + ); + return Fr.fromBuffer(Buffer.from(hash.toBuffer())); +} + +/** + * Create a poseidon hash (field) from an array of input fields and a domain separator. + * @param input - The input fields to hash. + * @param separator - The domain separator. + * @returns The poseidon hash. + */ +export function poseidon2HashWithSeparator(input: Fieldable[], separator: number): Fr { + const inputFields = serializeToFields(input); + inputFields.unshift(new Fr(separator)); + + const hash = BarretenbergSync.getSingleton().poseidon2Hash( + inputFields.map(i => new FrBarretenberg(i.toBuffer())), // TODO(#4189): remove this stupid conversion + ); + return Fr.fromBuffer(Buffer.from(hash.toBuffer())); +} + +export function poseidon2HashAccumulate(input: Fieldable[]): Fr { + const inputFields = serializeToFields(input); + const result = BarretenbergSync.getSingleton().poseidon2HashAccumulate( + inputFields.map(i => new FrBarretenberg(i.toBuffer())), + ); + return Fr.fromBuffer(Buffer.from(result.toBuffer())); +} + +/** + * Runs a Poseidon2 permutation. + * @param input the input state. Expected to be of size 4. + * @returns the output state, size 4. + */ +export function poseidon2Permutation(input: Fieldable[]): Fr[] { + const inputFields = serializeToFields(input); + // We'd like this assertion but it's not possible to use it in the browser. + // assert(input.length === 4, 'Input state must be of size 4'); + const res = BarretenbergSync.getSingleton().poseidon2Permutation( + inputFields.map(i => new FrBarretenberg(i.toBuffer())), + ); + // We'd like this assertion but it's not possible to use it in the browser. + // assert(res.length === 4, 'Output state must be of size 4'); + return res.map(o => Fr.fromBuffer(Buffer.from(o.toBuffer()))); +} + +export function poseidon2HashBytes(input: Buffer): Fr { + const inputFields = []; + for (let i = 0; i < input.length; i += 31) { + const fieldBytes = Buffer.alloc(32, 0); + input.slice(i, i + 31).copy(fieldBytes); + + // Noir builds the bytes as little-endian, so we need to reverse them. + fieldBytes.reverse(); + inputFields.push(Fr.fromBuffer(fieldBytes)); + } + + const res = BarretenbergSync.getSingleton().poseidon2Hash( + inputFields.map(i => new FrBarretenberg(i.toBuffer())), // TODO(#4189): remove this stupid conversion + ); + + return Fr.fromBuffer(Buffer.from(res.toBuffer())); +} diff --git a/yarn-project/foundation/src/fields/fields.ts b/yarn-project/foundation/src/fields/fields.ts index a9c0e49d6f2..fbecce8f06a 100644 --- a/yarn-project/foundation/src/fields/fields.ts +++ b/yarn-project/foundation/src/fields/fields.ts @@ -1,4 +1,4 @@ -import { BarretenbergLazy } from '@aztec/bb.js'; +import { BarretenbergSync } from '@aztec/bb.js'; import { inspect } from 'util'; @@ -319,8 +319,8 @@ export class Fr extends BaseField { * @returns A square root of the field element (null if it does not exist). */ async sqrt(): Promise { - const wasm = (await BarretenbergLazy.getSingleton()).getWasm(); - const [buf] = await wasm.callWasmExport('bn254_fr_sqrt', [this.toBuffer()], [Fr.SIZE_IN_BYTES + 1]); + const wasm = (await BarretenbergSync.initSingleton()).getWasm(); + const [buf] = wasm.callWasmExport('bn254_fr_sqrt', [this.toBuffer()], [Fr.SIZE_IN_BYTES + 1]); const isSqrt = buf[0] === 1; if (!isSqrt) { // Field element is not a quadratic residue mod p so it has no square root. diff --git a/yarn-project/foundation/src/fields/point.test.ts b/yarn-project/foundation/src/fields/point.test.ts index f969da13303..7f41ae1af9b 100644 --- a/yarn-project/foundation/src/fields/point.test.ts +++ b/yarn-project/foundation/src/fields/point.test.ts @@ -94,7 +94,7 @@ describe('Point', () => { it('serializes from and to JSON', async () => { const p = await Point.random(); - const p2 = jsonParseWithSchema(jsonStringify(p), schemas.Point); + const p2 = await jsonParseWithSchema(jsonStringify(p), schemas.Point); expect(p).toEqual(p2); expect(p2).toBeInstanceOf(Point); }); diff --git a/yarn-project/foundation/src/iterable/toArray.ts b/yarn-project/foundation/src/iterable/toArray.ts index af7554d01f8..6c586a6c3f3 100644 --- a/yarn-project/foundation/src/iterable/toArray.ts +++ b/yarn-project/foundation/src/iterable/toArray.ts @@ -1,4 +1,4 @@ -export async function toArray(iterator: AsyncIterableIterator | IterableIterator): Promise { +export async function toArray(iterator: Iterable | AsyncIterableIterator | IterableIterator): Promise { const arr = []; for await (const i of iterator) { arr.push(i); diff --git a/yarn-project/foundation/src/json-rpc/client/safe_json_rpc_client.ts b/yarn-project/foundation/src/json-rpc/client/safe_json_rpc_client.ts index 93c195a2ebd..471fd9f22ed 100644 --- a/yarn-project/foundation/src/json-rpc/client/safe_json_rpc_client.ts +++ b/yarn-project/foundation/src/json-rpc/client/safe_json_rpc_client.ts @@ -40,7 +40,7 @@ export function createSafeJsonRpcClient( if ([null, undefined, 'null', 'undefined'].includes(res.result)) { return; } - return (schema as ApiSchema)[methodName].returnType().parse(res.result); + return (schema as ApiSchema)[methodName].returnType().parseAsync(res.result); }; const proxy: any = {}; diff --git a/yarn-project/foundation/src/json-rpc/convert.ts b/yarn-project/foundation/src/json-rpc/convert.ts index c518b3faa36..563cb8e8a3a 100644 --- a/yarn-project/foundation/src/json-rpc/convert.ts +++ b/yarn-project/foundation/src/json-rpc/convert.ts @@ -8,8 +8,8 @@ import { type ZodFor } from '../schemas/types.js'; * @param schema - Zod schema. * @returns Result of parsing json with schema. */ -export function jsonParseWithSchema(json: string, schema: ZodFor): T { - return schema.parse(JSON.parse(json)); +export function jsonParseWithSchema(json: string, schema: ZodFor): Promise { + return schema.parseAsync(JSON.parse(json)); } /** diff --git a/yarn-project/foundation/src/json-rpc/server/safe_json_rpc_server.ts b/yarn-project/foundation/src/json-rpc/server/safe_json_rpc_server.ts index 723c0a00eb8..941f5436568 100644 --- a/yarn-project/foundation/src/json-rpc/server/safe_json_rpc_server.ts +++ b/yarn-project/foundation/src/json-rpc/server/safe_json_rpc_server.ts @@ -217,7 +217,7 @@ export class SafeJsonProxy implements Proxy { assert(schemaHasMethod(this.schema, methodName), `Method ${methodName} not found in schema`); const method = this.handler[methodName as keyof T]; assert(typeof method === 'function', `Method ${methodName} is not a function`); - const args = parseWithOptionals(jsonParams, this.schema[methodName].parameters()); + const args = await parseWithOptionals(jsonParams, this.schema[methodName].parameters()); const ret = await method.apply(this.handler, args); this.log.debug(format('response', methodName, ret)); return ret; diff --git a/yarn-project/foundation/src/schemas/parse.test.ts b/yarn-project/foundation/src/schemas/parse.test.ts index 3226c6c6e89..8a2e14e86ee 100644 --- a/yarn-project/foundation/src/schemas/parse.test.ts +++ b/yarn-project/foundation/src/schemas/parse.test.ts @@ -5,58 +5,58 @@ import { parseWithOptionals } from './parse.js'; import { optional } from './utils.js'; describe('parse', () => { - it('parses arguments without optionals', () => { - expect(parseWithOptionals([1, 2], z.tuple([z.number(), z.number()]))).toEqual([1, 2]); + it('parses arguments without optionals', async () => { + await expect(parseWithOptionals([1, 2], z.tuple([z.number(), z.number()]))).resolves.toEqual([1, 2]); }); - it('handles providing all optional arguments', () => { + it('handles providing all optional arguments', async () => { const schema = z.tuple([z.number(), z.number().optional(), z.number().optional()]); - expect(parseWithOptionals([1, 2, 3], schema)).toEqual([1, 2, 3]); + await expect(parseWithOptionals([1, 2, 3], schema)).resolves.toEqual([1, 2, 3]); }); - it('handles some missing optional arguments', () => { + it('handles some missing optional arguments', async () => { const schema = z.tuple([z.number(), z.number().optional(), z.number().optional()]); - expect(parseWithOptionals([1, 2], schema)).toEqual([1, 2, undefined]); + await expect(parseWithOptionals([1, 2], schema)).resolves.toEqual([1, 2, undefined]); }); - it('handles all missing optional arguments', () => { + it('handles all missing optional arguments', async () => { const schema = z.tuple([z.number(), z.number().optional(), z.number().optional()]); - expect(parseWithOptionals([1], schema)).toEqual([1, undefined, undefined]); + await expect(parseWithOptionals([1], schema)).resolves.toEqual([1, undefined, undefined]); }); - it('handles no arguments if all optional', () => { + it('handles no arguments if all optional', async () => { const schema = z.tuple([z.number().optional(), z.number().optional(), z.number().optional()]); - expect(parseWithOptionals([], schema)).toEqual([undefined, undefined, undefined]); + await expect(parseWithOptionals([], schema)).resolves.toEqual([undefined, undefined, undefined]); }); - it('fails if a required argument is missing', () => { + it('fails if a required argument is missing', async () => { const schema = z.tuple([z.number(), z.number(), z.number().optional()]); - expect(() => parseWithOptionals([1], schema)).toThrow(); + await expect(parseWithOptionals([1], schema)).rejects.toThrow(); }); - it('handles coerced bigint', () => { + it('handles coerced bigint', async () => { const schema = z.tuple([z.coerce.bigint()]); - expect(parseWithOptionals(['1'], schema)).toEqual([1n]); + await expect(parseWithOptionals(['1'], schema)).resolves.toEqual([1n]); }); - it('handles coerced optional bigint', () => { + it('handles coerced optional bigint', async () => { const schema = z.tuple([z.coerce.bigint().optional()]); - expect(parseWithOptionals(['1'], schema)).toEqual([1n]); + await expect(parseWithOptionals(['1'], schema)).resolves.toEqual([1n]); }); - it('handles missing coerced optional bigint', () => { + it('handles missing coerced optional bigint', async () => { const schema = z.tuple([z.coerce.bigint().optional()]); - expect(parseWithOptionals([], schema)).toEqual([undefined]); + await expect(parseWithOptionals([], schema)).resolves.toEqual([undefined]); }); - it('fails on missing coerced required bigint', () => { + it('fails on missing coerced required bigint', async () => { const schema = z.tuple([z.coerce.bigint()]); - expect(() => parseWithOptionals([], schema)).toThrow(); + await expect(parseWithOptionals([], schema)).rejects.toThrow(); }); - it('handles explicit undefined values', () => { + it('handles explicit undefined values', async () => { const schema = z.tuple([z.number(), optional(z.number())]); const parsed = JSON.parse(jsonStringify([3, undefined])); - expect(parseWithOptionals(parsed, schema)).toEqual([3, undefined]); + await expect(parseWithOptionals(parsed, schema)).resolves.toEqual([3, undefined]); }); }); diff --git a/yarn-project/foundation/src/schemas/parse.ts b/yarn-project/foundation/src/schemas/parse.ts index 6a3e7d11a8b..02494ea1994 100644 --- a/yarn-project/foundation/src/schemas/parse.ts +++ b/yarn-project/foundation/src/schemas/parse.ts @@ -11,12 +11,12 @@ export function parse(args: IA * Parses the given arguments against a tuple, allowing empty for optional items. * @dev Zod doesn't like tuplues with optional items. See https://github.com/colinhacks/zod/discussions/949. */ -export function parseWithOptionals(args: any[], schema: T): T['_output'] { +export function parseWithOptionals(args: any[], schema: T): Promise { const missingCount = schema.items.length - args.length; const optionalCount = schema.items.filter(isOptional).length; const toParse = missingCount > 0 && missingCount <= optionalCount ? args.concat(times(missingCount, () => undefined)) : args; - return schema.parse(toParse); + return schema.parseAsync(toParse); } function isOptional(schema: z.ZodTypeAny) { diff --git a/yarn-project/key-store/src/key_store.test.ts b/yarn-project/key-store/src/key_store.test.ts index 6ff7b8bea41..6c58ec6388b 100644 --- a/yarn-project/key-store/src/key_store.test.ts +++ b/yarn-project/key-store/src/key_store.test.ts @@ -11,7 +11,7 @@ describe('KeyStore', () => { const sk = new Fr(8923n); const keys = await deriveKeys(sk); const derivedMasterNullifierPublicKey = await derivePublicKeyFromSecretKey(keys.masterNullifierSecretKey); - const computedMasterNullifierPublicKeyHash = derivedMasterNullifierPublicKey.hash(); + const computedMasterNullifierPublicKeyHash = await derivedMasterNullifierPublicKey.hash(); const partialAddress = new Fr(243523n); diff --git a/yarn-project/key-store/src/key_store.ts b/yarn-project/key-store/src/key_store.ts index a9adfd151a1..4e32f837637 100644 --- a/yarn-project/key-store/src/key_store.ts +++ b/yarn-project/key-store/src/key_store.ts @@ -70,16 +70,14 @@ export class KeyStore { // We store pk_m_hash under `account-{n/iv/ov/t}pk_m_hash` key to be able to obtain address and key prefix // using the #getKeyPrefixAndAccount function later on - await this.#keys.set(`${account.toString()}-npk_m_hash`, publicKeys.masterNullifierPublicKey.hash().toBuffer()); - await this.#keys.set( - `${account.toString()}-ivpk_m_hash`, - publicKeys.masterIncomingViewingPublicKey.hash().toBuffer(), - ); - await this.#keys.set( - `${account.toString()}-ovpk_m_hash`, - publicKeys.masterOutgoingViewingPublicKey.hash().toBuffer(), - ); - await this.#keys.set(`${account.toString()}-tpk_m_hash`, publicKeys.masterTaggingPublicKey.hash().toBuffer()); + const masterNullifierPublicKeyHash = await publicKeys.masterNullifierPublicKey.hash(); + await this.#keys.set(`${account.toString()}-npk_m_hash`, masterNullifierPublicKeyHash.toBuffer()); + const masterIncomingViewingPublicKeyHash = await publicKeys.masterIncomingViewingPublicKey.hash(); + await this.#keys.set(`${account.toString()}-ivpk_m_hash`, masterIncomingViewingPublicKeyHash.toBuffer()); + const masterOutgoingViewingPublicKeyHash = await publicKeys.masterOutgoingViewingPublicKey.hash(); + await this.#keys.set(`${account.toString()}-ovpk_m_hash`, masterOutgoingViewingPublicKeyHash.toBuffer()); + const masterTaggingPublicKeyHash = await publicKeys.masterTaggingPublicKey.hash(); + await this.#keys.set(`${account.toString()}-tpk_m_hash`, masterTaggingPublicKeyHash.toBuffer()); // At last, we return the newly derived account address return completeAddress; @@ -115,8 +113,8 @@ export class KeyStore { } const pkM = Point.fromBuffer(pkMBuffer); - - if (!pkM.hash().equals(pkMHash)) { + const computedPkMHash = await pkM.hash(); + if (!computedPkMHash.equals(pkMHash)) { throw new Error(`Could not find ${keyPrefix}pkM for ${keyPrefix}pk_m_hash ${pkMHash.toString()}.`); } @@ -137,7 +135,7 @@ export class KeyStore { } // At last we silo the secret key and return the key validation request - const skApp = computeAppSecretKey(skM, contractAddress, keyPrefix!); + const skApp = await computeAppSecretKey(skM, contractAddress, keyPrefix!); return new KeyValidationRequest(pkM, skApp); } diff --git a/yarn-project/kv-store/src/stores/l2_tips_store.test.ts b/yarn-project/kv-store/src/stores/l2_tips_store.test.ts index 32010b33985..f78b8c1810f 100644 --- a/yarn-project/kv-store/src/stores/l2_tips_store.test.ts +++ b/yarn-project/kv-store/src/stores/l2_tips_store.test.ts @@ -22,7 +22,7 @@ describe('L2TipsStore', () => { }); const makeBlock = (number: number): L2Block => - ({ number, header: { hash: () => new Fr(number) } as BlockHeader } as L2Block); + ({ number, header: { hash: () => Promise.resolve(new Fr(number)) } as BlockHeader } as L2Block); const makeTip = (number: number) => ({ number, hash: number === 0 ? undefined : new Fr(number).toString() }); diff --git a/yarn-project/kv-store/src/stores/l2_tips_store.ts b/yarn-project/kv-store/src/stores/l2_tips_store.ts index 33c513097e5..d4313b1c4b1 100644 --- a/yarn-project/kv-store/src/stores/l2_tips_store.ts +++ b/yarn-project/kv-store/src/stores/l2_tips_store.ts @@ -48,7 +48,7 @@ export class L2TipsStore implements L2BlockStreamEventHandler, L2BlockStreamLoca switch (event.type) { case 'blocks-added': for (const block of event.blocks) { - await this.l2BlockHashesStore.set(block.number, block.header.hash().toString()); + await this.l2BlockHashesStore.set(block.number, (await block.header.hash()).toString()); } await this.l2TipsStore.set('latest', event.blocks.at(-1)!.number); break; diff --git a/yarn-project/merkle-tree/src/pedersen.ts b/yarn-project/merkle-tree/src/pedersen.ts index 8ddfa636549..0305f000f5c 100644 --- a/yarn-project/merkle-tree/src/pedersen.ts +++ b/yarn-project/merkle-tree/src/pedersen.ts @@ -1,4 +1,4 @@ -import { pedersenHash } from '@aztec/foundation/crypto'; +import { pedersenHash } from '@aztec/foundation/crypto/sync'; import { Fr } from '@aztec/foundation/fields'; import { type Hasher } from '@aztec/types/interfaces'; diff --git a/yarn-project/merkle-tree/src/poseidon.ts b/yarn-project/merkle-tree/src/poseidon.ts index a7e81bf97f2..90a76e0af30 100644 --- a/yarn-project/merkle-tree/src/poseidon.ts +++ b/yarn-project/merkle-tree/src/poseidon.ts @@ -1,4 +1,4 @@ -import { poseidon2Hash } from '@aztec/foundation/crypto'; +import { poseidon2Hash } from '@aztec/foundation/crypto/sync'; import { Fr } from '@aztec/foundation/fields'; import { type Hasher } from '@aztec/types/interfaces'; diff --git a/yarn-project/noir-protocol-circuits-types/src/entrypoint/vks.ts b/yarn-project/noir-protocol-circuits-types/src/entrypoint/vks.ts index 5d2f2a5b0f9..f2faf209aa8 100644 --- a/yarn-project/noir-protocol-circuits-types/src/entrypoint/vks.ts +++ b/yarn-project/noir-protocol-circuits-types/src/entrypoint/vks.ts @@ -90,9 +90,9 @@ export const ProtocolCircuitVkIndexes: Record = { ...PrivateKernelResetVkIndexes, }; -function buildVKTree() { - const calculator = new MerkleTreeCalculator(VK_TREE_HEIGHT, Buffer.alloc(32), (a, b) => - poseidon2Hash([a, b]).toBuffer(), +async function buildVKTree() { + const calculator = await MerkleTreeCalculator.create(VK_TREE_HEIGHT, Buffer.alloc(32), async (a, b) => + (await poseidon2Hash([a, b])).toBuffer(), ); const vkHashes = new Array(2 ** VK_TREE_HEIGHT).fill(Buffer.alloc(32)); @@ -108,18 +108,19 @@ function buildVKTree() { let vkTree: MerkleTree | undefined; -export function getVKTree() { +export async function getVKTree() { if (!vkTree) { - vkTree = buildVKTree(); + vkTree = await buildVKTree(); } return vkTree; } -export function getVKTreeRoot() { - return Fr.fromBuffer(getVKTree().root); +export async function getVKTreeRoot() { + const tree = await getVKTree(); + return Fr.fromBuffer(tree.root); } -export function getVKIndex(vk: VerificationKeyData | VerificationKeyAsFields | Fr) { +export async function getVKIndex(vk: VerificationKeyData | VerificationKeyAsFields | Fr) { let hash; if (vk instanceof VerificationKeyData) { hash = vk.keyAsFields.hash; @@ -129,18 +130,19 @@ export function getVKIndex(vk: VerificationKeyData | VerificationKeyAsFields | F hash = vk; } - const index = getVKTree().getIndex(hash.toBuffer()); + const tree = await getVKTree(); + + const index = tree.getIndex(hash.toBuffer()); if (index < 0) { throw new Error(`VK index for ${hash.toString()} not found in VK tree`); } return index; } -export function getVKSiblingPath(vkIndex: number) { +export async function getVKSiblingPath(vkIndex: number) { + const tree = await getVKTree(); return assertLength( - getVKTree() - .getSiblingPath(vkIndex) - .map(buf => new Fr(buf)), + tree.getSiblingPath(vkIndex).map(buf => new Fr(buf)), VK_TREE_HEIGHT, ); } diff --git a/yarn-project/noir-protocol-circuits-types/src/noir_test_gen.test.ts b/yarn-project/noir-protocol-circuits-types/src/noir_test_gen.test.ts index 33c5cf551f3..2ce0f96fcb3 100644 --- a/yarn-project/noir-protocol-circuits-types/src/noir_test_gen.test.ts +++ b/yarn-project/noir-protocol-circuits-types/src/noir_test_gen.test.ts @@ -51,15 +51,16 @@ describe('Data generation for noir tests', () => { test.each(contracts)('Computes contract info for %s', async contract => { const contractClass: ContractClass = { ...contract, publicFunctions: [], version: 1 }; - const contractClassId = computeContractClassId(contractClass); - const initializationHash = computeInitializationHashFromEncodedArgs(constructorSelector, []); - const { artifactHash, privateFunctionsRoot, publicBytecodeCommitment } = - computeContractClassIdPreimage(contractClass); + const contractClassId = await computeContractClassId(contractClass); + const initializationHash = await computeInitializationHashFromEncodedArgs(constructorSelector, []); + const { artifactHash, privateFunctionsRoot, publicBytecodeCommitment } = await computeContractClassIdPreimage( + contractClass, + ); const deployer = AztecAddress.ZERO; const instance: ContractInstance = { ...contract, version: 1, initializationHash, contractClassId, deployer }; const address = await computeContractAddressFromInstance(instance); - const saltedInitializationHash = computeSaltedInitializationHash(instance); - const partialAddress = computePartialAddress(instance); + const saltedInitializationHash = await computeSaltedInitializationHash(instance); + const partialAddress = await computePartialAddress(instance); /* eslint-disable camelcase */ expect( @@ -79,8 +80,8 @@ describe('Data generation for noir tests', () => { /* eslint-enable camelcase */ }); - test.each(contracts)('Computes function tree for %s', contract => { - const tree = computePrivateFunctionsTree(contract.privateFunctions); + test.each(contracts)('Computes function tree for %s', async contract => { + const tree = await computePrivateFunctionsTree(contract.privateFunctions); expect( tree.leaves.map((leaf, index) => ({ index, diff --git a/yarn-project/noir-protocol-circuits-types/src/scripts/generate_vk_hashes.ts b/yarn-project/noir-protocol-circuits-types/src/scripts/generate_vk_hashes.ts index 847b71a557f..e906a8418fe 100644 --- a/yarn-project/noir-protocol-circuits-types/src/scripts/generate_vk_hashes.ts +++ b/yarn-project/noir-protocol-circuits-types/src/scripts/generate_vk_hashes.ts @@ -33,7 +33,7 @@ const main = async () => { if (!content.vkHash) { const { keyAsFields } = content; - content.vkHash = hashVK(keyAsFields.map((str: string) => Fr.fromHexString(str))).toString(); + content.vkHash = (await hashVK(keyAsFields.map((str: string) => Fr.fromHexString(str)))).toString(); await fs.writeFile(keyPath, JSON.stringify(content, null, 2)); } } diff --git a/yarn-project/noir-protocol-circuits-types/src/utils/server/foreign_call_handler.ts b/yarn-project/noir-protocol-circuits-types/src/utils/server/foreign_call_handler.ts index d95dec07a13..00ce35f286f 100644 --- a/yarn-project/noir-protocol-circuits-types/src/utils/server/foreign_call_handler.ts +++ b/yarn-project/noir-protocol-circuits-types/src/utils/server/foreign_call_handler.ts @@ -14,7 +14,7 @@ function toACVMField(field: Fr): string { return `0x${field.toBuffer().toString('hex')}`; } -export function foreignCallHandler(name: string, args: ForeignCallInput[]): Promise { +export async function foreignCallHandler(name: string, args: ForeignCallInput[]): Promise { // ForeignCallInput is actually a string[], so the args are string[][]. const log = createLogger('noir-protocol-circuits:oracle'); @@ -42,10 +42,10 @@ export function foreignCallHandler(name: string, args: ForeignCallInput[]): Prom // const blobsAsFr: Fr[] = args[0].map((field: string) => fromACVMField(field)).filter(field => !field.isZero()); // ...but we now have private logs which have a fixed number of fields and may have 0 values. // TODO(Miranda): trim 0 fields from private logs - const blobs = Blob.getBlobs(blobsAsFr); + const blobs = await Blob.getBlobs(blobsAsFr); const blobPublicInputs = BlockBlobPublicInputs.fromBlobs(blobs); // Checks on injected values: - const hash = spongeBlob.squeeze(); + const hash = await spongeBlob.squeeze(); blobs.forEach((blob, i) => { const injected = kzgCommitments.slice(2 * i, 2 * i + 2); const calculated = blob.commitmentToFields(); diff --git a/yarn-project/p2p/src/client/p2p_client.test.ts b/yarn-project/p2p/src/client/p2p_client.test.ts index 4dc69f6d7cb..61273ddd725 100644 --- a/yarn-project/p2p/src/client/p2p_client.test.ts +++ b/yarn-project/p2p/src/client/p2p_client.test.ts @@ -28,7 +28,7 @@ describe('In-Memory P2P Client', () => { beforeEach(async () => { txPool = mock(); txPool.getAllTxs.mockReturnValue([]); - txPool.getPendingTxHashes.mockReturnValue([]); + txPool.getPendingTxHashes.mockReturnValue(Promise.resolve([])); txPool.getMinedTxHashes.mockReturnValue([]); txPool.getAllTxHashes.mockReturnValue([]); @@ -81,8 +81,8 @@ describe('In-Memory P2P Client', () => { it('adds txs to pool', async () => { await client.start(); - const tx1 = mockTx(); - const tx2 = mockTx(); + const tx1 = await mockTx(); + const tx2 = await mockTx(); await client.sendTx(tx1); await client.sendTx(tx2); @@ -92,14 +92,14 @@ describe('In-Memory P2P Client', () => { it('rejects txs after being stopped', async () => { await client.start(); - const tx1 = mockTx(); - const tx2 = mockTx(); + const tx1 = await mockTx(); + const tx2 = await mockTx(); await client.sendTx(tx1); await client.sendTx(tx2); expect(txPool.addTxs).toHaveBeenCalledTimes(2); await client.stop(); - const tx3 = mockTx(); + const tx3 = await mockTx(); await expect(client.sendTx(tx3)).rejects.toThrow(); expect(txPool.addTxs).toHaveBeenCalledTimes(2); }); @@ -247,17 +247,17 @@ describe('In-Memory P2P Client', () => { // add two txs to the pool. One build against block 90, one against block 95 // then prune the chain back to block 90 // only one tx should be deleted - const goodTx = mockTx(); + const goodTx = await mockTx(); goodTx.data.constants.historicalHeader.globalVariables.blockNumber = new Fr(90); - const badTx = mockTx(); + const badTx = await mockTx(); badTx.data.constants.historicalHeader.globalVariables.blockNumber = new Fr(95); txPool.getAllTxs.mockReturnValue([goodTx, badTx]); blockSource.removeBlocks(10); await sleep(150); - expect(txPool.deleteTxs).toHaveBeenCalledWith([badTx.getTxHash()]); + expect(txPool.deleteTxs).toHaveBeenCalledWith([await badTx.getTxHash()]); await client.stop(); }); @@ -271,26 +271,26 @@ describe('In-Memory P2P Client', () => { // add three txs to the pool built against different blocks // then prune the chain back to block 90 // only one tx should be deleted - const goodButOldTx = mockTx(); + const goodButOldTx = await mockTx(); goodButOldTx.data.constants.historicalHeader.globalVariables.blockNumber = new Fr(89); - const goodTx = mockTx(); + const goodTx = await mockTx(); goodTx.data.constants.historicalHeader.globalVariables.blockNumber = new Fr(90); - const badTx = mockTx(); + const badTx = await mockTx(); badTx.data.constants.historicalHeader.globalVariables.blockNumber = new Fr(95); txPool.getAllTxs.mockReturnValue([goodButOldTx, goodTx, badTx]); txPool.getMinedTxHashes.mockReturnValue([ - [goodButOldTx.getTxHash(), 90], - [goodTx.getTxHash(), 91], + [await goodButOldTx.getTxHash(), 90], + [await goodTx.getTxHash(), 91], ]); blockSource.removeBlocks(10); await sleep(150); - expect(txPool.deleteTxs).toHaveBeenCalledWith([badTx.getTxHash()]); + expect(txPool.deleteTxs).toHaveBeenCalledWith([await badTx.getTxHash()]); await sleep(150); - expect(txPool.markMinedAsPending).toHaveBeenCalledWith([goodTx.getTxHash()]); + expect(txPool.markMinedAsPending).toHaveBeenCalledWith([await goodTx.getTxHash()]); await client.stop(); }); }); diff --git a/yarn-project/p2p/src/client/p2p_client.ts b/yarn-project/p2p/src/client/p2p_client.ts index 1944df3f1d9..624848c0bed 100644 --- a/yarn-project/p2p/src/client/p2p_client.ts +++ b/yarn-project/p2p/src/client/p2p_client.ts @@ -150,10 +150,10 @@ export type P2P = P2PApi & { getTxStatus(txHash: TxHash): 'pending' | 'mined' | undefined; /** Returns an iterator over pending txs on the mempool. */ - iteratePendingTxs(): Iterable; + iteratePendingTxs(): AsyncIterableIterator; /** Returns the number of pending txs in the mempool. */ - getPendingTxCount(): number; + getPendingTxCount(): Promise; /** * Starts the p2p client. @@ -414,19 +414,19 @@ export class P2PClient this.log.info('P2P client stopped.'); } - @trackSpan('p2pClient.broadcastProposal', proposal => ({ + @trackSpan('p2pClient.broadcastProposal', async proposal => ({ [Attributes.BLOCK_NUMBER]: proposal.payload.header.globalVariables.blockNumber.toNumber(), [Attributes.SLOT_NUMBER]: proposal.payload.header.globalVariables.slotNumber.toNumber(), [Attributes.BLOCK_ARCHIVE]: proposal.archive.toString(), - [Attributes.P2P_ID]: proposal.p2pMessageIdentifier().toString(), + [Attributes.P2P_ID]: (await proposal.p2pMessageIdentifier()).toString(), })) public broadcastProposal(proposal: BlockProposal): void { this.log.verbose(`Broadcasting proposal ${proposal.p2pMessageIdentifier()} to peers`); return this.p2pService.propagate(proposal); } - public getAttestationsForSlot(slot: bigint, proposalId: string): Promise { - return Promise.resolve(this.attestationPool?.getAttestationsForSlot(slot, proposalId) ?? []); + public async getAttestationsForSlot(slot: bigint, proposalId: string): Promise { + return (await this.attestationPool?.getAttestationsForSlot(slot, proposalId)) ?? []; } // REVIEW: https://github.com/AztecProtocol/aztec-packages/issues/7963 @@ -475,13 +475,13 @@ export class P2PClient return Promise.resolve(this.getTxs('pending')); } - public getPendingTxCount(): number { - return this.txPool.getPendingTxHashes().length; + public async getPendingTxCount(): Promise { + return (await this.txPool.getPendingTxHashes()).length; } - public *iteratePendingTxs() { - const pendingTxHashes = this.txPool.getPendingTxHashes(); - for (const txHash of pendingTxHashes) { + public async *iteratePendingTxs(): AsyncIterableIterator { + const txHashes = await this.txPool.getPendingTxHashes(); + for (const txHash of txHashes) { const tx = this.txPool.getTxByHash(txHash); if (tx) { yield tx; @@ -493,7 +493,7 @@ export class P2PClient * Returns all transactions in the transaction pool. * @returns An array of Txs. */ - public getTxs(filter: 'all' | 'pending' | 'mined'): Tx[] { + public async getTxs(filter: 'all' | 'pending' | 'mined'): Promise { if (filter === 'all') { return this.txPool.getAllTxs(); } else if (filter === 'mined') { @@ -502,10 +502,8 @@ export class P2PClient .map(([txHash]) => this.txPool.getTxByHash(txHash)) .filter((tx): tx is Tx => !!tx); } else if (filter === 'pending') { - return this.txPool - .getPendingTxHashes() - .map(txHash => this.txPool.getTxByHash(txHash)) - .filter((tx): tx is Tx => !!tx); + const txHashes = await this.txPool.getPendingTxHashes(); + return txHashes.map(txHash => this.txPool.getTxByHash(txHash)).filter((tx): tx is Tx => !!tx); } else { const _: never = filter; throw new Error(`Unknown filter ${filter}`); @@ -616,7 +614,10 @@ export class P2PClient const blockHash = blockNumber == 0 ? '' - : await this.l2BlockSource.getBlockHeader(blockNumber).then(header => header?.hash().toString()); + : await this.l2BlockSource + .getBlockHeader(blockNumber) + .then(header => header?.hash()) + .then(hash => hash?.toString()); return Promise.resolve({ state: this.currentState, syncedToL2Block: { number: blockNumber, hash: blockHash }, @@ -660,7 +661,9 @@ export class P2PClient await this.markTxsAsMinedFromBlocks(blocks); const lastBlockNum = blocks[blocks.length - 1].number; - await Promise.all(blocks.map(block => this.synchedBlockHashes.set(block.number, block.hash().toString()))); + await Promise.all( + blocks.map(async block => this.synchedBlockHashes.set(block.number, (await block.hash()).toString())), + ); await this.synchedLatestBlockNumber.set(lastBlockNum); this.log.debug(`Synched to latest block ${lastBlockNum}`); await this.startServiceIfSynched(); @@ -717,7 +720,7 @@ export class P2PClient for (const tx of this.txPool.getAllTxs()) { // every tx that's been generated against a block that has now been pruned is no longer valid if (tx.data.constants.historicalHeader.globalVariables.blockNumber.toNumber() > latestBlock) { - txsToDelete.push(tx.getTxHash()); + txsToDelete.push(await tx.getTxHash()); } } diff --git a/yarn-project/p2p/src/mem_pools/attestation_pool/attestation_pool_test_suite.ts b/yarn-project/p2p/src/mem_pools/attestation_pool/attestation_pool_test_suite.ts index f8f838a08ab..daec7271ac8 100644 --- a/yarn-project/p2p/src/mem_pools/attestation_pool/attestation_pool_test_suite.ts +++ b/yarn-project/p2p/src/mem_pools/attestation_pool/attestation_pool_test_suite.ts @@ -29,7 +29,7 @@ export function describeAttestationPool(getAttestationPool: () => AttestationPoo const createAttestationsForSlot = (slotNumber: number) => { const archive = Fr.random(); - return signers.map(signer => mockAttestation(signer, slotNumber, archive)); + return Promise.all(signers.map(signer => mockAttestation(signer, slotNumber, archive))); }; // We compare buffers as the objects can have cached values attached to them which are not serialised @@ -44,7 +44,7 @@ export function describeAttestationPool(getAttestationPool: () => AttestationPoo it('should add attestations to pool', async () => { const slotNumber = 420; const archive = Fr.random(); - const attestations = signers.map(signer => mockAttestation(signer, slotNumber, archive)); + const attestations = await Promise.all(signers.map(signer => mockAttestation(signer, slotNumber, archive))); await ap.addAttestations(attestations); @@ -75,7 +75,7 @@ export function describeAttestationPool(getAttestationPool: () => AttestationPoo const attestations: BlockAttestation[] = []; const signer = signers[0]; for (let i = 0; i < NUMBER_OF_SIGNERS_PER_TEST; i++) { - attestations.push(mockAttestation(signer, slotNumber, archive, txs)); + attestations.push(await mockAttestation(signer, slotNumber, archive, txs)); } await ap.addAttestations(attestations); @@ -84,12 +84,12 @@ export function describeAttestationPool(getAttestationPool: () => AttestationPoo expect(retreivedAttestations.length).toBe(1); expect(retreivedAttestations[0].toBuffer()).toEqual(attestations[0].toBuffer()); expect(retreivedAttestations[0].payload.txHashes).toEqual(txs); - expect(retreivedAttestations[0].getSender().toString()).toEqual(signer.address.toString()); + expect((await retreivedAttestations[0].getSender()).toString()).toEqual(signer.address.toString()); }); it('Should store attestations by differing slot', async () => { const slotNumbers = [1, 2, 3, 4]; - const attestations = signers.map((signer, i) => mockAttestation(signer, slotNumbers[i])); + const attestations = await Promise.all(signers.map((signer, i) => mockAttestation(signer, slotNumbers[i]))); await ap.addAttestations(attestations); @@ -107,7 +107,9 @@ export function describeAttestationPool(getAttestationPool: () => AttestationPoo it('Should store attestations by differing slot and archive', async () => { const slotNumbers = [1, 1, 2, 3]; const archives = [Fr.random(), Fr.random(), Fr.random(), Fr.random()]; - const attestations = signers.map((signer, i) => mockAttestation(signer, slotNumbers[i], archives[i])); + const attestations = await Promise.all( + signers.map((signer, i) => mockAttestation(signer, slotNumbers[i], archives[i])), + ); await ap.addAttestations(attestations); @@ -125,7 +127,7 @@ export function describeAttestationPool(getAttestationPool: () => AttestationPoo it('Should delete attestations', async () => { const slotNumber = 420; const archive = Fr.random(); - const attestations = signers.map(signer => mockAttestation(signer, slotNumber, archive)); + const attestations = await Promise.all(signers.map(signer => mockAttestation(signer, slotNumber, archive))); const proposalId = attestations[0].archive.toString(); await ap.addAttestations(attestations); @@ -165,12 +167,12 @@ export function describeAttestationPool(getAttestationPool: () => AttestationPoo it('Should blanket delete attestations per slot and proposal', async () => { const slotNumber = 420; const archive = Fr.random(); - const attestations = signers.map(signer => mockAttestation(signer, slotNumber, archive)); + const attestations = await Promise.all(signers.map(signer => mockAttestation(signer, slotNumber, archive))); const proposalId = attestations[0].archive.toString(); // Add another set of attestations with a different proposalId, yet the same slot const archive2 = Fr.random(); - const attestations2 = signers.map(signer => mockAttestation(signer, slotNumber, archive2)); + const attestations2 = await Promise.all(signers.map(signer => mockAttestation(signer, slotNumber, archive2))); const proposalId2 = attestations2[0].archive.toString(); await ap.addAttestations(attestations); @@ -212,7 +214,9 @@ export function describeAttestationPool(getAttestationPool: () => AttestationPoo it('Should delete attestations older than a given slot', async () => { const slotNumbers = [1, 2, 3, 69, 72, 74, 88, 420]; - const attestations = slotNumbers.map(slotNumber => createAttestationsForSlot(slotNumber)).flat(); + const attestations = ( + await Promise.all(slotNumbers.map(slotNumber => createAttestationsForSlot(slotNumber))) + ).flat(); const proposalId = attestations[0].archive.toString(); await ap.addAttestations(attestations); diff --git a/yarn-project/p2p/src/mem_pools/attestation_pool/kv_attestation_pool.ts b/yarn-project/p2p/src/mem_pools/attestation_pool/kv_attestation_pool.ts index ea6f848e9fd..9b0a2fe93f9 100644 --- a/yarn-project/p2p/src/mem_pools/attestation_pool/kv_attestation_pool.ts +++ b/yarn-project/p2p/src/mem_pools/attestation_pool/kv_attestation_pool.ts @@ -44,7 +44,7 @@ export class KvAttestationPool implements AttestationPool { for (const attestation of attestations) { const slotNumber = attestation.payload.header.globalVariables.slotNumber.toString(); const proposalId = attestation.archive.toString(); - const address = attestation.getSender().toString(); + const address = (await attestation.getSender()).toString(); // Index the proposalId in the slot map await this.attestations.set(slotNumber, proposalId); @@ -135,7 +135,7 @@ export class KvAttestationPool implements AttestationPool { const proposalMap = this.getProposalMap(slotNumber, proposalId); if (proposalMap) { - const address = attestation.getSender().toString(); + const address = (await attestation.getSender()).toString(); deletionPromises.push(proposalMap.delete(address)); this.log.debug(`Deleted attestation for slot ${slotNumber} from ${address}`); } diff --git a/yarn-project/p2p/src/mem_pools/attestation_pool/memory_attestation_pool.ts b/yarn-project/p2p/src/mem_pools/attestation_pool/memory_attestation_pool.ts index 35d94c7b11b..7da164fb1d9 100644 --- a/yarn-project/p2p/src/mem_pools/attestation_pool/memory_attestation_pool.ts +++ b/yarn-project/p2p/src/mem_pools/attestation_pool/memory_attestation_pool.ts @@ -26,13 +26,13 @@ export class InMemoryAttestationPool implements AttestationPool { return Promise.resolve([]); } - public addAttestations(attestations: BlockAttestation[]): Promise { + public async addAttestations(attestations: BlockAttestation[]): Promise { for (const attestation of attestations) { // Perf: order and group by slot before insertion const slotNumber = attestation.payload.header.globalVariables.slotNumber; const proposalId = attestation.archive.toString(); - const address = attestation.getSender(); + const address = await attestation.getSender(); const slotAttestationMap = getSlotOrDefault(this.attestations, slotNumber.toBigInt()); const proposalAttestationMap = getProposalOrDefault(slotAttestationMap, proposalId); @@ -105,7 +105,7 @@ export class InMemoryAttestationPool implements AttestationPool { return Promise.resolve(); } - public deleteAttestations(attestations: BlockAttestation[]): Promise { + public async deleteAttestations(attestations: BlockAttestation[]): Promise { for (const attestation of attestations) { const slotNumber = attestation.payload.header.globalVariables.slotNumber; const slotAttestationMap = this.attestations.get(slotNumber.toBigInt()); @@ -113,7 +113,7 @@ export class InMemoryAttestationPool implements AttestationPool { const proposalId = attestation.archive.toString(); const proposalAttestationMap = getProposalOrDefault(slotAttestationMap, proposalId); if (proposalAttestationMap) { - const address = attestation.getSender(); + const address = await attestation.getSender(); proposalAttestationMap.delete(address.toString()); this.log.debug(`Deleted attestation for slot ${slotNumber} from ${address}`); } diff --git a/yarn-project/p2p/src/mem_pools/attestation_pool/mocks.ts b/yarn-project/p2p/src/mem_pools/attestation_pool/mocks.ts index 0d29cb8b2b9..1ee13c43d52 100644 --- a/yarn-project/p2p/src/mem_pools/attestation_pool/mocks.ts +++ b/yarn-project/p2p/src/mem_pools/attestation_pool/mocks.ts @@ -27,17 +27,17 @@ export const generateAccount = (): LocalAccount => { * @param slot The slot number the attestation is for * @returns A Block Attestation */ -export const mockAttestation = ( +export const mockAttestation = async ( signer: Secp256k1Signer, slot: number = 0, archive: Fr = Fr.random(), txs: TxHash[] = [0, 1, 2, 3, 4, 5].map(() => TxHash.random()), -): BlockAttestation => { +): Promise => { // Use arbitrary numbers for all other than slot const header = makeHeader(1, 2, slot); const payload = new ConsensusPayload(header, archive, txs); - const hash = getHashedSignaturePayloadEthSignedMessage(payload, SignatureDomainSeparator.blockAttestation); + const hash = await getHashedSignaturePayloadEthSignedMessage(payload, SignatureDomainSeparator.blockAttestation); const signature = signer.sign(hash); return new BlockAttestation(payload, signature); diff --git a/yarn-project/p2p/src/mem_pools/tx_pool/aztec_kv_tx_pool.test.ts b/yarn-project/p2p/src/mem_pools/tx_pool/aztec_kv_tx_pool.test.ts index ea584401412..b76166d1c12 100644 --- a/yarn-project/p2p/src/mem_pools/tx_pool/aztec_kv_tx_pool.test.ts +++ b/yarn-project/p2p/src/mem_pools/tx_pool/aztec_kv_tx_pool.test.ts @@ -16,30 +16,30 @@ describe('KV TX pool', () => { // set the archived tx limit to 2 txPool = new AztecKVTxPool(openTmpStore(), openTmpStore(), undefined, 2); - const tx1 = mockTx(1); - const tx2 = mockTx(2); - const tx3 = mockTx(3); - const tx4 = mockTx(4); - const tx5 = mockTx(5); + const tx1 = await mockTx(1); + const tx2 = await mockTx(2); + const tx3 = await mockTx(3); + const tx4 = await mockTx(4); + const tx5 = await mockTx(5); await txPool.addTxs([tx1, tx2, tx3, tx4, tx5]); // delete two txs and assert that they are properly archived - await txPool.deleteTxs([tx1.getTxHash(), tx2.getTxHash()]); - expect(txPool.getArchivedTxByHash(tx1.getTxHash())).toEqual(tx1); - expect(txPool.getArchivedTxByHash(tx2.getTxHash())).toEqual(tx2); + await txPool.deleteTxs([await tx1.getTxHash(), await tx2.getTxHash()]); + expect(txPool.getArchivedTxByHash(await tx1.getTxHash())).toEqual(tx1); + expect(txPool.getArchivedTxByHash(await tx2.getTxHash())).toEqual(tx2); // delete a single tx and assert that the first tx is purged and the new tx is archived - await txPool.deleteTxs([tx3.getTxHash()]); - expect(txPool.getArchivedTxByHash(tx1.getTxHash())).toBeUndefined(); - expect(txPool.getArchivedTxByHash(tx2.getTxHash())).toEqual(tx2); - expect(txPool.getArchivedTxByHash(tx3.getTxHash())).toEqual(tx3); + await txPool.deleteTxs([await tx3.getTxHash()]); + expect(txPool.getArchivedTxByHash(await tx1.getTxHash())).toBeUndefined(); + expect(txPool.getArchivedTxByHash(await tx2.getTxHash())).toEqual(tx2); + expect(txPool.getArchivedTxByHash(await tx3.getTxHash())).toEqual(tx3); // delete multiple txs and assert that the old txs are purged and the new txs are archived - await txPool.deleteTxs([tx4.getTxHash(), tx5.getTxHash()]); - expect(txPool.getArchivedTxByHash(tx1.getTxHash())).toBeUndefined(); - expect(txPool.getArchivedTxByHash(tx2.getTxHash())).toBeUndefined(); - expect(txPool.getArchivedTxByHash(tx3.getTxHash())).toBeUndefined(); - expect(txPool.getArchivedTxByHash(tx4.getTxHash())).toEqual(tx4); - expect(txPool.getArchivedTxByHash(tx5.getTxHash())).toEqual(tx5); + await txPool.deleteTxs([await tx4.getTxHash(), await tx5.getTxHash()]); + expect(txPool.getArchivedTxByHash(await tx1.getTxHash())).toBeUndefined(); + expect(txPool.getArchivedTxByHash(await tx2.getTxHash())).toBeUndefined(); + expect(txPool.getArchivedTxByHash(await tx3.getTxHash())).toBeUndefined(); + expect(txPool.getArchivedTxByHash(await tx4.getTxHash())).toEqual(tx4); + expect(txPool.getArchivedTxByHash(await tx5.getTxHash())).toEqual(tx5); }); }); diff --git a/yarn-project/p2p/src/mem_pools/tx_pool/aztec_kv_tx_pool.ts b/yarn-project/p2p/src/mem_pools/tx_pool/aztec_kv_tx_pool.ts index cc26247a930..61f15c16e4c 100644 --- a/yarn-project/p2p/src/mem_pools/tx_pool/aztec_kv_tx_pool.ts +++ b/yarn-project/p2p/src/mem_pools/tx_pool/aztec_kv_tx_pool.ts @@ -115,8 +115,10 @@ export class AztecKVTxPool implements TxPool { }); } - public getPendingTxHashes(): TxHash[] { - return Array.from(this.#pendingTxPriorityToHash.values({ reverse: true })).map(x => TxHash.fromString(x)); + public getPendingTxHashes(): Promise { + return Promise.resolve( + Array.from(this.#pendingTxPriorityToHash.values({ reverse: true })).map(x => TxHash.fromString(x)), + ); } public getMinedTxHashes(): [TxHash, number][] { @@ -172,14 +174,17 @@ export class AztecKVTxPool implements TxPool { * @param txs - An array of txs to be added to the pool. * @returns Empty promise. */ - public addTxs(txs: Tx[]): Promise { + public async addTxs(txs: Tx[]): Promise { + const hashesAndStats = await Promise.all( + txs.map(async tx => ({ txHash: await tx.getTxHash(), txStats: await tx.getStats() })), + ); return this.#store.transaction(() => { let pendingCount = 0; - for (const tx of txs) { - const txHash = tx.getTxHash(); + txs.forEach((tx, i) => { + const { txHash, txStats } = hashesAndStats[i]; this.#log.verbose(`Adding tx ${txHash.toString()} to pool`, { eventName: 'tx-added-to-pool', - ...tx.getStats(), + ...txStats, } satisfies TxAddedToPoolStats); const key = txHash.toString(); @@ -191,7 +196,7 @@ export class AztecKVTxPool implements TxPool { void this.#pendingTxPriorityToHash.set(getPendingTxPriority(tx), key); this.#metrics.recordSize(tx); } - } + }); this.#metrics.recordAddedObjects(pendingCount, 'pending'); }); @@ -264,13 +269,14 @@ export class AztecKVTxPool implements TxPool { * @param txs - The list of transactions to archive. * @returns Empty promise. */ - private archiveTxs(txs: Tx[]): Promise { + private async archiveTxs(txs: Tx[]): Promise { + const txHashes = await Promise.all(txs.map(tx => tx.getTxHash())); return this.#archive.transaction(() => { // calcualte the head and tail indices of the archived txs by insertion order. let headIdx = (this.#archivedTxIndices.entries({ limit: 1, reverse: true }).next().value?.[0] ?? -1) + 1; let tailIdx = this.#archivedTxIndices.entries({ limit: 1 }).next().value?.[0] ?? 0; - for (const tx of txs) { + txs.forEach((tx, i) => { while (headIdx - tailIdx >= this.#archivedTxLimit) { const txHash = this.#archivedTxIndices.get(tailIdx); if (txHash) { @@ -287,11 +293,11 @@ export class AztecKVTxPool implements TxPool { tx.enqueuedPublicFunctionCalls, tx.publicTeardownFunctionCall, ); - const txHash = tx.getTxHash().toString(); + const txHash = txHashes[i].toString(); void this.#archivedTxs.set(txHash, archivedTx.toBuffer()); void this.#archivedTxIndices.set(headIdx, txHash); headIdx++; - } + }); }); } } diff --git a/yarn-project/p2p/src/mem_pools/tx_pool/memory_tx_pool.ts b/yarn-project/p2p/src/mem_pools/tx_pool/memory_tx_pool.ts index 2fc985a6a1f..32670ea58df 100644 --- a/yarn-project/p2p/src/mem_pools/tx_pool/memory_tx_pool.ts +++ b/yarn-project/p2p/src/mem_pools/tx_pool/memory_tx_pool.ts @@ -68,11 +68,12 @@ export class InMemoryTxPool implements TxPool { return Promise.resolve(); } - public getPendingTxHashes(): TxHash[] { - return this.getAllTxs() - .sort((tx1, tx2) => -getPendingTxPriority(tx1).localeCompare(getPendingTxPriority(tx2))) - .map(tx => tx.getTxHash()) - .filter(txHash => this.pendingTxs.has(txHash.toBigInt())); + public async getPendingTxHashes(): Promise { + const txs = this.getAllTxs().sort( + (tx1, tx2) => -getPendingTxPriority(tx1).localeCompare(getPendingTxPriority(tx2)), + ); + const txHashes = await Promise.all(txs.map(tx => tx.getTxHash())); + return txHashes.filter(txHash => this.pendingTxs.has(txHash.toBigInt())); } public getMinedTxHashes(): [TxHash, number][] { @@ -109,13 +110,13 @@ export class InMemoryTxPool implements TxPool { * @param txs - An array of txs to be added to the pool. * @returns Empty promise. */ - public addTxs(txs: Tx[]): Promise { + public async addTxs(txs: Tx[]): Promise { let pending = 0; for (const tx of txs) { - const txHash = tx.getTxHash(); + const txHash = await tx.getTxHash(); this.log.verbose(`Adding tx ${txHash.toString()} to pool`, { eventName: 'tx-added-to-pool', - ...tx.getStats(), + ...(await tx.getStats()), } satisfies TxAddedToPoolStats); const key = txHash.toBigInt(); @@ -128,7 +129,7 @@ export class InMemoryTxPool implements TxPool { } this.metrics.recordAddedObjects(pending, 'pending'); - return Promise.resolve(); + return; } /** diff --git a/yarn-project/p2p/src/mem_pools/tx_pool/tx_pool.ts b/yarn-project/p2p/src/mem_pools/tx_pool/tx_pool.ts index 6ad69b3c7de..9774fda3322 100644 --- a/yarn-project/p2p/src/mem_pools/tx_pool/tx_pool.ts +++ b/yarn-project/p2p/src/mem_pools/tx_pool/tx_pool.ts @@ -59,7 +59,7 @@ export interface TxPool { * Gets the hashes of pending transactions currently in the tx pool sorted by priority (see getPendingTxPriority). * @returns An array of pending transaction hashes found in the tx pool. */ - getPendingTxHashes(): TxHash[]; + getPendingTxHashes(): Promise; /** * Gets the hashes of mined transactions currently in the tx pool. diff --git a/yarn-project/p2p/src/mem_pools/tx_pool/tx_pool_test_suite.ts b/yarn-project/p2p/src/mem_pools/tx_pool/tx_pool_test_suite.ts index 056d12d6e4a..93ead92156c 100644 --- a/yarn-project/p2p/src/mem_pools/tx_pool/tx_pool_test_suite.ts +++ b/yarn-project/p2p/src/mem_pools/tx_pool/tx_pool_test_suite.ts @@ -16,76 +16,77 @@ export function describeTxPool(getTxPool: () => TxPool) { }); it('Adds txs to the pool as pending', async () => { - const tx1 = mockTx(); + const tx1 = await mockTx(); await pool.addTxs([tx1]); - const poolTx = pool.getTxByHash(tx1.getTxHash()); - expect(poolTx!.getTxHash()).toEqual(tx1.getTxHash()); - expect(pool.getTxStatus(tx1.getTxHash())).toEqual('pending'); - expect(pool.getPendingTxHashes()).toEqual([tx1.getTxHash()]); + const poolTx = pool.getTxByHash(await tx1.getTxHash()); + expect(await poolTx!.getTxHash()).toEqual(await tx1.getTxHash()); + expect(pool.getTxStatus(await tx1.getTxHash())).toEqual('pending'); + await expect(pool.getPendingTxHashes()).resolves.toEqual([await tx1.getTxHash()]); }); it('Removes txs from the pool', async () => { - const tx1 = mockTx(); + const tx1 = await mockTx(); await pool.addTxs([tx1]); - await pool.deleteTxs([tx1.getTxHash()]); + await pool.deleteTxs([await tx1.getTxHash()]); - expect(pool.getTxByHash(tx1.getTxHash())).toBeFalsy(); - expect(pool.getTxStatus(tx1.getTxHash())).toBeUndefined(); + expect(pool.getTxByHash(await tx1.getTxHash())).toBeFalsy(); + expect(pool.getTxStatus(await tx1.getTxHash())).toBeUndefined(); }); it('Marks txs as mined', async () => { - const tx1 = mockTx(1); - const tx2 = mockTx(2); + const tx1 = await mockTx(1); + const tx2 = await mockTx(2); await pool.addTxs([tx1, tx2]); - await pool.markAsMined([tx1.getTxHash()], 1); + await pool.markAsMined([await tx1.getTxHash()], 1); - expect(pool.getTxByHash(tx1.getTxHash())).toEqual(tx1); - expect(pool.getTxStatus(tx1.getTxHash())).toEqual('mined'); - expect(pool.getMinedTxHashes()).toEqual([[tx1.getTxHash(), 1]]); - expect(pool.getPendingTxHashes()).toEqual([tx2.getTxHash()]); + expect(pool.getTxByHash(await tx1.getTxHash())).toEqual(tx1); + expect(pool.getTxStatus(await tx1.getTxHash())).toEqual('mined'); + expect(pool.getMinedTxHashes()).toEqual([[await tx1.getTxHash(), 1]]); + await expect(pool.getPendingTxHashes()).resolves.toEqual([await tx2.getTxHash()]); }); it('Marks txs as pending after being mined', async () => { - const tx1 = mockTx(1); - const tx2 = mockTx(2); + const tx1 = await mockTx(1); + const tx2 = await mockTx(2); await pool.addTxs([tx1, tx2]); - await pool.markAsMined([tx1.getTxHash()], 1); + await pool.markAsMined([await tx1.getTxHash()], 1); - await pool.markMinedAsPending([tx1.getTxHash()]); + await pool.markMinedAsPending([await tx1.getTxHash()]); expect(pool.getMinedTxHashes()).toEqual([]); - const pending = pool.getPendingTxHashes(); + const pending = await pool.getPendingTxHashes(); expect(pending).toHaveLength(2); - expect(pending).toEqual(expect.arrayContaining([tx1.getTxHash(), tx2.getTxHash()])); + expect(pending).toEqual(expect.arrayContaining([await tx1.getTxHash(), await tx2.getTxHash()])); }); it('Only marks txs as pending if they are known', async () => { - const tx1 = mockTx(1); + const tx1 = await mockTx(1); // simulate a situation where not all peers have all the txs - const someTxHashThatThisPeerDidNotSee = mockTx(2).getTxHash(); + const tx2 = await mockTx(2); + const someTxHashThatThisPeerDidNotSee = await tx2.getTxHash(); await pool.addTxs([tx1]); // this peer knows that tx2 was mined, but it does not have the tx object - await pool.markAsMined([tx1.getTxHash(), someTxHashThatThisPeerDidNotSee], 1); + await pool.markAsMined([await tx1.getTxHash(), someTxHashThatThisPeerDidNotSee], 1); expect(new Set(pool.getMinedTxHashes())).toEqual( new Set([ - [tx1.getTxHash(), 1], + [await tx1.getTxHash(), 1], [someTxHashThatThisPeerDidNotSee, 1], ]), ); // reorg: both txs should now become available again - await pool.markMinedAsPending([tx1.getTxHash(), someTxHashThatThisPeerDidNotSee]); + await pool.markMinedAsPending([await tx1.getTxHash(), someTxHashThatThisPeerDidNotSee]); expect(pool.getMinedTxHashes()).toEqual([]); - expect(pool.getPendingTxHashes()).toEqual([tx1.getTxHash()]); // tx2 is not in the pool + await expect(pool.getPendingTxHashes()).resolves.toEqual([await tx1.getTxHash()]); // tx2 is not in the pool }); it('Returns all transactions in the pool', async () => { - const tx1 = mockTx(1); - const tx2 = mockTx(2); - const tx3 = mockTx(3); + const tx1 = await mockTx(1); + const tx2 = await mockTx(2); + const tx3 = await mockTx(3); await pool.addTxs([tx1, tx2, tx3]); @@ -95,15 +96,17 @@ export function describeTxPool(getTxPool: () => TxPool) { }); it('Returns all txHashes in the pool', async () => { - const tx1 = mockTx(1); - const tx2 = mockTx(2); - const tx3 = mockTx(3); + const tx1 = await mockTx(1); + const tx2 = await mockTx(2); + const tx3 = await mockTx(3); await pool.addTxs([tx1, tx2, tx3]); const poolTxHashes = pool.getAllTxHashes(); expect(poolTxHashes).toHaveLength(3); - expect(poolTxHashes).toEqual(expect.arrayContaining([tx1.getTxHash(), tx2.getTxHash(), tx3.getTxHash()])); + expect(poolTxHashes).toEqual( + expect.arrayContaining([await tx1.getTxHash(), await tx2.getTxHash(), await tx3.getTxHash()]), + ); }); it('Returns pending tx hashes sorted by priority', async () => { @@ -112,15 +115,15 @@ export function describeTxPool(getTxPool: () => TxPool) { return tx; }; - const tx1 = withPriorityFee(mockTx(0), 1000); - const tx2 = withPriorityFee(mockTx(1), 100); - const tx3 = withPriorityFee(mockTx(2), 200); - const tx4 = withPriorityFee(mockTx(3), 3000); + const tx1 = withPriorityFee(await mockTx(0), 1000); + const tx2 = withPriorityFee(await mockTx(1), 100); + const tx3 = withPriorityFee(await mockTx(2), 200); + const tx4 = withPriorityFee(await mockTx(3), 3000); await pool.addTxs([tx1, tx2, tx3, tx4]); - const poolTxHashes = pool.getPendingTxHashes(); + const poolTxHashes = await pool.getPendingTxHashes(); expect(poolTxHashes).toHaveLength(4); - expect(poolTxHashes).toEqual([tx4, tx1, tx3, tx2].map(tx => tx.getTxHash())); + expect(poolTxHashes).toEqual(await Promise.all([tx4, tx1, tx3, tx2].map(tx => tx.getTxHash()))); }); } diff --git a/yarn-project/p2p/src/msg_validators/attestation_validator/attestation_validator.test.ts b/yarn-project/p2p/src/msg_validators/attestation_validator/attestation_validator.test.ts index 59fed59ac05..92a187fdfe0 100644 --- a/yarn-project/p2p/src/msg_validators/attestation_validator/attestation_validator.test.ts +++ b/yarn-project/p2p/src/msg_validators/attestation_validator/attestation_validator.test.ts @@ -18,7 +18,7 @@ describe('AttestationValidator', () => { it('returns high tolerance error if slot number is not current or next slot', async () => { // Create an attestation for slot 97 const header = makeHeader(1, 97, 97); - const mockAttestation = makeBlockAttestation({ + const mockAttestation = await makeBlockAttestation({ header, }); @@ -35,7 +35,7 @@ describe('AttestationValidator', () => { it('returns high tolerance error if attester is not in committee', async () => { // The slot is correct, but the attester is not in the committee - const mockAttestation = makeBlockAttestation({ + const mockAttestation = await makeBlockAttestation({ header: makeHeader(1, 100, 100), }); @@ -52,7 +52,7 @@ describe('AttestationValidator', () => { it('returns undefined if attestation is valid (current slot)', async () => { // Create an attestation for slot 100 - const mockAttestation = makeBlockAttestation({ + const mockAttestation = await makeBlockAttestation({ header: makeHeader(1, 100, 100), }); @@ -69,7 +69,7 @@ describe('AttestationValidator', () => { it('returns undefined if attestation is valid (next slot)', async () => { // Setup attestation for next slot - const mockAttestation = makeBlockAttestation({ + const mockAttestation = await makeBlockAttestation({ header: makeHeader(1, 101, 101), }); diff --git a/yarn-project/p2p/src/msg_validators/attestation_validator/attestation_validator.ts b/yarn-project/p2p/src/msg_validators/attestation_validator/attestation_validator.ts index ab07e9862ac..098e55a8e83 100644 --- a/yarn-project/p2p/src/msg_validators/attestation_validator/attestation_validator.ts +++ b/yarn-project/p2p/src/msg_validators/attestation_validator/attestation_validator.ts @@ -16,7 +16,7 @@ export class AttestationValidator implements P2PValidator { return PeerErrorSeverity.HighToleranceError; } - const attester = message.getSender(); + const attester = await message.getSender(); if (!(await this.epochCache.isInCommittee(attester))) { return PeerErrorSeverity.HighToleranceError; } diff --git a/yarn-project/p2p/src/msg_validators/block_proposal_validator/block_proposal_validator.test.ts b/yarn-project/p2p/src/msg_validators/block_proposal_validator/block_proposal_validator.test.ts index 9ed0f37f928..ccddddceae7 100644 --- a/yarn-project/p2p/src/msg_validators/block_proposal_validator/block_proposal_validator.test.ts +++ b/yarn-project/p2p/src/msg_validators/block_proposal_validator/block_proposal_validator.test.ts @@ -19,7 +19,7 @@ describe('BlockProposalValidator', () => { it('returns high tolerance error if slot number is not current or next slot', async () => { // Create a block proposal for slot 97 - const mockProposal = makeBlockProposal({ + const mockProposal = await makeBlockProposal({ header: makeHeader(1, 97, 97), }); @@ -41,7 +41,7 @@ describe('BlockProposalValidator', () => { const invalidProposer = Secp256k1Signer.random(); // Create a block proposal with correct slot but wrong proposer - const mockProposal = makeBlockProposal({ + const mockProposal = await makeBlockProposal({ header: makeHeader(1, 100, 100), signer: invalidProposer, }); @@ -63,7 +63,7 @@ describe('BlockProposalValidator', () => { const nextProposer = Secp256k1Signer.random(); // Create a block proposal for current slot with correct proposer - const mockProposal = makeBlockProposal({ + const mockProposal = await makeBlockProposal({ header: makeHeader(1, 100, 100), signer: currentProposer, }); @@ -85,7 +85,7 @@ describe('BlockProposalValidator', () => { const nextProposer = Secp256k1Signer.random(); // Create a block proposal for next slot with correct proposer - const mockProposal = makeBlockProposal({ + const mockProposal = await makeBlockProposal({ header: makeHeader(1, 101, 101), signer: nextProposer, }); diff --git a/yarn-project/p2p/src/msg_validators/block_proposal_validator/block_proposal_validator.ts b/yarn-project/p2p/src/msg_validators/block_proposal_validator/block_proposal_validator.ts index 34063408d09..e3f1da35a70 100644 --- a/yarn-project/p2p/src/msg_validators/block_proposal_validator/block_proposal_validator.ts +++ b/yarn-project/p2p/src/msg_validators/block_proposal_validator/block_proposal_validator.ts @@ -19,7 +19,7 @@ export class BlockProposalValidator implements P2PValidator { } // Check that the block proposal is from the current or next proposer - const proposer = block.getSender(); + const proposer = await block.getSender(); if (!proposer.equals(currentProposer) && !proposer.equals(nextProposer)) { return PeerErrorSeverity.HighToleranceError; } diff --git a/yarn-project/p2p/src/msg_validators/tx_validator/aggregate_tx_validator.test.ts b/yarn-project/p2p/src/msg_validators/tx_validator/aggregate_tx_validator.test.ts index 194779508c8..76d83849153 100644 --- a/yarn-project/p2p/src/msg_validators/tx_validator/aggregate_tx_validator.test.ts +++ b/yarn-project/p2p/src/msg_validators/tx_validator/aggregate_tx_validator.test.ts @@ -4,10 +4,10 @@ import { AggregateTxValidator } from './aggregate_tx_validator.js'; describe('AggregateTxValidator', () => { it('allows txs that pass all validation', async () => { - const txs = [mockTx(0), mockTx(1), mockTx(2), mockTx(3), mockTx(4)]; + const txs = await Promise.all([mockTx(0), mockTx(1), mockTx(2), mockTx(3), mockTx(4)]); const agg = new AggregateTxValidator( - new TxDenyList([txs[0].getTxHash(), txs[1].getTxHash(), txs[4].getTxHash()], []), - new TxDenyList([txs[2].getTxHash(), txs[4].getTxHash()], []), + new TxDenyList(await Promise.all([txs[0].getTxHash(), txs[1].getTxHash(), txs[4].getTxHash()]), []), + new TxDenyList(await Promise.all([txs[2].getTxHash(), txs[4].getTxHash()]), []), ); await expect(agg.validateTx(txs[0])).resolves.toEqual({ result: 'invalid', reason: ['Denied'] }); @@ -18,11 +18,11 @@ describe('AggregateTxValidator', () => { }); it('aggregate skipped txs ', async () => { - const txs = [mockTx(0), mockTx(1), mockTx(2), mockTx(3), mockTx(4)]; + const txs = await Promise.all([mockTx(0), mockTx(1), mockTx(2), mockTx(3), mockTx(4)]); const agg = new AggregateTxValidator( - new TxDenyList([txs[0].getTxHash()], []), - new TxDenyList([txs[4].getTxHash()], [txs[1].getTxHash(), txs[2].getTxHash()]), - new TxDenyList([], [txs[4].getTxHash()]), + new TxDenyList([await txs[0].getTxHash()], []), + new TxDenyList([await txs[4].getTxHash()], [await txs[1].getTxHash(), await txs[2].getTxHash()]), + new TxDenyList([], [await txs[4].getTxHash()]), ); await expect(agg.validateTx(txs[0])).resolves.toEqual({ result: 'invalid', reason: ['Denied'] }); @@ -41,14 +41,15 @@ describe('AggregateTxValidator', () => { this.skippedList = new Set(skippedTxHashes.map(hash => hash.toString())); } - validateTx(tx: AnyTx): Promise { - if (this.skippedList.has(Tx.getHash(tx).toString())) { - return Promise.resolve({ result: 'skipped', reason: ['Skipped'] }); + async validateTx(tx: AnyTx): Promise { + const txHash = await Tx.getHash(tx); + if (this.skippedList.has(txHash.toString())) { + return { result: 'skipped', reason: ['Skipped'] }; } - if (this.denyList.has(Tx.getHash(tx).toString())) { - return Promise.resolve({ result: 'invalid', reason: ['Denied'] }); + if (this.denyList.has(txHash.toString())) { + return { result: 'invalid', reason: ['Denied'] }; } - return Promise.resolve({ result: 'valid' }); + return { result: 'valid' }; } } }); diff --git a/yarn-project/p2p/src/msg_validators/tx_validator/block_header_validator.test.ts b/yarn-project/p2p/src/msg_validators/tx_validator/block_header_validator.test.ts index ec745265a12..d36341f1f57 100644 --- a/yarn-project/p2p/src/msg_validators/tx_validator/block_header_validator.test.ts +++ b/yarn-project/p2p/src/msg_validators/tx_validator/block_header_validator.test.ts @@ -19,16 +19,16 @@ describe('BlockHeaderTxValidator', () => { }); it('rejects tx with invalid block header', async () => { - const badTx = mockTxForRollup(); + const badTx = await mockTxForRollup(); badTx.data.constants.historicalHeader.globalVariables.blockNumber = badTx.data.constants.historicalHeader.globalVariables.blockNumber.add(new Fr(1)); - const goodTx = mockTxForRollup(); - archiveSource.getArchiveIndices.mockImplementation((archives: Fr[]) => { - if (archives[0].equals(goodTx.data.constants.historicalHeader.hash())) { - return Promise.resolve([1n]); + const goodTx = await mockTxForRollup(); + archiveSource.getArchiveIndices.mockImplementation(async (archives: Fr[]) => { + if (archives[0].equals(await goodTx.data.constants.historicalHeader.hash())) { + return [1n]; } else { - return Promise.resolve([undefined]); + return [undefined]; } }); await expect(txValidator.validateTx(goodTx)).resolves.toEqual({ result: 'valid' } satisfies TxValidationResult); diff --git a/yarn-project/p2p/src/msg_validators/tx_validator/block_header_validator.ts b/yarn-project/p2p/src/msg_validators/tx_validator/block_header_validator.ts index 79784be7fa8..ed994d45779 100644 --- a/yarn-project/p2p/src/msg_validators/tx_validator/block_header_validator.ts +++ b/yarn-project/p2p/src/msg_validators/tx_validator/block_header_validator.ts @@ -15,9 +15,9 @@ export class BlockHeaderTxValidator implements TxValidator { } async validateTx(tx: T): Promise { - const [index] = await this.#archiveSource.getArchiveIndices([tx.data.constants.historicalHeader.hash()]); + const [index] = await this.#archiveSource.getArchiveIndices([await tx.data.constants.historicalHeader.hash()]); if (index === undefined) { - this.#log.warn(`Rejecting tx ${Tx.getHash(tx)} for referencing an unknown block header`); + this.#log.warn(`Rejecting tx ${await Tx.getHash(tx)} for referencing an unknown block header`); return { result: 'invalid', reason: ['Block header not found'] }; } return { result: 'valid' }; diff --git a/yarn-project/p2p/src/msg_validators/tx_validator/data_validator.test.ts b/yarn-project/p2p/src/msg_validators/tx_validator/data_validator.test.ts index 52b97bb83a0..8514685bd8c 100644 --- a/yarn-project/p2p/src/msg_validators/tx_validator/data_validator.test.ts +++ b/yarn-project/p2p/src/msg_validators/tx_validator/data_validator.test.ts @@ -1,18 +1,17 @@ import { type Tx, mockTx } from '@aztec/circuit-types'; import { AztecAddress, Fr, FunctionSelector } from '@aztec/circuits.js'; +import { timesParallel } from '@aztec/foundation/collection'; import { DataTxValidator } from './data_validator.js'; const mockTxs = (numTxs: number) => - Array(numTxs) - .fill(0) - .map((_, i) => - mockTx(i, { - numberOfNonRevertiblePublicCallRequests: 2, - numberOfRevertiblePublicCallRequests: 2, - hasPublicTeardownCallRequest: true, - }), - ); + timesParallel(numTxs, i => + mockTx(i, { + numberOfNonRevertiblePublicCallRequests: 2, + numberOfRevertiblePublicCallRequests: 2, + hasPublicTeardownCallRequest: true, + }), + ); describe('TxDataValidator', () => { let validator: DataTxValidator; @@ -32,13 +31,13 @@ describe('TxDataValidator', () => { }; it('allows transactions with the correct data', async () => { - const [tx] = mockTxs(1); + const [tx] = await mockTxs(1); await expect(validator.validateTx(tx)).resolves.toEqual({ result: 'valid' }); }); it('rejects txs with mismatch non revertible execution requests', async () => { - const goodTxs = mockTxs(3); - const badTxs = mockTxs(2); + const goodTxs = await mockTxs(3); + const badTxs = await mockTxs(2); badTxs[0].data.forPublic!.nonRevertibleAccumulatedData.publicCallRequests[0].argsHash = Fr.random(); badTxs[1].data.forPublic!.nonRevertibleAccumulatedData.publicCallRequests[1].contractAddress = await AztecAddress.random(); @@ -50,8 +49,8 @@ describe('TxDataValidator', () => { }); it('rejects txs with mismatch revertible execution requests', async () => { - const goodTxs = mockTxs(3); - const badTxs = mockTxs(4); + const goodTxs = await mockTxs(3); + const badTxs = await mockTxs(4); badTxs[0].data.forPublic!.revertibleAccumulatedData.publicCallRequests[0].msgSender = await AztecAddress.random(); badTxs[1].data.forPublic!.revertibleAccumulatedData.publicCallRequests[1].contractAddress = await AztecAddress.random(); @@ -69,8 +68,8 @@ describe('TxDataValidator', () => { }); it('rejects txs with mismatch teardown execution requests', async () => { - const goodTxs = mockTxs(3); - const badTxs = mockTxs(2); + const goodTxs = await mockTxs(3); + const badTxs = await mockTxs(2); badTxs[0].data.forPublic!.publicTeardownCallRequest.contractAddress = await AztecAddress.random(); badTxs[1].data.forPublic!.publicTeardownCallRequest.msgSender = await AztecAddress.random(); @@ -81,8 +80,8 @@ describe('TxDataValidator', () => { }); it('rejects txs with mismatch number of execution requests', async () => { - const goodTxs = mockTxs(3); - const badTxs = mockTxs(2); + const goodTxs = await mockTxs(3); + const badTxs = await mockTxs(2); // Missing an enqueuedPublicFunctionCall. const execRequest = badTxs[0].enqueuedPublicFunctionCalls.pop()!; // Having an extra enqueuedPublicFunctionCall. diff --git a/yarn-project/p2p/src/msg_validators/tx_validator/data_validator.ts b/yarn-project/p2p/src/msg_validators/tx_validator/data_validator.ts index ddc5d43ca87..3c32ec5c8b1 100644 --- a/yarn-project/p2p/src/msg_validators/tx_validator/data_validator.ts +++ b/yarn-project/p2p/src/msg_validators/tx_validator/data_validator.ts @@ -5,10 +5,10 @@ export class DataTxValidator implements TxValidator { #log = createLogger('p2p:tx_validator:tx_data'); validateTx(tx: Tx): Promise { - return Promise.resolve(this.#hasCorrectExecutionRequests(tx)); + return this.#hasCorrectExecutionRequests(tx); } - #hasCorrectExecutionRequests(tx: Tx): TxValidationResult { + async #hasCorrectExecutionRequests(tx: Tx): Promise { const callRequests = [ ...tx.data.getRevertiblePublicCallRequests(), ...tx.data.getNonRevertiblePublicCallRequests(), @@ -21,13 +21,16 @@ export class DataTxValidator implements TxValidator { ); return { result: 'invalid', reason: ['Wrong number of execution requests for public calls'] }; } - - const invalidExecutionRequestIndex = tx.enqueuedPublicFunctionCalls.findIndex( - (execRequest, i) => !execRequest.isForCallRequest(callRequests[i]), - ); + const invalidExecutionRequestIndex = ( + await Promise.all( + tx.enqueuedPublicFunctionCalls.map( + async (execRequest, i) => !(await execRequest.isForCallRequest(callRequests[i])), + ), + ) + ).findIndex(Boolean); if (invalidExecutionRequestIndex !== -1) { this.#log.warn( - `Rejecting tx ${Tx.getHash( + `Rejecting tx ${await Tx.getHash( tx, )} because of incorrect execution requests for public call at index ${invalidExecutionRequestIndex}.`, ); @@ -37,9 +40,9 @@ export class DataTxValidator implements TxValidator { const teardownCallRequest = tx.data.getTeardownPublicCallRequest(); const isInvalidTeardownExecutionRequest = (!teardownCallRequest && !tx.publicTeardownFunctionCall.isEmpty()) || - (teardownCallRequest && !tx.publicTeardownFunctionCall.isForCallRequest(teardownCallRequest)); + (teardownCallRequest && !(await tx.publicTeardownFunctionCall.isForCallRequest(teardownCallRequest))); if (isInvalidTeardownExecutionRequest) { - this.#log.warn(`Rejecting tx ${Tx.getHash(tx)} because of incorrect teardown execution requests.`); + this.#log.warn(`Rejecting tx ${await Tx.getHash(tx)} because of incorrect teardown execution requests.`); return { result: 'invalid', reason: ['Incorrect teardown execution request'] }; } diff --git a/yarn-project/p2p/src/msg_validators/tx_validator/double_spend_validator.test.ts b/yarn-project/p2p/src/msg_validators/tx_validator/double_spend_validator.test.ts index 3a64e1fb601..25c39c2ad06 100644 --- a/yarn-project/p2p/src/msg_validators/tx_validator/double_spend_validator.test.ts +++ b/yarn-project/p2p/src/msg_validators/tx_validator/double_spend_validator.test.ts @@ -19,19 +19,19 @@ describe('DoubleSpendTxValidator', () => { }); it('rejects duplicates in non revertible data', async () => { - const badTx = mockTxForRollup(); + const badTx = await mockTxForRollup(); badTx.data.forRollup!.end.nullifiers[1] = badTx.data.forRollup!.end.nullifiers[0]; await expectInvalid(badTx, 'Duplicate nullifier in tx'); }); it('rejects duplicates in revertible data', async () => { - const badTx = mockTxForRollup(); + const badTx = await mockTxForRollup(); badTx.data.forRollup!.end.nullifiers[1] = badTx.data.forRollup!.end.nullifiers[0]; await expectInvalid(badTx, 'Duplicate nullifier in tx'); }); it('rejects duplicates across phases', async () => { - const badTx = mockTx(1, { + const badTx = await mockTx(1, { numberOfNonRevertiblePublicCallRequests: 1, numberOfRevertiblePublicCallRequests: 1, }); @@ -41,7 +41,7 @@ describe('DoubleSpendTxValidator', () => { }); it('rejects duplicates against history', async () => { - const badTx = mockTx(); + const badTx = await mockTx(); nullifierSource.nullifiersExist.mockResolvedValue([true]); await expectInvalid(badTx, 'Existing nullifier'); }); diff --git a/yarn-project/p2p/src/msg_validators/tx_validator/double_spend_validator.ts b/yarn-project/p2p/src/msg_validators/tx_validator/double_spend_validator.ts index 7ec67bbbc39..a134d5b6aa8 100644 --- a/yarn-project/p2p/src/msg_validators/tx_validator/double_spend_validator.ts +++ b/yarn-project/p2p/src/msg_validators/tx_validator/double_spend_validator.ts @@ -19,12 +19,12 @@ export class DoubleSpendTxValidator implements TxValidator { // Ditch this tx if it has repeated nullifiers const uniqueNullifiers = new Set(nullifiers); if (uniqueNullifiers.size !== nullifiers.length) { - this.#log.warn(`Rejecting tx ${Tx.getHash(tx)} for emitting duplicate nullifiers`); + this.#log.warn(`Rejecting tx ${await Tx.getHash(tx)} for emitting duplicate nullifiers`); return { result: 'invalid', reason: ['Duplicate nullifier in tx'] }; } if ((await this.#nullifierSource.nullifiersExist(nullifiers.map(n => n.toBuffer()))).some(Boolean)) { - this.#log.warn(`Rejecting tx ${Tx.getHash(tx)} for repeating a nullifier`); + this.#log.warn(`Rejecting tx ${await Tx.getHash(tx)} for repeating a nullifier`); return { result: 'invalid', reason: ['Existing nullifier'] }; } diff --git a/yarn-project/p2p/src/msg_validators/tx_validator/metadata_validator.test.ts b/yarn-project/p2p/src/msg_validators/tx_validator/metadata_validator.test.ts index 0dbf8964e71..940b69ce720 100644 --- a/yarn-project/p2p/src/msg_validators/tx_validator/metadata_validator.test.ts +++ b/yarn-project/p2p/src/msg_validators/tx_validator/metadata_validator.test.ts @@ -23,8 +23,8 @@ describe('MetadataTxValidator', () => { }; it('allows only transactions for the right chain', async () => { - const goodTxs = [mockTx(1), mockTxForRollup(2)]; - const badTxs = [mockTx(3), mockTxForRollup(4)]; + const goodTxs = await Promise.all([mockTx(1), mockTxForRollup(2)]); + const badTxs = await Promise.all([mockTx(3), mockTxForRollup(4)]); goodTxs.forEach(tx => { tx.data.constants.txContext.chainId = chainId; @@ -41,7 +41,7 @@ describe('MetadataTxValidator', () => { }); it.each([42, 43])('allows txs with valid max block number', async maxBlockNumber => { - const goodTx = mockTxForRollup(1); + const goodTx = await mockTxForRollup(1); goodTx.data.constants.txContext.chainId = chainId; goodTx.data.rollupValidationRequests.maxBlockNumber = new MaxBlockNumber(true, new Fr(maxBlockNumber)); @@ -49,7 +49,7 @@ describe('MetadataTxValidator', () => { }); it('allows txs with unset max block number', async () => { - const goodTx = mockTxForRollup(1); + const goodTx = await mockTxForRollup(1); goodTx.data.constants.txContext.chainId = chainId; goodTx.data.rollupValidationRequests.maxBlockNumber = new MaxBlockNumber(false, Fr.ZERO); @@ -57,7 +57,7 @@ describe('MetadataTxValidator', () => { }); it('rejects txs with lower max block number', async () => { - const badTx = mockTxForRollup(1); + const badTx = await mockTxForRollup(1); badTx.data.constants.txContext.chainId = chainId; badTx.data.rollupValidationRequests.maxBlockNumber = new MaxBlockNumber(true, blockNumber.sub(new Fr(1))); diff --git a/yarn-project/p2p/src/msg_validators/tx_validator/metadata_validator.ts b/yarn-project/p2p/src/msg_validators/tx_validator/metadata_validator.ts index aefde1dfd72..97e9acd73c8 100644 --- a/yarn-project/p2p/src/msg_validators/tx_validator/metadata_validator.ts +++ b/yarn-project/p2p/src/msg_validators/tx_validator/metadata_validator.ts @@ -7,21 +7,21 @@ export class MetadataTxValidator implements TxValidator { constructor(private chainId: Fr, private blockNumber: Fr) {} - validateTx(tx: T): Promise { + async validateTx(tx: T): Promise { const errors = []; - if (!this.#hasCorrectChainId(tx)) { + if (!(await this.#hasCorrectChainId(tx))) { errors.push('Incorrect chain id'); } - if (!this.#isValidForBlockNumber(tx)) { + if (!(await this.#isValidForBlockNumber(tx))) { errors.push('Invalid block number'); } - return Promise.resolve(errors.length > 0 ? { result: 'invalid', reason: errors } : { result: 'valid' }); + return errors.length > 0 ? { result: 'invalid', reason: errors } : { result: 'valid' }; } - #hasCorrectChainId(tx: T): boolean { + async #hasCorrectChainId(tx: T): Promise { if (!tx.data.constants.txContext.chainId.equals(this.chainId)) { this.#log.warn( - `Rejecting tx ${Tx.getHash( + `Rejecting tx ${await Tx.getHash( tx, )} because of incorrect chain ${tx.data.constants.txContext.chainId.toNumber()} != ${this.chainId.toNumber()}`, ); @@ -31,12 +31,12 @@ export class MetadataTxValidator implements TxValidator { } } - #isValidForBlockNumber(tx: T): boolean { + async #isValidForBlockNumber(tx: T): Promise { const maxBlockNumber = tx.data.rollupValidationRequests.maxBlockNumber; if (maxBlockNumber.isSome && maxBlockNumber.value < this.blockNumber) { this.#log.warn( - `Rejecting tx ${Tx.getHash(tx)} for low max block number. Tx max block number: ${ + `Rejecting tx ${await Tx.getHash(tx)} for low max block number. Tx max block number: ${ maxBlockNumber.value }, current block number: ${this.blockNumber}.`, ); diff --git a/yarn-project/p2p/src/services/libp2p/libp2p_service.ts b/yarn-project/p2p/src/services/libp2p/libp2p_service.ts index cde9c2ebf5b..50fe1079653 100644 --- a/yarn-project/p2p/src/services/libp2p/libp2p_service.ts +++ b/yarn-project/p2p/src/services/libp2p/libp2p_service.ts @@ -578,7 +578,7 @@ export class LibP2PService extends WithTracer implement const validProof = await proofValidator.validateTx(responseTx); // If the node returns the wrong data, we penalize it - if (!requestedTxHash.equals(responseTx.getTxHash())) { + if (!requestedTxHash.equals(await responseTx.getTxHash())) { // Returning the wrong data is a low tolerance error this.peerManager.penalizePeer(peerId, PeerErrorSeverity.MidToleranceError); return false; @@ -606,7 +606,7 @@ export class LibP2PService extends WithTracer implement const tx = Tx.fromBuffer(Buffer.from(msg.data)); const isValid = await this.validatePropagatedTx(tx, propagationSource); this.logger.trace(`validatePropagatedTx: ${isValid}`, { - [Attributes.TX_HASH]: tx.getTxHash().toString(), + [Attributes.TX_HASH]: (await tx.getTxHash()).toString(), [Attributes.P2P_ID]: propagationSource.toString(), }); return isValid ? TopicValidatorResult.Accept : TopicValidatorResult.Reject; @@ -669,8 +669,8 @@ export class LibP2PService extends WithTracer implement return isValid ? TopicValidatorResult.Accept : TopicValidatorResult.Reject; } - @trackSpan('Libp2pService.validatePropagatedTx', tx => ({ - [Attributes.TX_HASH]: tx.getTxHash().toString(), + @trackSpan('Libp2pService.validatePropagatedTx', async tx => ({ + [Attributes.TX_HASH]: (await tx.getTxHash()).toString(), })) private async validatePropagatedTx(tx: Tx, peerId: PeerId): Promise { const blockNumber = (await this.l2BlockSource.getBlockNumber()) + 1; diff --git a/yarn-project/p2p/src/services/reqresp/reqresp.integration.test.ts b/yarn-project/p2p/src/services/reqresp/reqresp.integration.test.ts index d69c0b3efed..550b1537954 100644 --- a/yarn-project/p2p/src/services/reqresp/reqresp.integration.test.ts +++ b/yarn-project/p2p/src/services/reqresp/reqresp.integration.test.ts @@ -167,8 +167,8 @@ describe('Req Resp p2p client integration', () => { await sleep(2000); // Perform a get tx request from client 1 - const tx = mockTx(); - const txHash = tx.getTxHash(); + const tx = await mockTx(); + const txHash = await tx.getTxHash(); const requestedTx = await client1.requestTxByHash(txHash); expect(requestedTx).toBeUndefined(); @@ -190,8 +190,8 @@ describe('Req Resp p2p client integration', () => { await sleep(6000); // Perform a get tx request from client 1 - const tx = mockTx(); - const txHash = tx.getTxHash(); + const tx = await mockTx(); + const txHash = await tx.getTxHash(); // Mock the tx pool to return the tx we are looking for txPool.getTxByHash.mockImplementationOnce(() => tx); @@ -219,8 +219,8 @@ describe('Req Resp p2p client integration', () => { const penalizePeerSpy = jest.spyOn((client1 as any).p2pService.peerManager, 'penalizePeer'); // Perform a get tx request from client 1 - const tx = mockTx(); - const txHash = tx.getTxHash(); + const tx = await mockTx(); + const txHash = await tx.getTxHash(); // Return the correct tx with an invalid proof -> active attack txPool.getTxByHash.mockImplementationOnce(() => tx); @@ -251,9 +251,9 @@ describe('Req Resp p2p client integration', () => { const penalizePeerSpy = jest.spyOn((client1 as any).p2pService.peerManager, 'penalizePeer'); // Perform a get tx request from client 1 - const tx = mockTx(); - const txHash = tx.getTxHash(); - const tx2 = mockTx(420); + const tx = await mockTx(); + const txHash = await tx.getTxHash(); + const tx2 = await mockTx(420); // Return an invalid tx txPool.getTxByHash.mockImplementationOnce(() => tx2); diff --git a/yarn-project/p2p/src/services/reqresp/reqresp.test.ts b/yarn-project/p2p/src/services/reqresp/reqresp.test.ts index a6cd9554704..c8e6313a670 100644 --- a/yarn-project/p2p/src/services/reqresp/reqresp.test.ts +++ b/yarn-project/p2p/src/services/reqresp/reqresp.test.ts @@ -137,8 +137,8 @@ describe('ReqResp', () => { describe('Tx req protocol', () => { it('Can request a Tx from TxHash', async () => { - const tx = mockTx(); - const txHash = tx.getTxHash(); + const tx = await mockTx(); + const txHash = await tx.getTxHash(); const protocolHandlers = MOCK_SUB_PROTOCOL_HANDLERS; protocolHandlers[ReqRespSubProtocol.TX] = (_peerId: PeerId, message: Buffer): Promise => { @@ -158,13 +158,13 @@ describe('ReqResp', () => { const res = await nodes[0].req.sendRequest(ReqRespSubProtocol.TX, txHash); // Set tx hash since expect will compare private properties - res.getTxHash(); + await res.getTxHash(); expect(res).toEqual(tx); }); it('Handle returning empty buffers', async () => { - const tx = mockTx(); - const txHash = tx.getTxHash(); + const tx = await mockTx(); + const txHash = await tx.getTxHash(); const protocolHandlers = MOCK_SUB_PROTOCOL_HANDLERS; protocolHandlers[ReqRespSubProtocol.TX] = (_peerId: PeerId, _message: Buffer): Promise => { @@ -186,8 +186,8 @@ describe('ReqResp', () => { }); it('Does not crash if tx hash returns undefined', async () => { - const tx = mockTx(); - const txHash = tx.getTxHash(); + const tx = await mockTx(); + const txHash = await tx.getTxHash(); const protocolHandlers = MOCK_SUB_PROTOCOL_HANDLERS; // Return nothing @@ -275,8 +275,8 @@ describe('ReqResp', () => { }); it('should penalize peer if transaction validation fails', async () => { - const tx = mockTx(); - const txHash = tx.getTxHash(); + const tx = await mockTx(); + const txHash = await tx.getTxHash(); // Mock that the node will respond with the tx const protocolHandlers = MOCK_SUB_PROTOCOL_HANDLERS; diff --git a/yarn-project/protocol-contracts/src/build_protocol_contract_tree.ts b/yarn-project/protocol-contracts/src/build_protocol_contract_tree.ts index 43ae9c3aaf9..b6f38ff88b3 100644 --- a/yarn-project/protocol-contracts/src/build_protocol_contract_tree.ts +++ b/yarn-project/protocol-contracts/src/build_protocol_contract_tree.ts @@ -7,9 +7,9 @@ import { } from '@aztec/circuits.js'; import { poseidon2Hash } from '@aztec/foundation/crypto'; -export function buildProtocolContractTree(contracts: { address: AztecAddress; leaf: Fr }[]): MerkleTree { - const calculator = new MerkleTreeCalculator(PROTOCOL_CONTRACT_TREE_HEIGHT, Buffer.alloc(32), (a, b) => - poseidon2Hash([a, b]).toBuffer(), +export async function buildProtocolContractTree(contracts: { address: AztecAddress; leaf: Fr }[]): Promise { + const calculator = await MerkleTreeCalculator.create(PROTOCOL_CONTRACT_TREE_HEIGHT, Buffer.alloc(32), async (a, b) => + (await poseidon2Hash([a, b])).toBuffer(), ); const leaves = new Array(2 ** PROTOCOL_CONTRACT_TREE_HEIGHT).fill(Buffer.alloc(32)); diff --git a/yarn-project/protocol-contracts/src/bundle/index.ts b/yarn-project/protocol-contracts/src/bundle/index.ts index c6afe731ece..cf27c11b79d 100644 --- a/yarn-project/protocol-contracts/src/bundle/index.ts +++ b/yarn-project/protocol-contracts/src/bundle/index.ts @@ -16,7 +16,7 @@ export async function getCanonicalProtocolContract(name: ProtocolContractName): const address = ProtocolContractAddress[name]; const salt = ProtocolContractSalt[name]; // TODO(@spalladino): This computes the contract class from the artifact twice. - const contractClass = getContractClassFromArtifact(artifact); + const contractClass = await getContractClassFromArtifact(artifact); const instance = await getContractInstanceFromDeployParams(artifact, { salt }); return { instance: { ...instance, address }, diff --git a/yarn-project/protocol-contracts/src/class-registerer/contract_class_registered_event.test.ts b/yarn-project/protocol-contracts/src/class-registerer/contract_class_registered_event.test.ts index ee02a62a3b6..9de837e51b5 100644 --- a/yarn-project/protocol-contracts/src/class-registerer/contract_class_registered_event.test.ts +++ b/yarn-project/protocol-contracts/src/class-registerer/contract_class_registered_event.test.ts @@ -4,7 +4,7 @@ import { getSampleContractClassRegisteredEventPayload } from '../tests/fixtures. import { ContractClassRegisteredEvent } from './contract_class_registered_event.js'; describe('ContractClassRegisteredEvent', () => { - it('parses an event as emitted by the ContractClassRegisterer', () => { + it('parses an event as emitted by the ContractClassRegisterer', async () => { const log = getSampleContractClassRegisteredEventPayload(); expect(ContractClassRegisteredEvent.isContractClassRegisteredEvent(log)).toBe(true); @@ -14,7 +14,8 @@ describe('ContractClassRegisteredEvent', () => { ); expect(event.artifactHash.toString()).toEqual('0x072dce903b1a299d6820eeed695480fe9ec46658b1101885816aed6dd86037f0'); expect(event.packedPublicBytecode.length).toEqual(27090); - expect(computePublicBytecodeCommitment(event.packedPublicBytecode).toString()).toEqual( + const publicBytecodeCommitment = await computePublicBytecodeCommitment(event.packedPublicBytecode); + expect(publicBytecodeCommitment.toString()).toEqual( '0x1d7d509f736d09975b88d01b5779a6f52e70905ba9294776d4881e811e6c1e9f', ); }); diff --git a/yarn-project/protocol-contracts/src/class-registerer/contract_class_registered_event.ts b/yarn-project/protocol-contracts/src/class-registerer/contract_class_registered_event.ts index b8c935dd948..9f9a9caeb17 100644 --- a/yarn-project/protocol-contracts/src/class-registerer/contract_class_registered_event.ts +++ b/yarn-project/protocol-contracts/src/class-registerer/contract_class_registered_event.ts @@ -46,11 +46,11 @@ export class ContractClassRegisteredEvent { ); } - toContractClassPublic(): ContractClassPublic { - const computedClassId = computeContractClassId({ + async toContractClassPublic(): Promise { + const computedClassId = await computeContractClassId({ artifactHash: this.artifactHash, privateFunctionsRoot: this.privateFunctionsRoot, - publicBytecodeCommitment: computePublicBytecodeCommitment(this.packedPublicBytecode), + publicBytecodeCommitment: await computePublicBytecodeCommitment(this.packedPublicBytecode), }); if (!computedClassId.equals(this.contractClassId)) { diff --git a/yarn-project/protocol-contracts/src/make_protocol_contract.ts b/yarn-project/protocol-contracts/src/make_protocol_contract.ts index 62e0a3b4b64..53dc83b7496 100644 --- a/yarn-project/protocol-contracts/src/make_protocol_contract.ts +++ b/yarn-project/protocol-contracts/src/make_protocol_contract.ts @@ -15,7 +15,7 @@ export async function makeProtocolContract( const address = ProtocolContractAddress[name]; const salt = ProtocolContractSalt[name]; // TODO(@spalladino): This computes the contract class from the artifact twice. - const contractClass = getContractClassFromArtifact(artifact); + const contractClass = await getContractClassFromArtifact(artifact); const instance = await getContractInstanceFromDeployParams(artifact, { salt }); return { instance: { ...instance, address }, diff --git a/yarn-project/protocol-contracts/src/protocol_contract_tree.ts b/yarn-project/protocol-contracts/src/protocol_contract_tree.ts index 6a90c995063..8edc6784544 100644 --- a/yarn-project/protocol-contracts/src/protocol_contract_tree.ts +++ b/yarn-project/protocol-contracts/src/protocol_contract_tree.ts @@ -6,19 +6,19 @@ import { ProtocolContractAddress, ProtocolContractLeaf, protocolContractNames } let protocolContractTree: MerkleTree | undefined; -function getTree() { +async function getTree() { if (!protocolContractTree) { const leaves = protocolContractNames.map(name => ({ address: ProtocolContractAddress[name], leaf: ProtocolContractLeaf[name], })); - protocolContractTree = buildProtocolContractTree(leaves); + protocolContractTree = await buildProtocolContractTree(leaves); } return protocolContractTree; } -export function getProtocolContractSiblingPath(address: AztecAddress) { - const tree = getTree(); +export async function getProtocolContractSiblingPath(address: AztecAddress) { + const tree = await getTree(); const index = address.toField().toNumber(); return assertLength( tree.getSiblingPath(index).map(buf => new Fr(buf)), diff --git a/yarn-project/protocol-contracts/src/scripts/generate_data.ts b/yarn-project/protocol-contracts/src/scripts/generate_data.ts index d78f570ec5c..fa5b0e6c4b1 100644 --- a/yarn-project/protocol-contracts/src/scripts/generate_data.ts +++ b/yarn-project/protocol-contracts/src/scripts/generate_data.ts @@ -69,12 +69,12 @@ async function computeContractLeaf(artifact: NoirCompiledContract) { return instance.address; } -function computeRoot(names: string[], leaves: Fr[]) { +async function computeRoot(names: string[], leaves: Fr[]) { const data = names.map((name, i) => ({ address: new AztecAddress(new Fr(contractAddressMapping[name])), leaf: leaves[i], })); - const tree = buildProtocolContractTree(data); + const tree = await buildProtocolContractTree(data); return Fr.fromBuffer(tree.root); } @@ -122,19 +122,19 @@ function generateContractLeaves(names: string[], leaves: Fr[]) { `; } -function generateRoot(names: string[], leaves: Fr[]) { - const root = computeRoot(names, leaves); +async function generateRoot(names: string[], leaves: Fr[]) { + const root = await computeRoot(names, leaves); return ` export const protocolContractTreeRoot = Fr.fromHexString('${root.toString()}'); `; } -function generateLogTags() { +async function generateLogTags() { return ` export const REGISTERER_CONTRACT_CLASS_REGISTERED_TAG = new Fr(${REGISTERER_CONTRACT_CLASS_REGISTERED_MAGIC_VALUE}n); export const REGISTERER_PRIVATE_FUNCTION_BROADCASTED_TAG = new Fr(${REGISTERER_PRIVATE_FUNCTION_BROADCASTED_MAGIC_VALUE}n); export const REGISTERER_UNCONSTRAINED_FUNCTION_BROADCASTED_TAG = new Fr(${REGISTERER_UNCONSTRAINED_FUNCTION_BROADCASTED_MAGIC_VALUE}n); - export const DEPLOYER_CONTRACT_INSTANCE_DEPLOYED_TAG = Fr.fromHexString('${poseidon2Hash([ + export const DEPLOYER_CONTRACT_INSTANCE_DEPLOYED_TAG = Fr.fromHexString('${await poseidon2Hash([ DEPLOYER_CONTRACT_ADDRESS, DEPLOYER_CONTRACT_INSTANCE_DEPLOYED_MAGIC_VALUE, ])}'); @@ -154,9 +154,9 @@ async function generateOutputFile(names: string[], leaves: Fr[]) { ${generateContractLeaves(names, leaves)} - ${generateRoot(names, leaves)} + ${await generateRoot(names, leaves)} - ${generateLogTags()} + ${await generateLogTags()} `; await fs.writeFile(outputFilePath, content); } diff --git a/yarn-project/prover-client/src/bin/get-proof-inputs.ts b/yarn-project/prover-client/src/bin/get-proof-inputs.ts index 08d28435881..37225c6fd9e 100644 --- a/yarn-project/prover-client/src/bin/get-proof-inputs.ts +++ b/yarn-project/prover-client/src/bin/get-proof-inputs.ts @@ -35,7 +35,7 @@ async function main() { logger.info(`Found inputs for ${ProvingRequestType[input.type]}`); writeProofInputs(input, outDir); - console.log(jsonParseWithSchema(jsonStringify(input), ProvingJobInputs).inputs); + console.log((await jsonParseWithSchema(jsonStringify(input), ProvingJobInputs)).inputs); } // This mimics the behavior of bb-prover/src/bb/execute.ts diff --git a/yarn-project/prover-client/src/block_builder/light.test.ts b/yarn-project/prover-client/src/block_builder/light.test.ts index f5bcb49a6a9..8a149e9554d 100644 --- a/yarn-project/prover-client/src/block_builder/light.test.ts +++ b/yarn-project/prover-client/src/block_builder/light.test.ts @@ -50,7 +50,7 @@ import { } from '@aztec/circuits.js/rollup'; import { makeGlobalVariables } from '@aztec/circuits.js/testing'; import { Blob } from '@aztec/foundation/blob'; -import { padArrayEnd, times } from '@aztec/foundation/collection'; +import { padArrayEnd, times, timesParallel } from '@aztec/foundation/collection'; import { sha256ToField } from '@aztec/foundation/crypto'; import { type Logger, createLogger } from '@aztec/foundation/log'; import { type Tuple, assertLength } from '@aztec/foundation/serialize'; @@ -96,7 +96,7 @@ describe('LightBlockBuilder', () => { beforeAll(async () => { logger = createLogger('prover-client:test:block-builder'); simulator = new TestCircuitProver(); - vkTreeRoot = getVKTreeRoot(); + vkTreeRoot = await getVKTreeRoot(); emptyProof = makeEmptyRecursiveProof(NESTED_RECURSIVE_PROOF_LENGTH); emptyRollupProof = makeEmptyRecursiveProof(NESTED_RECURSIVE_ROLLUP_HONK_PROOF_LENGTH); db = await NativeWorldStateService.tmp(); @@ -120,7 +120,7 @@ describe('LightBlockBuilder', () => { }); it('builds a 2 tx header', async () => { - const txs = times(2, makeTx); + const txs = await timesParallel(2, makeTx); const header = await buildHeader(txs, l1ToL2Messages); const expectedHeader = await buildExpectedHeader(txs, l1ToL2Messages); @@ -129,7 +129,7 @@ describe('LightBlockBuilder', () => { }); it('builds a 3 tx header', async () => { - const txs = times(3, makeTx); + const txs = await timesParallel(3, makeTx); const header = await buildHeader(txs, l1ToL2Messages); const expectedHeader = await buildExpectedHeader(txs, l1ToL2Messages, async rollupOutputs => { @@ -141,7 +141,7 @@ describe('LightBlockBuilder', () => { }); it('builds a 4 tx header', async () => { - const txs = times(4, makeTx); + const txs = await timesParallel(4, makeTx); const header = await buildHeader(txs, l1ToL2Messages); const expectedHeader = await buildExpectedHeader(txs, l1ToL2Messages, async rollupOutputs => { @@ -155,7 +155,7 @@ describe('LightBlockBuilder', () => { it('builds a 4 tx header with no l1 to l2 messages', async () => { const l1ToL2Messages: Fr[] = []; - const txs = times(4, makeTx); + const txs = await timesParallel(4, makeTx); const header = await buildHeader(txs, l1ToL2Messages); const expectedHeader = await buildExpectedHeader(txs, l1ToL2Messages, async rollupOutputs => { @@ -168,7 +168,7 @@ describe('LightBlockBuilder', () => { }); it('builds a 5 tx header', async () => { - const txs = times(5, makeTx); + const txs = await timesParallel(5, makeTx); const header = await buildHeader(txs, l1ToL2Messages); const expectedHeader = await buildExpectedHeader(txs, l1ToL2Messages, async rollupOutputs => { @@ -182,7 +182,7 @@ describe('LightBlockBuilder', () => { }); it('builds a single tx header', async () => { - const txs = times(1, makeTx); + const txs = await timesParallel(1, makeTx); const header = await buildHeader(txs, l1ToL2Messages); const expectedHeader = await buildExpectedHeader(txs, l1ToL2Messages); @@ -243,13 +243,19 @@ describe('LightBlockBuilder', () => { ); const endState = new StateReference(messageTreeSnapshot, partialState); - const expectedHeader = buildHeaderFromCircuitOutputs(previousRollups, parityOutput, rootOutput, endState, logger); + const expectedHeader = await buildHeaderFromCircuitOutputs( + previousRollups, + parityOutput, + rootOutput, + endState, + logger, + ); // Ensure that the expected mana used is the sum of the txs' gas used const expectedManaUsed = txs.reduce((acc, tx) => acc + tx.gasUsed.totalGas.l2Gas, 0); expect(expectedHeader.totalManaUsed.toNumber()).toBe(expectedManaUsed); - expect(expectedHeader.hash()).toEqual(rootOutput.endBlockHash); + expect(await expectedHeader.hash()).toEqual(rootOutput.endBlockHash); return expectedHeader; }; @@ -271,7 +277,7 @@ describe('LightBlockBuilder', () => { const spongeBlobState = SpongeBlob.init(toNumBlobFields(txs)); for (const tx of txs) { const vkIndex = TUBE_VK_INDEX; - const vkPath = getVKSiblingPath(vkIndex); + const vkPath = await getVKSiblingPath(vkIndex); const vkData = new VkWitnessData(TubeVk, vkIndex, vkPath); const tubeData = new PrivateTubeData( tx.data.toPrivateToRollupKernelCircuitPublicInputs(), @@ -288,7 +294,7 @@ describe('LightBlockBuilder', () => { const getMergeOutput = async (left: BaseOrMergeRollupPublicInputs, right: BaseOrMergeRollupPublicInputs) => { const baseRollupVk = ProtocolCircuitVks['PrivateBaseRollupArtifact'].keyAsFields; - const baseRollupVkWitness = getVkMembershipWitness(baseRollupVk); + const baseRollupVkWitness = await getVkMembershipWitness(baseRollupVk); const leftInput = new PreviousRollupData(left, emptyRollupProof, baseRollupVk, baseRollupVkWitness); const rightInput = new PreviousRollupData(right, emptyRollupProof, baseRollupVk, baseRollupVkWitness); const inputs = new MergeRollupInputs([leftInput, rightInput]); @@ -302,7 +308,7 @@ describe('LightBlockBuilder', () => { const rootParityInputs: RootParityInput[] = []; const baseParityVk = ProtocolCircuitVks['BaseParityArtifact'].keyAsFields; - const baseParityVkWitness = getVkMembershipWitness(baseParityVk); + const baseParityVkWitness = await getVkMembershipWitness(baseParityVk); for (let i = 0; i < NUM_BASE_PARITY_PER_ROOT_PARITY; i++) { const input = BaseParityInputs.fromSlice(l1ToL2Messages, i, vkTreeRoot); const { inputs } = await simulator.getBaseParityProof(input); @@ -326,7 +332,7 @@ describe('LightBlockBuilder', () => { txs: ProcessedTx[], ) => { const mergeRollupVk = ProtocolCircuitVks['MergeRollupArtifact'].keyAsFields; - const mergeRollupVkWitness = getVkMembershipWitness(mergeRollupVk); + const mergeRollupVkWitness = await getVkMembershipWitness(mergeRollupVk); const previousRollupData = previousRollups.map( r => new PreviousRollupData(r, emptyRollupProof, mergeRollupVk, mergeRollupVkWitness), ); @@ -334,10 +340,10 @@ describe('LightBlockBuilder', () => { const startArchiveSnapshot = await getTreeSnapshot(MerkleTreeId.ARCHIVE, expectsFork); const newArchiveSiblingPath = await getRootTreeSiblingPath(MerkleTreeId.ARCHIVE, expectsFork); const blobFields = txs.map(tx => tx.txEffect.toBlobFields()).flat(); - const blobs = Blob.getBlobs(blobFields); + const blobs = await Blob.getBlobs(blobFields); const blobsHash = sha256ToField(blobs.map(b => b.getEthVersionedBlobHash())); const rootParityVk = ProtocolCircuitVks['RootParityArtifact'].keyAsFields; - const rootParityVkWitness = getVkMembershipWitness(rootParityVk); + const rootParityVkWitness = await getVkMembershipWitness(rootParityVk); const rootParityInput = new RootParityInput( emptyProof, @@ -360,7 +366,7 @@ describe('LightBlockBuilder', () => { const constants = ConstantRollupData.from({ lastArchive: startArchiveSnapshot, globalVariables, - vkTreeRoot: getVKTreeRoot(), + vkTreeRoot: await getVKTreeRoot(), protocolContractTreeRoot, }); const inputs = EmptyBlockRootRollupInputs.from({ @@ -398,8 +404,8 @@ describe('LightBlockBuilder', () => { } }; - function getVkMembershipWitness(vk: VerificationKeyAsFields) { - const leafIndex = getVKIndex(vk); - return new MembershipWitness(VK_TREE_HEIGHT, BigInt(leafIndex), getVKSiblingPath(leafIndex)); + async function getVkMembershipWitness(vk: VerificationKeyAsFields) { + const leafIndex = await getVKIndex(vk); + return new MembershipWitness(VK_TREE_HEIGHT, BigInt(leafIndex), await getVKSiblingPath(leafIndex)); } }); diff --git a/yarn-project/prover-client/src/mocks/test_context.ts b/yarn-project/prover-client/src/mocks/test_context.ts index 0bcf76fd14a..1b5224e2dc8 100644 --- a/yarn-project/prover-client/src/mocks/test_context.ts +++ b/yarn-project/prover-client/src/mocks/test_context.ts @@ -14,7 +14,7 @@ import { type GlobalVariables, TreeSnapshots, } from '@aztec/circuits.js'; -import { times } from '@aztec/foundation/collection'; +import { times, timesParallel } from '@aztec/foundation/collection'; import { Fr } from '@aztec/foundation/fields'; import { type Logger } from '@aztec/foundation/log'; import { TestDateProvider } from '@aztec/foundation/timer'; @@ -158,15 +158,17 @@ export class TestContext { } } - public makeProcessedTx(opts?: Parameters[0]): ProcessedTx; - public makeProcessedTx(seed?: number): ProcessedTx; - public makeProcessedTx(seedOrOpts?: Parameters[0] | number): ProcessedTx { + public makeProcessedTx(opts?: Parameters[0]): Promise; + public makeProcessedTx(seed?: number): Promise; + public async makeProcessedTx( + seedOrOpts?: Parameters[0] | number, + ): Promise { const opts = typeof seedOrOpts === 'number' ? { seed: seedOrOpts } : seedOrOpts; const blockNum = (opts?.globalVariables ?? this.globalVariables).blockNumber.toNumber(); const header = this.getBlockHeader(blockNum - 1); return makeBloatedProcessedTx({ header, - vkTreeRoot: getVKTreeRoot(), + vkTreeRoot: await getVKTreeRoot(), protocolContractTreeRoot, globalVariables: this.globalVariables, ...opts, @@ -184,7 +186,7 @@ export class TestContext { const blockNum = globalVariables.blockNumber.toNumber(); const db = await this.worldState.fork(); const msgs = times(numMsgs, i => new Fr(blockNum * 100 + i)); - const txs = times(numTxs, i => + const txs = await timesParallel(numTxs, i => this.makeProcessedTx({ seed: i + blockNum * 1000, globalVariables, ...makeProcessedTxOpts(i) }), ); await this.setEndTreeRoots(txs); diff --git a/yarn-project/prover-client/src/orchestrator/block-building-helpers.ts b/yarn-project/prover-client/src/orchestrator/block-building-helpers.ts index 20ca0b92c4e..f4b93e2a624 100644 --- a/yarn-project/prover-client/src/orchestrator/block-building-helpers.ts +++ b/yarn-project/prover-client/src/orchestrator/block-building-helpers.ts @@ -136,7 +136,7 @@ export const buildBaseRollupHints = runInSpan( // Append new data to startSpongeBlob const inputSpongeBlob = startSpongeBlob.clone(); - startSpongeBlob.absorb(tx.txEffect.toBlobFields()); + await startSpongeBlob.absorb(tx.txEffect.toBlobFields()); if (tx.avmProvingRequest) { // Build public base rollup hints @@ -172,7 +172,7 @@ export const buildBaseRollupHints = runInSpan( ), }); - const blockHash = tx.constants.historicalHeader.hash(); + const blockHash = await tx.constants.historicalHeader.hash(); const archiveRootMembershipWitness = await getMembershipWitnessFor( blockHash, MerkleTreeId.ARCHIVE, @@ -198,7 +198,7 @@ export const buildBaseRollupHints = runInSpan( // Create data hint for reading fee payer initial balance in Fee Juice // If no fee payer is set, read hint should be empty - const leafSlot = computeFeePayerBalanceLeafSlot(tx.data.feePayer); + const leafSlot = await computeFeePayerBalanceLeafSlot(tx.data.feePayer); const feePayerFeeJuiceBalanceReadHint = tx.data.feePayer.isZero() ? PublicDataHint.empty() : await getPublicDataHint(db, leafSlot.toBigInt()); @@ -232,7 +232,7 @@ export const buildBaseRollupHints = runInSpan( feeWriteSiblingPath, }); - const blockHash = tx.constants.historicalHeader.hash(); + const blockHash = await tx.constants.historicalHeader.hash(); const archiveRootMembershipWitness = await getMembershipWitnessFor( blockHash, MerkleTreeId.ARCHIVE, @@ -275,9 +275,9 @@ export async function getPublicDataHint(db: MerkleTreeWriteOperations, leafSlot: export const buildBlobHints = runInSpan( 'BlockBuilderHelpers', 'buildBlobHints', - (_span: Span, txEffects: TxEffect[]) => { + async (_span: Span, txEffects: TxEffect[]) => { const blobFields = txEffects.flatMap(tx => tx.toBlobFields()); - const blobs = Blob.getBlobs(blobFields); + const blobs = await Blob.getBlobs(blobFields); const blobCommitments = blobs.map(b => b.commitmentToFields()); const blobsHash = new Fr(getBlobsHashFromBlobs(blobs)); return { blobFields, blobCommitments, blobs, blobsHash }; @@ -287,7 +287,7 @@ export const buildBlobHints = runInSpan( export const buildHeaderFromCircuitOutputs = runInSpan( 'BlockBuilderHelpers', 'buildHeaderFromCircuitOutputs', - ( + async ( _span, previousRollupData: BaseOrMergeRollupPublicInputs[], parityPublicInputs: ParityPublicInputs, @@ -326,7 +326,7 @@ export const buildHeaderFromCircuitOutputs = runInSpan( accumulatedFees, accumulatedManaUsed, ); - if (!header.hash().equals(rootRollupOutputs.endBlockHash)) { + if (!(await header.hash()).equals(rootRollupOutputs.endBlockHash)) { logger?.error( `Block header mismatch when building header from circuit outputs.` + `\n\nHeader: ${inspect(header)}` + @@ -375,12 +375,11 @@ export const buildHeaderAndBodyFromTxs = runInSpan( ); l1ToL2Messages = padArrayEnd(l1ToL2Messages, Fr.ZERO, NUMBER_OF_L1_L2_MESSAGES_PER_ROLLUP); - const hasher = (left: Buffer, right: Buffer) => sha256Trunc(Buffer.concat([left, right])); + const hasher = (left: Buffer, right: Buffer) => Promise.resolve(sha256Trunc(Buffer.concat([left, right]))); const parityHeight = Math.ceil(Math.log2(NUMBER_OF_L1_L2_MESSAGES_PER_ROLLUP)); - const parityShaRoot = new MerkleTreeCalculator(parityHeight, Fr.ZERO.toBuffer(), hasher).computeTreeRoot( - l1ToL2Messages.map(msg => msg.toBuffer()), - ); - const blobsHash = getBlobsHashFromBlobs(Blob.getBlobs(body.toBlobFields())); + const parityCalculator = await MerkleTreeCalculator.create(parityHeight, Fr.ZERO.toBuffer(), hasher); + const parityShaRoot = await parityCalculator.computeTreeRoot(l1ToL2Messages.map(msg => msg.toBuffer())); + const blobsHash = getBlobsHashFromBlobs(await Blob.getBlobs(body.toBlobFields())); const contentCommitment = new ContentCommitment(new Fr(numTxs), blobsHash, parityShaRoot, outHash); @@ -442,7 +441,7 @@ export const getConstantRollupData = runInSpan( 'getConstantRollupData', async (_span, globalVariables: GlobalVariables, db: MerkleTreeReadOperations): Promise => { return ConstantRollupData.from({ - vkTreeRoot: getVKTreeRoot(), + vkTreeRoot: await getVKTreeRoot(), protocolContractTreeRoot, lastArchive: await getTreeSnapshot(MerkleTreeId.ARCHIVE, db), globalVariables, diff --git a/yarn-project/prover-client/src/orchestrator/block-proving-state.ts b/yarn-project/prover-client/src/orchestrator/block-proving-state.ts index b6394f13c91..1cfd01f33da 100644 --- a/yarn-project/prover-client/src/orchestrator/block-proving-state.ts +++ b/yarn-project/prover-client/src/orchestrator/block-proving-state.ts @@ -164,16 +164,16 @@ export class BlockProvingState { return this.baseOrMergeProvingOutputs.getParentLocation(location); } - public getMergeRollupInputs(mergeLocation: TreeNodeLocation) { + public async getMergeRollupInputs(mergeLocation: TreeNodeLocation) { const [left, right] = this.baseOrMergeProvingOutputs.getChildren(mergeLocation); if (!left || !right) { throw new Error('At lease one child is not ready.'); } - return new MergeRollupInputs([this.#getPreviousRollupData(left), this.#getPreviousRollupData(right)]); + return new MergeRollupInputs([await this.#getPreviousRollupData(left), await this.#getPreviousRollupData(right)]); } - public getBlockRootRollupTypeAndInputs(proverId: Fr) { + public async getBlockRootRollupTypeAndInputs(proverId: Fr) { if (!this.rootParityProvingOutput) { throw new Error('Root parity is not ready.'); } @@ -184,13 +184,13 @@ export class BlockProvingState { throw new Error('At lease one child is not ready for the block root.'); } - const data = this.#getBlockRootRollupData(proverId); + const data = await this.#getBlockRootRollupData(proverId); if (this.totalNumTxs === 0) { const constants = ConstantRollupData.from({ lastArchive: this.lastArchiveSnapshot, globalVariables: this.globalVariables, - vkTreeRoot: getVKTreeRoot(), + vkTreeRoot: await getVKTreeRoot(), protocolContractTreeRoot, }); @@ -204,8 +204,8 @@ export class BlockProvingState { }; } - const previousRollupData = nonEmptyProofs.map(p => this.#getPreviousRollupData(p!)); - const blobData = this.#getBlockRootRollupBlobData(); + const previousRollupData = await Promise.all(nonEmptyProofs.map(p => this.#getPreviousRollupData(p!))); + const blobData = await this.#getBlockRootRollupBlobData(); if (previousRollupData.length === 1) { return { @@ -224,17 +224,17 @@ export class BlockProvingState { } } - public getPaddingBlockRootInputs(proverId: Fr) { + public async getPaddingBlockRootInputs(proverId: Fr) { if (!this.rootParityProvingOutput) { throw new Error('Root parity is not ready.'); } // Use the new block header and archive of the current block as the previous header and archiver of the next padding block. - const newBlockHeader = this.buildHeaderFromProvingOutputs(); + const newBlockHeader = await this.buildHeaderFromProvingOutputs(); const newArchive = this.blockRootProvingOutput!.inputs.newArchive; const data = BlockRootRollupData.from({ - l1ToL2Roots: this.#getRootParityData(this.rootParityProvingOutput!), + l1ToL2Roots: await this.#getRootParityData(this.rootParityProvingOutput!), l1ToL2MessageSubtreeSiblingPath: this.l1ToL2MessageSubtreeSiblingPath, newArchiveSiblingPath: this.newArchiveSiblingPath, previousBlockHeader: newBlockHeader, @@ -244,7 +244,7 @@ export class BlockProvingState { const constants = ConstantRollupData.from({ lastArchive: newArchive, globalVariables: this.globalVariables, - vkTreeRoot: getVKTreeRoot(), + vkTreeRoot: await getVKTreeRoot(), protocolContractTreeRoot, }); @@ -255,12 +255,12 @@ export class BlockProvingState { }); } - public getRootParityInputs() { + public async getRootParityInputs() { if (!this.baseParityProvingOutputs.every(p => !!p)) { throw new Error('At lease one base parity is not ready.'); } - const children = this.baseParityProvingOutputs.map(p => this.#getRootParityData(p!)); + const children = await Promise.all(this.baseParityProvingOutputs.map(p => this.#getRootParityData(p!))); return new RootParityInputs( children as Tuple, typeof NUM_BASE_PARITY_PER_ROOT_PARITY>, ); @@ -271,9 +271,11 @@ export class BlockProvingState { return this.txs[txIndex]; } - public buildHeaderFromProvingOutputs(logger?: Logger) { + public async buildHeaderFromProvingOutputs(logger?: Logger) { const previousRollupData = - this.totalNumTxs === 0 ? [] : this.#getChildProofsForBlockRoot().map(p => this.#getPreviousRollupData(p!)); + this.totalNumTxs === 0 + ? [] + : await Promise.all(this.#getChildProofsForBlockRoot().map(p => this.#getPreviousRollupData(p!))); let endPartialState = this.previousBlockHeader.state.partial; if (this.totalNumTxs !== 0) { @@ -324,9 +326,9 @@ export class BlockProvingState { this.parentEpoch.reject(reason); } - #getBlockRootRollupData(proverId: Fr) { + async #getBlockRootRollupData(proverId: Fr) { return BlockRootRollupData.from({ - l1ToL2Roots: this.#getRootParityData(this.rootParityProvingOutput!), + l1ToL2Roots: await this.#getRootParityData(this.rootParityProvingOutput!), l1ToL2MessageSubtreeSiblingPath: this.l1ToL2MessageSubtreeSiblingPath, newArchiveSiblingPath: this.newArchiveSiblingPath, previousBlockHeader: this.previousBlockHeader, @@ -334,9 +336,9 @@ export class BlockProvingState { }); } - #getBlockRootRollupBlobData() { + async #getBlockRootRollupBlobData() { const txEffects = this.txs.map(txProvingState => txProvingState.processedTx.txEffect); - const { blobFields, blobCommitments, blobsHash } = buildBlobHints(txEffects); + const { blobFields, blobCommitments, blobsHash } = await buildBlobHints(txEffects); return BlockRootRollupBlobData.from({ blobFields: padArrayEnd(blobFields, Fr.ZERO, FIELDS_PER_BLOB * BLOBS_PER_BLOCK), blobCommitments: padArrayEnd(blobCommitments, [Fr.ZERO, Fr.ZERO], BLOBS_PER_BLOCK), @@ -356,25 +358,25 @@ export class BlockProvingState { : this.baseOrMergeProvingOutputs.getChildren(rootLocation); } - #getPreviousRollupData({ + async #getPreviousRollupData({ inputs, proof, verificationKey, }: PublicInputsAndRecursiveProof) { - const leafIndex = getVKIndex(verificationKey.keyAsFields); + const leafIndex = await getVKIndex(verificationKey.keyAsFields); return new PreviousRollupData( inputs, proof, verificationKey.keyAsFields, - new MembershipWitness(VK_TREE_HEIGHT, BigInt(leafIndex), getVKSiblingPath(leafIndex)), + new MembershipWitness(VK_TREE_HEIGHT, BigInt(leafIndex), await getVKSiblingPath(leafIndex)), ); } - #getRootParityData({ inputs, proof, verificationKey }: PublicInputsAndRecursiveProof) { + async #getRootParityData({ inputs, proof, verificationKey }: PublicInputsAndRecursiveProof) { return new RootParityInput( proof, verificationKey.keyAsFields, - getVKSiblingPath(getVKIndex(verificationKey)), + await getVKSiblingPath(await getVKIndex(verificationKey)), inputs, ); } diff --git a/yarn-project/prover-client/src/orchestrator/block_building_helpers.test.ts b/yarn-project/prover-client/src/orchestrator/block_building_helpers.test.ts index 18a9f033da3..f73fa2219f9 100644 --- a/yarn-project/prover-client/src/orchestrator/block_building_helpers.test.ts +++ b/yarn-project/prover-client/src/orchestrator/block_building_helpers.test.ts @@ -10,8 +10,8 @@ function fieldArrToStr(arr: Fr[]) { } describe('buildBlobHints', () => { - it('correctly builds hints for empty blob fields', () => { - const { blobFields, blobCommitments, blobsHash, blobs } = buildBlobHints([]); + it('correctly builds hints for empty blob fields', async () => { + const { blobFields, blobCommitments, blobsHash, blobs } = await buildBlobHints([]); expect(blobFields).toEqual([]); @@ -47,7 +47,7 @@ describe('buildBlobHints', () => { ); }); - it('correctly builds hints for non-empty blob fields', () => { + it('correctly builds hints for non-empty blob fields', async () => { const txEffect0 = TxEffect.empty(); txEffect0.txHash = new TxHash(new Fr(42)); txEffect0.nullifiers[0] = new Fr(0x123); @@ -55,7 +55,7 @@ describe('buildBlobHints', () => { txEffect1.txHash = new TxHash(new Fr(43)); txEffect1.noteHashes[0] = new Fr(0x6789); txEffect1.nullifiers[0] = new Fr(0x45); - const { blobFields, blobCommitments, blobsHash, blobs } = buildBlobHints([txEffect0, txEffect1]); + const { blobFields, blobCommitments, blobsHash, blobs } = await buildBlobHints([txEffect0, txEffect1]); const blobFields0Str = fieldArrToStr(blobFields.slice(0, 5)); const blobFields1Str = fieldArrToStr(blobFields.slice(5)); diff --git a/yarn-project/prover-client/src/orchestrator/epoch-proving-state.ts b/yarn-project/prover-client/src/orchestrator/epoch-proving-state.ts index c135e2c9f92..0319dabfaaf 100644 --- a/yarn-project/prover-client/src/orchestrator/epoch-proving-state.ts +++ b/yarn-project/prover-client/src/orchestrator/epoch-proving-state.ts @@ -150,23 +150,26 @@ export class EpochProvingState { return this.blockRootOrMergeProvingOutputs.getParentLocation(location); } - public getBlockMergeRollupInputs(mergeLocation: TreeNodeLocation) { + public async getBlockMergeRollupInputs(mergeLocation: TreeNodeLocation) { const [left, right] = this.blockRootOrMergeProvingOutputs.getChildren(mergeLocation); if (!left || !right) { throw new Error('At lease one child is not ready.'); } - return new BlockMergeRollupInputs([this.#getPreviousRollupData(left), this.#getPreviousRollupData(right)]); + return new BlockMergeRollupInputs([ + await this.#getPreviousRollupData(left), + await this.#getPreviousRollupData(right), + ]); } - public getRootRollupInputs(proverId: Fr) { + public async getRootRollupInputs(proverId: Fr) { const [left, right] = this.#getChildProofsForRoot(); if (!left || !right) { throw new Error('At lease one child is not ready.'); } return RootRollupInputs.from({ - previousRollupData: [this.#getPreviousRollupData(left), this.#getPreviousRollupData(right)], + previousRollupData: [await this.#getPreviousRollupData(left), await this.#getPreviousRollupData(right)], proverId, }); } @@ -238,7 +241,7 @@ export class EpochProvingState { : this.blockRootOrMergeProvingOutputs.getChildren(rootLocation); } - #getPreviousRollupData({ + async #getPreviousRollupData({ inputs, proof, verificationKey, @@ -246,12 +249,12 @@ export class EpochProvingState { BlockRootOrBlockMergePublicInputs, typeof NESTED_RECURSIVE_ROLLUP_HONK_PROOF_LENGTH >) { - const leafIndex = getVKIndex(verificationKey.keyAsFields); + const leafIndex = await getVKIndex(verificationKey.keyAsFields); return new PreviousRollupBlockData( inputs, proof, verificationKey.keyAsFields, - new MembershipWitness(VK_TREE_HEIGHT, BigInt(leafIndex), getVKSiblingPath(leafIndex)), + new MembershipWitness(VK_TREE_HEIGHT, BigInt(leafIndex), await getVKSiblingPath(leafIndex)), ); } } diff --git a/yarn-project/prover-client/src/orchestrator/orchestrator.ts b/yarn-project/prover-client/src/orchestrator/orchestrator.ts index 3d96c3b06d6..b1f5e2cb661 100644 --- a/yarn-project/prover-client/src/orchestrator/orchestrator.ts +++ b/yarn-project/prover-client/src/orchestrator/orchestrator.ts @@ -36,7 +36,7 @@ import { SingleTxBlockRootRollupInputs, TubeInputs, } from '@aztec/circuits.js/rollup'; -import { padArrayEnd } from '@aztec/foundation/collection'; +import { padArrayEnd, timesParallel } from '@aztec/foundation/collection'; import { AbortError } from '@aztec/foundation/error'; import { createLogger } from '@aztec/foundation/log'; import { promiseWithResolvers } from '@aztec/foundation/promise'; @@ -238,12 +238,12 @@ export class ProvingOrchestrator implements EpochProver { * Note that if the tube circuits are not started this way, they will be started nontheless after processing. */ @trackSpan('ProvingOrchestrator.startTubeCircuits') - public startTubeCircuits(txs: Tx[]) { + public async startTubeCircuits(txs: Tx[]) { if (!this.provingState?.verifyState()) { throw new Error(`Invalid proving state, call startNewEpoch before starting tube circuits`); } for (const tx of txs) { - const txHash = tx.getTxHash().toString(); + const txHash = (await tx.getTxHash()).toString(); const tubeInputs = new TubeInputs(tx.clientIvcProof); const tubeProof = promiseWithResolvers>(); logger.debug(`Starting tube circuit for tx ${txHash}`); @@ -280,7 +280,7 @@ export class ProvingOrchestrator implements EpochProver { await this.buildBlock(provingState, expectedHeader); // If the proofs were faster than the block building, then we need to try the block root rollup again here - this.checkAndEnqueueBlockRootRollup(provingState); + await this.checkAndEnqueueBlockRootRollup(provingState); return provingState.block!; } @@ -314,7 +314,9 @@ export class ProvingOrchestrator implements EpochProver { throw new Error('Block header mismatch'); } - logger.verbose(`Updating archive tree with block ${provingState.blockNumber} header ${header.hash().toString()}`); + logger.verbose( + `Updating archive tree with block ${provingState.blockNumber} header ${(await header.hash()).toString()}`, + ); await db.updateArchive(header); // Assemble the L2 block @@ -456,8 +458,8 @@ export class ProvingOrchestrator implements EpochProver { NUMBER_OF_L1_L2_MESSAGES_PER_ROLLUP, 'Too many L1 to L2 messages', ); - const baseParityInputs = Array.from({ length: NUM_BASE_PARITY_PER_ROOT_PARITY }, (_, i) => - BaseParityInputs.fromSlice(l1ToL2MessagesPadded, i, getVKTreeRoot()), + const baseParityInputs = await timesParallel(NUM_BASE_PARITY_PER_ROOT_PARITY, async i => + BaseParityInputs.fromSlice(l1ToL2MessagesPadded, i, await getVKTreeRoot()), ); const l1ToL2MessageSubtreeSiblingPath = assertLength( @@ -515,7 +517,7 @@ export class ProvingOrchestrator implements EpochProver { // Executes the base rollup circuit and stored the output as intermediate state for the parent merge/root circuit // Executes the next level of merge if all inputs are available - private enqueueBaseRollup(provingState: BlockProvingState, txIndex: number) { + private async enqueueBaseRollup(provingState: BlockProvingState, txIndex: number) { if (!provingState.verifyState()) { logger.debug('Not running base rollup, state invalid'); return; @@ -523,7 +525,7 @@ export class ProvingOrchestrator implements EpochProver { const txProvingState = provingState.getTxProvingState(txIndex); const { processedTx } = txProvingState; - const { rollupType, inputs } = txProvingState.getBaseRollupTypeAndInputs(); + const { rollupType, inputs } = await txProvingState.getBaseRollupTypeAndInputs(); logger.debug(`Enqueuing deferred proving base rollup for ${processedTx.hash.toString()}`); @@ -547,14 +549,14 @@ export class ProvingOrchestrator implements EpochProver { } }, ), - result => { + async result => { logger.debug(`Completed proof for ${rollupType} for tx ${processedTx.hash.toString()}`); validatePartialState(result.inputs.end, txProvingState.treeSnapshots); const leafLocation = provingState.setBaseRollupProof(txIndex, result); if (provingState.totalNumTxs === 1) { - this.checkAndEnqueueBlockRootRollup(provingState); + await this.checkAndEnqueueBlockRootRollup(provingState); } else { - this.checkAndEnqueueNextMergeRollup(provingState, leafLocation); + await this.checkAndEnqueueNextMergeRollup(provingState, leafLocation); } }, ); @@ -571,11 +573,11 @@ export class ProvingOrchestrator implements EpochProver { const txProvingState = provingState.getTxProvingState(txIndex); const txHash = txProvingState.processedTx.hash.toString(); - const handleResult = (result: ProofAndVerificationKey) => { + const handleResult = async (result: ProofAndVerificationKey) => { logger.debug(`Got tube proof for tx index: ${txIndex}`, { txHash }); txProvingState.setTubeProof(result); this.provingState?.cachedTubeProofs.delete(txHash); - this.checkAndEnqueueNextTxCircuit(provingState, txIndex); + await this.checkAndEnqueueNextTxCircuit(provingState, txIndex); }; if (this.provingState?.cachedTubeProofs.has(txHash)) { @@ -617,13 +619,13 @@ export class ProvingOrchestrator implements EpochProver { // Executes the merge rollup circuit and stored the output as intermediate state for the parent merge/block root circuit // Enqueues the next level of merge if all inputs are available - private enqueueMergeRollup(provingState: BlockProvingState, location: TreeNodeLocation) { + private async enqueueMergeRollup(provingState: BlockProvingState, location: TreeNodeLocation) { if (!provingState.verifyState()) { logger.debug('Not running merge rollup. State no longer valid.'); return; } - const inputs = provingState.getMergeRollupInputs(location); + const inputs = await provingState.getMergeRollupInputs(location); this.deferredProving( provingState, @@ -636,15 +638,15 @@ export class ProvingOrchestrator implements EpochProver { }, signal => this.prover.getMergeRollupProof(inputs, signal, provingState.epochNumber), ), - result => { + async result => { provingState.setMergeRollupProof(location, result); - this.checkAndEnqueueNextMergeRollup(provingState, location); + await this.checkAndEnqueueNextMergeRollup(provingState, location); }, ); } // Executes the block root rollup circuit - private enqueueBlockRootRollup(provingState: BlockProvingState) { + private async enqueueBlockRootRollup(provingState: BlockProvingState) { if (!provingState.verifyState()) { logger.debug('Not running block root rollup, state no longer valid'); return; @@ -652,7 +654,7 @@ export class ProvingOrchestrator implements EpochProver { provingState.blockRootRollupStarted = true; - const { rollupType, inputs } = provingState.getBlockRootRollupTypeAndInputs(this.proverId); + const { rollupType, inputs } = await provingState.getBlockRootRollupTypeAndInputs(this.proverId); logger.debug( `Enqueuing ${rollupType} for block ${provingState.blockNumber} with ${provingState.newL1ToL2Messages.length} l1 to l2 msgs.`, @@ -677,10 +679,10 @@ export class ProvingOrchestrator implements EpochProver { } }, ), - result => { + async result => { provingState.setBlockRootRollupProof(result); - const header = provingState.buildHeaderFromProvingOutputs(logger); - if (!header.hash().equals(provingState.block!.header.hash())) { + const header = await provingState.buildHeaderFromProvingOutputs(logger); + if (!(await header.hash()).equals(await provingState.block!.header.hash())) { logger.error( `Block header mismatch\nCircuit:${inspect(header)}\nComputed:${inspect(provingState.block!.header)}`, ); @@ -693,9 +695,9 @@ export class ProvingOrchestrator implements EpochProver { const epochProvingState = this.provingState!; const leafLocation = epochProvingState.setBlockRootRollupProof(provingState.index, result); if (epochProvingState.totalNumBlocks === 1) { - this.enqueueEpochPadding(epochProvingState); + await this.enqueueEpochPadding(epochProvingState); } else { - this.checkAndEnqueueNextBlockMergeRollup(epochProvingState, leafLocation); + await this.checkAndEnqueueNextBlockMergeRollup(epochProvingState, leafLocation); } }, ); @@ -720,30 +722,30 @@ export class ProvingOrchestrator implements EpochProver { }, signal => this.prover.getBaseParityProof(inputs, signal, provingState.epochNumber), ), - provingOutput => { + async provingOutput => { provingState.setBaseParityProof(index, provingOutput); - this.checkAndEnqueueRootParityCircuit(provingState); + await this.checkAndEnqueueRootParityCircuit(provingState); }, ); } - private checkAndEnqueueRootParityCircuit(provingState: BlockProvingState) { + private async checkAndEnqueueRootParityCircuit(provingState: BlockProvingState) { if (!provingState.isReadyForRootParity()) { return; } - this.enqueueRootParityCircuit(provingState); + await this.enqueueRootParityCircuit(provingState); } // Runs the root parity circuit ans stored the outputs // Enqueues the root rollup proof if all inputs are available - private enqueueRootParityCircuit(provingState: BlockProvingState) { + private async enqueueRootParityCircuit(provingState: BlockProvingState) { if (!provingState.verifyState()) { logger.debug('Not running root parity. State no longer valid.'); return; } - const inputs = provingState.getRootParityInputs(); + const inputs = await provingState.getRootParityInputs(); this.deferredProving( provingState, @@ -756,22 +758,22 @@ export class ProvingOrchestrator implements EpochProver { }, signal => this.prover.getRootParityProof(inputs, signal, provingState.epochNumber), ), - result => { + async result => { provingState.setRootParityProof(result); - this.checkAndEnqueueBlockRootRollup(provingState); + await this.checkAndEnqueueBlockRootRollup(provingState); }, ); } // Executes the block merge rollup circuit and stored the output as intermediate state for the parent merge/block root circuit // Enqueues the next level of merge if all inputs are available - private enqueueBlockMergeRollup(provingState: EpochProvingState, location: TreeNodeLocation) { + private async enqueueBlockMergeRollup(provingState: EpochProvingState, location: TreeNodeLocation) { if (!provingState.verifyState()) { logger.debug('Not running block merge rollup. State no longer valid.'); return; } - const inputs = provingState.getBlockMergeRollupInputs(location); + const inputs = await provingState.getBlockMergeRollupInputs(location); this.deferredProving( provingState, @@ -784,14 +786,14 @@ export class ProvingOrchestrator implements EpochProver { }, signal => this.prover.getBlockMergeRollupProof(inputs, signal, provingState.epochNumber), ), - result => { + async result => { provingState.setBlockMergeRollupProof(location, result); - this.checkAndEnqueueNextBlockMergeRollup(provingState, location); + await this.checkAndEnqueueNextBlockMergeRollup(provingState, location); }, ); } - private enqueueEpochPadding(provingState: EpochProvingState) { + private async enqueueEpochPadding(provingState: EpochProvingState) { if (!provingState.verifyState()) { logger.debug('Not running epoch padding. State no longer valid.'); return; @@ -799,7 +801,7 @@ export class ProvingOrchestrator implements EpochProver { logger.debug('Padding epoch proof with an empty block root proof.'); - const inputs = provingState.getPaddingBlockRootInputs(this.proverId); + const inputs = await provingState.getPaddingBlockRootInputs(this.proverId); this.deferredProving( provingState, @@ -812,16 +814,16 @@ export class ProvingOrchestrator implements EpochProver { }, signal => this.prover.getEmptyBlockRootRollupProof(inputs, signal, provingState.epochNumber), ), - result => { + async result => { logger.debug('Completed proof for padding block root.'); provingState.setPaddingBlockRootProof(result); - this.checkAndEnqueueRootRollup(provingState); + await this.checkAndEnqueueRootRollup(provingState); }, ); } // Executes the root rollup circuit - private enqueueRootRollup(provingState: EpochProvingState) { + private async enqueueRootRollup(provingState: EpochProvingState) { if (!provingState.verifyState()) { logger.debug('Not running root rollup, state no longer valid'); return; @@ -829,7 +831,7 @@ export class ProvingOrchestrator implements EpochProver { logger.debug(`Preparing root rollup`); - const inputs = provingState.getRootRollupInputs(this.proverId); + const inputs = await provingState.getRootRollupInputs(this.proverId); this.deferredProving( provingState, @@ -850,20 +852,20 @@ export class ProvingOrchestrator implements EpochProver { ); } - private checkAndEnqueueNextMergeRollup(provingState: BlockProvingState, currentLocation: TreeNodeLocation) { + private async checkAndEnqueueNextMergeRollup(provingState: BlockProvingState, currentLocation: TreeNodeLocation) { if (!provingState.isReadyForMergeRollup(currentLocation)) { return; } const parentLocation = provingState.getParentLocation(currentLocation); if (parentLocation.level === 0) { - this.checkAndEnqueueBlockRootRollup(provingState); + await this.checkAndEnqueueBlockRootRollup(provingState); } else { - this.enqueueMergeRollup(provingState, parentLocation); + await this.enqueueMergeRollup(provingState, parentLocation); } } - private checkAndEnqueueBlockRootRollup(provingState: BlockProvingState) { + private async checkAndEnqueueBlockRootRollup(provingState: BlockProvingState) { if (!provingState.isReadyForBlockRootRollup()) { logger.debug('Not ready for root rollup'); return; @@ -885,29 +887,32 @@ export class ProvingOrchestrator implements EpochProver { .then(() => this.dbs.delete(blockNumber)) .catch(err => logger.error(`Error closing db for block ${blockNumber}`, err)); - this.enqueueBlockRootRollup(provingState); + await this.enqueueBlockRootRollup(provingState); } - private checkAndEnqueueNextBlockMergeRollup(provingState: EpochProvingState, currentLocation: TreeNodeLocation) { + private async checkAndEnqueueNextBlockMergeRollup( + provingState: EpochProvingState, + currentLocation: TreeNodeLocation, + ) { if (!provingState.isReadyForBlockMerge(currentLocation)) { return; } const parentLocation = provingState.getParentLocation(currentLocation); if (parentLocation.level === 0) { - this.checkAndEnqueueRootRollup(provingState); + await this.checkAndEnqueueRootRollup(provingState); } else { - this.enqueueBlockMergeRollup(provingState, parentLocation); + await this.enqueueBlockMergeRollup(provingState, parentLocation); } } - private checkAndEnqueueRootRollup(provingState: EpochProvingState) { + private async checkAndEnqueueRootRollup(provingState: EpochProvingState) { if (!provingState.isReadyForRootRollup()) { logger.debug('Not ready for root rollup'); return; } - this.enqueueRootRollup(provingState); + await this.enqueueRootRollup(provingState); } /** @@ -955,14 +960,14 @@ export class ProvingOrchestrator implements EpochProver { }, ); - this.deferredProving(provingState, doAvmProving, proofAndVk => { + this.deferredProving(provingState, doAvmProving, async proofAndVk => { logger.debug(`Proven VM for tx index: ${txIndex}`); txProvingState.setAvmProof(proofAndVk); - this.checkAndEnqueueNextTxCircuit(provingState, txIndex); + await this.checkAndEnqueueNextTxCircuit(provingState, txIndex); }); } - private checkAndEnqueueNextTxCircuit(provingState: BlockProvingState, txIndex: number) { + private async checkAndEnqueueNextTxCircuit(provingState: BlockProvingState, txIndex: number) { const txProvingState = provingState.getTxProvingState(txIndex); if (!txProvingState.ready()) { return; @@ -971,6 +976,6 @@ export class ProvingOrchestrator implements EpochProver { // We must have completed all proving (tube proof and (if required) vm proof are generated), we now move to the base rollup. logger.debug(`Public functions completed for tx ${txIndex} enqueueing base rollup`); - this.enqueueBaseRollup(provingState, txIndex); + await this.enqueueBaseRollup(provingState, txIndex); } } diff --git a/yarn-project/prover-client/src/orchestrator/orchestrator_errors.test.ts b/yarn-project/prover-client/src/orchestrator/orchestrator_errors.test.ts index a0ddf574264..a55cab7951c 100644 --- a/yarn-project/prover-client/src/orchestrator/orchestrator_errors.test.ts +++ b/yarn-project/prover-client/src/orchestrator/orchestrator_errors.test.ts @@ -1,5 +1,5 @@ import { Fr } from '@aztec/circuits.js'; -import { times } from '@aztec/foundation/collection'; +import { timesParallel } from '@aztec/foundation/collection'; import { createLogger } from '@aztec/foundation/log'; import { TestContext } from '../mocks/test_context.js'; @@ -24,14 +24,14 @@ describe('prover/orchestrator/errors', () => { describe('errors', () => { it('throws if adding too many transactions', async () => { - const txs = times(4, i => context.makeProcessedTx(i + 1)); + const txs = await timesParallel(4, i => context.makeProcessedTx(i + 1)); await context.setEndTreeRoots(txs); orchestrator.startNewEpoch(1, 1, 1); await orchestrator.startNewBlock(context.globalVariables, [], context.getPreviousBlockHeader()); await orchestrator.addTxs(txs); - await expect(async () => await orchestrator.addTxs([context.makeProcessedTx()])).rejects.toThrow( + await expect(async () => await orchestrator.addTxs([await context.makeProcessedTx()])).rejects.toThrow( `Block ${context.blockNumber} has been initialized with transactions.`, ); @@ -51,14 +51,14 @@ describe('prover/orchestrator/errors', () => { }); it('throws if adding a transaction before starting epoch', async () => { - await expect(async () => await orchestrator.addTxs([context.makeProcessedTx()])).rejects.toThrow( + await expect(async () => await orchestrator.addTxs([await context.makeProcessedTx()])).rejects.toThrow( /Block proving state for 1 not found/, ); }); it('throws if adding a transaction before starting block', async () => { orchestrator.startNewEpoch(1, 1, 1); - await expect(async () => await orchestrator.addTxs([context.makeProcessedTx()])).rejects.toThrow( + await expect(async () => await orchestrator.addTxs([await context.makeProcessedTx()])).rejects.toThrow( /Block proving state for 1 not found/, ); }); @@ -75,7 +75,7 @@ describe('prover/orchestrator/errors', () => { await orchestrator.startNewBlock(context.globalVariables, [], context.getPreviousBlockHeader()); orchestrator.cancel(); - await expect(async () => await context.orchestrator.addTxs([context.makeProcessedTx()])).rejects.toThrow( + await expect(async () => await context.orchestrator.addTxs([await context.makeProcessedTx()])).rejects.toThrow( 'Invalid proving state when adding a tx', ); }); diff --git a/yarn-project/prover-client/src/orchestrator/orchestrator_mixed_blocks.test.ts b/yarn-project/prover-client/src/orchestrator/orchestrator_mixed_blocks.test.ts index 3279db55cf4..f33af88fb5c 100644 --- a/yarn-project/prover-client/src/orchestrator/orchestrator_mixed_blocks.test.ts +++ b/yarn-project/prover-client/src/orchestrator/orchestrator_mixed_blocks.test.ts @@ -1,7 +1,7 @@ import { NUMBER_OF_L1_L2_MESSAGES_PER_ROLLUP } from '@aztec/circuits.js'; import { fr } from '@aztec/circuits.js/testing'; import { range } from '@aztec/foundation/array'; -import { times } from '@aztec/foundation/collection'; +import { timesParallel } from '@aztec/foundation/collection'; import { createLogger } from '@aztec/foundation/log'; import { TestContext } from '../mocks/test_context.js'; @@ -12,7 +12,7 @@ describe('prover/orchestrator/mixed-blocks', () => { let context: TestContext; const runTest = async (numTxs: number) => { - const txs = times(numTxs, i => context.makeProcessedTx(i + 1)); + const txs = await timesParallel(numTxs, i => context.makeProcessedTx(i + 1)); await context.setEndTreeRoots(txs); const l1ToL2Messages = range(NUMBER_OF_L1_L2_MESSAGES_PER_ROLLUP, 1 + 0x400).map(fr); diff --git a/yarn-project/prover-client/src/orchestrator/orchestrator_multi_public_functions.test.ts b/yarn-project/prover-client/src/orchestrator/orchestrator_multi_public_functions.test.ts index fd709e5d713..b70a3a4d10c 100644 --- a/yarn-project/prover-client/src/orchestrator/orchestrator_multi_public_functions.test.ts +++ b/yarn-project/prover-client/src/orchestrator/orchestrator_multi_public_functions.test.ts @@ -1,5 +1,5 @@ import { mockTx } from '@aztec/circuit-types'; -import { times } from '@aztec/foundation/collection'; +import { timesParallel } from '@aztec/foundation/collection'; import { createLogger } from '@aztec/foundation/log'; import { getVKTreeRoot } from '@aztec/noir-protocol-circuits-types/vks'; import { protocolContractTreeRoot } from '@aztec/protocol-contracts'; @@ -28,7 +28,7 @@ describe('prover/orchestrator/public-functions', () => { numberOfNonRevertiblePublicCallRequests: number, numberOfRevertiblePublicCallRequests: number, ) => { - const txs = times(numTransactions, (i: number) => + const txs = await timesParallel(numTransactions, (i: number) => mockTx(100000 * testCount++ + 1000 * i, { numberOfNonRevertiblePublicCallRequests, numberOfRevertiblePublicCallRequests, @@ -36,7 +36,7 @@ describe('prover/orchestrator/public-functions', () => { ); for (const tx of txs) { tx.data.constants.historicalHeader = context.getBlockHeader(0); - tx.data.constants.vkTreeRoot = getVKTreeRoot(); + tx.data.constants.vkTreeRoot = await getVKTreeRoot(); tx.data.constants.protocolContractTreeRoot = protocolContractTreeRoot; } diff --git a/yarn-project/prover-client/src/orchestrator/orchestrator_public_functions.test.ts b/yarn-project/prover-client/src/orchestrator/orchestrator_public_functions.test.ts index d64dae5356f..a33772019b3 100644 --- a/yarn-project/prover-client/src/orchestrator/orchestrator_public_functions.test.ts +++ b/yarn-project/prover-client/src/orchestrator/orchestrator_public_functions.test.ts @@ -31,12 +31,12 @@ describe('prover/orchestrator/public-functions', () => { ] as const)( 'builds an L2 block with %i non-revertible and %i revertible calls', async (numberOfNonRevertiblePublicCallRequests: number, numberOfRevertiblePublicCallRequests: number) => { - const tx = mockTx(1000 * testCount++, { + const tx = await mockTx(1000 * testCount++, { numberOfNonRevertiblePublicCallRequests, numberOfRevertiblePublicCallRequests, }); tx.data.constants.historicalHeader = context.getBlockHeader(0); - tx.data.constants.vkTreeRoot = getVKTreeRoot(); + tx.data.constants.vkTreeRoot = await getVKTreeRoot(); tx.data.constants.protocolContractTreeRoot = protocolContractTreeRoot; const [processed, _] = await context.processPublicFunctions([tx], 1); diff --git a/yarn-project/prover-client/src/orchestrator/orchestrator_single_blocks.test.ts b/yarn-project/prover-client/src/orchestrator/orchestrator_single_blocks.test.ts index f00df6f370f..6dccedb628b 100644 --- a/yarn-project/prover-client/src/orchestrator/orchestrator_single_blocks.test.ts +++ b/yarn-project/prover-client/src/orchestrator/orchestrator_single_blocks.test.ts @@ -1,7 +1,7 @@ import { NUMBER_OF_L1_L2_MESSAGES_PER_ROLLUP } from '@aztec/circuits.js'; import { fr } from '@aztec/circuits.js/testing'; import { range } from '@aztec/foundation/array'; -import { times } from '@aztec/foundation/collection'; +import { timesParallel } from '@aztec/foundation/collection'; import { createLogger } from '@aztec/foundation/log'; import { TestContext } from '../mocks/test_context.js'; @@ -30,7 +30,7 @@ describe('prover/orchestrator/blocks', () => { }); it('builds a block with 1 transaction', async () => { - const txs = [context.makeProcessedTx(1)]; + const txs = [await context.makeProcessedTx(1)]; await context.setEndTreeRoots(txs); // This will need to be a 2 tx block @@ -45,7 +45,7 @@ describe('prover/orchestrator/blocks', () => { }); it('builds a block concurrently with transaction simulation', async () => { - const txs = times(4, i => context.makeProcessedTx(i + 1)); + const txs = await timesParallel(4, i => context.makeProcessedTx(i + 1)); await context.setEndTreeRoots(txs); const l1ToL2Messages = range(NUMBER_OF_L1_L2_MESSAGES_PER_ROLLUP, 1 + 0x400).map(fr); diff --git a/yarn-project/prover-client/src/orchestrator/orchestrator_workflow.test.ts b/yarn-project/prover-client/src/orchestrator/orchestrator_workflow.test.ts index 7d9636b1878..d3a83da47a2 100644 --- a/yarn-project/prover-client/src/orchestrator/orchestrator_workflow.test.ts +++ b/yarn-project/prover-client/src/orchestrator/orchestrator_workflow.test.ts @@ -114,7 +114,7 @@ describe('prover/orchestrator', () => { it('waits for block to be completed before enqueueing block root proof', async () => { orchestrator.startNewEpoch(1, 1, 1); await orchestrator.startNewBlock(globalVariables, [], previousBlockHeader); - const txs = [context.makeProcessedTx(1), context.makeProcessedTx(2)]; + const txs = await Promise.all([context.makeProcessedTx(1), context.makeProcessedTx(2)]); await context.setEndTreeRoots(txs); await orchestrator.addTxs(txs); @@ -131,10 +131,12 @@ describe('prover/orchestrator', () => { it('can start tube proofs before adding processed txs', async () => { const getTubeSpy = jest.spyOn(prover, 'getTubeProof'); orchestrator.startNewEpoch(1, 1, 1); - const processedTxs = [context.makeProcessedTx(1), context.makeProcessedTx(2)]; + const processedTxs = await Promise.all([context.makeProcessedTx(1), context.makeProcessedTx(2)]); processedTxs.forEach((tx, i) => (tx.clientIvcProof = ClientIvcProof.fake(i + 1))); - const txs = processedTxs.map(tx => ({ getTxHash: () => tx.hash, clientIvcProof: tx.clientIvcProof } as Tx)); - orchestrator.startTubeCircuits(txs); + const txs = processedTxs.map( + tx => ({ getTxHash: () => Promise.resolve(tx.hash), clientIvcProof: tx.clientIvcProof } as Tx), + ); + await orchestrator.startTubeCircuits(txs); await sleep(100); expect(getTubeSpy).toHaveBeenCalledTimes(2); diff --git a/yarn-project/prover-client/src/orchestrator/tx-proving-state.ts b/yarn-project/prover-client/src/orchestrator/tx-proving-state.ts index 457a6b3e4b6..2e5f00b7909 100644 --- a/yarn-project/prover-client/src/orchestrator/tx-proving-state.ts +++ b/yarn-project/prover-client/src/orchestrator/tx-proving-state.ts @@ -52,16 +52,16 @@ export class TxProvingState { return this.processedTx.avmProvingRequest!.inputs; } - public getBaseRollupTypeAndInputs() { + public async getBaseRollupTypeAndInputs() { if (this.requireAvmProof) { return { rollupType: 'public-base-rollup' satisfies CircuitName, - inputs: this.#getPublicBaseInputs(), + inputs: await this.#getPublicBaseInputs(), }; } else { return { rollupType: 'private-base-rollup' satisfies CircuitName, - inputs: this.#getPrivateBaseInputs(), + inputs: await this.#getPrivateBaseInputs(), }; } } @@ -74,12 +74,12 @@ export class TxProvingState { this.avm = avmProofAndVk; } - #getPrivateBaseInputs() { + async #getPrivateBaseInputs() { if (!this.tube) { throw new Error('Tx not ready for proving base rollup.'); } - const vkData = this.#getTubeVkData(); + const vkData = await this.#getTubeVkData(); const tubeData = new PrivateTubeData( this.processedTx.data.toPrivateToRollupKernelCircuitPublicInputs(), this.tube.proof, @@ -92,7 +92,7 @@ export class TxProvingState { return new PrivateBaseRollupInputs(tubeData, this.baseRollupHints); } - #getPublicBaseInputs() { + async #getPublicBaseInputs() { if (!this.processedTx.avmProvingRequest) { throw new Error('Should create private base rollup for a tx not requiring avm proof.'); } @@ -106,13 +106,13 @@ export class TxProvingState { const tubeData = new PublicTubeData( this.processedTx.data.toPrivateToPublicKernelCircuitPublicInputs(), this.tube.proof, - this.#getTubeVkData(), + await this.#getTubeVkData(), ); const avmProofData = new AvmProofData( this.processedTx.avmProvingRequest.inputs.output, this.avm.proof, - this.#getAvmVkData(), + await this.#getAvmVkData(), ); if (!(this.baseRollupHints instanceof PublicBaseRollupHints)) { @@ -122,21 +122,21 @@ export class TxProvingState { return new PublicBaseRollupInputs(tubeData, avmProofData, this.baseRollupHints); } - #getTubeVkData() { + async #getTubeVkData() { let vkIndex = TUBE_VK_INDEX; try { - vkIndex = getVKIndex(this.tube!.verificationKey); + vkIndex = await getVKIndex(this.tube!.verificationKey); } catch (_ignored) { // TODO(#7410) The VK for the tube won't be in the tree for now, so we manually set it to the tube vk index } - const vkPath = getVKSiblingPath(vkIndex); + const vkPath = await getVKSiblingPath(vkIndex); return new VkWitnessData(this.tube!.verificationKey, vkIndex, vkPath); } - #getAvmVkData() { + async #getAvmVkData() { const vkIndex = AVM_VK_INDEX; - const vkPath = getVKSiblingPath(vkIndex); + const vkPath = await getVKSiblingPath(vkIndex); return new VkWitnessData(this.avm!.verificationKey, AVM_VK_INDEX, vkPath); } } diff --git a/yarn-project/prover-client/src/prover-client/server-epoch-prover.ts b/yarn-project/prover-client/src/prover-client/server-epoch-prover.ts index b222098dead..ee36120a0a0 100644 --- a/yarn-project/prover-client/src/prover-client/server-epoch-prover.ts +++ b/yarn-project/prover-client/src/prover-client/server-epoch-prover.ts @@ -13,8 +13,8 @@ export class ServerEpochProver implements EpochProver { this.orchestrator.startNewEpoch(epochNumber, firstBlockNumber, totalNumBlocks); this.facade.start(); } - startTubeCircuits(txs: Tx[]): void { - this.orchestrator.startTubeCircuits(txs); + startTubeCircuits(txs: Tx[]): Promise { + return this.orchestrator.startTubeCircuits(txs); } setBlockCompleted(blockNumber: number, expectedBlockHeader?: BlockHeader): Promise { return this.orchestrator.setBlockCompleted(blockNumber, expectedBlockHeader); diff --git a/yarn-project/prover-client/src/proving_broker/proof_store/inline_proof_store.ts b/yarn-project/prover-client/src/proving_broker/proof_store/inline_proof_store.ts index 58a0cb8e524..6b87c419b69 100644 --- a/yarn-project/prover-client/src/proving_broker/proof_store/inline_proof_store.ts +++ b/yarn-project/prover-client/src/proving_broker/proof_store/inline_proof_store.ts @@ -52,7 +52,7 @@ export class InlineProofStore implements ProofStore { return (PREFIX + SEPARATOR + encoded) as ProofUri; } - private decode(uri: ProofUri, schema: ZodFor): T { + private decode(uri: ProofUri, schema: ZodFor): Promise { const [prefix, data] = uri.split(SEPARATOR); if (prefix !== PREFIX) { throw new Error('Invalid proof input URI: ' + prefix); diff --git a/yarn-project/prover-client/src/proving_broker/proving_broker.ts b/yarn-project/prover-client/src/proving_broker/proving_broker.ts index 5eabd41ea37..239ca9ee750 100644 --- a/yarn-project/prover-client/src/proving_broker/proving_broker.ts +++ b/yarn-project/prover-client/src/proving_broker/proving_broker.ts @@ -147,13 +147,13 @@ export class ProvingBroker implements ProvingJobProducer, ProvingJobConsumer, Tr return count; }; - public start(): Promise { + public async start(): Promise { if (this.started) { this.logger.info('Proving Broker already started'); return Promise.resolve(); } this.logger.info('Proving Broker started'); - for (const [item, result] of this.database.allProvingJobs()) { + for await (const [item, result] of this.database.allProvingJobs()) { this.logger.info(`Restoring proving job id=${item.id} settled=${!!result}`, { provingJobId: item.id, status: result ? result.status : 'pending', diff --git a/yarn-project/prover-client/src/proving_broker/proving_broker_database.ts b/yarn-project/prover-client/src/proving_broker/proving_broker_database.ts index a713431390a..9d1b0836476 100644 --- a/yarn-project/prover-client/src/proving_broker/proving_broker_database.ts +++ b/yarn-project/prover-client/src/proving_broker/proving_broker_database.ts @@ -19,7 +19,7 @@ export interface ProvingBrokerDatabase { /** * Returns an iterator over all saved proving jobs */ - allProvingJobs(): Iterable<[ProvingJob, ProvingJobSettledResult | undefined]>; + allProvingJobs(): AsyncIterableIterator<[ProvingJob, ProvingJobSettledResult | undefined]>; /** * Saves the result of a proof request diff --git a/yarn-project/prover-client/src/proving_broker/proving_broker_database/broker_persisted_database.test.ts b/yarn-project/prover-client/src/proving_broker/proving_broker_database/broker_persisted_database.test.ts index 1672baf4d9d..9e015ee95af 100644 --- a/yarn-project/prover-client/src/proving_broker/proving_broker_database/broker_persisted_database.test.ts +++ b/yarn-project/prover-client/src/proving_broker/proving_broker_database/broker_persisted_database.test.ts @@ -1,4 +1,5 @@ import { type ProofUri, type ProvingJob, type ProvingJobSettledResult, ProvingRequestType } from '@aztec/circuit-types'; +import { toArray } from '@aztec/foundation/iterable'; import { existsSync } from 'fs'; import { mkdir, mkdtemp, rm } from 'fs/promises'; @@ -159,7 +160,7 @@ describe('ProvingBrokerPersistedDatabase', () => { expectedJobs.push([job, result]); } } - const allJobs = Array.from(db.allProvingJobs()); + const allJobs = await toArray(db.allProvingJobs()); expect(allJobs.length).toBe(numJobs); expectArrayEquivalence(expectedJobs, allJobs); }); @@ -210,7 +211,7 @@ describe('ProvingBrokerPersistedDatabase', () => { expectSubdirectoriesExist(directory, epochNumbers, true); const expectedJobsAfterEpoch14 = expectedJobs.filter(x => x[0].epochNumber > 14); await db.deleteAllProvingJobsOlderThanEpoch(15); - const allJobs = Array.from(db.allProvingJobs()); + const allJobs = await toArray(db.allProvingJobs()); expect(allJobs.length).toBe(expectedJobsAfterEpoch14.length); expectArrayEquivalence(expectedJobsAfterEpoch14, allJobs); @@ -261,7 +262,7 @@ describe('ProvingBrokerPersistedDatabase', () => { const secondDb = await KVBrokerDatabase.new(config); // All data should be restored - const allJobs = Array.from(secondDb.allProvingJobs()); + const allJobs = await toArray(secondDb.allProvingJobs()); expect(allJobs.length).toBe(numJobs); expectArrayEquivalence(expectedJobs, allJobs); }); diff --git a/yarn-project/prover-client/src/proving_broker/proving_broker_database/memory.ts b/yarn-project/prover-client/src/proving_broker/proving_broker_database/memory.ts index 1a20ff8757d..770210210f2 100644 --- a/yarn-project/prover-client/src/proving_broker/proving_broker_database/memory.ts +++ b/yarn-project/prover-client/src/proving_broker/proving_broker_database/memory.ts @@ -51,8 +51,8 @@ export class InMemoryBrokerDatabase implements ProvingBrokerDatabase { return this.deleteProvingJobs(toDelete); } - *allProvingJobs(): Iterable<[ProvingJob, ProvingJobSettledResult | undefined]> { - for (const item of this.jobs.values()) { + async *allProvingJobs(): AsyncIterableIterator<[ProvingJob, ProvingJobSettledResult | undefined]> { + for await (const item of this.jobs.values()) { yield [item, this.results.get(item.id)] as const; } } diff --git a/yarn-project/prover-client/src/proving_broker/proving_broker_database/persisted.ts b/yarn-project/prover-client/src/proving_broker/proving_broker_database/persisted.ts index 39e0a74e46c..31a89a81c96 100644 --- a/yarn-project/prover-client/src/proving_broker/proving_broker_database/persisted.ts +++ b/yarn-project/prover-client/src/proving_broker/proving_broker_database/persisted.ts @@ -5,6 +5,7 @@ import { ProvingJobSettledResult, getEpochFromProvingJobId, } from '@aztec/circuit-types'; +import { toArray } from '@aztec/foundation/iterable'; import { jsonParseWithSchema, jsonStringify } from '@aztec/foundation/json-rpc'; import { type Logger, createLogger } from '@aztec/foundation/log'; import { type AztecMap } from '@aztec/kv-store'; @@ -34,11 +35,11 @@ class SingleEpochDatabase { await this.jobs.set(job.id, jsonStringify(job)); } - *allProvingJobs(): Iterable<[ProvingJob, ProvingJobSettledResult | undefined]> { + async *allProvingJobs(): AsyncIterableIterator<[ProvingJob, ProvingJobSettledResult | undefined]> { for (const jobStr of this.jobs.values()) { - const job = jsonParseWithSchema(jobStr, ProvingJob); + const job = await jsonParseWithSchema(jobStr, ProvingJob); const resultStr = this.jobResults.get(job.id); - const result = resultStr ? jsonParseWithSchema(resultStr, ProvingJobSettledResult) : undefined; + const result = resultStr ? await jsonParseWithSchema(resultStr, ProvingJobSettledResult) : undefined; yield [job, result]; } } @@ -151,8 +152,8 @@ export class KVBrokerDatabase implements ProvingBrokerDatabase { await epochDb.addProvingJob(job); } - *allProvingJobs(): Iterable<[ProvingJob, ProvingJobSettledResult | undefined]> { - const iterators = Array.from(this.epochs.values()).map(x => x.allProvingJobs()); + async *allProvingJobs(): AsyncIterableIterator<[ProvingJob, ProvingJobSettledResult | undefined]> { + const iterators = (await toArray(this.epochs.values())).map(x => x.allProvingJobs()); for (const it of iterators) { yield* it; } diff --git a/yarn-project/prover-client/src/test/bb_prover_full_rollup.test.ts b/yarn-project/prover-client/src/test/bb_prover_full_rollup.test.ts index 8ec558a32d6..76933ac092b 100644 --- a/yarn-project/prover-client/src/test/bb_prover_full_rollup.test.ts +++ b/yarn-project/prover-client/src/test/bb_prover_full_rollup.test.ts @@ -2,7 +2,7 @@ import { BBNativeRollupProver, type BBProverConfig } from '@aztec/bb-prover'; import { mockTx } from '@aztec/circuit-types'; import { Fr, NUMBER_OF_L1_L2_MESSAGES_PER_ROLLUP } from '@aztec/circuits.js'; import { makeTuple } from '@aztec/foundation/array'; -import { times } from '@aztec/foundation/collection'; +import { timesParallel } from '@aztec/foundation/collection'; import { type Logger, createLogger } from '@aztec/foundation/log'; import { getTestData, isGenerateTestDataEnabled } from '@aztec/foundation/testing'; import { writeTestData } from '@aztec/foundation/testing/files'; @@ -46,11 +46,11 @@ describe('prover/bb_prover/full-rollup', () => { for (let blockNum = 1; blockNum <= blockCount; blockNum++) { const globals = makeGlobals(blockNum); const l1ToL2Messages = makeTuple(NUMBER_OF_L1_L2_MESSAGES_PER_ROLLUP, Fr.random); - const txs = times(nonEmptyTxs, (i: number) => { + const txs = await timesParallel(nonEmptyTxs, async (i: number) => { const txOpts = { numberOfNonRevertiblePublicCallRequests: 0, numberOfRevertiblePublicCallRequests: 0 }; - const tx = mockTx(blockNum * 100_000 + 1000 * (i + 1), txOpts); + const tx = await mockTx(blockNum * 100_000 + 1000 * (i + 1), txOpts); tx.data.constants.historicalHeader = initialHeader; - tx.data.constants.vkTreeRoot = getVKTreeRoot(); + tx.data.constants.vkTreeRoot = await getVKTreeRoot(); return tx; }); @@ -91,7 +91,7 @@ describe('prover/bb_prover/full-rollup', () => { // TODO(@PhilWindle): Remove public functions and re-enable once we can handle empty tx slots it.skip('proves all circuits', async () => { const numTransactions = 4; - const txs = times(numTransactions, (i: number) => + const txs = await timesParallel(numTransactions, (i: number) => mockTx(1000 * (i + 1), { numberOfNonRevertiblePublicCallRequests: 2, numberOfRevertiblePublicCallRequests: 1, diff --git a/yarn-project/prover-client/src/test/bb_prover_parity.test.ts b/yarn-project/prover-client/src/test/bb_prover_parity.test.ts index 1f65db5607f..cd6e9f8fd90 100644 --- a/yarn-project/prover-client/src/test/bb_prover_parity.test.ts +++ b/yarn-project/prover-client/src/test/bb_prover_parity.test.ts @@ -11,7 +11,7 @@ import { VerificationKeyAsFields, makeRecursiveProof, } from '@aztec/circuits.js'; -import { makeTuple } from '@aztec/foundation/array'; +import { makeTuple, makeTupleAsync } from '@aztec/foundation/array'; import { randomBytes } from '@aztec/foundation/crypto'; import { createLogger } from '@aztec/foundation/log'; import { @@ -47,8 +47,8 @@ describe('prover/bb_prover/parity', () => { NUMBER_OF_L1_L2_MESSAGES_PER_ROLLUP, Fr.random, ); - const baseParityInputs = makeTuple(NUM_BASE_PARITY_PER_ROOT_PARITY, i => - BaseParityInputs.fromSlice(l1ToL2Messages, i, getVKTreeRoot()), + const baseParityInputs = await makeTupleAsync(NUM_BASE_PARITY_PER_ROOT_PARITY, async i => + BaseParityInputs.fromSlice(l1ToL2Messages, i, await getVKTreeRoot()), ); // Generate the base parity proofs @@ -56,12 +56,12 @@ describe('prover/bb_prover/parity', () => { baseParityInputs.map(baseInputs => context.prover.getBaseParityProof(baseInputs)), ); - const rootInputs = makeTuple(NUM_BASE_PARITY_PER_ROOT_PARITY, i => { + const rootInputs = await makeTupleAsync(NUM_BASE_PARITY_PER_ROOT_PARITY, async i => { const { proof, inputs, verificationKey } = baseParityProofsAndPublicInputs[i]; return new RootParityInput( proof, verificationKey.keyAsFields, - getVKSiblingPath(ProtocolCircuitVkIndexes.BaseParityArtifact), + await getVKSiblingPath(ProtocolCircuitVkIndexes.BaseParityArtifact), inputs, ); }); @@ -93,7 +93,7 @@ describe('prover/bb_prover/parity', () => { // In each case either the proof should fail to generate or verify const validVk = rootParityInputs.children[0].verificationKey; - const baseParityVkPath = getVKSiblingPath(ProtocolCircuitVkIndexes.BaseParityArtifact); + const baseParityVkPath = await getVKSiblingPath(ProtocolCircuitVkIndexes.BaseParityArtifact); const validPublicInputs = rootParityInputs.children[0].publicInputs; const validProof = rootParityInputs.children[0].proof; @@ -111,7 +111,7 @@ describe('prover/bb_prover/parity', () => { validProof, validVk, baseParityVkPath, - new ParityPublicInputs(Fr.fromBuffer(shaRoot), Fr.random(), getVKTreeRoot()), + new ParityPublicInputs(Fr.fromBuffer(shaRoot), Fr.random(), await getVKTreeRoot()), ); const defectiveVerificationKey = new RootParityInput( diff --git a/yarn-project/prover-node/src/job/epoch-proving-job.test.ts b/yarn-project/prover-node/src/job/epoch-proving-job.test.ts index a1bab2aa09e..d550fce81bb 100644 --- a/yarn-project/prover-node/src/job/epoch-proving-job.test.ts +++ b/yarn-project/prover-node/src/job/epoch-proving-job.test.ts @@ -11,6 +11,7 @@ import { import { BlockHeader, Proof } from '@aztec/circuits.js'; import { RootRollupPublicInputs } from '@aztec/circuits.js/rollup'; import { times, timesParallel } from '@aztec/foundation/collection'; +import { toArray } from '@aztec/foundation/iterable'; import { sleep } from '@aztec/foundation/sleep'; import { type L1Publisher } from '@aztec/sequencer-client'; import { type PublicProcessor, type PublicProcessorFactory } from '@aztec/simulator/server'; @@ -83,7 +84,7 @@ describe('epoch-proving-job', () => { blocks = await timesParallel(NUM_BLOCKS, i => L2Block.random(i + 1, TXS_PER_BLOCK)); txs = times(NUM_TXS, i => mock({ - getTxHash: () => blocks[i % NUM_BLOCKS].body.txEffects[i % TXS_PER_BLOCK].txHash, + getTxHash: () => Promise.resolve(blocks[i % NUM_BLOCKS].body.txEffects[i % TXS_PER_BLOCK].txHash), }), ); @@ -94,9 +95,13 @@ describe('epoch-proving-job', () => { worldState.fork.mockResolvedValue(db); prover.finaliseEpoch.mockResolvedValue({ publicInputs, proof }); publisher.submitEpochProof.mockResolvedValue(true); - publicProcessor.process.mockImplementation((txs: Iterable) => - Promise.resolve([Array.from(txs).map(tx => mock({ hash: tx.getTxHash() })), [], []]), - ); + publicProcessor.process.mockImplementation(async txs => { + const txsArray = await toArray(txs); + const processedTxs = await Promise.all( + txsArray.map(async tx => mock({ hash: await tx.getTxHash() })), + ); + return [processedTxs, [], []]; + }); }); it('works', async () => { @@ -112,9 +117,11 @@ describe('epoch-proving-job', () => { }); it('fails if fails to process txs for a block', async () => { - publicProcessor.process.mockImplementation((txs: Iterable) => - Promise.resolve([[], Array.from(txs).map(tx => ({ error: new Error('Failed to process tx'), tx })), []]), - ); + publicProcessor.process.mockImplementation(async txs => { + const txsArray = await toArray(txs); + const errors = txsArray.map(tx => ({ error: new Error('Failed to process tx'), tx })); + return [[], errors, []]; + }); const job = createJob(); await job.run(); @@ -124,7 +131,7 @@ describe('epoch-proving-job', () => { }); it('fails if does not process all txs for a block', async () => { - publicProcessor.process.mockImplementation((_txs: Iterable) => Promise.resolve([[], [], []])); + publicProcessor.process.mockImplementation(_txs => Promise.resolve([[], [], []])); const job = createJob(); await job.run(); diff --git a/yarn-project/prover-node/src/job/epoch-proving-job.ts b/yarn-project/prover-node/src/job/epoch-proving-job.ts index f9999973fd6..f81bb7a3242 100644 --- a/yarn-project/prover-node/src/job/epoch-proving-job.ts +++ b/yarn-project/prover-node/src/job/epoch-proving-job.ts @@ -91,19 +91,19 @@ export class EpochProvingJob implements Traceable { try { this.prover.startNewEpoch(epochNumber, fromBlock, epochSizeBlocks); - this.prover.startTubeCircuits(this.txs); + await this.prover.startTubeCircuits(this.txs); await asyncPool(this.config.parallelBlockLimit, this.blocks, async block => { this.checkState(); const globalVariables = block.header.globalVariables; - const txs = this.getTxs(block); + const txs = await this.getTxs(block); const l1ToL2Messages = await this.getL1ToL2Messages(block); const previousHeader = (await this.getBlockHeader(block.number - 1))!; this.log.verbose(`Starting processing block ${block.number}`, { number: block.number, - blockHash: block.hash().toString(), + blockHash: (await block.hash()).toString(), lastArchive: block.header.lastArchive.root, noteHashTreeRoot: block.header.state.partial.noteHashTree.root, nullifierTreeRoot: block.header.state.partial.nullifierTree.root, @@ -124,7 +124,7 @@ export class EpochProvingJob implements Traceable { await db.close(); this.log.verbose(`Processed all ${txs.length} txs for block ${block.number}`, { blockNumber: block.number, - blockHash: block.hash().toString(), + blockHash: (await block.hash()).toString(), uuid: this.uuid, }); @@ -210,9 +210,12 @@ export class EpochProvingJob implements Traceable { return this.l2BlockSource.getBlockHeader(blockNumber); } - private getTxs(block: L2Block): Tx[] { + private async getTxs(block: L2Block): Promise { const txHashes = block.body.txEffects.map(tx => tx.txHash.toBigInt()); - return this.txs.filter(tx => txHashes.includes(tx.getTxHash().toBigInt())); + const txsAndHashes = await Promise.all(this.txs.map(async tx => ({ tx, hash: await tx.getTxHash() }))); + return txsAndHashes + .filter(txAndHash => txHashes.includes(txAndHash.hash.toBigInt())) + .map(txAndHash => txAndHash.tx); } private getL1ToL2Messages(block: L2Block) { diff --git a/yarn-project/prover-node/src/prover-node.test.ts b/yarn-project/prover-node/src/prover-node.test.ts index 1d210665a49..fd30c9ac9e5 100644 --- a/yarn-project/prover-node/src/prover-node.test.ts +++ b/yarn-project/prover-node/src/prover-node.test.ts @@ -151,7 +151,9 @@ describe('prover-node', () => { l2BlockSource.getL1Constants.mockResolvedValue(EmptyL1RollupConstants); // Coordination plays along and returns a tx whenever requested - mockCoordination.getTxByHash.mockImplementation(hash => Promise.resolve(mock({ getTxHash: () => hash }))); + mockCoordination.getTxByHash.mockImplementation(hash => + Promise.resolve(mock({ getTxHash: () => Promise.resolve(hash) })), + ); // A sample claim claim = { epochToProve: 10n, bondProvider: address } as EpochProofClaim; @@ -399,7 +401,7 @@ describe('prover-node', () => { coordination = p2pClient; // But still mock getTxByHash - const mockGetTxByHash = (hash: TxHash) => Promise.resolve(mock({ getTxHash: () => hash })); + const mockGetTxByHash = (hash: TxHash) => Promise.resolve(mock({ getTxHash: () => Promise.resolve(hash) })); jest.spyOn(p2pClient, 'getTxByHash').mockImplementation(mockGetTxByHash); jest.spyOn(otherP2PClient, 'getTxByHash').mockImplementation(mockGetTxByHash); diff --git a/yarn-project/pxe/src/bin/index.ts b/yarn-project/pxe/src/bin/index.ts index 66f451dae92..22142b06e55 100644 --- a/yarn-project/pxe/src/bin/index.ts +++ b/yarn-project/pxe/src/bin/index.ts @@ -1,6 +1,5 @@ #!/usr/bin/env -S node --no-warnings import { createAztecNodeClient } from '@aztec/circuit-types'; -import { init } from '@aztec/foundation/crypto'; import { createLogger } from '@aztec/foundation/log'; import { getPXEServiceConfig } from '../config/index.js'; @@ -17,8 +16,6 @@ const logger = createLogger('pxe:service'); async function main() { logger.info(`Setting up PXE...`); - await init(); - const pxeConfig = getPXEServiceConfig(); const nodeRpcClient = createAztecNodeClient(AZTEC_NODE_URL); const pxeService = await createPXEService(nodeRpcClient, pxeConfig); diff --git a/yarn-project/pxe/src/contract_data_oracle/index.ts b/yarn-project/pxe/src/contract_data_oracle/index.ts index 981366f3602..86963078092 100644 --- a/yarn-project/pxe/src/contract_data_oracle/index.ts +++ b/yarn-project/pxe/src/contract_data_oracle/index.ts @@ -97,7 +97,7 @@ export class ContractDataOracle { selector: FunctionSelector, ): Promise { const tree = await this.getTreeForAddress(contractAddress); - const artifact = tree.getFunctionArtifact(selector); + const artifact = await tree.getFunctionArtifact(selector); return getFunctionDebugMetadata(tree.getArtifact(), artifact); } @@ -138,7 +138,7 @@ export class ContractDataOracle { public async getDebugFunctionName(contractAddress: AztecAddress, selector: FunctionSelector) { const tree = await this.getTreeForAddress(contractAddress); const { name: contractName } = tree.getArtifact(); - const { name: functionName } = tree.getFunctionArtifact(selector); + const { name: functionName } = await tree.getFunctionArtifact(selector); return `${contractName}:${functionName}`; } @@ -158,7 +158,7 @@ export class ContractDataOracle { if (!artifact) { throw new ContractClassNotFoundError(classId.toString()); } - const tree = new PrivateFunctionsTree(artifact); + const tree = await PrivateFunctionsTree.create(artifact); this.contractClasses.set(classId.toString(), tree); } return this.contractClasses.get(classId.toString())!; diff --git a/yarn-project/pxe/src/contract_data_oracle/private_functions_tree.ts b/yarn-project/pxe/src/contract_data_oracle/private_functions_tree.ts index dabd6cc0b9e..e22e6def66e 100644 --- a/yarn-project/pxe/src/contract_data_oracle/private_functions_tree.ts +++ b/yarn-project/pxe/src/contract_data_oracle/private_functions_tree.ts @@ -7,7 +7,7 @@ import { getContractClassFromArtifact, } from '@aztec/circuits.js'; import { type MerkleTree } from '@aztec/circuits.js/merkle'; -import { type ContractArtifact, type FunctionSelector } from '@aztec/foundation/abi'; +import { type ContractArtifact, FunctionSelector } from '@aztec/foundation/abi'; import { Fr } from '@aztec/foundation/fields'; import { assertLength } from '@aztec/foundation/serialize'; @@ -19,10 +19,12 @@ import { assertLength } from '@aztec/foundation/serialize'; */ export class PrivateFunctionsTree { private tree?: MerkleTree; - private contractClass: ContractClassWithId; - constructor(private readonly artifact: ContractArtifact) { - this.contractClass = getContractClassFromArtifact(artifact); + private constructor(private readonly artifact: ContractArtifact, private contractClass: ContractClassWithId) {} + + static async create(artifact: ContractArtifact) { + const contractClass = await getContractClassFromArtifact(artifact); + return new PrivateFunctionsTree(artifact, contractClass); } /** @@ -33,8 +35,14 @@ export class PrivateFunctionsTree { * @param selector - The function selector. * @returns The artifact object containing relevant information about the targeted function. */ - public getFunctionArtifact(selector: FunctionSelector) { - const artifact = this.artifact.functions.find(f => selector.equals(f.name, f.parameters)); + public async getFunctionArtifact(selector: FunctionSelector) { + const functionsAndSelectors = await Promise.all( + this.artifact.functions.map(async f => ({ + f, + selector: await FunctionSelector.fromNameAndParameters(f.name, f.parameters), + })), + ); + const artifact = functionsAndSelectors.find(f => selector.equals(f.selector))?.f; if (!artifact) { throw new Error( `Unknown function. Selector ${selector.toString()} not found in the artifact ${ @@ -53,8 +61,9 @@ export class PrivateFunctionsTree { * @param selector - The selector of a function to get bytecode for. * @returns The bytecode of the function as a string. */ - public getBytecode(selector: FunctionSelector) { - return this.getFunctionArtifact(selector).bytecode; + public async getBytecode(selector: FunctionSelector) { + const artifact = await this.getFunctionArtifact(selector); + return artifact.bytecode; } /** @@ -94,7 +103,7 @@ export class PrivateFunctionsTree { * @param selector - The function selector. * @returns A MembershipWitness instance representing the position and authentication path of the function in the function tree. */ - public getFunctionMembershipWitness( + public async getFunctionMembershipWitness( selector: FunctionSelector, ): Promise> { const fn = this.getContractClass().privateFunctions.find(f => f.selector.equals(selector)); @@ -102,22 +111,21 @@ export class PrivateFunctionsTree { throw new Error(`Private function with selector ${selector.toString()} not found in contract class.`); } - const leaf = computePrivateFunctionLeaf(fn); - const index = this.getTree().getIndex(leaf); - const path = this.getTree().getSiblingPath(index); - return Promise.resolve( - new MembershipWitness( - FUNCTION_TREE_HEIGHT, - BigInt(index), - assertLength(path.map(Fr.fromBuffer), FUNCTION_TREE_HEIGHT), - ), + const leaf = await computePrivateFunctionLeaf(fn); + const tree = await this.getTree(); + const index = tree.getIndex(leaf); + const path = tree.getSiblingPath(index); + return new MembershipWitness( + FUNCTION_TREE_HEIGHT, + BigInt(index), + assertLength(path.map(Fr.fromBuffer), FUNCTION_TREE_HEIGHT), ); } - private getTree() { + private async getTree() { if (!this.tree) { const fns = this.getContractClass().privateFunctions; - this.tree = computePrivateFunctionsTree(fns); + this.tree = await computePrivateFunctionsTree(fns); } return this.tree; } diff --git a/yarn-project/pxe/src/database/kv_pxe_database.ts b/yarn-project/pxe/src/database/kv_pxe_database.ts index 269b673ad77..7966fec8b53 100644 --- a/yarn-project/pxe/src/database/kv_pxe_database.ts +++ b/yarn-project/pxe/src/database/kv_pxe_database.ts @@ -135,14 +135,17 @@ export class KVPxeDatabase implements PxeDatabase { } public async addContractArtifact(id: Fr, contract: ContractArtifact): Promise { - const privateSelectors = contract.functions - .filter(functionArtifact => functionArtifact.functionType === FunctionType.PRIVATE) - .map(privateFunctionArtifact => - FunctionSelector.fromNameAndParameters( - privateFunctionArtifact.name, - privateFunctionArtifact.parameters, + const privateFunctions = contract.functions.filter( + functionArtifact => functionArtifact.functionType === FunctionType.PRIVATE, + ); + + const privateSelectors = await Promise.all( + privateFunctions.map(async privateFunctionArtifact => + ( + await FunctionSelector.fromNameAndParameters(privateFunctionArtifact.name, privateFunctionArtifact.parameters) ).toString(), - ); + ), + ); if (privateSelectors.length !== new Set(privateSelectors).size) { throw new Error('Repeated function selectors of private functions'); @@ -537,7 +540,7 @@ export class KVPxeDatabase implements PxeDatabase { } const value = await this.#completeAddresses.atAsync(index); - return value ? CompleteAddress.fromBuffer(value) : undefined; + return value ? await CompleteAddress.fromBuffer(value) : undefined; } getCompleteAddress(account: AztecAddress): Promise { diff --git a/yarn-project/pxe/src/kernel_oracle/index.ts b/yarn-project/pxe/src/kernel_oracle/index.ts index 29364ebad86..6e8e67de8f1 100644 --- a/yarn-project/pxe/src/kernel_oracle/index.ts +++ b/yarn-project/pxe/src/kernel_oracle/index.ts @@ -37,7 +37,7 @@ export class KernelOracle implements ProvingDataOracle { public async getContractAddressPreimage(address: AztecAddress) { const instance = await this.contractDataOracle.getContractInstance(address); return { - saltedInitializationHash: computeSaltedInitializationHash(instance), + saltedInitializationHash: await computeSaltedInitializationHash(instance), ...instance, }; } @@ -51,9 +51,9 @@ export class KernelOracle implements ProvingDataOracle { return await this.contractDataOracle.getFunctionMembershipWitness(contractAddress, selector); } - public getVkMembershipWitness(vk: VerificationKeyAsFields) { - const leafIndex = getVKIndex(vk); - return Promise.resolve(new MembershipWitness(VK_TREE_HEIGHT, BigInt(leafIndex), getVKSiblingPath(leafIndex))); + public async getVkMembershipWitness(vk: VerificationKeyAsFields) { + const leafIndex = await getVKIndex(vk); + return new MembershipWitness(VK_TREE_HEIGHT, BigInt(leafIndex), await getVKSiblingPath(leafIndex)); } async getNoteHashMembershipWitness(leafIndex: bigint): Promise> { diff --git a/yarn-project/pxe/src/kernel_prover/kernel_prover.ts b/yarn-project/pxe/src/kernel_prover/kernel_prover.ts index 271cc184e63..b4aad8fc7ad 100644 --- a/yarn-project/pxe/src/kernel_prover/kernel_prover.ts +++ b/yarn-project/pxe/src/kernel_prover/kernel_prover.ts @@ -214,7 +214,7 @@ export class KernelProver { if (firstIteration) { const proofInput = new PrivateKernelInitCircuitPrivateInputs( txRequest, - getVKTreeRoot(), + await getVKTreeRoot(), protocolContractTreeRoot, privateCallData, isPrivateOnlyTx, @@ -339,8 +339,8 @@ export class KernelProver { private async createPrivateCallData({ publicInputs, vk: vkAsBuffer }: PrivateCallExecutionResult) { const { contractAddress, functionSelector } = publicInputs.callContext; - const vkAsFields = vkAsFieldsMegaHonk(vkAsBuffer); - const vk = new VerificationKeyAsFields(vkAsFields, hashVK(vkAsFields)); + const vkAsFields = await vkAsFieldsMegaHonk(vkAsBuffer); + const vk = new VerificationKeyAsFields(vkAsFields, await hashVK(vkAsFields)); const functionLeafMembershipWitness = await this.oracle.getFunctionMembershipWitness( contractAddress, @@ -357,7 +357,7 @@ export class KernelProver { const acirHash = Fr.fromBuffer(Buffer.alloc(32, 0)); const protocolContractSiblingPath = isProtocolContract(contractAddress) - ? getProtocolContractSiblingPath(contractAddress) + ? await getProtocolContractSiblingPath(contractAddress) : makeTuple(PROTOCOL_CONTRACT_TREE_HEIGHT, Fr.zero); return PrivateCallData.from({ diff --git a/yarn-project/pxe/src/pxe_service/error_enriching.ts b/yarn-project/pxe/src/pxe_service/error_enriching.ts index 928c4b14ca4..bb2d4f90dc4 100644 --- a/yarn-project/pxe/src/pxe_service/error_enriching.ts +++ b/yarn-project/pxe/src/pxe_service/error_enriching.ts @@ -32,12 +32,12 @@ export async function enrichSimulationError(err: SimulationError, db: PxeDatabas const contract = await db.getContract(parsedContractAddress); if (contract) { err.enrichWithContractName(parsedContractAddress, contract.name); - fnNames.forEach(fnName => { + for (const fnName of fnNames) { const functionArtifact = contract.functions.find(f => fnName === f.name); if (functionArtifact) { err.enrichWithFunctionName( parsedContractAddress, - FunctionSelector.fromNameAndParameters(functionArtifact), + await FunctionSelector.fromNameAndParameters(functionArtifact), functionArtifact.name, ); } else { @@ -45,7 +45,7 @@ export async function enrichSimulationError(err: SimulationError, db: PxeDatabas `Could not find function artifact in contract ${contract.name} for function '${fnName}' when enriching error callstack`, ); } - }); + } } else { logger.warn( `Could not find contract in database for address: ${parsedContractAddress} when enriching error callstack`, diff --git a/yarn-project/pxe/src/pxe_service/pxe_service.ts b/yarn-project/pxe/src/pxe_service/pxe_service.ts index 7be423a1fc4..efe576a375d 100644 --- a/yarn-project/pxe/src/pxe_service/pxe_service.ts +++ b/yarn-project/pxe/src/pxe_service/pxe_service.ts @@ -217,7 +217,7 @@ export class PXEService implements PXE { } public async registerContractClass(artifact: ContractArtifact): Promise { - const contractClassId = computeContractClassId(getContractClassFromArtifact(artifact)); + const contractClassId = await computeContractClassId(await getContractClassFromArtifact(artifact)); await this.db.addContractArtifact(contractClassId, artifact); this.log.info(`Added contract class ${artifact.name} with id ${contractClassId}`); } @@ -228,8 +228,8 @@ export class PXEService implements PXE { if (artifact) { // If the user provides an artifact, validate it against the expected class id and register it - const contractClass = getContractClassFromArtifact(artifact); - const contractClassId = computeContractClassId(contractClass); + const contractClass = await getContractClassFromArtifact(artifact); + const contractClassId = await computeContractClassId(contractClass); if (!contractClassId.equals(instance.contractClassId)) { throw new Error( `Artifact does not match expected class id (computed ${contractClassId} but instance refers to ${instance.contractClassId})`, @@ -341,7 +341,7 @@ export class PXEService implements PXE { throw new Error('Note does not exist.'); } - const siloedNullifier = siloNullifier(note.contractAddress, innerNullifier!); + const siloedNullifier = await siloNullifier(note.contractAddress, innerNullifier!); const [nullifierIndex] = await this.node.findLeavesIndexes('latest', MerkleTreeId.NULLIFIER_TREE, [ siloedNullifier, ]); @@ -434,7 +434,7 @@ export class PXEService implements PXE { break; } - const nonce = computeNoteHashNonce(firstNullifier, i); + const nonce = await computeNoteHashNonce(firstNullifier, i); const { uniqueNoteHash } = await this.simulator.computeNoteHashAndOptionallyANullifier( note.contractAddress, nonce, @@ -526,8 +526,9 @@ export class PXEService implements PXE { } } - this.log.info(`Simulation completed for ${simulatedTx.getTxHash()} in ${timer.ms()}ms`, { - txHash: simulatedTx.getTxHash(), + const txHash = await simulatedTx.getTxHash(); + this.log.info(`Simulation completed for ${txHash.toString()} in ${timer.ms()}ms`, { + txHash, ...txInfo, ...(profileResult ? { gateCounts: profileResult.gateCounts } : {}), ...(publicOutput @@ -558,7 +559,7 @@ export class PXEService implements PXE { } public async sendTx(tx: Tx): Promise { - const txHash = tx.getTxHash(); + const txHash = await tx.getTxHash(); if (await this.node.getTxEffect(txHash)) { throw new Error(`A settled tx with equal hash ${txHash.toString()} exists.`); } @@ -645,7 +646,7 @@ export class PXEService implements PXE { return { name: functionDao.name, args: encodeArguments(functionDao, args), - selector: FunctionSelector.fromNameAndParameters(functionDao.name, functionDao.parameters), + selector: await FunctionSelector.fromNameAndParameters(functionDao.name, functionDao.parameters), type: functionDao.functionType, to, isStatic: functionDao.isStatic, @@ -833,7 +834,7 @@ export class PXEService implements PXE { } public async isContractInitialized(address: AztecAddress): Promise { - const initNullifier = siloNullifier(address, address.toField()); + const initNullifier = await siloNullifier(address, address.toField()); return !!(await this.node.getNullifierMembershipWitness('latest', initNullifier)); } @@ -866,7 +867,7 @@ export class PXEService implements PXE { throw new Error('No registered account'); } - const preaddress = registeredAccount.getPreaddress(); + const preaddress = await registeredAccount.getPreaddress(); secretKey = await computeAddressSecret(preaddress, secretKey); } diff --git a/yarn-project/pxe/src/pxe_service/test/pxe_service.test.ts b/yarn-project/pxe/src/pxe_service/test/pxe_service.test.ts index 8c79681aab9..7284ce5c819 100644 --- a/yarn-project/pxe/src/pxe_service/test/pxe_service.test.ts +++ b/yarn-project/pxe/src/pxe_service/test/pxe_service.test.ts @@ -92,7 +92,7 @@ describe('PXEService', () => { it('throws when submitting a tx with a nullifier of already settled tx', async () => { const settledTx = await TxEffect.random(); - const duplicateTx = mockTx(); + const duplicateTx = await mockTx(); node.getTxEffect.mockResolvedValue(randomInBlock(settledTx)); diff --git a/yarn-project/pxe/src/pxe_service/test/pxe_test_suite.ts b/yarn-project/pxe/src/pxe_service/test/pxe_test_suite.ts index 2995f5569a2..c478b7747e0 100644 --- a/yarn-project/pxe/src/pxe_service/test/pxe_test_suite.ts +++ b/yarn-project/pxe/src/pxe_service/test/pxe_test_suite.ts @@ -47,7 +47,7 @@ export const pxeTestSuite = (testName: string, pxeSetup: () => Promise) => it('registers a class and adds a contract for it', async () => { const artifact = randomContractArtifact(); - const contractClass = getContractClassFromArtifact(artifact); + const contractClass = await getContractClassFromArtifact(artifact); const contractClassId = contractClass.id; const instance = await randomContractInstanceWithAddress({ contractClassId }); @@ -62,7 +62,7 @@ export const pxeTestSuite = (testName: string, pxeSetup: () => Promise) => it('refuses to register a class with a mismatched address', async () => { const artifact = randomContractArtifact(); - const contractClass = getContractClassFromArtifact(artifact); + const contractClass = await getContractClassFromArtifact(artifact); const contractClassId = contractClass.id; const instance = await randomContractInstanceWithAddress({ contractClassId }); await expect( diff --git a/yarn-project/pxe/src/simulator_oracle/index.ts b/yarn-project/pxe/src/simulator_oracle/index.ts index 1317c88eddf..2220f41fdee 100644 --- a/yarn-project/pxe/src/simulator_oracle/index.ts +++ b/yarn-project/pxe/src/simulator_oracle/index.ts @@ -39,6 +39,7 @@ import { encodeArguments, getFunctionArtifact, } from '@aztec/foundation/abi'; +import { timesParallel } from '@aztec/foundation/collection'; import { poseidon2Hash } from '@aztec/foundation/crypto'; import { createLogger } from '@aztec/foundation/log'; import { type KeyStore } from '@aztec/key-store'; @@ -393,7 +394,7 @@ export class SimulatorOracle implements DBOracle { let [numConsecutiveEmptyLogs, currentIndex] = [0, oldIndex]; do { // We compute the tags for the current window of indexes - const currentTags = [...new Array(WINDOW_SIZE)].map((_, i) => { + const currentTags = await timesParallel(WINDOW_SIZE, i => { const indexedAppTaggingSecret = new IndexedTaggingSecret(appTaggingSecret, currentIndex + i); return indexedAppTaggingSecret.computeSiloedTag(recipient, contractAddress); }); @@ -489,8 +490,8 @@ export class SimulatorOracle implements DBOracle { while (secretsAndWindows.length > 0) { const secretsForTheWholeWindow = getIndexedTaggingSecretsForTheWindow(secretsAndWindows); - const tagsForTheWholeWindow = secretsForTheWholeWindow.map(secret => - secret.computeSiloedTag(recipient, contractAddress), + const tagsForTheWholeWindow = await Promise.all( + secretsForTheWholeWindow.map(secret => secret.computeSiloedTag(recipient, contractAddress)), ); // We store the new largest indexes we find in the iteration in the following map to later on construct @@ -607,7 +608,7 @@ export class SimulatorOracle implements DBOracle { const ivskM = await this.keyStore.getMasterSecretKey( recipientCompleteAddress.publicKeys.masterIncomingViewingPublicKey, ); - const addressSecret = await computeAddressSecret(recipientCompleteAddress.getPreaddress(), ivskM); + const addressSecret = await computeAddressSecret(await recipientCompleteAddress.getPreaddress(), ivskM); // Since we could have notes with the same index for different txs, we need // to keep track of them scoping by txHash @@ -753,8 +754,8 @@ export class SimulatorOracle implements DBOracle { // Siloed and unique hashes are computed by us instead of relying on values sent by the contract to make sure // we're not e.g. storing notes that belong to some other contract, which would constitute a security breach. - const uniqueNoteHash = computeUniqueNoteHash(nonce, siloNoteHash(contractAddress, noteHash)); - const siloedNullifier = siloNullifier(contractAddress, nullifier); + const uniqueNoteHash = await computeUniqueNoteHash(nonce, await siloNoteHash(contractAddress, noteHash)); + const siloedNullifier = await siloNullifier(contractAddress, nullifier); // We store notes by their index in the global note hash tree, which has the convenient side effect of validating // note existence in said tree. Note that while this is technically a historical query, we perform it at the latest @@ -808,7 +809,7 @@ export class SimulatorOracle implements DBOracle { const execRequest: FunctionCall = { name: artifact.name, to: contractAddress, - selector: FunctionSelector.fromNameAndParameters(artifact), + selector: await FunctionSelector.fromNameAndParameters(artifact), type: FunctionType.UNCONSTRAINED, isStatic: artifact.isStatic, args: encodeArguments(artifact, [ diff --git a/yarn-project/pxe/src/simulator_oracle/simulator_oracle.test.ts b/yarn-project/pxe/src/simulator_oracle/simulator_oracle.test.ts index 0c7786f205e..3363fb56098 100644 --- a/yarn-project/pxe/src/simulator_oracle/simulator_oracle.test.ts +++ b/yarn-project/pxe/src/simulator_oracle/simulator_oracle.test.ts @@ -115,8 +115,8 @@ async function computeSiloedTagForIndex( index: number, ) { const secretPoint = await computeTaggingSecretPoint(sender.completeAddress, sender.ivsk, recipient); - const appSecret = poseidon2Hash([secretPoint.x, secretPoint.y, contractAddress]); - const tag = poseidon2Hash([appSecret, recipient, index]); + const appSecret = await poseidon2Hash([secretPoint.x, secretPoint.y, contractAddress]); + const tag = await poseidon2Hash([appSecret, recipient, index]); return poseidon2Hash([contractAddress, tag]); } @@ -609,7 +609,7 @@ describe('Simulator oracle', () => { (request.blockNumber - 1) * NUM_NOTE_HASHES_PER_BLOCK + request.txIndex * MAX_NOTE_HASHES_PER_TX; const taggedLog = new TxScopedL2Log(txHash, dataStartIndex, blockNumber, false, await request.encrypt()); const note = request.snippetOfNoteDao.note; - const noteHash = pedersenHash(note.items); + const noteHash = await pedersenHash(note.items); txEffectsMap[txHash.toString()].noteHashes[request.noteHashIndex] = noteHash; taggedLogs.push(taggedLog); } @@ -685,8 +685,8 @@ describe('Simulator oracle', () => { let requestedNullifier; aztecNode.findNullifiersIndexesWithBlock.mockImplementationOnce(async (_blockNumber, nullifiers) => { const block = await L2Block.random(2); - requestedNullifier = wrapInBlock(nullifiers[0], block); - return [wrapInBlock(1n, await L2Block.random(2)), undefined, undefined]; + requestedNullifier = await wrapInBlock(nullifiers[0], block); + return [await wrapInBlock(1n, await L2Block.random(2)), undefined, undefined]; }); await simulatorOracle.removeNullifiedNotes(contractAddress); diff --git a/yarn-project/sequencer-client/src/client/sequencer-client.ts b/yarn-project/sequencer-client/src/client/sequencer-client.ts index 211b8f8f9b0..21d32328cae 100644 --- a/yarn-project/sequencer-client/src/client/sequencer-client.ts +++ b/yarn-project/sequencer-client/src/client/sequencer-client.ts @@ -117,7 +117,7 @@ export class SequencerClient { * @param config - New parameters. */ public updateSequencerConfig(config: SequencerConfig) { - this.sequencer.updateConfig(config); + return this.sequencer.updateConfig(config); } /** diff --git a/yarn-project/sequencer-client/src/publisher/l1-publisher.test.ts b/yarn-project/sequencer-client/src/publisher/l1-publisher.test.ts index 67d0edc1eb1..eab6b801864 100644 --- a/yarn-project/sequencer-client/src/publisher/l1-publisher.test.ts +++ b/yarn-project/sequencer-client/src/publisher/l1-publisher.test.ts @@ -109,7 +109,7 @@ describe('L1Publisher', () => { header = l2Block.header.toBuffer(); archive = l2Block.archive.root.toBuffer(); - blockHash = l2Block.header.hash().toBuffer(); + blockHash = (await l2Block.header.hash()).toBuffer(); body = l2Block.body.toBuffer(); proposeTxHash = `0x${Buffer.from('txHashPropose').toString('hex')}`; // random tx hash @@ -206,7 +206,7 @@ describe('L1Publisher', () => { const kzg = Blob.getViemKzgInstance(); - const expectedBlobs = Blob.getBlobs(l2Block.body.toBlobFields()); + const expectedBlobs = await Blob.getBlobs(l2Block.body.toBlobFields()); // Check the blobs were forwarded to the blob sink service const sendToBlobSinkSpy = jest.spyOn(publisher as any, 'sendBlobsToBlobSink'); diff --git a/yarn-project/sequencer-client/src/publisher/l1-publisher.ts b/yarn-project/sequencer-client/src/publisher/l1-publisher.ts index 947678fe291..7de9e408e1a 100644 --- a/yarn-project/sequencer-client/src/publisher/l1-publisher.ts +++ b/yarn-project/sequencer-client/src/publisher/l1-publisher.ts @@ -581,18 +581,18 @@ export class L1Publisher { const ctx = { blockNumber: block.number, slotNumber: block.header.globalVariables.slotNumber.toBigInt(), - blockHash: block.hash().toString(), + blockHash: (await block.hash()).toString(), }; const consensusPayload = new ConsensusPayload(block.header, block.archive.root, txHashes ?? []); - const digest = getHashedSignaturePayload(consensusPayload, SignatureDomainSeparator.blockAttestation); + const digest = await getHashedSignaturePayload(consensusPayload, SignatureDomainSeparator.blockAttestation); - const blobs = Blob.getBlobs(block.body.toBlobFields()); + const blobs = await Blob.getBlobs(block.body.toBlobFields()); const proposeTxArgs = { header: block.header.toBuffer(), archive: block.archive.root.toBuffer(), - blockHash: block.header.hash().toBuffer(), + blockHash: (await block.header.hash()).toBuffer(), body: block.body.toBuffer(), blobs, attestations, diff --git a/yarn-project/sequencer-client/src/sequencer/allowed.ts b/yarn-project/sequencer-client/src/sequencer/allowed.ts index c78c900a8ea..a343b1d6674 100644 --- a/yarn-project/sequencer-client/src/sequencer/allowed.ts +++ b/yarn-project/sequencer-client/src/sequencer/allowed.ts @@ -6,7 +6,7 @@ import { ProtocolContractAddress } from '@aztec/protocol-contracts'; let defaultAllowedSetupFunctions: AllowedElement[] | undefined = undefined; -export function getDefaultAllowedSetupFunctions(): AllowedElement[] { +export async function getDefaultAllowedSetupFunctions(): Promise { if (defaultAllowedSetupFunctions === undefined) { defaultAllowedSetupFunctions = [ // needed for authwit support @@ -21,12 +21,12 @@ export function getDefaultAllowedSetupFunctions(): AllowedElement[] { }, // needed for private transfers via FPC { - classId: getContractClassFromArtifact(TokenContractArtifact).id, + classId: (await getContractClassFromArtifact(TokenContractArtifact)).id, // We can't restrict the selector because public functions get routed via dispatch. // selector: FunctionSelector.fromSignature('_increase_public_balance((Field),(Field,Field))'), }, { - classId: getContractClassFromArtifact(FPCContract.artifact).id, + classId: (await getContractClassFromArtifact(FPCContract.artifact)).id, // We can't restrict the selector because public functions get routed via dispatch. // selector: FunctionSelector.fromSignature('prepare_fee((Field),Field,(Field),Field)'), }, diff --git a/yarn-project/sequencer-client/src/sequencer/sequencer.test.ts b/yarn-project/sequencer-client/src/sequencer/sequencer.test.ts index 47105d7f75a..571d3e8c06e 100644 --- a/yarn-project/sequencer-client/src/sequencer/sequencer.test.ts +++ b/yarn-project/sequencer-client/src/sequencer/sequencer.test.ts @@ -33,8 +33,9 @@ import { import { makeAppendOnlyTreeSnapshot } from '@aztec/circuits.js/testing'; import { DefaultL1ContractsConfig } from '@aztec/ethereum'; import { Buffer32 } from '@aztec/foundation/buffer'; -import { times } from '@aztec/foundation/collection'; +import { times, timesParallel } from '@aztec/foundation/collection'; import { Signature } from '@aztec/foundation/eth-signature'; +import { toArray } from '@aztec/foundation/iterable'; import { type Logger, createLogger } from '@aztec/foundation/log'; import { TestDateProvider, type Timer } from '@aztec/foundation/timer'; import { type P2P, P2PClientState } from '@aztec/p2p'; @@ -110,9 +111,15 @@ describe('sequencer', () => { return await Promise.all(txs.map(tx => makeProcessedTxFromPrivateOnlyTx(tx, Fr.ZERO, undefined, globalVariables))); }; + const mockTxIterator = async function* (txs: Promise): AsyncIterableIterator { + for (const tx of await txs) { + yield tx; + } + }; + const mockPendingTxs = (txs: Tx[]) => { - p2p.getPendingTxCount.mockReturnValue(txs.length); - p2p.iteratePendingTxs.mockReturnValue(txs); + p2p.getPendingTxCount.mockReturnValue(Promise.resolve(txs.length)); + p2p.iteratePendingTxs.mockReturnValue(mockTxIterator(Promise.resolve(txs))); }; const makeBlock = async (txs: Tx[]) => { @@ -125,8 +132,8 @@ describe('sequencer', () => { return block; }; - const makeTx = (seed?: number) => { - const tx = mockTxForRollup(seed); + const makeTx = async (seed?: number) => { + const tx = await mockTxForRollup(seed); tx.data.constants.txContext.chainId = chainId; return tx; }; @@ -197,7 +204,7 @@ describe('sequencer', () => { publicProcessor = mock(); publicProcessor.process.mockImplementation(async txsIter => { - const txs = Array.from(txsIter); + const txs = await toArray(txsIter); const processed = await processTxs(txs); logger.verbose(`Processed ${txs.length} txs`, { txHashes: txs.map(tx => tx.getTxHash()) }); return [processed, [], []]; @@ -235,7 +242,7 @@ describe('sequencer', () => { const l1GenesisTime = BigInt(Math.floor(Date.now() / 1000)); const l1Constants = { l1GenesisTime, slotDuration, ethereumSlotDuration }; const slasherClient = mock(); - + const config = { enforceTimeTable: true, maxTxsPerBlock: 4 }; sequencer = new TestSubject( publisher, // TODO(md): add the relevant methods to the validator client that will prevent it stalling when waiting for attestations @@ -251,17 +258,16 @@ describe('sequencer', () => { contractSource, l1Constants, new TestDateProvider(), - { enforceTimeTable: true, maxTxsPerBlock: 4 }, ); + await sequencer.updateConfig(config); }); it('builds a block out of a single tx', async () => { - const tx = makeTx(); - const txHash = tx.getTxHash(); + const tx = await makeTx(); + const txHash = await tx.getTxHash(); block = await makeBlock([tx]); mockPendingTxs([tx]); - await sequencer.doRealWork(); expect(blockBuilder.startNewBlock).toHaveBeenCalledWith( @@ -274,7 +280,7 @@ describe('sequencer', () => { }); it('builds a block for proposal setting limits', async () => { - const txs = times(5, i => makeTx(i * 0x10000)); + const txs = await timesParallel(5, i => makeTx(i * 0x10000)); await sequencer.buildBlock(txs, globalVariables, { validateOnly: false }); expect(publicProcessor.process).toHaveBeenCalledWith( @@ -290,7 +296,7 @@ describe('sequencer', () => { }); it('builds a block for validation ignoring limits', async () => { - const txs = times(5, i => makeTx(i * 0x10000)); + const txs = await timesParallel(5, i => makeTx(i * 0x10000)); await sequencer.buildBlock(txs, globalVariables, { validateOnly: true }); expect(publicProcessor.process).toHaveBeenCalledWith(txs, { deadline: expect.any(Date) }, expect.anything()); @@ -302,7 +308,7 @@ describe('sequencer', () => { Math.floor(Date.now() / 1000) - slotDuration * 1 - (sequencer.getTimeTable().initialTime + 1), ); - const tx = makeTx(); + const tx = await makeTx(); mockPendingTxs([tx]); block = await makeBlock([tx]); @@ -318,8 +324,8 @@ describe('sequencer', () => { }); it('builds a block when it is their turn', async () => { - const tx = makeTx(); - const txHash = tx.getTxHash(); + const tx = await makeTx(); + const txHash = await tx.getTxHash(); mockPendingTxs([tx]); block = await makeBlock([tx]); @@ -354,10 +360,10 @@ describe('sequencer', () => { }); it('builds a block out of several txs rejecting invalid txs', async () => { - const txs = [makeTx(0x10000), makeTx(0x20000), makeTx(0x30000)]; + const txs = await Promise.all([makeTx(0x10000), makeTx(0x20000), makeTx(0x30000)]); const validTxs = [txs[0], txs[2]]; const invalidTx = txs[1]; - const validTxHashes = validTxs.map(tx => tx.getTxHash()); + const validTxHashes = await Promise.all(validTxs.map(tx => tx.getTxHash())); mockPendingTxs(txs); block = await makeBlock([txs[0], txs[2]]); @@ -375,12 +381,12 @@ describe('sequencer', () => { initialBlockHeader, ); expectPublisherProposeL2Block(validTxHashes); - expect(p2p.deleteTxs).toHaveBeenCalledWith([invalidTx.getTxHash()]); + expect(p2p.deleteTxs).toHaveBeenCalledWith([await invalidTx.getTxHash()]); }); it('builds a block once it reaches the minimum number of transactions', async () => { - const txs = times(8, i => makeTx(i * 0x10000)); - sequencer.updateConfig({ minTxsPerBlock: 4 }); + const txs: Tx[] = await timesParallel(8, i => makeTx(i * 0x10000)); + await sequencer.updateConfig({ minTxsPerBlock: 4 }); // block is not built with 0 txs mockPendingTxs([]); @@ -406,13 +412,13 @@ describe('sequencer', () => { initialBlockHeader, ); - expectPublisherProposeL2Block(neededTxs.map(tx => tx.getTxHash())); + expectPublisherProposeL2Block(await Promise.all(neededTxs.map(tx => tx.getTxHash()))); }); it('builds a block that contains zero real transactions once flushed', async () => { - const txs = times(8, i => makeTx(i * 0x10000)); + const txs = await timesParallel(8, i => makeTx(i * 0x10000)); - sequencer.updateConfig({ minTxsPerBlock: 4 }); + await sequencer.updateConfig({ minTxsPerBlock: 4 }); // block is not built with 0 txs mockPendingTxs([]); @@ -444,9 +450,9 @@ describe('sequencer', () => { }); it('builds a block that contains less than the minimum number of transactions once flushed', async () => { - const txs = times(8, i => makeTx(i * 0x10000)); + const txs = await timesParallel(8, i => makeTx(i * 0x10000)); - sequencer.updateConfig({ minTxsPerBlock: 4 }); + await sequencer.updateConfig({ minTxsPerBlock: 4 }); // block is not built with 0 txs mockPendingTxs([]); @@ -465,7 +471,7 @@ describe('sequencer', () => { const postFlushTxs = txs.slice(0, 3); mockPendingTxs(postFlushTxs); block = await makeBlock(postFlushTxs); - const postFlushTxHashes = postFlushTxs.map(tx => tx.getTxHash()); + const postFlushTxHashes = await Promise.all(postFlushTxs.map(tx => tx.getTxHash())); await sequencer.doRealWork(); expect(blockBuilder.startNewBlock).toHaveBeenCalledTimes(1); @@ -479,7 +485,7 @@ describe('sequencer', () => { }); it('aborts building a block if the chain moves underneath it', async () => { - const tx = makeTx(); + const tx = await makeTx(); mockPendingTxs([tx]); block = await makeBlock([tx]); @@ -492,7 +498,7 @@ describe('sequencer', () => { }); it('does not publish a block if the block proposal failed', async () => { - const tx = makeTx(); + const tx = await makeTx(); mockPendingTxs([tx]); block = await makeBlock([tx]); @@ -547,8 +553,8 @@ describe('sequencer', () => { Promise.resolve(slotNumber / BigInt(epochDuration)), ); - tx = makeTx(); - txHash = tx.getTxHash(); + tx = await makeTx(); + txHash = await tx.getTxHash(); mockPendingTxs([tx]); block = await makeBlock([tx]); @@ -734,7 +740,7 @@ class TestSubject extends Sequencer { } public override buildBlock( - pendingTxs: Iterable, + pendingTxs: Iterable | AsyncIterableIterator, newGlobalVariables: GlobalVariables, opts?: { validateOnly?: boolean | undefined }, ): Promise<{ diff --git a/yarn-project/sequencer-client/src/sequencer/sequencer.ts b/yarn-project/sequencer-client/src/sequencer/sequencer.ts index d1300a73f44..8998440e7a4 100644 --- a/yarn-project/sequencer-client/src/sequencer/sequencer.ts +++ b/yarn-project/sequencer-client/src/sequencer/sequencer.ts @@ -69,7 +69,7 @@ export class Sequencer { private _coinbase = EthAddress.ZERO; private _feeRecipient = AztecAddress.ZERO; private state = SequencerState.STOPPED; - private allowedInSetup: AllowedElement[] = getDefaultAllowedSetupFunctions(); + private allowedInSetup: AllowedElement[] = []; private maxBlockSizeInBytes: number = 1024 * 1024; private maxBlockGas: Gas = new Gas(100e9, 100e9); private metrics: SequencerMetrics; @@ -98,7 +98,6 @@ export class Sequencer { telemetry: TelemetryClient = getTelemetryClient(), protected log = createLogger('sequencer'), ) { - this.updateConfig(config); this.metrics = new SequencerMetrics(telemetry, () => this.state, 'Sequencer'); // Register the block builder with the validator client for re-execution @@ -116,7 +115,7 @@ export class Sequencer { * Updates sequencer config. * @param config - New parameters. */ - public updateConfig(config: SequencerConfig) { + public async updateConfig(config: SequencerConfig) { this.log.info(`Sequencer config set`, omit(pickFromSchema(config, SequencerConfigSchema), 'allowedInSetup')); if (config.transactionPollingIntervalMS !== undefined) { @@ -142,6 +141,8 @@ export class Sequencer { } if (config.allowedInSetup) { this.allowedInSetup = config.allowedInSetup; + } else { + this.allowedInSetup = await getDefaultAllowedSetupFunctions(); } if (config.maxBlockSizeInBytes !== undefined) { this.maxBlockSizeInBytes = config.maxBlockSizeInBytes; @@ -177,12 +178,12 @@ export class Sequencer { /** * Starts the sequencer and moves to IDLE state. */ - public start() { + public async start() { + await this.updateConfig(this.config); this.runningPromise = new RunningPromise(this.work.bind(this), this.log, this.pollingIntervalMs); this.setState(SequencerState.IDLE, 0n, true /** force */); this.runningPromise.start(); this.log.info(`Sequencer started with address ${this.publisher.getSenderAddress().toString()}`); - return Promise.resolve(); } /** @@ -261,7 +262,7 @@ export class Sequencer { void this.publisher.castVote(slot, newGlobalVariables.timestamp.toBigInt(), VoteType.SLASHING); // Check the pool has enough txs to build a block - const pendingTxCount = this.p2pClient.getPendingTxCount(); + const pendingTxCount = await this.p2pClient.getPendingTxCount(); if (pendingTxCount < this.minTxsPerBlock && !this.isFlushing) { this.log.verbose(`Not enough txs to propose block. Got ${pendingTxCount} min ${this.minTxsPerBlock}.`, { slot, @@ -374,13 +375,12 @@ export class Sequencer { * @param opts - Whether to just validate the block as a validator, as opposed to building it as a proposal */ protected async buildBlock( - pendingTxs: Iterable, + pendingTxs: Iterable | AsyncIterableIterator, newGlobalVariables: GlobalVariables, opts: { validateOnly?: boolean } = {}, ) { const blockNumber = newGlobalVariables.blockNumber.toNumber(); const slot = newGlobalVariables.slotNumber.toBigInt(); - this.log.debug(`Requesting L1 to L2 messages from contract for block ${blockNumber}`); const l1ToL2Messages = await this.l1ToL2MessageSource.getL1ToL2Messages(BigInt(blockNumber)); const msgCount = l1ToL2Messages.length; @@ -450,8 +450,9 @@ export class Sequencer { if (!opts.validateOnly && failedTxs.length > 0) { const failedTxData = failedTxs.map(fail => fail.tx); - this.log.verbose(`Dropping failed txs ${Tx.getHashes(failedTxData).join(', ')}`); - await this.p2pClient.deleteTxs(Tx.getHashes(failedTxData)); + const failedTxHashes = await Tx.getHashes(failedTxData); + this.log.verbose(`Dropping failed txs ${failedTxHashes.join(', ')}`); + await this.p2pClient.deleteTxs(failedTxHashes); } if ( @@ -516,7 +517,10 @@ export class Sequencer { @trackSpan('Sequencer.buildBlockAndAttemptToPublish', (_validTxs, proposalHeader) => ({ [Attributes.BLOCK_NUMBER]: proposalHeader.globalVariables.blockNumber.toNumber(), })) - private async buildBlockAndAttemptToPublish(pendingTxs: Iterable, proposalHeader: BlockHeader): Promise { + private async buildBlockAndAttemptToPublish( + pendingTxs: AsyncIterableIterator, + proposalHeader: BlockHeader, + ): Promise { await this.publisher.validateBlockForSubmission(proposalHeader); const newGlobalVariables = proposalHeader.globalVariables; @@ -548,7 +552,7 @@ export class Sequencer { ...block.getStats(), }; - const blockHash = block.hash(); + const blockHash = await block.hash(); const txHashes = block.body.txEffects.map(tx => tx.txHash); this.log.info(`Built block ${block.number} for slot ${slot} with ${numTxs} txs`, { blockHash, diff --git a/yarn-project/sequencer-client/src/sequencer/utils.ts b/yarn-project/sequencer-client/src/sequencer/utils.ts index af90e2f45dd..c4253c4cf0f 100644 --- a/yarn-project/sequencer-client/src/sequencer/utils.ts +++ b/yarn-project/sequencer-client/src/sequencer/utils.ts @@ -49,12 +49,15 @@ export function sequencerStateToNumber(state: SequencerState): number { * * @todo: perform this logic within the memory attestation store instead? */ -export function orderAttestations(attestations: BlockAttestation[], orderAddresses: EthAddress[]): Signature[] { +export async function orderAttestations( + attestations: BlockAttestation[], + orderAddresses: EthAddress[], +): Promise { // Create a map of sender addresses to BlockAttestations const attestationMap = new Map(); for (const attestation of attestations) { - const sender = attestation.getSender(); + const sender = await attestation.getSender(); if (sender) { attestationMap.set(sender.toString(), attestation); } diff --git a/yarn-project/sequencer-client/src/slasher/slasher_client.ts b/yarn-project/sequencer-client/src/slasher/slasher_client.ts index f2022c9c55d..5c1eeb560d3 100644 --- a/yarn-project/sequencer-client/src/slasher/slasher_client.ts +++ b/yarn-project/sequencer-client/src/slasher/slasher_client.ts @@ -315,7 +315,10 @@ export class SlasherClient extends WithTracer { const blockHash = blockNumber == 0 ? '' - : await this.l2BlockSource.getBlockHeader(blockNumber).then(header => header?.hash().toString()); + : await this.l2BlockSource + .getBlockHeader(blockNumber) + .then(header => header?.hash()) + .then(hash => hash?.toString()); return Promise.resolve({ state: this.currentState, syncedToL2Block: { number: blockNumber, hash: blockHash }, @@ -333,7 +336,9 @@ export class SlasherClient extends WithTracer { } const lastBlockNum = blocks[blocks.length - 1].number; - await Promise.all(blocks.map(block => this.synchedBlockHashes.set(block.number, block.hash().toString()))); + await Promise.all( + blocks.map(async block => this.synchedBlockHashes.set(block.number, (await block.hash()).toString())), + ); await this.synchedLatestBlockNumber.set(lastBlockNum); this.log.debug(`Synched to latest block ${lastBlockNum}`); this.startServiceIfSynched(); diff --git a/yarn-project/sequencer-client/src/tx_validator/gas_validator.test.ts b/yarn-project/sequencer-client/src/tx_validator/gas_validator.test.ts index 1386dfbef4f..ae5d1a4ced2 100644 --- a/yarn-project/sequencer-client/src/tx_validator/gas_validator.test.ts +++ b/yarn-project/sequencer-client/src/tx_validator/gas_validator.test.ts @@ -31,11 +31,11 @@ describe('GasTxValidator', () => { enforceFees = false; gasFees = new GasFees(11, 22); - tx = mockTx(1, { numberOfNonRevertiblePublicCallRequests: 2 }); + tx = await mockTx(1, { numberOfNonRevertiblePublicCallRequests: 2 }); tx.data.feePayer = await AztecAddress.random(); tx.data.constants.txContext.gasSettings = GasSettings.default({ maxFeesPerGas: gasFees.clone() }); payer = tx.data.feePayer; - expectedBalanceSlot = poseidon2Hash([FeeJuiceContract.storage.balances.slot, payer]); + expectedBalanceSlot = await poseidon2Hash([FeeJuiceContract.storage.balances.slot, payer]); feeLimit = tx.data.constants.txContext.gasSettings.getFeeLimit().toBigInt(); }); @@ -69,8 +69,8 @@ describe('GasTxValidator', () => { it('allows fee paying txs if fee payer claims enough balance during setup', async () => { mockBalance(feeLimit - 1n); - const selector = FunctionSelector.fromSignature('_increase_public_balance((Field),(Field,Field))'); - patchNonRevertibleFn(tx, 0, { + const selector = await FunctionSelector.fromSignature('_increase_public_balance((Field),(Field,Field))'); + await patchNonRevertibleFn(tx, 0, { address: ProtocolContractAddress.FeeJuice, selector: FunctionSelector.fromField(new Fr(PUBLIC_DISPATCH_SELECTOR)), args: [selector.toField(), payer.toField(), ...new U128(1n).toFields()], @@ -90,8 +90,8 @@ describe('GasTxValidator', () => { it('rejects txs if fee payer claims balance outside setup', async () => { mockBalance(feeLimit - 1n); - patchRevertibleFn(tx, 0, { - selector: FunctionSelector.fromSignature('_increase_public_balance((Field),(Field,Field))'), + await patchRevertibleFn(tx, 0, { + selector: await FunctionSelector.fromSignature('_increase_public_balance((Field),(Field,Field))'), args: [payer.toField(), ...new U128(1n).toFields()], }); await expectInvalid(tx, 'Insufficient fee payer balance'); diff --git a/yarn-project/sequencer-client/src/tx_validator/gas_validator.ts b/yarn-project/sequencer-client/src/tx_validator/gas_validator.ts index b00cb451e07..1b807ebb4e6 100644 --- a/yarn-project/sequencer-client/src/tx_validator/gas_validator.ts +++ b/yarn-project/sequencer-client/src/tx_validator/gas_validator.ts @@ -74,20 +74,21 @@ export class GasTxValidator implements TxValidator { // Read current balance of the feePayer const initialBalance = await this.#publicDataSource.storageRead( this.#feeJuiceAddress, - computeFeePayerBalanceStorageSlot(feePayer), + await computeFeePayerBalanceStorageSlot(feePayer), ); // If there is a claim in this tx that increases the fee payer balance in Fee Juice, add it to balance const setupFns = getExecutionRequestsByPhase(tx, TxExecutionPhase.SETUP); + const increasePublicBalanceSelector = await FunctionSelector.fromSignature( + '_increase_public_balance((Field),(Field,Field))', + ); const claimFunctionCall = setupFns.find( fn => fn.callContext.contractAddress.equals(this.#feeJuiceAddress) && fn.callContext.msgSender.equals(this.#feeJuiceAddress) && fn.args.length > 2 && // Public functions get routed through the dispatch function, whose first argument is the target function selector. - fn.args[0].equals( - FunctionSelector.fromSignature('_increase_public_balance((Field),(Field,Field))').toField(), - ) && + fn.args[0].equals(increasePublicBalanceSelector.toField()) && fn.args[1].equals(feePayer.toField()) && !fn.callContext.isStaticCall, ); diff --git a/yarn-project/sequencer-client/src/tx_validator/phases_validator.test.ts b/yarn-project/sequencer-client/src/tx_validator/phases_validator.test.ts index 53894c3e97c..d5572b9cae2 100644 --- a/yarn-project/sequencer-client/src/tx_validator/phases_validator.test.ts +++ b/yarn-project/sequencer-client/src/tx_validator/phases_validator.test.ts @@ -58,15 +58,15 @@ describe('PhasesTxValidator', () => { }); it('allows setup functions on the contracts allow list', async () => { - const tx = mockTx(1, { numberOfNonRevertiblePublicCallRequests: 1 }); - patchNonRevertibleFn(tx, 0, { address: allowedContract, selector: allowedSetupSelector1 }); + const tx = await mockTx(1, { numberOfNonRevertiblePublicCallRequests: 1 }); + await patchNonRevertibleFn(tx, 0, { address: allowedContract, selector: allowedSetupSelector1 }); await expectValid(tx); }); it('allows setup functions on the contracts class allow list', async () => { - const tx = mockTx(1, { numberOfNonRevertiblePublicCallRequests: 1 }); - const { address } = patchNonRevertibleFn(tx, 0, { selector: allowedSetupSelector1 }); + const tx = await mockTx(1, { numberOfNonRevertiblePublicCallRequests: 1 }); + const { address } = await patchNonRevertibleFn(tx, 0, { selector: allowedSetupSelector1 }); contractDataSource.getContract.mockImplementationOnce(contractAddress => { if (address.equals(contractAddress)) { @@ -82,15 +82,15 @@ describe('PhasesTxValidator', () => { }); it('rejects txs with setup functions not on the allow list', async () => { - const tx = mockTx(1, { numberOfNonRevertiblePublicCallRequests: 2 }); + const tx = await mockTx(1, { numberOfNonRevertiblePublicCallRequests: 2 }); await expectInvalid(tx, 'Setup function not on allow list'); }); it('rejects setup functions not on the contracts class list', async () => { - const tx = mockTx(1, { numberOfNonRevertiblePublicCallRequests: 1 }); + const tx = await mockTx(1, { numberOfNonRevertiblePublicCallRequests: 1 }); // good selector, bad contract class - const { address } = patchNonRevertibleFn(tx, 0, { selector: allowedSetupSelector1 }); + const { address } = await patchNonRevertibleFn(tx, 0, { selector: allowedSetupSelector1 }); contractDataSource.getContract.mockImplementationOnce(contractAddress => { if (address.equals(contractAddress)) { return Promise.resolve({ @@ -105,16 +105,16 @@ describe('PhasesTxValidator', () => { }); it('allows multiple setup functions on the allow list', async () => { - const tx = mockTx(1, { numberOfNonRevertiblePublicCallRequests: 2 }); - patchNonRevertibleFn(tx, 0, { address: allowedContract, selector: allowedSetupSelector1 }); - patchNonRevertibleFn(tx, 1, { address: allowedContract, selector: allowedSetupSelector2 }); + const tx = await mockTx(1, { numberOfNonRevertiblePublicCallRequests: 2 }); + await patchNonRevertibleFn(tx, 0, { address: allowedContract, selector: allowedSetupSelector1 }); + await patchNonRevertibleFn(tx, 1, { address: allowedContract, selector: allowedSetupSelector2 }); await expectValid(tx); }); it('rejects if one setup functions is not on the allow list', async () => { - const tx = mockTx(1, { numberOfNonRevertiblePublicCallRequests: 2 }); - patchNonRevertibleFn(tx, 0, { address: allowedContract, selector: allowedSetupSelector1 }); + const tx = await mockTx(1, { numberOfNonRevertiblePublicCallRequests: 2 }); + await patchNonRevertibleFn(tx, 0, { address: allowedContract, selector: allowedSetupSelector1 }); await expectInvalid(tx, 'Setup function not on allow list'); }); diff --git a/yarn-project/sequencer-client/src/tx_validator/test_utils.ts b/yarn-project/sequencer-client/src/tx_validator/test_utils.ts index dace92c0258..623f667b8bd 100644 --- a/yarn-project/sequencer-client/src/tx_validator/test_utils.ts +++ b/yarn-project/sequencer-client/src/tx_validator/test_utils.ts @@ -6,7 +6,7 @@ export function patchNonRevertibleFn( tx: Tx, index: number, overrides: { address?: AztecAddress; selector: FunctionSelector; args?: Fr[]; msgSender?: AztecAddress }, -): { address: AztecAddress; selector: FunctionSelector } { +): Promise<{ address: AztecAddress; selector: FunctionSelector }> { return patchFn('nonRevertibleAccumulatedData', tx, index, overrides); } @@ -14,16 +14,16 @@ export function patchRevertibleFn( tx: Tx, index: number, overrides: { address?: AztecAddress; selector: FunctionSelector; args?: Fr[]; msgSender?: AztecAddress }, -): { address: AztecAddress; selector: FunctionSelector } { +): Promise<{ address: AztecAddress; selector: FunctionSelector }> { return patchFn('revertibleAccumulatedData', tx, index, overrides); } -function patchFn( +async function patchFn( where: 'revertibleAccumulatedData' | 'nonRevertibleAccumulatedData', tx: Tx, index: number, overrides: { address?: AztecAddress; selector: FunctionSelector; args?: Fr[]; msgSender?: AztecAddress }, -): { address: AztecAddress; selector: FunctionSelector } { +): Promise<{ address: AztecAddress; selector: FunctionSelector }> { const fn = tx.enqueuedPublicFunctionCalls.at(-1 * index - 1)!; fn.callContext.contractAddress = overrides.address ?? fn.callContext.contractAddress; fn.callContext.functionSelector = overrides.selector; @@ -36,7 +36,7 @@ function patchFn( request.msgSender = fn.callContext.msgSender; request.functionSelector = fn.callContext.functionSelector; request.isStaticCall = fn.callContext.isStaticCall; - request.argsHash = computeVarArgsHash(fn.args); + request.argsHash = await computeVarArgsHash(fn.args); tx.data.forPublic![where].publicCallRequests[index] = request; return { diff --git a/yarn-project/simulator/src/avm/avm_simulator.test.ts b/yarn-project/simulator/src/avm/avm_simulator.test.ts index 81c4f4ffa23..90287aa79df 100644 --- a/yarn-project/simulator/src/avm/avm_simulator.test.ts +++ b/yarn-project/simulator/src/avm/avm_simulator.test.ts @@ -209,7 +209,7 @@ describe('AVM simulator: transpiled Noir contracts', () => { it('addition via dispatch', async () => { const calldata: Fr[] = [ - FunctionSelector.fromSignature('add_args_return(Field,Field)').toField(), + (await FunctionSelector.fromSignature('add_args_return(Field,Field)')).toField(), new Fr(1), new Fr(2), ]; @@ -224,14 +224,17 @@ describe('AVM simulator: transpiled Noir contracts', () => { it('get_args_hash via dispatch', async () => { const calldata = [new Fr(8), new Fr(1), new Fr(2), new Fr(3)]; - const dispatchCalldata = [FunctionSelector.fromSignature('get_args_hash(u8,[Field;3])').toField(), ...calldata]; + const dispatchCalldata = [ + (await FunctionSelector.fromSignature('get_args_hash(u8,[Field;3])')).toField(), + ...calldata, + ]; const context = initContext({ env: initExecutionEnvironment({ calldata: dispatchCalldata }) }); const bytecode = getAvmTestContractBytecode('public_dispatch'); const results = await new AvmSimulator(context).executeBytecode(bytecode); expect(results.reverted).toBe(false); - expect(results.output).toEqual([computeVarArgsHash(calldata)]); + expect(results.output).toEqual([await computeVarArgsHash(calldata)]); }); it('modulo and u1', async () => { @@ -334,7 +337,7 @@ describe('AVM simulator: transpiled Noir contracts', () => { expect(results.reverted).toBe(false); // This doesnt include infinites - const expectedResult = pedersenCommit([Buffer.from([100]), Buffer.from([1])], 20).map(f => new Fr(f)); + const expectedResult = (await pedersenCommit([Buffer.from([100]), Buffer.from([1])], 20)).map(f => new Fr(f)); // TODO: Come back to the handling of infinities when we confirm how they're handled in bb const isInf = expectedResult[0] === new Fr(0) && expectedResult[1] === new Fr(0); expectedResult.push(new Fr(isInf)); @@ -430,7 +433,7 @@ describe('AVM simulator: transpiled Noir contracts', () => { ['poseidon2_hash', /*input=*/ randomMemoryFields(10), /*output=*/ poseidon2FromMemoryFields], ['pedersen_hash', /*input=*/ randomMemoryFields(10), /*output=*/ pedersenFromMemoryFields], ['pedersen_hash_with_index', /*input=*/ randomMemoryFields(10), /*output=*/ indexedPedersenFromMemoryFields], - ])('Hashes in noir contracts', (name: string, input: MemoryValue[], output: (msg: any[]) => Fr[]) => { + ])('Hashes in noir contracts', (name: string, input: MemoryValue[], output: (msg: any[]) => Promise) => { it(`Should execute contract function that performs ${name}`, async () => { const calldata = input.map(e => e.toFr()); @@ -439,7 +442,7 @@ describe('AVM simulator: transpiled Noir contracts', () => { const results = await new AvmSimulator(context).executeBytecode(bytecode); expect(results.reverted).toBe(false); - expect(results.output).toEqual(output(input)); + expect(results.output).toEqual(await output(input)); }); }); @@ -526,12 +529,16 @@ describe('AVM simulator: transpiled Noir contracts', () => { const listSlot1 = new Fr(listSlotNumber1); const value0 = new Fr(420); const value1 = new Fr(69); - const siloedNullifier0 = siloNullifier(address, value0); + let siloedNullifier0: Fr; let worldStateDB: WorldStateDB; let trace: PublicSideEffectTraceInterface; let persistableState: AvmPersistableStateManager; + beforeAll(async () => { + siloedNullifier0 = await siloNullifier(address, value0); + }); + beforeEach(() => { worldStateDB = mock(); trace = mock(); @@ -652,9 +659,9 @@ describe('AVM simulator: transpiled Noir contracts', () => { expect(results.output).toEqual([]); expect(trace.traceNewNoteHash).toHaveBeenCalledTimes(1); - const siloedNotehash = siloNoteHash(address, value0); - const nonce = computeNoteHashNonce(context.persistableState.firstNullifier, 0); - const uniqueNoteHash = computeUniqueNoteHash(nonce, siloedNotehash); + const siloedNotehash = await siloNoteHash(address, value0); + const nonce = await computeNoteHashNonce(context.persistableState.firstNullifier, 0); + const uniqueNoteHash = await computeUniqueNoteHash(nonce, siloedNotehash); expect(trace.traceNewNoteHash).toHaveBeenCalledWith(uniqueNoteHash); }); @@ -888,9 +895,9 @@ describe('AVM simulator: transpiled Noir contracts', () => { mockGetContractInstance(worldStateDB, contractInstanceWithAddress); mockGetContractInstance(worldStateDB, contractInstanceWithAddress); mockGetContractInstance(worldStateDB, contractInstanceWithAddress); - mockNullifierExists(worldStateDB, siloAddress(contractInstanceWithAddress.address)); - mockNullifierExists(worldStateDB, siloAddress(contractInstanceWithAddress.address)); - mockNullifierExists(worldStateDB, siloAddress(contractInstanceWithAddress.address)); + mockNullifierExists(worldStateDB, await siloAddress(contractInstanceWithAddress.address)); + mockNullifierExists(worldStateDB, await siloAddress(contractInstanceWithAddress.address)); + mockNullifierExists(worldStateDB, await siloAddress(contractInstanceWithAddress.address)); const bytecode = getAvmTestContractBytecode('test_get_contract_instance'); @@ -923,16 +930,16 @@ describe('AVM simulator: transpiled Noir contracts', () => { const context = createContext(calldata); const callBytecode = getAvmTestContractBytecode('nested_call_to_add'); const nestedBytecode = getAvmTestContractBytecode('public_dispatch'); - mockGetBytecode(worldStateDB, nestedBytecode); + await mockGetBytecode(worldStateDB, nestedBytecode); - const contractClass = makeContractClassPublic(0, { + const contractClass = await makeContractClassPublic(0, { bytecode: nestedBytecode, selector: FunctionSelector.random(), }); mockGetContractClass(worldStateDB, contractClass); const contractInstance = await makeContractInstanceFromClassId(contractClass.id); mockGetContractInstance(worldStateDB, contractInstance); - mockNullifierExists(worldStateDB, siloAddress(contractInstance.address)); + mockNullifierExists(worldStateDB, await siloAddress(contractInstance.address)); const nestedTrace = mock(); mockTraceFork(trace, nestedTrace); @@ -947,16 +954,16 @@ describe('AVM simulator: transpiled Noir contracts', () => { const context = createContext(calldata); const callBytecode = getAvmTestContractBytecode('nested_static_call_to_add'); const nestedBytecode = getAvmTestContractBytecode('public_dispatch'); - mockGetBytecode(worldStateDB, nestedBytecode); + await mockGetBytecode(worldStateDB, nestedBytecode); - const contractClass = makeContractClassPublic(0, { + const contractClass = await makeContractClassPublic(0, { bytecode: nestedBytecode, selector: FunctionSelector.random(), }); mockGetContractClass(worldStateDB, contractClass); const contractInstance = await makeContractInstanceFromClassId(contractClass.id); mockGetContractInstance(worldStateDB, contractInstance); - mockNullifierExists(worldStateDB, siloAddress(contractInstance.address)); + mockNullifierExists(worldStateDB, await siloAddress(contractInstance.address)); const nestedTrace = mock(); mockTraceFork(trace, nestedTrace); @@ -968,22 +975,22 @@ describe('AVM simulator: transpiled Noir contracts', () => { it(`Nested call with not enough gas (expect failure)`, async () => { const gas = [/*l2=*/ 5, /*da=*/ 10000].map(g => new Fr(g)); - const targetFunctionSelector = FunctionSelector.fromSignature( + const targetFunctionSelector = await FunctionSelector.fromSignature( 'nested_call_to_add_with_gas(Field,Field,Field,Field)', ); const calldata: Fr[] = [targetFunctionSelector.toField(), value0, value1, ...gas]; const context = createContext(calldata); const artifact = getAvmTestContractArtifact('public_dispatch'); - mockGetBytecode(worldStateDB, artifact.bytecode); + await mockGetBytecode(worldStateDB, artifact.bytecode); - const contractClass = makeContractClassPublic(0, { + const contractClass = await makeContractClassPublic(0, { bytecode: artifact.bytecode, selector: FunctionSelector.random(), }); mockGetContractClass(worldStateDB, contractClass); const contractInstance = await makeContractInstanceFromClassId(contractClass.id); mockGetContractInstance(worldStateDB, contractInstance); - mockNullifierExists(worldStateDB, siloAddress(contractInstance.address)); + mockNullifierExists(worldStateDB, await siloAddress(contractInstance.address)); mockTraceFork(trace); @@ -996,16 +1003,16 @@ describe('AVM simulator: transpiled Noir contracts', () => { const context = createContext(); const callBytecode = getAvmTestContractBytecode('nested_static_call_to_set_storage'); const nestedBytecode = getAvmTestContractBytecode('public_dispatch'); - mockGetBytecode(worldStateDB, nestedBytecode); + await mockGetBytecode(worldStateDB, nestedBytecode); - const contractClass = makeContractClassPublic(0, { + const contractClass = await makeContractClassPublic(0, { bytecode: nestedBytecode, selector: FunctionSelector.random(), }); mockGetContractClass(worldStateDB, contractClass); const contractInstance = await makeContractInstanceFromClassId(contractClass.id); mockGetContractInstance(worldStateDB, contractInstance); - mockNullifierExists(worldStateDB, siloAddress(contractInstance.address)); + mockNullifierExists(worldStateDB, await siloAddress(contractInstance.address)); const nestedTrace = mock(); mockTraceFork(trace, nestedTrace); @@ -1026,16 +1033,16 @@ describe('AVM simulator: transpiled Noir contracts', () => { const context = createContext(calldata); const callBytecode = getAvmTestContractBytecode('nested_call_to_assert_same'); const nestedBytecode = getAvmTestContractBytecode('public_dispatch'); - mockGetBytecode(worldStateDB, nestedBytecode); + await mockGetBytecode(worldStateDB, nestedBytecode); - const contractClass = makeContractClassPublic(0, { + const contractClass = await makeContractClassPublic(0, { bytecode: nestedBytecode, selector: FunctionSelector.random(), }); mockGetContractClass(worldStateDB, contractClass); const contractInstance = await makeContractInstanceFromClassId(contractClass.id); mockGetContractInstance(worldStateDB, contractInstance); - mockNullifierExists(worldStateDB, siloAddress(contractInstance.address)); + mockNullifierExists(worldStateDB, await siloAddress(contractInstance.address)); mockTraceFork(trace); @@ -1051,16 +1058,16 @@ describe('AVM simulator: transpiled Noir contracts', () => { const context = createContext(); const callBytecode = getAvmTestContractBytecode('returndata_copy_oracle'); const nestedBytecode = getAvmTestContractBytecode('public_dispatch'); - mockGetBytecode(worldStateDB, nestedBytecode); + await mockGetBytecode(worldStateDB, nestedBytecode); - const contractClass = makeContractClassPublic(0, { + const contractClass = await makeContractClassPublic(0, { bytecode: nestedBytecode, selector: FunctionSelector.random(), }); mockGetContractClass(worldStateDB, contractClass); const contractInstance = await makeContractInstanceFromClassId(contractClass.id); mockGetContractInstance(worldStateDB, contractInstance); - mockNullifierExists(worldStateDB, siloAddress(contractInstance.address)); + mockNullifierExists(worldStateDB, await siloAddress(contractInstance.address)); mockTraceFork(trace); @@ -1117,7 +1124,6 @@ describe('AVM simulator: transpiled Noir contracts', () => { const slotNumber0 = 1; // must update Noir contract if changing this const slot0 = new Fr(slotNumber0); - const leafSlot0 = computePublicDataTreeLeafSlot(address, slot0); let worldStateDB: WorldStateDB; let merkleTrees: MerkleTreeWriteOperations; @@ -1125,6 +1131,12 @@ describe('AVM simulator: transpiled Noir contracts', () => { let persistableState: AvmPersistableStateManager; let ephemeralForest: AvmEphemeralForest; + let leafSlot0: Fr; + + beforeAll(async () => { + leafSlot0 = await computePublicDataTreeLeafSlot(address, slot0); + }); + beforeEach(async () => { trace = mock(); @@ -1334,26 +1346,26 @@ describe('AVM simulator: transpiled Noir contracts', () => { }); }); -function sha256FromMemoryBytes(bytes: Uint8[]): Fr[] { - return [...sha256(Buffer.concat(bytes.map(b => b.toBuffer())))].map(b => new Fr(b)); +function sha256FromMemoryBytes(bytes: Uint8[]): Promise { + return Promise.resolve([...sha256(Buffer.concat(bytes.map(b => b.toBuffer())))].map(b => new Fr(b))); } -function keccak256FromMemoryBytes(bytes: Uint8[]): Fr[] { - return [...keccak256(Buffer.concat(bytes.map(b => b.toBuffer())))].map(b => new Fr(b)); +function keccak256FromMemoryBytes(bytes: Uint8[]): Promise { + return Promise.resolve([...keccak256(Buffer.concat(bytes.map(b => b.toBuffer())))].map(b => new Fr(b))); } -function keccakF1600FromMemoryUint64s(mem: Uint64[]): Fr[] { - return [...keccakf1600(mem.map(u => u.toBigInt()))].map(b => new Fr(b)); +function keccakF1600FromMemoryUint64s(mem: Uint64[]): Promise { + return Promise.resolve([...keccakf1600(mem.map(u => u.toBigInt()))].map(b => new Fr(b))); } -function poseidon2FromMemoryFields(fields: Fieldable[]): Fr[] { - return [poseidon2Hash(fields)]; +async function poseidon2FromMemoryFields(fields: Fieldable[]): Promise { + return [await poseidon2Hash(fields)]; } -function pedersenFromMemoryFields(fields: Fieldable[]): Fr[] { - return [pedersenHash(fields)]; +async function pedersenFromMemoryFields(fields: Fieldable[]): Promise { + return [await pedersenHash(fields)]; } -function indexedPedersenFromMemoryFields(fields: Fieldable[]): Fr[] { - return [pedersenHash(fields, /*index=*/ 20)]; +async function indexedPedersenFromMemoryFields(fields: Fieldable[]): Promise { + return [await pedersenHash(fields, /*index=*/ 20)]; } diff --git a/yarn-project/simulator/src/avm/avm_tree.test.ts b/yarn-project/simulator/src/avm/avm_tree.test.ts index 85b30629e13..f80de76b826 100644 --- a/yarn-project/simulator/src/avm/avm_tree.test.ts +++ b/yarn-project/simulator/src/avm/avm_tree.test.ts @@ -130,13 +130,13 @@ describe('Simple Note Hash Consistency', () => { it('Should do a simple append and check the root starting with empty', async () => { const treeContainer = await AvmEphemeralForest.create(copyState); for (const noteHash of noteHashes) { - treeContainer.appendNoteHash(noteHash); + await treeContainer.appendNoteHash(noteHash); } await mainState.appendLeaves(treeId, noteHashes); // Check that the roots are consistent const wsRoot = await getWorldStateRoot(treeId); - const computedRoot = treeContainer.treeMap.get(treeId)!.getRoot(); + const computedRoot = await treeContainer.treeMap.get(treeId)!.getRoot(); expect(computedRoot.toBuffer()).toEqual(wsRoot); // Check a sibling path from a random index is consistent @@ -155,7 +155,7 @@ describe('Simple Note Hash Consistency', () => { // Append the remaining note hashes within the container const postInserted = noteHashes.slice(32); for (const noteHash of postInserted) { - treeContainer.appendNoteHash(noteHash); + await treeContainer.appendNoteHash(noteHash); } // Build a worldstateDB with all the note hashes @@ -163,7 +163,7 @@ describe('Simple Note Hash Consistency', () => { // Check that the roots are consistent const wsRoot = await getWorldStateRoot(treeId); - const computedRoot = treeContainer.treeMap.get(treeId)!.getRoot(); + const computedRoot = await treeContainer.treeMap.get(treeId)!.getRoot(); expect(computedRoot.toBuffer()).toEqual(wsRoot); // Check the sibling path from an index before the fork @@ -202,7 +202,7 @@ describe('Simple Public Data Consistency', () => { // Compare the roots of the container and the world state trees const wsRoot = await getWorldStateRoot(treeId); - const computedRoot = treeContainer.treeMap.get(treeId)!.getRoot(); + const computedRoot = await treeContainer.treeMap.get(treeId)!.getRoot(); expect(computedRoot.toBuffer()).toEqual(wsRoot); // Check that all the accumulated insertion results match @@ -231,7 +231,7 @@ describe('Simple Public Data Consistency', () => { // Compare the roots of the container and the world state trees const wsRoot = await getWorldStateRoot(treeId); - const computedRoot = treeContainer.treeMap.get(treeId)!.getRoot(); + const computedRoot = await treeContainer.treeMap.get(treeId)!.getRoot(); expect(computedRoot.toBuffer()).toEqual(wsRoot); // Get a sibling path from a random index and check it is consistent @@ -270,7 +270,7 @@ describe('Simple Public Data Consistency', () => { // Check the roots are consistent const wsRoot = await getWorldStateRoot(treeId); - const computedRoot = treeContainer.treeMap.get(treeId)!.getRoot(); + const computedRoot = await treeContainer.treeMap.get(treeId)!.getRoot(); expect(computedRoot.toBuffer()).toEqual(wsRoot); // Check the insertion results match @@ -300,7 +300,7 @@ describe('Simple Nullifier Consistency', () => { // Compare the roots of the container and the world state const wsRoot = await getWorldStateRoot(treeId); - const computedRoot = treeContainer.treeMap.get(treeId)!.getRoot(); + const computedRoot = await treeContainer.treeMap.get(treeId)!.getRoot(); expect(computedRoot.toBuffer()).toEqual(wsRoot); // Check that all the accumulated insertion results match @@ -328,7 +328,7 @@ describe('Simple Nullifier Consistency', () => { // Compare the roots of the container and the world state const wsRoot = await getWorldStateRoot(treeId); - const computedRoot = treeContainer.treeMap.get(treeId)!.getRoot(); + const computedRoot = await treeContainer.treeMap.get(treeId)!.getRoot(); expect(computedRoot.toBuffer()).toEqual(wsRoot); // Check insertion results - note we can only compare against the post-insertion results @@ -341,17 +341,17 @@ describe('Simple Nullifier Consistency', () => { it('Should check that the insertion paths resolve to the root', async () => { const treeContainer = await AvmEphemeralForest.create(copyState); - const rootBefore = treeContainer.treeMap.get(MerkleTreeId.NULLIFIER_TREE)!.getRoot().toBuffer(); + const rootBefore = (await treeContainer.treeMap.get(MerkleTreeId.NULLIFIER_TREE)!.getRoot()).toBuffer(); const containerInsert = await treeContainer.appendNullifier(indexedHashes[0]); - const rootAfter = treeContainer.treeMap.get(MerkleTreeId.NULLIFIER_TREE)!.getRoot().toBuffer(); + const rootAfter = (await treeContainer.treeMap.get(MerkleTreeId.NULLIFIER_TREE)!.getRoot()).toBuffer(); - const calcRootFromPath = (path: Fr[], leaf: Fr, index: bigint) => { + const calcRootFromPath = async (path: Fr[], leaf: Fr, index: bigint) => { for (const sibling of path) { if (index % 2n === 0n) { - leaf = poseidon2Hash([leaf, sibling]); + leaf = await poseidon2Hash([leaf, sibling]); } else { - leaf = poseidon2Hash([sibling, leaf]); + leaf = await poseidon2Hash([sibling, leaf]); } index = index / 2n; } @@ -366,9 +366,9 @@ describe('Simple Nullifier Consistency', () => { // (5) Check the root after the insertion // Step 1 - const membershipRoot = calcRootFromPath( + const membershipRoot = await calcRootFromPath( containerInsert.lowWitness.siblingPath, - treeContainer.hashPreimage(containerInsert.lowWitness.preimage), + await treeContainer.hashPreimage(containerInsert.lowWitness.preimage), containerInsert.lowWitness.index, ); expect(membershipRoot.toBuffer()).toEqual(rootBefore); @@ -379,20 +379,24 @@ describe('Simple Nullifier Consistency', () => { newLowNullifier.nextIndex = containerInsert.leafIndex; newLowNullifier.nextNullifier = containerInsert.element.nullifier; // Compute new root - const updatedRoot = calcRootFromPath( + const updatedRoot = await calcRootFromPath( containerInsert.lowWitness.siblingPath, - treeContainer.hashPreimage(newLowNullifier), + await treeContainer.hashPreimage(newLowNullifier), containerInsert.lowWitness.index, ); //Step 3 - const zeroMembershipRoot = calcRootFromPath(containerInsert.insertionPath, Fr.ZERO, containerInsert.leafIndex); + const zeroMembershipRoot = await calcRootFromPath( + containerInsert.insertionPath, + Fr.ZERO, + containerInsert.leafIndex, + ); expect(zeroMembershipRoot.toBuffer()).toEqual(updatedRoot.toBuffer()); // Step 4 - const finalRoot = calcRootFromPath( + const finalRoot = await calcRootFromPath( containerInsert.insertionPath, - treeContainer.hashPreimage(containerInsert.element), + await treeContainer.hashPreimage(containerInsert.element), containerInsert.leafIndex, ); expect(finalRoot.toBuffer()).toEqual(rootAfter); @@ -426,7 +430,7 @@ describe('Big Random Avm Ephemeral Container Test', () => { for (let i = 0; i < ENTRY_COUNT; i++) { await nullifierInsertWorldState(indexedHashes[i]); await publicDataInsertWorldState(slots[i], values[i]); - treeContainer.appendNoteHash(noteHashes[i]); + await treeContainer.appendNoteHash(noteHashes[i]); await treeContainer.appendNullifier(indexedHashes[i]); await treeContainer.writePublicStorage(slots[i], values[i]); } @@ -435,7 +439,7 @@ describe('Big Random Avm Ephemeral Container Test', () => { const computedRoots = []; for (const treeId of [MerkleTreeId.NOTE_HASH_TREE, MerkleTreeId.NULLIFIER_TREE, MerkleTreeId.PUBLIC_DATA_TREE]) { wsRoots.push(await getWorldStateRoot(treeId)); - computedRoots.push(treeContainer.treeMap.get(treeId)!.getRoot().toBuffer()); + computedRoots.push((await treeContainer.treeMap.get(treeId)!.getRoot()).toBuffer()); } // All the roots should match @@ -472,15 +476,15 @@ describe('Checking forking and merging', () => { // Write the last element to the forked container await forkedContainer.writePublicStorage(slots[slots.length - 1], values[slots.length - 1]); - const forkedRoot = forkedContainer.treeMap.get(treeId)!.getRoot(); - let originalRoot = treeContainer.treeMap.get(treeId)!.getRoot(); + const forkedRoot = await forkedContainer.treeMap.get(treeId)!.getRoot(); + let originalRoot = await treeContainer.treeMap.get(treeId)!.getRoot(); // The roots should NOT match since we have an extra element expect(forkedRoot.toBuffer()).not.toEqual(originalRoot.toBuffer()); // Write the last element to original container await treeContainer.writePublicStorage(slots[slots.length - 1], values[slots.length - 1]); - originalRoot = treeContainer.treeMap.get(treeId)!.getRoot(); + originalRoot = await treeContainer.treeMap.get(treeId)!.getRoot(); // We should be consistent now expect(forkedRoot.toBuffer()).toEqual(originalRoot.toBuffer()); @@ -512,7 +516,7 @@ describe('Checking forking and merging', () => { wsInsertionResults.push(await publicDataInsertWorldState(slots[1], values[1])); wsInsertionResults.push(await publicDataInsertWorldState(slots[3], values[3])); - const containerRoot = forkedContainer.treeMap.get(treeId)!.getRoot(); + const containerRoot = await forkedContainer.treeMap.get(treeId)!.getRoot(); const wsRoot = await getWorldStateRoot(treeId); expect(containerRoot.toBuffer()).toEqual(wsRoot); @@ -545,17 +549,17 @@ describe('AVM Ephemeral Tree Sanity Test', () => { ); const expectedFrontier0 = new Fr(4); - const exepctedFrontier1 = poseidon2Hash([new Fr(4), new Fr(5)]); - const expectedFrontier2 = poseidon2Hash([ - poseidon2Hash([new Fr(0), new Fr(1)]), - poseidon2Hash([new Fr(2), new Fr(3)]), + const exepctedFrontier1 = await poseidon2Hash([new Fr(4), new Fr(5)]); + const expectedFrontier2 = await poseidon2Hash([ + await poseidon2Hash([new Fr(0), new Fr(1)]), + await poseidon2Hash([new Fr(2), new Fr(3)]), ]); const expectedFrontier = [expectedFrontier0, exepctedFrontier1, expectedFrontier2]; expect(tree.frontier).toEqual(expectedFrontier); // Check root await worldStateTrees.appendLeaves(MerkleTreeId.NOTE_HASH_TREE, leaves); const treeInfo = await worldStateTrees.getTreeInfo(MerkleTreeId.NOTE_HASH_TREE, true); - const localRoot = tree.getRoot(); + const localRoot = await tree.getRoot(); expect(localRoot.toBuffer()).toEqual(treeInfo.root); }); }); @@ -573,7 +577,7 @@ describe('Batch Insertion', () => { // Check root const wsRoot = await getWorldStateRoot(MerkleTreeId.NULLIFIER_TREE); - const computedRoot = treeContainer.treeMap.get(MerkleTreeId.NULLIFIER_TREE)!.getRoot(); + const computedRoot = await treeContainer.treeMap.get(MerkleTreeId.NULLIFIER_TREE)!.getRoot(); expect(computedRoot.toBuffer()).toEqual(wsRoot); }); }); @@ -595,7 +599,7 @@ describe('A basic benchmark', () => { // Check Roots before benchmarking let wsRoot = await getWorldStateRoot(MerkleTreeId.PUBLIC_DATA_TREE); - let computedRoot = container.treeMap.get(MerkleTreeId.PUBLIC_DATA_TREE)!.getRoot(); + let computedRoot = await container.treeMap.get(MerkleTreeId.PUBLIC_DATA_TREE)!.getRoot(); expect(computedRoot.toBuffer()).toEqual(wsRoot); console.time('benchmark'); @@ -611,7 +615,7 @@ describe('A basic benchmark', () => { } // Check roots wsRoot = await getWorldStateRoot(MerkleTreeId.PUBLIC_DATA_TREE); - computedRoot = container.treeMap.get(MerkleTreeId.PUBLIC_DATA_TREE)!.getRoot(); + computedRoot = await container.treeMap.get(MerkleTreeId.PUBLIC_DATA_TREE)!.getRoot(); expect(computedRoot.toBuffer()).toEqual(wsRoot); }); }); diff --git a/yarn-project/simulator/src/avm/avm_tree.ts b/yarn-project/simulator/src/avm/avm_tree.ts index e7e18b721cb..b971da3ee4e 100644 --- a/yarn-project/simulator/src/avm/avm_tree.ts +++ b/yarn-project/simulator/src/avm/avm_tree.ts @@ -109,7 +109,7 @@ export class AvmEphemeralForest { */ async getSiblingPath(treeId: MerkleTreeId, index: bigint): Promise { const tree = this.treeMap.get(treeId)!; - let path = tree.getSiblingPath(index); + let path = await tree.getSiblingPath(index); if (path === undefined) { // We dont have the sibling path in our tree - we have to get it from the DB path = (await this.treeDb.getSiblingPath(treeId, index)).toFields(); @@ -119,7 +119,7 @@ export class AvmEphemeralForest { const siblingIndex = index ^ 1n; const node = tree.getNode(siblingIndex, tree.depth - i); if (node !== undefined) { - const nodeHash = tree.hashTree(node, i + 1); + const nodeHash = await tree.hashTree(node, i + 1); if (!nodeHash.equals(path[i])) { path[i] = nodeHash; } @@ -138,17 +138,17 @@ export class AvmEphemeralForest { * @param newLeafPreimage - The preimage of the new leaf to be inserted. * @returns The sibling path of the new leaf (i.e. the insertion path) */ - appendIndexedTree( + async appendIndexedTree( treeId: ID, lowLeafIndex: bigint, lowLeafPreimage: T, newLeafPreimage: T, - ): Fr[] { + ): Promise { const tree = this.treeMap.get(treeId)!; - const newLeaf = this.hashPreimage(newLeafPreimage); + const newLeaf = await this.hashPreimage(newLeafPreimage); const insertIndex = tree.leafCount; - const lowLeaf = this.hashPreimage(lowLeafPreimage); + const lowLeaf = await this.hashPreimage(lowLeafPreimage); // Update the low nullifier hash this.setIndexedUpdates(treeId, lowLeafIndex, lowLeafPreimage); tree.updateLeaf(lowLeaf, lowLeafIndex); @@ -156,7 +156,7 @@ export class AvmEphemeralForest { tree.appendLeaf(newLeaf); this.setIndexedUpdates(treeId, insertIndex, newLeafPreimage); - return tree.getSiblingPath(insertIndex)!; + return (await tree.getSiblingPath(insertIndex))!; } /** @@ -186,7 +186,7 @@ export class AvmEphemeralForest { const existingPublicDataSiblingPath = siblingPath; updatedPreimage.value = newValue; - tree.updateLeaf(this.hashPreimage(updatedPreimage), lowLeafIndex); + tree.updateLeaf(await this.hashPreimage(updatedPreimage), lowLeafIndex); this.setIndexedUpdates(treeId, lowLeafIndex, updatedPreimage); this._updateSortedKeys(treeId, [updatedPreimage.slot], [lowLeafIndex]); @@ -212,7 +212,7 @@ export class AvmEphemeralForest { new Fr(preimage.getNextKey()), preimage.getNextIndex(), ); - const insertionPath = this.appendIndexedTree(treeId, lowLeafIndex, updatedLowLeaf, newPublicDataLeaf); + const insertionPath = await this.appendIndexedTree(treeId, lowLeafIndex, updatedLowLeaf, newPublicDataLeaf); // Even though the low leaf key is not updated, we still need to update the sorted keys in case we have // not seen the low leaf before @@ -281,7 +281,7 @@ export class AvmEphemeralForest { updatedLowNullifier.nextIndex = insertionIndex; const newNullifierLeaf = new NullifierLeafPreimage(nullifier, preimage.nextNullifier, preimage.nextIndex); - const insertionPath = this.appendIndexedTree(treeId, index, updatedLowNullifier, newNullifierLeaf); + const insertionPath = await this.appendIndexedTree(treeId, index, updatedLowNullifier, newNullifierLeaf); // Even though the low nullifier key is not updated, we still need to update the sorted keys in case we have // not seen the low nullifier before @@ -308,11 +308,11 @@ export class AvmEphemeralForest { * @param value - The note hash to be appended * @returns The insertion result which contains the insertion path */ - appendNoteHash(noteHash: Fr): Fr[] { + async appendNoteHash(noteHash: Fr): Promise { const tree = this.treeMap.get(MerkleTreeId.NOTE_HASH_TREE)!; tree.appendLeaf(noteHash); // We use leafCount - 1 here because we would have just appended a leaf - const insertionPath = tree.getSiblingPath(tree.leafCount - 1n); + const insertionPath = await tree.getSiblingPath(tree.leafCount - 1n); return insertionPath!; } @@ -484,14 +484,15 @@ export class AvmEphemeralForest { /** * This hashes the preimage to a field element */ - hashPreimage(preimage: T): Fr { + hashPreimage(preimage: T): Promise { const input = preimage.toHashInputs().map(x => Fr.fromBuffer(x)); return poseidon2Hash(input); } - getTreeSnapshot(id: MerkleTreeId): AppendOnlyTreeSnapshot { + async getTreeSnapshot(id: MerkleTreeId): Promise { const tree = this.treeMap.get(id)!; - return new AppendOnlyTreeSnapshot(tree.getRoot(), Number(tree.leafCount)); + const root = await tree.getRoot(); + return new AppendOnlyTreeSnapshot(root, Number(tree.leafCount)); } } @@ -551,19 +552,10 @@ const Leaf = (value: Fr): Leaf => ({ */ export class EphemeralAvmTree { private tree: Tree; - private readonly zeroHashes: Fr[]; public frontier: Fr[]; - private constructor(public leafCount: bigint, public depth: number) { - let zeroHash = Fr.zero(); - // Can probably cache this elsewhere - const zeroHashes = []; - for (let i = 0; i < this.depth; i++) { - zeroHashes.push(zeroHash); - zeroHash = poseidon2Hash([zeroHash, zeroHash]); - } - this.tree = Leaf(zeroHash); - this.zeroHashes = zeroHashes; + private constructor(private root: Leaf, public leafCount: bigint, public depth: number, private zeroHashes: Fr[]) { + this.tree = root; this.frontier = []; } @@ -573,7 +565,14 @@ export class EphemeralAvmTree { treeDb: MerkleTreeReadOperations, merkleId: MerkleTreeId, ): Promise { - const tree = new EphemeralAvmTree(forkedLeafCount, depth); + let zeroHash = Fr.zero(); + // Can probably cache this elsewhere + const zeroHashes = []; + for (let i = 0; i < depth; i++) { + zeroHashes.push(zeroHash); + zeroHash = await poseidon2Hash([zeroHash, zeroHash]); + } + const tree = new EphemeralAvmTree(Leaf(zeroHash), forkedLeafCount, depth, zeroHashes); await tree.initializeFrontier(treeDb, merkleId); return tree; } @@ -604,10 +603,10 @@ export class EphemeralAvmTree { * @param index - The index of the leaf for which a sibling path should be returned. * @returns The sibling path of the leaf, can fail if the path is not found */ - getSiblingPath(index: bigint): Fr[] | undefined { + async getSiblingPath(index: bigint): Promise { const searchPath = this._derivePathLE(index); // Handle cases where we error out - const { path, status } = this._getSiblingPath(searchPath, this.tree, []); + const { path, status } = await this._getSiblingPath(searchPath, this.tree, []); if (status === SiblingStatus.ERROR) { return undefined; } @@ -695,7 +694,7 @@ export class EphemeralAvmTree { /** * Computes the root of the tree */ - public getRoot(): Fr { + public getRoot(): Promise { return this.hashTree(this.tree, this.depth); } @@ -704,10 +703,13 @@ export class EphemeralAvmTree { * @param tree - The tree to be hashed * @param depth - The depth of the tree */ - public hashTree(tree: Tree, depth: number): Fr { + public async hashTree(tree: Tree, depth: number): Promise { switch (tree.tag) { case TreeType.NODE: { - return poseidon2Hash([this.hashTree(tree.leftTree, depth - 1), this.hashTree(tree.rightTree, depth - 1)]); + return poseidon2Hash([ + await this.hashTree(tree.leftTree, depth - 1), + await this.hashTree(tree.rightTree, depth - 1), + ]); } case TreeType.LEAF: { return tree.value; @@ -807,7 +809,7 @@ export class EphemeralAvmTree { * @param tree - The current tree * @param acc - The accumulated sibling path */ - private _getSiblingPath(searchPath: number[], tree: Tree, acc: Fr[]): AccumulatedSiblingPath { + private async _getSiblingPath(searchPath: number[], tree: Tree, acc: Fr[]): Promise { // If we have reached the end of the path, we should be at a leaf or empty node // If it is a leaf, we check if the value is equal to the leaf value // If it is empty we check if the value is equal to zero @@ -824,15 +826,15 @@ export class EphemeralAvmTree { case TreeType.NODE: { // Look at the next element of the path to decided if we go left or right, note this mutates! return searchPath.pop() === 0 - ? this._getSiblingPath( + ? await this._getSiblingPath( searchPath, tree.leftTree, - [this.hashTree(tree.rightTree, searchPath.length)].concat(acc), + [await this.hashTree(tree.rightTree, searchPath.length)].concat(acc), ) - : this._getSiblingPath( + : await this._getSiblingPath( searchPath, tree.rightTree, - [this.hashTree(tree.leftTree, searchPath.length)].concat(acc), + [await this.hashTree(tree.leftTree, searchPath.length)].concat(acc), ); } // In these two situations we are exploring a subtree we dont have information about diff --git a/yarn-project/simulator/src/avm/fixtures/index.ts b/yarn-project/simulator/src/avm/fixtures/index.ts index 9ad3778127a..71ca6423177 100644 --- a/yarn-project/simulator/src/avm/fixtures/index.ts +++ b/yarn-project/simulator/src/avm/fixtures/index.ts @@ -124,7 +124,7 @@ export function randomMemoryFields(length: number): Field[] { return [...Array(length)].map(_ => new Field(Fr.random())); } -export function getAvmTestContractFunctionSelector(functionName: string): FunctionSelector { +export function getAvmTestContractFunctionSelector(functionName: string): Promise { const artifact = AvmTestContractArtifact.functions.find(f => f.name === functionName)!; assert(!!artifact, `Function ${functionName} not found in AvmTestContractArtifact`); const params = artifact.parameters; diff --git a/yarn-project/simulator/src/avm/journal/journal.test.ts b/yarn-project/simulator/src/avm/journal/journal.test.ts index 4fcfe104fcd..88a7ebf1e7d 100644 --- a/yarn-project/simulator/src/avm/journal/journal.test.ts +++ b/yarn-project/simulator/src/avm/journal/journal.test.ts @@ -81,20 +81,20 @@ describe('journal', () => { expect(trace.traceNoteHashCheck).toHaveBeenCalledWith(address, utxo, leafIndex, exists); }); - it('writeNoteHash works', () => { + it('writeNoteHash works', async () => { mockNoteHashCount(trace, 1); - persistableState.writeNoteHash(address, utxo); + await persistableState.writeNoteHash(address, utxo); expect(trace.traceNewNoteHash).toHaveBeenCalledTimes(1); - const siloedNotehash = siloNoteHash(address, utxo); - const nonce = computeNoteHashNonce(persistableState.firstNullifier, 1); - const uniqueNoteHash = computeUniqueNoteHash(nonce, siloedNotehash); + const siloedNotehash = await siloNoteHash(address, utxo); + const nonce = await computeNoteHashNonce(persistableState.firstNullifier, 1); + const uniqueNoteHash = await computeUniqueNoteHash(nonce, siloedNotehash); expect(trace.traceNewNoteHash).toHaveBeenCalledWith(uniqueNoteHash); }); it('checkNullifierExists works for missing nullifiers', async () => { const exists = await persistableState.checkNullifierExists(address, utxo); expect(exists).toEqual(false); - const siloedNullifier = siloNullifier(address, utxo); + const siloedNullifier = await siloNullifier(address, utxo); expect(trace.traceNullifierCheck).toHaveBeenCalledTimes(1); expect(trace.traceNullifierCheck).toHaveBeenCalledWith(siloedNullifier, exists); }); @@ -103,14 +103,14 @@ describe('journal', () => { mockNullifierExists(worldStateDB, leafIndex, utxo); const exists = await persistableState.checkNullifierExists(address, utxo); expect(exists).toEqual(true); - const siloedNullifier = siloNullifier(address, utxo); + const siloedNullifier = await siloNullifier(address, utxo); expect(trace.traceNullifierCheck).toHaveBeenCalledTimes(1); expect(trace.traceNullifierCheck).toHaveBeenCalledWith(siloedNullifier, exists); }); it('writeNullifier works', async () => { await persistableState.writeNullifier(address, utxo); - const siloedNullifier = siloNullifier(address, utxo); + const siloedNullifier = await siloNullifier(address, utxo); expect(trace.traceNewNullifier).toHaveBeenCalledTimes(1); expect(trace.traceNewNullifier).toHaveBeenCalledWith(siloedNullifier); }); @@ -157,14 +157,14 @@ describe('journal', () => { describe('Getting bytecode', () => { it('Should get bytecode', async () => { const bytecode = Buffer.from('0xdeadbeef'); - const bytecodeCommitment = computePublicBytecodeCommitment(bytecode); + const bytecodeCommitment = await computePublicBytecodeCommitment(bytecode); const contractInstance = SerializableContractInstance.default(); - const contractClass = makeContractClassPublic(); + const contractClass = await makeContractClassPublic(); mockNullifierExists(worldStateDB, leafIndex, utxo); mockGetContractInstance(worldStateDB, contractInstance.withAddress(address)); mockGetContractClass(worldStateDB, contractClass); - mockGetBytecode(worldStateDB, bytecode); + await mockGetBytecode(worldStateDB, bytecode); const expectedContractClassPreimage = { artifactHash: contractClass.artifactHash, diff --git a/yarn-project/simulator/src/avm/journal/journal.ts b/yarn-project/simulator/src/avm/journal/journal.ts index a635424049f..e08f6e9e510 100644 --- a/yarn-project/simulator/src/avm/journal/journal.ts +++ b/yarn-project/simulator/src/avm/journal/journal.ts @@ -144,7 +144,7 @@ export class AvmPersistableStateManager { */ public async writeStorage(contractAddress: AztecAddress, slot: Fr, value: Fr, protocolWrite = false): Promise { this.log.debug(`Storage write (address=${contractAddress}, slot=${slot}): value=${value}`); - const leafSlot = computePublicDataTreeLeafSlot(contractAddress, slot); + const leafSlot = await computePublicDataTreeLeafSlot(contractAddress, slot); this.log.debug(`leafSlot=${leafSlot}`); // Cache storage writes for later reference/reads this.publicStorage.write(contractAddress, slot, value); @@ -169,7 +169,7 @@ export class AvmPersistableStateManager { ); } - this.trace.tracePublicStorageWrite( + await this.trace.tracePublicStorageWrite( contractAddress, slot, value, @@ -181,7 +181,7 @@ export class AvmPersistableStateManager { insertionPath, ); } else { - this.trace.tracePublicStorageWrite(contractAddress, slot, value, protocolWrite); + await this.trace.tracePublicStorageWrite(contractAddress, slot, value, protocolWrite); } } @@ -195,7 +195,7 @@ export class AvmPersistableStateManager { public async readStorage(contractAddress: AztecAddress, slot: Fr): Promise { const { value, cached } = await this.publicStorage.read(contractAddress, slot); this.log.debug(`Storage read (address=${contractAddress}, slot=${slot}): value=${value}, cached=${cached}`); - const leafSlot = computePublicDataTreeLeafSlot(contractAddress, slot); + const leafSlot = await computePublicDataTreeLeafSlot(contractAddress, slot); this.log.debug(`leafSlot=${leafSlot}`); if (this.doMerkleOperations) { @@ -283,34 +283,34 @@ export class AvmPersistableStateManager { * Write a raw note hash, silo it and make it unique, then trace the write. * @param noteHash - the unsiloed note hash to write */ - public writeNoteHash(contractAddress: AztecAddress, noteHash: Fr): void { - const siloedNoteHash = siloNoteHash(contractAddress, noteHash); + public async writeNoteHash(contractAddress: AztecAddress, noteHash: Fr): Promise { + const siloedNoteHash = await siloNoteHash(contractAddress, noteHash); - this.writeSiloedNoteHash(siloedNoteHash); + await this.writeSiloedNoteHash(siloedNoteHash); } /** * Write a note hash, make it unique, trace the write. * @param noteHash - the non unique note hash to write */ - public writeSiloedNoteHash(noteHash: Fr): void { - const nonce = computeNoteHashNonce(this.firstNullifier, this.trace.getNoteHashCount()); - const uniqueNoteHash = computeUniqueNoteHash(nonce, noteHash); + public async writeSiloedNoteHash(noteHash: Fr): Promise { + const nonce = await computeNoteHashNonce(this.firstNullifier, this.trace.getNoteHashCount()); + const uniqueNoteHash = await computeUniqueNoteHash(nonce, noteHash); - this.writeUniqueNoteHash(uniqueNoteHash); + await this.writeUniqueNoteHash(uniqueNoteHash); } /** * Write a note hash, trace the write. * @param noteHash - the siloed unique hash to write */ - public writeUniqueNoteHash(noteHash: Fr): void { + public async writeUniqueNoteHash(noteHash: Fr): Promise { this.log.debug(`noteHashes += @${noteHash}.`); if (this.doMerkleOperations) { // Should write a helper for this const leafIndex = new Fr(this.merkleTrees.treeMap.get(MerkleTreeId.NOTE_HASH_TREE)!.leafCount); - const insertionPath = this.merkleTrees.appendNoteHash(noteHash); + const insertionPath = await this.merkleTrees.appendNoteHash(noteHash); this.trace.traceNewNoteHash(noteHash, leafIndex, insertionPath); } else { this.trace.traceNewNoteHash(noteHash); @@ -325,7 +325,7 @@ export class AvmPersistableStateManager { */ public async checkNullifierExists(contractAddress: AztecAddress, nullifier: Fr): Promise { this.log.debug(`Checking existence of nullifier (address=${contractAddress}, nullifier=${nullifier})`); - const siloedNullifier = siloNullifier(contractAddress, nullifier); + const siloedNullifier = await siloNullifier(contractAddress, nullifier); const [exists, leafOrLowLeafPreimage, leafOrLowLeafIndex, leafOrLowLeafPath] = await this.getNullifierMembership( siloedNullifier, ); @@ -407,7 +407,7 @@ export class AvmPersistableStateManager { */ public async writeNullifier(contractAddress: AztecAddress, nullifier: Fr) { this.log.debug(`Inserting new nullifier (address=${nullifier}, nullifier=${contractAddress})`); - const siloedNullifier = siloNullifier(contractAddress, nullifier); + const siloedNullifier = await siloNullifier(contractAddress, nullifier); await this.writeSiloedNullifier(siloedNullifier); } @@ -447,13 +447,15 @@ export class AvmPersistableStateManager { await this.nullifiers.append(siloedNullifier); // We append the new nullifier this.log.debug( - `Nullifier tree root before insertion ${this.merkleTrees.treeMap + `Nullifier tree root before insertion ${await this.merkleTrees.treeMap .get(MerkleTreeId.NULLIFIER_TREE)! .getRoot()}`, ); const appendResult = await this.merkleTrees.appendNullifier(siloedNullifier); this.log.debug( - `Nullifier tree root after insertion ${this.merkleTrees.treeMap.get(MerkleTreeId.NULLIFIER_TREE)!.getRoot()}`, + `Nullifier tree root after insertion ${await this.merkleTrees.treeMap + .get(MerkleTreeId.NULLIFIER_TREE)! + .getRoot()}`, ); const lowLeafPreimage = appendResult.lowWitness.preimage as NullifierLeafPreimage; const lowLeafIndex = appendResult.lowWitness.index; @@ -550,7 +552,7 @@ export class AvmPersistableStateManager { new Array(), ]; if (!contractAddressIsCanonical(contractAddress)) { - const contractAddressNullifier = siloNullifier( + const contractAddressNullifier = await siloNullifier( AztecAddress.fromNumber(DEPLOYER_CONTRACT_ADDRESS), contractAddress.toField(), ); @@ -615,7 +617,7 @@ export class AvmPersistableStateManager { new Array(), ]; if (!contractAddressIsCanonical(contractAddress)) { - const contractAddressNullifier = siloNullifier( + const contractAddressNullifier = await siloNullifier( AztecAddress.fromNumber(DEPLOYER_CONTRACT_ADDRESS), contractAddress.toField(), ); diff --git a/yarn-project/simulator/src/avm/opcodes/accrued_substate.test.ts b/yarn-project/simulator/src/avm/opcodes/accrued_substate.test.ts index dd0c97eb724..09d00f787d2 100644 --- a/yarn-project/simulator/src/avm/opcodes/accrued_substate.test.ts +++ b/yarn-project/simulator/src/avm/opcodes/accrued_substate.test.ts @@ -36,7 +36,11 @@ describe('Accrued Substate', () => { const leafIndex = new Fr(7); const leafIndexOffset = 1; const existsOffset = 2; - const siloedNullifier0 = siloNullifier(address, value0); + let siloedNullifier0: Fr; + + beforeAll(async () => { + siloedNullifier0 = await siloNullifier(address, value0); + }); beforeEach(() => { worldStateDB = mock(); @@ -124,9 +128,9 @@ describe('Accrued Substate', () => { context.machineState.memory.set(value0Offset, new Field(value0)); await new EmitNoteHash(/*indirect=*/ 0, /*offset=*/ value0Offset).execute(context); expect(trace.traceNewNoteHash).toHaveBeenCalledTimes(1); - const siloedNotehash = siloNoteHash(address, value0); - const nonce = computeNoteHashNonce(persistableState.firstNullifier, 0); - const uniqueNoteHash = computeUniqueNoteHash(nonce, siloedNotehash); + const siloedNotehash = await siloNoteHash(address, value0); + const nonce = await computeNoteHashNonce(persistableState.firstNullifier, 0); + const uniqueNoteHash = await computeUniqueNoteHash(nonce, siloedNotehash); expect(trace.traceNewNoteHash).toHaveBeenCalledWith(uniqueNoteHash); }); }); diff --git a/yarn-project/simulator/src/avm/opcodes/accrued_substate.ts b/yarn-project/simulator/src/avm/opcodes/accrued_substate.ts index ee514957245..4c0a0c88173 100644 --- a/yarn-project/simulator/src/avm/opcodes/accrued_substate.ts +++ b/yarn-project/simulator/src/avm/opcodes/accrued_substate.ts @@ -70,7 +70,7 @@ export class EmitNoteHash extends Instruction { } const noteHash = memory.get(noteHashOffset).toFr(); - context.persistableState.writeNoteHash(context.environment.address, noteHash); + await context.persistableState.writeNoteHash(context.environment.address, noteHash); memory.assert({ reads: 1, addressing }); } diff --git a/yarn-project/simulator/src/avm/opcodes/external_calls.test.ts b/yarn-project/simulator/src/avm/opcodes/external_calls.test.ts index 9f137f87dae..9b00a735679 100644 --- a/yarn-project/simulator/src/avm/opcodes/external_calls.test.ts +++ b/yarn-project/simulator/src/avm/opcodes/external_calls.test.ts @@ -120,9 +120,9 @@ describe('External Calls', () => { new Return(/*indirect=*/ 0, /*retOffset=*/ 3, /*sizeOffset=*/ 2), ]), ); - mockGetBytecode(worldStateDB, otherContextInstructionsBytecode); + await mockGetBytecode(worldStateDB, otherContextInstructionsBytecode); - const contractClass = makeContractClassPublic(0, { + const contractClass = await makeContractClassPublic(0, { bytecode: otherContextInstructionsBytecode, selector: FunctionSelector.random(), }); @@ -172,10 +172,10 @@ describe('External Calls', () => { new Return(/*indirect=*/ 0, /*retOffset=*/ 0, /*size=*/ 1), ]), ); - mockGetBytecode(worldStateDB, otherContextInstructionsBytecode); + await mockGetBytecode(worldStateDB, otherContextInstructionsBytecode); mockNullifierExists(worldStateDB, addr); - const contractClass = makeContractClassPublic(0, { + const contractClass = await makeContractClassPublic(0, { bytecode: otherContextInstructionsBytecode, selector: FunctionSelector.random(), }); @@ -259,10 +259,10 @@ describe('External Calls', () => { ]; const otherContextInstructionsBytecode = markBytecodeAsAvm(encodeToBytecode(otherContextInstructions)); - mockGetBytecode(worldStateDB, otherContextInstructionsBytecode); + await mockGetBytecode(worldStateDB, otherContextInstructionsBytecode); mockNullifierExists(worldStateDB, addr.toFr()); - const contractClass = makeContractClassPublic(0, { + const contractClass = await makeContractClassPublic(0, { bytecode: otherContextInstructionsBytecode, selector: FunctionSelector.random(), }); diff --git a/yarn-project/simulator/src/avm/opcodes/hashing.ts b/yarn-project/simulator/src/avm/opcodes/hashing.ts index 1afde431e46..c2d32a376e3 100644 --- a/yarn-project/simulator/src/avm/opcodes/hashing.ts +++ b/yarn-project/simulator/src/avm/opcodes/hashing.ts @@ -34,7 +34,7 @@ export class Poseidon2 extends Instruction { const inputState = memory.getSlice(inputOffset, Poseidon2.stateSize); memory.checkTagsRange(TypeTag.FIELD, inputOffset, Poseidon2.stateSize); - const outputState = poseidon2Permutation(inputState); + const outputState = await poseidon2Permutation(inputState); memory.setSlice( outputOffset, outputState.map(word => new Field(word)), diff --git a/yarn-project/simulator/src/avm/test_utils.ts b/yarn-project/simulator/src/avm/test_utils.ts index 27785930567..6c63a1116fe 100644 --- a/yarn-project/simulator/src/avm/test_utils.ts +++ b/yarn-project/simulator/src/avm/test_utils.ts @@ -11,11 +11,10 @@ import { mock } from 'jest-mock-extended'; import { type WorldStateDB } from '../public/public_db_sources.js'; import { type PublicSideEffectTraceInterface } from '../public/side_effect_trace_interface.js'; -export function mockGetBytecode(worldStateDB: WorldStateDB, bytecode: Buffer) { +export async function mockGetBytecode(worldStateDB: WorldStateDB, bytecode: Buffer) { + const commitment = await computePublicBytecodeCommitment(bytecode); (worldStateDB as jest.Mocked).getBytecode.mockResolvedValue(bytecode); - (worldStateDB as jest.Mocked).getBytecodeCommitment.mockResolvedValue( - computePublicBytecodeCommitment(bytecode), - ); + (worldStateDB as jest.Mocked).getBytecodeCommitment.mockResolvedValue(commitment); } export function mockTraceFork(trace: PublicSideEffectTraceInterface, nestedTrace?: PublicSideEffectTraceInterface) { diff --git a/yarn-project/simulator/src/client/client_execution_context.ts b/yarn-project/simulator/src/client/client_execution_context.ts index 69ea55c2724..2f66724b614 100644 --- a/yarn-project/simulator/src/client/client_execution_context.ts +++ b/yarn-project/simulator/src/client/client_execution_context.ts @@ -166,7 +166,7 @@ export class ClientExecutionContext extends ViewDataOracle { * @returns The hash of the values. */ public override storeInExecutionCache(values: Fr[]): Promise { - return Promise.resolve(this.executionCache.store(values)); + return this.executionCache.store(values); } /** @@ -241,14 +241,22 @@ export class ClientExecutionContext extends ViewDataOracle { .join(', ')}`, ); - notes.forEach(n => { - if (n.index !== undefined) { - const siloedNoteHash = siloNoteHash(n.contractAddress, n.noteHash); - const uniqueNoteHash = computeUniqueNoteHash(n.nonce, siloedNoteHash); + const noteHashesAndIndexes = await Promise.all( + notes.map(async n => { + if (n.index !== undefined) { + const siloedNoteHash = await siloNoteHash(n.contractAddress, n.noteHash); + const uniqueNoteHash = await computeUniqueNoteHash(n.nonce, siloedNoteHash); - this.noteHashLeafIndexMap.set(uniqueNoteHash.toBigInt(), n.index); - } - }); + return { hash: uniqueNoteHash, index: n.index }; + } + }), + ); + + noteHashesAndIndexes + .filter(n => n !== undefined) + .forEach(n => { + this.noteHashLeafIndexMap.set(n!.hash.toBigInt(), n!.index); + }); return notes; } @@ -291,8 +299,8 @@ export class ClientExecutionContext extends ViewDataOracle { * @param innerNullifier - The pending nullifier to add in the list (not yet siloed by contract address). * @param noteHash - A hash of the new note. */ - public override notifyNullifiedNote(innerNullifier: Fr, noteHash: Fr, counter: number) { - const nullifiedNoteHashCounter = this.noteCache.nullifyNote( + public override async notifyNullifiedNote(innerNullifier: Fr, noteHash: Fr, counter: number) { + const nullifiedNoteHashCounter = await this.noteCache.nullifyNote( this.callContext.contractAddress, innerNullifier, noteHash, @@ -300,7 +308,6 @@ export class ClientExecutionContext extends ViewDataOracle { if (nullifiedNoteHashCounter !== undefined) { this.noteHashNullifierCounterMap.set(nullifiedNoteHashCounter, counter); } - return Promise.resolve(); } /** @@ -310,8 +317,7 @@ export class ClientExecutionContext extends ViewDataOracle { * @param noteHash - A hash of the new note. */ public override notifyCreatedNullifier(innerNullifier: Fr) { - this.noteCache.nullifierCreated(this.callContext.contractAddress, innerNullifier); - return Promise.resolve(); + return this.noteCache.nullifierCreated(this.callContext.contractAddress, innerNullifier); } /** @@ -369,7 +375,7 @@ export class ClientExecutionContext extends ViewDataOracle { const derivedTxContext = this.txContext.clone(); - const derivedCallContext = this.deriveCallContext(targetContractAddress, targetArtifact, isStaticCall); + const derivedCallContext = await this.deriveCallContext(targetContractAddress, targetArtifact, isStaticCall); const context = new ClientExecutionContext( argsHash, @@ -426,7 +432,7 @@ export class ClientExecutionContext extends ViewDataOracle { isStaticCall: boolean, ) { const targetArtifact = await this.db.getFunctionArtifact(targetContractAddress, functionSelector); - const derivedCallContext = this.deriveCallContext(targetContractAddress, targetArtifact, isStaticCall); + const derivedCallContext = await this.deriveCallContext(targetContractAddress, targetArtifact, isStaticCall); const args = this.executionCache.getPreimage(argsHash); this.log.verbose( @@ -476,7 +482,7 @@ export class ClientExecutionContext extends ViewDataOracle { // new_args = [selector, ...old_args], so as to make it suitable to call the public dispatch function. // We don't validate or compute it in the circuit because a) it's harder to do with slices, and // b) this is only temporary. - const newArgsHash = this.executionCache.store([ + const newArgsHash = await this.executionCache.store([ functionSelector.toField(), ...this.executionCache.getPreimage(argsHash), ]); @@ -515,7 +521,7 @@ export class ClientExecutionContext extends ViewDataOracle { // new_args = [selector, ...old_args], so as to make it suitable to call the public dispatch function. // We don't validate or compute it in the circuit because a) it's harder to do with slices, and // b) this is only temporary. - const newArgsHash = this.executionCache.store([ + const newArgsHash = await this.executionCache.store([ functionSelector.toField(), ...this.executionCache.getPreimage(argsHash), ]); @@ -530,8 +536,8 @@ export class ClientExecutionContext extends ViewDataOracle { return newArgsHash; } - public override notifySetMinRevertibleSideEffectCounter(minRevertibleSideEffectCounter: number): void { - this.noteCache.setMinRevertibleSideEffectCounter(minRevertibleSideEffectCounter); + public override notifySetMinRevertibleSideEffectCounter(minRevertibleSideEffectCounter: number): Promise { + return this.noteCache.setMinRevertibleSideEffectCounter(minRevertibleSideEffectCounter); } /** @@ -541,7 +547,7 @@ export class ClientExecutionContext extends ViewDataOracle { * @param isStaticCall - Whether the call is a static call. * @returns The derived call context. */ - private deriveCallContext( + private async deriveCallContext( targetContractAddress: AztecAddress, targetArtifact: FunctionArtifact, isStaticCall = false, @@ -549,7 +555,7 @@ export class ClientExecutionContext extends ViewDataOracle { return new CallContext( this.contractAddress, targetContractAddress, - FunctionSelector.fromNameAndParameters(targetArtifact.name, targetArtifact.parameters), + await FunctionSelector.fromNameAndParameters(targetArtifact.name, targetArtifact.parameters), isStaticCall, ); } diff --git a/yarn-project/simulator/src/client/execution_note_cache.ts b/yarn-project/simulator/src/client/execution_note_cache.ts index ed651031bcc..7d6f6e12533 100644 --- a/yarn-project/simulator/src/client/execution_note_cache.ts +++ b/yarn-project/simulator/src/client/execution_note_cache.ts @@ -52,7 +52,7 @@ export class ExecutionNoteCache { * Enters the revertible phase of the transaction. * @param minRevertibleSideEffectCounter - The counter at which the transaction enters the revertible phase. */ - public setMinRevertibleSideEffectCounter(minRevertibleSideEffectCounter: number) { + public async setMinRevertibleSideEffectCounter(minRevertibleSideEffectCounter: number) { if (this.inRevertiblePhase) { throw new Error( `Cannot enter the revertible phase twice. Current counter: ${minRevertibleSideEffectCounter}. Previous enter counter: ${this.minRevertibleSideEffectCounter}`, @@ -72,15 +72,20 @@ export class ExecutionNoteCache { // They cannot be squashed by nullifiers emitted after minRevertibleSideEffectCounter is set. // Their indexes in the tx are known at this point and won't change. So we can assign a nonce to each one of them. // The nonces will be used to create the "complete" nullifier. - const updatedNotes = this.notes.map(({ note, counter }, i) => { - const nonce = computeNoteHashNonce(nonceGenerator, i); - const uniqueNoteHash = computeUniqueNoteHash(nonce, siloNoteHash(note.contractAddress, note.noteHash)); - return { - counter, - note: { ...note, nonce }, - noteHashForConsumption: uniqueNoteHash, - }; - }); + const updatedNotes = await Promise.all( + this.notes.map(async ({ note, counter }, i) => { + const nonce = await computeNoteHashNonce(nonceGenerator, i); + const uniqueNoteHash = await computeUniqueNoteHash( + nonce, + await siloNoteHash(note.contractAddress, note.noteHash), + ); + return { + counter, + note: { ...note, nonce }, + noteHashForConsumption: uniqueNoteHash, + }; + }), + ); // Rebuild the data. this.notes = []; this.noteMap = new Map(); @@ -120,8 +125,8 @@ export class ExecutionNoteCache { * @param noteHash - A hash of the note. If this value equals 0, it means the note being nullified is from a previous * transaction (and thus not a new note). */ - public nullifyNote(contractAddress: AztecAddress, innerNullifier: Fr, noteHash: Fr) { - const siloedNullifier = siloNullifier(contractAddress, innerNullifier); + public async nullifyNote(contractAddress: AztecAddress, innerNullifier: Fr, noteHash: Fr) { + const siloedNullifier = await siloNullifier(contractAddress, innerNullifier); let nullifiedNoteHashCounter: number | undefined = undefined; // Find and remove the matching new note and log(s) if the emitted noteHash is not empty. if (!noteHash.isEmpty()) { @@ -152,8 +157,8 @@ export class ExecutionNoteCache { * @param contractAddress - Contract address that emitted the nullifier. * @param innerNullifier */ - public nullifierCreated(contractAddress: AztecAddress, innerNullifier: Fr) { - const siloedNullifier = siloNullifier(contractAddress, innerNullifier); + public async nullifierCreated(contractAddress: AztecAddress, innerNullifier: Fr) { + const siloedNullifier = await siloNullifier(contractAddress, innerNullifier); this.recordNullifier(contractAddress, siloedNullifier); } diff --git a/yarn-project/simulator/src/client/private_execution.test.ts b/yarn-project/simulator/src/client/private_execution.test.ts index e02c4e1124b..c8797415dd8 100644 --- a/yarn-project/simulator/src/client/private_execution.test.ts +++ b/yarn-project/simulator/src/client/private_execution.test.ts @@ -45,6 +45,7 @@ import { type NoteSelector, encodeArguments, getFunctionArtifact, + getFunctionArtifactByName, } from '@aztec/foundation/abi'; import { asyncMap } from '@aztec/foundation/async-map'; import { AztecAddress } from '@aztec/foundation/aztec-address'; @@ -109,7 +110,7 @@ describe('Private Execution test suite', () => { gasSettings: GasSettings.default({ maxFeesPerGas: new GasFees(10, 10) }), }; - const runSimulator = ({ + const runSimulator = async ({ artifact, args = [], msgSender = AztecAddress.fromField(Fr.MAX_FIELD_VALUE), @@ -122,12 +123,12 @@ describe('Private Execution test suite', () => { args?: any[]; txContext?: Partial>; }) => { - const hashedArguments = HashedValues.fromValues(encodeArguments(artifact, args)); + const hashedArguments = await HashedValues.fromValues(encodeArguments(artifact, args)); contractAddress = contractAddress ?? defaultContractAddress; const txRequest = TxExecutionRequest.from({ origin: contractAddress, firstCallArgsHash: hashedArguments.hash, - functionSelector: FunctionSelector.fromNameAndParameters(artifact.name, artifact.parameters), + functionSelector: await FunctionSelector.fromNameAndParameters(artifact.name, artifact.parameters), txContext: TxContext.from({ ...txContextFields, ...txContext }), argsOfCalls: [hashedArguments], authWitnesses: [], @@ -204,20 +205,20 @@ describe('Private Execution test suite', () => { beforeEach(async () => { trees = {}; oracle = mock(); - oracle.getKeyValidationRequest.mockImplementation((pkMHash: Fr, contractAddress: AztecAddress) => { - if (pkMHash.equals(ownerCompleteAddress.publicKeys.masterNullifierPublicKey.hash())) { + oracle.getKeyValidationRequest.mockImplementation(async (pkMHash: Fr, contractAddress: AztecAddress) => { + if (pkMHash.equals(await ownerCompleteAddress.publicKeys.masterNullifierPublicKey.hash())) { return Promise.resolve( new KeyValidationRequest( ownerCompleteAddress.publicKeys.masterNullifierPublicKey, - computeAppNullifierSecretKey(ownerNskM, contractAddress), + await computeAppNullifierSecretKey(ownerNskM, contractAddress), ), ); } - if (pkMHash.equals(recipientCompleteAddress.publicKeys.masterNullifierPublicKey.hash())) { + if (pkMHash.equals(await recipientCompleteAddress.publicKeys.masterNullifierPublicKey.hash())) { return Promise.resolve( new KeyValidationRequest( recipientCompleteAddress.publicKeys.masterNullifierPublicKey, - computeAppNullifierSecretKey(recipientNskM, contractAddress), + await computeAppNullifierSecretKey(recipientNskM, contractAddress), ), ); } @@ -260,7 +261,7 @@ describe('Private Execution test suite', () => { it('emits a field array as an encrypted log', async () => { // NB: this test does NOT cover correct enc/dec of values, just whether // the contexts correctly populate non-note encrypted logs - const artifact = getFunctionArtifact(TestContractArtifact, 'emit_array_as_encrypted_log'); + const artifact = getFunctionArtifactByName(TestContractArtifact, 'emit_array_as_encrypted_log'); const sender = recipient; // Needed for tagging. const args = [times(5, () => Fr.random()), owner, sender, false]; const result = await runSimulator({ artifact, msgSender: owner, args }); @@ -277,7 +278,7 @@ describe('Private Execution test suite', () => { const mockFirstNullifier = new Fr(1111); let currentNoteIndex = 0n; - const buildNote = (amount: bigint, ownerAddress: AztecAddress, storageSlot: Fr, noteTypeId: NoteSelector) => { + const buildNote = async (amount: bigint, ownerAddress: AztecAddress, storageSlot: Fr, noteTypeId: NoteSelector) => { // WARNING: this is not actually how nonces are computed! // For the purpose of this test we use a mocked firstNullifier and and a random number // to compute the nonce. Proper nonces are only enforced later by the kernel/later circuits @@ -287,10 +288,10 @@ describe('Private Execution test suite', () => { // array index at the output of the final kernel/ordering circuit are used to derive nonce via: // `hash(firstNullifier, noteHashIndex)` const noteHashIndex = randomInt(1); // mock index in TX's final noteHashes array - const nonce = computeNoteHashNonce(mockFirstNullifier, noteHashIndex); + const nonce = await computeNoteHashNonce(mockFirstNullifier, noteHashIndex); const note = new Note([new Fr(amount), ownerAddress.toField(), Fr.random()]); // Note: The following does not correspond to how note hashing is generally done in real notes. - const noteHash = poseidon2Hash([storageSlot, ...note.items]); + const noteHash = await poseidon2Hash([storageSlot, ...note.items]); return { contractAddress, storageSlot, @@ -306,7 +307,7 @@ describe('Private Execution test suite', () => { beforeEach(async () => { contractAddress = await AztecAddress.random(); oracle.getFunctionArtifactByName.mockImplementation((_, functionName: string) => - Promise.resolve(getFunctionArtifact(StatefulTestContractArtifact, functionName)), + Promise.resolve(getFunctionArtifactByName(StatefulTestContractArtifact, functionName)), ); oracle.getFunctionArtifact.mockImplementation((_, selector: FunctionSelector) => @@ -320,13 +321,13 @@ describe('Private Execution test suite', () => { constructorArgs: initArgs, }); oracle.getContractInstance.mockResolvedValue(instance); - const artifact = getFunctionArtifact(StatefulTestContractArtifact, 'constructor'); + const artifact = getFunctionArtifactByName(StatefulTestContractArtifact, 'constructor'); const executionResult = await runSimulator({ args: initArgs, artifact, contractAddress: instance.address }); const result = executionResult.entrypoint.nestedExecutions[0]; expect(result.newNotes).toHaveLength(1); const newNote = result.newNotes[0]; - expect(newNote.storageSlot).toEqual(deriveStorageSlotInMap(new Fr(1n), owner)); + expect(newNote.storageSlot).toEqual(await deriveStorageSlotInMap(new Fr(1n), owner)); expect(newNote.noteTypeId).toEqual(valueNoteTypeId); // ValueNote const noteHashes = getNonEmptyItems(result.publicInputs.noteHashes); @@ -340,13 +341,13 @@ describe('Private Execution test suite', () => { }); it('should run the create_note function', async () => { - const artifact = getFunctionArtifact(StatefulTestContractArtifact, 'create_note_no_init_check'); + const artifact = getFunctionArtifactByName(StatefulTestContractArtifact, 'create_note_no_init_check'); const { entrypoint: result } = await runSimulator({ args: [owner, owner, 140], artifact }); expect(result.newNotes).toHaveLength(1); const newNote = result.newNotes[0]; - expect(newNote.storageSlot).toEqual(deriveStorageSlotInMap(new Fr(1n), owner)); + expect(newNote.storageSlot).toEqual(await deriveStorageSlotInMap(new Fr(1n), owner)); expect(newNote.noteTypeId).toEqual(valueNoteTypeId); // ValueNote const noteHashes = getNonEmptyItems(result.publicInputs.noteHashes); @@ -361,18 +362,18 @@ describe('Private Execution test suite', () => { it('should run the destroy_and_create function', async () => { const amountToTransfer = 100n; - const artifact = getFunctionArtifact(StatefulTestContractArtifact, 'destroy_and_create_no_init_check'); + const artifact = getFunctionArtifactByName(StatefulTestContractArtifact, 'destroy_and_create_no_init_check'); - const storageSlot = deriveStorageSlotInMap(StatefulTestContractArtifact.storageLayout['notes'].slot, owner); - const recipientStorageSlot = deriveStorageSlotInMap( + const storageSlot = await deriveStorageSlotInMap(StatefulTestContractArtifact.storageLayout['notes'].slot, owner); + const recipientStorageSlot = await deriveStorageSlotInMap( StatefulTestContractArtifact.storageLayout['notes'].slot, recipient, ); - const notes = [ + const notes = await Promise.all([ buildNote(60n, ownerCompleteAddress.address, storageSlot, valueNoteTypeId), buildNote(80n, ownerCompleteAddress.address, storageSlot, valueNoteTypeId), - ]; + ]); oracle.syncTaggedLogs.mockResolvedValue(new Map()); oracle.processTaggedLogs.mockResolvedValue(); oracle.getNotes.mockResolvedValue(notes); @@ -402,7 +403,10 @@ describe('Private Execution test suite', () => { expect(nullifiers).toHaveLength(consumedNotes.length); expect(nullifiers).toEqual(expect.arrayContaining(consumedNotes.map(n => n.innerNullifier))); // Uses one of the notes as first nullifier, not requiring a protocol injected nullifier. - expect(consumedNotes.map(n => siloNullifier(contractAddress, n.innerNullifier))).toContainEqual(firstNullifier); + const consumedNotesNullifiers = await Promise.all( + consumedNotes.map(n => siloNullifier(contractAddress, n.innerNullifier)), + ); + expect(consumedNotesNullifiers).toContainEqual(firstNullifier); expect(result.newNotes).toHaveLength(2); const [changeNote, recipientNote] = result.newNotes; @@ -433,11 +437,11 @@ describe('Private Execution test suite', () => { it('should be able to destroy_and_create with dummy notes', async () => { const amountToTransfer = 100n; const balance = 160n; - const artifact = getFunctionArtifact(StatefulTestContractArtifact, 'destroy_and_create_no_init_check'); + const artifact = getFunctionArtifactByName(StatefulTestContractArtifact, 'destroy_and_create_no_init_check'); - const storageSlot = deriveStorageSlotInMap(new Fr(1n), owner); + const storageSlot = await deriveStorageSlotInMap(new Fr(1n), owner); - const notes = [buildNote(balance, ownerCompleteAddress.address, storageSlot, valueNoteTypeId)]; + const notes = await Promise.all([buildNote(balance, ownerCompleteAddress.address, storageSlot, valueNoteTypeId)]); oracle.syncTaggedLogs.mockResolvedValue(new Map()); oracle.processTaggedLogs.mockResolvedValue(); oracle.getNotes.mockResolvedValue(notes); @@ -475,18 +479,18 @@ describe('Private Execution test suite', () => { it('child function should be callable', async () => { const initialValue = 100n; - const artifact = getFunctionArtifact(ChildContractArtifact, 'value'); + const artifact = getFunctionArtifactByName(ChildContractArtifact, 'value'); const { entrypoint: result } = await runSimulator({ args: [initialValue], artifact }); expect(result.returnValues).toEqual([new Fr(initialValue + privateIncrement)]); }); it('parent should call child', async () => { - const childArtifact = getFunctionArtifact(ChildContractArtifact, 'value'); - const parentArtifact = getFunctionArtifact(ParentContractArtifact, 'entry_point'); + const childArtifact = getFunctionArtifactByName(ChildContractArtifact, 'value'); + const parentArtifact = getFunctionArtifactByName(ParentContractArtifact, 'entry_point'); const parentAddress = await AztecAddress.random(); const childAddress = await AztecAddress.random(); - const childSelector = FunctionSelector.fromNameAndParameters(childArtifact.name, childArtifact.parameters); + const childSelector = await FunctionSelector.fromNameAndParameters(childArtifact.name, childArtifact.parameters); oracle.getFunctionArtifact.mockImplementation(() => Promise.resolve(childArtifact)); @@ -512,16 +516,16 @@ describe('Private Execution test suite', () => { let argsHash: Fr; let testCodeGenArtifact: FunctionArtifact; - beforeAll(() => { + beforeAll(async () => { // These args should match the ones hardcoded in importer contract // eslint-disable-next-line camelcase const dummyNote = { amount: 1, secret_hash: 2 }; // eslint-disable-next-line camelcase const deepStruct = { a_field: 1, a_bool: true, a_note: dummyNote, many_notes: [dummyNote, dummyNote, dummyNote] }; args = [1, true, 1, [1, 2], dummyNote, deepStruct]; - testCodeGenArtifact = getFunctionArtifact(TestContractArtifact, 'test_code_gen'); + testCodeGenArtifact = getFunctionArtifactByName(TestContractArtifact, 'test_code_gen'); const serializedArgs = encodeArguments(testCodeGenArtifact, args); - argsHash = computeVarArgsHash(serializedArgs); + argsHash = await computeVarArgsHash(serializedArgs); }); it('test function should be directly callable', async () => { @@ -533,8 +537,8 @@ describe('Private Execution test suite', () => { it('test function should be callable through autogenerated interface', async () => { const testAddress = await AztecAddress.random(); - const parentArtifact = getFunctionArtifact(ImportTestContractArtifact, 'main_contract'); - const testCodeGenSelector = FunctionSelector.fromNameAndParameters( + const parentArtifact = getFunctionArtifactByName(ImportTestContractArtifact, 'main_contract'); + const testCodeGenSelector = await FunctionSelector.fromNameAndParameters( testCodeGenArtifact.name, testCodeGenArtifact.parameters, ); @@ -559,7 +563,7 @@ describe('Private Execution test suite', () => { contractAddress = await AztecAddress.random(); }); describe('L1 to L2', () => { - const artifact = getFunctionArtifact(TestContractArtifact, 'consume_mint_to_private_message'); + const artifact = getFunctionArtifactByName(TestContractArtifact, 'consume_mint_to_private_message'); let bridgedAmount = 100n; const l1ToL2MessageIndex = 0; @@ -607,7 +611,7 @@ describe('Private Execution test suite', () => { }; it('Should be able to consume a dummy cross chain message', async () => { - preimage = computePreimage(); + preimage = await computePreimage(); args = computeArgs(); await mockOracles(); @@ -624,7 +628,7 @@ describe('Private Execution test suite', () => { }); it('Invalid membership proof', async () => { - preimage = computePreimage(); + preimage = await computePreimage(); args = computeArgs(); @@ -644,7 +648,7 @@ describe('Private Execution test suite', () => { it('Invalid recipient', async () => { crossChainMsgRecipient = await AztecAddress.random(); - preimage = computePreimage(); + preimage = await computePreimage(); args = computeArgs(); @@ -664,7 +668,7 @@ describe('Private Execution test suite', () => { it('Invalid sender', async () => { crossChainMsgSender = EthAddress.random(); - preimage = computePreimage(); + preimage = await computePreimage(); args = computeArgs(); @@ -683,7 +687,7 @@ describe('Private Execution test suite', () => { }); it('Invalid chainid', async () => { - preimage = computePreimage(); + preimage = await computePreimage(); args = computeArgs(); @@ -702,7 +706,7 @@ describe('Private Execution test suite', () => { }); it('Invalid version', async () => { - preimage = computePreimage(); + preimage = await computePreimage(); args = computeArgs(); @@ -721,7 +725,7 @@ describe('Private Execution test suite', () => { }); it('Invalid content', async () => { - preimage = computePreimage(); + preimage = await computePreimage(); bridgedAmount = bridgedAmount + 1n; // Invalid amount args = computeArgs(); @@ -741,7 +745,7 @@ describe('Private Execution test suite', () => { }); it('Invalid Secret', async () => { - preimage = computePreimage(); + preimage = await computePreimage(); secretForL1ToL2MessageConsumption = Fr.random(); args = computeArgs(); @@ -762,9 +766,9 @@ describe('Private Execution test suite', () => { }); it('Should be able to consume a dummy public to private message', async () => { - const artifact = getFunctionArtifact(TestContractArtifact, 'consume_note_from_secret'); + const artifact = getFunctionArtifactByName(TestContractArtifact, 'consume_note_from_secret'); const secret = new Fr(1n); - const secretHash = computeSecretHash(secret); + const secretHash = await computeSecretHash(secret); const note = new Note([secretHash]); const storageSlot = TestContractArtifact.storageLayout['example_set'].slot; oracle.syncTaggedLogs.mockResolvedValue(new Map()); @@ -795,11 +799,11 @@ describe('Private Execution test suite', () => { describe('enqueued calls', () => { it.each([false, true])('parent should enqueue call to child (internal %p)', async isInternal => { - const parentArtifact = getFunctionArtifact(ParentContractArtifact, 'enqueue_call_to_child'); + const parentArtifact = getFunctionArtifactByName(ParentContractArtifact, 'enqueue_call_to_child'); const childContractArtifact = ChildContractArtifact.functions.find(fn => fn.name === 'public_dispatch')!; expect(childContractArtifact).toBeDefined(); const childAddress = await AztecAddress.random(); - const childSelector = FunctionSelector.fromSignature('pub_set_value(Field)'); + const childSelector = await FunctionSelector.fromSignature('pub_set_value(Field)'); const parentAddress = await AztecAddress.random(); oracle.getFunctionArtifact.mockImplementation(() => Promise.resolve({ ...childContractArtifact, isInternal })); @@ -831,13 +835,13 @@ describe('Private Execution test suite', () => { describe('setting teardown function', () => { it('should be able to set a teardown function', async () => { - const entrypoint = getFunctionArtifact(TestContractArtifact, 'test_setting_teardown'); - const teardown = getFunctionArtifact(TestContractArtifact, 'dummy_public_call'); + const entrypoint = getFunctionArtifactByName(TestContractArtifact, 'test_setting_teardown'); + const teardown = getFunctionArtifactByName(TestContractArtifact, 'dummy_public_call'); oracle.getFunctionArtifact.mockImplementation(() => Promise.resolve({ ...teardown })); const { entrypoint: result } = await runSimulator({ artifact: entrypoint }); expect(result.publicTeardownFunctionCall.isEmpty()).toBeFalsy(); expect(result.publicTeardownFunctionCall.callContext.functionSelector).toEqual( - FunctionSelector.fromNameAndParameters(teardown.name, teardown.parameters), + await FunctionSelector.fromNameAndParameters(teardown.name, teardown.parameters), ); }); }); @@ -845,14 +849,14 @@ describe('Private Execution test suite', () => { describe('setting fee payer', () => { it('should default to not being a fee payer', async () => { // arbitrary random function that doesn't set a fee payer - const entrypoint = getFunctionArtifact(TestContractArtifact, 'get_this_address'); + const entrypoint = getFunctionArtifactByName(TestContractArtifact, 'get_this_address'); const contractAddress = await AztecAddress.random(); const { entrypoint: result } = await runSimulator({ artifact: entrypoint, contractAddress }); expect(result.publicInputs.isFeePayer).toBe(false); }); it('should be able to set a fee payer', async () => { - const entrypoint = getFunctionArtifact(TestContractArtifact, 'test_setting_fee_payer'); + const entrypoint = getFunctionArtifactByName(TestContractArtifact, 'test_setting_fee_payer'); const contractAddress = await AztecAddress.random(); const { entrypoint: result } = await runSimulator({ artifact: entrypoint, contractAddress }); expect(result.publicInputs.isFeePayer).toBe(true); @@ -879,7 +883,10 @@ describe('Private Execution test suite', () => { const amountToTransfer = 100n; const contractAddress = await AztecAddress.random(); - const artifact = getFunctionArtifact(PendingNoteHashesContractArtifact, 'test_insert_then_get_then_nullify_flat'); + const artifact = getFunctionArtifactByName( + PendingNoteHashesContractArtifact, + 'test_insert_then_get_then_nullify_flat', + ); const sender = owner; const args = [amountToTransfer, owner, sender]; @@ -891,7 +898,7 @@ describe('Private Execution test suite', () => { expect(result.newNotes).toHaveLength(1); const noteAndSlot = result.newNotes[0]; - expect(noteAndSlot.storageSlot).toEqual(deriveStorageSlotInMap(new Fr(1n), owner)); + expect(noteAndSlot.storageSlot).toEqual(await deriveStorageSlotInMap(new Fr(1n), owner)); expect(noteAndSlot.note.items[0]).toEqual(new Fr(amountToTransfer)); @@ -899,7 +906,7 @@ describe('Private Execution test suite', () => { expect(noteHashesFromCall).toHaveLength(1); const noteHashFromCall = noteHashesFromCall[0].value; - const storageSlot = deriveStorageSlotInMap( + const storageSlot = await deriveStorageSlotInMap( PendingNoteHashesContractArtifact.storageLayout['balances'].slot, owner, ); @@ -922,8 +929,8 @@ describe('Private Execution test suite', () => { expect(result.returnValues).toEqual([new Fr(amountToTransfer)]); const nullifier = result.publicInputs.nullifiers[0]; - const expectedNullifier = poseidon2HashWithSeparator( - [derivedNoteHash, computeAppNullifierSecretKey(ownerNskM, contractAddress)], + const expectedNullifier = await poseidon2HashWithSeparator( + [derivedNoteHash, await computeAppNullifierSecretKey(ownerNskM, contractAddress)], GeneratorIndex.NOTE_NULLIFIER, ); expect(nullifier.value).toEqual(expectedNullifier); @@ -937,16 +944,22 @@ describe('Private Execution test suite', () => { const amountToTransfer = 100n; const contractAddress = await AztecAddress.random(); - const artifact = getFunctionArtifact( + const artifact = getFunctionArtifactByName( PendingNoteHashesContractArtifact, 'test_insert_then_get_then_nullify_all_in_nested_calls', ); - const insertArtifact = getFunctionArtifact(PendingNoteHashesContractArtifact, 'insert_note'); + const insertArtifact = getFunctionArtifactByName(PendingNoteHashesContractArtifact, 'insert_note'); - const getThenNullifyArtifact = getFunctionArtifact(PendingNoteHashesContractArtifact, 'get_then_nullify_note'); + const getThenNullifyArtifact = getFunctionArtifactByName( + PendingNoteHashesContractArtifact, + 'get_then_nullify_note', + ); - const insertFnSelector = FunctionSelector.fromNameAndParameters(insertArtifact.name, insertArtifact.parameters); - const getThenNullifyFnSelector = FunctionSelector.fromNameAndParameters( + const insertFnSelector = await FunctionSelector.fromNameAndParameters( + insertArtifact.name, + insertArtifact.parameters, + ); + const getThenNullifyFnSelector = await FunctionSelector.fromNameAndParameters( getThenNullifyArtifact.name, getThenNullifyArtifact.parameters, ); @@ -962,7 +975,7 @@ describe('Private Execution test suite', () => { const execInsert = result.nestedExecutions[0]; const execGetThenNullify = result.nestedExecutions[1]; - const storageSlot = deriveStorageSlotInMap( + const storageSlot = await deriveStorageSlotInMap( PendingNoteHashesContractArtifact.storageLayout['balances'].slot, owner, ); @@ -995,8 +1008,8 @@ describe('Private Execution test suite', () => { expect(execGetThenNullify.returnValues).toEqual([new Fr(amountToTransfer)]); const nullifier = execGetThenNullify.publicInputs.nullifiers[0]; - const expectedNullifier = poseidon2HashWithSeparator( - [derivedNoteHash, computeAppNullifierSecretKey(ownerNskM, contractAddress)], + const expectedNullifier = await poseidon2HashWithSeparator( + [derivedNoteHash, await computeAppNullifierSecretKey(ownerNskM, contractAddress)], GeneratorIndex.NOTE_NULLIFIER, ); expect(nullifier.value).toEqual(expectedNullifier); @@ -1011,7 +1024,7 @@ describe('Private Execution test suite', () => { const contractAddress = await AztecAddress.random(); - const artifact = getFunctionArtifact(PendingNoteHashesContractArtifact, 'test_bad_get_then_insert_flat'); + const artifact = getFunctionArtifactByName(PendingNoteHashesContractArtifact, 'test_bad_get_then_insert_flat'); const args = [amountToTransfer, owner]; // This will throw if we read the note before it was inserted @@ -1026,7 +1039,7 @@ describe('Private Execution test suite', () => { describe('get master incoming viewing public key', () => { it('gets the public key for an address', async () => { // Tweak the contract artifact so we can extract return values - const artifact = getFunctionArtifact(TestContractArtifact, 'get_master_incoming_viewing_public_key'); + const artifact = getFunctionArtifactByName(TestContractArtifact, 'get_master_incoming_viewing_public_key'); // Generate a partial address, pubkey, and resulting address const completeAddress = await CompleteAddress.random(); @@ -1041,7 +1054,7 @@ describe('Private Execution test suite', () => { describe('Get notes', () => { it('fails if returning no notes', async () => { - const artifact = getFunctionArtifact(TestContractArtifact, 'call_get_notes'); + const artifact = getFunctionArtifactByName(TestContractArtifact, 'call_get_notes'); const args = [2n, true]; oracle.syncTaggedLogs.mockResolvedValue(new Map()); @@ -1059,7 +1072,7 @@ describe('Private Execution test suite', () => { const contractAddress = await AztecAddress.random(); // Tweak the contract artifact so we can extract return values - const artifact = getFunctionArtifact(TestContractArtifact, 'get_this_address'); + const artifact = getFunctionArtifactByName(TestContractArtifact, 'get_this_address'); // Overwrite the oracle return value const { entrypoint: result } = await runSimulator({ artifact, args: [], contractAddress }); @@ -1078,7 +1091,7 @@ describe('Private Execution test suite', () => { version = Fr.random(); args = [chainId, version]; - artifact = getFunctionArtifact(TestContractArtifact, 'assert_private_global_vars'); + artifact = getFunctionArtifactByName(TestContractArtifact, 'assert_private_global_vars'); oracle.getFunctionArtifact.mockImplementation(() => Promise.resolve(artifact)); }); @@ -1108,7 +1121,7 @@ describe('Private Execution test suite', () => { let artifact: FunctionArtifact; beforeEach(() => { - artifact = getFunctionArtifact(TestContractArtifact, 'assert_header_private'); + artifact = getFunctionArtifactByName(TestContractArtifact, 'assert_header_private'); oracle.getFunctionArtifact.mockImplementation(() => Promise.resolve(artifact)); header = makeHeader(); @@ -1118,7 +1131,7 @@ describe('Private Execution test suite', () => { }); it('Header is correctly set', async () => { - const args = [header.hash()]; + const args = [await header.hash()]; await runSimulator({ artifact, msgSender: owner, args }); }); diff --git a/yarn-project/simulator/src/client/simulator.test.ts b/yarn-project/simulator/src/client/simulator.test.ts index c6adcf3d85f..38b4f3308b0 100644 --- a/yarn-project/simulator/src/client/simulator.test.ts +++ b/yarn-project/simulator/src/client/simulator.test.ts @@ -1,6 +1,6 @@ import { type AztecNode, CompleteAddress, Note } from '@aztec/circuit-types'; import { KeyValidationRequest, computeAppNullifierSecretKey, deriveKeys } from '@aztec/circuits.js'; -import { type FunctionArtifact, getFunctionArtifact } from '@aztec/foundation/abi'; +import { type FunctionArtifact, getFunctionArtifactByName } from '@aztec/foundation/abi'; import { AztecAddress } from '@aztec/foundation/aztec-address'; import { Fr, type Point } from '@aztec/foundation/fields'; import { TokenBlacklistContractArtifact } from '@aztec/noir-contracts.js/TokenBlacklist'; @@ -34,7 +34,7 @@ describe('Simulator', () => { const ownerPartialAddress = Fr.random(); const ownerCompleteAddress = CompleteAddress.fromSecretKeyAndPartialAddress(ownerSk, ownerPartialAddress); - appNullifierSecretKey = computeAppNullifierSecretKey(ownerMasterNullifierSecretKey, contractAddress); + appNullifierSecretKey = await computeAppNullifierSecretKey(ownerMasterNullifierSecretKey, contractAddress); oracle = mock(); node = mock(); @@ -47,7 +47,7 @@ describe('Simulator', () => { }); describe('computeNoteHashAndOptionallyANullifier', () => { - const artifact = getFunctionArtifact( + const artifact = getFunctionArtifactByName( TokenBlacklistContractArtifact, 'compute_note_hash_and_optionally_a_nullifier', ); @@ -56,20 +56,20 @@ describe('Simulator', () => { const noteTypeId = TokenBlacklistContractArtifact.notes['TokenNote'].id; // Amount is a U128, with a lo and hi limbs - const createNote = (amount = 123n) => - new Note([new Fr(amount), new Fr(0), ownerMasterNullifierPublicKey.hash(), Fr.random()]); + const createNote = async (amount = 123n) => + new Note([new Fr(amount), new Fr(0), await ownerMasterNullifierPublicKey.hash(), Fr.random()]); it('throw if the contract does not implement "compute_note_hash_and_optionally_a_nullifier"', async () => { oracle.getFunctionArtifactByName.mockResolvedValue(undefined); - const note = createNote(); + const note = await createNote(); await expect( simulator.computeNoteHashAndOptionallyANullifier(contractAddress, nonce, storageSlot, noteTypeId, true, note), ).rejects.toThrow(/Mandatory implementation of "compute_note_hash_and_optionally_a_nullifier" missing/); }); it('throw if "compute_note_hash_and_optionally_a_nullifier" has the wrong number of parameters', async () => { - const note = createNote(); + const note = await createNote(); const modifiedArtifact: FunctionArtifact = { ...artifact, @@ -87,7 +87,7 @@ describe('Simulator', () => { }); it('throw if a note has more fields than "compute_note_hash_and_optionally_a_nullifier" can process', async () => { - const note = createNote(); + const note = await createNote(); const wrongPreimageLength = note.length - 1; const modifiedArtifact: FunctionArtifact = { diff --git a/yarn-project/simulator/src/client/simulator.ts b/yarn-project/simulator/src/client/simulator.ts index 9a77707fcd5..381552e0725 100644 --- a/yarn-project/simulator/src/client/simulator.ts +++ b/yarn-project/simulator/src/client/simulator.ts @@ -72,11 +72,11 @@ export class AcirSimulator { const callContext = new CallContext( msgSender, contractAddress, - FunctionSelector.fromNameAndParameters(entryPointArtifact.name, entryPointArtifact.parameters), + await FunctionSelector.fromNameAndParameters(entryPointArtifact.name, entryPointArtifact.parameters), entryPointArtifact.isStatic, ); - const txRequestHash = request.toTxRequest().hash(); + const txRequestHash = await request.toTxRequest().hash(); const noteCache = new ExecutionNoteCache(txRequestHash); const context = new ClientExecutionContext( @@ -192,7 +192,7 @@ export class AcirSimulator { const execRequest: FunctionCall = { name: artifact.name, to: contractAddress, - selector: FunctionSelector.fromNameAndParameters(artifact), + selector: await FunctionSelector.fromNameAndParameters(artifact), type: FunctionType.UNCONSTRAINED, isStatic: artifact.isStatic, args: encodeArguments(artifact, [ diff --git a/yarn-project/simulator/src/client/view_data_oracle.ts b/yarn-project/simulator/src/client/view_data_oracle.ts index 64f2e2c5cec..0ca67be9f6e 100644 --- a/yarn-project/simulator/src/client/view_data_oracle.ts +++ b/yarn-project/simulator/src/client/view_data_oracle.ts @@ -230,7 +230,7 @@ export class ViewDataOracle extends TypedOracle { * @returns A boolean indicating whether the nullifier exists in the tree or not. */ public override async checkNullifierExists(innerNullifier: Fr) { - const nullifier = siloNullifier(this.contractAddress, innerNullifier!); + const nullifier = await siloNullifier(this.contractAddress, innerNullifier!); const index = await this.db.getNullifierIndex(nullifier); return index !== undefined; } diff --git a/yarn-project/simulator/src/common/hashed_values_cache.ts b/yarn-project/simulator/src/common/hashed_values_cache.ts index 34b7b545b28..22b4d15c6d0 100644 --- a/yarn-project/simulator/src/common/hashed_values_cache.ts +++ b/yarn-project/simulator/src/common/hashed_values_cache.ts @@ -32,7 +32,7 @@ export class HashedValuesCache { if (hash.equals(Fr.ZERO)) { return []; } - const hashedValues = this.cache.get(hash.value); + const hashedValues = this.cache.get(hash.toBigInt()); if (!hashedValues) { throw new Error(`Preimage for hash ${hash.toString()} not found in cache`); } @@ -44,12 +44,12 @@ export class HashedValuesCache { * @param values - The values to store. * @returns The hash of the values. */ - public store(values: Fr[]) { + public async store(values: Fr[]) { if (values.length === 0) { return Fr.ZERO; } - const hashedValues = HashedValues.fromValues(values); - this.cache.set(hashedValues.hash.value, hashedValues.values); + const hashedValues = await HashedValues.fromValues(values); + this.cache.set(hashedValues.hash.toBigInt(), hashedValues.values); return hashedValues.hash; } } diff --git a/yarn-project/simulator/src/public/enqueued_call_side_effect_trace.test.ts b/yarn-project/simulator/src/public/enqueued_call_side_effect_trace.test.ts index a3ca1010560..22bfc852b30 100644 --- a/yarn-project/simulator/src/public/enqueued_call_side_effect_trace.test.ts +++ b/yarn-project/simulator/src/public/enqueued_call_side_effect_trace.test.ts @@ -68,11 +68,11 @@ describe('Enqueued-call Side Effect Trace', () => { expect(trace.getAvmCircuitHints().publicDataReads.items).toEqual([expected]); }); - it('Should trace storage writes', () => { + it('Should trace storage writes', async () => { const lowLeafPreimage = new PublicDataTreeLeafPreimage(slot, value, Fr.ZERO, 0n); const newLeafPreimage = new PublicDataTreeLeafPreimage(slot, value, Fr.ZERO, 0n); - trace.tracePublicStorageWrite( + await trace.tracePublicStorageWrite( address, slot, value, @@ -85,7 +85,7 @@ describe('Enqueued-call Side Effect Trace', () => { ); expect(trace.getCounter()).toBe(startCounterPlus1); - const leafSlot = computePublicDataTreeLeafSlot(address, slot); + const leafSlot = await computePublicDataTreeLeafSlot(address, slot); const expected = [new PublicDataUpdateRequest(leafSlot, value, startCounter /*contractAddress*/)]; expect(trace.getSideEffects().publicDataWrites).toEqual(expected); @@ -211,14 +211,24 @@ describe('Enqueued-call Side Effect Trace', () => { }); describe('Maximum accesses', () => { - it('Should enforce maximum number of user public storage writes', () => { + it('Should enforce maximum number of user public storage writes', async () => { for (let i = 0; i < MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX; i++) { const lowLeafPreimage = new PublicDataTreeLeafPreimage(new Fr(i), new Fr(i), Fr.ZERO, 0n); const newLeafPreimage = new PublicDataTreeLeafPreimage(new Fr(i + 1), new Fr(i + 1), Fr.ZERO, 0n); - trace.tracePublicStorageWrite(address, slot, value, false, lowLeafPreimage, Fr.ZERO, [], newLeafPreimage, []); + await trace.tracePublicStorageWrite( + address, + slot, + value, + false, + lowLeafPreimage, + Fr.ZERO, + [], + newLeafPreimage, + [], + ); } const leafPreimage = new PublicDataTreeLeafPreimage(new Fr(42), new Fr(42), Fr.ZERO, 0n); - expect(() => + await expect( trace.tracePublicStorageWrite( AztecAddress.fromNumber(42), new Fr(42), @@ -230,9 +240,9 @@ describe('Enqueued-call Side Effect Trace', () => { leafPreimage, [], ), - ).toThrow(SideEffectLimitReachedError); + ).rejects.toThrow(SideEffectLimitReachedError); // Still allows protocol writes - expect(() => + await expect( trace.tracePublicStorageWrite( AztecAddress.fromNumber(42), new Fr(42), @@ -244,17 +254,27 @@ describe('Enqueued-call Side Effect Trace', () => { leafPreimage, [], ), - ).not.toThrow(); + ).resolves.not.toThrow(); }); - it('Should enforce maximum number of protocol public storage writes', () => { + it('Should enforce maximum number of protocol public storage writes', async () => { for (let i = 0; i < PROTOCOL_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX; i++) { const lowLeafPreimage = new PublicDataTreeLeafPreimage(new Fr(i), new Fr(i), Fr.ZERO, 0n); const newLeafPreimage = new PublicDataTreeLeafPreimage(new Fr(i + 1), new Fr(i + 1), Fr.ZERO, 0n); - trace.tracePublicStorageWrite(address, slot, value, true, lowLeafPreimage, Fr.ZERO, [], newLeafPreimage, []); + await trace.tracePublicStorageWrite( + address, + slot, + value, + true, + lowLeafPreimage, + Fr.ZERO, + [], + newLeafPreimage, + [], + ); } const leafPreimage = new PublicDataTreeLeafPreimage(new Fr(42), new Fr(42), Fr.ZERO, 0n); - expect(() => + await expect( trace.tracePublicStorageWrite( AztecAddress.fromNumber(42), new Fr(42), @@ -266,9 +286,9 @@ describe('Enqueued-call Side Effect Trace', () => { leafPreimage, [], ), - ).toThrow(SideEffectLimitReachedError); + ).rejects.toThrow(SideEffectLimitReachedError); // Still allows user writes - expect(() => + await expect( trace.tracePublicStorageWrite( AztecAddress.fromNumber(42), new Fr(42), @@ -280,7 +300,7 @@ describe('Enqueued-call Side Effect Trace', () => { leafPreimage, [], ), - ).not.toThrow(); + ).resolves.not.toThrow(); }); it('Should enforce maximum number of new note hashes', () => { @@ -350,7 +370,7 @@ describe('Enqueued-call Side Effect Trace', () => { trace.traceGetBytecode(differentAddr, /*exists=*/ false); }); - it('PreviousValidationRequestArrayLengths and PreviousAccumulatedDataArrayLengths contribute to limits', () => { + it('PreviousValidationRequestArrayLengths and PreviousAccumulatedDataArrayLengths contribute to limits', async () => { trace = new PublicEnqueuedCallSideEffectTrace( 0, new SideEffectArrayLengths( @@ -362,12 +382,12 @@ describe('Enqueued-call Side Effect Trace', () => { MAX_PUBLIC_LOGS_PER_TX, ), ); - expect(() => trace.tracePublicStorageWrite(AztecAddress.fromNumber(42), new Fr(42), new Fr(42), false)).toThrow( - SideEffectLimitReachedError, - ); - expect(() => trace.tracePublicStorageWrite(AztecAddress.fromNumber(42), new Fr(42), new Fr(42), true)).toThrow( - SideEffectLimitReachedError, - ); + await expect( + trace.tracePublicStorageWrite(AztecAddress.fromNumber(42), new Fr(42), new Fr(42), false), + ).rejects.toThrow(SideEffectLimitReachedError); + await expect( + trace.tracePublicStorageWrite(AztecAddress.fromNumber(42), new Fr(42), new Fr(42), true), + ).rejects.toThrow(SideEffectLimitReachedError); expect(() => trace.traceNewNoteHash(new Fr(42), new Fr(42))).toThrow(SideEffectLimitReachedError); expect(() => trace.traceNewNullifier(new Fr(42))).toThrow(SideEffectLimitReachedError); expect(() => trace.traceNewL2ToL1Message(AztecAddress.fromNumber(42), new Fr(42), new Fr(42))).toThrow( @@ -380,7 +400,7 @@ describe('Enqueued-call Side Effect Trace', () => { }); describe.each([false, true])('Should merge forked traces', reverted => { - it(`${reverted ? 'Reverted' : 'Successful'} forked trace should be merged properly`, () => { + it(`${reverted ? 'Reverted' : 'Successful'} forked trace should be merged properly`, async () => { const existsDefault = true; const nestedTrace = new PublicEnqueuedCallSideEffectTrace(startCounter); @@ -389,7 +409,17 @@ describe('Enqueued-call Side Effect Trace', () => { const lowLeafPreimage = new NullifierLeafPreimage(utxo, Fr.ZERO, 0n); nestedTrace.tracePublicStorageRead(address, slot, value, leafPreimage, Fr.ZERO, []); testCounter++; - nestedTrace.tracePublicStorageWrite(address, slot, value, false, leafPreimage, Fr.ZERO, [], leafPreimage, []); + await nestedTrace.tracePublicStorageWrite( + address, + slot, + value, + false, + leafPreimage, + Fr.ZERO, + [], + leafPreimage, + [], + ); testCounter++; nestedTrace.traceNoteHashCheck(address, utxo, leafIndex, existsDefault, []); // counter does not increment for note hash checks diff --git a/yarn-project/simulator/src/public/enqueued_call_side_effect_trace.ts b/yarn-project/simulator/src/public/enqueued_call_side_effect_trace.ts index 179ff3465b0..ef15ad99568 100644 --- a/yarn-project/simulator/src/public/enqueued_call_side_effect_trace.ts +++ b/yarn-project/simulator/src/public/enqueued_call_side_effect_trace.ts @@ -224,7 +224,7 @@ export class PublicEnqueuedCallSideEffectTrace implements PublicSideEffectTraceI this.incrementSideEffectCounter(); } - public tracePublicStorageWrite( + public async tracePublicStorageWrite( contractAddress: AztecAddress, slot: Fr, value: Fr, @@ -234,7 +234,7 @@ export class PublicEnqueuedCallSideEffectTrace implements PublicSideEffectTraceI lowLeafPath: Fr[] = emptyPublicDataPath(), newLeafPreimage: PublicDataTreeLeafPreimage = PublicDataTreeLeafPreimage.empty(), insertionPath: Fr[] = emptyPublicDataPath(), - ) { + ): Promise { if (protocolWrite) { if ( this.protocolPublicDataWritesLength + this.previousSideEffectArrayLengths.protocolPublicDataWrites >= @@ -259,7 +259,7 @@ export class PublicEnqueuedCallSideEffectTrace implements PublicSideEffectTraceI this.userPublicDataWritesLength++; } - const leafSlot = computePublicDataTreeLeafSlot(contractAddress, slot); + const leafSlot = await computePublicDataTreeLeafSlot(contractAddress, slot); this.publicDataWrites.push(new PublicDataUpdateRequest(leafSlot, value, this.sideEffectCounter)); // New hinting diff --git a/yarn-project/simulator/src/public/execution.ts b/yarn-project/simulator/src/public/execution.ts index c130619f047..543fb8299da 100644 --- a/yarn-project/simulator/src/public/execution.ts +++ b/yarn-project/simulator/src/public/execution.ts @@ -127,13 +127,13 @@ export interface PublicFunctionCallResult { functionName: string; } -export function resultToPublicCallRequest(result: PublicFunctionCallResult) { +export async function resultToPublicCallRequest(result: PublicFunctionCallResult) { const request = result.executionRequest; const item = new PublicCallStackItemCompressed( request.callContext.contractAddress, request.callContext, - computeVarArgsHash(request.args), - computeVarArgsHash(result.returnValues), + await computeVarArgsHash(request.args), + await computeVarArgsHash(result.returnValues), // TODO(@just-mitch): need better mapping from simulator to revert code. result.reverted ? RevertCode.APP_LOGIC_REVERTED : RevertCode.OK, Gas.from(result.startGasLeft), diff --git a/yarn-project/simulator/src/public/fee_payment.ts b/yarn-project/simulator/src/public/fee_payment.ts index e8879ffae47..bf055af3287 100644 --- a/yarn-project/simulator/src/public/fee_payment.ts +++ b/yarn-project/simulator/src/public/fee_payment.ts @@ -14,10 +14,10 @@ export function computeFeePayerBalanceStorageSlot(feePayer: AztecAddress) { /** * Computes the leaf slot in the public data tree for the balance of the fee payer in the Fee Juice. */ -export function computeFeePayerBalanceLeafSlot(feePayer: AztecAddress): Fr { +export async function computeFeePayerBalanceLeafSlot(feePayer: AztecAddress): Promise { if (feePayer.isZero()) { return Fr.ZERO; } - const balanceSlot = computeFeePayerBalanceStorageSlot(feePayer); + const balanceSlot = await computeFeePayerBalanceStorageSlot(feePayer); return computePublicDataTreeLeafSlot(ProtocolContractAddress.FeeJuice, balanceSlot); } diff --git a/yarn-project/simulator/src/public/fixtures/index.ts b/yarn-project/simulator/src/public/fixtures/index.ts index 643393dd154..6ef4f94e544 100644 --- a/yarn-project/simulator/src/public/fixtures/index.ts +++ b/yarn-project/simulator/src/public/fixtures/index.ts @@ -76,14 +76,14 @@ export async function simulateAvmTestContractGenerateCircuitInputs( ); const setupExecutionRequests: PublicExecutionRequest[] = []; for (let i = 0; i < setupFunctionNames.length; i++) { - const functionSelector = getAvmTestContractFunctionSelector(setupFunctionNames[i]); + const functionSelector = await getAvmTestContractFunctionSelector(setupFunctionNames[i]); const fnArgs = [functionSelector.toField(), ...setupArgs[i]]; const executionRequest = new PublicExecutionRequest(callContext, fnArgs); setupExecutionRequests.push(executionRequest); } const appExecutionRequests: PublicExecutionRequest[] = []; for (let i = 0; i < appFunctionNames.length; i++) { - const functionSelector = getAvmTestContractFunctionSelector(appFunctionNames[i]); + const functionSelector = await getAvmTestContractFunctionSelector(appFunctionNames[i]); const fnArgs = [functionSelector.toField(), ...appArgs[i]]; const executionRequest = new PublicExecutionRequest(callContext, fnArgs); appExecutionRequests.push(executionRequest); @@ -91,12 +91,12 @@ export async function simulateAvmTestContractGenerateCircuitInputs( let teardownExecutionRequest: PublicExecutionRequest | undefined = undefined; if (teardownFunctionName) { - const functionSelector = getAvmTestContractFunctionSelector(teardownFunctionName); + const functionSelector = await getAvmTestContractFunctionSelector(teardownFunctionName); const fnArgs = [functionSelector.toField(), ...teardownArgs]; teardownExecutionRequest = new PublicExecutionRequest(callContext, fnArgs); } - const tx: Tx = createTxForPublicCalls( + const tx: Tx = await createTxForPublicCalls( setupExecutionRequests, appExecutionRequests, Fr.random(), @@ -147,7 +147,7 @@ export async function simulateAvmTestContractCall( }); const sender = await AztecAddress.random(); - const functionSelector = getAvmTestContractFunctionSelector(functionName); + const functionSelector = await getAvmTestContractFunctionSelector(functionName); args = [functionSelector.toField(), ...args]; const environment = initExecutionEnvironment({ calldata: args, @@ -167,19 +167,19 @@ export async function simulateAvmTestContractCall( /** * Craft a carrier transaction for some public calls for simulation by PublicTxSimulator. */ -export function createTxForPublicCalls( +export async function createTxForPublicCalls( setupExecutionRequests: PublicExecutionRequest[], appExecutionRequests: PublicExecutionRequest[], firstNullifier: Fr, teardownExecutionRequest?: PublicExecutionRequest, gasUsedByPrivate: Gas = Gas.empty(), -): Tx { +): Promise { assert( setupExecutionRequests.length > 0 || appExecutionRequests.length > 0 || teardownExecutionRequest !== undefined, "Can't create public tx with no enqueued calls", ); - const setupCallRequests = setupExecutionRequests.map(er => er.toCallRequest()); - const appCallRequests = appExecutionRequests.map(er => er.toCallRequest()); + const setupCallRequests = await Promise.all(setupExecutionRequests.map(er => er.toCallRequest())); + const appCallRequests = await Promise.all(appExecutionRequests.map(er => er.toCallRequest())); // use max limits const gasLimits = new Gas(DEFAULT_GAS_LIMIT, MAX_L2_GAS_PER_TX_PUBLIC_PORTION); @@ -195,7 +195,7 @@ export function createTxForPublicCalls( forPublic.revertibleAccumulatedData.publicCallRequests[i] = appCallRequests[i]; } if (teardownExecutionRequest) { - forPublic.publicTeardownCallRequest = teardownExecutionRequest.toCallRequest(); + forPublic.publicTeardownCallRequest = await teardownExecutionRequest.toCallRequest(); } const teardownGasLimits = teardownExecutionRequest ? gasLimits : Gas.empty(); @@ -225,12 +225,6 @@ export function createTxForPublicCalls( } export class MockedAvmTestContractDataSource implements ContractDataSource { - private fnName = 'public_dispatch'; - public fnSelector: FunctionSelector = getAvmTestContractFunctionSelector(this.fnName); - private bytecode: Buffer; - private publicFn: PublicFunction; - private bytecodeCommitment: Fr; - // maps contract class ID to class private contractClasses: Map = new Map(); // maps contract instance address to instance @@ -243,24 +237,27 @@ export class MockedAvmTestContractDataSource implements ContractDataSource { SerializableContractInstance.default().withAddress(AztecAddress.fromNumber(0)); public otherContractInstance!: ContractInstanceWithAddress; - private constructor(private skipContractDeployments: boolean) { - this.bytecode = getAvmTestContractBytecode(this.fnName); - this.fnSelector = getAvmTestContractFunctionSelector(this.fnName); - this.publicFn = { bytecode: this.bytecode, selector: this.fnSelector }; - this.bytecodeCommitment = computePublicBytecodeCommitment(this.bytecode); + private constructor( + private skipContractDeployments: boolean, + public fnName: string, + private publicFn: PublicFunction, + ) {} + + get fnSelector() { + return this.publicFn.selector; } async deployContracts(merkleTrees: MerkleTreeWriteOperations) { if (!this.skipContractDeployments) { for (const contractInstance of this.contractInstances.values()) { - const contractAddressNullifier = siloNullifier( + const contractAddressNullifier = await siloNullifier( AztecAddress.fromNumber(DEPLOYER_CONTRACT_ADDRESS), contractInstance.address.toField(), ); await merkleTrees.batchInsert(MerkleTreeId.NULLIFIER_TREE, [contractAddressNullifier.toBuffer()], 0); } - const instanceSameClassAsFirstContractNullifier = siloNullifier( + const instanceSameClassAsFirstContractNullifier = await siloNullifier( AztecAddress.fromNumber(DEPLOYER_CONTRACT_ADDRESS), this.instanceSameClassAsFirstContract.address.toField(), ); @@ -271,7 +268,7 @@ export class MockedAvmTestContractDataSource implements ContractDataSource { ); // other contract address used by the bulk test's GETCONTRACTINSTANCE test - const otherContractAddressNullifier = siloNullifier( + const otherContractAddressNullifier = await siloNullifier( AztecAddress.fromNumber(DEPLOYER_CONTRACT_ADDRESS), this.otherContractInstance.address.toField(), ); @@ -280,10 +277,14 @@ export class MockedAvmTestContractDataSource implements ContractDataSource { } public static async create(skipContractDeployments: boolean = false): Promise { - const dataSource = new MockedAvmTestContractDataSource(skipContractDeployments); + const fnName = 'public_dispatch'; + const bytecode = getAvmTestContractBytecode(fnName); + const fnSelector = await getAvmTestContractFunctionSelector(fnName); + const publicFn = { bytecode: bytecode, selector: fnSelector }; + const dataSource = new MockedAvmTestContractDataSource(skipContractDeployments, fnName, publicFn); // create enough unique classes to hit the limit (plus two extra) for (let i = 0; i < MAX_PUBLIC_CALLS_TO_UNIQUE_CONTRACT_CLASS_IDS + 1; i++) { - const contractClass = makeContractClassPublic(/*seed=*/ i, dataSource.publicFn); + const contractClass = await makeContractClassPublic(/*seed=*/ i, dataSource.publicFn); const contractInstance = await makeContractInstanceFromClassId(contractClass.id, /*seed=*/ i); dataSource.contractClasses.set(contractClass.id.toString(), contractClass); dataSource.contractInstances.set(contractInstance.address.toString(), contractInstance); @@ -328,7 +329,7 @@ export class MockedAvmTestContractDataSource implements ContractDataSource { } getBytecodeCommitment(_id: Fr): Promise { - return Promise.resolve(this.bytecodeCommitment); + return computePublicBytecodeCommitment(this.publicFn.bytecode); } addContractClass(_contractClass: ContractClassPublic): Promise { @@ -365,7 +366,7 @@ export class MockedAvmTestContractDataSource implements ContractDataSource { } } -function getAvmTestContractFunctionSelector(functionName: string): FunctionSelector { +function getAvmTestContractFunctionSelector(functionName: string): Promise { const artifact = AvmTestContractArtifact.functions.find(f => f.name === functionName)!; assert(!!artifact, `Function ${functionName} not found in AvmTestContractArtifact`); const params = artifact.parameters; diff --git a/yarn-project/simulator/src/public/public_db_sources.test.ts b/yarn-project/simulator/src/public/public_db_sources.test.ts index 304d5f29b44..24fed1209fa 100644 --- a/yarn-project/simulator/src/public/public_db_sources.test.ts +++ b/yarn-project/simulator/src/public/public_db_sources.test.ts @@ -1,6 +1,7 @@ import { MerkleTreeId, type MerkleTreeWriteOperations } from '@aztec/circuit-types'; import { AztecAddress, type ContractDataSource, Fr, PublicDataTreeLeafPreimage } from '@aztec/circuits.js'; import { computePublicDataTreeLeafSlot } from '@aztec/circuits.js/hash'; +import { timesParallel } from '@aztec/foundation/collection'; import { type IndexedTreeLeafPreimage } from '@aztec/foundation/trees'; import { type MockProxy, mock } from 'jest-mock-extended'; @@ -23,12 +24,10 @@ describe('world_state_public_db', () => { addresses = await Promise.all(Array(DB_VALUES_SIZE).fill(0).map(AztecAddress.random)); slots = Array(DB_VALUES_SIZE).fill(0).map(Fr.random); dbValues = Array(DB_VALUES_SIZE).fill(0).map(Fr.random); - const publicDataEntries = Array(DB_VALUES_SIZE) - .fill(0) - .map((_, idx: number) => { - const leafSlot = computePublicDataTreeLeafSlot(addresses[idx], slots[idx]); - return new PublicDataTreeLeafPreimage(leafSlot, dbValues[idx], Fr.ZERO, 0n); - }); + const publicDataEntries = await timesParallel(DB_VALUES_SIZE, async (idx: number) => { + const leafSlot = await computePublicDataTreeLeafSlot(addresses[idx], slots[idx]); + return new PublicDataTreeLeafPreimage(leafSlot, dbValues[idx], Fr.ZERO, 0n); + }); dbStorage = new Map>([ [ MerkleTreeId.PUBLIC_DATA_TREE, diff --git a/yarn-project/simulator/src/public/public_db_sources.ts b/yarn-project/simulator/src/public/public_db_sources.ts index 7a4185ed72a..899fa487eba 100644 --- a/yarn-project/simulator/src/public/public_db_sources.ts +++ b/yarn-project/simulator/src/public/public_db_sources.ts @@ -44,16 +44,18 @@ export class ContractsDataSourcePublicDB implements PublicContractsDB { * Add new contracts from a transaction * @param tx - The transaction to add contracts from. */ - public addNewContracts(tx: Tx): Promise { + public async addNewContracts(tx: Tx): Promise { // Extract contract class and instance data from logs and add to cache for this block const logs = tx.contractClassLogs.unrollLogs(); - logs + const contractClassRegisteredEvents = logs .filter(log => ContractClassRegisteredEvent.isContractClassRegisteredEvent(log.data)) - .forEach(log => { - const event = ContractClassRegisteredEvent.fromLog(log.data); + .map(log => ContractClassRegisteredEvent.fromLog(log.data)); + await Promise.all( + contractClassRegisteredEvents.map(async event => { this.log.debug(`Adding class ${event.contractClassId.toString()} to public execution contract cache`); - this.classCache.set(event.contractClassId.toString(), event.toContractClassPublic()); - }); + this.classCache.set(event.contractClassId.toString(), await event.toContractClassPublic()); + }), + ); // We store the contract instance deployed event log in private logs, contract_instance_deployer_contract/src/main.nr const contractInstanceEvents = tx.data @@ -66,8 +68,6 @@ export class ContractsDataSourcePublicDB implements PublicContractsDB { ); this.instanceCache.set(e.address.toString(), e.toContractInstance()); }); - - return Promise.resolve(); } /** @@ -124,7 +124,7 @@ export class ContractsDataSourcePublicDB implements PublicContractsDB { return undefined; } - const value = computePublicBytecodeCommitment(contractClass.packedBytecode); + const value = await computePublicBytecodeCommitment(contractClass.packedBytecode); this.bytecodeCommitmentCache.set(key, value); return value; } @@ -171,7 +171,7 @@ export class WorldStateDB extends ContractsDataSourcePublicDB implements PublicS * @returns The current value in the storage slot. */ public async storageRead(contract: AztecAddress, slot: Fr): Promise { - const leafSlot = computePublicDataTreeLeafSlot(contract, slot).value; + const leafSlot = (await computePublicDataTreeLeafSlot(contract, slot)).toBigInt(); const uncommitted = this.publicUncommittedWriteCache.get(leafSlot); if (uncommitted !== undefined) { return uncommitted; @@ -195,10 +195,10 @@ export class WorldStateDB extends ContractsDataSourcePublicDB implements PublicS * @param newValue - The new value to store. * @returns The slot of the written leaf in the public data tree. */ - public storageWrite(contract: AztecAddress, slot: Fr, newValue: Fr): Promise { - const index = computePublicDataTreeLeafSlot(contract, slot).value; + public async storageWrite(contract: AztecAddress, slot: Fr, newValue: Fr): Promise { + const index = (await computePublicDataTreeLeafSlot(contract, slot)).toBigInt(); this.publicUncommittedWriteCache.set(index, newValue); - return Promise.resolve(index); + return index; } public async getNullifierMembershipWitnessAtLatestBlock( @@ -243,7 +243,7 @@ export class WorldStateDB extends ContractsDataSourcePublicDB implements PublicS throw new Error(`No L1 to L2 message found for message hash ${messageHash.toString()}`); } - const messageNullifier = computeL1ToL2MessageNullifier(contractAddress, messageHash, secret); + const messageNullifier = await computeL1ToL2MessageNullifier(contractAddress, messageHash, secret); const nullifierIndex = await this.getNullifierIndex(messageNullifier); if (nullifierIndex !== undefined) { @@ -348,7 +348,7 @@ export class WorldStateDB extends ContractsDataSourcePublicDB implements PublicS } export async function readPublicState(db: MerkleTreeReadOperations, contract: AztecAddress, slot: Fr): Promise { - const leafSlot = computePublicDataTreeLeafSlot(contract, slot).toBigInt(); + const leafSlot = (await computePublicDataTreeLeafSlot(contract, slot)).toBigInt(); const lowLeafResult = await db.getPreviousValueIndex(MerkleTreeId.PUBLIC_DATA_TREE, leafSlot); if (!lowLeafResult || !lowLeafResult.alreadyPresent) { diff --git a/yarn-project/simulator/src/public/public_processor.test.ts b/yarn-project/simulator/src/public/public_processor.test.ts index eb4872cbda0..5e50df9c4bd 100644 --- a/yarn-project/simulator/src/public/public_processor.test.ts +++ b/yarn-project/simulator/src/public/public_processor.test.ts @@ -19,7 +19,7 @@ import { RevertCode, } from '@aztec/circuits.js'; import { computePublicDataTreeLeafSlot } from '@aztec/circuits.js/hash'; -import { times } from '@aztec/foundation/collection'; +import { timesParallel } from '@aztec/foundation/collection'; import { sleep } from '@aztec/foundation/sleep'; import { TestDateProvider } from '@aztec/foundation/timer'; import { getTelemetryClient } from '@aztec/telemetry-client'; @@ -92,23 +92,23 @@ describe('public_processor', () => { describe('process txs', () => { it('process private-only txs', async function () { - const tx = mockPrivateOnlyTx(); + const tx = await mockPrivateOnlyTx(); const [processed, failed] = await processor.process([tx]); expect(processed.length).toBe(1); - expect(processed[0].hash).toEqual(tx.getTxHash()); + expect(processed[0].hash).toEqual(await tx.getTxHash()); expect(processed[0].data).toEqual(tx.data); expect(failed).toEqual([]); }); it('runs a tx with enqueued public calls', async function () { - const tx = mockTxWithPublicCalls(); + const tx = await mockTxWithPublicCalls(); const [processed, failed] = await processor.process([tx]); expect(processed.length).toBe(1); - expect(processed[0].hash).toEqual(tx.getTxHash()); + expect(processed[0].hash).toEqual(await tx.getTxHash()); expect(processed[0].data).toEqual(tx.data); expect(failed).toEqual([]); @@ -116,7 +116,7 @@ describe('public_processor', () => { }); it('runs a tx with reverted enqueued public calls', async function () { - const tx = mockTxWithPublicCalls(); + const tx = await mockTxWithPublicCalls(); mockedEnqueuedCallsResult.revertCode = RevertCode.APP_LOGIC_REVERTED; mockedEnqueuedCallsResult.revertReason = new SimulationError(`Failed`, []); @@ -124,7 +124,7 @@ describe('public_processor', () => { const [processed, failed] = await processor.process([tx]); expect(processed.length).toBe(1); - expect(processed[0].hash).toEqual(tx.getTxHash()); + expect(processed[0].hash).toEqual(await tx.getTxHash()); expect(failed).toEqual([]); expect(worldStateDB.commit).toHaveBeenCalledTimes(1); @@ -133,7 +133,7 @@ describe('public_processor', () => { it('returns failed txs without aborting entire operation', async function () { publicTxSimulator.simulate.mockRejectedValue(new SimulationError(`Failed`, [])); - const tx = mockTxWithPublicCalls(); + const tx = await mockTxWithPublicCalls(); const [processed, failed] = await processor.process([tx]); expect(processed).toEqual([]); @@ -145,21 +145,21 @@ describe('public_processor', () => { }); it('does not attempt to overfill a block', async function () { - const txs = Array.from([1, 2, 3], seed => mockPrivateOnlyTx({ seed })); + const txs = await Promise.all(Array.from([1, 2, 3], seed => mockPrivateOnlyTx({ seed }))); // We are passing 3 txs but only 2 can fit in the block const [processed, failed] = await processor.process(txs, { maxTransactions: 2 }); expect(processed.length).toBe(2); - expect(processed[0].hash).toEqual(txs[0].getTxHash()); - expect(processed[1].hash).toEqual(txs[1].getTxHash()); + expect(processed[0].hash).toEqual(await txs[0].getTxHash()); + expect(processed[1].hash).toEqual(await txs[1].getTxHash()); expect(failed).toEqual([]); expect(worldStateDB.commit).toHaveBeenCalledTimes(2); }); it('does not send a transaction to the prover if pre validation fails', async function () { - const tx = mockPrivateOnlyTx(); + const tx = await mockPrivateOnlyTx(); const txValidator: MockProxy> = mock(); txValidator.validateTx.mockResolvedValue({ result: 'invalid', reason: ['Invalid'] }); @@ -171,7 +171,7 @@ describe('public_processor', () => { }); it('does not send a transaction to the prover if post validation fails', async function () { - const tx = mockPrivateOnlyTx(); + const tx = await mockPrivateOnlyTx(); const txValidator: MockProxy> = mock(); txValidator.validateTx.mockResolvedValue({ result: 'invalid', reason: ['Invalid'] }); @@ -184,7 +184,7 @@ describe('public_processor', () => { }); it('does not go past the deadline', async function () { - const txs = times(3, seed => mockTxWithPublicCalls({ seed })); + const txs = await timesParallel(3, seed => mockTxWithPublicCalls({ seed })); // The simulator will take 400ms to process each tx publicTxSimulator.simulate.mockImplementation(async () => { @@ -197,8 +197,8 @@ describe('public_processor', () => { const [processed, failed] = await processor.process(txs, { deadline }); expect(processed.length).toBe(2); - expect(processed[0].hash).toEqual(txs[0].getTxHash()); - expect(processed[1].hash).toEqual(txs[1].getTxHash()); + expect(processed[0].hash).toEqual(await txs[0].getTxHash()); + expect(processed[1].hash).toEqual(await txs[1].getTxHash()); expect(failed).toEqual([]); expect(worldStateDB.commit).toHaveBeenCalledTimes(2); }); @@ -211,13 +211,13 @@ describe('public_processor', () => { beforeEach(() => { worldStateDB.storageRead.mockResolvedValue(initialBalance); - worldStateDB.storageWrite.mockImplementation((address: AztecAddress, slot: Fr) => - Promise.resolve(computePublicDataTreeLeafSlot(address, slot).toBigInt()), + worldStateDB.storageWrite.mockImplementation(async (address: AztecAddress, slot: Fr) => + (await computePublicDataTreeLeafSlot(address, slot)).toBigInt(), ); }); it('injects balance update with no public calls', async function () { - const tx = mockPrivateOnlyTx({ + const tx = await mockPrivateOnlyTx({ feePayer, }); @@ -231,7 +231,7 @@ describe('public_processor', () => { expect(processed).toHaveLength(1); expect(processed[0].data.feePayer).toEqual(feePayer); expect(processed[0].txEffect.publicDataWrites[0]).toEqual( - new PublicDataWrite(computeFeePayerBalanceLeafSlot(feePayer), initialBalance.sub(txFee)), + new PublicDataWrite(await computeFeePayerBalanceLeafSlot(feePayer), initialBalance.sub(txFee)), ); expect(failed).toEqual([]); @@ -240,7 +240,7 @@ describe('public_processor', () => { }); it('rejects tx if fee payer has not enough balance', async function () { - const tx = mockPrivateOnlyTx({ + const tx = await mockPrivateOnlyTx({ feePayer, }); diff --git a/yarn-project/simulator/src/public/public_processor.ts b/yarn-project/simulator/src/public/public_processor.ts index f95595699ac..4db45b532e6 100644 --- a/yarn-project/simulator/src/public/public_processor.ts +++ b/yarn-project/simulator/src/public/public_processor.ts @@ -137,7 +137,7 @@ export class PublicProcessor implements Traceable { * @returns The list of processed txs with their circuit simulation outputs. */ public async process( - txs: Iterable, + txs: Iterable | AsyncIterableIterator, limits: { maxTransactions?: number; maxBlockSize?: number; @@ -161,7 +161,7 @@ export class PublicProcessor implements Traceable { let totalPublicGas = new Gas(0, 0); let totalBlockGas = new Gas(0, 0); - for (const origTx of txs) { + for await (const origTx of txs) { // Only process up to the max tx limit if (maxTransactions !== undefined && result.length >= maxTransactions) { this.log.debug(`Stopping tx processing due to reaching the max tx limit.`); @@ -175,7 +175,7 @@ export class PublicProcessor implements Traceable { } // Skip this tx if it'd exceed max block size - const txHash = origTx.getTxHash().toString(); + const txHash = (await origTx.getTxHash()).toString(); const preTxSizeInBytes = origTx.getEstimatedPrivateTxEffectsSize(); if (maxBlockSize !== undefined && totalSizeInBytes + preTxSizeInBytes > maxBlockSize) { this.log.warn(`Skipping processing of tx ${txHash} sized ${preTxSizeInBytes} bytes due to block size limit`, { @@ -205,19 +205,20 @@ export class PublicProcessor implements Traceable { // We validate the tx before processing it, to avoid unnecessary work. if (preprocessValidator) { const result = await preprocessValidator.validateTx(tx); + const txHash = await tx.getTxHash(); if (result.result === 'invalid') { const reason = result.reason.join(', '); - this.log.warn(`Rejecting tx ${tx.getTxHash().toString()} due to pre-process validation fail: ${reason}`); + this.log.warn(`Rejecting tx ${txHash.toString()} due to pre-process validation fail: ${reason}`); failed.push({ tx, error: new Error(`Tx failed preprocess validation: ${reason}`) }); returns.push(new NestedProcessReturnValues([])); continue; } else if (result.result === 'skipped') { const reason = result.reason.join(', '); - this.log.warn(`Skipping tx ${tx.getTxHash().toString()} due to pre-process validation: ${reason}`); + this.log.warn(`Skipping tx ${txHash.toString()} due to pre-process validation: ${reason}`); returns.push(new NestedProcessReturnValues([])); continue; } else { - this.log.trace(`Tx ${tx.getTxHash().toString()} is valid before processing.`); + this.log.trace(`Tx ${txHash.toString()} is valid before processing.`); } } @@ -250,7 +251,7 @@ export class PublicProcessor implements Traceable { failed.push({ tx, error: new Error(`Tx failed post-process validation: ${reason}`) }); continue; } else { - this.log.trace(`Tx ${tx.getTxHash().toString()} is valid post processing.`); + this.log.trace(`Tx ${(await tx.getTxHash()).toString()} is valid post processing.`); } } @@ -291,7 +292,7 @@ export class PublicProcessor implements Traceable { return [result, failed, returns]; } - @trackSpan('PublicProcessor.processTx', tx => ({ [Attributes.TX_HASH]: tx.getTxHash().toString() })) + @trackSpan('PublicProcessor.processTx', async tx => ({ [Attributes.TX_HASH]: (await tx.getTxHash()).toString() })) private async processTx(tx: Tx, deadline?: Date): Promise<[ProcessedTx, NestedProcessReturnValues[]]> { const [time, [processedTx, returnValues]] = await elapsed(() => this.processTxWithinDeadline(tx, deadline)); @@ -372,13 +373,13 @@ export class PublicProcessor implements Traceable { return await processFn(); } - const txHash = tx.getTxHash().toString(); + const txHash = await tx.getTxHash(); const timeout = +deadline - this.dateProvider.now(); if (timeout <= 0) { throw new PublicProcessorTimeoutError(); } - this.log.debug(`Processing tx ${tx.getTxHash().toString()} within ${timeout}ms`, { + this.log.debug(`Processing tx ${txHash.toString()} within ${timeout}ms`, { deadline: deadline.toISOString(), now: new Date(this.dateProvider.now()).toISOString(), txHash, @@ -403,8 +404,8 @@ export class PublicProcessor implements Traceable { } const feeJuiceAddress = ProtocolContractAddress.FeeJuice; - const balanceSlot = computeFeePayerBalanceStorageSlot(feePayer); - const leafSlot = computeFeePayerBalanceLeafSlot(feePayer); + const balanceSlot = await computeFeePayerBalanceStorageSlot(feePayer); + const leafSlot = await computeFeePayerBalanceLeafSlot(feePayer); this.log.debug(`Deducting ${txFee.toBigInt()} balance in Fee Juice for ${feePayer}`); @@ -422,8 +423,8 @@ export class PublicProcessor implements Traceable { return new PublicDataWrite(leafSlot, updatedBalance); } - @trackSpan('PublicProcessor.processPrivateOnlyTx', (tx: Tx) => ({ - [Attributes.TX_HASH]: tx.getTxHash().toString(), + @trackSpan('PublicProcessor.processPrivateOnlyTx', async (tx: Tx) => ({ + [Attributes.TX_HASH]: (await tx.getTxHash()).toString(), })) private async processPrivateOnlyTx(tx: Tx): Promise<[ProcessedTx, undefined]> { const gasFees = this.globalVariables.gasFees; @@ -431,7 +432,7 @@ export class PublicProcessor implements Traceable { const feePaymentPublicDataWrite = await this.getFeePaymentPublicDataWrite(transactionFee, tx.data.feePayer); - const processedTx = makeProcessedTxFromPrivateOnlyTx( + const processedTx = await makeProcessedTxFromPrivateOnlyTx( tx, transactionFee, feePaymentPublicDataWrite, @@ -447,8 +448,8 @@ export class PublicProcessor implements Traceable { return [processedTx, undefined]; } - @trackSpan('PublicProcessor.processTxWithPublicCalls', tx => ({ - [Attributes.TX_HASH]: tx.getTxHash().toString(), + @trackSpan('PublicProcessor.processTxWithPublicCalls', async tx => ({ + [Attributes.TX_HASH]: (await tx.getTxHash()).toString(), })) private async processTxWithPublicCalls(tx: Tx): Promise<[ProcessedTx, NestedProcessReturnValues[]]> { const timer = new Timer(); @@ -480,7 +481,13 @@ export class PublicProcessor implements Traceable { const durationMs = timer.ms(); this.metrics.recordTx(phaseCount, durationMs, gasUsed.publicGas); - const processedTx = makeProcessedTxFromTxWithPublicCalls(tx, avmProvingRequest, gasUsed, revertCode, revertReason); + const processedTx = await makeProcessedTxFromTxWithPublicCalls( + tx, + avmProvingRequest, + gasUsed, + revertCode, + revertReason, + ); const returnValues = processedPhases.find(({ phase }) => phase === TxExecutionPhase.APP_LOGIC)?.returnValues ?? []; diff --git a/yarn-project/simulator/src/public/public_tx_context.ts b/yarn-project/simulator/src/public/public_tx_context.ts index f840955a0f7..156c1fa7aca 100644 --- a/yarn-project/simulator/src/public/public_tx_context.ts +++ b/yarn-project/simulator/src/public/public_tx_context.ts @@ -120,7 +120,7 @@ export class PublicTxContext { const gasAllocatedToPublic = applyMaxToAvailableGas(gasSettings.gasLimits.sub(gasUsedByPrivate)); return new PublicTxContext( - tx.getTxHash(), + await tx.getTxHash(), new PhaseStateManager(txStateManager), globalVariables, await db.getStateReference(), @@ -319,13 +319,14 @@ export class PublicTxContext { /** * Generate the public inputs for the AVM circuit. */ - private generateAvmCircuitPublicInputs(endStateReference: StateReference): AvmCircuitPublicInputs { + private async generateAvmCircuitPublicInputs(endStateReference: StateReference): Promise { assert(this.halted, 'Can only get AvmCircuitPublicInputs after tx execution ends'); const ephemeralTrees = this.state.getActiveStateManager().merkleTrees; - const noteHashTree = ephemeralTrees.getTreeSnapshot(MerkleTreeId.NOTE_HASH_TREE); - const nullifierTree = ephemeralTrees.getTreeSnapshot(MerkleTreeId.NULLIFIER_TREE); - const publicDataTree = ephemeralTrees.getTreeSnapshot(MerkleTreeId.PUBLIC_DATA_TREE); + const noteHashTree = await ephemeralTrees.getTreeSnapshot(MerkleTreeId.NOTE_HASH_TREE); + const nullifierTree = await ephemeralTrees.getTreeSnapshot(MerkleTreeId.NULLIFIER_TREE); + const publicDataTree = await ephemeralTrees.getTreeSnapshot(MerkleTreeId.PUBLIC_DATA_TREE); + // Pad the note hash and nullifier trees const paddedNoteHashTreeSize = this.startStateReference.partial.noteHashTree.nextAvailableLeafIndex + MAX_NOTE_HASHES_PER_TX; @@ -374,7 +375,7 @@ export class PublicTxContext { /** * Generate the proving request for the AVM circuit. */ - generateProvingRequest(endStateReference: StateReference): AvmProvingRequest { + async generateProvingRequest(endStateReference: StateReference): Promise { const hints = this.trace.getAvmCircuitHints(); return { type: ProvingRequestType.PUBLIC_VM, @@ -383,7 +384,7 @@ export class PublicTxContext { [], PublicCircuitPublicInputs.empty(), hints, - this.generateAvmCircuitPublicInputs(endStateReference), + await this.generateAvmCircuitPublicInputs(endStateReference), ), }; } diff --git a/yarn-project/simulator/src/public/public_tx_simulator.test.ts b/yarn-project/simulator/src/public/public_tx_simulator.test.ts index 3b03c62e63d..bdf18d83fcb 100644 --- a/yarn-project/simulator/src/public/public_tx_simulator.test.ts +++ b/yarn-project/simulator/src/public/public_tx_simulator.test.ts @@ -76,7 +76,7 @@ describe('public_tx_simulator', () => { ) => Promise >; - const mockTxWithPublicCalls = ({ + const mockTxWithPublicCalls = async ({ numberOfSetupCalls = 0, numberOfAppLogicCalls = 0, hasPublicTeardownCall = false, @@ -88,7 +88,7 @@ describe('public_tx_simulator', () => { feePayer?: AztecAddress; }) => { // seed with min nullifier to prevent insertion of a nullifier < min - const tx = mockTx(/*seed=*/ MIN_NULLIFIER, { + const tx = await mockTx(/*seed=*/ MIN_NULLIFIER, { numberOfNonRevertiblePublicCallRequests: numberOfSetupCalls, numberOfRevertiblePublicCallRequests: numberOfAppLogicCalls, hasPublicTeardownCallRequest: hasPublicTeardownCall, @@ -117,8 +117,8 @@ describe('public_tx_simulator', () => { const setFeeBalance = async (feePayer: AztecAddress, balance: Fr) => { const feeJuiceAddress = ProtocolContractAddress.FeeJuice; - const balanceSlot = computeFeePayerBalanceStorageSlot(feePayer); - const balancePublicDataTreeLeafSlot = computePublicDataTreeLeafSlot(feeJuiceAddress, balanceSlot); + const balanceSlot = await computeFeePayerBalanceStorageSlot(feePayer); + const balancePublicDataTreeLeafSlot = await computePublicDataTreeLeafSlot(feeJuiceAddress, balanceSlot); await db.batchInsert( MerkleTreeId.PUBLIC_DATA_TREE, [new PublicDataTreeLeaf(balancePublicDataTreeLeafSlot, balance).toBuffer()], @@ -275,7 +275,7 @@ describe('public_tx_simulator', () => { }); it('runs a tx with enqueued public calls in setup phase only', async () => { - const tx = mockTxWithPublicCalls({ + const tx = await mockTxWithPublicCalls({ numberOfSetupCalls: 2, }); @@ -311,7 +311,7 @@ describe('public_tx_simulator', () => { }); it('runs a tx with enqueued public calls in app logic phase only', async () => { - const tx = mockTxWithPublicCalls({ + const tx = await mockTxWithPublicCalls({ numberOfAppLogicCalls: 2, }); @@ -347,7 +347,7 @@ describe('public_tx_simulator', () => { }); it('runs a tx with enqueued public calls in teardown phase only', async () => { - const tx = mockTxWithPublicCalls({ + const tx = await mockTxWithPublicCalls({ hasPublicTeardownCall: true, }); @@ -381,7 +381,7 @@ describe('public_tx_simulator', () => { }); it('runs a tx with all phases', async () => { - const tx = mockTxWithPublicCalls({ + const tx = await mockTxWithPublicCalls({ numberOfSetupCalls: 2, numberOfAppLogicCalls: 1, hasPublicTeardownCall: true, @@ -430,7 +430,7 @@ describe('public_tx_simulator', () => { }); it('deduplicates public data writes', async function () { - const tx = mockTxWithPublicCalls({ + const tx = await mockTxWithPublicCalls({ numberOfSetupCalls: 1, numberOfAppLogicCalls: 1, hasPublicTeardownCall: true, @@ -475,15 +475,15 @@ describe('public_tx_simulator', () => { const numPublicDataWrites = 3; expect(countAccumulatedItems(output.accumulatedData.publicDataWrites)).toBe(numPublicDataWrites); expect(output.accumulatedData.publicDataWrites.slice(0, numPublicDataWrites)).toEqual([ - new PublicDataWrite(computePublicDataTreeLeafSlot(contractAddress, contractSlotA), fr(0x103)), // 0x101 replaced with 0x103 - new PublicDataWrite(computePublicDataTreeLeafSlot(contractAddress, contractSlotB), fr(0x151)), - new PublicDataWrite(computePublicDataTreeLeafSlot(contractAddress, contractSlotC), fr(0x152)), // 0x201 replaced with 0x102 and then 0x152 + new PublicDataWrite(await computePublicDataTreeLeafSlot(contractAddress, contractSlotA), fr(0x103)), // 0x101 replaced with 0x103 + new PublicDataWrite(await computePublicDataTreeLeafSlot(contractAddress, contractSlotB), fr(0x151)), + new PublicDataWrite(await computePublicDataTreeLeafSlot(contractAddress, contractSlotC), fr(0x152)), // 0x201 replaced with 0x102 and then 0x152 ]); }); it('fails a transaction that reverts in setup', async function () { // seed with min nullifier to prevent insertion of a nullifier < min - const tx = mockTx(/*seed=*/ MIN_NULLIFIER, { + const tx = await mockTx(/*seed=*/ MIN_NULLIFIER, { numberOfNonRevertiblePublicCallRequests: 1, numberOfRevertiblePublicCallRequests: 1, hasPublicTeardownCallRequest: true, @@ -506,7 +506,7 @@ describe('public_tx_simulator', () => { }); it('includes a transaction that reverts in app logic only', async function () { - const tx = mockTxWithPublicCalls({ + const tx = await mockTxWithPublicCalls({ numberOfSetupCalls: 1, numberOfAppLogicCalls: 2, hasPublicTeardownCall: true, @@ -590,7 +590,7 @@ describe('public_tx_simulator', () => { }); it('includes a transaction that reverts in teardown only', async function () { - const tx = mockTxWithPublicCalls({ + const tx = await mockTxWithPublicCalls({ numberOfSetupCalls: 1, numberOfAppLogicCalls: 2, hasPublicTeardownCall: true, @@ -671,7 +671,7 @@ describe('public_tx_simulator', () => { }); it('includes a transaction that reverts in app logic and teardown', async function () { - const tx = mockTxWithPublicCalls({ + const tx = await mockTxWithPublicCalls({ numberOfSetupCalls: 1, numberOfAppLogicCalls: 2, hasPublicTeardownCall: true, @@ -755,7 +755,7 @@ describe('public_tx_simulator', () => { }); it('nullifier tree root is right', async function () { - const tx = mockTxWithPublicCalls({ + const tx = await mockTxWithPublicCalls({ numberOfSetupCalls: 1, numberOfAppLogicCalls: 2, hasPublicTeardownCall: true, @@ -793,7 +793,7 @@ describe('public_tx_simulator', () => { // The max fee is gasFee + priorityFee + 1. maxFeesPerGas = new GasFees(2 + 5 + 1, 3 + 7 + 1); - const tx = mockTxWithPublicCalls({ + const tx = await mockTxWithPublicCalls({ numberOfSetupCalls: 1, numberOfAppLogicCalls: 1, hasPublicTeardownCall: true, @@ -826,7 +826,7 @@ describe('public_tx_simulator', () => { const feePayer = await AztecAddress.random(); await setFeeBalance(feePayer, Fr.MAX_FIELD_VALUE); - const tx = mockTxWithPublicCalls({ + const tx = await mockTxWithPublicCalls({ numberOfSetupCalls: 1, numberOfAppLogicCalls: 1, hasPublicTeardownCall: true, @@ -842,7 +842,7 @@ describe('public_tx_simulator', () => { await expect( simulator.simulate( - mockTxWithPublicCalls({ + await mockTxWithPublicCalls({ numberOfSetupCalls: 1, numberOfAppLogicCalls: 1, hasPublicTeardownCall: true, @@ -857,7 +857,7 @@ describe('public_tx_simulator', () => { const feePayer = await AztecAddress.random(); const txResult = await simulator.simulate( - mockTxWithPublicCalls({ + await mockTxWithPublicCalls({ numberOfSetupCalls: 1, numberOfAppLogicCalls: 1, hasPublicTeardownCall: true, diff --git a/yarn-project/simulator/src/public/public_tx_simulator.ts b/yarn-project/simulator/src/public/public_tx_simulator.ts index 349311b88f7..7ace94a4330 100644 --- a/yarn-project/simulator/src/public/public_tx_simulator.ts +++ b/yarn-project/simulator/src/public/public_tx_simulator.ts @@ -70,7 +70,7 @@ export class PublicTxSimulator { * @returns The result of the transaction's public execution. */ public async simulate(tx: Tx): Promise { - const txHash = tx.getTxHash(); + const txHash = await tx.getTxHash(); this.log.debug(`Simulating ${tx.enqueuedPublicFunctionCalls.length} public calls for tx ${txHash}`, { txHash }); const context = await PublicTxContext.create( @@ -119,7 +119,7 @@ export class PublicTxSimulator { const endStateReference = await this.db.getStateReference(); - const avmProvingRequest = context.generateProvingRequest(endStateReference); + const avmProvingRequest = await context.generateProvingRequest(endStateReference); const revertCode = context.getFinalRevertCode(); if (!revertCode.isOK()) { @@ -384,7 +384,7 @@ export class PublicTxSimulator { } for (const noteHash of context.nonRevertibleAccumulatedDataFromPrivate.noteHashes) { if (!noteHash.isEmpty()) { - stateManager.writeUniqueNoteHash(noteHash); + await stateManager.writeUniqueNoteHash(noteHash); } } } @@ -409,7 +409,7 @@ export class PublicTxSimulator { for (const noteHash of context.revertibleAccumulatedDataFromPrivate.noteHashes) { if (!noteHash.isEmpty()) { // Revertible note hashes from private are not hashed with nonce, since private can't know their final position, only we can. - stateManager.writeSiloedNoteHash(noteHash); + await stateManager.writeSiloedNoteHash(noteHash); } } } @@ -423,7 +423,7 @@ export class PublicTxSimulator { } const feeJuiceAddress = ProtocolContractAddress.FeeJuice; - const balanceSlot = computeFeePayerBalanceStorageSlot(context.feePayer); + const balanceSlot = await computeFeePayerBalanceStorageSlot(context.feePayer); this.log.debug(`Deducting ${txFee.toBigInt()} balance in Fee Juice for ${context.feePayer}`); const stateManager = context.state.getActiveStateManager(); diff --git a/yarn-project/simulator/src/public/side_effect_trace_interface.ts b/yarn-project/simulator/src/public/side_effect_trace_interface.ts index b4230efe0d0..7a9e2b2c2c4 100644 --- a/yarn-project/simulator/src/public/side_effect_trace_interface.ts +++ b/yarn-project/simulator/src/public/side_effect_trace_interface.ts @@ -37,7 +37,7 @@ export interface PublicSideEffectTraceInterface { lowLeafPath?: Fr[], newLeafPreimage?: PublicDataTreeLeafPreimage, insertionPath?: Fr[], - ): void; + ): Promise; traceNoteHashCheck(contractAddress: AztecAddress, noteHash: Fr, leafIndex: Fr, exists: boolean, path?: Fr[]): void; traceNewNoteHash(uniqueNoteHash: Fr, leafIndex?: Fr, path?: Fr[]): void; getNoteHashCount(): number; diff --git a/yarn-project/simulator/src/test/utils.ts b/yarn-project/simulator/src/test/utils.ts index 363034d6686..1e2aa48ace6 100644 --- a/yarn-project/simulator/src/test/utils.ts +++ b/yarn-project/simulator/src/test/utils.ts @@ -11,7 +11,7 @@ import { sha256ToField } from '@aztec/foundation/crypto'; * @param secret - The secret to unlock the message. * @returns The L1 to L2 message. */ -export const buildL1ToL2Message = ( +export const buildL1ToL2Message = async ( selector: string, contentPreimage: Fr[], targetContract: AztecAddress, @@ -22,7 +22,7 @@ export const buildL1ToL2Message = ( const selectorBuf = Buffer.from(selector, 'hex'); const content = sha256ToField([selectorBuf, ...contentPreimage]); - const secretHash = computeSecretHash(secret); + const secretHash = await computeSecretHash(secret); return new L1ToL2Message( new L1Actor(EthAddress.random(), 1), diff --git a/yarn-project/telemetry-client/package.json b/yarn-project/telemetry-client/package.json index d2f76ca021e..887088da917 100644 --- a/yarn-project/telemetry-client/package.json +++ b/yarn-project/telemetry-client/package.json @@ -88,4 +88,4 @@ "../../foundation/src/jest/setup.mjs" ] } -} \ No newline at end of file +} diff --git a/yarn-project/telemetry-client/src/telemetry.ts b/yarn-project/telemetry-client/src/telemetry.ts index 3e4a2a1e887..d2f5e2b3819 100644 --- a/yarn-project/telemetry-client/src/telemetry.ts +++ b/yarn-project/telemetry-client/src/telemetry.ts @@ -151,16 +151,16 @@ type SpanDecorator any> = ( */ export function trackSpan any>( spanName: string | ((this: T, ...args: Parameters) => string), - attributes?: Attributes | ((this: T, ...args: Parameters) => Attributes), + attributes?: Attributes | ((this: T, ...args: Parameters) => Promise | Attributes), extraAttributes?: (this: T, returnValue: Awaited>) => Attributes, ): SpanDecorator { // the return value of trackSpan is a decorator return (originalMethod: F, _context: ClassMethodDecoratorContext) => { // the return value of the decorator replaces the original method // in this wrapper method we start a span, call the original method, and then end the span - return function replacementMethod(this: T, ...args: Parameters): Promise>> { + return async function replacementMethod(this: T, ...args: Parameters): Promise>> { const name = typeof spanName === 'function' ? spanName.call(this, ...args) : spanName; - const currentAttrs = typeof attributes === 'function' ? attributes.call(this, ...args) : attributes; + const currentAttrs = typeof attributes === 'function' ? await attributes.call(this, ...args) : attributes; // run originalMethod wrapped in an active span // "active" means the span will be alive for the duration of the function execution diff --git a/yarn-project/txe/src/node/txe_node.ts b/yarn-project/txe/src/node/txe_node.ts index 47298a455a9..c66ab9f6543 100644 --- a/yarn-project/txe/src/node/txe_node.ts +++ b/yarn-project/txe/src/node/txe_node.ts @@ -95,10 +95,10 @@ export class TXENode implements AztecNode { * @param txHash - The transaction hash of the transaction. * @param effect - The tx effect to set. */ - setTxEffect(blockNumber: number, txHash: TxHash, effect: TxEffect) { + async setTxEffect(blockNumber: number, txHash: TxHash, effect: TxEffect) { // We are not creating real blocks on which membership proofs can be constructed - we instead define its hash as // simply the hash of the block number. - const blockHash = poseidon2Hash([blockNumber]); + const blockHash = await poseidon2Hash([blockNumber]); this.#txEffectsByTxHash.set(txHash.toString(), { l2BlockHash: blockHash.toString(), diff --git a/yarn-project/txe/src/oracle/txe_oracle.ts b/yarn-project/txe/src/oracle/txe_oracle.ts index a3457c91444..297c307f38c 100644 --- a/yarn-project/txe/src/oracle/txe_oracle.ts +++ b/yarn-project/txe/src/oracle/txe_oracle.ts @@ -230,8 +230,8 @@ export class TXE implements TypedOracle { } async addContractArtifact(artifact: ContractArtifact) { - const contractClass = getContractClassFromArtifact(artifact); - await this.txeDatabase.addContractArtifact(computeContractClassId(contractClass), artifact); + const contractClass = await getContractClassFromArtifact(artifact); + await this.txeDatabase.addContractArtifact(await computeContractClassId(contractClass), artifact); } async getPrivateContextInputs( @@ -288,7 +288,7 @@ export class TXE implements TypedOracle { } async checkNullifiersNotInTree(contractAddress: AztecAddress, nullifiers: Fr[]) { - const siloedNullifiers = nullifiers.map(nullifier => siloNullifier(contractAddress, nullifier)); + const siloedNullifiers = await Promise.all(nullifiers.map(nullifier => siloNullifier(contractAddress, nullifier))); const db = await this.trees.getLatest(); const nullifierIndexesInTree = await db.findLeafIndices( MerkleTreeId.NULLIFIER_TREE, @@ -306,7 +306,7 @@ export class TXE implements TypedOracle { } async addNullifiers(contractAddress: AztecAddress, nullifiers: Fr[]) { - const siloedNullifiers = nullifiers.map(nullifier => siloNullifier(contractAddress, nullifier)); + const siloedNullifiers = await Promise.all(nullifiers.map(nullifier => siloNullifier(contractAddress, nullifier))); await this.addSiloedNullifiers(siloedNullifiers); } @@ -321,15 +321,15 @@ export class TXE implements TypedOracle { } async addNoteHashes(contractAddress: AztecAddress, noteHashes: Fr[]) { - const siloedNoteHashes = noteHashes.map(noteHash => siloNoteHash(contractAddress, noteHash)); + const siloedNoteHashes = await Promise.all(noteHashes.map(noteHash => siloNoteHash(contractAddress, noteHash))); await this.addUniqueNoteHashes(siloedNoteHashes); } - addPrivateLogs(contractAddress: AztecAddress, privateLogs: PrivateLog[]) { - privateLogs.forEach(privateLog => { - privateLog.fields[0] = poseidon2Hash([contractAddress, privateLog.fields[0]]); - }); + async addPrivateLogs(contractAddress: AztecAddress, privateLogs: PrivateLog[]) { + for (const privateLog of privateLogs) { + privateLog.fields[0] = await poseidon2Hash([contractAddress, privateLog.fields[0]]); + } this.privateLogs.push(...privateLogs); } @@ -371,7 +371,7 @@ export class TXE implements TypedOracle { } storeInExecutionCache(values: Fr[]) { - return Promise.resolve(this.executionCache.store(values)); + return this.executionCache.store(values); } loadFromExecutionCache(returnsHash: Fr) { @@ -557,19 +557,17 @@ export class TXE implements TypedOracle { async notifyNullifiedNote(innerNullifier: Fr, noteHash: Fr, counter: number) { await this.checkNullifiersNotInTree(this.contractAddress, [innerNullifier]); - this.noteCache.nullifyNote(this.contractAddress, innerNullifier, noteHash); + await this.noteCache.nullifyNote(this.contractAddress, innerNullifier, noteHash); this.sideEffectCounter = counter + 1; - return Promise.resolve(); } async notifyCreatedNullifier(innerNullifier: Fr): Promise { await this.checkNullifiersNotInTree(this.contractAddress, [innerNullifier]); - this.noteCache.nullifierCreated(this.contractAddress, innerNullifier); - return Promise.resolve(); + await this.noteCache.nullifierCreated(this.contractAddress, innerNullifier); } async checkNullifierExists(innerNullifier: Fr): Promise { - const nullifier = siloNullifier(this.contractAddress, innerNullifier!); + const nullifier = await siloNullifier(this.contractAddress, innerNullifier!); const db = await this.trees.getLatest(); const index = (await db.findLeafIndices(MerkleTreeId.NULLIFIER_TREE, [nullifier.toBuffer()]))[0]; return index !== undefined; @@ -593,7 +591,7 @@ export class TXE implements TypedOracle { const values = []; for (let i = 0n; i < numberOfElements; i++) { const storageSlot = startStorageSlot.add(new Fr(i)); - const leafSlot = computePublicDataTreeLeafSlot(contractAddress, storageSlot).toBigInt(); + const leafSlot = (await computePublicDataTreeLeafSlot(contractAddress, storageSlot)).toBigInt(); const lowLeafResult = await db.getPreviousValueIndex(MerkleTreeId.PUBLIC_DATA_TREE, leafSlot); @@ -614,11 +612,13 @@ export class TXE implements TypedOracle { async storageWrite(startStorageSlot: Fr, values: Fr[]): Promise { const db = await this.trees.getLatest(); - const publicDataWrites = values.map((value, i) => { - const storageSlot = startStorageSlot.add(new Fr(i)); - this.logger.debug(`Oracle storage write: slot=${storageSlot.toString()} value=${value}`); - return new PublicDataTreeLeaf(computePublicDataTreeLeafSlot(this.contractAddress, storageSlot), value); - }); + const publicDataWrites = await Promise.all( + values.map(async (value, i) => { + const storageSlot = startStorageSlot.add(new Fr(i)); + this.logger.debug(`Oracle storage write: slot=${storageSlot.toString()} value=${value}`); + return new PublicDataTreeLeaf(await computePublicDataTreeLeafSlot(this.contractAddress, storageSlot), value); + }), + ); await db.batchInsert( MerkleTreeId.PUBLIC_DATA_TREE, publicDataWrites.map(write => write.toBuffer()), @@ -641,23 +641,23 @@ export class TXE implements TypedOracle { const nonceGenerator = usedTxRequestHashForNonces ? this.getTxRequestHash() : this.noteCache.getAllNullifiers()[0]; let i = 0; - txEffect.noteHashes = [ - ...this.noteCache + const uniqueNoteHashesFromPrivate = await Promise.all( + this.noteCache .getAllNotes() - .map(pendingNote => + .map(async pendingNote => computeUniqueNoteHash( - computeNoteHashNonce(nonceGenerator, i++), - siloNoteHash(pendingNote.note.contractAddress, pendingNote.noteHashForConsumption), + await computeNoteHashNonce(nonceGenerator, i++), + await siloNoteHash(pendingNote.note.contractAddress, pendingNote.noteHashForConsumption), ), ), - ...this.uniqueNoteHashesFromPublic, - ]; + ); + txEffect.noteHashes = [...uniqueNoteHashesFromPrivate, ...this.uniqueNoteHashesFromPublic]; txEffect.nullifiers = this.noteCache.getAllNullifiers(); if (usedTxRequestHashForNonces) { txEffect.nullifiers.unshift(this.getTxRequestHash()); } - this.node.setTxEffect(blockNumber, new TxHash(new Fr(blockNumber)), txEffect); + await this.node.setTxEffect(blockNumber, new TxHash(new Fr(blockNumber)), txEffect); this.node.setNullifiersIndexesWithBlock(blockNumber, txEffect.nullifiers); this.node.addNoteLogsByTags(this.blockNumber, this.privateLogs); this.node.addPublicLogsByTags(this.blockNumber, this.publicLogs); @@ -743,7 +743,7 @@ export class TXE implements TypedOracle { const endSideEffectCounter = publicInputs.endSideEffectCounter; this.sideEffectCounter = endSideEffectCounter.toNumber() + 1; - this.addPrivateLogs( + await this.addPrivateLogs( targetContractAddress, publicInputs.privateLogs.filter(privateLog => !privateLog.isEmpty()).map(privateLog => privateLog.log), ); @@ -787,15 +787,18 @@ export class TXE implements TypedOracle { if (!artifact) { return undefined; } - - const f = artifact.functions.find(f => - FunctionSelector.fromNameAndParameters(f.name, f.parameters).equals(selector), + const functionSelectorsAndNames = await Promise.all( + artifact.functions.map(async f => ({ + name: f.name, + selector: await FunctionSelector.fromNameAndParameters(f.name, f.parameters), + })), ); - if (!f) { + const functionSelectorAndName = functionSelectorsAndNames.find(f => f.selector.equals(selector)); + if (!functionSelectorAndName) { return undefined; } - return `${artifact.name}:${f.name}`; + return `${artifact.name}:${functionSelectorAndName.name}`; } private async executePublicFunction(args: Fr[], callContext: CallContext, isTeardown: boolean = false) { @@ -813,7 +816,7 @@ export class TXE implements TypedOracle { // If the contract instance exists in the TXE's world state, make sure its nullifier is present in the tree // so its nullifier check passes. if ((await worldStateDb.getContractInstance(callContext.contractAddress)) !== undefined) { - const contractAddressNullifier = siloNullifier( + const contractAddressNullifier = await siloNullifier( AztecAddress.fromNumber(DEPLOYER_CONTRACT_ADDRESS), callContext.contractAddress.toField(), ); @@ -834,7 +837,7 @@ export class TXE implements TypedOracle { // When setting up a teardown call, we tell it that // private execution used Gas(1, 1) so it can compute a tx fee. const gasUsedByPrivate = isTeardown ? new Gas(1, 1) : Gas.empty(); - const tx = createTxForPublicCalls( + const tx = await createTxForPublicCalls( /*setupExecutionRequests=*/ [], /*appExecutionRequests=*/ isTeardown ? [] : [executionRequest], firstNullifier, @@ -880,7 +883,7 @@ export class TXE implements TypedOracle { ); const args = [this.functionSelector.toField(), ...this.executionCache.getPreimage(argsHash)]; - const newArgsHash = this.executionCache.store(args); + const newArgsHash = await this.executionCache.store(args); const executionResult = await this.executePublicFunction(args, callContext, isTeardown); @@ -938,8 +941,8 @@ export class TXE implements TypedOracle { ); } - notifySetMinRevertibleSideEffectCounter(minRevertibleSideEffectCounter: number) { - this.noteCache.setMinRevertibleSideEffectCounter(minRevertibleSideEffectCounter); + async notifySetMinRevertibleSideEffectCounter(minRevertibleSideEffectCounter: number) { + await this.noteCache.setMinRevertibleSideEffectCounter(minRevertibleSideEffectCounter); } debugLog(message: string, fields: Fr[]): void { @@ -1046,7 +1049,7 @@ export class TXE implements TypedOracle { } async avmOpcodeNullifierExists(innerNullifier: Fr, targetAddress: AztecAddress): Promise { - const nullifier = siloNullifier(targetAddress, innerNullifier!); + const nullifier = await siloNullifier(targetAddress, innerNullifier!); const db = await this.trees.getLatest(); const index = (await db.findLeafIndices(MerkleTreeId.NULLIFIER_TREE, [nullifier.toBuffer()]))[0]; return index !== undefined; @@ -1054,14 +1057,14 @@ export class TXE implements TypedOracle { async avmOpcodeEmitNullifier(nullifier: Fr) { const db = await this.trees.getLatest(); - const siloedNullifier = siloNullifier(this.contractAddress, nullifier); + const siloedNullifier = await siloNullifier(this.contractAddress, nullifier); await db.batchInsert(MerkleTreeId.NULLIFIER_TREE, [siloedNullifier.toBuffer()], NULLIFIER_SUBTREE_HEIGHT); return Promise.resolve(); } async avmOpcodeEmitNoteHash(noteHash: Fr) { const db = await this.trees.getLatest(); - const siloedNoteHash = siloNoteHash(this.contractAddress, noteHash); + const siloedNoteHash = await siloNoteHash(this.contractAddress, noteHash); await db.appendLeaves(MerkleTreeId.NOTE_HASH_TREE, [siloedNoteHash]); return Promise.resolve(); } @@ -1069,7 +1072,7 @@ export class TXE implements TypedOracle { async avmOpcodeStorageRead(slot: Fr) { const db = await this.trees.getLatest(); - const leafSlot = computePublicDataTreeLeafSlot(this.contractAddress, slot); + const leafSlot = await computePublicDataTreeLeafSlot(this.contractAddress, slot); const lowLeafResult = await db.getPreviousValueIndex(MerkleTreeId.PUBLIC_DATA_TREE, leafSlot.toBigInt()); if (!lowLeafResult || !lowLeafResult.alreadyPresent) { diff --git a/yarn-project/txe/src/txe_service/txe_service.ts b/yarn-project/txe/src/txe_service/txe_service.ts index a881a0d12e1..a47a081cb84 100644 --- a/yarn-project/txe/src/txe_service/txe_service.ts +++ b/yarn-project/txe/src/txe_service/txe_service.ts @@ -79,10 +79,11 @@ export class TXEService { const l2Block = L2Block.empty(); header.state = await trees.getStateReference(true); header.globalVariables.blockNumber = new Fr(blockNumber); - await trees.appendLeaves(MerkleTreeId.ARCHIVE, [header.hash()]); + await trees.appendLeaves(MerkleTreeId.ARCHIVE, [await header.hash()]); l2Block.archive.root = Fr.fromBuffer((await trees.getTreeInfo(MerkleTreeId.ARCHIVE, true)).root); l2Block.header = header; - this.logger.debug(`Block ${blockNumber} created, header hash ${header.hash().toString()}`); + const headerHash = await header.hash(); + this.logger.debug(`Block ${blockNumber} created, header hash ${headerHash.toString()}`); await trees.handleL2BlockAndMessages(l2Block, []); (this.typedOracle as TXE).setBlockNumber(blockNumber + 1); } @@ -151,11 +152,13 @@ export class TXEService { const contractAddressFr = addressFromSingle(contractAddress); const db = await trees.getLatest(); - const publicDataWrites = valuesFr.map((value, i) => { - const storageSlot = startStorageSlotFr.add(new Fr(i)); - this.logger.debug(`Oracle storage write: slot=${storageSlot.toString()} value=${value}`); - return new PublicDataTreeLeaf(computePublicDataTreeLeafSlot(contractAddressFr, storageSlot), value); - }); + const publicDataWrites = await Promise.all( + valuesFr.map(async (value, i) => { + const storageSlot = startStorageSlotFr.add(new Fr(i)); + this.logger.debug(`Oracle storage write: slot=${storageSlot.toString()} value=${value}`); + return new PublicDataTreeLeaf(await computePublicDataTreeLeafSlot(contractAddressFr, storageSlot), value); + }), + ); await db.batchInsert( MerkleTreeId.PUBLIC_DATA_TREE, publicDataWrites.map(write => write.toBuffer()), @@ -194,7 +197,7 @@ export class TXEService { await (this.typedOracle as TXE).addContractArtifact(artifact); const keyStore = (this.typedOracle as TXE).getKeyStore(); - const completeAddress = await keyStore.addAccount(fromSingle(secret), computePartialAddress(instance)); + const completeAddress = await keyStore.addAccount(fromSingle(secret), await computePartialAddress(instance)); const accountStore = (this.typedOracle as TXE).getTXEDatabase(); await accountStore.setAccount(completeAddress.address, completeAddress); this.logger.debug(`Created account ${completeAddress.address}`); diff --git a/yarn-project/txe/src/util/txe_public_contract_data_source.ts b/yarn-project/txe/src/util/txe_public_contract_data_source.ts index 7d36fd880e7..a9cccb092b8 100644 --- a/yarn-project/txe/src/util/txe_public_contract_data_source.ts +++ b/yarn-project/txe/src/util/txe_public_contract_data_source.ts @@ -32,8 +32,8 @@ export class TXEPublicContractDataSource implements ContractDataSource { async getContractClass(id: Fr): Promise { const contractClass = await this.txeOracle.getContractDataOracle().getContractClass(id); const artifact = await this.txeOracle.getContractDataOracle().getContractArtifact(id); - const tree = new PrivateFunctionsTree(artifact); - const privateFunctionsRoot = tree.getFunctionTreeRoot(); + const tree = await PrivateFunctionsTree.create(artifact); + const privateFunctionsRoot = await tree.getFunctionTreeRoot(); const publicFunctions: PublicFunction[] = []; if (contractClass!.packedBytecode.length > 0) { @@ -57,7 +57,7 @@ export class TXEPublicContractDataSource implements ContractDataSource { async getBytecodeCommitment(id: Fr): Promise { const contractClass = await this.txeOracle.getContractDataOracle().getContractClass(id); - return Promise.resolve(computePublicBytecodeCommitment(contractClass.packedBytecode)); + return computePublicBytecodeCommitment(contractClass.packedBytecode); } async getContract(address: AztecAddress): Promise { @@ -79,9 +79,14 @@ export class TXEPublicContractDataSource implements ContractDataSource { if (!artifact) { return undefined; } - const func = artifact.functions.find(f => - FunctionSelector.fromNameAndParameters({ name: f.name, parameters: f.parameters }).equals(selector), + const functionSelectorsAndNames = await Promise.all( + artifact.functions.map(async f => ({ + name: f.name, + selector: await FunctionSelector.fromNameAndParameters({ name: f.name, parameters: f.parameters }), + })), ); + const func = functionSelectorsAndNames.find(f => f.selector.equals(selector)); + return Promise.resolve(func?.name); } diff --git a/yarn-project/txe/src/util/txe_world_state_db.ts b/yarn-project/txe/src/util/txe_world_state_db.ts index d617ee5e72e..191c3056217 100644 --- a/yarn-project/txe/src/util/txe_world_state_db.ts +++ b/yarn-project/txe/src/util/txe_world_state_db.ts @@ -15,7 +15,7 @@ export class TXEWorldStateDB extends WorldStateDB { } override async storageRead(contract: AztecAddress, slot: Fr): Promise { - const leafSlot = computePublicDataTreeLeafSlot(contract, slot).toBigInt(); + const leafSlot = (await computePublicDataTreeLeafSlot(contract, slot)).toBigInt(); const lowLeafResult = await this.merkleDb.getPreviousValueIndex(MerkleTreeId.PUBLIC_DATA_TREE, leafSlot); @@ -33,7 +33,7 @@ export class TXEWorldStateDB extends WorldStateDB { override async storageWrite(contract: AztecAddress, slot: Fr, newValue: Fr): Promise { await this.merkleDb.batchInsert( MerkleTreeId.PUBLIC_DATA_TREE, - [new PublicDataTreeLeaf(computePublicDataTreeLeafSlot(contract, slot), newValue).toBuffer()], + [new PublicDataTreeLeaf(await computePublicDataTreeLeafSlot(contract, slot), newValue).toBuffer()], 0, ); return newValue.toBigInt(); diff --git a/yarn-project/types/src/abi/contract_artifact.test.ts b/yarn-project/types/src/abi/contract_artifact.test.ts index 1e1bb30e6e7..83ac5273287 100644 --- a/yarn-project/types/src/abi/contract_artifact.test.ts +++ b/yarn-project/types/src/abi/contract_artifact.test.ts @@ -2,10 +2,10 @@ import { getSampleContractArtifact } from '../test/fixtures.js'; import { contractArtifactFromBuffer, contractArtifactToBuffer } from './contract_artifact.js'; describe('contract_artifact', () => { - it('serializes and deserializes an instance', () => { + it('serializes and deserializes an instance', async () => { const artifact = getSampleContractArtifact(); const serialized = contractArtifactToBuffer(artifact); - const deserialized = contractArtifactFromBuffer(serialized); + const deserialized = await contractArtifactFromBuffer(serialized); expect(deserialized).toEqual(artifact); }); }); diff --git a/yarn-project/types/src/abi/contract_artifact.ts b/yarn-project/types/src/abi/contract_artifact.ts index 7e2f5b54bdd..2182451baf4 100644 --- a/yarn-project/types/src/abi/contract_artifact.ts +++ b/yarn-project/types/src/abi/contract_artifact.ts @@ -38,7 +38,7 @@ export function contractArtifactToBuffer(artifact: ContractArtifact): Buffer { * @param buffer - Buffer to deserialize. * @returns Deserialized artifact. */ -export function contractArtifactFromBuffer(buffer: Buffer): ContractArtifact { +export function contractArtifactFromBuffer(buffer: Buffer): Promise { return jsonParseWithSchema(buffer.toString('utf-8'), ContractArtifactSchema); } diff --git a/yarn-project/validator-client/src/duties/validation_service.ts b/yarn-project/validator-client/src/duties/validation_service.ts index 0772063a1f3..d269ac7144f 100644 --- a/yarn-project/validator-client/src/duties/validation_service.ts +++ b/yarn-project/validator-client/src/duties/validation_service.ts @@ -43,7 +43,7 @@ export class ValidationService { // TODO(https://github.com/AztecProtocol/aztec-packages/issues/7961): check that the current validator is correct const buf = Buffer32.fromBuffer( - keccak256(proposal.payload.getPayloadToSign(SignatureDomainSeparator.blockAttestation)), + keccak256(await proposal.payload.getPayloadToSign(SignatureDomainSeparator.blockAttestation)), ); const sig = await this.keyStore.signMessage(buf); return new BlockAttestation(proposal.payload, sig); diff --git a/yarn-project/validator-client/src/metrics.ts b/yarn-project/validator-client/src/metrics.ts index c1996fbb86f..80e864003ae 100644 --- a/yarn-project/validator-client/src/metrics.ts +++ b/yarn-project/validator-client/src/metrics.ts @@ -40,11 +40,11 @@ export class ValidatorMetrics { this.reExecutionTime.record(time); } - public recordFailedReexecution(proposal: BlockProposal) { + public async recordFailedReexecution(proposal: BlockProposal) { this.failedReexecutionCounter.add(1, { [Attributes.STATUS]: 'failed', [Attributes.BLOCK_NUMBER]: proposal.payload.header.globalVariables.blockNumber.toString(), - [Attributes.BLOCK_PROPOSER]: proposal.getSender()?.toString(), + [Attributes.BLOCK_PROPOSER]: (await proposal.getSender())?.toString(), }); } } diff --git a/yarn-project/validator-client/src/validator.test.ts b/yarn-project/validator-client/src/validator.test.ts index 341abf1fb01..74e59a86301 100644 --- a/yarn-project/validator-client/src/validator.test.ts +++ b/yarn-project/validator-client/src/validator.test.ts @@ -61,7 +61,7 @@ describe('ValidationService', () => { config.validatorReexecute = true; p2pClient.getTxByHash.mockImplementation(() => Promise.resolve(mockTx())); const val = ValidatorClient.new(config, epochCache, p2pClient); - await expect(val.reExecuteTransactions(makeBlockProposal())).rejects.toThrow(BlockBuilderNotProvidedError); + await expect(val.reExecuteTransactions(await makeBlockProposal())).rejects.toThrow(BlockBuilderNotProvidedError); }); it('Should create a valid block proposal', async () => { @@ -74,11 +74,11 @@ describe('ValidationService', () => { expect(blockProposal).toBeDefined(); const validatorAddress = EthAddress.fromString(validatorAccount.address); - expect(blockProposal?.getSender()).toEqual(validatorAddress); + expect(await blockProposal?.getSender()).toEqual(validatorAddress); }); it('Should a timeout if we do not collect enough attestations in time', async () => { - const proposal = makeBlockProposal(); + const proposal = await makeBlockProposal(); await expect(validatorClient.collectAttestations(proposal, 2, new Date(dateProvider.now() + 100))).rejects.toThrow( AttestationTimeoutError, @@ -86,7 +86,7 @@ describe('ValidationService', () => { }); it('Should throw an error if the transactions are not available', async () => { - const proposal = makeBlockProposal(); + const proposal = await makeBlockProposal(); // mock the p2pClient.getTxStatus to return undefined for all transactions p2pClient.getTxStatus.mockImplementation(() => undefined); @@ -99,18 +99,16 @@ describe('ValidationService', () => { }); it('Should not return an attestation if re-execution fails', async () => { - const proposal = makeBlockProposal(); + const proposal = await makeBlockProposal(); // mock the p2pClient.getTxStatus to return undefined for all transactions p2pClient.getTxStatus.mockImplementation(() => undefined); - epochCache.getProposerInCurrentOrNextSlot.mockImplementation(() => - Promise.resolve({ - currentProposer: proposal.getSender(), - nextProposer: proposal.getSender(), - currentSlot: proposal.slotNumber.toBigInt(), - nextSlot: proposal.slotNumber.toBigInt() + 1n, - }), - ); + epochCache.getProposerInCurrentOrNextSlot.mockImplementation(async () => ({ + currentProposer: await proposal.getSender(), + nextProposer: await proposal.getSender(), + currentSlot: proposal.slotNumber.toBigInt(), + nextSlot: proposal.slotNumber.toBigInt() + 1n, + })); epochCache.isInCommittee.mockImplementation(() => Promise.resolve(true)); const val = ValidatorClient.new(config, epochCache, p2pClient); @@ -123,17 +121,15 @@ describe('ValidationService', () => { }); it('Should not return an attestation if the validator is not in the committee', async () => { - const proposal = makeBlockProposal(); + const proposal = await makeBlockProposal(); // Setup epoch cache mocks - epochCache.getProposerInCurrentOrNextSlot.mockImplementation(() => - Promise.resolve({ - currentProposer: proposal.getSender(), - nextProposer: proposal.getSender(), - currentSlot: proposal.slotNumber.toBigInt(), - nextSlot: proposal.slotNumber.toBigInt() + 1n, - }), - ); + epochCache.getProposerInCurrentOrNextSlot.mockImplementation(async () => ({ + currentProposer: await proposal.getSender(), + nextProposer: await proposal.getSender(), + currentSlot: proposal.slotNumber.toBigInt(), + nextSlot: proposal.slotNumber.toBigInt() + 1n, + })); epochCache.isInCommittee.mockImplementation(() => Promise.resolve(false)); const attestation = await validatorClient.attestToProposal(proposal); @@ -141,7 +137,7 @@ describe('ValidationService', () => { }); it('Should not return an attestation if the proposer is not the current proposer', async () => { - const proposal = makeBlockProposal(); + const proposal = await makeBlockProposal(); // Setup epoch cache mocks epochCache.getProposerInCurrentOrNextSlot.mockImplementation(() => @@ -159,17 +155,15 @@ describe('ValidationService', () => { }); it('Should not return an attestation if the proposal is not for the current or next slot', async () => { - const proposal = makeBlockProposal(); + const proposal = await makeBlockProposal(); // Setup epoch cache mocks - epochCache.getProposerInCurrentOrNextSlot.mockImplementation(() => - Promise.resolve({ - currentProposer: proposal.getSender(), - nextProposer: proposal.getSender(), - currentSlot: proposal.slotNumber.toBigInt() + 20n, - nextSlot: proposal.slotNumber.toBigInt() + 21n, - }), - ); + epochCache.getProposerInCurrentOrNextSlot.mockImplementation(async () => ({ + currentProposer: await proposal.getSender(), + nextProposer: await proposal.getSender(), + currentSlot: proposal.slotNumber.toBigInt() + 20n, + nextSlot: proposal.slotNumber.toBigInt() + 21n, + })); epochCache.isInCommittee.mockImplementation(() => Promise.resolve(true)); const attestation = await validatorClient.attestToProposal(proposal); @@ -184,13 +178,13 @@ describe('ValidationService', () => { const archive = Fr.random(); const txHashes = [0, 1, 2, 3, 4, 5].map(() => TxHash.random()); - const proposal = makeBlockProposal({ signer, archive, txHashes }); + const proposal = await makeBlockProposal({ signer, archive, txHashes }); // Mock the attestations to be returned - const expectedAttestations = [ + const expectedAttestations = await Promise.all([ makeBlockAttestation({ signer: attestor1, archive, txHashes }), makeBlockAttestation({ signer: attestor2, archive, txHashes }), - ]; + ]); p2pClient.getAttestationsForSlot.mockImplementation((slot, proposalId) => { if ( slot === proposal.payload.header.globalVariables.slotNumber.toBigInt() && diff --git a/yarn-project/validator-client/src/validator.ts b/yarn-project/validator-client/src/validator.ts index d9ee3f53374..9a0d3d60407 100644 --- a/yarn-project/validator-client/src/validator.ts +++ b/yarn-project/validator-client/src/validator.ts @@ -32,7 +32,7 @@ import { ValidatorMetrics } from './metrics.js'; * We reuse the sequencer's block building functionality for re-execution */ type BlockBuilderCallback = ( - txs: Iterable, + txs: Iterable | AsyncIterableIterator, globalVariables: GlobalVariables, opts?: { validateOnly?: boolean }, ) => Promise<{ @@ -251,18 +251,18 @@ export class ValidatorClient extends WithTracer implements Validator { this.log.verbose(`Transaction re-execution complete`); if (numFailedTxs > 0) { - this.metrics.recordFailedReexecution(proposal); + await this.metrics.recordFailedReexecution(proposal); throw new ReExFailedTxsError(numFailedTxs); } if (block.body.txEffects.length !== txHashes.length) { - this.metrics.recordFailedReexecution(proposal); + await this.metrics.recordFailedReexecution(proposal); throw new ReExTimeoutError(); } // This function will throw an error if state updates do not match if (!block.archive.root.equals(proposal.archive)) { - this.metrics.recordFailedReexecution(proposal); + await this.metrics.recordFailedReexecution(proposal); throw new ReExStateMismatchError(); } } @@ -327,11 +327,12 @@ export class ValidatorClient extends WithTracer implements Validator { let attestations: BlockAttestation[] = []; while (true) { const collectedAttestations = [myAttestation, ...(await this.p2pClient.getAttestationsForSlot(slot, proposalId))]; - const newAttestations = collectedAttestations.filter( - collected => !attestations.some(old => old.getSender().equals(collected.getSender())), - ); - for (const attestation of newAttestations) { - this.log.debug(`Received attestation for slot ${slot} from ${attestation.getSender().toString()}`); + const oldSenders = await Promise.all(attestations.map(attestation => attestation.getSender())); + for (const collected of collectedAttestations) { + const collectedSender = await collected.getSender(); + if (!oldSenders.some(sender => sender.equals(collectedSender))) { + this.log.debug(`Received attestation for slot ${slot} from ${collectedSender.toString()}`); + } } attestations = collectedAttestations; diff --git a/yarn-project/world-state/src/native/merkle_trees_facade.ts b/yarn-project/world-state/src/native/merkle_trees_facade.ts index 4e2c1d40611..cf0f561d751 100644 --- a/yarn-project/world-state/src/native/merkle_trees_facade.ts +++ b/yarn-project/world-state/src/native/merkle_trees_facade.ts @@ -193,7 +193,7 @@ export class MerkleTreesForkFacade extends MerkleTreesFacade implements MerkleTr async updateArchive(header: BlockHeader): Promise { await this.instance.call(WorldStateMessageType.UPDATE_ARCHIVE, { forkId: this.revision.forkId, - blockHeaderHash: header.hash().toBuffer(), + blockHeaderHash: (await header.hash()).toBuffer(), blockStateRef: blockStateReference(header.state), }); } diff --git a/yarn-project/world-state/src/native/native_world_state.ts b/yarn-project/world-state/src/native/native_world_state.ts index dd926aadcea..15953f0f0b0 100644 --- a/yarn-project/world-state/src/native/native_world_state.ts +++ b/yarn-project/world-state/src/native/native_world_state.ts @@ -143,7 +143,7 @@ export class NativeWorldStateService implements MerkleTreeDatabase { // the initial header _must_ be the first element in the archive tree // if this assertion fails, check that the hashing done in Header in yarn-project matches the initial header hash done in world_state.cpp - const indices = await committed.findLeafIndices(MerkleTreeId.ARCHIVE, [this.initialHeader.hash()]); + const indices = await committed.findLeafIndices(MerkleTreeId.ARCHIVE, [await this.initialHeader.hash()]); const initialHeaderIndex = indices[0]; assert.strictEqual(initialHeaderIndex, 0n, 'Invalid initial archive state'); } @@ -193,7 +193,7 @@ export class NativeWorldStateService implements MerkleTreeDatabase { WorldStateMessageType.SYNC_BLOCK, { blockNumber: l2Block.number, - blockHeaderHash: l2Block.header.hash(), + blockHeaderHash: await l2Block.header.hash(), paddedL1ToL2Messages: paddedL1ToL2Messages.map(serializeLeaf), paddedNoteHashes: paddedNoteHashes.map(serializeLeaf), paddedNullifiers: paddedNullifiers.map(serializeLeaf), diff --git a/yarn-project/world-state/src/native/world_state_version.ts b/yarn-project/world-state/src/native/world_state_version.ts index 2be422b9e16..95dff163628 100644 --- a/yarn-project/world-state/src/native/world_state_version.ts +++ b/yarn-project/world-state/src/native/world_state_version.ts @@ -9,7 +9,7 @@ export class WorldStateVersion { static async readVersion(filename: string) { const versionData = await readFile(filename, 'utf-8').catch(() => undefined); - return versionData === undefined ? undefined : jsonParseWithSchema(versionData, WorldStateVersion.schema); + return versionData === undefined ? undefined : await jsonParseWithSchema(versionData, WorldStateVersion.schema); } public async writeVersionFile(filename: string) { diff --git a/yarn-project/world-state/src/synchronizer/server_world_state_synchronizer.test.ts b/yarn-project/world-state/src/synchronizer/server_world_state_synchronizer.test.ts index b1cc2df2cd8..2e7d3d71a46 100644 --- a/yarn-project/world-state/src/synchronizer/server_world_state_synchronizer.test.ts +++ b/yarn-project/world-state/src/synchronizer/server_world_state_synchronizer.test.ts @@ -38,18 +38,17 @@ describe('ServerWorldStateSynchronizer', () => { const LATEST_BLOCK_NUMBER = 5; - beforeAll(() => { + beforeAll(async () => { log = createLogger('world-state:test:server_world_state_synchronizer'); // Seed l1 to l2 msgs l1ToL2Messages = times(randomInt(2 ** L1_TO_L2_MSG_SUBTREE_HEIGHT), Fr.random); // Compute inHash for verification - inHash = new MerkleTreeCalculator( - L1_TO_L2_MSG_SUBTREE_HEIGHT, - Buffer.alloc(32), - new SHA256Trunc().hash, - ).computeTreeRoot(l1ToL2Messages.map(msg => msg.toBuffer())); + const calculator = await MerkleTreeCalculator.create(L1_TO_L2_MSG_SUBTREE_HEIGHT, Buffer.alloc(32), (lhs, rhs) => + Promise.resolve(new SHA256Trunc().hash(lhs, rhs)), + ); + inHash = await calculator.computeTreeRoot(l1ToL2Messages.map(msg => msg.toBuffer())); }); beforeEach(() => { diff --git a/yarn-project/world-state/src/synchronizer/server_world_state_synchronizer.ts b/yarn-project/world-state/src/synchronizer/server_world_state_synchronizer.ts index ff631287a65..dd5d0337108 100644 --- a/yarn-project/world-state/src/synchronizer/server_world_state_synchronizer.ts +++ b/yarn-project/world-state/src/synchronizer/server_world_state_synchronizer.ts @@ -170,7 +170,7 @@ export class ServerWorldStateSynchronizer /** Returns the L2 block hash for a given number. Used by the L2BlockStream for detecting reorgs. */ public async getL2BlockHash(number: number): Promise { if (number === 0) { - return Promise.resolve(this.merkleTreeCommitted.getInitialHeader().hash().toString()); + return (await this.merkleTreeCommitted.getInitialHeader().hash()).toString(); } if (this.latestBlockHashQuery?.hash === undefined || number !== this.latestBlockHashQuery.blockNumber) { this.latestBlockHashQuery = { @@ -257,7 +257,7 @@ export class ServerWorldStateSynchronizer // Note that we cannot optimize this check by checking the root of the subtree after inserting the messages // to the real L1_TO_L2_MESSAGE_TREE (like we do in merkleTreeDb.handleL2BlockAndMessages(...)) because that // tree uses pedersen and we don't have access to the converted root. - this.verifyMessagesHashToInHash(l1ToL2Messages, l2Block.header.contentCommitment.inHash); + await this.verifyMessagesHashToInHash(l1ToL2Messages, l2Block.header.contentCommitment.inHash); // If the above check succeeds, we can proceed to handle the block. const result = await this.merkleTreeDb.handleL2BlockAndMessages(l2Block, l1ToL2Messages); @@ -311,14 +311,14 @@ export class ServerWorldStateSynchronizer * @param inHash - The inHash of the block. * @throws If the L1 to L2 messages do not hash to the block inHash. */ - protected verifyMessagesHashToInHash(l1ToL2Messages: Fr[], inHash: Buffer) { - const treeCalculator = new MerkleTreeCalculator( + protected async verifyMessagesHashToInHash(l1ToL2Messages: Fr[], inHash: Buffer) { + const treeCalculator = await MerkleTreeCalculator.create( L1_TO_L2_MSG_SUBTREE_HEIGHT, Buffer.alloc(32), - new SHA256Trunc().hash, + (lhs, rhs) => Promise.resolve(new SHA256Trunc().hash(lhs, rhs)), ); - const root = treeCalculator.computeTreeRoot(l1ToL2Messages.map(msg => msg.toBuffer())); + const root = await treeCalculator.computeTreeRoot(l1ToL2Messages.map(msg => msg.toBuffer())); if (!root.equals(inHash)) { throw new Error('Obtained L1 to L2 messages failed to be hashed to the block inHash'); diff --git a/yarn-project/world-state/src/test/integration.test.ts b/yarn-project/world-state/src/test/integration.test.ts index e8b9c8aded9..3e4ed20c462 100644 --- a/yarn-project/world-state/src/test/integration.test.ts +++ b/yarn-project/world-state/src/test/integration.test.ts @@ -286,7 +286,9 @@ describe('world-state integration', () => { class TestWorldStateSynchronizer extends ServerWorldStateSynchronizer { // Skip validation for the sake of this test - protected override verifyMessagesHashToInHash(_l1ToL2Messages: Fr[], _inHash: Buffer): void {} + protected override verifyMessagesHashToInHash(_l1ToL2Messages: Fr[], _inHash: Buffer): Promise { + return Promise.resolve(); + } // Stops the block stream but not the db so we can reuse it for another synchronizer public async stopBlockStream() { diff --git a/yarn-project/world-state/src/world-state-db/merkle_trees.ts b/yarn-project/world-state/src/world-state-db/merkle_trees.ts index 18e84758ef1..a7ffcb2120b 100644 --- a/yarn-project/world-state/src/world-state-db/merkle_trees.ts +++ b/yarn-project/world-state/src/world-state-db/merkle_trees.ts @@ -531,7 +531,7 @@ export class MerkleTrees implements MerkleTreeAdminDatabase { throw new Error('State in header does not match current state'); } - const blockHash = header.hash(); + const blockHash = await header.hash(); await this.#appendLeaves(MerkleTreeId.ARCHIVE, [blockHash]); } diff --git a/yarn-project/yarn.lock b/yarn-project/yarn.lock index 3a9045f4188..a9e250f3a67 100644 --- a/yarn-project/yarn.lock +++ b/yarn-project/yarn.lock @@ -199,6 +199,7 @@ __metadata: "@types/node": "npm:^18.7.23" axios: "npm:^1.7.2" buffer: "npm:^6.0.3" + copy-webpack-plugin: "npm:^12.0.2" crypto-browserify: "npm:^3.12.0" jest: "npm:^29.5.0" jest-mock-extended: "npm:^3.0.3" @@ -630,6 +631,7 @@ __metadata: "@viem/anvil": "npm:^0.0.9" buffer: "npm:^6.0.3" concurrently: "npm:^7.6.0" + copy-webpack-plugin: "npm:^12.0.2" crypto-browserify: "npm:^3.12.0" fs-extra: "npm:^11.2.0" get-port: "npm:^7.1.0"