From 39e88bff5bb8488a7d0924918586a253019a397a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicol=C3=A1s=20Venturo?= Date: Thu, 30 May 2024 20:45:22 +0000 Subject: [PATCH 01/60] Adapt public immutable --- .../aztec/src/state_vars/public_immutable.nr | 58 +++++----- .../src/state_vars/public_immutable/test.nr | 105 ++++++++++++++++++ .../aztec-nr/aztec/src/test/mocks.nr | 1 + .../aztec/src/test/mocks/mock_struct.nr | 36 ++++++ 4 files changed, 175 insertions(+), 25 deletions(-) create mode 100644 noir-projects/aztec-nr/aztec/src/state_vars/public_immutable/test.nr create mode 100644 noir-projects/aztec-nr/aztec/src/test/mocks/mock_struct.nr diff --git a/noir-projects/aztec-nr/aztec/src/state_vars/public_immutable.nr b/noir-projects/aztec-nr/aztec/src/state_vars/public_immutable.nr index 5d9ad2b7e300..b35f4eae1cf2 100644 --- a/noir-projects/aztec-nr/aztec/src/state_vars/public_immutable.nr +++ b/noir-projects/aztec-nr/aztec/src/state_vars/public_immutable.nr @@ -1,6 +1,11 @@ -use crate::{context::PublicContext, oracle::{storage::{storage_read, storage_write}}, state_vars::storage::Storage}; +use crate::{ + context::PublicContext, public_storage, oracle::{storage::{storage_read, storage_write}}, + state_vars::storage::Storage +}; use dep::protocol_types::{constants::INITIALIZATION_SLOT_SEPARATOR, traits::{Deserialize, Serialize}}; +mod test; + // Just like SharedImmutable but without the ability to read from private functions. // docs:start:public_immutable_struct struct PublicImmutable { @@ -13,53 +18,56 @@ impl Storage for PublicImmutable {} impl PublicImmutable { // docs:start:public_immutable_struct_new - pub fn new( - // Note: Passing the contexts to new(...) just to have an interface compatible with a Map. - context: Context, - storage_slot: Field - ) -> Self { + pub fn new(context: Context, storage_slot: Field) -> Self { assert(storage_slot != 0, "Storage slot 0 not allowed. Storage slots must start from 1."); PublicImmutable { context, storage_slot } } // docs:end:public_immutable_struct_new + + fn get_initialization_slot(self) -> Field { + INITIALIZATION_SLOT_SEPARATOR + self.storage_slot + } } impl PublicImmutable { // docs:start:public_immutable_struct_write pub fn initialize(self, value: T) where T: Serialize { - // TODO(#4738): Uncomment the following assert - // assert( - // self.context.public.unwrap_unchecked().is_deployment(), "PublicImmutable can only be initialized during contract deployment" - // ); - - // We check that the struct is not yet initialized by checking if the initialization slot is 0 - let initialization_slot = INITIALIZATION_SLOT_SEPARATOR + self.storage_slot; - let fields_read: [Field; 1] = storage_read(initialization_slot); - assert(fields_read[0] == 0, "PublicImmutable already initialized"); + assert(!self.is_initialized(), "PublicImmutable already initialized"); // We populate the initialization slot with a non-zero value to indicate that the struct is initialized - storage_write(initialization_slot, [0xdead]); + public_storage::write(self.get_initialization_slot(), 0xdead); - let fields_write = T::serialize(value); - storage_write(self.storage_slot, fields_write); + // And we then store the actual value + public_storage::write(self.storage_slot, value); } // docs:end:public_immutable_struct_write // Note that we don't access the context, but we do call oracles that are only available in public // docs:start:public_immutable_struct_read pub fn read(self) -> T where T: Deserialize { - let fields = storage_read(self.storage_slot); - T::deserialize(fields) + assert(self.is_initialized(), "PublicImmutable not initialized"); + public_storage::read(self.storage_slot) } // docs:end:public_immutable_struct_read + + pub fn is_initialized(self) -> bool { + let init_slot_contents: Field = public_storage::read(self.get_initialization_slot()); + init_slot_contents != 0 + } } impl PublicImmutable { + // Note that this is the exact same implementation as for public execution, though it might change in the future + // since unconstrained execution might not rely on the same oracles as used for public execution (which + // transpile to AVM opcodes). + pub fn read(self) -> T where T: Deserialize { - // Note that this is the exact same implementation as for public execution, though it might change in the future - // since unconstrained execution might not rely on the same oracles as used for public execution (which - // transpile to AVM opcodes). - let fields = storage_read(self.storage_slot); - T::deserialize(fields) + assert(self.is_initialized(), "PublicImmutable not initialized"); + public_storage::read(self.storage_slot) + } + + pub fn is_initialized(self) -> bool { + let init_slot_contents: Field = public_storage::read(self.get_initialization_slot()); + init_slot_contents != 0 } } diff --git a/noir-projects/aztec-nr/aztec/src/state_vars/public_immutable/test.nr b/noir-projects/aztec-nr/aztec/src/state_vars/public_immutable/test.nr new file mode 100644 index 000000000000..86c5dc49a183 --- /dev/null +++ b/noir-projects/aztec-nr/aztec/src/state_vars/public_immutable/test.nr @@ -0,0 +1,105 @@ +use crate::{context::PublicContext, state_vars::public_immutable::PublicImmutable}; +use crate::test::{helpers::context_builder::ContextBuilder, mocks::mock_struct::MockStruct}; +use dep::std::test::OracleMock; +use dep::protocol_types::traits::Serialize; + +fn setup() -> PublicImmutable { + let mut context = ContextBuilder::new().public(); + let storage_slot = 7; + + PublicImmutable::new(&mut context, storage_slot) +} + +impl PublicImmutable { + fn mock_initialized_storage(self) { + let _ = OracleMock::mock("storageRead").with_params((self.get_initialization_slot(), 1)).returns([1]).times(1); + } + + fn mock_uninitialized_storage(self) { + let _ = OracleMock::mock("storageRead").with_params((self.get_initialization_slot(), 1)).returns([0]).times(1); + } + + fn mock_initialize_write(self) -> OracleMock { + // Unfortunately we can't only pass the storage slot to with_params, so we must know the initialization value. + OracleMock::mock("storageWrite").with_params((self.get_initialization_slot(), [0xdead])).returns([0; 1]) + } + + fn assert_initialize_write(self, mock: OracleMock) { + assert(mock.get_last_params() != (self.storage_slot, [0])); + } + + fn mock_value_write(self, value: MockStruct) -> OracleMock where MockStruct: Serialize { + // Unfortunately we can't only pass the storage slot to with_params, so we must know what will be stored as + // well. + OracleMock::mock("storageWrite").with_params((self.storage_slot, value.serialize())).returns([0; N]) + } + + fn mock_value_read(self, value: MockStruct) where MockStruct: Serialize { + // TBD https://github.com/noir-lang/noir/issues/4633: replace 2 with N + let _ = OracleMock::mock("storageRead").with_params((self.storage_slot, 2)).returns(value.serialize()).times(1); + } + + fn assert_value_write(self, mock: OracleMock, value: MockStruct) { + assert_eq(mock.get_last_params(), (self.storage_slot, value.serialize())); + } +} + +#[test] +fn test_is_initialized() { + let state_var = setup(); + + state_var.mock_uninitialized_storage(); + assert_eq(state_var.is_initialized(), false); + + state_var.mock_initialized_storage(); + assert_eq(state_var.is_initialized(), true); +} + +#[test] +fn test_initialize_uninitialized() { + let state_var = setup(); + + let value = MockStruct::new(5, 6); + + state_var.mock_uninitialized_storage(); + + let init_mock = state_var.mock_initialize_write(); + let value_mock = state_var.mock_value_write(value); + + state_var.initialize(value); + + state_var.assert_initialize_write(init_mock); + state_var.assert_value_write(value_mock, value); +} + +#[test(should_fail_with="PublicImmutable already initialized")] +fn test_initialize_already_initialized() { + let state_var = setup(); + + let value = MockStruct::new(5, 6); + + state_var.mock_initialized_storage(); + + state_var.initialize(value); +} + +#[test] +fn test_read_initialized() { + let state_var = setup(); + + let value = MockStruct::new(5, 6); + + state_var.mock_initialized_storage(); + state_var.mock_value_read(value); + + assert_eq(state_var.read(), value); +} + +#[test(should_fail_with="PublicImmutable not initialized")] +fn test_read_uninitialized() { + let state_var = setup(); + + state_var.mock_uninitialized_storage(); + + let _ = state_var.read(); +} diff --git a/noir-projects/aztec-nr/aztec/src/test/mocks.nr b/noir-projects/aztec-nr/aztec/src/test/mocks.nr index fb8ef27c7bf9..148cc284a9f0 100644 --- a/noir-projects/aztec-nr/aztec/src/test/mocks.nr +++ b/noir-projects/aztec-nr/aztec/src/test/mocks.nr @@ -1 +1,2 @@ mod mock_note; +mod mock_struct; diff --git a/noir-projects/aztec-nr/aztec/src/test/mocks/mock_struct.nr b/noir-projects/aztec-nr/aztec/src/test/mocks/mock_struct.nr new file mode 100644 index 000000000000..1a0ed1a304ee --- /dev/null +++ b/noir-projects/aztec-nr/aztec/src/test/mocks/mock_struct.nr @@ -0,0 +1,36 @@ +use dep::protocol_types::traits::{Eq, Serialize, Deserialize}; + +struct MockStruct { + a: Field, + b: Field, +} + +impl MockStruct { + fn new(a: Field, b: Field) -> Self { + Self { a, b } + } +} + +impl Eq for MockStruct { + fn eq(self, other: Self) -> bool { + (self.a == other.a) & (self.b == other.b) + } +} + +impl Serialize<2> for MockStruct { + fn serialize(self) -> [Field; 2] { + [self.a, self.b] + } +} + +impl Deserialize<2> for MockStruct { + fn deserialize(fields: [Field; 2]) -> Self { + Self { a: fields[0], b: fields[1] } + } +} + +#[test] +fn test_serde() { + let val = MockStruct::new(5, 6); + assert_eq(val, MockStruct::deserialize(val.serialize())); +} From de4dfef19afd9960b3d85c2e5ce70c206f95f727 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicol=C3=A1s=20Venturo?= Date: Fri, 31 May 2024 03:23:51 +0000 Subject: [PATCH 02/60] Add comments to private imnmutable --- .../aztec/src/state_vars/private_immutable.nr | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/noir-projects/aztec-nr/aztec/src/state_vars/private_immutable.nr b/noir-projects/aztec-nr/aztec/src/state_vars/private_immutable.nr index 3c70465a8d8b..8e3fa474323f 100644 --- a/noir-projects/aztec-nr/aztec/src/state_vars/private_immutable.nr +++ b/noir-projects/aztec-nr/aztec/src/state_vars/private_immutable.nr @@ -50,7 +50,8 @@ impl PrivateImmutable { ovpk_m: GrumpkinPoint, ivpk_m: GrumpkinPoint ) where Note: NoteInterface { - // Nullify the storage slot. + // Nullify the storage slot - since we always push the same nullifier, this can only be done once, as further + // attempts to do it will result in the transaction being reverted by the sequencer. let nullifier = self.compute_initialization_nullifier(); self.context.push_new_nullifier(nullifier, 0); @@ -60,8 +61,10 @@ impl PrivateImmutable { // docs:start:get_note pub fn get_note(self) -> Note where Note: NoteInterface { - let storage_slot = self.storage_slot; - get_note(self.context, storage_slot) + // We do not need to check for initialization because we'll only be able to read a note and prove its inclusion + // if one was actually created, and this can be done during initialization. Therefore, a successful read implies + // initialization. + get_note(self.context, self.storage_slot) } // docs:end:get_note } @@ -77,8 +80,8 @@ impl PrivateImmutable { // view_note does not actually use the context, but it calls oracles that are only available in private // docs:start:view_note unconstrained pub fn view_note(self) -> Note where Note: NoteInterface { - let mut options = NoteViewerOptions::new(); - view_notes(self.storage_slot, options.set_limit(1))[0].unwrap() + let options = NoteViewerOptions::new(); + view_notes(self.storage_slot, options)[0].unwrap() } // docs:end:view_note } From 8c5bdf1a62ef263953afc7087297cac2887652c2 Mon Sep 17 00:00:00 2001 From: thunkar Date: Mon, 3 Jun 2024 17:14:35 +0000 Subject: [PATCH 03/60] TXE --- .../src/state_vars/public_immutable/test.nr | 8 +- yarn-project/package.json | 1 + yarn-project/txe/.eslintrc.cjs | 1 + yarn-project/txe/package.json | 74 ++++++ yarn-project/txe/src/bin/index.ts | 28 ++ yarn-project/txe/src/http_rpc_server/index.ts | 29 ++ yarn-project/txe/src/index.ts | 1 + .../txe/src/txe_service/txe_service.ts | 251 ++++++++++++++++++ yarn-project/txe/src/util/encoding.ts | 29 ++ yarn-project/txe/tsconfig.json | 9 + yarn-project/yarn.lock | 20 ++ 11 files changed, 444 insertions(+), 7 deletions(-) create mode 100644 yarn-project/txe/.eslintrc.cjs create mode 100644 yarn-project/txe/package.json create mode 100644 yarn-project/txe/src/bin/index.ts create mode 100644 yarn-project/txe/src/http_rpc_server/index.ts create mode 100644 yarn-project/txe/src/index.ts create mode 100644 yarn-project/txe/src/txe_service/txe_service.ts create mode 100644 yarn-project/txe/src/util/encoding.ts create mode 100644 yarn-project/txe/tsconfig.json diff --git a/noir-projects/aztec-nr/aztec/src/state_vars/public_immutable/test.nr b/noir-projects/aztec-nr/aztec/src/state_vars/public_immutable/test.nr index 86c5dc49a183..65fe0a7a253f 100644 --- a/noir-projects/aztec-nr/aztec/src/state_vars/public_immutable/test.nr +++ b/noir-projects/aztec-nr/aztec/src/state_vars/public_immutable/test.nr @@ -61,15 +61,9 @@ fn test_initialize_uninitialized() { let value = MockStruct::new(5, 6); - state_var.mock_uninitialized_storage(); - - let init_mock = state_var.mock_initialize_write(); - let value_mock = state_var.mock_value_write(value); - state_var.initialize(value); - state_var.assert_initialize_write(init_mock); - state_var.assert_value_write(value_mock, value); + assert(state_var.read() == value); } #[test(should_fail_with="PublicImmutable already initialized")] diff --git a/yarn-project/package.json b/yarn-project/package.json index fa08f05b063d..388c8f4d6df4 100644 --- a/yarn-project/package.json +++ b/yarn-project/package.json @@ -51,6 +51,7 @@ "sequencer-client", "scripts", "types", + "txe", "world-state" ], "prettier": "@aztec/foundation/prettier", diff --git a/yarn-project/txe/.eslintrc.cjs b/yarn-project/txe/.eslintrc.cjs new file mode 100644 index 000000000000..e659927475c0 --- /dev/null +++ b/yarn-project/txe/.eslintrc.cjs @@ -0,0 +1 @@ +module.exports = require('@aztec/foundation/eslint'); diff --git a/yarn-project/txe/package.json b/yarn-project/txe/package.json new file mode 100644 index 000000000000..c3c56084a646 --- /dev/null +++ b/yarn-project/txe/package.json @@ -0,0 +1,74 @@ +{ + "name": "@aztec/txe", + "version": "0.0.0", + "type": "module", + "exports": "./dest/index.js", + "bin": "./dest/bin/index.js", + "typedocOptions": { + "entryPoints": [ + "./src/index.ts" + ], + "name": "TXE", + "tsconfig": "./tsconfig.json" + }, + "scripts": { + "build": "yarn clean && tsc -b", + "build:dev": "tsc -b --watch", + "clean": "rm -rf ./dest .tsbuildinfo", + "formatting": "run -T prettier --check ./src && run -T eslint ./src", + "formatting:fix": "run -T eslint --fix ./src && run -T prettier -w ./src", + "test": "NODE_NO_WARNINGS=1 node --experimental-vm-modules ../node_modules/.bin/jest --passWithNoTests", + "start": "DEBUG='aztec:*' && node ./dest/bin/index.js" + }, + "inherits": [ + "../package.common.json" + ], + "jest": { + "moduleNameMapper": { + "^(\\.{1,2}/.*)\\.[cm]?js$": "$1" + }, + "testRegex": "./src/.*\\.test\\.(js|mjs|ts)$", + "rootDir": "./src", + "workerThreads": true, + "transform": { + "^.+\\.tsx?$": [ + "@swc/jest" + ] + }, + "extensionsToTreatAsEsm": [ + ".ts" + ], + "reporters": [ + [ + "default", + { + "summaryThreshold": 9999 + } + ] + ] + }, + "dependencies": { + "@aztec/circuits.js": "workspace:^", + "@aztec/foundation": "workspace:^", + "@aztec/types": "workspace:^", + "@aztec/world-state": "workspace:^" + }, + "devDependencies": { + "@jest/globals": "^29.5.0", + "@types/jest": "^29.5.0", + "@types/node": "^18.7.23", + "jest": "^29.5.0", + "jest-mock-extended": "^3.0.3", + "ts-node": "^10.9.1", + "typescript": "^5.0.4" + }, + "files": [ + "dest", + "src", + "!*.test.*" + ], + "types": "./dest/index.d.ts", + "engines": { + "node": ">=18" + } +} diff --git a/yarn-project/txe/src/bin/index.ts b/yarn-project/txe/src/bin/index.ts new file mode 100644 index 000000000000..9787db5de838 --- /dev/null +++ b/yarn-project/txe/src/bin/index.ts @@ -0,0 +1,28 @@ +#!/usr/bin/env -S node --no-warnings +import { createDebugLogger } from '@aztec/foundation/log'; + +import { startTXEHttpServer } from '../index.js'; +import { TXE, TXEService } from '../txe_service/txe_service.js'; + +const { TXE_PORT = 8080 } = process.env; + +const logger = createDebugLogger('aztec:txe_service'); + +/** + * Create and start a new PXE HTTP Server + */ +async function main() { + logger.info(`Setting up TXE...`); + + const txe = await TXE.init(logger); + const txeService = new TXEService(txe); + + startTXEHttpServer(txeService, TXE_PORT); + + logger.info(`TXE listening on port ${TXE_PORT}`); +} + +main().catch(err => { + logger.error(err); + process.exit(1); +}); diff --git a/yarn-project/txe/src/http_rpc_server/index.ts b/yarn-project/txe/src/http_rpc_server/index.ts new file mode 100644 index 000000000000..f861fb446d0b --- /dev/null +++ b/yarn-project/txe/src/http_rpc_server/index.ts @@ -0,0 +1,29 @@ +import { JsonRpcServer } from '@aztec/foundation/json-rpc/server'; + +import http from 'http'; + +import { type TXEService } from '../txe_service/txe_service.js'; + +/** + * Wraps an instance of Private eXecution Environment (TXE) implementation to a JSON RPC HTTP interface. + * @returns A new instance of the HTTP server. + */ +export function createTXERpcServer(txeService: TXEService): JsonRpcServer { + return new JsonRpcServer(txeService, {}, {}, ['start', 'stop']); +} + +/** + * Creates an http server that forwards calls to the TXE and starts it on the given port. + * @param txeService - TXE that answers queries to the created HTTP server. + * @param port - Port to listen in. + * @returns A running http server. + */ +export function startTXEHttpServer(txeService: TXEService, port: string | number): http.Server { + const txeServer = createTXERpcServer(txeService); + + const app = txeServer.getApp(); + const httpServer = http.createServer(app.callback()); + httpServer.listen(port); + + return httpServer; +} diff --git a/yarn-project/txe/src/index.ts b/yarn-project/txe/src/index.ts new file mode 100644 index 000000000000..6f83d1f63b2e --- /dev/null +++ b/yarn-project/txe/src/index.ts @@ -0,0 +1 @@ +export * from './http_rpc_server/index.js'; diff --git a/yarn-project/txe/src/txe_service/txe_service.ts b/yarn-project/txe/src/txe_service/txe_service.ts new file mode 100644 index 000000000000..a8d3ae81ece0 --- /dev/null +++ b/yarn-project/txe/src/txe_service/txe_service.ts @@ -0,0 +1,251 @@ +import { + type MerkleTreeId, + type NoteStatus, + type NullifierMembershipWitness, + type PublicDataWitness, + type UnencryptedL2Log, +} from '@aztec/circuit-types'; +import { + type CompleteAddress, + type Header, + type KeyValidationRequest, + type PrivateCallStackItem, + type PublicCallRequest, +} from '@aztec/circuits.js'; +import { type FunctionSelector } from '@aztec/foundation/abi'; +import { AztecAddress } from '@aztec/foundation/aztec-address'; +import { Fr, type Point } from '@aztec/foundation/fields'; +import { type Logger } from '@aztec/foundation/log'; +import { openTmpStore } from '@aztec/kv-store/utils'; +import { + type ACVMField, + type MessageLoadOracleInputs, + type NoteData, + type TypedOracle, + WorldStateDB, + WorldStatePublicDB, + fromACVMField, + toACVMField, +} from '@aztec/simulator'; +import { type ContractInstance } from '@aztec/types/contracts'; +import { MerkleTrees } from '@aztec/world-state'; + +import { + ForeignCallArray, + ForeignCallSingle, + fromArray, + fromSingle, + toArray, + toForeignCallResult, +} from '../util/encoding.js'; + +export class TXE implements TypedOracle { + constructor( + private logger: Logger, + private worldStatePublicDB: WorldStatePublicDB, + private worldStateDB: WorldStateDB, + private contractAddress: AztecAddress, + ) {} + + static async init(logger: Logger) { + const store = openTmpStore(true); + const merkleTrees = await MerkleTrees.new(store, logger); + const worldStatePublicDB = new WorldStatePublicDB(merkleTrees.asLatest()); + const worldStateDB = new WorldStateDB(merkleTrees.asLatest()); + const contractAddress = AztecAddress.random(); + return new TXE(logger, worldStatePublicDB, worldStateDB, contractAddress); + } + + getRandomField() { + return Fr.random(); + } + + packArgumentsArray(_args: Fr[]): Promise { + throw new Error('Method not implemented.'); + } + packReturns(_returns: Fr[]): Promise { + throw new Error('Method not implemented.'); + } + unpackReturns(_returnsHash: Fr): Promise { + throw new Error('Method not implemented.'); + } + getKeyValidationRequest(_pkMHash: Fr): Promise { + throw new Error('Method not implemented.'); + } + getContractInstance(_address: AztecAddress): Promise { + throw new Error('Method not implemented.'); + } + getMembershipWitness(_blockNumber: number, _treeId: MerkleTreeId, _leafValue: Fr): Promise { + throw new Error('Method not implemented.'); + } + getSiblingPath(_blockNumber: number, _treeId: MerkleTreeId, _leafIndex: Fr): Promise { + throw new Error('Method not implemented.'); + } + getNullifierMembershipWitness(_blockNumber: number, _nullifier: Fr): Promise { + throw new Error('Method not implemented.'); + } + getPublicDataTreeWitness(_blockNumber: number, _leafSlot: Fr): Promise { + throw new Error('Method not implemented.'); + } + getLowNullifierMembershipWitness( + _blockNumber: number, + _nullifier: Fr, + ): Promise { + throw new Error('Method not implemented.'); + } + getHeader(_blockNumber: number): Promise
{ + throw new Error('Method not implemented.'); + } + getCompleteAddress(_account: AztecAddress): Promise { + throw new Error('Method not implemented.'); + } + getAuthWitness(_messageHash: Fr): Promise { + throw new Error('Method not implemented.'); + } + popCapsule(): Promise { + throw new Error('Method not implemented.'); + } + getNotes( + _storageSlot: Fr, + _numSelects: number, + _selectByIndexes: number[], + _selectByOffsets: number[], + _selectByLengths: number[], + _selectValues: Fr[], + _selectComparators: number[], + _sortByIndexes: number[], + _sortByOffsets: number[], + _sortByLengths: number[], + _sortOrder: number[], + _limit: number, + _offset: number, + _status: NoteStatus, + ): Promise { + throw new Error('Method not implemented.'); + } + notifyCreatedNote(_storageSlot: Fr, _noteTypeId: Fr, _note: Fr[], _innerNoteHash: Fr, _counter: number): void { + throw new Error('Method not implemented.'); + } + notifyNullifiedNote(_innerNullifier: Fr, _innerNoteHash: Fr, _counter: number): Promise { + throw new Error('Method not implemented.'); + } + checkNullifierExists(_innerNullifier: Fr): Promise { + throw new Error('Method not implemented.'); + } + getL1ToL2MembershipWitness( + _contractAddress: AztecAddress, + _messageHash: Fr, + _secret: Fr, + ): Promise> { + throw new Error('Method not implemented.'); + } + async storageRead(startStorageSlot: Fr, numberOfElements: number): Promise { + console.log(`startStorageSlot ${startStorageSlot}`); + console.log(`numberOfElements ${numberOfElements}`); + const values = []; + for (let i = 0n; i < numberOfElements; i++) { + const storageSlot = startStorageSlot.add(new Fr(i)); + const value = await this.worldStatePublicDB.storageRead(this.contractAddress, storageSlot); + this.logger.debug(`Oracle storage read: slot=${storageSlot.toString()} value=${value}`); + values.push(value); + } + return values; + } + async storageWrite(startStorageSlot: Fr, values: Fr[]): Promise { + return await Promise.all( + values.map(async (value, i) => { + const storageSlot = startStorageSlot.add(new Fr(i)); + const result = await this.worldStatePublicDB.storageWrite(this.contractAddress, storageSlot, value); + this.logger.debug(`Oracle storage write: slot=${storageSlot.toString()} value=${value}`); + return new Fr(result); + }), + ); + } + emitEncryptedLog(_contractAddress: AztecAddress, _randomness: Fr, _encryptedNote: Buffer, _counter: number): void { + throw new Error('Method not implemented.'); + } + emitEncryptedNoteLog(_noteHashCounter: number, _encryptedNote: Buffer, _counter: number): void { + throw new Error('Method not implemented.'); + } + computeEncryptedLog( + _contractAddress: AztecAddress, + _storageSlot: Fr, + _noteTypeId: Fr, + _ovKeys: KeyValidationRequest, + _ivpkM: Point, + _preimage: Fr[], + ): Buffer { + throw new Error('Method not implemented.'); + } + emitUnencryptedLog(_log: UnencryptedL2Log, _counter: number): void { + throw new Error('Method not implemented.'); + } + emitContractClassUnencryptedLog(_log: UnencryptedL2Log, _counter: number): Fr { + throw new Error('Method not implemented.'); + } + callPrivateFunction( + _targetContractAddress: AztecAddress, + _functionSelector: FunctionSelector, + _argsHash: Fr, + _sideEffectCounter: number, + _isStaticCall: boolean, + _isDelegateCall: boolean, + ): Promise { + throw new Error('Method not implemented.'); + } + callPublicFunction( + _targetContractAddress: AztecAddress, + _functionSelector: FunctionSelector, + _argsHash: Fr, + _sideEffectCounter: number, + _isStaticCall: boolean, + _isDelegateCall: boolean, + ): Promise { + throw new Error('Method not implemented.'); + } + enqueuePublicFunctionCall( + _targetContractAddress: AztecAddress, + _functionSelector: FunctionSelector, + _argsHash: Fr, + _sideEffectCounter: number, + _isStaticCall: boolean, + _isDelegateCall: boolean, + ): Promise { + throw new Error('Method not implemented.'); + } + setPublicTeardownFunctionCall( + _targetContractAddress: AztecAddress, + _functionSelector: FunctionSelector, + _argsHash: Fr, + _sideEffectCounter: number, + _isStaticCall: boolean, + _isDelegateCall: boolean, + ): Promise { + throw new Error('Method not implemented.'); + } + aes128Encrypt(_input: Buffer, _initializationVector: Buffer, _key: Buffer): Buffer { + throw new Error('Method not implemented.'); + } + debugLog(_message: string, _fields: Fr[]): void { + throw new Error('Method not implemented.'); + } +} + +export class TXEService { + constructor(private typedOracle: TypedOracle) { + this.typedOracle = typedOracle; + } + + async storageRead(startStorageSlot: ForeignCallSingle, numberOfElements: ForeignCallSingle) { + const values = await this.typedOracle.storageRead( + fromSingle(startStorageSlot), + fromSingle(numberOfElements).toNumber(), + ); + return toForeignCallResult([toArray(values)]); + } + + async storageWrite(startStorageSlot: ForeignCallSingle, values: ForeignCallArray) { + const newValues = await this.typedOracle.storageWrite(fromSingle(startStorageSlot), fromArray(values)); + return toForeignCallResult([toArray(newValues)]); + } +} diff --git a/yarn-project/txe/src/util/encoding.ts b/yarn-project/txe/src/util/encoding.ts new file mode 100644 index 000000000000..fdc1941e6dd3 --- /dev/null +++ b/yarn-project/txe/src/util/encoding.ts @@ -0,0 +1,29 @@ +import { Fr } from '@aztec/foundation/fields'; + +export type ForeignCallSingle = { + Single: string; +}; + +export type ForeignCallArray = { + Array: string[]; +}; + +export function fromSingle(obj: ForeignCallSingle) { + return Fr.fromBuffer(Buffer.from(obj.Single, 'hex')); +} + +export function fromArray(obj: ForeignCallArray) { + return obj.Array.map(str => Fr.fromBuffer(Buffer.from(str, 'hex'))); +} + +export function toSingle(obj: Fr) { + return { Single: obj.toString() }; +} + +export function toArray(objs: Fr[]) { + return { Array: objs.map(obj => obj.toString()) }; +} + +export function toForeignCallResult(obj: (ForeignCallSingle | ForeignCallArray)[]) { + return { values: obj }; +} diff --git a/yarn-project/txe/tsconfig.json b/yarn-project/txe/tsconfig.json new file mode 100644 index 000000000000..f67ddec9fd6b --- /dev/null +++ b/yarn-project/txe/tsconfig.json @@ -0,0 +1,9 @@ +{ + "extends": "..", + "compilerOptions": { + "outDir": "dest", + "rootDir": "src", + "tsBuildInfoFile": ".tsbuildinfo" + }, + "include": ["src"] +} diff --git a/yarn-project/yarn.lock b/yarn-project/yarn.lock index bac3f94a04cf..4ddea3b0d954 100644 --- a/yarn-project/yarn.lock +++ b/yarn-project/yarn.lock @@ -930,6 +930,26 @@ __metadata: languageName: unknown linkType: soft +"@aztec/txe@workspace:txe": + version: 0.0.0-use.local + resolution: "@aztec/txe@workspace:txe" + dependencies: + "@aztec/circuits.js": "workspace:^" + "@aztec/foundation": "workspace:^" + "@aztec/types": "workspace:^" + "@aztec/world-state": "workspace:^" + "@jest/globals": ^29.5.0 + "@types/jest": ^29.5.0 + "@types/node": ^18.7.23 + jest: ^29.5.0 + jest-mock-extended: ^3.0.3 + ts-node: ^10.9.1 + typescript: ^5.0.4 + bin: + txe: ./dest/bin/index.js + languageName: unknown + linkType: soft + "@aztec/types@workspace:^, @aztec/types@workspace:types": version: 0.0.0-use.local resolution: "@aztec/types@workspace:types" From 3fbc3ee5102619f70da04407894a37b60bae2716 Mon Sep 17 00:00:00 2001 From: thunkar Date: Mon, 3 Jun 2024 17:36:18 +0000 Subject: [PATCH 04/60] improvements --- yarn-project/txe/src/bin/index.ts | 5 +- .../txe/src/txe_service/txe_service.ts | 49 ++++++++++--------- 2 files changed, 28 insertions(+), 26 deletions(-) diff --git a/yarn-project/txe/src/bin/index.ts b/yarn-project/txe/src/bin/index.ts index 9787db5de838..365e2054be7b 100644 --- a/yarn-project/txe/src/bin/index.ts +++ b/yarn-project/txe/src/bin/index.ts @@ -2,7 +2,7 @@ import { createDebugLogger } from '@aztec/foundation/log'; import { startTXEHttpServer } from '../index.js'; -import { TXE, TXEService } from '../txe_service/txe_service.js'; +import { TXEService } from '../txe_service/txe_service.js'; const { TXE_PORT = 8080 } = process.env; @@ -14,8 +14,7 @@ const logger = createDebugLogger('aztec:txe_service'); async function main() { logger.info(`Setting up TXE...`); - const txe = await TXE.init(logger); - const txeService = new TXEService(txe); + const txeService = await TXEService.init(logger); startTXEHttpServer(txeService, TXE_PORT); diff --git a/yarn-project/txe/src/txe_service/txe_service.ts b/yarn-project/txe/src/txe_service/txe_service.ts index a8d3ae81ece0..e4d9a48026f8 100644 --- a/yarn-project/txe/src/txe_service/txe_service.ts +++ b/yarn-project/txe/src/txe_service/txe_service.ts @@ -16,23 +16,21 @@ import { type FunctionSelector } from '@aztec/foundation/abi'; import { AztecAddress } from '@aztec/foundation/aztec-address'; import { Fr, type Point } from '@aztec/foundation/fields'; import { type Logger } from '@aztec/foundation/log'; +import { type AztecKVStore } from '@aztec/kv-store'; import { openTmpStore } from '@aztec/kv-store/utils'; import { - type ACVMField, type MessageLoadOracleInputs, type NoteData, type TypedOracle, WorldStateDB, WorldStatePublicDB, - fromACVMField, - toACVMField, } from '@aztec/simulator'; import { type ContractInstance } from '@aztec/types/contracts'; -import { MerkleTrees } from '@aztec/world-state'; +import { type MerkleTreeOperations, MerkleTrees } from '@aztec/world-state'; import { - ForeignCallArray, - ForeignCallSingle, + type ForeignCallArray, + type ForeignCallSingle, fromArray, fromSingle, toArray, @@ -40,20 +38,12 @@ import { } from '../util/encoding.js'; export class TXE implements TypedOracle { - constructor( - private logger: Logger, - private worldStatePublicDB: WorldStatePublicDB, - private worldStateDB: WorldStateDB, - private contractAddress: AztecAddress, - ) {} + private worldStatePublicDB: WorldStatePublicDB; + private worldStateDB: WorldStateDB; - static async init(logger: Logger) { - const store = openTmpStore(true); - const merkleTrees = await MerkleTrees.new(store, logger); - const worldStatePublicDB = new WorldStatePublicDB(merkleTrees.asLatest()); - const worldStateDB = new WorldStateDB(merkleTrees.asLatest()); - const contractAddress = AztecAddress.random(); - return new TXE(logger, worldStatePublicDB, worldStateDB, contractAddress); + constructor(private logger: Logger, private trees: MerkleTreeOperations, private contractAddress: AztecAddress) { + this.worldStatePublicDB = new WorldStatePublicDB(this.trees); + this.worldStateDB = new WorldStateDB(this.trees); } getRandomField() { @@ -140,8 +130,6 @@ export class TXE implements TypedOracle { throw new Error('Method not implemented.'); } async storageRead(startStorageSlot: Fr, numberOfElements: number): Promise { - console.log(`startStorageSlot ${startStorageSlot}`); - console.log(`numberOfElements ${numberOfElements}`); const values = []; for (let i = 0n; i < numberOfElements; i++) { const storageSlot = startStorageSlot.add(new Fr(i)); @@ -151,6 +139,7 @@ export class TXE implements TypedOracle { } return values; } + async storageWrite(startStorageSlot: Fr, values: Fr[]): Promise { return await Promise.all( values.map(async (value, i) => { @@ -232,8 +221,22 @@ export class TXE implements TypedOracle { } export class TXEService { - constructor(private typedOracle: TypedOracle) { - this.typedOracle = typedOracle; + constructor(private typedOracle: TypedOracle, private store: AztecKVStore, private contractAddress: AztecAddress) {} + + static async init(logger: Logger, contractAddress = AztecAddress.random()) { + const store = openTmpStore(true); + const trees = await MerkleTrees.new(store, logger); + const txe = new TXE(logger, trees.asLatest(), contractAddress); + return new TXEService(txe, store, contractAddress); + } + + setContractAddress(address = AztecAddress.random()): AztecAddress { + this.contractAddress = address; + return this.contractAddress; + } + + async reset() { + await this.store.clear(); } async storageRead(startStorageSlot: ForeignCallSingle, numberOfElements: ForeignCallSingle) { From 58c4c8294c311e037e3ab78a9717dd00470ad7d6 Mon Sep 17 00:00:00 2001 From: thunkar Date: Mon, 3 Jun 2024 17:39:00 +0000 Subject: [PATCH 05/60] fixed tsconfig --- yarn-project/txe/tsconfig.json | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/yarn-project/txe/tsconfig.json b/yarn-project/txe/tsconfig.json index f67ddec9fd6b..effb5a7151c9 100644 --- a/yarn-project/txe/tsconfig.json +++ b/yarn-project/txe/tsconfig.json @@ -5,5 +5,37 @@ "rootDir": "src", "tsBuildInfoFile": ".tsbuildinfo" }, + "references": [ + { + "path": "../circuit-types" + }, + { + "path": "../circuits.js" + }, + { + "path": "../foundation" + }, + { + "path": "../noir-protocol-circuits-types" + }, + { + "path": "../protocol-contracts" + }, + { + "path": "../types" + }, + { + "path": "../world-state" + }, + { + "path": "../kv-store" + }, + { + "path": "../merkle-tree" + }, + { + "path": "../noir-contracts.js" + } + ], "include": ["src"] } From 84af5e866cd27edca10ff67d4316899c43bedc0b Mon Sep 17 00:00:00 2001 From: thunkar Date: Mon, 3 Jun 2024 18:03:41 +0000 Subject: [PATCH 06/60] public data tree witness --- yarn-project/txe/src/http_rpc_server/index.ts | 2 +- .../txe/src/txe_service/txe_service.ts | 35 ++++++++++++++++--- 2 files changed, 32 insertions(+), 5 deletions(-) diff --git a/yarn-project/txe/src/http_rpc_server/index.ts b/yarn-project/txe/src/http_rpc_server/index.ts index f861fb446d0b..69d9e08464b9 100644 --- a/yarn-project/txe/src/http_rpc_server/index.ts +++ b/yarn-project/txe/src/http_rpc_server/index.ts @@ -9,7 +9,7 @@ import { type TXEService } from '../txe_service/txe_service.js'; * @returns A new instance of the HTTP server. */ export function createTXERpcServer(txeService: TXEService): JsonRpcServer { - return new JsonRpcServer(txeService, {}, {}, ['start', 'stop']); + return new JsonRpcServer(txeService, {}, {}, ['init']); } /** diff --git a/yarn-project/txe/src/txe_service/txe_service.ts b/yarn-project/txe/src/txe_service/txe_service.ts index e4d9a48026f8..4efd24995450 100644 --- a/yarn-project/txe/src/txe_service/txe_service.ts +++ b/yarn-project/txe/src/txe_service/txe_service.ts @@ -1,16 +1,18 @@ import { - type MerkleTreeId, + MerkleTreeId, type NoteStatus, type NullifierMembershipWitness, - type PublicDataWitness, + PublicDataWitness, type UnencryptedL2Log, } from '@aztec/circuit-types'; import { type CompleteAddress, type Header, type KeyValidationRequest, + PUBLIC_DATA_TREE_HEIGHT, type PrivateCallStackItem, type PublicCallRequest, + PublicDataTreeLeafPreimage, } from '@aztec/circuits.js'; import { type FunctionSelector } from '@aztec/foundation/abi'; import { AztecAddress } from '@aztec/foundation/aztec-address'; @@ -74,8 +76,22 @@ export class TXE implements TypedOracle { getNullifierMembershipWitness(_blockNumber: number, _nullifier: Fr): Promise { throw new Error('Method not implemented.'); } - getPublicDataTreeWitness(_blockNumber: number, _leafSlot: Fr): Promise { - throw new Error('Method not implemented.'); + async getPublicDataTreeWitness(_blockNumber: number, leafSlot: Fr): Promise { + const committedDb = this.trees; + const lowLeafResult = await committedDb.getPreviousValueIndex(MerkleTreeId.PUBLIC_DATA_TREE, leafSlot.toBigInt()); + if (!lowLeafResult) { + return undefined; + } else { + const preimage = (await committedDb.getLeafPreimage( + MerkleTreeId.PUBLIC_DATA_TREE, + lowLeafResult.index, + )) as PublicDataTreeLeafPreimage; + const path = await committedDb.getSiblingPath( + MerkleTreeId.PUBLIC_DATA_TREE, + lowLeafResult.index, + ); + return new PublicDataWitness(lowLeafResult.index, preimage, path); + } } getLowNullifierMembershipWitness( _blockNumber: number, @@ -251,4 +267,15 @@ export class TXEService { const newValues = await this.typedOracle.storageWrite(fromSingle(startStorageSlot), fromArray(values)); return toForeignCallResult([toArray(newValues)]); } + + async getPublicDataTreeWitness(blockNumber: ForeignCallSingle, leafSlot: ForeignCallSingle) { + const parsedBlockNumber = fromSingle(blockNumber).toNumber(); + const parsedLeafSlot = fromSingle(leafSlot); + + const witness = await this.typedOracle.getPublicDataTreeWitness(parsedBlockNumber, parsedLeafSlot); + if (!witness) { + throw new Error(`Public data witness not found for slot ${parsedLeafSlot} at block ${parsedBlockNumber}.`); + } + return toForeignCallResult([toArray(witness.toFields())]); + } } From aab9959ea348864cc9617d2683ad1d0d6b33b990 Mon Sep 17 00:00:00 2001 From: thunkar Date: Mon, 3 Jun 2024 18:07:01 +0000 Subject: [PATCH 07/60] return null --- yarn-project/txe/src/txe_service/txe_service.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/yarn-project/txe/src/txe_service/txe_service.ts b/yarn-project/txe/src/txe_service/txe_service.ts index 4efd24995450..48f0fc4210c8 100644 --- a/yarn-project/txe/src/txe_service/txe_service.ts +++ b/yarn-project/txe/src/txe_service/txe_service.ts @@ -253,6 +253,7 @@ export class TXEService { async reset() { await this.store.clear(); + return toForeignCallResult([]); } async storageRead(startStorageSlot: ForeignCallSingle, numberOfElements: ForeignCallSingle) { From c94d32eaa7f6aa00c77ff6567d49e16d3a6c8e7e Mon Sep 17 00:00:00 2001 From: thunkar Date: Tue, 4 Jun 2024 09:31:38 +0000 Subject: [PATCH 08/60] more oracle calls --- yarn-project/txe/package.json | 2 + .../txe/src/txe_service/txe_service.ts | 112 +++++++++++++++--- yarn-project/yarn.lock | 2 + 3 files changed, 99 insertions(+), 17 deletions(-) diff --git a/yarn-project/txe/package.json b/yarn-project/txe/package.json index c3c56084a646..97214621a3a6 100644 --- a/yarn-project/txe/package.json +++ b/yarn-project/txe/package.json @@ -48,8 +48,10 @@ ] }, "dependencies": { + "@aztec/circuit-types": "workspace:^", "@aztec/circuits.js": "workspace:^", "@aztec/foundation": "workspace:^", + "@aztec/simulator": "workspace:^", "@aztec/types": "workspace:^", "@aztec/world-state": "workspace:^" }, diff --git a/yarn-project/txe/src/txe_service/txe_service.ts b/yarn-project/txe/src/txe_service/txe_service.ts index 48f0fc4210c8..720686918438 100644 --- a/yarn-project/txe/src/txe_service/txe_service.ts +++ b/yarn-project/txe/src/txe_service/txe_service.ts @@ -9,20 +9,22 @@ import { type CompleteAddress, type Header, type KeyValidationRequest, - PUBLIC_DATA_TREE_HEIGHT, + type PUBLIC_DATA_TREE_HEIGHT, type PrivateCallStackItem, type PublicCallRequest, - PublicDataTreeLeafPreimage, + type PublicDataTreeLeafPreimage, } from '@aztec/circuits.js'; +import { Aes128 } from '@aztec/circuits.js/barretenberg'; import { type FunctionSelector } from '@aztec/foundation/abi'; import { AztecAddress } from '@aztec/foundation/aztec-address'; import { Fr, type Point } from '@aztec/foundation/fields'; -import { type Logger } from '@aztec/foundation/log'; +import { type Logger, applyStringFormatting } from '@aztec/foundation/log'; import { type AztecKVStore } from '@aztec/kv-store'; import { openTmpStore } from '@aztec/kv-store/utils'; import { type MessageLoadOracleInputs, type NoteData, + PackedValuesCache, type TypedOracle, WorldStateDB, WorldStatePublicDB, @@ -37,45 +39,60 @@ import { fromSingle, toArray, toForeignCallResult, + toSingle, } from '../util/encoding.js'; export class TXE implements TypedOracle { private worldStatePublicDB: WorldStatePublicDB; private worldStateDB: WorldStateDB; - constructor(private logger: Logger, private trees: MerkleTreeOperations, private contractAddress: AztecAddress) { + constructor( + private logger: Logger, + private trees: MerkleTreeOperations, + private packedValuesCache: PackedValuesCache, + private contractAddress: AztecAddress, + ) { this.worldStatePublicDB = new WorldStatePublicDB(this.trees); this.worldStateDB = new WorldStateDB(this.trees); + this.packedValuesCache = packedValuesCache; } getRandomField() { return Fr.random(); } - packArgumentsArray(_args: Fr[]): Promise { - throw new Error('Method not implemented.'); + packArgumentsArray(args: Fr[]): Promise { + return Promise.resolve(this.packedValuesCache.pack(args)); } - packReturns(_returns: Fr[]): Promise { - throw new Error('Method not implemented.'); + + packReturns(returns: Fr[]): Promise { + return Promise.resolve(this.packedValuesCache.pack(returns)); } - unpackReturns(_returnsHash: Fr): Promise { - throw new Error('Method not implemented.'); + + unpackReturns(returnsHash: Fr): Promise { + return Promise.resolve(this.packedValuesCache.unpack(returnsHash)); } + getKeyValidationRequest(_pkMHash: Fr): Promise { throw new Error('Method not implemented.'); } + getContractInstance(_address: AztecAddress): Promise { throw new Error('Method not implemented.'); } + getMembershipWitness(_blockNumber: number, _treeId: MerkleTreeId, _leafValue: Fr): Promise { throw new Error('Method not implemented.'); } + getSiblingPath(_blockNumber: number, _treeId: MerkleTreeId, _leafIndex: Fr): Promise { throw new Error('Method not implemented.'); } + getNullifierMembershipWitness(_blockNumber: number, _nullifier: Fr): Promise { throw new Error('Method not implemented.'); } + async getPublicDataTreeWitness(_blockNumber: number, leafSlot: Fr): Promise { const committedDb = this.trees; const lowLeafResult = await committedDb.getPreviousValueIndex(MerkleTreeId.PUBLIC_DATA_TREE, leafSlot.toBigInt()); @@ -93,24 +110,30 @@ export class TXE implements TypedOracle { return new PublicDataWitness(lowLeafResult.index, preimage, path); } } + getLowNullifierMembershipWitness( _blockNumber: number, _nullifier: Fr, ): Promise { throw new Error('Method not implemented.'); } + getHeader(_blockNumber: number): Promise
{ throw new Error('Method not implemented.'); } + getCompleteAddress(_account: AztecAddress): Promise { throw new Error('Method not implemented.'); } + getAuthWitness(_messageHash: Fr): Promise { throw new Error('Method not implemented.'); } + popCapsule(): Promise { throw new Error('Method not implemented.'); } + getNotes( _storageSlot: Fr, _numSelects: number, @@ -129,15 +152,19 @@ export class TXE implements TypedOracle { ): Promise { throw new Error('Method not implemented.'); } + notifyCreatedNote(_storageSlot: Fr, _noteTypeId: Fr, _note: Fr[], _innerNoteHash: Fr, _counter: number): void { throw new Error('Method not implemented.'); } + notifyNullifiedNote(_innerNullifier: Fr, _innerNoteHash: Fr, _counter: number): Promise { throw new Error('Method not implemented.'); } + checkNullifierExists(_innerNullifier: Fr): Promise { throw new Error('Method not implemented.'); } + getL1ToL2MembershipWitness( _contractAddress: AztecAddress, _messageHash: Fr, @@ -145,6 +172,7 @@ export class TXE implements TypedOracle { ): Promise> { throw new Error('Method not implemented.'); } + async storageRead(startStorageSlot: Fr, numberOfElements: number): Promise { const values = []; for (let i = 0n; i < numberOfElements; i++) { @@ -166,12 +194,15 @@ export class TXE implements TypedOracle { }), ); } + emitEncryptedLog(_contractAddress: AztecAddress, _randomness: Fr, _encryptedNote: Buffer, _counter: number): void { throw new Error('Method not implemented.'); } + emitEncryptedNoteLog(_noteHashCounter: number, _encryptedNote: Buffer, _counter: number): void { throw new Error('Method not implemented.'); } + computeEncryptedLog( _contractAddress: AztecAddress, _storageSlot: Fr, @@ -182,12 +213,15 @@ export class TXE implements TypedOracle { ): Buffer { throw new Error('Method not implemented.'); } + emitUnencryptedLog(_log: UnencryptedL2Log, _counter: number): void { throw new Error('Method not implemented.'); } + emitContractClassUnencryptedLog(_log: UnencryptedL2Log, _counter: number): Fr { throw new Error('Method not implemented.'); } + callPrivateFunction( _targetContractAddress: AztecAddress, _functionSelector: FunctionSelector, @@ -198,6 +232,7 @@ export class TXE implements TypedOracle { ): Promise { throw new Error('Method not implemented.'); } + callPublicFunction( _targetContractAddress: AztecAddress, _functionSelector: FunctionSelector, @@ -208,6 +243,7 @@ export class TXE implements TypedOracle { ): Promise { throw new Error('Method not implemented.'); } + enqueuePublicFunctionCall( _targetContractAddress: AztecAddress, _functionSelector: FunctionSelector, @@ -218,6 +254,7 @@ export class TXE implements TypedOracle { ): Promise { throw new Error('Method not implemented.'); } + setPublicTeardownFunctionCall( _targetContractAddress: AztecAddress, _functionSelector: FunctionSelector, @@ -228,22 +265,31 @@ export class TXE implements TypedOracle { ): Promise { throw new Error('Method not implemented.'); } - aes128Encrypt(_input: Buffer, _initializationVector: Buffer, _key: Buffer): Buffer { - throw new Error('Method not implemented.'); + + aes128Encrypt(input: Buffer, initializationVector: Buffer, key: Buffer): Buffer { + const aes128 = new Aes128(); + return aes128.encryptBufferCBC(input, initializationVector, key); } - debugLog(_message: string, _fields: Fr[]): void { - throw new Error('Method not implemented.'); + + debugLog(message: string, fields: Fr[]): void { + this.logger.verbose(`debug_log ${applyStringFormatting(message, fields)}`); } } export class TXEService { - constructor(private typedOracle: TypedOracle, private store: AztecKVStore, private contractAddress: AztecAddress) {} + constructor( + private typedOracle: TypedOracle, + private store: AztecKVStore, + private packedValuesCache: PackedValuesCache, + private contractAddress: AztecAddress, + ) {} static async init(logger: Logger, contractAddress = AztecAddress.random()) { const store = openTmpStore(true); const trees = await MerkleTrees.new(store, logger); - const txe = new TXE(logger, trees.asLatest(), contractAddress); - return new TXEService(txe, store, contractAddress); + const packedValuesCache = new PackedValuesCache(); + const txe = new TXE(logger, trees.asLatest(), packedValuesCache, contractAddress); + return new TXEService(txe, store, packedValuesCache, contractAddress); } setContractAddress(address = AztecAddress.random()): AztecAddress { @@ -253,6 +299,38 @@ export class TXEService { async reset() { await this.store.clear(); + this.packedValuesCache = new PackedValuesCache(); + return toForeignCallResult([]); + } + + async packArgumentsArray(args: ForeignCallArray) { + const packed = await this.typedOracle.packArgumentsArray(fromArray(args)); + return toForeignCallResult([toSingle(packed)]); + } + + async packArguments(_length: ForeignCallSingle, values: ForeignCallArray) { + const packed = await this.typedOracle.packArgumentsArray(fromArray(values)); + return toForeignCallResult([toSingle(packed)]); + } + + // Since the argument is a slice, noir automatically adds a length field to oracle call. + async packReturns(_length: ForeignCallSingle, values: ForeignCallArray) { + const packed = await this.typedOracle.packReturns(fromArray(values)); + return toForeignCallResult([toSingle(packed)]); + } + + async unpackReturns(returnsHash: ForeignCallSingle) { + const unpacked = await this.typedOracle.unpackReturns(fromSingle(returnsHash)); + return toForeignCallResult([toArray(unpacked)]); + } + + // Since the argument is a slice, noir automatically adds a length field to oracle call. + debugLog(message: ForeignCallArray, _length: ForeignCallSingle, fields: ForeignCallArray) { + const messageStr = fromArray(message) + .map(field => String.fromCharCode(field.toNumber())) + .join(''); + const fieldsFr = fromArray(fields); + this.typedOracle.debugLog(messageStr, fieldsFr); return toForeignCallResult([]); } diff --git a/yarn-project/yarn.lock b/yarn-project/yarn.lock index 4ddea3b0d954..678e3a9c4eed 100644 --- a/yarn-project/yarn.lock +++ b/yarn-project/yarn.lock @@ -934,8 +934,10 @@ __metadata: version: 0.0.0-use.local resolution: "@aztec/txe@workspace:txe" dependencies: + "@aztec/circuit-types": "workspace:^" "@aztec/circuits.js": "workspace:^" "@aztec/foundation": "workspace:^" + "@aztec/simulator": "workspace:^" "@aztec/types": "workspace:^" "@aztec/world-state": "workspace:^" "@jest/globals": ^29.5.0 From 5d857ae7896c06c63be342859f407bac4044717e Mon Sep 17 00:00:00 2001 From: thunkar Date: Tue, 4 Jun 2024 11:39:37 +0000 Subject: [PATCH 09/60] more oracles, timetravel --- yarn-project/txe/package.json | 1 + yarn-project/txe/src/oracle/txe_oracle.ts | 264 ++++++++++++++ .../txe/src/txe_service/txe_service.ts | 331 ++++-------------- .../world-state/src/world-state-db/index.ts | 2 + yarn-project/yarn.lock | 1 + 5 files changed, 333 insertions(+), 266 deletions(-) create mode 100644 yarn-project/txe/src/oracle/txe_oracle.ts diff --git a/yarn-project/txe/package.json b/yarn-project/txe/package.json index 97214621a3a6..19aa02bf07c5 100644 --- a/yarn-project/txe/package.json +++ b/yarn-project/txe/package.json @@ -51,6 +51,7 @@ "@aztec/circuit-types": "workspace:^", "@aztec/circuits.js": "workspace:^", "@aztec/foundation": "workspace:^", + "@aztec/kv-store": "workspace:^", "@aztec/simulator": "workspace:^", "@aztec/types": "workspace:^", "@aztec/world-state": "workspace:^" diff --git a/yarn-project/txe/src/oracle/txe_oracle.ts b/yarn-project/txe/src/oracle/txe_oracle.ts new file mode 100644 index 000000000000..bfc01defe113 --- /dev/null +++ b/yarn-project/txe/src/oracle/txe_oracle.ts @@ -0,0 +1,264 @@ +import { + MerkleTreeId, + type NoteStatus, + type NullifierMembershipWitness, + PublicDataWitness, + type UnencryptedL2Log, +} from '@aztec/circuit-types'; +import { + type CompleteAddress, + type Header, + type KeyValidationRequest, + type PUBLIC_DATA_TREE_HEIGHT, + type PrivateCallStackItem, + type PublicCallRequest, + type PublicDataTreeLeafPreimage, +} from '@aztec/circuits.js'; +import { Aes128 } from '@aztec/circuits.js/barretenberg'; +import { type FunctionSelector } from '@aztec/foundation/abi'; +import { type AztecAddress } from '@aztec/foundation/aztec-address'; +import { Fr, type Point } from '@aztec/foundation/fields'; +import { type Logger, applyStringFormatting } from '@aztec/foundation/log'; +import { + type MessageLoadOracleInputs, + type NoteData, + type PackedValuesCache, + type TypedOracle, + WorldStatePublicDB, +} from '@aztec/simulator'; +import { type ContractInstance } from '@aztec/types/contracts'; +import { MerkleTreeSnapshotOperationsFacade, type MerkleTrees } from '@aztec/world-state'; + +export class TXE implements TypedOracle { + private worldStatePublicDB: WorldStatePublicDB; + + constructor( + private logger: Logger, + private trees: MerkleTrees, + private packedValuesCache: PackedValuesCache, + private contractAddress: AztecAddress, + ) { + this.packedValuesCache = packedValuesCache; + this.worldStatePublicDB = new WorldStatePublicDB(this.trees.asLatest()); + } + + getRandomField() { + return Fr.random(); + } + + packArgumentsArray(args: Fr[]): Promise { + return Promise.resolve(this.packedValuesCache.pack(args)); + } + + packReturns(returns: Fr[]): Promise { + return Promise.resolve(this.packedValuesCache.pack(returns)); + } + + unpackReturns(returnsHash: Fr): Promise { + return Promise.resolve(this.packedValuesCache.unpack(returnsHash)); + } + + getKeyValidationRequest(_pkMHash: Fr): Promise { + throw new Error('Method not implemented.'); + } + + getContractInstance(_address: AztecAddress): Promise { + throw new Error('Method not implemented.'); + } + + getMembershipWitness(_blockNumber: number, _treeId: MerkleTreeId, _leafValue: Fr): Promise { + throw new Error('Method not implemented.'); + } + + async getSiblingPath(blockNumber: number, treeId: MerkleTreeId, leafIndex: Fr) { + const committedDb = new MerkleTreeSnapshotOperationsFacade(this.trees, blockNumber); + const result = await committedDb.getSiblingPath(treeId, leafIndex.toBigInt()); + return result.toFields(); + } + + getNullifierMembershipWitness(_blockNumber: number, _nullifier: Fr): Promise { + throw new Error('Method not implemented.'); + } + + async getPublicDataTreeWitness(blockNumber: number, leafSlot: Fr): Promise { + const committedDb = new MerkleTreeSnapshotOperationsFacade(this.trees, blockNumber); + const lowLeafResult = await committedDb.getPreviousValueIndex(MerkleTreeId.PUBLIC_DATA_TREE, leafSlot.toBigInt()); + if (!lowLeafResult) { + return undefined; + } else { + const preimage = (await committedDb.getLeafPreimage( + MerkleTreeId.PUBLIC_DATA_TREE, + lowLeafResult.index, + )) as PublicDataTreeLeafPreimage; + const path = await committedDb.getSiblingPath( + MerkleTreeId.PUBLIC_DATA_TREE, + lowLeafResult.index, + ); + return new PublicDataWitness(lowLeafResult.index, preimage, path); + } + } + + getLowNullifierMembershipWitness( + _blockNumber: number, + _nullifier: Fr, + ): Promise { + throw new Error('Method not implemented.'); + } + + getHeader(_blockNumber: number): Promise
{ + throw new Error('Method not implemented.'); + } + + getCompleteAddress(_account: AztecAddress): Promise { + throw new Error('Method not implemented.'); + } + + getAuthWitness(_messageHash: Fr): Promise { + throw new Error('Method not implemented.'); + } + + popCapsule(): Promise { + throw new Error('Method not implemented.'); + } + + getNotes( + _storageSlot: Fr, + _numSelects: number, + _selectByIndexes: number[], + _selectByOffsets: number[], + _selectByLengths: number[], + _selectValues: Fr[], + _selectComparators: number[], + _sortByIndexes: number[], + _sortByOffsets: number[], + _sortByLengths: number[], + _sortOrder: number[], + _limit: number, + _offset: number, + _status: NoteStatus, + ): Promise { + throw new Error('Method not implemented.'); + } + + notifyCreatedNote(_storageSlot: Fr, _noteTypeId: Fr, _note: Fr[], _innerNoteHash: Fr, _counter: number): void { + throw new Error('Method not implemented.'); + } + + notifyNullifiedNote(_innerNullifier: Fr, _innerNoteHash: Fr, _counter: number): Promise { + throw new Error('Method not implemented.'); + } + + checkNullifierExists(_innerNullifier: Fr): Promise { + throw new Error('Method not implemented.'); + } + + getL1ToL2MembershipWitness( + _contractAddress: AztecAddress, + _messageHash: Fr, + _secret: Fr, + ): Promise> { + throw new Error('Method not implemented.'); + } + + async storageRead(startStorageSlot: Fr, numberOfElements: number): Promise { + const values = []; + for (let i = 0n; i < numberOfElements; i++) { + const storageSlot = startStorageSlot.add(new Fr(i)); + const value = await this.worldStatePublicDB.storageRead(this.contractAddress, storageSlot); + this.logger.debug(`Oracle storage read: slot=${storageSlot.toString()} value=${value}`); + values.push(value); + } + return values; + } + + async storageWrite(startStorageSlot: Fr, values: Fr[]): Promise { + return await Promise.all( + values.map(async (value, i) => { + const storageSlot = startStorageSlot.add(new Fr(i)); + const result = await this.worldStatePublicDB.storageWrite(this.contractAddress, storageSlot, value); + this.logger.debug(`Oracle storage write: slot=${storageSlot.toString()} value=${value}`); + return new Fr(result); + }), + ); + } + + emitEncryptedLog(_contractAddress: AztecAddress, _randomness: Fr, _encryptedNote: Buffer, _counter: number): void { + throw new Error('Method not implemented.'); + } + + emitEncryptedNoteLog(_noteHashCounter: number, _encryptedNote: Buffer, _counter: number): void { + throw new Error('Method not implemented.'); + } + + computeEncryptedLog( + _contractAddress: AztecAddress, + _storageSlot: Fr, + _noteTypeId: Fr, + _ovKeys: KeyValidationRequest, + _ivpkM: Point, + _preimage: Fr[], + ): Buffer { + throw new Error('Method not implemented.'); + } + + emitUnencryptedLog(_log: UnencryptedL2Log, _counter: number): void { + throw new Error('Method not implemented.'); + } + + emitContractClassUnencryptedLog(_log: UnencryptedL2Log, _counter: number): Fr { + throw new Error('Method not implemented.'); + } + + callPrivateFunction( + _targetContractAddress: AztecAddress, + _functionSelector: FunctionSelector, + _argsHash: Fr, + _sideEffectCounter: number, + _isStaticCall: boolean, + _isDelegateCall: boolean, + ): Promise { + throw new Error('Method not implemented.'); + } + + callPublicFunction( + _targetContractAddress: AztecAddress, + _functionSelector: FunctionSelector, + _argsHash: Fr, + _sideEffectCounter: number, + _isStaticCall: boolean, + _isDelegateCall: boolean, + ): Promise { + throw new Error('Method not implemented.'); + } + + enqueuePublicFunctionCall( + _targetContractAddress: AztecAddress, + _functionSelector: FunctionSelector, + _argsHash: Fr, + _sideEffectCounter: number, + _isStaticCall: boolean, + _isDelegateCall: boolean, + ): Promise { + throw new Error('Method not implemented.'); + } + + setPublicTeardownFunctionCall( + _targetContractAddress: AztecAddress, + _functionSelector: FunctionSelector, + _argsHash: Fr, + _sideEffectCounter: number, + _isStaticCall: boolean, + _isDelegateCall: boolean, + ): Promise { + throw new Error('Method not implemented.'); + } + + aes128Encrypt(input: Buffer, initializationVector: Buffer, key: Buffer): Buffer { + const aes128 = new Aes128(); + return aes128.encryptBufferCBC(input, initializationVector, key); + } + + debugLog(message: string, fields: Fr[]): void { + this.logger.verbose(`debug_log ${applyStringFormatting(message, fields)}`); + } +} diff --git a/yarn-project/txe/src/txe_service/txe_service.ts b/yarn-project/txe/src/txe_service/txe_service.ts index 720686918438..6ae302081b77 100644 --- a/yarn-project/txe/src/txe_service/txe_service.ts +++ b/yarn-project/txe/src/txe_service/txe_service.ts @@ -1,37 +1,13 @@ -import { - MerkleTreeId, - type NoteStatus, - type NullifierMembershipWitness, - PublicDataWitness, - type UnencryptedL2Log, -} from '@aztec/circuit-types'; -import { - type CompleteAddress, - type Header, - type KeyValidationRequest, - type PUBLIC_DATA_TREE_HEIGHT, - type PrivateCallStackItem, - type PublicCallRequest, - type PublicDataTreeLeafPreimage, -} from '@aztec/circuits.js'; -import { Aes128 } from '@aztec/circuits.js/barretenberg'; -import { type FunctionSelector } from '@aztec/foundation/abi'; +import { L2Block, MerkleTreeId } from '@aztec/circuit-types'; +import { Fr, Header } from '@aztec/circuits.js'; import { AztecAddress } from '@aztec/foundation/aztec-address'; -import { Fr, type Point } from '@aztec/foundation/fields'; -import { type Logger, applyStringFormatting } from '@aztec/foundation/log'; +import { type Logger } from '@aztec/foundation/log'; import { type AztecKVStore } from '@aztec/kv-store'; import { openTmpStore } from '@aztec/kv-store/utils'; -import { - type MessageLoadOracleInputs, - type NoteData, - PackedValuesCache, - type TypedOracle, - WorldStateDB, - WorldStatePublicDB, -} from '@aztec/simulator'; -import { type ContractInstance } from '@aztec/types/contracts'; -import { type MerkleTreeOperations, MerkleTrees } from '@aztec/world-state'; +import { PackedValuesCache, type TypedOracle } from '@aztec/simulator'; +import { MerkleTrees } from '@aztec/world-state'; +import { TXE } from '../oracle/txe_oracle.js'; import { type ForeignCallArray, type ForeignCallSingle, @@ -42,244 +18,13 @@ import { toSingle, } from '../util/encoding.js'; -export class TXE implements TypedOracle { - private worldStatePublicDB: WorldStatePublicDB; - private worldStateDB: WorldStateDB; - - constructor( - private logger: Logger, - private trees: MerkleTreeOperations, - private packedValuesCache: PackedValuesCache, - private contractAddress: AztecAddress, - ) { - this.worldStatePublicDB = new WorldStatePublicDB(this.trees); - this.worldStateDB = new WorldStateDB(this.trees); - this.packedValuesCache = packedValuesCache; - } - - getRandomField() { - return Fr.random(); - } - - packArgumentsArray(args: Fr[]): Promise { - return Promise.resolve(this.packedValuesCache.pack(args)); - } - - packReturns(returns: Fr[]): Promise { - return Promise.resolve(this.packedValuesCache.pack(returns)); - } - - unpackReturns(returnsHash: Fr): Promise { - return Promise.resolve(this.packedValuesCache.unpack(returnsHash)); - } - - getKeyValidationRequest(_pkMHash: Fr): Promise { - throw new Error('Method not implemented.'); - } - - getContractInstance(_address: AztecAddress): Promise { - throw new Error('Method not implemented.'); - } - - getMembershipWitness(_blockNumber: number, _treeId: MerkleTreeId, _leafValue: Fr): Promise { - throw new Error('Method not implemented.'); - } - - getSiblingPath(_blockNumber: number, _treeId: MerkleTreeId, _leafIndex: Fr): Promise { - throw new Error('Method not implemented.'); - } - - getNullifierMembershipWitness(_blockNumber: number, _nullifier: Fr): Promise { - throw new Error('Method not implemented.'); - } - - async getPublicDataTreeWitness(_blockNumber: number, leafSlot: Fr): Promise { - const committedDb = this.trees; - const lowLeafResult = await committedDb.getPreviousValueIndex(MerkleTreeId.PUBLIC_DATA_TREE, leafSlot.toBigInt()); - if (!lowLeafResult) { - return undefined; - } else { - const preimage = (await committedDb.getLeafPreimage( - MerkleTreeId.PUBLIC_DATA_TREE, - lowLeafResult.index, - )) as PublicDataTreeLeafPreimage; - const path = await committedDb.getSiblingPath( - MerkleTreeId.PUBLIC_DATA_TREE, - lowLeafResult.index, - ); - return new PublicDataWitness(lowLeafResult.index, preimage, path); - } - } - - getLowNullifierMembershipWitness( - _blockNumber: number, - _nullifier: Fr, - ): Promise { - throw new Error('Method not implemented.'); - } - - getHeader(_blockNumber: number): Promise
{ - throw new Error('Method not implemented.'); - } - - getCompleteAddress(_account: AztecAddress): Promise { - throw new Error('Method not implemented.'); - } - - getAuthWitness(_messageHash: Fr): Promise { - throw new Error('Method not implemented.'); - } - - popCapsule(): Promise { - throw new Error('Method not implemented.'); - } - - getNotes( - _storageSlot: Fr, - _numSelects: number, - _selectByIndexes: number[], - _selectByOffsets: number[], - _selectByLengths: number[], - _selectValues: Fr[], - _selectComparators: number[], - _sortByIndexes: number[], - _sortByOffsets: number[], - _sortByLengths: number[], - _sortOrder: number[], - _limit: number, - _offset: number, - _status: NoteStatus, - ): Promise { - throw new Error('Method not implemented.'); - } - - notifyCreatedNote(_storageSlot: Fr, _noteTypeId: Fr, _note: Fr[], _innerNoteHash: Fr, _counter: number): void { - throw new Error('Method not implemented.'); - } - - notifyNullifiedNote(_innerNullifier: Fr, _innerNoteHash: Fr, _counter: number): Promise { - throw new Error('Method not implemented.'); - } - - checkNullifierExists(_innerNullifier: Fr): Promise { - throw new Error('Method not implemented.'); - } - - getL1ToL2MembershipWitness( - _contractAddress: AztecAddress, - _messageHash: Fr, - _secret: Fr, - ): Promise> { - throw new Error('Method not implemented.'); - } - - async storageRead(startStorageSlot: Fr, numberOfElements: number): Promise { - const values = []; - for (let i = 0n; i < numberOfElements; i++) { - const storageSlot = startStorageSlot.add(new Fr(i)); - const value = await this.worldStatePublicDB.storageRead(this.contractAddress, storageSlot); - this.logger.debug(`Oracle storage read: slot=${storageSlot.toString()} value=${value}`); - values.push(value); - } - return values; - } - - async storageWrite(startStorageSlot: Fr, values: Fr[]): Promise { - return await Promise.all( - values.map(async (value, i) => { - const storageSlot = startStorageSlot.add(new Fr(i)); - const result = await this.worldStatePublicDB.storageWrite(this.contractAddress, storageSlot, value); - this.logger.debug(`Oracle storage write: slot=${storageSlot.toString()} value=${value}`); - return new Fr(result); - }), - ); - } - - emitEncryptedLog(_contractAddress: AztecAddress, _randomness: Fr, _encryptedNote: Buffer, _counter: number): void { - throw new Error('Method not implemented.'); - } - - emitEncryptedNoteLog(_noteHashCounter: number, _encryptedNote: Buffer, _counter: number): void { - throw new Error('Method not implemented.'); - } - - computeEncryptedLog( - _contractAddress: AztecAddress, - _storageSlot: Fr, - _noteTypeId: Fr, - _ovKeys: KeyValidationRequest, - _ivpkM: Point, - _preimage: Fr[], - ): Buffer { - throw new Error('Method not implemented.'); - } - - emitUnencryptedLog(_log: UnencryptedL2Log, _counter: number): void { - throw new Error('Method not implemented.'); - } - - emitContractClassUnencryptedLog(_log: UnencryptedL2Log, _counter: number): Fr { - throw new Error('Method not implemented.'); - } - - callPrivateFunction( - _targetContractAddress: AztecAddress, - _functionSelector: FunctionSelector, - _argsHash: Fr, - _sideEffectCounter: number, - _isStaticCall: boolean, - _isDelegateCall: boolean, - ): Promise { - throw new Error('Method not implemented.'); - } - - callPublicFunction( - _targetContractAddress: AztecAddress, - _functionSelector: FunctionSelector, - _argsHash: Fr, - _sideEffectCounter: number, - _isStaticCall: boolean, - _isDelegateCall: boolean, - ): Promise { - throw new Error('Method not implemented.'); - } - - enqueuePublicFunctionCall( - _targetContractAddress: AztecAddress, - _functionSelector: FunctionSelector, - _argsHash: Fr, - _sideEffectCounter: number, - _isStaticCall: boolean, - _isDelegateCall: boolean, - ): Promise { - throw new Error('Method not implemented.'); - } - - setPublicTeardownFunctionCall( - _targetContractAddress: AztecAddress, - _functionSelector: FunctionSelector, - _argsHash: Fr, - _sideEffectCounter: number, - _isStaticCall: boolean, - _isDelegateCall: boolean, - ): Promise { - throw new Error('Method not implemented.'); - } - - aes128Encrypt(input: Buffer, initializationVector: Buffer, key: Buffer): Buffer { - const aes128 = new Aes128(); - return aes128.encryptBufferCBC(input, initializationVector, key); - } - - debugLog(message: string, fields: Fr[]): void { - this.logger.verbose(`debug_log ${applyStringFormatting(message, fields)}`); - } -} - export class TXEService { + private blockNumber = 0; + constructor( private typedOracle: TypedOracle, private store: AztecKVStore, + private trees: MerkleTrees, private packedValuesCache: PackedValuesCache, private contractAddress: AztecAddress, ) {} @@ -288,8 +33,37 @@ export class TXEService { const store = openTmpStore(true); const trees = await MerkleTrees.new(store, logger); const packedValuesCache = new PackedValuesCache(); - const txe = new TXE(logger, trees.asLatest(), packedValuesCache, contractAddress); - return new TXEService(txe, store, packedValuesCache, contractAddress); + logger.info(`TXE service initialized`); + const txe = new TXE(logger, trees, packedValuesCache, contractAddress); + const service = new TXEService(txe, store, trees, packedValuesCache, contractAddress); + await service.timeTravel(1); + return service; + } + + async timeTravel(blocks: number) { + this.blockNumber += blocks; + const header = Header.empty(); + const l2Block = L2Block.empty(); + + for (let i = 0; i < blocks; i++) { + header.state = await this.trees.getStateReference(true); + header.state.partial.nullifierTree.root = Fr.fromBuffer( + (await this.trees.getTreeInfo(MerkleTreeId.NULLIFIER_TREE, true)).root, + ); + header.state.partial.noteHashTree.root = Fr.fromBuffer( + (await this.trees.getTreeInfo(MerkleTreeId.NOTE_HASH_TREE, true)).root, + ); + header.state.partial.publicDataTree.root = Fr.fromBuffer( + (await this.trees.getTreeInfo(MerkleTreeId.PUBLIC_DATA_TREE, true)).root, + ); + header.state.l1ToL2MessageTree.root = Fr.fromBuffer( + (await this.trees.getTreeInfo(MerkleTreeId.L1_TO_L2_MESSAGE_TREE, true)).root, + ); + l2Block.archive.root = Fr.fromBuffer((await this.trees.getTreeInfo(MerkleTreeId.ARCHIVE, true)).root); + l2Block.header = header; + await this.trees.handleL2BlockAndMessages(l2Block, []); + this.blockNumber++; + } } setContractAddress(address = AztecAddress.random()): AztecAddress { @@ -297,6 +71,22 @@ export class TXEService { return this.contractAddress; } + getContractAddress() { + return toForeignCallResult([toSingle(this.contractAddress)]); + } + + getBlockNumber() { + return toForeignCallResult([toSingle(new Fr(this.blockNumber))]); + } + + avmOpcodeAddress() { + return toForeignCallResult([toSingle(this.contractAddress)]); + } + + avmOpcodeBlockNumber() { + return toForeignCallResult([toSingle(new Fr(this.blockNumber))]); + } + async reset() { await this.store.clear(); this.packedValuesCache = new PackedValuesCache(); @@ -357,4 +147,13 @@ export class TXEService { } return toForeignCallResult([toArray(witness.toFields())]); } + + async getSiblingPath(blockNumber: ForeignCallSingle, treeId: ForeignCallSingle, leafIndex: ForeignCallSingle) { + const result = await this.typedOracle.getSiblingPath( + fromSingle(blockNumber).toNumber(), + fromSingle(treeId).toNumber(), + fromSingle(leafIndex), + ); + return toForeignCallResult([toArray(result)]); + } } diff --git a/yarn-project/world-state/src/world-state-db/index.ts b/yarn-project/world-state/src/world-state-db/index.ts index 9d72e0991e84..f4c20a567f9e 100644 --- a/yarn-project/world-state/src/world-state-db/index.ts +++ b/yarn-project/world-state/src/world-state-db/index.ts @@ -1,3 +1,5 @@ export * from './merkle_trees.js'; export * from './merkle_tree_db.js'; export * from './merkle_tree_operations.js'; +export * from './merkle_tree_operations_facade.js'; +export * from './merkle_tree_snapshot_operations_facade.js'; diff --git a/yarn-project/yarn.lock b/yarn-project/yarn.lock index 678e3a9c4eed..e02b14abb9fc 100644 --- a/yarn-project/yarn.lock +++ b/yarn-project/yarn.lock @@ -937,6 +937,7 @@ __metadata: "@aztec/circuit-types": "workspace:^" "@aztec/circuits.js": "workspace:^" "@aztec/foundation": "workspace:^" + "@aztec/kv-store": "workspace:^" "@aztec/simulator": "workspace:^" "@aztec/types": "workspace:^" "@aztec/world-state": "workspace:^" From 717a16a2f02bc209db82ff7760b7906f555c6a40 Mon Sep 17 00:00:00 2001 From: thunkar Date: Tue, 4 Jun 2024 11:44:49 +0000 Subject: [PATCH 10/60] fix --- yarn-project/txe/src/txe_service/txe_service.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/yarn-project/txe/src/txe_service/txe_service.ts b/yarn-project/txe/src/txe_service/txe_service.ts index 6ae302081b77..9d70f063cf8a 100644 --- a/yarn-project/txe/src/txe_service/txe_service.ts +++ b/yarn-project/txe/src/txe_service/txe_service.ts @@ -64,11 +64,12 @@ export class TXEService { await this.trees.handleL2BlockAndMessages(l2Block, []); this.blockNumber++; } + return toForeignCallResult([]); } - setContractAddress(address = AztecAddress.random()): AztecAddress { + setContractAddress(address = AztecAddress.random()) { this.contractAddress = address; - return this.contractAddress; + return toForeignCallResult([]); } getContractAddress() { From 58e7bfa5d893537201d3251cc9167243800d0d1e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicol=C3=A1s=20Venturo?= Date: Tue, 4 Jun 2024 11:46:17 +0000 Subject: [PATCH 11/60] Sketch nr tests for txe testing --- .../src/state_vars/public_immutable/test.nr | 58 +------------------ .../shared_mutable/shared_mutable.nr | 15 ++--- .../src/state_vars/shared_mutable/test.nr | 58 +++++++++++-------- .../aztec-nr/aztec/src/test/helpers.nr | 1 + .../aztec/src/test/helpers/cheatcodes.nr | 36 ++++++++++++ .../aztec/src/test/helpers/context_builder.nr | 31 ++++++---- 6 files changed, 100 insertions(+), 99 deletions(-) create mode 100644 noir-projects/aztec-nr/aztec/src/test/helpers/cheatcodes.nr diff --git a/noir-projects/aztec-nr/aztec/src/state_vars/public_immutable/test.nr b/noir-projects/aztec-nr/aztec/src/state_vars/public_immutable/test.nr index 65fe0a7a253f..59e7b0a37efe 100644 --- a/noir-projects/aztec-nr/aztec/src/state_vars/public_immutable/test.nr +++ b/noir-projects/aztec-nr/aztec/src/state_vars/public_immutable/test.nr @@ -10,49 +10,11 @@ fn setup() -> PublicImmutable { PublicImmutable::new(&mut context, storage_slot) } -impl PublicImmutable { - fn mock_initialized_storage(self) { - let _ = OracleMock::mock("storageRead").with_params((self.get_initialization_slot(), 1)).returns([1]).times(1); - } - - fn mock_uninitialized_storage(self) { - let _ = OracleMock::mock("storageRead").with_params((self.get_initialization_slot(), 1)).returns([0]).times(1); - } - - fn mock_initialize_write(self) -> OracleMock { - // Unfortunately we can't only pass the storage slot to with_params, so we must know the initialization value. - OracleMock::mock("storageWrite").with_params((self.get_initialization_slot(), [0xdead])).returns([0; 1]) - } - - fn assert_initialize_write(self, mock: OracleMock) { - assert(mock.get_last_params() != (self.storage_slot, [0])); - } - - fn mock_value_write(self, value: MockStruct) -> OracleMock where MockStruct: Serialize { - // Unfortunately we can't only pass the storage slot to with_params, so we must know what will be stored as - // well. - OracleMock::mock("storageWrite").with_params((self.storage_slot, value.serialize())).returns([0; N]) - } - - fn mock_value_read(self, value: MockStruct) where MockStruct: Serialize { - // TBD https://github.com/noir-lang/noir/issues/4633: replace 2 with N - let _ = OracleMock::mock("storageRead").with_params((self.storage_slot, 2)).returns(value.serialize()).times(1); - } - - fn assert_value_write(self, mock: OracleMock, value: MockStruct) { - assert_eq(mock.get_last_params(), (self.storage_slot, value.serialize())); - } -} - #[test] -fn test_is_initialized() { +fn test_uninitialized_by_default() { let state_var = setup(); - state_var.mock_uninitialized_storage(); assert_eq(state_var.is_initialized(), false); - - state_var.mock_initialized_storage(); - assert_eq(state_var.is_initialized(), true); } #[test] @@ -63,6 +25,7 @@ fn test_initialize_uninitialized() { state_var.initialize(value); + assert(state_var.is_initialized()); assert(state_var.read() == value); } @@ -72,28 +35,13 @@ fn test_initialize_already_initialized() { let value = MockStruct::new(5, 6); - state_var.mock_initialized_storage(); - state_var.initialize(value); -} - -#[test] -fn test_read_initialized() { - let state_var = setup(); - - let value = MockStruct::new(5, 6); - - state_var.mock_initialized_storage(); - state_var.mock_value_read(value); - - assert_eq(state_var.read(), value); + state_var.initialize(value); } #[test(should_fail_with="PublicImmutable not initialized")] fn test_read_uninitialized() { let state_var = setup(); - state_var.mock_uninitialized_storage(); - let _ = state_var.read(); } diff --git a/noir-projects/aztec-nr/aztec/src/state_vars/shared_mutable/shared_mutable.nr b/noir-projects/aztec-nr/aztec/src/state_vars/shared_mutable/shared_mutable.nr index 91c864a03b23..38f36ae41db3 100644 --- a/noir-projects/aztec-nr/aztec/src/state_vars/shared_mutable/shared_mutable.nr +++ b/noir-projects/aztec-nr/aztec/src/state_vars/shared_mutable/shared_mutable.nr @@ -121,7 +121,7 @@ impl SharedMutable { // will only be valid for however many blocks we can ensure the value will not change, which will depend on the // current delay and any scheduled delay changes. - let (value_change, delay_change, historical_block_number) = self.historical_read_from_public_storage(*self.context); + let (value_change, delay_change, historical_block_number) = self.historical_read_from_public_storage(); // We use the effective minimum delay as opposed to the current delay at the historical block as this one also // takes into consideration any scheduled delay changes. @@ -139,29 +139,26 @@ impl SharedMutable { value_change.get_current_at(historical_block_number) } - fn historical_read_from_public_storage( - self, - context: PrivateContext - ) -> (ScheduledValueChange, ScheduledDelayChange, u32) where T: FromField { - let header = context.get_header(); + fn historical_read_from_public_storage(self) -> (ScheduledValueChange, ScheduledDelayChange, u32) where T: FromField { + let header = self.context.get_header(); // Ideally the following would be simply public_storage::read_historical, but we can't implement that yet. let value_change_slot = self.get_value_change_storage_slot(); let mut raw_value_change_fields = [0; 3]; for i in 0..3 { raw_value_change_fields[i] = header.public_storage_historical_read( value_change_slot + i as Field, - context.this_address() + self.context.this_address() ); } // Ideally the following would be simply public_storage::read_historical, but we can't implement that yet. let delay_change_slot = self.get_delay_change_storage_slot(); - let raw_delay_change_fields = [header.public_storage_historical_read(delay_change_slot, context.this_address())]; + let raw_delay_change_fields = [header.public_storage_historical_read(delay_change_slot, self.context.this_address())]; let value_change = ScheduledValueChange::deserialize(raw_value_change_fields); let delay_change = ScheduledDelayChange::deserialize(raw_delay_change_fields); - let historical_block_number = context.historical_header.global_variables.block_number as u32; + let historical_block_number = self.context.historical_header.global_variables.block_number as u32; (value_change, delay_change, historical_block_number) } diff --git a/noir-projects/aztec-nr/aztec/src/state_vars/shared_mutable/test.nr b/noir-projects/aztec-nr/aztec/src/state_vars/shared_mutable/test.nr index 6403f166e9e8..7214057b9bcd 100644 --- a/noir-projects/aztec-nr/aztec/src/state_vars/shared_mutable/test.nr +++ b/noir-projects/aztec-nr/aztec/src/state_vars/shared_mutable/test.nr @@ -6,7 +6,8 @@ use crate::{ shared_mutable::SharedMutable, scheduled_value_change::ScheduledValueChange, scheduled_delay_change::ScheduledDelayChange }, - test::helpers::context_builder::ContextBuilder, oracle::get_public_data_witness::PublicDataWitness + test::helpers::{cheatcodes, context_builder::ContextBuilder}, + oracle::get_public_data_witness::PublicDataWitness }; use dep::protocol_types::{ @@ -135,18 +136,15 @@ fn test_get_current_value_in_public() { #[test] fn test_get_scheduled_value_in_public() { let (state_var, block_number) = setup(); - - // Change in the future, scheduled is post (always is) - mock_value_change_read(state_var, pre_value, post_value, block_number + 1); - assert_eq(state_var.get_scheduled_value_in_public(), (post_value, (block_number + 1) as u32)); - - // Change in the current block, scheduled is post (always is) - mock_value_change_read(state_var, pre_value, post_value, block_number); - assert_eq(state_var.get_scheduled_value_in_public(), (post_value, block_number as u32)); - - // Change in the past, scheduled is post (always is) - mock_value_change_read(state_var, pre_value, post_value, block_number - 1); - assert_eq(state_var.get_scheduled_value_in_public(), (post_value, (block_number - 1) as u32)); + // // Change in the future, scheduled is post (always is) + // mock_value_change_read(state_var, pre_value, post_value, block_number + 1); + // assert_eq(state_var.get_scheduled_value_in_public(), (post_value, (block_number + 1) as u32)); + // // Change in the current block, scheduled is post (always is) + // mock_value_change_read(state_var, pre_value, post_value, block_number); + // assert_eq(state_var.get_scheduled_value_in_public(), (post_value, block_number as u32)); + // // Change in the past, scheduled is post (always is) + // mock_value_change_read(state_var, pre_value, post_value, block_number - 1); + // assert_eq(state_var.get_scheduled_value_in_public(), (post_value, (block_number - 1) as u32)); } #[test] @@ -363,15 +361,25 @@ fn test_schedule_delay_reduction_after_change() { ); } -#[test] -fn test_get_current_value_in_private_before_change() { - // Here we'd want to test that the private getter returns the correct value and sets max_block_number in the - // context to the expected block horizon, in all the possible scenarios (long before change, before near change, - // after change). - // However, this requires mocking the getPublicDataTreeWitness oracle so that we can convince the circuit that - // it got a valid historical proof. Because we can set the tree root to whatever we want in the context, this is - // trivial for a single historical value (we add a leaf and compute the root with any random path), but is quite - // hard if we're reading more than one value for the same root (as SharedMutable does): we essentially need to - // create an actual indexed tree and compute the correct path for each of the inserted values. - // TODO: implement an actual tree and use it here https://github.com/AztecProtocol/aztec-packages/issues/5494 -} +// #[test] +// fn test_get_current_value_in_private_before_change() { +// let (state_var, block_number) = setup(); +// state_var.schedule_value_change(5); + +// let block_number = 40; +// let mut context = ContextBuilder::new().block_number(block_number).private(); + +// let storage_slot = 57; +// let private_state_var: SharedMutable = SharedMutable::new(&mut context, storage_slot); + +// let foo = private_state_var.get_current_value_in_private(); +// // Here we'd want to test that the private getter returns the correct value and sets max_block_number in the +// // context to the expected block horizon, in all the possible scenarios (long before change, before near change, +// // after change). +// // However, this requires mocking the getPublicDataTreeWitness oracle so that we can convince the circuit that +// // it got a valid historical proof. Because we can set the tree root to whatever we want in the context, this is +// // trivial for a single historical value (we add a leaf and compute the root with any random path), but is quite +// // hard if we're reading more than one value for the same root (as SharedMutable does): we essentially need to +// // create an actual indexed tree and compute the correct path for each of the inserted values. +// // TODO: implement an actual tree and use it here https://github.com/AztecProtocol/aztec-packages/issues/5494 +// } diff --git a/noir-projects/aztec-nr/aztec/src/test/helpers.nr b/noir-projects/aztec-nr/aztec/src/test/helpers.nr index c5c7a4b5f311..333197dd09bb 100644 --- a/noir-projects/aztec-nr/aztec/src/test/helpers.nr +++ b/noir-projects/aztec-nr/aztec/src/test/helpers.nr @@ -1 +1,2 @@ mod context_builder; +mod cheatcodes; diff --git a/noir-projects/aztec-nr/aztec/src/test/helpers/cheatcodes.nr b/noir-projects/aztec-nr/aztec/src/test/helpers/cheatcodes.nr new file mode 100644 index 000000000000..18dd20aaff86 --- /dev/null +++ b/noir-projects/aztec-nr/aztec/src/test/helpers/cheatcodes.nr @@ -0,0 +1,36 @@ +use dep::protocol_types::address::AztecAddress; + +#[oracle(reset)] +fn oracle_reset() {} + +unconstrained pub fn reset() { + oracle_reset(); +} + +#[oracle(getContractAddress)] +fn oracle_get_contract_address() -> AztecAddress {} + +unconstrained pub fn get_contract_address() -> AztecAddress { + oracle_get_contract_address() +} + +#[oracle(setContractAddress)] +fn oracle_set_contract_address(address: AztecAddress) {} + +unconstrained pub fn set_contract_address(address: AztecAddress) { + oracle_set_contract_address(address); +} + +#[oracle(getBlockNumber)] +fn oracle_get_block_number() -> Field {} + +unconstrained pub fn get_block_number() -> Field { + oracle_get_block_number() +} + +#[oracle(timeTravel)] +fn oracle_time_travel(blocks: Field) {} + +unconstrained pub fn advance_blocks(blocks: Field) { + oracle_time_travel(blocks); +} diff --git a/noir-projects/aztec-nr/aztec/src/test/helpers/context_builder.nr b/noir-projects/aztec-nr/aztec/src/test/helpers/context_builder.nr index 0b04ac52ccbd..ed71ad750932 100644 --- a/noir-projects/aztec-nr/aztec/src/test/helpers/context_builder.nr +++ b/noir-projects/aztec-nr/aztec/src/test/helpers/context_builder.nr @@ -1,7 +1,6 @@ use crate::context::{PrivateContext, PublicContext}; +use crate::test::helpers::cheatcodes; use dep::protocol_types::address::AztecAddress; -use dep::std::test::OracleMock; - struct ContextBuilder { block_number: Option, contract_address: Option, @@ -22,31 +21,43 @@ impl ContextBuilder { self } - fn private(&mut self) -> PrivateContext { + fn private(self) -> PrivateContext { + cheatcodes::reset(); let mut context = PrivateContext::empty(); if self.block_number.is_some() { - context.inputs.historical_header.global_variables.block_number = self.block_number.unwrap_unchecked(); + let block_number = self.block_number.unwrap_unchecked(); + context.inputs.historical_header.global_variables.block_number = block_number; + + let difference = block_number - cheatcodes::get_block_number(); + cheatcodes::advance_blocks(difference); } if self.contract_address.is_some() { - context.inputs.call_context.storage_contract_address = self.contract_address.unwrap_unchecked(); + let contract_address = self.contract_address.unwrap_unchecked(); + context.inputs.call_context.storage_contract_address = contract_address; + cheatcodes::set_contract_address(contract_address); } context } - fn public(&mut self) -> PublicContext { + fn public(self) -> PublicContext { + // cheatcodes::reset(); let mut context = PublicContext::empty(); if self.block_number.is_some() { - let _ = OracleMock::mock("avmOpcodeBlockNumber").returns(self.block_number.unwrap()); - } + let block_number = self.block_number.unwrap_unchecked(); - if self.contract_address.is_some() { - let _ = OracleMock::mock("avmOpcodeAddress").returns(self.contract_address.unwrap()); + let difference = block_number - cheatcodes::get_block_number(); + // cheatcodes::advance_blocks(difference); } + // if self.contract_address.is_some() { + // let contract_address = self.contract_address.unwrap_unchecked(); + // cheatcodes::set_contract_address(contract_address); + // } + context } } From d543edf5479bdc27da979510a593e28138f10a2c Mon Sep 17 00:00:00 2001 From: thunkar Date: Tue, 4 Jun 2024 11:49:49 +0000 Subject: [PATCH 12/60] fix --- yarn-project/txe/src/txe_service/txe_service.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/yarn-project/txe/src/txe_service/txe_service.ts b/yarn-project/txe/src/txe_service/txe_service.ts index 9d70f063cf8a..dd85991f6b3a 100644 --- a/yarn-project/txe/src/txe_service/txe_service.ts +++ b/yarn-project/txe/src/txe_service/txe_service.ts @@ -73,7 +73,7 @@ export class TXEService { } getContractAddress() { - return toForeignCallResult([toSingle(this.contractAddress)]); + return toForeignCallResult([toSingle(this.contractAddress.toField())]); } getBlockNumber() { @@ -81,7 +81,7 @@ export class TXEService { } avmOpcodeAddress() { - return toForeignCallResult([toSingle(this.contractAddress)]); + return toForeignCallResult([toSingle(this.contractAddress.toField())]); } avmOpcodeBlockNumber() { From 92b9a485fc68323821a48571d99d4290144e3e9f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicol=C3=A1s=20Venturo?= Date: Tue, 4 Jun 2024 12:47:21 +0000 Subject: [PATCH 13/60] Begin test refactor --- .../src/state_vars/shared_mutable/test.nr | 22 +++++++++++-------- .../aztec/src/test/helpers/cheatcodes.nr | 6 +++++ .../aztec/src/test/helpers/context_builder.nr | 19 +++++++--------- 3 files changed, 27 insertions(+), 20 deletions(-) diff --git a/noir-projects/aztec-nr/aztec/src/state_vars/shared_mutable/test.nr b/noir-projects/aztec-nr/aztec/src/state_vars/shared_mutable/test.nr index 7214057b9bcd..5875740b3501 100644 --- a/noir-projects/aztec-nr/aztec/src/state_vars/shared_mutable/test.nr +++ b/noir-projects/aztec-nr/aztec/src/state_vars/shared_mutable/test.nr @@ -136,15 +136,19 @@ fn test_get_current_value_in_public() { #[test] fn test_get_scheduled_value_in_public() { let (state_var, block_number) = setup(); - // // Change in the future, scheduled is post (always is) - // mock_value_change_read(state_var, pre_value, post_value, block_number + 1); - // assert_eq(state_var.get_scheduled_value_in_public(), (post_value, (block_number + 1) as u32)); - // // Change in the current block, scheduled is post (always is) - // mock_value_change_read(state_var, pre_value, post_value, block_number); - // assert_eq(state_var.get_scheduled_value_in_public(), (post_value, block_number as u32)); - // // Change in the past, scheduled is post (always is) - // mock_value_change_read(state_var, pre_value, post_value, block_number - 1); - // assert_eq(state_var.get_scheduled_value_in_public(), (post_value, (block_number - 1) as u32)); + + state_var.schedule_value_change(post_value); + + // Change in the future, scheduled is post (always is) + assert_eq( + state_var.get_scheduled_value_in_public(), (post_value, (block_number + TEST_INITIAL_DELAY) as u32) + ); + + cheatcodes::advance_blocks(TEST_INITIAL_DELAY); + + assert_eq( + state_var.get_scheduled_value_in_public(), (post_value, (block_number + TEST_INITIAL_DELAY) as u32) + ); } #[test] diff --git a/noir-projects/aztec-nr/aztec/src/test/helpers/cheatcodes.nr b/noir-projects/aztec-nr/aztec/src/test/helpers/cheatcodes.nr index 18dd20aaff86..5a3fc46f0c42 100644 --- a/noir-projects/aztec-nr/aztec/src/test/helpers/cheatcodes.nr +++ b/noir-projects/aztec-nr/aztec/src/test/helpers/cheatcodes.nr @@ -34,3 +34,9 @@ fn oracle_time_travel(blocks: Field) {} unconstrained pub fn advance_blocks(blocks: Field) { oracle_time_travel(blocks); } + +unconstrained pub fn advance_block_to(block_number: Field) { + let difference = block_number - get_block_number(); + advance_blocks(difference); +} + diff --git a/noir-projects/aztec-nr/aztec/src/test/helpers/context_builder.nr b/noir-projects/aztec-nr/aztec/src/test/helpers/context_builder.nr index ed71ad750932..1b069ea02146 100644 --- a/noir-projects/aztec-nr/aztec/src/test/helpers/context_builder.nr +++ b/noir-projects/aztec-nr/aztec/src/test/helpers/context_builder.nr @@ -1,6 +1,7 @@ use crate::context::{PrivateContext, PublicContext}; use crate::test::helpers::cheatcodes; use dep::protocol_types::address::AztecAddress; + struct ContextBuilder { block_number: Option, contract_address: Option, @@ -28,9 +29,7 @@ impl ContextBuilder { if self.block_number.is_some() { let block_number = self.block_number.unwrap_unchecked(); context.inputs.historical_header.global_variables.block_number = block_number; - - let difference = block_number - cheatcodes::get_block_number(); - cheatcodes::advance_blocks(difference); + cheatcodes::advance_block_to(block_number); } if self.contract_address.is_some() { @@ -43,20 +42,18 @@ impl ContextBuilder { } fn public(self) -> PublicContext { - // cheatcodes::reset(); + cheatcodes::reset(); let mut context = PublicContext::empty(); if self.block_number.is_some() { let block_number = self.block_number.unwrap_unchecked(); - - let difference = block_number - cheatcodes::get_block_number(); - // cheatcodes::advance_blocks(difference); + cheatcodes::advance_block_to(block_number); } - // if self.contract_address.is_some() { - // let contract_address = self.contract_address.unwrap_unchecked(); - // cheatcodes::set_contract_address(contract_address); - // } + if self.contract_address.is_some() { + let contract_address = self.contract_address.unwrap_unchecked(); + cheatcodes::set_contract_address(contract_address); + } context } From c6fdef68897eefb102fddf33f8cf3a83d1913527 Mon Sep 17 00:00:00 2001 From: thunkar Date: Tue, 4 Jun 2024 12:52:17 +0000 Subject: [PATCH 14/60] fix --- yarn-project/txe/src/txe_service/txe_service.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/yarn-project/txe/src/txe_service/txe_service.ts b/yarn-project/txe/src/txe_service/txe_service.ts index dd85991f6b3a..2abb77b74791 100644 --- a/yarn-project/txe/src/txe_service/txe_service.ts +++ b/yarn-project/txe/src/txe_service/txe_service.ts @@ -41,7 +41,6 @@ export class TXEService { } async timeTravel(blocks: number) { - this.blockNumber += blocks; const header = Header.empty(); const l2Block = L2Block.empty(); From b29b3a41a5d265b2e93b2834a5ea1b98baf935f4 Mon Sep 17 00:00:00 2001 From: thunkar Date: Tue, 4 Jun 2024 13:13:35 +0000 Subject: [PATCH 15/60] fixes --- yarn-project/txe/src/txe_service/txe_service.ts | 16 +++++++++++----- yarn-project/txe/src/util/encoding.ts | 2 +- 2 files changed, 12 insertions(+), 6 deletions(-) diff --git a/yarn-project/txe/src/txe_service/txe_service.ts b/yarn-project/txe/src/txe_service/txe_service.ts index 2abb77b74791..bc81791466d2 100644 --- a/yarn-project/txe/src/txe_service/txe_service.ts +++ b/yarn-project/txe/src/txe_service/txe_service.ts @@ -22,6 +22,7 @@ export class TXEService { private blockNumber = 0; constructor( + private logger: Logger, private typedOracle: TypedOracle, private store: AztecKVStore, private trees: MerkleTrees, @@ -35,17 +36,22 @@ export class TXEService { const packedValuesCache = new PackedValuesCache(); logger.info(`TXE service initialized`); const txe = new TXE(logger, trees, packedValuesCache, contractAddress); - const service = new TXEService(txe, store, trees, packedValuesCache, contractAddress); - await service.timeTravel(1); + const service = new TXEService(logger, txe, store, trees, packedValuesCache, contractAddress); + await service.timeTravel(toSingle(new Fr(1n))); return service; } - async timeTravel(blocks: number) { - const header = Header.empty(); - const l2Block = L2Block.empty(); + timeTravel(blocks: ForeignCallSingle) { + return this.#timeTravelInner(fromSingle(blocks).toNumber()); + } + async #timeTravelInner(blocks: number) { + this.logger.info(`time traveling ${blocks} blocks`); for (let i = 0; i < blocks; i++) { + const header = Header.empty(); + const l2Block = L2Block.empty(); header.state = await this.trees.getStateReference(true); + header.globalVariables.blockNumber = new Fr(this.blockNumber); header.state.partial.nullifierTree.root = Fr.fromBuffer( (await this.trees.getTreeInfo(MerkleTreeId.NULLIFIER_TREE, true)).root, ); diff --git a/yarn-project/txe/src/util/encoding.ts b/yarn-project/txe/src/util/encoding.ts index fdc1941e6dd3..42dc32b88b4c 100644 --- a/yarn-project/txe/src/util/encoding.ts +++ b/yarn-project/txe/src/util/encoding.ts @@ -17,7 +17,7 @@ export function fromArray(obj: ForeignCallArray) { } export function toSingle(obj: Fr) { - return { Single: obj.toString() }; + return { Single: obj.toString().slice(2) }; } export function toArray(objs: Fr[]) { From 6628ab035299ad90a9c3965b3b0fd1eb704d2d84 Mon Sep 17 00:00:00 2001 From: thunkar Date: Tue, 4 Jun 2024 13:21:40 +0000 Subject: [PATCH 16/60] fix --- yarn-project/txe/src/txe_service/txe_service.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/yarn-project/txe/src/txe_service/txe_service.ts b/yarn-project/txe/src/txe_service/txe_service.ts index bc81791466d2..a564b09bd72d 100644 --- a/yarn-project/txe/src/txe_service/txe_service.ts +++ b/yarn-project/txe/src/txe_service/txe_service.ts @@ -94,8 +94,10 @@ export class TXEService { } async reset() { + this.blockNumber = 0; await this.store.clear(); this.packedValuesCache = new PackedValuesCache(); + await this.#timeTravelInner(1); return toForeignCallResult([]); } From 460a1d1bb6b4d8ed78792e2ef58e33ba0d424878 Mon Sep 17 00:00:00 2001 From: thunkar Date: Tue, 4 Jun 2024 13:38:50 +0000 Subject: [PATCH 17/60] private context inputs --- yarn-project/txe/src/txe_service/txe_service.ts | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/yarn-project/txe/src/txe_service/txe_service.ts b/yarn-project/txe/src/txe_service/txe_service.ts index a564b09bd72d..e7abe5dcf111 100644 --- a/yarn-project/txe/src/txe_service/txe_service.ts +++ b/yarn-project/txe/src/txe_service/txe_service.ts @@ -1,5 +1,5 @@ import { L2Block, MerkleTreeId } from '@aztec/circuit-types'; -import { Fr, Header } from '@aztec/circuits.js'; +import { Fr, Header, PrivateContextInputs } from '@aztec/circuits.js'; import { AztecAddress } from '@aztec/foundation/aztec-address'; import { type Logger } from '@aztec/foundation/log'; import { type AztecKVStore } from '@aztec/kv-store'; @@ -41,6 +41,15 @@ export class TXEService { return service; } + async getPrivateContextInputs() { + const inputs = PrivateContextInputs.empty(); + inputs.historicalHeader.globalVariables.blockNumber = new Fr(this.blockNumber); + inputs.historicalHeader.state.partial = (await this.trees.getStateReference(true)).partial; + inputs.callContext.msgSender = AztecAddress.random(); + inputs.callContext.storageContractAddress = this.contractAddress; + return toForeignCallResult([toArray(inputs.toFields())]); + } + timeTravel(blocks: ForeignCallSingle) { return this.#timeTravelInner(fromSingle(blocks).toNumber()); } From 069a09a374f5158c3f772d3e77e9d2bd2c69644c Mon Sep 17 00:00:00 2001 From: thunkar Date: Tue, 4 Jun 2024 13:52:22 +0000 Subject: [PATCH 18/60] return struct --- yarn-project/txe/src/txe_service/txe_service.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/yarn-project/txe/src/txe_service/txe_service.ts b/yarn-project/txe/src/txe_service/txe_service.ts index e7abe5dcf111..d5149d40d494 100644 --- a/yarn-project/txe/src/txe_service/txe_service.ts +++ b/yarn-project/txe/src/txe_service/txe_service.ts @@ -47,7 +47,7 @@ export class TXEService { inputs.historicalHeader.state.partial = (await this.trees.getStateReference(true)).partial; inputs.callContext.msgSender = AztecAddress.random(); inputs.callContext.storageContractAddress = this.contractAddress; - return toForeignCallResult([toArray(inputs.toFields())]); + return toForeignCallResult(inputs.toFields().map(toSingle)); } timeTravel(blocks: ForeignCallSingle) { From 7edc7190ad094d7f112fc137f41ee5231beeddd7 Mon Sep 17 00:00:00 2001 From: thunkar Date: Tue, 4 Jun 2024 14:03:56 +0000 Subject: [PATCH 19/60] fix --- yarn-project/txe/src/txe_service/txe_service.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/yarn-project/txe/src/txe_service/txe_service.ts b/yarn-project/txe/src/txe_service/txe_service.ts index d5149d40d494..c80e9df1fb76 100644 --- a/yarn-project/txe/src/txe_service/txe_service.ts +++ b/yarn-project/txe/src/txe_service/txe_service.ts @@ -43,8 +43,9 @@ export class TXEService { async getPrivateContextInputs() { const inputs = PrivateContextInputs.empty(); + const stateReference = await this.trees.getStateReference(true); inputs.historicalHeader.globalVariables.blockNumber = new Fr(this.blockNumber); - inputs.historicalHeader.state.partial = (await this.trees.getStateReference(true)).partial; + inputs.historicalHeader.state = stateReference; inputs.callContext.msgSender = AztecAddress.random(); inputs.callContext.storageContractAddress = this.contractAddress; return toForeignCallResult(inputs.toFields().map(toSingle)); From f2820a7f4865b305af3b565156c7e9eed558fe94 Mon Sep 17 00:00:00 2001 From: thunkar Date: Tue, 4 Jun 2024 14:12:18 +0000 Subject: [PATCH 20/60] configurable private inputs --- yarn-project/txe/src/txe_service/txe_service.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/yarn-project/txe/src/txe_service/txe_service.ts b/yarn-project/txe/src/txe_service/txe_service.ts index c80e9df1fb76..4c23cda4d9cb 100644 --- a/yarn-project/txe/src/txe_service/txe_service.ts +++ b/yarn-project/txe/src/txe_service/txe_service.ts @@ -41,10 +41,10 @@ export class TXEService { return service; } - async getPrivateContextInputs() { + async getPrivateContextInputs(blockNumber: ForeignCallSingle) { const inputs = PrivateContextInputs.empty(); const stateReference = await this.trees.getStateReference(true); - inputs.historicalHeader.globalVariables.blockNumber = new Fr(this.blockNumber); + inputs.historicalHeader.globalVariables.blockNumber = fromSingle(blockNumber); inputs.historicalHeader.state = stateReference; inputs.callContext.msgSender = AztecAddress.random(); inputs.callContext.storageContractAddress = this.contractAddress; From cbe1564254326df2ac9605018414e44e839c9cd1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicol=C3=A1s=20Venturo?= Date: Tue, 4 Jun 2024 14:15:47 +0000 Subject: [PATCH 21/60] ??? --- .../src/state_vars/shared_mutable/test.nr | 408 +++++++++--------- .../aztec/src/test/helpers/cheatcodes.nr | 44 +- .../aztec/src/test/helpers/context_builder.nr | 21 +- 3 files changed, 244 insertions(+), 229 deletions(-) diff --git a/noir-projects/aztec-nr/aztec/src/state_vars/shared_mutable/test.nr b/noir-projects/aztec-nr/aztec/src/state_vars/shared_mutable/test.nr index 5875740b3501..d21a0f4f54e4 100644 --- a/noir-projects/aztec-nr/aztec/src/state_vars/shared_mutable/test.nr +++ b/noir-projects/aztec-nr/aztec/src/state_vars/shared_mutable/test.nr @@ -23,9 +23,9 @@ global new_value = 57; global pre_delay = 20; global post_delay = 15; -global TEST_INITIAL_DELAY = 3; +global TEST_INITIAL_DELAY: u32 = 3; -fn setup() -> (SharedMutable, Field) { +fn setup() -> (SharedMutable, u32) { let block_number = 40; let mut context = ContextBuilder::new().block_number(block_number).public(); @@ -120,270 +120,276 @@ fn assert_delay_change_write( fn test_get_current_value_in_public() { let (state_var, block_number) = setup(); - // Change in the future, current value is pre - mock_value_change_read(state_var, pre_value, post_value, block_number + 1); - assert_eq(state_var.get_current_value_in_public(), pre_value); + state_var.schedule_value_change(new_value); + + let (scheduled_value, block_of_change) = state_var.get_scheduled_value_in_public(); + + assert_eq(scheduled_value, new_value); + assert_eq(block_of_change, block_number + TEST_INITIAL_DELAY); + assert_eq(state_var.get_current_value_in_public(), 0); // initial value - // Change in the current block, current value is post - mock_value_change_read(state_var, pre_value, post_value, block_number); - assert_eq(state_var.get_current_value_in_public(), post_value); + cheatcodes::advance_block_to(block_of_change); - // Change in the past, current value is post - mock_value_change_read(state_var, pre_value, post_value, block_number - 1); - assert_eq(state_var.get_current_value_in_public(), post_value); + assert_eq(state_var.get_current_value_in_public(), new_value); + assert_eq(state_var.get_scheduled_value_in_public(), (scheduled_value, block_of_change)); + + cheatcodes::advance_blocks(10); + + assert_eq(state_var.get_current_value_in_public(), new_value); + assert_eq(state_var.get_scheduled_value_in_public(), (scheduled_value, block_of_change)); } #[test] fn test_get_scheduled_value_in_public() { let (state_var, block_number) = setup(); - state_var.schedule_value_change(post_value); + state_var.schedule_value_change(new_value); - // Change in the future, scheduled is post (always is) assert_eq( - state_var.get_scheduled_value_in_public(), (post_value, (block_number + TEST_INITIAL_DELAY) as u32) + state_var.get_scheduled_value_in_public(), (new_value, (block_number + TEST_INITIAL_DELAY) as u32) ); - cheatcodes::advance_blocks(TEST_INITIAL_DELAY); + cheatcodes::advance_blocks(TEST_INITIAL_DELAY * 2); assert_eq( - state_var.get_scheduled_value_in_public(), (post_value, (block_number + TEST_INITIAL_DELAY) as u32) + state_var.get_scheduled_value_in_public(), (new_value, (block_number + TEST_INITIAL_DELAY) as u32) ); } -#[test] -fn test_get_current_delay_in_public() { - let (state_var, block_number) = setup(); +// #[test] +// fn test_get_current_delay_in_public() { +// let (state_var, block_number) = setup(); - // Uninitialized - mock_delay_change_read_uninitialized(state_var); - assert_eq(state_var.get_current_delay_in_public(), TEST_INITIAL_DELAY as u32); +// // Uninitialized +// mock_delay_change_read_uninitialized(state_var); +// assert_eq(state_var.get_current_delay_in_public(), TEST_INITIAL_DELAY as u32); - // Change in the future, current value is pre - mock_delay_change_read(state_var, pre_delay, post_delay, block_number + 1); - assert_eq(state_var.get_current_delay_in_public(), pre_delay as u32); +// // Change in the future, current value is pre +// mock_delay_change_read(state_var, pre_delay, post_delay, block_number + 1); +// assert_eq(state_var.get_current_delay_in_public(), pre_delay as u32); - // Change in the current block, current value is post - mock_delay_change_read(state_var, pre_delay, post_delay, block_number); - assert_eq(state_var.get_current_delay_in_public(), post_delay as u32); +// // Change in the current block, current value is post +// mock_delay_change_read(state_var, pre_delay, post_delay, block_number); +// assert_eq(state_var.get_current_delay_in_public(), post_delay as u32); - // Change in the past, current value is post - mock_delay_change_read(state_var, pre_delay, post_delay, block_number - 1); - assert_eq(state_var.get_current_delay_in_public(), post_delay as u32); -} +// // Change in the past, current value is post +// mock_delay_change_read(state_var, pre_delay, post_delay, block_number - 1); +// assert_eq(state_var.get_current_delay_in_public(), post_delay as u32); +// } -#[test] -fn test_get_scheduled_delay_in_public_before_change() { - let (state_var, block_number) = setup(); +// #[test] +// fn test_get_scheduled_delay_in_public_before_change() { +// let (state_var, block_number) = setup(); - // Uninitialized - mock_delay_change_read_uninitialized(state_var); - assert_eq(state_var.get_scheduled_delay_in_public(), (TEST_INITIAL_DELAY as u32, 0)); +// // Uninitialized +// mock_delay_change_read_uninitialized(state_var); +// assert_eq(state_var.get_scheduled_delay_in_public(), (TEST_INITIAL_DELAY as u32, 0)); - // Change in the future, scheduled is post (always is) - mock_delay_change_read(state_var, pre_delay, post_delay, block_number + 1); - assert_eq(state_var.get_scheduled_delay_in_public(), (post_delay as u32, (block_number + 1) as u32)); +// // Change in the future, scheduled is post (always is) +// mock_delay_change_read(state_var, pre_delay, post_delay, block_number + 1); +// assert_eq(state_var.get_scheduled_delay_in_public(), (post_delay as u32, (block_number + 1) as u32)); - // Change in the current block, scheduled is post (always is) - mock_delay_change_read(state_var, pre_delay, post_delay, block_number); - assert_eq(state_var.get_scheduled_delay_in_public(), (post_delay as u32, block_number as u32)); +// // Change in the current block, scheduled is post (always is) +// mock_delay_change_read(state_var, pre_delay, post_delay, block_number); +// assert_eq(state_var.get_scheduled_delay_in_public(), (post_delay as u32, block_number as u32)); - // Change in the past, scheduled is post (always is) - mock_delay_change_read(state_var, pre_delay, post_delay, block_number - 1); - assert_eq(state_var.get_scheduled_delay_in_public(), (post_delay as u32, (block_number - 1) as u32)); -} +// // Change in the past, scheduled is post (always is) +// mock_delay_change_read(state_var, pre_delay, post_delay, block_number - 1); +// assert_eq(state_var.get_scheduled_delay_in_public(), (post_delay as u32, (block_number - 1) as u32)); +// } -#[test] -fn test_schedule_value_change_no_delay() { - let (state_var, block_number) = setup(); +// #[test] +// fn test_schedule_value_change_no_delay() { +// let (state_var, block_number) = setup(); - // Last value change was in the past - mock_value_change_read(state_var, pre_value, post_value, 0); +// // Last value change was in the past +// mock_value_change_read(state_var, pre_value, post_value, 0); - // Current delay is 0 - mock_delay_change_read(state_var, 0, 0, block_number); +// // Current delay is 0 +// mock_delay_change_read(state_var, 0, 0, block_number); - let write_mock = mock_value_change_write(); +// let write_mock = mock_value_change_write(); - state_var.schedule_value_change(new_value); +// state_var.schedule_value_change(new_value); - // The new value has a block of change equal to the current block, i.e. it is the current value - assert_value_change_write(state_var, write_mock, post_value, new_value, block_number); -} +// // The new value has a block of change equal to the current block, i.e. it is the current value +// assert_value_change_write(state_var, write_mock, post_value, new_value, block_number); +// } -#[test] -fn test_schedule_value_change_before_change_no_scheduled_delay() { - let (state_var, block_number) = setup(); +// #[test] +// fn test_schedule_value_change_before_change_no_scheduled_delay() { +// let (state_var, block_number) = setup(); - // Value change in the future, delay change in the past - mock_value_and_delay_read(state_var, block_number + 1, block_number - 1); - let write_mock = mock_value_change_write(); +// // Value change in the future, delay change in the past +// mock_value_and_delay_read(state_var, block_number + 1, block_number - 1); +// let write_mock = mock_value_change_write(); - state_var.schedule_value_change(new_value); +// state_var.schedule_value_change(new_value); - // The new scheduled value change replaces the old one, post delay (current) is used - assert_value_change_write( - state_var, - write_mock, - pre_value, - new_value, - block_number + post_delay - ); -} +// // The new scheduled value change replaces the old one, post delay (current) is used +// assert_value_change_write( +// state_var, +// write_mock, +// pre_value, +// new_value, +// block_number + post_delay +// ); +// } -#[test] -fn test_schedule_value_change_before_change_scheduled_delay() { - let (state_var, block_number) = setup(); +// #[test] +// fn test_schedule_value_change_before_change_scheduled_delay() { +// let (state_var, block_number) = setup(); - // Value change in the future, delay change in the future - mock_value_and_delay_read(state_var, block_number + 1, block_number + 1); +// // Value change in the future, delay change in the future +// mock_value_and_delay_read(state_var, block_number + 1, block_number + 1); - let write_mock = mock_value_change_write(); +// let write_mock = mock_value_change_write(); - state_var.schedule_value_change(new_value); +// state_var.schedule_value_change(new_value); - // The new scheduled value change replaces the old one, pre delay (current, not scheduled) is used - assert_value_change_write( - state_var, - write_mock, - pre_value, - new_value, - block_number + pre_delay - ); -} +// // The new scheduled value change replaces the old one, pre delay (current, not scheduled) is used +// assert_value_change_write( +// state_var, +// write_mock, +// pre_value, +// new_value, +// block_number + pre_delay +// ); +// } -#[test] -fn test_schedule_value_change_after_change_no_scheduled_delay() { - let (state_var, block_number) = setup(); +// #[test] +// fn test_schedule_value_change_after_change_no_scheduled_delay() { +// let (state_var, block_number) = setup(); - // Value change in the past, delay change in the past - mock_value_and_delay_read(state_var, block_number - 1, block_number - 1); - let write_mock = mock_value_change_write(); +// // Value change in the past, delay change in the past +// mock_value_and_delay_read(state_var, block_number - 1, block_number - 1); +// let write_mock = mock_value_change_write(); - state_var.schedule_value_change(new_value); +// state_var.schedule_value_change(new_value); - // The previous post value becomes the pre value, post delay (current) is used - assert_value_change_write( - state_var, - write_mock, - post_value, - new_value, - block_number + post_delay - ); -} +// // The previous post value becomes the pre value, post delay (current) is used +// assert_value_change_write( +// state_var, +// write_mock, +// post_value, +// new_value, +// block_number + post_delay +// ); +// } -#[test] -fn test_schedule_value_change_after_change_scheduled_delay() { - let (state_var, block_number) = setup(); +// #[test] +// fn test_schedule_value_change_after_change_scheduled_delay() { +// let (state_var, block_number) = setup(); - // Value change in the past, delay change in the future - mock_value_and_delay_read(state_var, block_number - 1, block_number + 1); +// // Value change in the past, delay change in the future +// mock_value_and_delay_read(state_var, block_number - 1, block_number + 1); - let write_mock = mock_value_change_write(); +// let write_mock = mock_value_change_write(); - state_var.schedule_value_change(new_value); +// state_var.schedule_value_change(new_value); - // The previous post value becomes the pre value, pre delay (current, not scheduled) is used - assert_value_change_write( - state_var, - write_mock, - post_value, - new_value, - block_number + pre_delay - ); -} +// // The previous post value becomes the pre value, pre delay (current, not scheduled) is used +// assert_value_change_write( +// state_var, +// write_mock, +// post_value, +// new_value, +// block_number + pre_delay +// ); +// } -#[test] -fn test_schedule_delay_increase_before_change() { - let (state_var, block_number) = setup(); +// #[test] +// fn test_schedule_delay_increase_before_change() { +// let (state_var, block_number) = setup(); - // Delay change in future, current delay is pre - mock_delay_change_read(state_var, pre_delay, post_delay, block_number + 1); - let write_mock = mock_delay_change_write(); +// // Delay change in future, current delay is pre +// mock_delay_change_read(state_var, pre_delay, post_delay, block_number + 1); +// let write_mock = mock_delay_change_write(); - let new_delay = pre_delay + 1; - state_var.schedule_delay_change(new_delay as u32); +// let new_delay = pre_delay + 1; +// state_var.schedule_delay_change(new_delay as u32); - // The previous scheduled change is lost, change is immediate (due to increase) - assert_delay_change_write(state_var, write_mock, pre_delay, new_delay, block_number); -} +// // The previous scheduled change is lost, change is immediate (due to increase) +// assert_delay_change_write(state_var, write_mock, pre_delay, new_delay, block_number); +// } -#[test] -fn test_schedule_delay_reduction_before_change() { - let (state_var, block_number) = setup(); +// #[test] +// fn test_schedule_delay_reduction_before_change() { +// let (state_var, block_number) = setup(); - // Delay change in future, current delay is pre - mock_delay_change_read(state_var, pre_delay, post_delay, block_number + 1); - let write_mock = mock_delay_change_write(); +// // Delay change in future, current delay is pre +// mock_delay_change_read(state_var, pre_delay, post_delay, block_number + 1); +// let write_mock = mock_delay_change_write(); + +// let new_delay = pre_delay - 1; +// state_var.schedule_delay_change(new_delay as u32); + +// // The previous scheduled change is lost, change delay equals difference (due to reduction) +// assert_delay_change_write( +// state_var, +// write_mock, +// pre_delay, +// new_delay, +// block_number + pre_delay - new_delay +// ); +// } - let new_delay = pre_delay - 1; - state_var.schedule_delay_change(new_delay as u32); +// #[test] +// fn test_schedule_delay_increase_after_change() { +// let (state_var, block_number) = setup(); - // The previous scheduled change is lost, change delay equals difference (due to reduction) - assert_delay_change_write( - state_var, - write_mock, - pre_delay, - new_delay, - block_number + pre_delay - new_delay - ); -} +// // Delay change in the past, current delay is post +// mock_delay_change_read(state_var, pre_delay, post_delay, block_number - 1); +// let write_mock = mock_delay_change_write(); -#[test] -fn test_schedule_delay_increase_after_change() { - let (state_var, block_number) = setup(); +// let new_delay = post_delay + 1; +// state_var.schedule_delay_change(new_delay as u32); - // Delay change in the past, current delay is post - mock_delay_change_read(state_var, pre_delay, post_delay, block_number - 1); - let write_mock = mock_delay_change_write(); +// // The current value becomes pre, change is immediate (due to increase) +// assert_delay_change_write(state_var, write_mock, post_delay, new_delay, block_number); +// } - let new_delay = post_delay + 1; - state_var.schedule_delay_change(new_delay as u32); +// #[test] +// fn test_schedule_delay_reduction_after_change() { +// let (state_var, block_number) = setup(); - // The current value becomes pre, change is immediate (due to increase) - assert_delay_change_write(state_var, write_mock, post_delay, new_delay, block_number); -} +// // Delay change in the past, current delay is post +// mock_delay_change_read(state_var, pre_delay, post_delay, block_number - 1); +// let write_mock = mock_delay_change_write(); + +// let new_delay = post_delay - 1; +// state_var.schedule_delay_change(new_delay as u32); + +// // The current value becomes pre, change delay equals difference (due to reduction) +// assert_delay_change_write( +// state_var, +// write_mock, +// post_delay, +// new_delay, +// block_number + post_delay - new_delay +// ); +// } #[test] -fn test_schedule_delay_reduction_after_change() { +fn test_get_current_value_in_private_before_change() { let (state_var, block_number) = setup(); + state_var.schedule_value_change(new_value); - // Delay change in the past, current delay is post - mock_delay_change_read(state_var, pre_delay, post_delay, block_number - 1); - let write_mock = mock_delay_change_write(); - - let new_delay = post_delay - 1; - state_var.schedule_delay_change(new_delay as u32); + cheatcodes::advance_blocks(1); - // The current value becomes pre, change delay equals difference (due to reduction) - assert_delay_change_write( - state_var, - write_mock, - post_delay, - new_delay, - block_number + post_delay - new_delay - ); + let block_number = 40; + let mut context = ContextBuilder::new().block_number(block_number).private(); + let storage_slot = 57; + let private_state_var: SharedMutable = SharedMutable::new(&mut context, storage_slot); + + assert_eq(private_state_var.get_current_value_in_private(), 0); + // Here we'd want to test that the private getter returns the correct value and sets max_block_number in the + // context to the expected block horizon, in all the possible scenarios (long before change, before near change, + // after change). + // However, this requires mocking the getPublicDataTreeWitness oracle so that we can convince the circuit that + // it got a valid historical proof. Because we can set the tree root to whatever we want in the context, this is + // trivial for a single historical value (we add a leaf and compute the root with any random path), but is quite + // hard if we're reading more than one value for the same root (as SharedMutable does): we essentially need to + // create an actual indexed tree and compute the correct path for each of the inserted values. + // TODO: implement an actual tree and use it here https://github.com/AztecProtocol/aztec-packages/issues/5494 } - -// #[test] -// fn test_get_current_value_in_private_before_change() { -// let (state_var, block_number) = setup(); -// state_var.schedule_value_change(5); - -// let block_number = 40; -// let mut context = ContextBuilder::new().block_number(block_number).private(); - -// let storage_slot = 57; -// let private_state_var: SharedMutable = SharedMutable::new(&mut context, storage_slot); - -// let foo = private_state_var.get_current_value_in_private(); -// // Here we'd want to test that the private getter returns the correct value and sets max_block_number in the -// // context to the expected block horizon, in all the possible scenarios (long before change, before near change, -// // after change). -// // However, this requires mocking the getPublicDataTreeWitness oracle so that we can convince the circuit that -// // it got a valid historical proof. Because we can set the tree root to whatever we want in the context, this is -// // trivial for a single historical value (we add a leaf and compute the root with any random path), but is quite -// // hard if we're reading more than one value for the same root (as SharedMutable does): we essentially need to -// // create an actual indexed tree and compute the correct path for each of the inserted values. -// // TODO: implement an actual tree and use it here https://github.com/AztecProtocol/aztec-packages/issues/5494 -// } diff --git a/noir-projects/aztec-nr/aztec/src/test/helpers/cheatcodes.nr b/noir-projects/aztec-nr/aztec/src/test/helpers/cheatcodes.nr index 5a3fc46f0c42..39c4d304cb66 100644 --- a/noir-projects/aztec-nr/aztec/src/test/helpers/cheatcodes.nr +++ b/noir-projects/aztec-nr/aztec/src/test/helpers/cheatcodes.nr @@ -1,42 +1,50 @@ use dep::protocol_types::address::AztecAddress; - -#[oracle(reset)] -fn oracle_reset() {} +use crate::context::inputs::PrivateContextInputs; unconstrained pub fn reset() { oracle_reset(); } -#[oracle(getContractAddress)] -fn oracle_get_contract_address() -> AztecAddress {} - unconstrained pub fn get_contract_address() -> AztecAddress { oracle_get_contract_address() } -#[oracle(setContractAddress)] -fn oracle_set_contract_address(address: AztecAddress) {} - unconstrained pub fn set_contract_address(address: AztecAddress) { oracle_set_contract_address(address); } -#[oracle(getBlockNumber)] -fn oracle_get_block_number() -> Field {} - -unconstrained pub fn get_block_number() -> Field { +unconstrained pub fn get_block_number() -> u32 { oracle_get_block_number() } -#[oracle(timeTravel)] -fn oracle_time_travel(blocks: Field) {} - -unconstrained pub fn advance_blocks(blocks: Field) { +unconstrained pub fn advance_blocks(blocks: u32) { oracle_time_travel(blocks); } -unconstrained pub fn advance_block_to(block_number: Field) { +unconstrained pub fn advance_block_to(block_number: u32) { let difference = block_number - get_block_number(); advance_blocks(difference); } +unconstrained pub fn get_private_context_inputs(block_number: u32) -> PrivateContextInputs { + oracle_get_private_context_inputs(block_number) +} + +#[oracle(reset)] +fn oracle_reset() {} + +#[oracle(getContractAddress)] +fn oracle_get_contract_address() -> AztecAddress {} + +#[oracle(setContractAddress)] +fn oracle_set_contract_address(address: AztecAddress) {} + +#[oracle(getBlockNumber)] +fn oracle_get_block_number() -> u32 {} + +#[oracle(timeTravel)] +fn oracle_time_travel(blocks: u32) {} + +#[oracle(getPrivateContextInputs)] +fn oracle_get_private_context_inputs(block_number: u32) -> PrivateContextInputs {} + diff --git a/noir-projects/aztec-nr/aztec/src/test/helpers/context_builder.nr b/noir-projects/aztec-nr/aztec/src/test/helpers/context_builder.nr index 1b069ea02146..6a99fe6241bc 100644 --- a/noir-projects/aztec-nr/aztec/src/test/helpers/context_builder.nr +++ b/noir-projects/aztec-nr/aztec/src/test/helpers/context_builder.nr @@ -2,8 +2,10 @@ use crate::context::{PrivateContext, PublicContext}; use crate::test::helpers::cheatcodes; use dep::protocol_types::address::AztecAddress; +use dep::protocol_types::debug_log; + struct ContextBuilder { - block_number: Option, + block_number: Option, contract_address: Option, } @@ -12,7 +14,7 @@ impl ContextBuilder { Self { block_number: Option::none(), contract_address: Option::none() } } - fn block_number(&mut self, block_number: Field) -> &mut Self { + fn block_number(&mut self, block_number: u32) -> &mut Self { self.block_number = Option::some(block_number); self } @@ -24,35 +26,34 @@ impl ContextBuilder { fn private(self) -> PrivateContext { cheatcodes::reset(); - let mut context = PrivateContext::empty(); if self.block_number.is_some() { let block_number = self.block_number.unwrap_unchecked(); - context.inputs.historical_header.global_variables.block_number = block_number; cheatcodes::advance_block_to(block_number); } if self.contract_address.is_some() { let contract_address = self.contract_address.unwrap_unchecked(); - context.inputs.call_context.storage_contract_address = contract_address; cheatcodes::set_contract_address(contract_address); } - context + let inputs = cheatcodes::get_private_context_inputs(self.block_number.unwrap()); + + let args_hash = 0; + PrivateContext::new(inputs, args_hash) } fn public(self) -> PublicContext { cheatcodes::reset(); + let mut context = PublicContext::empty(); if self.block_number.is_some() { - let block_number = self.block_number.unwrap_unchecked(); - cheatcodes::advance_block_to(block_number); + cheatcodes::advance_block_to(self.block_number.unwrap_unchecked()); } if self.contract_address.is_some() { - let contract_address = self.contract_address.unwrap_unchecked(); - cheatcodes::set_contract_address(contract_address); + cheatcodes::set_contract_address(self.contract_address.unwrap_unchecked()); } context From 2250b98af50f94ab03bf6daaf1fd2309f5746e75 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicol=C3=A1s=20Venturo?= Date: Tue, 4 Jun 2024 14:36:43 +0000 Subject: [PATCH 22/60] ??? --- .../src/state_vars/public_immutable/test.nr | 4 ++- .../src/state_vars/shared_mutable/test.nr | 25 ++++++++++++++----- .../aztec/src/test/helpers/context_builder.nr | 14 ----------- 3 files changed, 22 insertions(+), 21 deletions(-) diff --git a/noir-projects/aztec-nr/aztec/src/state_vars/public_immutable/test.nr b/noir-projects/aztec-nr/aztec/src/state_vars/public_immutable/test.nr index 59e7b0a37efe..d1e84deae03a 100644 --- a/noir-projects/aztec-nr/aztec/src/state_vars/public_immutable/test.nr +++ b/noir-projects/aztec-nr/aztec/src/state_vars/public_immutable/test.nr @@ -1,9 +1,11 @@ use crate::{context::PublicContext, state_vars::public_immutable::PublicImmutable}; -use crate::test::{helpers::context_builder::ContextBuilder, mocks::mock_struct::MockStruct}; +use crate::test::{helpers::{cheatcodes, context_builder::ContextBuilder}, mocks::mock_struct::MockStruct}; use dep::std::test::OracleMock; use dep::protocol_types::traits::Serialize; fn setup() -> PublicImmutable { + cheatcodes::reset(); + let mut context = ContextBuilder::new().public(); let storage_slot = 7; diff --git a/noir-projects/aztec-nr/aztec/src/state_vars/shared_mutable/test.nr b/noir-projects/aztec-nr/aztec/src/state_vars/shared_mutable/test.nr index d21a0f4f54e4..98e61e46754d 100644 --- a/noir-projects/aztec-nr/aztec/src/state_vars/shared_mutable/test.nr +++ b/noir-projects/aztec-nr/aztec/src/state_vars/shared_mutable/test.nr @@ -26,6 +26,8 @@ global post_delay = 15; global TEST_INITIAL_DELAY: u32 = 3; fn setup() -> (SharedMutable, u32) { + cheatcodes::reset(); + let block_number = 40; let mut context = ContextBuilder::new().block_number(block_number).public(); @@ -35,6 +37,13 @@ fn setup() -> (SharedMutable, u32 (state_var, block_number) } +fn private_setup(block_number: u32) -> SharedMutable { + let mut context = ContextBuilder::new().block_number(block_number).private(); + + let storage_slot = 57; + SharedMutable::new(&mut context, storage_slot) +} + fn mock_value_change_read( state_var: SharedMutable, pre: Field, @@ -375,14 +384,18 @@ fn test_get_current_value_in_private_before_change() { let (state_var, block_number) = setup(); state_var.schedule_value_change(new_value); - cheatcodes::advance_blocks(1); + let (_, block_horizon) = state_var.get_scheduled_value_in_public(); - let block_number = 40; - let mut context = ContextBuilder::new().block_number(block_number).private(); - let storage_slot = 57; - let private_state_var: SharedMutable = SharedMutable::new(&mut context, storage_slot); + cheatcodes::advance_blocks(100); + + let pre_change_state_var = private_setup(block_number); + assert_eq(pre_change_state_var.get_current_value_in_private(), 0); + + let pre_change_state_var = private_setup(block_horizon - 1); + assert_eq(pre_change_state_var.get_current_value_in_private(), 0); - assert_eq(private_state_var.get_current_value_in_private(), 0); + let pre_change_state_var = private_setup(block_horizon); + assert_eq(pre_change_state_var.get_current_value_in_private(), new_value); // Here we'd want to test that the private getter returns the correct value and sets max_block_number in the // context to the expected block horizon, in all the possible scenarios (long before change, before near change, // after change). diff --git a/noir-projects/aztec-nr/aztec/src/test/helpers/context_builder.nr b/noir-projects/aztec-nr/aztec/src/test/helpers/context_builder.nr index 6a99fe6241bc..45e7e7571ff5 100644 --- a/noir-projects/aztec-nr/aztec/src/test/helpers/context_builder.nr +++ b/noir-projects/aztec-nr/aztec/src/test/helpers/context_builder.nr @@ -25,18 +25,6 @@ impl ContextBuilder { } fn private(self) -> PrivateContext { - cheatcodes::reset(); - - if self.block_number.is_some() { - let block_number = self.block_number.unwrap_unchecked(); - cheatcodes::advance_block_to(block_number); - } - - if self.contract_address.is_some() { - let contract_address = self.contract_address.unwrap_unchecked(); - cheatcodes::set_contract_address(contract_address); - } - let inputs = cheatcodes::get_private_context_inputs(self.block_number.unwrap()); let args_hash = 0; @@ -44,8 +32,6 @@ impl ContextBuilder { } fn public(self) -> PublicContext { - cheatcodes::reset(); - let mut context = PublicContext::empty(); if self.block_number.is_some() { From 45434f9d91773c0e6aeceb3e866aeed1808995fe Mon Sep 17 00:00:00 2001 From: thunkar Date: Tue, 4 Jun 2024 16:39:36 +0000 Subject: [PATCH 23/60] fixes --- yarn-project/txe/src/oracle/txe_oracle.ts | 38 +++++++++++++++---- .../txe/src/txe_service/txe_service.ts | 3 +- 2 files changed, 32 insertions(+), 9 deletions(-) diff --git a/yarn-project/txe/src/oracle/txe_oracle.ts b/yarn-project/txe/src/oracle/txe_oracle.ts index bfc01defe113..be15cfd2859f 100644 --- a/yarn-project/txe/src/oracle/txe_oracle.ts +++ b/yarn-project/txe/src/oracle/txe_oracle.ts @@ -3,18 +3,22 @@ import { type NoteStatus, type NullifierMembershipWitness, PublicDataWitness, + PublicDataWrite, type UnencryptedL2Log, } from '@aztec/circuit-types'; import { type CompleteAddress, type Header, type KeyValidationRequest, + PUBLIC_DATA_SUBTREE_HEIGHT, type PUBLIC_DATA_TREE_HEIGHT, type PrivateCallStackItem, type PublicCallRequest, + PublicDataTreeLeaf, type PublicDataTreeLeafPreimage, } from '@aztec/circuits.js'; import { Aes128 } from '@aztec/circuits.js/barretenberg'; +import { computePublicDataTreeLeafSlot } from '@aztec/circuits.js/hash'; import { type FunctionSelector } from '@aztec/foundation/abi'; import { type AztecAddress } from '@aztec/foundation/aztec-address'; import { Fr, type Point } from '@aztec/foundation/fields'; @@ -161,10 +165,23 @@ export class TXE implements TypedOracle { } async storageRead(startStorageSlot: Fr, numberOfElements: number): Promise { + const db = this.trees.asLatest(); + const values = []; for (let i = 0n; i < numberOfElements; i++) { const storageSlot = startStorageSlot.add(new Fr(i)); - const value = await this.worldStatePublicDB.storageRead(this.contractAddress, storageSlot); + const leafSlot = computePublicDataTreeLeafSlot(this.contractAddress, storageSlot).toBigInt(); + + const lowLeafResult = await db.getPreviousValueIndex(MerkleTreeId.PUBLIC_DATA_TREE, leafSlot); + + let value = Fr.ZERO; + if (lowLeafResult && lowLeafResult.alreadyPresent) { + const preimage = (await db.getLeafPreimage( + MerkleTreeId.PUBLIC_DATA_TREE, + lowLeafResult.index, + )) as PublicDataTreeLeafPreimage; + value = preimage.value; + } this.logger.debug(`Oracle storage read: slot=${storageSlot.toString()} value=${value}`); values.push(value); } @@ -172,14 +189,19 @@ export class TXE implements TypedOracle { } async storageWrite(startStorageSlot: Fr, values: Fr[]): Promise { - return await Promise.all( - values.map(async (value, i) => { - const storageSlot = startStorageSlot.add(new Fr(i)); - const result = await this.worldStatePublicDB.storageWrite(this.contractAddress, storageSlot, value); - this.logger.debug(`Oracle storage write: slot=${storageSlot.toString()} value=${value}`); - return new Fr(result); - }), + const db = this.trees.asLatest(); + + 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 PublicDataWrite(computePublicDataTreeLeafSlot(this.contractAddress, storageSlot), value); + }); + await db.batchInsert( + MerkleTreeId.PUBLIC_DATA_TREE, + publicDataWrites.map(write => new PublicDataTreeLeaf(write.leafIndex, write.newValue).toBuffer()), + PUBLIC_DATA_SUBTREE_HEIGHT, ); + return publicDataWrites.map(write => write.newValue); } emitEncryptedLog(_contractAddress: AztecAddress, _randomness: Fr, _encryptedNote: Buffer, _counter: number): void { diff --git a/yarn-project/txe/src/txe_service/txe_service.ts b/yarn-project/txe/src/txe_service/txe_service.ts index 4c23cda4d9cb..373c556ec3ca 100644 --- a/yarn-project/txe/src/txe_service/txe_service.ts +++ b/yarn-project/txe/src/txe_service/txe_service.ts @@ -105,7 +105,8 @@ export class TXEService { async reset() { this.blockNumber = 0; - await this.store.clear(); + this.store = openTmpStore(true); + this.trees = await MerkleTrees.new(this.store, this.logger); this.packedValuesCache = new PackedValuesCache(); await this.#timeTravelInner(1); return toForeignCallResult([]); From c180bdbf6128a71bd41da91144c4f68abc7b65e2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicol=C3=A1s=20Venturo?= Date: Tue, 4 Jun 2024 16:54:43 +0000 Subject: [PATCH 24/60] git add . --- .../aztec/src/note/note_getter/test.nr | 41 ++-- .../src/state_vars/private_mutable/test.nr | 14 +- .../src/state_vars/public_immutable/test.nr | 25 +- .../src/state_vars/shared_mutable/test.nr | 221 +++++++----------- .../aztec/src/test/helpers/cheatcodes.nr | 11 +- .../aztec/src/test/helpers/context_builder.nr | 57 +++-- 6 files changed, 173 insertions(+), 196 deletions(-) diff --git a/noir-projects/aztec-nr/aztec/src/note/note_getter/test.nr b/noir-projects/aztec-nr/aztec/src/note/note_getter/test.nr index 7229268f1e55..46020d5606e7 100644 --- a/noir-projects/aztec-nr/aztec/src/note/note_getter/test.nr +++ b/noir-projects/aztec-nr/aztec/src/note/note_getter/test.nr @@ -9,13 +9,13 @@ use crate::{ }; use dep::protocol_types::address::AztecAddress; -use crate::test::{helpers::context_builder::ContextBuilder, mocks::mock_note::MockNote}; +use crate::test::{helpers::{context_builder::TestEnvironment, cheatcodes}, mocks::mock_note::MockNote}; global contract_address = AztecAddress::from_field(69); global storage_slot: Field = 42; -fn setup() -> PrivateContext { - ContextBuilder::new().contract_address(contract_address).private() +fn setup() -> TestEnvironment { + TestEnvironment::new().contract_address(contract_address) } fn build_valid_note(value: Field) -> MockNote { @@ -24,7 +24,8 @@ fn build_valid_note(value: Field) -> MockNote { #[test] fn processes_single_note() { - let mut context = setup(); + let mut env = setup(); + let mut context = env.private(); let mut notes_to_constrain = [Option::none(); MAX_NOTE_HASH_READ_REQUESTS_PER_CALL]; notes_to_constrain[0] = Option::some(build_valid_note(13)); @@ -38,7 +39,8 @@ fn processes_single_note() { #[test] fn processes_many_notes() { - let mut context = setup(); + let mut env = setup(); + let mut context = env.private(); let mut notes_to_constrain = [Option::none(); MAX_NOTE_HASH_READ_REQUESTS_PER_CALL]; notes_to_constrain[0] = Option::some(build_valid_note(13)); @@ -53,7 +55,8 @@ fn processes_many_notes() { #[test] fn collapses_notes_at_the_beginning_of_the_array() { - let mut context = setup(); + let mut env = setup(); + let mut context = env.private(); let mut opt_notes = [Option::none(); MAX_NOTE_HASH_READ_REQUESTS_PER_CALL]; opt_notes[1] = Option::some(build_valid_note(0)); @@ -80,8 +83,9 @@ fn collapses_notes_at_the_beginning_of_the_array() { } #[test(should_fail_with="Cannot return zero notes")] - fn rejects_zero_notes() { - let mut context = setup(); +fn rejects_zero_notes() { + let mut env = setup(); + let mut context = env.private(); let opt_notes: [Option; MAX_NOTE_HASH_READ_REQUESTS_PER_CALL] = [Option::none(); MAX_NOTE_HASH_READ_REQUESTS_PER_CALL]; @@ -91,7 +95,8 @@ fn collapses_notes_at_the_beginning_of_the_array() { #[test(should_fail_with="Got more notes than limit.")] fn rejects_mote_notes_than_limit() { - let mut context = setup(); + let mut env = setup(); + let mut context = env.private(); let mut opt_notes: [Option; MAX_NOTE_HASH_READ_REQUESTS_PER_CALL] = [Option::none(); MAX_NOTE_HASH_READ_REQUESTS_PER_CALL]; opt_notes[1] = Option::some(build_valid_note(0)); @@ -105,7 +110,8 @@ fn rejects_mote_notes_than_limit() { #[test] fn applies_filter_before_constraining() { - let mut context = setup(); + let mut env = setup(); + let mut context = env.private(); let mut notes_to_constrain = [Option::none(); MAX_NOTE_HASH_READ_REQUESTS_PER_CALL]; let invalid_note = MockNote::new(13).build(); // This note does not have the correct address or storage slot @@ -138,7 +144,8 @@ fn applies_filter_before_constraining() { #[test(should_fail_with="Mismatch note header contract address.")] fn rejects_mismatched_address() { - let mut context = setup(); + let mut env = setup(); + let mut context = env.private(); let note = MockNote::new(1).storage_slot(storage_slot).build(); // We're not setting the right contract address @@ -151,7 +158,8 @@ fn rejects_mismatched_address() { #[test(should_fail_with="Mismatch note header storage slot.")] fn rejects_mismatched_storage_slot() { - let mut context = setup(); + let mut env = setup(); + let mut context = env.private(); let note = MockNote::new(1).contract_address(contract_address).build(); // We're not setting the right storage slot @@ -164,7 +172,8 @@ fn rejects_mismatched_storage_slot() { #[test(should_fail_with="Mismatch return note field.")] fn rejects_mismatched_selector() { - let mut context = setup(); + let mut env = setup(); + let mut context = env.private(); let value = 10; let note = build_valid_note(value); @@ -184,7 +193,8 @@ fn rejects_mismatched_selector() { #[test(should_fail_with="Return notes not sorted in descending order.")] fn rejects_mismatched_desc_sort_order() { - let mut context = setup(); + let mut env = setup(); + let mut context = env.private(); let mut opt_notes = [Option::none(); MAX_NOTE_HASH_READ_REQUESTS_PER_CALL]; // Notes in ascending order @@ -201,7 +211,8 @@ fn rejects_mismatched_desc_sort_order() { #[test(should_fail_with="Return notes not sorted in ascending order.")] fn rejects_mismatched_asc_sort_order() { - let mut context = setup(); + let mut env = setup(); + let mut context = env.private(); let mut opt_notes = [Option::none(); MAX_NOTE_HASH_READ_REQUESTS_PER_CALL]; // Notes in descending order diff --git a/noir-projects/aztec-nr/aztec/src/state_vars/private_mutable/test.nr b/noir-projects/aztec-nr/aztec/src/state_vars/private_mutable/test.nr index f0272af161f1..fce592476393 100644 --- a/noir-projects/aztec-nr/aztec/src/state_vars/private_mutable/test.nr +++ b/noir-projects/aztec-nr/aztec/src/state_vars/private_mutable/test.nr @@ -1,14 +1,17 @@ use dep::protocol_types::{address::AztecAddress, grumpkin_point::GrumpkinPoint}; use crate::{context::PrivateContext, state_vars::private_mutable::PrivateMutable}; -use crate::test::{mocks::mock_note::MockNote, helpers::context_builder::ContextBuilder}; +use crate::test::{mocks::mock_note::MockNote, helpers::context_builder::TestEnvironment}; use dep::std::{unsafe::zeroed, test::OracleMock}; global contract_address = AztecAddress::from_field(13); global storage_slot = 17; -fn setup() -> PrivateMutable { - let mut context = ContextBuilder::new().contract_address(contract_address).private(); - let state_var = PrivateMutable::new(&mut context, storage_slot); +fn setup() -> TestEnvironment { + TestEnvironment::new().contract_address(contract_address) +} + +fn in_private(env: &mut TestEnvironment) -> PrivateMutable { + let state_var = PrivateMutable::new(&mut env.private(), storage_slot); // This oracle is called for its side effects alone - it's always expected to return 0. OracleMock::mock("notifyCreatedNote").returns(0); @@ -18,7 +21,8 @@ fn setup() -> PrivateMutable { #[test] fn test_initialize_or_replace_without_nullifier() { - let state_var = setup(); + let mut env = setup(); + let state_var = in_private(&mut env); let ovpk_m: GrumpkinPoint = zeroed(); let ivpk_m: GrumpkinPoint = zeroed(); diff --git a/noir-projects/aztec-nr/aztec/src/state_vars/public_immutable/test.nr b/noir-projects/aztec-nr/aztec/src/state_vars/public_immutable/test.nr index d1e84deae03a..a54640ecaa18 100644 --- a/noir-projects/aztec-nr/aztec/src/state_vars/public_immutable/test.nr +++ b/noir-projects/aztec-nr/aztec/src/state_vars/public_immutable/test.nr @@ -1,27 +1,30 @@ use crate::{context::PublicContext, state_vars::public_immutable::PublicImmutable}; -use crate::test::{helpers::{cheatcodes, context_builder::ContextBuilder}, mocks::mock_struct::MockStruct}; +use crate::test::{helpers::context_builder::TestEnvironment, mocks::mock_struct::MockStruct}; use dep::std::test::OracleMock; use dep::protocol_types::traits::Serialize; -fn setup() -> PublicImmutable { - cheatcodes::reset(); +global storage_slot = 7; - let mut context = ContextBuilder::new().public(); - let storage_slot = 7; +fn setup() -> TestEnvironment { + TestEnvironment::new() +} - PublicImmutable::new(&mut context, storage_slot) +fn in_public(env: TestEnvironment) -> PublicImmutable { + PublicImmutable::new(&mut env.public(), storage_slot) } #[test] fn test_uninitialized_by_default() { - let state_var = setup(); + let env = setup(); + let state_var = in_public(env); assert_eq(state_var.is_initialized(), false); } #[test] fn test_initialize_uninitialized() { - let state_var = setup(); + let env = setup(); + let state_var = in_public(env); let value = MockStruct::new(5, 6); @@ -33,7 +36,8 @@ fn test_initialize_uninitialized() { #[test(should_fail_with="PublicImmutable already initialized")] fn test_initialize_already_initialized() { - let state_var = setup(); + let env = setup(); + let state_var = in_public(env); let value = MockStruct::new(5, 6); @@ -43,7 +47,8 @@ fn test_initialize_already_initialized() { #[test(should_fail_with="PublicImmutable not initialized")] fn test_read_uninitialized() { - let state_var = setup(); + let env = setup(); + let state_var = in_public(env); let _ = state_var.read(); } diff --git a/noir-projects/aztec-nr/aztec/src/state_vars/shared_mutable/test.nr b/noir-projects/aztec-nr/aztec/src/state_vars/shared_mutable/test.nr index 98e61e46754d..f81b9bc499ea 100644 --- a/noir-projects/aztec-nr/aztec/src/state_vars/shared_mutable/test.nr +++ b/noir-projects/aztec-nr/aztec/src/state_vars/shared_mutable/test.nr @@ -6,8 +6,7 @@ use crate::{ shared_mutable::SharedMutable, scheduled_value_change::ScheduledValueChange, scheduled_delay_change::ScheduledDelayChange }, - test::helpers::{cheatcodes, context_builder::ContextBuilder}, - oracle::get_public_data_witness::PublicDataWitness + test::helpers::context_builder::TestEnvironment, oracle::get_public_data_witness::PublicDataWitness }; use dep::protocol_types::{ @@ -15,154 +14,82 @@ use dep::protocol_types::{ address::AztecAddress, public_data_tree_leaf_preimage::PublicDataTreeLeafPreimage }; -global pre_value = 13; -global post_value = 42; - global new_value = 57; global pre_delay = 20; global post_delay = 15; -global TEST_INITIAL_DELAY: u32 = 3; - -fn setup() -> (SharedMutable, u32) { - cheatcodes::reset(); +global storage_slot = 57; - let block_number = 40; - let mut context = ContextBuilder::new().block_number(block_number).public(); - - let storage_slot = 57; - let state_var = SharedMutable::new(&mut context, storage_slot); +global TEST_INITIAL_DELAY: u32 = 3; - (state_var, block_number) +fn setup() -> TestEnvironment { + TestEnvironment::new() } -fn private_setup(block_number: u32) -> SharedMutable { - let mut context = ContextBuilder::new().block_number(block_number).private(); - - let storage_slot = 57; - SharedMutable::new(&mut context, storage_slot) +fn in_public(env: TestEnvironment) -> SharedMutable { + SharedMutable::new(&mut env.public(), storage_slot) } -fn mock_value_change_read( - state_var: SharedMutable, - pre: Field, - post: Field, - block_of_change: Field -) { - let value_change_slot = state_var.get_value_change_storage_slot(); - let fields = ScheduledValueChange::new(pre, post, block_of_change as u32).serialize(); - - let _ = OracleMock::mock("storageRead").with_params((value_change_slot, 3)).returns(fields).times(1); +fn in_private( + env: &mut TestEnvironment, + historical_block_number: u32 +) -> SharedMutable { + SharedMutable::new(&mut env.private_at(historical_block_number), storage_slot) } -fn mock_delay_change_read( - state_var: SharedMutable, - pre: Field, - post: Field, - block_of_change: Field -) { - let delay_change_slot = state_var.get_delay_change_storage_slot(); - let delay_change: ScheduledDelayChange = ScheduledDelayChange::new( - Option::some(pre as u32), - Option::some(post as u32), - block_of_change as u32 - ); - let fields = delay_change.serialize(); - - let _ = OracleMock::mock("storageRead").with_params((delay_change_slot, 1)).returns(fields).times(1); -} +#[test] +fn test_get_current_value_in_public_initial() { + let env = setup(); + let state_var = in_public(env); -fn mock_delay_change_read_uninitialized(state_var: SharedMutable) { - let delay_change_slot = state_var.get_delay_change_storage_slot(); - let _ = OracleMock::mock("storageRead").with_params((delay_change_slot, 1)).returns([0]).times(1); + // 0 is the default empty value for a Field + assert_eq(state_var.get_current_value_in_public(), 0); } -// Useful since change and delay values are always the global pre/post ones, so we typically only care about their -// block of change. -fn mock_value_and_delay_read( - state_var: SharedMutable, - value_block_of_change: Field, - delay_block_of_change: Field -) { - mock_value_change_read(state_var, pre_value, post_value, value_block_of_change); - mock_delay_change_read(state_var, pre_delay, post_delay, delay_block_of_change); -} +#[test] +fn test_get_current_value_in_public_before_scheduled_change() { + let mut env = setup(); + let state_var = in_public(env); -fn mock_value_change_write() -> OracleMock { - OracleMock::mock("storageWrite").returns([0; 3]) -} + state_var.schedule_value_change(new_value); -fn mock_delay_change_write() -> OracleMock { - OracleMock::mock("storageWrite").returns([0; 1]) -} + let (_, block_of_change) = state_var.get_scheduled_value_in_public(); -fn assert_value_change_write( - state_var: SharedMutable, - mock: OracleMock, - pre: Field, - post: Field, - block_of_change: Field -) { - let fields = ScheduledValueChange::new(pre, post, block_of_change as u32).serialize(); - assert_eq(mock.get_last_params(), (state_var.get_value_change_storage_slot(), fields)); -} + let original_value = 0; -fn assert_delay_change_write( - state_var: SharedMutable, - mock: OracleMock, - pre: Field, - post: Field, - block_of_change: Field -) { - let delay_change: ScheduledDelayChange = ScheduledDelayChange::new( - Option::some(pre as u32), - Option::some(post as u32), - block_of_change as u32 - ); - - let fields = delay_change.serialize(); - assert_eq(mock.get_last_params(), (state_var.get_delay_change_storage_slot(), fields)); + // The current value has not changed + assert_eq(state_var.get_current_value_in_public(), original_value); + + // The current value still does not change right before the block of change + env.advance_block_to(block_of_change - 1); + assert_eq(state_var.get_current_value_in_public(), original_value); } #[test] -fn test_get_current_value_in_public() { - let (state_var, block_number) = setup(); +fn test_get_current_value_in_public_at_scheduled_change() { + let mut env = setup(); + let state_var = in_public(env); state_var.schedule_value_change(new_value); - let (scheduled_value, block_of_change) = state_var.get_scheduled_value_in_public(); - - assert_eq(scheduled_value, new_value); - assert_eq(block_of_change, block_number + TEST_INITIAL_DELAY); - assert_eq(state_var.get_current_value_in_public(), 0); // initial value - - cheatcodes::advance_block_to(block_of_change); + let (_, block_of_change) = state_var.get_scheduled_value_in_public(); + env.advance_block_to(block_of_change); assert_eq(state_var.get_current_value_in_public(), new_value); - assert_eq(state_var.get_scheduled_value_in_public(), (scheduled_value, block_of_change)); - - cheatcodes::advance_blocks(10); - - assert_eq(state_var.get_current_value_in_public(), new_value); - assert_eq(state_var.get_scheduled_value_in_public(), (scheduled_value, block_of_change)); } #[test] -fn test_get_scheduled_value_in_public() { - let (state_var, block_number) = setup(); +fn test_get_current_value_in_public_after_scheduled_change() { + let mut env = setup(); + let state_var = in_public(env); state_var.schedule_value_change(new_value); - assert_eq( - state_var.get_scheduled_value_in_public(), (new_value, (block_number + TEST_INITIAL_DELAY) as u32) - ); + let (_, block_of_change) = state_var.get_scheduled_value_in_public(); - cheatcodes::advance_blocks(TEST_INITIAL_DELAY * 2); - - assert_eq( - state_var.get_scheduled_value_in_public(), (new_value, (block_number + TEST_INITIAL_DELAY) as u32) - ); + env.advance_block_to(block_of_change + 10); + assert_eq(state_var.get_current_value_in_public(), new_value); } // #[test] @@ -381,28 +308,52 @@ fn test_get_scheduled_value_in_public() { #[test] fn test_get_current_value_in_private_before_change() { - let (state_var, block_number) = setup(); + let mut env = setup(); + + let public_state_var = in_public(env); + public_state_var.schedule_value_change(new_value); + + let schedule_block_number = env.block_number; + + let private_state_var = in_private(&mut env, schedule_block_number); + assert_eq(private_state_var.get_current_value_in_private(), 0); +} + +#[test] +fn test_get_current_value_in_private_immediately_before_change() { + let mut env = setup(); + + let state_var = in_public(env); + state_var.schedule_value_change(new_value); + + let (_, block_horizon) = state_var.get_scheduled_value_in_public(); + + let private_state_var = in_private(&mut env, block_horizon - 1); + assert_eq(private_state_var.get_current_value_in_private(), 0); +} + +#[test] +fn test_get_current_value_in_private_at_change() { + let mut env = setup(); + + let state_var = in_public(env); + state_var.schedule_value_change(new_value); + + let (_, block_horizon) = state_var.get_scheduled_value_in_public(); + + let private_state_var = in_private(&mut env, block_horizon); + assert_eq(private_state_var.get_current_value_in_private(), new_value); +} + +#[test] +fn test_get_current_value_in_private_after_change() { + let mut env = setup(); + + let state_var = in_public(env); state_var.schedule_value_change(new_value); let (_, block_horizon) = state_var.get_scheduled_value_in_public(); - cheatcodes::advance_blocks(100); - - let pre_change_state_var = private_setup(block_number); - assert_eq(pre_change_state_var.get_current_value_in_private(), 0); - - let pre_change_state_var = private_setup(block_horizon - 1); - assert_eq(pre_change_state_var.get_current_value_in_private(), 0); - - let pre_change_state_var = private_setup(block_horizon); - assert_eq(pre_change_state_var.get_current_value_in_private(), new_value); - // Here we'd want to test that the private getter returns the correct value and sets max_block_number in the - // context to the expected block horizon, in all the possible scenarios (long before change, before near change, - // after change). - // However, this requires mocking the getPublicDataTreeWitness oracle so that we can convince the circuit that - // it got a valid historical proof. Because we can set the tree root to whatever we want in the context, this is - // trivial for a single historical value (we add a leaf and compute the root with any random path), but is quite - // hard if we're reading more than one value for the same root (as SharedMutable does): we essentially need to - // create an actual indexed tree and compute the correct path for each of the inserted values. - // TODO: implement an actual tree and use it here https://github.com/AztecProtocol/aztec-packages/issues/5494 + let private_state_var = in_private(&mut env, block_horizon + 10); + assert_eq(private_state_var.get_current_value_in_private(), new_value); } diff --git a/noir-projects/aztec-nr/aztec/src/test/helpers/cheatcodes.nr b/noir-projects/aztec-nr/aztec/src/test/helpers/cheatcodes.nr index 39c4d304cb66..b043543b1424 100644 --- a/noir-projects/aztec-nr/aztec/src/test/helpers/cheatcodes.nr +++ b/noir-projects/aztec-nr/aztec/src/test/helpers/cheatcodes.nr @@ -21,13 +21,8 @@ unconstrained pub fn advance_blocks(blocks: u32) { oracle_time_travel(blocks); } -unconstrained pub fn advance_block_to(block_number: u32) { - let difference = block_number - get_block_number(); - advance_blocks(difference); -} - -unconstrained pub fn get_private_context_inputs(block_number: u32) -> PrivateContextInputs { - oracle_get_private_context_inputs(block_number) +unconstrained pub fn get_private_context_inputs(historical_block_number: u32) -> PrivateContextInputs { + oracle_get_private_context_inputs(historical_block_number) } #[oracle(reset)] @@ -46,5 +41,5 @@ fn oracle_get_block_number() -> u32 {} fn oracle_time_travel(blocks: u32) {} #[oracle(getPrivateContextInputs)] -fn oracle_get_private_context_inputs(block_number: u32) -> PrivateContextInputs {} +fn oracle_get_private_context_inputs(historical_block_number: u32) -> PrivateContextInputs {} diff --git a/noir-projects/aztec-nr/aztec/src/test/helpers/context_builder.nr b/noir-projects/aztec-nr/aztec/src/test/helpers/context_builder.nr index 45e7e7571ff5..31ef8fe6c419 100644 --- a/noir-projects/aztec-nr/aztec/src/test/helpers/context_builder.nr +++ b/noir-projects/aztec-nr/aztec/src/test/helpers/context_builder.nr @@ -1,47 +1,58 @@ +use dep::protocol_types::address::AztecAddress; use crate::context::{PrivateContext, PublicContext}; use crate::test::helpers::cheatcodes; -use dep::protocol_types::address::AztecAddress; - -use dep::protocol_types::debug_log; -struct ContextBuilder { - block_number: Option, +struct TestEnvironment { + block_number: u32, contract_address: Option, } -impl ContextBuilder { +impl TestEnvironment { fn new() -> Self { - Self { block_number: Option::none(), contract_address: Option::none() } - } + cheatcodes::reset(); - fn block_number(&mut self, block_number: u32) -> &mut Self { - self.block_number = Option::some(block_number); - self + Self { block_number: 1, contract_address: Option::none() } } - fn contract_address(&mut self, contract_address: AztecAddress) -> &mut Self { + fn contract_address(&mut self, contract_address: AztecAddress) -> Self { self.contract_address = Option::some(contract_address); - self + *self } - fn private(self) -> PrivateContext { - let inputs = cheatcodes::get_private_context_inputs(self.block_number.unwrap()); + fn advance_block_to(&mut self, block_number: u32) { + let difference = block_number - self.block_number; + self.advance_block_by(difference); + } - let args_hash = 0; - PrivateContext::new(inputs, args_hash) + fn advance_block_by(&mut self, blocks: u32) { + self.block_number += blocks; + cheatcodes::advance_blocks(blocks); } fn public(self) -> PublicContext { - let mut context = PublicContext::empty(); + if (self.contract_address.is_some()) { + cheatcodes::set_contract_address(self.contract_address.unwrap_unchecked()); + } - if self.block_number.is_some() { - cheatcodes::advance_block_to(self.block_number.unwrap_unchecked()); + PublicContext::empty() + } + + fn private(&mut self) -> PrivateContext { + self.private_at(self.block_number) + } + + fn private_at(&mut self, historical_block_number: u32) -> PrivateContext { + if historical_block_number >= self.block_number { + self.advance_block_to(historical_block_number + 2); } - if self.contract_address.is_some() { - cheatcodes::set_contract_address(self.contract_address.unwrap_unchecked()); + let mut inputs = cheatcodes::get_private_context_inputs(historical_block_number); + let args_hash = 0; + + if (self.contract_address.is_some()) { + inputs.call_context.storage_contract_address = self.contract_address.unwrap_unchecked(); } - context + PrivateContext::new(inputs, args_hash) } } From a34bf85b3d27af0c010a1266f84bc5649694f1a9 Mon Sep 17 00:00:00 2001 From: thunkar Date: Tue, 4 Jun 2024 17:02:37 +0000 Subject: [PATCH 25/60] fixes and moar --- yarn-project/txe/src/oracle/txe_oracle.ts | 4 ---- yarn-project/txe/src/txe_service/txe_service.ts | 5 ++--- 2 files changed, 2 insertions(+), 7 deletions(-) diff --git a/yarn-project/txe/src/oracle/txe_oracle.ts b/yarn-project/txe/src/oracle/txe_oracle.ts index be15cfd2859f..9d73681632f9 100644 --- a/yarn-project/txe/src/oracle/txe_oracle.ts +++ b/yarn-project/txe/src/oracle/txe_oracle.ts @@ -28,14 +28,11 @@ import { type NoteData, type PackedValuesCache, type TypedOracle, - WorldStatePublicDB, } from '@aztec/simulator'; import { type ContractInstance } from '@aztec/types/contracts'; import { MerkleTreeSnapshotOperationsFacade, type MerkleTrees } from '@aztec/world-state'; export class TXE implements TypedOracle { - private worldStatePublicDB: WorldStatePublicDB; - constructor( private logger: Logger, private trees: MerkleTrees, @@ -43,7 +40,6 @@ export class TXE implements TypedOracle { private contractAddress: AztecAddress, ) { this.packedValuesCache = packedValuesCache; - this.worldStatePublicDB = new WorldStatePublicDB(this.trees.asLatest()); } getRandomField() { diff --git a/yarn-project/txe/src/txe_service/txe_service.ts b/yarn-project/txe/src/txe_service/txe_service.ts index 373c556ec3ca..f696a404f1d4 100644 --- a/yarn-project/txe/src/txe_service/txe_service.ts +++ b/yarn-project/txe/src/txe_service/txe_service.ts @@ -26,7 +26,6 @@ export class TXEService { private typedOracle: TypedOracle, private store: AztecKVStore, private trees: MerkleTrees, - private packedValuesCache: PackedValuesCache, private contractAddress: AztecAddress, ) {} @@ -36,7 +35,7 @@ export class TXEService { const packedValuesCache = new PackedValuesCache(); logger.info(`TXE service initialized`); const txe = new TXE(logger, trees, packedValuesCache, contractAddress); - const service = new TXEService(logger, txe, store, trees, packedValuesCache, contractAddress); + const service = new TXEService(logger, txe, store, trees, contractAddress); await service.timeTravel(toSingle(new Fr(1n))); return service; } @@ -107,7 +106,7 @@ export class TXEService { this.blockNumber = 0; this.store = openTmpStore(true); this.trees = await MerkleTrees.new(this.store, this.logger); - this.packedValuesCache = new PackedValuesCache(); + this.typedOracle = new TXE(this.logger, this.trees, new PackedValuesCache(), this.contractAddress); await this.#timeTravelInner(1); return toForeignCallResult([]); } From fdbeaf87fa54169f735ef478dd260a262f53ff4c Mon Sep 17 00:00:00 2001 From: thunkar Date: Tue, 4 Jun 2024 18:20:44 +0000 Subject: [PATCH 26/60] note handling --- yarn-project/simulator/src/client/index.ts | 2 + yarn-project/txe/src/oracle/txe_oracle.ts | 100 ++++++++++++---- .../txe/src/txe_service/txe_service.ts | 109 +++++++++++++++++- 3 files changed, 185 insertions(+), 26 deletions(-) diff --git a/yarn-project/simulator/src/client/index.ts b/yarn-project/simulator/src/client/index.ts index 60fcf15d4a98..bb30eff13125 100644 --- a/yarn-project/simulator/src/client/index.ts +++ b/yarn-project/simulator/src/client/index.ts @@ -1,3 +1,5 @@ export * from './simulator.js'; export * from './db_oracle.js'; export * from './execution_result.js'; +export * from './pick_notes.js'; +export * from './execution_note_cache.js'; diff --git a/yarn-project/txe/src/oracle/txe_oracle.ts b/yarn-project/txe/src/oracle/txe_oracle.ts index 9d73681632f9..fae81f7375cf 100644 --- a/yarn-project/txe/src/oracle/txe_oracle.ts +++ b/yarn-project/txe/src/oracle/txe_oracle.ts @@ -1,5 +1,6 @@ import { MerkleTreeId, + Note, type NoteStatus, type NullifierMembershipWitness, PublicDataWitness, @@ -10,6 +11,7 @@ import { type CompleteAddress, type Header, type KeyValidationRequest, + NULLIFIER_SUBTREE_HEIGHT, PUBLIC_DATA_SUBTREE_HEIGHT, type PUBLIC_DATA_TREE_HEIGHT, type PrivateCallStackItem, @@ -18,16 +20,18 @@ import { type PublicDataTreeLeafPreimage, } from '@aztec/circuits.js'; import { Aes128 } from '@aztec/circuits.js/barretenberg'; -import { computePublicDataTreeLeafSlot } from '@aztec/circuits.js/hash'; +import { computePublicDataTreeLeafSlot, siloNoteHash, siloNullifier } from '@aztec/circuits.js/hash'; import { type FunctionSelector } from '@aztec/foundation/abi'; import { type AztecAddress } from '@aztec/foundation/aztec-address'; import { Fr, type Point } from '@aztec/foundation/fields'; import { type Logger, applyStringFormatting } from '@aztec/foundation/log'; import { + type ExecutionNoteCache, type MessageLoadOracleInputs, type NoteData, type PackedValuesCache, type TypedOracle, + pickNotes, } from '@aztec/simulator'; import { type ContractInstance } from '@aztec/types/contracts'; import { MerkleTreeSnapshotOperationsFacade, type MerkleTrees } from '@aztec/world-state'; @@ -37,9 +41,11 @@ export class TXE implements TypedOracle { private logger: Logger, private trees: MerkleTrees, private packedValuesCache: PackedValuesCache, + private noteCache: ExecutionNoteCache, private contractAddress: AztecAddress, ) { this.packedValuesCache = packedValuesCache; + this.noteCache = noteCache; } getRandomField() { @@ -122,34 +128,82 @@ export class TXE implements TypedOracle { } getNotes( - _storageSlot: Fr, - _numSelects: number, - _selectByIndexes: number[], - _selectByOffsets: number[], - _selectByLengths: number[], - _selectValues: Fr[], - _selectComparators: number[], - _sortByIndexes: number[], - _sortByOffsets: number[], - _sortByLengths: number[], - _sortOrder: number[], - _limit: number, - _offset: number, + storageSlot: Fr, + numSelects: number, + selectByIndexes: number[], + selectByOffsets: number[], + selectByLengths: number[], + selectValues: Fr[], + selectComparators: number[], + sortByIndexes: number[], + sortByOffsets: number[], + sortByLengths: number[], + sortOrder: number[], + limit: number, + offset: number, _status: NoteStatus, - ): Promise { - throw new Error('Method not implemented.'); - } + ) { + // Nullified pending notes are already removed from the list. + const pendingNotes = this.noteCache.getNotes(this.contractAddress, storageSlot); + + // const pendingNullifiers = this.noteCache.getNullifiers(this.contractAddress); + // const dbNotes = await this.db.getNotes(this.contractAddress, storageSlot, status); + // const dbNotesFiltered = dbNotes.filter(n => !pendingNullifiers.has((n.siloedNullifier as Fr).value)); + + const notes = pickNotes(pendingNotes, { + selects: selectByIndexes.slice(0, numSelects).map((index, i) => ({ + selector: { index, offset: selectByOffsets[i], length: selectByLengths[i] }, + value: selectValues[i], + comparator: selectComparators[i], + })), + sorts: sortByIndexes.map((index, i) => ({ + selector: { index, offset: sortByOffsets[i], length: sortByLengths[i] }, + order: sortOrder[i], + })), + limit, + offset, + }); - notifyCreatedNote(_storageSlot: Fr, _noteTypeId: Fr, _note: Fr[], _innerNoteHash: Fr, _counter: number): void { - throw new Error('Method not implemented.'); + this.logger.debug( + `Returning ${notes.length} notes for ${this.contractAddress} at ${storageSlot}: ${notes + .map(n => `${n.nonce.toString()}:[${n.note.items.map(i => i.toString()).join(',')}]`) + .join(', ')}`, + ); + + return Promise.resolve(notes); + } + + async notifyCreatedNote(storageSlot: Fr, noteTypeId: Fr, noteItems: Fr[], innerNoteHash: Fr, counter: number) { + const note = new Note(noteItems); + this.noteCache.addNewNote( + { + contractAddress: this.contractAddress, + storageSlot, + nonce: Fr.ZERO, // Nonce cannot be known during private execution. + note, + siloedNullifier: undefined, // Siloed nullifier cannot be known for newly created note. + innerNoteHash, + }, + counter, + ); + const db = this.trees.asLatest(); + const noteHash = siloNoteHash(this.contractAddress, innerNoteHash); + await db.appendLeaves(MerkleTreeId.NOTE_HASH_TREE, [noteHash]); } - notifyNullifiedNote(_innerNullifier: Fr, _innerNoteHash: Fr, _counter: number): Promise { - throw new Error('Method not implemented.'); + async notifyNullifiedNote(innerNullifier: Fr, innerNoteHash: Fr, _counter: number) { + this.noteCache.nullifyNote(this.contractAddress, innerNullifier, innerNoteHash); + const db = this.trees.asLatest(); + const siloedNullifier = siloNullifier(this.contractAddress, innerNullifier); + await db.batchInsert(MerkleTreeId.NULLIFIER_TREE, [siloedNullifier.toBuffer()], NULLIFIER_SUBTREE_HEIGHT); + return Promise.resolve(); } - checkNullifierExists(_innerNullifier: Fr): Promise { - throw new Error('Method not implemented.'); + async checkNullifierExists(innerNullifier: Fr): Promise { + const nullifier = siloNullifier(this.contractAddress, innerNullifier!); + const db = this.trees.asLatest(); + const index = await db.findLeafIndex(MerkleTreeId.NULLIFIER_TREE, nullifier.toBuffer()); + return index !== undefined; } getL1ToL2MembershipWitness( diff --git a/yarn-project/txe/src/txe_service/txe_service.ts b/yarn-project/txe/src/txe_service/txe_service.ts index f696a404f1d4..3aef9ab03019 100644 --- a/yarn-project/txe/src/txe_service/txe_service.ts +++ b/yarn-project/txe/src/txe_service/txe_service.ts @@ -4,7 +4,7 @@ import { AztecAddress } from '@aztec/foundation/aztec-address'; import { type Logger } from '@aztec/foundation/log'; import { type AztecKVStore } from '@aztec/kv-store'; import { openTmpStore } from '@aztec/kv-store/utils'; -import { PackedValuesCache, type TypedOracle } from '@aztec/simulator'; +import { ExecutionNoteCache, PackedValuesCache, type TypedOracle } from '@aztec/simulator'; import { MerkleTrees } from '@aztec/world-state'; import { TXE } from '../oracle/txe_oracle.js'; @@ -33,8 +33,9 @@ export class TXEService { const store = openTmpStore(true); const trees = await MerkleTrees.new(store, logger); const packedValuesCache = new PackedValuesCache(); + const noteCache = new ExecutionNoteCache(); logger.info(`TXE service initialized`); - const txe = new TXE(logger, trees, packedValuesCache, contractAddress); + const txe = new TXE(logger, trees, packedValuesCache, noteCache, contractAddress); const service = new TXEService(logger, txe, store, trees, contractAddress); await service.timeTravel(toSingle(new Fr(1n))); return service; @@ -106,7 +107,13 @@ export class TXEService { this.blockNumber = 0; this.store = openTmpStore(true); this.trees = await MerkleTrees.new(this.store, this.logger); - this.typedOracle = new TXE(this.logger, this.trees, new PackedValuesCache(), this.contractAddress); + this.typedOracle = new TXE( + this.logger, + this.trees, + new PackedValuesCache(), + new ExecutionNoteCache(), + this.contractAddress, + ); await this.#timeTravelInner(1); return toForeignCallResult([]); } @@ -174,4 +181,100 @@ export class TXEService { ); return toForeignCallResult([toArray(result)]); } + + async getNotes( + storageSlot: ForeignCallSingle, + numSelects: ForeignCallSingle, + selectByIndexes: ForeignCallArray, + selectByOffsets: ForeignCallArray, + selectByLengths: ForeignCallArray, + selectValues: ForeignCallArray, + selectComparators: ForeignCallArray, + sortByIndexes: ForeignCallArray, + sortByOffsets: ForeignCallArray, + sortByLengths: ForeignCallArray, + sortOrder: ForeignCallArray, + limit: ForeignCallSingle, + offset: ForeignCallSingle, + status: ForeignCallSingle, + returnSize: ForeignCallSingle, + ) { + const noteDatas = await this.typedOracle.getNotes( + fromSingle(storageSlot), + fromSingle(numSelects).toNumber(), + fromArray(selectByIndexes).map(fr => fr.toNumber()), + fromArray(selectByOffsets).map(fr => fr.toNumber()), + fromArray(selectByLengths).map(fr => fr.toNumber()), + fromArray(selectValues), + fromArray(selectComparators).map(fr => fr.toNumber()), + fromArray(sortByIndexes).map(fr => fr.toNumber()), + fromArray(sortByOffsets).map(fr => fr.toNumber()), + fromArray(sortByLengths).map(fr => fr.toNumber()), + fromArray(sortOrder).map(fr => fr.toNumber()), + fromSingle(limit).toNumber(), + fromSingle(offset).toNumber(), + fromSingle(status).toNumber(), + ); + const noteLength = noteDatas?.[0]?.note.items.length ?? 0; + if (!noteDatas.every(({ note }) => noteLength === note.items.length)) { + throw new Error('Notes should all be the same length.'); + } + + const contractAddress = noteDatas[0]?.contractAddress ?? Fr.ZERO; + + // Values indicates whether the note is settled or transient. + const noteTypes = { + isSettled: new Fr(0), + isTransient: new Fr(1), + }; + const flattenData = noteDatas.flatMap(({ nonce, note, index }) => [ + nonce, + index === undefined ? noteTypes.isTransient : noteTypes.isSettled, + ...note.items, + ]); + + const returnFieldSize = fromSingle(returnSize).toNumber(); + const returnData = [noteDatas.length, contractAddress, ...flattenData].map(v => new Fr(v)); + if (returnData.length > returnFieldSize) { + throw new Error(`Return data size too big. Maximum ${returnFieldSize} fields. Got ${flattenData.length}.`); + } + + const paddedZeros = Array(returnFieldSize - returnData.length).fill(new Fr(0)); + return toForeignCallResult([toArray([...returnData, ...paddedZeros])]); + } + + notifyCreatedNote( + storageSlot: ForeignCallSingle, + noteTypeId: ForeignCallSingle, + note: ForeignCallArray, + innerNoteHash: ForeignCallSingle, + counter: ForeignCallSingle, + ) { + this.typedOracle.notifyCreatedNote( + fromSingle(storageSlot), + fromSingle(noteTypeId), + fromArray(note), + fromSingle(innerNoteHash), + fromSingle(counter).toNumber(), + ); + return toForeignCallResult([]); + } + + async notifyNullifiedNote( + innerNullifier: ForeignCallSingle, + innerNoteHash: ForeignCallSingle, + counter: ForeignCallSingle, + ) { + await this.typedOracle.notifyNullifiedNote( + fromSingle(innerNullifier), + fromSingle(innerNoteHash), + fromSingle(counter).toNumber(), + ); + return toForeignCallResult([]); + } + + async checkNullifierExists(innerNullifier: ForeignCallSingle) { + const exists = await this.typedOracle.checkNullifierExists(fromSingle(innerNullifier)); + return toForeignCallResult([toSingle(new Fr(exists))]); + } } From 12453eae864f6bc3def7d2f06c2ef83227f56a31 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicol=C3=A1s=20Venturo?= Date: Wed, 5 Jun 2024 15:05:25 +0000 Subject: [PATCH 27/60] Samples --- .../aztec/src/note/note_getter/test.nr | 2 +- .../src/state_vars/private_mutable/test.nr | 2 +- .../src/state_vars/public_immutable/test.nr | 3 +- .../src/state_vars/shared_mutable/test.nr | 136 +++++++++--------- .../aztec-nr/aztec/src/test/helpers.nr | 2 +- ...context_builder.nr => test_environment.nr} | 2 +- 6 files changed, 75 insertions(+), 72 deletions(-) rename noir-projects/aztec-nr/aztec/src/test/helpers/{context_builder.nr => test_environment.nr} (96%) diff --git a/noir-projects/aztec-nr/aztec/src/note/note_getter/test.nr b/noir-projects/aztec-nr/aztec/src/note/note_getter/test.nr index 46020d5606e7..0ba1c1c08bb5 100644 --- a/noir-projects/aztec-nr/aztec/src/note/note_getter/test.nr +++ b/noir-projects/aztec-nr/aztec/src/note/note_getter/test.nr @@ -9,7 +9,7 @@ use crate::{ }; use dep::protocol_types::address::AztecAddress; -use crate::test::{helpers::{context_builder::TestEnvironment, cheatcodes}, mocks::mock_note::MockNote}; +use crate::test::{helpers::{test_environment::TestEnvironment, cheatcodes}, mocks::mock_note::MockNote}; global contract_address = AztecAddress::from_field(69); global storage_slot: Field = 42; diff --git a/noir-projects/aztec-nr/aztec/src/state_vars/private_mutable/test.nr b/noir-projects/aztec-nr/aztec/src/state_vars/private_mutable/test.nr index fce592476393..3db22ca38e24 100644 --- a/noir-projects/aztec-nr/aztec/src/state_vars/private_mutable/test.nr +++ b/noir-projects/aztec-nr/aztec/src/state_vars/private_mutable/test.nr @@ -1,6 +1,6 @@ use dep::protocol_types::{address::AztecAddress, grumpkin_point::GrumpkinPoint}; use crate::{context::PrivateContext, state_vars::private_mutable::PrivateMutable}; -use crate::test::{mocks::mock_note::MockNote, helpers::context_builder::TestEnvironment}; +use crate::test::{mocks::mock_note::MockNote, helpers::test_environment::TestEnvironment}; use dep::std::{unsafe::zeroed, test::OracleMock}; global contract_address = AztecAddress::from_field(13); diff --git a/noir-projects/aztec-nr/aztec/src/state_vars/public_immutable/test.nr b/noir-projects/aztec-nr/aztec/src/state_vars/public_immutable/test.nr index a54640ecaa18..7614ffff1d10 100644 --- a/noir-projects/aztec-nr/aztec/src/state_vars/public_immutable/test.nr +++ b/noir-projects/aztec-nr/aztec/src/state_vars/public_immutable/test.nr @@ -1,6 +1,5 @@ use crate::{context::PublicContext, state_vars::public_immutable::PublicImmutable}; -use crate::test::{helpers::context_builder::TestEnvironment, mocks::mock_struct::MockStruct}; -use dep::std::test::OracleMock; +use crate::test::{helpers::test_environment::TestEnvironment, mocks::mock_struct::MockStruct}; use dep::protocol_types::traits::Serialize; global storage_slot = 7; diff --git a/noir-projects/aztec-nr/aztec/src/state_vars/shared_mutable/test.nr b/noir-projects/aztec-nr/aztec/src/state_vars/shared_mutable/test.nr index f81b9bc499ea..d16344201ce1 100644 --- a/noir-projects/aztec-nr/aztec/src/state_vars/shared_mutable/test.nr +++ b/noir-projects/aztec-nr/aztec/src/state_vars/shared_mutable/test.nr @@ -1,18 +1,9 @@ -use dep::std::{merkle::compute_merkle_root, test::OracleMock}; - use crate::{ - context::{PublicContext, PrivateContext}, - state_vars::shared_mutable::{ - shared_mutable::SharedMutable, scheduled_value_change::ScheduledValueChange, - scheduled_delay_change::ScheduledDelayChange -}, - test::helpers::context_builder::TestEnvironment, oracle::get_public_data_witness::PublicDataWitness + context::{PublicContext, PrivateContext}, state_vars::shared_mutable::shared_mutable::SharedMutable, + test::helpers::test_environment::TestEnvironment }; -use dep::protocol_types::{ - constants::{GENERATOR_INDEX__PUBLIC_LEAF_INDEX, PUBLIC_DATA_TREE_HEIGHT}, hash::pedersen_hash, - address::AztecAddress, public_data_tree_leaf_preimage::PublicDataTreeLeafPreimage -}; +use dep::protocol_types::address::AztecAddress; global new_value = 57; @@ -21,7 +12,7 @@ global post_delay = 15; global storage_slot = 57; -global TEST_INITIAL_DELAY: u32 = 3; +global TEST_INITIAL_DELAY: u32 = 30; fn setup() -> TestEnvironment { TestEnvironment::new() @@ -92,6 +83,71 @@ fn test_get_current_value_in_public_after_scheduled_change() { assert_eq(state_var.get_current_value_in_public(), new_value); } +#[test] +fn test_get_current_value_in_private_before_change() { + let mut env = setup(); + + let public_state_var = in_public(env); + public_state_var.schedule_value_change(new_value); + + let (_, block_of_change) = public_state_var.get_scheduled_value_in_public(); + + let schedule_block_number = env.block_number; + + let private_state_var = in_private(&mut env, schedule_block_number); + assert_eq(private_state_var.get_current_value_in_private(), 0); + assert_eq(private_state_var.context.max_block_number.unwrap(), block_of_change - 1); +} + +#[test] +fn test_get_current_value_in_private_immediately_before_change() { + let mut env = setup(); + + let public_state_var = in_public(env); + public_state_var.schedule_value_change(new_value); + + let (_, block_of_change) = public_state_var.get_scheduled_value_in_public(); + + let private_state_var = in_private(&mut env, block_of_change - 1); + + assert_eq(private_state_var.get_current_value_in_private(), 0); + assert_eq(private_state_var.context.max_block_number.unwrap(), block_of_change - 1); +} + +#[test] +fn test_get_current_value_in_private_at_change() { + let mut env = setup(); + + let public_state_var = in_public(env); + public_state_var.schedule_value_change(new_value); + + let (_, block_of_change) = public_state_var.get_scheduled_value_in_public(); + + let historical_block_number = block_of_change; + let private_state_var = in_private(&mut env, historical_block_number); + assert_eq(private_state_var.get_current_value_in_private(), new_value); + assert_eq( + private_state_var.context.max_block_number.unwrap(), historical_block_number + TEST_INITIAL_DELAY + ); +} + +#[test] +fn test_get_current_value_in_private_after_change() { + let mut env = setup(); + + let public_state_var = in_public(env); + public_state_var.schedule_value_change(new_value); + + let (_, block_of_change) = public_state_var.get_scheduled_value_in_public(); + + let historical_block_number = block_of_change + 10; + let private_state_var = in_private(&mut env, historical_block_number); + assert_eq(private_state_var.get_current_value_in_private(), new_value); + assert_eq( + private_state_var.context.max_block_number.unwrap(), historical_block_number + TEST_INITIAL_DELAY + ); +} + // #[test] // fn test_get_current_delay_in_public() { // let (state_var, block_number) = setup(); @@ -304,56 +360,4 @@ fn test_get_current_value_in_public_after_scheduled_change() { // new_delay, // block_number + post_delay - new_delay // ); -// } - -#[test] -fn test_get_current_value_in_private_before_change() { - let mut env = setup(); - - let public_state_var = in_public(env); - public_state_var.schedule_value_change(new_value); - - let schedule_block_number = env.block_number; - - let private_state_var = in_private(&mut env, schedule_block_number); - assert_eq(private_state_var.get_current_value_in_private(), 0); -} - -#[test] -fn test_get_current_value_in_private_immediately_before_change() { - let mut env = setup(); - - let state_var = in_public(env); - state_var.schedule_value_change(new_value); - - let (_, block_horizon) = state_var.get_scheduled_value_in_public(); - - let private_state_var = in_private(&mut env, block_horizon - 1); - assert_eq(private_state_var.get_current_value_in_private(), 0); -} - -#[test] -fn test_get_current_value_in_private_at_change() { - let mut env = setup(); - - let state_var = in_public(env); - state_var.schedule_value_change(new_value); - - let (_, block_horizon) = state_var.get_scheduled_value_in_public(); - - let private_state_var = in_private(&mut env, block_horizon); - assert_eq(private_state_var.get_current_value_in_private(), new_value); -} - -#[test] -fn test_get_current_value_in_private_after_change() { - let mut env = setup(); - - let state_var = in_public(env); - state_var.schedule_value_change(new_value); - - let (_, block_horizon) = state_var.get_scheduled_value_in_public(); - - let private_state_var = in_private(&mut env, block_horizon + 10); - assert_eq(private_state_var.get_current_value_in_private(), new_value); -} +// } \ No newline at end of file diff --git a/noir-projects/aztec-nr/aztec/src/test/helpers.nr b/noir-projects/aztec-nr/aztec/src/test/helpers.nr index 333197dd09bb..ae72bf389e7d 100644 --- a/noir-projects/aztec-nr/aztec/src/test/helpers.nr +++ b/noir-projects/aztec-nr/aztec/src/test/helpers.nr @@ -1,2 +1,2 @@ -mod context_builder; +mod test_environment; mod cheatcodes; diff --git a/noir-projects/aztec-nr/aztec/src/test/helpers/context_builder.nr b/noir-projects/aztec-nr/aztec/src/test/helpers/test_environment.nr similarity index 96% rename from noir-projects/aztec-nr/aztec/src/test/helpers/context_builder.nr rename to noir-projects/aztec-nr/aztec/src/test/helpers/test_environment.nr index 31ef8fe6c419..6eb8f766c208 100644 --- a/noir-projects/aztec-nr/aztec/src/test/helpers/context_builder.nr +++ b/noir-projects/aztec-nr/aztec/src/test/helpers/test_environment.nr @@ -43,7 +43,7 @@ impl TestEnvironment { fn private_at(&mut self, historical_block_number: u32) -> PrivateContext { if historical_block_number >= self.block_number { - self.advance_block_to(historical_block_number + 2); + self.advance_block_to(historical_block_number + 1); } let mut inputs = cheatcodes::get_private_context_inputs(historical_block_number); From fdd5cf1e7a3ffc42ccfd7cbdcbc67fb033c026f0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicol=C3=A1s=20Venturo?= Date: Fri, 7 Jun 2024 15:24:00 +0000 Subject: [PATCH 28/60] Add address fixes --- .../simulator/src/client/execution_note_cache.ts | 3 +++ yarn-project/txe/src/oracle/txe_oracle.ts | 5 +++++ yarn-project/txe/src/txe_service/txe_service.ts | 13 +++++++++---- 3 files changed, 17 insertions(+), 4 deletions(-) diff --git a/yarn-project/simulator/src/client/execution_note_cache.ts b/yarn-project/simulator/src/client/execution_note_cache.ts index 325e0b8f80b1..e74123138e69 100644 --- a/yarn-project/simulator/src/client/execution_note_cache.ts +++ b/yarn-project/simulator/src/client/execution_note_cache.ts @@ -54,6 +54,7 @@ export class ExecutionNoteCache { let nullifiedNoteHashCounter: number | undefined = undefined; // Find and remove the matching new note and log(s) if the emitted innerNoteHash is not empty. if (!innerNoteHash.equals(Fr.ZERO)) { + console.log('DELETING A NOTE'); const notes = this.newNotes.get(contractAddress.toBigInt()) ?? []; const noteIndexToRemove = notes.findIndex(n => n.note.innerNoteHash.equals(innerNoteHash)); if (noteIndexToRemove === -1) { @@ -62,6 +63,8 @@ export class ExecutionNoteCache { const note = notes.splice(noteIndexToRemove, 1)[0]; nullifiedNoteHashCounter = note.counter; this.newNotes.set(contractAddress.toBigInt(), notes); + } else { + console.log('NOT DELETING A NOTE'); } return nullifiedNoteHashCounter; diff --git a/yarn-project/txe/src/oracle/txe_oracle.ts b/yarn-project/txe/src/oracle/txe_oracle.ts index fae81f7375cf..83cf514fe645 100644 --- a/yarn-project/txe/src/oracle/txe_oracle.ts +++ b/yarn-project/txe/src/oracle/txe_oracle.ts @@ -48,6 +48,10 @@ export class TXE implements TypedOracle { this.noteCache = noteCache; } + setContractAddress(contractAddress: AztecAddress) { + this.contractAddress = contractAddress; + } + getRandomField() { return Fr.random(); } @@ -144,6 +148,7 @@ export class TXE implements TypedOracle { _status: NoteStatus, ) { // Nullified pending notes are already removed from the list. + console.log(`fecthing notes for ${this.contractAddress}`); const pendingNotes = this.noteCache.getNotes(this.contractAddress, storageSlot); // const pendingNullifiers = this.noteCache.getNullifiers(this.contractAddress); diff --git a/yarn-project/txe/src/txe_service/txe_service.ts b/yarn-project/txe/src/txe_service/txe_service.ts index 3aef9ab03019..9655b498b582 100644 --- a/yarn-project/txe/src/txe_service/txe_service.ts +++ b/yarn-project/txe/src/txe_service/txe_service.ts @@ -82,8 +82,10 @@ export class TXEService { return toForeignCallResult([]); } - setContractAddress(address = AztecAddress.random()) { - this.contractAddress = address; + setContractAddress(address: ForeignCallSingle) { + const typedAddress = AztecAddress.fromField(fromSingle(address)); + this.contractAddress = typedAddress; + (this.typedOracle as TXE).setContractAddress(typedAddress); return toForeignCallResult([]); } @@ -105,8 +107,10 @@ export class TXEService { async reset() { this.blockNumber = 0; + this.contractAddress = AztecAddress.random(); this.store = openTmpStore(true); this.trees = await MerkleTrees.new(this.store, this.logger); + this.typedOracle = new TXE( this.logger, this.trees, @@ -221,6 +225,7 @@ export class TXEService { } const contractAddress = noteDatas[0]?.contractAddress ?? Fr.ZERO; + console.log(`injected header: ${contractAddress}`); // Values indicates whether the note is settled or transient. const noteTypes = { @@ -257,7 +262,7 @@ export class TXEService { fromSingle(innerNoteHash), fromSingle(counter).toNumber(), ); - return toForeignCallResult([]); + return toForeignCallResult([toSingle(new Fr(0))]); } async notifyNullifiedNote( @@ -270,7 +275,7 @@ export class TXEService { fromSingle(innerNoteHash), fromSingle(counter).toNumber(), ); - return toForeignCallResult([]); + return toForeignCallResult([toSingle(new Fr(0))]); } async checkNullifierExists(innerNullifier: ForeignCallSingle) { From ff3fa7165d9bf7aed8a9057ea96d2a9d8d83965f Mon Sep 17 00:00:00 2001 From: thunkar Date: Fri, 7 Jun 2024 15:34:20 +0000 Subject: [PATCH 29/60] almost there --- .vscode/settings.json | 2 +- .../aztec/src/context/call_interfaces.nr | 135 ++++++++--- .../aztec-nr/aztec/src/keys/getters.nr | 2 +- .../aztec-nr/aztec/src/state_vars/storage.nr | 5 +- .../aztec-nr/aztec/src/test/helpers.nr | 1 + .../aztec/src/test/helpers/cheatcodes.nr | 39 +++- .../src/test/helpers/test_environment.nr | 75 +++++- .../aztec-nr/aztec/src/test/helpers/types.nr | 44 ++++ .../contracts/counter_contract/src/main.nr | 21 ++ .../crates/types/src/traits.nr | 8 +- noir/noir-repo/aztec_macros/src/lib.rs | 2 +- .../src/transforms/contract_interface.rs | 214 ++++++++++++------ .../aztec_macros/src/transforms/storage.rs | 16 +- .../src/monomorphization/mod.rs | 7 +- yarn-project/archiver/src/archiver/index.ts | 1 + .../src/contract-interface-gen/typescript.ts | 3 +- .../src/contract/contract_instance.ts | 1 - yarn-project/foundation/src/abi/abi.ts | 4 - yarn-project/txe/package.json | 2 + yarn-project/txe/src/oracle/txe_oracle.ts | 16 +- .../txe/src/txe_service/txe_service.ts | 142 +++++++++--- .../types/src/abi/contract_artifact.ts | 3 - yarn-project/yarn.lock | 2 + 23 files changed, 571 insertions(+), 174 deletions(-) create mode 100644 noir-projects/aztec-nr/aztec/src/test/helpers/types.nr diff --git a/.vscode/settings.json b/.vscode/settings.json index ab8056e8a7c3..6d67a9599fd9 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -167,5 +167,5 @@ "**/barretenberg/cpp/build*/**": true }, "cmake.sourceDirectory": "${workspaceFolder}/barretenberg/cpp", - "noir.nargoPath": "./noir/noir-repo/target/release/nargo" + "noir.nargoPath": "/mnt/user-data/grego/.nargo/bin/nargo" } diff --git a/noir-projects/aztec-nr/aztec/src/context/call_interfaces.nr b/noir-projects/aztec-nr/aztec/src/context/call_interfaces.nr index 69904595c964..d57a4eb3b3d7 100644 --- a/noir-projects/aztec-nr/aztec/src/context/call_interfaces.nr +++ b/noir-projects/aztec-nr/aztec/src/context/call_interfaces.nr @@ -1,20 +1,51 @@ -use dep::protocol_types::{abis::function_selector::FunctionSelector, address::AztecAddress, traits::Deserialize}; +use dep::protocol_types::{ + abis::{function_selector::FunctionSelector, private_circuit_public_inputs::PrivateCircuitPublicInputs}, + address::AztecAddress, traits::Deserialize +}; -use crate::context::private_context::PrivateContext; -use crate::context::public_context::PublicContext; -use crate::context::gas::GasOpts; -use crate::context::public_context::FunctionReturns; +use crate::context::{ + private_context::PrivateContext, public_context::PublicContext, gas::GasOpts, + public_context::FunctionReturns, inputs::{PrivateContextInputs, PublicContextInputs} +}; use crate::oracle::arguments; -struct PrivateCallInterface { +trait CallInterface { + fn get_args(self) -> [Field]; + fn get_original(self) -> fn[Env](T) -> P; + fn get_selector(self) -> FunctionSelector; + fn get_name(self) -> str; +} + +impl CallInterface for PrivateCallInterface { + fn get_args(self) -> [Field] { + self.args + } + + fn get_original(self) -> fn[Env](PrivateContextInputs) -> PrivateCircuitPublicInputs { + self.original + } + + fn get_selector(self) -> FunctionSelector { + self.selector + } + + fn get_name(self) -> str { + self.name + } +} + +struct PrivateCallInterface { target_contract: AztecAddress, selector: FunctionSelector, + name: str, args_hash: Field, + args: [Field], + original: fn[Env](PrivateContextInputs) -> PrivateCircuitPublicInputs } -impl PrivateCallInterface { - pub fn call(self, context: &mut PrivateContext) -> T where T: Deserialize { +impl PrivateCallInterface { + pub fn call(self, context: &mut PrivateContext) -> T where T: Deserialize { let returns = context.call_private_function_with_packed_args( self.target_contract, self.selector, @@ -26,24 +57,45 @@ impl PrivateCallInterface { unpacked } - pub fn view(self, context: &mut PrivateContext) -> T where T: Deserialize { + pub fn view(self, context: &mut PrivateContext) -> T where T: Deserialize { let returns = context.call_private_function_with_packed_args(self.target_contract, self.selector, self.args_hash, true, false); returns.unpack_into() } - pub fn delegate_call(self, context: &mut PrivateContext) -> T where T: Deserialize { + pub fn delegate_call(self, context: &mut PrivateContext) -> T where T: Deserialize { let returns = context.call_private_function_with_packed_args(self.target_contract, self.selector, self.args_hash, false, true); returns.unpack_into() } } -struct PrivateVoidCallInterface { +impl CallInterface for PrivateVoidCallInterface { + fn get_args(self) -> [Field] { + self.args + } + + fn get_original(self) -> fn[Env](PrivateContextInputs) -> PrivateCircuitPublicInputs { + self.original + } + + fn get_selector(self) -> FunctionSelector { + self.selector + } + + fn get_name(self) -> str { + self.name + } +} + +struct PrivateVoidCallInterface { target_contract: AztecAddress, selector: FunctionSelector, + name: str, args_hash: Field, + args: [Field], + original: fn[Env](PrivateContextInputs) -> PrivateCircuitPublicInputs } -impl PrivateVoidCallInterface { +impl PrivateVoidCallInterface { pub fn call(self, context: &mut PrivateContext) { context.call_private_function_with_packed_args( self.target_contract, @@ -63,55 +115,63 @@ impl PrivateVoidCallInterface { } } -struct PrivateStaticCallInterface { +struct PrivateStaticCallInterface { target_contract: AztecAddress, selector: FunctionSelector, + name: str, args_hash: Field, + args: [Field], + original: fn[Env](PrivateContextInputs) -> PrivateCircuitPublicInputs } -impl PrivateStaticCallInterface { - pub fn view(self, context: &mut PrivateContext) -> T where T: Deserialize { +impl PrivateStaticCallInterface { + pub fn view(self, context: &mut PrivateContext) -> T where T: Deserialize { let returns = context.call_private_function_with_packed_args(self.target_contract, self.selector, self.args_hash, true, false); returns.unpack_into() } } -struct PrivateStaticVoidCallInterface { +struct PrivateStaticVoidCallInterface { target_contract: AztecAddress, selector: FunctionSelector, + name: str, args_hash: Field, + args: [Field], + original: fn[Env](PrivateContextInputs) -> PrivateCircuitPublicInputs } -impl PrivateStaticVoidCallInterface { +impl PrivateStaticVoidCallInterface { pub fn view(self, context: &mut PrivateContext) { context.call_private_function_with_packed_args(self.target_contract, self.selector, self.args_hash, true, false).assert_empty(); } } -struct PublicCallInterface { +struct PublicCallInterface { target_contract: AztecAddress, selector: FunctionSelector, + name: str, args: [Field], gas_opts: GasOpts, + original: fn[Env](PublicContextInputs) -> T } -impl PublicCallInterface { +impl PublicCallInterface { pub fn with_gas(self: &mut Self, gas_opts: GasOpts) -> &mut Self { self.gas_opts = gas_opts; self } - pub fn call(self, context: &mut PublicContext) -> T where T: Deserialize { + pub fn call(self, context: &mut PublicContext) -> T where T: Deserialize { let returns = context.call_public_function(self.target_contract, self.selector, self.args, self.gas_opts); returns.deserialize_into() } - pub fn view(self, context: &mut PublicContext) -> T where T: Deserialize { + pub fn view(self, context: &mut PublicContext) -> T where T: Deserialize { let returns = context.static_call_public_function(self.target_contract, self.selector, self.args, self.gas_opts); returns.deserialize_into() } - pub fn delegate_call(self, context: &mut PublicContext) -> T where T: Deserialize { + pub fn delegate_call(self, context: &mut PublicContext) -> T where T: Deserialize { let returns = context.delegate_call_public_function(self.target_contract, self.selector, self.args); returns.deserialize_into() } @@ -153,30 +213,32 @@ impl PublicCallInterface { } } -struct PublicVoidCallInterface { +struct PublicVoidCallInterface { target_contract: AztecAddress, selector: FunctionSelector, + name: str, args: [Field], gas_opts: GasOpts, + original: fn[Env](PublicContextInputs) -> () } -impl PublicVoidCallInterface { +impl PublicVoidCallInterface { pub fn with_gas(self: &mut Self, gas_opts: GasOpts) -> &mut Self { self.gas_opts = gas_opts; self } - pub fn call(self, context: &mut PublicContext) { + pub fn call(self, context: &mut PublicContext) { let returns = context.call_public_function(self.target_contract, self.selector, self.args, self.gas_opts); returns.assert_empty() } - pub fn view(self, context: &mut PublicContext) { + pub fn view(self, context: &mut PublicContext) { let returns = context.static_call_public_function(self.target_contract, self.selector, self.args, self.gas_opts); returns.assert_empty() } - pub fn delegate_call(self, context: &mut PublicContext) { + pub fn delegate_call(self, context: &mut PublicContext) { let returns = context.delegate_call_public_function(self.target_contract, self.selector, self.args); returns.assert_empty() } @@ -218,22 +280,25 @@ impl PublicVoidCallInterface { } } -struct PublicStaticCallInterface { +struct PublicStaticCallInterface { target_contract: AztecAddress, selector: FunctionSelector, + name: str, args: [Field], gas_opts: GasOpts, + original: fn[Env](PublicContextInputs) -> T } -impl PublicStaticCallInterface { +impl PublicStaticCallInterface { pub fn with_gas(self: &mut Self, gas_opts: GasOpts) -> &mut Self { self.gas_opts = gas_opts; self } - pub fn view(self, context: &mut PublicContext) -> T where T: Deserialize { + pub fn view(self, context: &mut PublicContext) -> T where T: Deserialize { let returns = context.static_call_public_function(self.target_contract, self.selector, self.args, self.gas_opts); - returns.deserialize_into() + let unpacked: T = returns.deserialize_into(); + unpacked } pub fn enqueue_view(self, context: &mut PrivateContext) { @@ -249,20 +314,22 @@ impl PublicStaticCallInterface { } } -struct PublicStaticVoidCallInterface { +struct PublicStaticVoidCallInterface { target_contract: AztecAddress, selector: FunctionSelector, + name: str, args: [Field], gas_opts: GasOpts, + original: fn[Env](PublicContextInputs) -> () } -impl PublicStaticVoidCallInterface { +impl PublicStaticVoidCallInterface { pub fn with_gas(self: &mut Self, gas_opts: GasOpts) -> &mut Self { self.gas_opts = gas_opts; self } - pub fn view(self, context: &mut PublicContext) { + pub fn view(self, context: &mut PublicContext) { let returns = context.static_call_public_function(self.target_contract, self.selector, self.args, self.gas_opts); returns.assert_empty() } diff --git a/noir-projects/aztec-nr/aztec/src/keys/getters.nr b/noir-projects/aztec-nr/aztec/src/keys/getters.nr index 8e56174ac835..8a2c7c440fb2 100644 --- a/noir-projects/aztec-nr/aztec/src/keys/getters.nr +++ b/noir-projects/aztec-nr/aztec/src/keys/getters.nr @@ -95,7 +95,7 @@ fn fetch_and_constrain_keys(address: AztecAddress) -> PublicKeys { let computed_address = AztecAddress::compute(public_keys.hash(), partial_address); - assert(computed_address.eq(address)); + //assert(computed_address.eq(address)); public_keys } diff --git a/noir-projects/aztec-nr/aztec/src/state_vars/storage.nr b/noir-projects/aztec-nr/aztec/src/state_vars/storage.nr index 0f8cce2323cc..8ae9faf228c4 100644 --- a/noir-projects/aztec-nr/aztec/src/state_vars/storage.nr +++ b/noir-projects/aztec-nr/aztec/src/state_vars/storage.nr @@ -8,9 +8,8 @@ trait Storage where T: Serialize + Deserialize { // Struct representing an exportable storage variable in the contract // Every entry in the storage struct will be exported in the compilation artifact as a -// Storable entity, containing the storage slot and the type of the variable -struct Storable { +// Storable entity, containing the storage slot +struct Storable { slot: Field, - typ: str } diff --git a/noir-projects/aztec-nr/aztec/src/test/helpers.nr b/noir-projects/aztec-nr/aztec/src/test/helpers.nr index ae72bf389e7d..80045b39a642 100644 --- a/noir-projects/aztec-nr/aztec/src/test/helpers.nr +++ b/noir-projects/aztec-nr/aztec/src/test/helpers.nr @@ -1,2 +1,3 @@ mod test_environment; mod cheatcodes; +mod types; diff --git a/noir-projects/aztec-nr/aztec/src/test/helpers/cheatcodes.nr b/noir-projects/aztec-nr/aztec/src/test/helpers/cheatcodes.nr index b043543b1424..033e2823574d 100644 --- a/noir-projects/aztec-nr/aztec/src/test/helpers/cheatcodes.nr +++ b/noir-projects/aztec-nr/aztec/src/test/helpers/cheatcodes.nr @@ -1,5 +1,6 @@ use dep::protocol_types::address::AztecAddress; -use crate::context::inputs::PrivateContextInputs; +use crate::context::inputs::{PublicContextInputs, PrivateContextInputs}; +use crate::test::helpers::types::Deployer; unconstrained pub fn reset() { oracle_reset(); @@ -25,6 +26,30 @@ unconstrained pub fn get_private_context_inputs(historical_block_number: u32) -> oracle_get_private_context_inputs(historical_block_number) } +unconstrained pub fn get_public_context_inputs() -> PublicContextInputs { + oracle_get_public_context_inputs() +} + +unconstrained pub fn deploy_inner( + path: str, + initializer: str, + args: [Field] +) -> AztecAddress { + oracle_deploy(path, initializer, args) +} + +pub fn deploy(path: str) -> Deployer { + Deployer { path } +} + +unconstrained pub fn direct_storage_write( + contract_address: AztecAddress, + storage_slot: Field, + fields: [Field; N] +) { + let _hash = direct_storage_write_oracle(contract_address, storage_slot, fields); +} + #[oracle(reset)] fn oracle_reset() {} @@ -43,3 +68,15 @@ fn oracle_time_travel(blocks: u32) {} #[oracle(getPrivateContextInputs)] fn oracle_get_private_context_inputs(historical_block_number: u32) -> PrivateContextInputs {} +#[oracle(getPublicContextInputs)] +fn oracle_get_public_context_inputs() -> PublicContextInputs {} + +#[oracle(deploy)] +fn oracle_deploy(path: str, initializer: str, args: [Field]) -> AztecAddress {} + +#[oracle(directStorageWrite)] +fn direct_storage_write_oracle( + _contract_address: AztecAddress, + _storage_slot: Field, + _values: [Field; N] +) -> [Field; N] {} diff --git a/noir-projects/aztec-nr/aztec/src/test/helpers/test_environment.nr b/noir-projects/aztec-nr/aztec/src/test/helpers/test_environment.nr index 6eb8f766c208..865ff9482860 100644 --- a/noir-projects/aztec-nr/aztec/src/test/helpers/test_environment.nr +++ b/noir-projects/aztec-nr/aztec/src/test/helpers/test_environment.nr @@ -1,17 +1,30 @@ -use dep::protocol_types::address::AztecAddress; -use crate::context::{PrivateContext, PublicContext}; +use dep::protocol_types::{ + abis::function_selector::FunctionSelector, address::{AztecAddress, PartialAddress}, + storage::map::derive_storage_slot_in_map, constants::CANONICAL_KEY_REGISTRY_ADDRESS, + grumpkin_point::GrumpkinPoint +}; + +use crate::context::{PrivateContext, PublicContext, PrivateVoidCallInterface}; use crate::test::helpers::cheatcodes; +use crate::keys::{public_keys::PublicKeys, constants::{NULLIFIER_INDEX, INCOMING_INDEX, OUTGOING_INDEX, TAGGING_INDEX}}; struct TestEnvironment { block_number: u32, contract_address: Option, + args_hash: Option, + function_selector: Option } impl TestEnvironment { fn new() -> Self { cheatcodes::reset(); - Self { block_number: 1, contract_address: Option::none() } + Self { + block_number: 1, + contract_address: Option::none(), + args_hash: Option::none(), + function_selector: Option::none() + } } fn contract_address(&mut self, contract_address: AztecAddress) -> Self { @@ -19,6 +32,16 @@ impl TestEnvironment { *self } + fn function_selector(&mut self, function_selector: FunctionSelector) -> Self { + self.function_selector = Option::some(function_selector); + *self + } + + fn args_hash(&mut self, args_hash: Field) -> Self { + self.args_hash = Option::some(args_hash); + *self + } + fn advance_block_to(&mut self, block_number: u32) { let difference = block_number - self.block_number; self.advance_block_by(difference); @@ -47,12 +70,56 @@ impl TestEnvironment { } let mut inputs = cheatcodes::get_private_context_inputs(historical_block_number); - let args_hash = 0; if (self.contract_address.is_some()) { inputs.call_context.storage_contract_address = self.contract_address.unwrap_unchecked(); } + if (self.function_selector.is_some()) { + inputs.call_context.function_selector = self.function_selector.unwrap_unchecked(); + } + + let mut args_hash = 0; + + if (self.args_hash.is_some()) { + args_hash = self.args_hash.unwrap_unchecked(); + } + PrivateContext::new(inputs, args_hash) } + + fn store_master_key(self, key_index: Field, address: AztecAddress) -> GrumpkinPoint { + let x_coordinate_map_slot = key_index * 2 + 1; + let y_coordinate_map_slot = x_coordinate_map_slot + 1; + let x_coordinate_derived_slot = derive_storage_slot_in_map(x_coordinate_map_slot, address); + let y_coordinate_derived_slot = derive_storage_slot_in_map(y_coordinate_map_slot, address); + + let canonical_registry_address = AztecAddress::from_field(CANONICAL_KEY_REGISTRY_ADDRESS); + + let fake_key = GrumpkinPoint { x: 1, y: 2 }; + + cheatcodes::direct_storage_write( + canonical_registry_address, + x_coordinate_derived_slot, + [fake_key.x] + ); + + cheatcodes::direct_storage_write( + canonical_registry_address, + y_coordinate_derived_slot, + [fake_key.y] + ); + fake_key + } + + fn get_address_with_keys(self, address: AztecAddress) -> AztecAddress { + let keys = PublicKeys { + npk_m: self.store_master_key(NULLIFIER_INDEX, address), + ivpk_m: self.store_master_key(INCOMING_INDEX, address), + ovpk_m: self.store_master_key(OUTGOING_INDEX, address), + tpk_m: self.store_master_key(TAGGING_INDEX, address) + }; + + AztecAddress::compute(keys.hash(), PartialAddress::from_field(1)) + } } diff --git a/noir-projects/aztec-nr/aztec/src/test/helpers/types.nr b/noir-projects/aztec-nr/aztec/src/test/helpers/types.nr new file mode 100644 index 000000000000..d443e0072245 --- /dev/null +++ b/noir-projects/aztec-nr/aztec/src/test/helpers/types.nr @@ -0,0 +1,44 @@ +use dep::protocol_types::address::AztecAddress; + +use crate::context::inputs::{PublicContextInputs, PrivateContextInputs}; +use crate::context::call_interfaces::CallInterface; +use crate::test::helpers::cheatcodes::{get_private_context_inputs, get_public_context_inputs, deploy_inner}; + +struct Deployer { + path: str, + } + +impl Deployer { + pub fn with_private_initializer( + self, + call_interface: C, + historical_block_number: u32 + ) -> AztecAddress where C: CallInterface { + let address = deploy_inner( + self.path, + call_interface.get_name(), + call_interface.get_args() + ); + let original_fn = call_interface.get_original(); + let mut inputs = get_private_context_inputs(historical_block_number); + inputs.call_context.storage_contract_address = address; + inputs.call_context.function_selector = call_interface.get_selector(); + let _result = original_fn(inputs); + address + } + + pub fn with_public_initializer( + self, + call_interface: C + ) -> AztecAddress where C: CallInterface { + let address = deploy_inner( + self.path, + call_interface.get_name(), + call_interface.get_args() + ); + let original_fn = call_interface.get_original(); + let mut inputs = get_public_context_inputs(); + let _result = original_fn(inputs); + address + } +} diff --git a/noir-projects/noir-contracts/contracts/counter_contract/src/main.nr b/noir-projects/noir-contracts/contracts/counter_contract/src/main.nr index cf2a235e7987..dcd8a272fc6f 100644 --- a/noir-projects/noir-contracts/contracts/counter_contract/src/main.nr +++ b/noir-projects/noir-contracts/contracts/counter_contract/src/main.nr @@ -37,4 +37,25 @@ contract Counter { balance_utils::get_balance(counters.at(owner).set) } // docs:end:get_counter + + use dep::aztec::test::{helpers::{cheatcodes, test_environment::TestEnvironment}}; + use dep::aztec::protocol_types::storage::map::derive_storage_slot_in_map; + use dep::aztec::oracle::{storage::{storage_read, storage_write}}; + use dep::aztec::keys::PublicKeys; + use dep::aztec::protocol_types::grumpkin_point::GrumpkinPoint; + use dep::aztec::protocol_types::address::PartialAddress; + + #[test] + fn test_initialize() { + let mut env = TestEnvironment::new(); + let owner = env.get_address_with_keys(AztecAddress::from_field(13)); + let outgoing_viewer = env.get_address_with_keys(AztecAddress::from_field(14)); + cheatcodes::advance_blocks(30); + let initializer = Counter::interface().initialize(5, owner, outgoing_viewer); + let _contract_address = cheatcodes::deploy("@aztec/noir-contracts.js/Counter").with_private_initializer(initializer, 30); + let counter_slot = Counter::storage().counters.slot; + let owner_slot = derive_storage_slot_in_map(counter_slot, owner); + let stored: [Field; 1] = storage_read(owner_slot); + assert(stored[0] == 5); + } } diff --git a/noir-projects/noir-protocol-circuits/crates/types/src/traits.nr b/noir-projects/noir-protocol-circuits/crates/types/src/traits.nr index e4e2b1825ec8..2a92992cc4f8 100644 --- a/noir-projects/noir-protocol-circuits/crates/types/src/traits.nr +++ b/noir-projects/noir-protocol-circuits/crates/types/src/traits.nr @@ -105,4 +105,10 @@ impl Serialize for str { trait Deserialize { fn deserialize(fields: [Field; N]) -> Self; } -// docs:end:deserialize \ No newline at end of file +// docs:end:deserialize + +impl Deserialize for [Field; N] { + fn deserialize(fields: [Field; N]) -> Self { + fields + } +} diff --git a/noir/noir-repo/aztec_macros/src/lib.rs b/noir/noir-repo/aztec_macros/src/lib.rs index 2daf86dc6432..217f86c4d9a9 100644 --- a/noir/noir-repo/aztec_macros/src/lib.rs +++ b/noir/noir-repo/aztec_macros/src/lib.rs @@ -160,7 +160,7 @@ fn transform_module( // Apply transformations to the function based on collected attributes if is_private || is_public { let fn_type = if is_private { "Private" } else { "Public" }; - let stub_src = stub_function(fn_type, func, is_static); + let stub_src = stub_function(fn_type, func, is_static, is_initializer); stubs.push((stub_src, Location { file: *file_id, span: func.name_ident().span() })); export_fn_abi(&mut module.types, func)?; diff --git a/noir/noir-repo/aztec_macros/src/transforms/contract_interface.rs b/noir/noir-repo/aztec_macros/src/transforms/contract_interface.rs index 90f9ce6164a7..c63538e36011 100644 --- a/noir/noir-repo/aztec_macros/src/transforms/contract_interface.rs +++ b/noir/noir-repo/aztec_macros/src/transforms/contract_interface.rs @@ -40,7 +40,12 @@ use crate::utils::{ // } // // The selector placeholder has to be replaced with the actual function signature after type checking in the next macro pass -pub fn stub_function(aztec_visibility: &str, func: &NoirFunction, is_static_call: bool) -> String { +pub fn stub_function( + aztec_visibility: &str, + func: &NoirFunction, + is_static_call: bool, + is_initializer: bool, +) -> (String, bool) { let fn_name = func.name().to_string(); let fn_parameters = func .parameters() @@ -61,11 +66,7 @@ pub fn stub_function(aztec_visibility: &str, func: &NoirFunction, is_static_call let parameters = func.parameters(); let is_void = if matches!(fn_return_type.typ, UnresolvedTypeData::Unit) { "Void" } else { "" }; let is_static = if is_static_call { "Static" } else { "" }; - let return_type_hint = if is_void == "Void" { - "".to_string() - } else { - format!("<{}>", fn_return_type.typ.to_string().replace("plain::", "")) - }; + let return_type_hint = fn_return_type.typ.to_string().replace("plain::", ""); let call_args = parameters .iter() .map(|arg| { @@ -74,21 +75,64 @@ pub fn stub_function(aztec_visibility: &str, func: &NoirFunction, is_static_call UnresolvedTypeData::Array(_, typ) => { format!( "let hash_{0} = {0}.map(|x: {1}| x.serialize()); - for i in 0..{0}.len() {{ - args_acc = args_acc.append(hash_{0}[i].as_slice()); - }}\n", + for i in 0..{0}.len() {{ + args_acc = args_acc.append(hash_{0}[i].as_slice()); + }}\n", param_name, typ.typ.to_string().replace("plain::", "") ) } - _ => { + UnresolvedTypeData::Named(_, _, _) | UnresolvedTypeData::String(_) => { format!("args_acc = args_acc.append({}.serialize().as_slice());\n", param_name) } + _ => { + format!("args_acc = args_acc.append(&[{}.to_field()]);\n", param_name) + } } }) .collect::>() .join(""); - if aztec_visibility != "Public" { + + let param_types = if parameters.len() > 0 { + parameters + .iter() + .map(|param| param.pattern.name_ident().0.contents.clone()) + .collect::>() + .join(", ") + } else { + "".to_string() + }; + + let original = format!( + "| inputs: dep::aztec::context::inputs::{}ContextInputs | -> {} {{ + let _ = failing_env_workaround; + {}(inputs{}) + }}", + aztec_visibility, + if aztec_visibility == "Private" { + "dep::aztec::protocol_types::abis::private_circuit_public_inputs::PrivateCircuitPublicInputs".to_string() + } else { + return_type_hint.clone() + }, + fn_name, + if param_types.is_empty() { "".to_string() } else { format!(" ,{} ", param_types) } + ); + let arg_types = format!( + "(Field, {})", + parameters + .iter() + .map(|param| param.typ.typ.to_string().replace("plain::", "")) + .collect::>() + .join(",") + ); + + let generics = if is_void == "Void" { + format!("{}>", arg_types) + } else { + format!("{}, {}>", return_type_hint, arg_types) + }; + + let fn_body = if aztec_visibility != "Public" { let args_hash = if !parameters.is_empty() { format!( "let mut args_acc: [Field] = &[]; @@ -101,20 +145,19 @@ pub fn stub_function(aztec_visibility: &str, func: &NoirFunction, is_static_call "let args_hash = 0;".to_string() }; - let fn_body = format!( - "{} - dep::aztec::context::{}{}{}CallInterface {{ - target_contract: self.target_contract, - selector: {}, - args_hash, - }}", - args_hash, aztec_visibility, is_static, is_void, fn_selector, - ); format!( - "pub fn {}(self, {}) -> dep::aztec::context::{}{}{}CallInterface{} {{ - {} - }}", - fn_name, fn_parameters, aztec_visibility, is_static, is_void, return_type_hint, fn_body + "{} + let failing_env_workaround = 0; + let selector = {}; + dep::aztec::context::{}{}{}CallInterface {{ + target_contract: self.target_contract, + selector, + name: \"{}\", + args_hash, + args: args_acc, + original: {} + }}", + args_hash, fn_selector, aztec_visibility, is_static, is_void, fn_name, original ) } else { let args = format!( @@ -123,23 +166,37 @@ pub fn stub_function(aztec_visibility: &str, func: &NoirFunction, is_static_call ", call_args ); - let fn_body = format!( + format!( "{} - dep::aztec::context::Public{}{}CallInterface {{ + let failing_env_workaround = 0; + let selector = {}; + dep::aztec::context::{}{}{}CallInterface {{ target_contract: self.target_contract, - selector: {}, + selector, + name: \"{}\", args: args_acc, gas_opts: dep::aztec::context::gas::GasOpts::default(), + original: {} }}", - args, is_static, is_void, fn_selector, - ); - format!( - "pub fn {}(self, {}) -> dep::aztec::context::Public{}{}CallInterface{} {{ - {} - }}", - fn_name, fn_parameters, is_static, is_void, return_type_hint, fn_body + args, fn_selector, aztec_visibility, is_static, is_void, fn_name, original ) - } + }; + ( + format!( + "pub fn {}(self, {}) -> dep::aztec::context::{}{}{}CallInterface<{},{} {{ + {} + }}", + fn_name, + fn_parameters, + aztec_visibility, + is_static, + is_void, + fn_name.len(), + generics, + fn_body + ), + is_initializer, + ) } // Generates the contract interface as a struct with an `at` function that holds the stubbed functions and provides @@ -148,7 +205,7 @@ pub fn stub_function(aztec_visibility: &str, func: &NoirFunction, is_static_call pub fn generate_contract_interface( module: &mut SortedModule, module_name: &str, - stubs: &[(String, Location)], + stubs: &[((String, bool), Location)], ) -> Result<(), AztecMacroError> { let contract_interface = format!( " @@ -164,6 +221,14 @@ pub fn generate_contract_interface( ) -> Self {{ Self {{ target_contract }} }} + + pub fn interface() -> Self {{ + Self {{ target_contract: dep::aztec::protocol_types::address::AztecAddress::zero() }} + }} + + pub fn storage() -> StorageLayout {{ + STORAGE_LAYOUT + }} }} #[contract_library_method] @@ -172,9 +237,19 @@ pub fn generate_contract_interface( ) -> {0} {{ {0} {{ target_contract }} }} + + #[contract_library_method] + pub fn interface() -> {0} {{ + {0} {{ target_contract: dep::aztec::protocol_types::address::AztecAddress::zero() }} + }} + + #[contract_library_method] + pub fn storage() -> StorageLayout {{ + STORAGE_LAYOUT + }} ", module_name, - stubs.iter().map(|(src, _)| src.to_owned()).collect::>().join("\n"), + stubs.iter().map(|((src, _), _)| src.to_owned()).collect::>().join("\n"), ); let (contract_interface_ast, errors) = parse_program(&contract_interface); @@ -191,7 +266,7 @@ pub fn generate_contract_interface( .iter() .enumerate() .map(|(i, (method, orig_span))| { - if method.name() == "at" { + if method.name() == "at" || method.name() == "interface" || method.name() == "storage" { (method.clone(), *orig_span) } else { let (_, new_location) = stubs[i]; @@ -205,7 +280,9 @@ pub fn generate_contract_interface( module.types.push(contract_interface_ast.types.pop().unwrap()); module.impls.push(impl_with_locations); - module.functions.push(contract_interface_ast.functions.pop().unwrap()); + for function in contract_interface_ast.functions { + module.functions.push(function); + } Ok(()) } @@ -244,7 +321,7 @@ pub fn update_fn_signatures_in_contract_interface( let name = context.def_interner.function_name(func_id); let fn_parameters = &context.def_interner.function_meta(func_id).parameters.clone(); - if name == "at" { + if name == "at" || name == "interface" || name == "storage" { continue; } @@ -257,42 +334,29 @@ pub fn update_fn_signatures_in_contract_interface( .collect::>(), ); let hir_func = context.def_interner.function(func_id).block(&context.def_interner); - let call_interface_constructor_statement = context.def_interner.statement( - hir_func - .statements() - .last() - .ok_or((AztecMacroError::AztecDepNotFound, file_id))?, + + let function_selector_statement = context.def_interner.statement( + hir_func.statements().get(hir_func.statements().len() - 2).ok_or(( + AztecMacroError::CouldNotGenerateContractInterface { + secondary_message: Some( + "Function signature statement not found, invalid body length" + .to_string(), + ), + }, + file_id, + ))?, ); - let call_interface_constructor_expression = - match call_interface_constructor_statement { - HirStatement::Expression(expression_id) => { - match context.def_interner.expression(&expression_id) { - HirExpression::Constructor(hir_constructor_expression) => { - Ok(hir_constructor_expression) - } - _ => Err(( - AztecMacroError::CouldNotGenerateContractInterface { - secondary_message: Some( - "CallInterface constructor statement must be a constructor expression" - .to_string(), - ), - }, - file_id, - )), - } - } - _ => Err(( - AztecMacroError::CouldNotGenerateContractInterface { - secondary_message: Some( - "CallInterface constructor statement must be an expression" - .to_string(), - ), - }, - file_id, - )), - }?; - let (_, function_selector_expression_id) = - call_interface_constructor_expression.fields[1]; + let function_selector_expression_id = match function_selector_statement { + HirStatement::Let(let_statement) => Ok(let_statement.expression), + _ => Err(( + AztecMacroError::CouldNotGenerateContractInterface { + secondary_message: Some( + "Function selector statement must be an expression".to_string(), + ), + }, + file_id, + )), + }?; let function_selector_expression = context.def_interner.expression(&function_selector_expression_id); diff --git a/noir/noir-repo/aztec_macros/src/transforms/storage.rs b/noir/noir-repo/aztec_macros/src/transforms/storage.rs index bd9fff3c3d3f..99f1f76fb062 100644 --- a/noir/noir-repo/aztec_macros/src/transforms/storage.rs +++ b/noir/noir-repo/aztec_macros/src/transforms/storage.rs @@ -504,23 +504,18 @@ pub fn generate_storage_layout( .find(|r#struct| r#struct.name.0.contents == *storage_struct_name) .unwrap(); - let mut generic_args = vec![]; let mut storable_fields = vec![]; let mut storable_fields_impl = vec![]; - definition.fields.iter().enumerate().for_each(|(index, (field_ident, field_type))| { - storable_fields.push(format!("{}: dep::aztec::prelude::Storable", field_ident, index)); - generic_args.push(format!("N{}", index)); - storable_fields_impl.push(format!( - "{}: dep::aztec::prelude::Storable {{ slot: 0, typ: \"{}\" }}", - field_ident, - field_type.to_string().replace("plain::", "") - )); + definition.fields.iter().for_each(|(field_ident, _)| { + storable_fields.push(format!("{}: dep::aztec::prelude::Storable", field_ident)); + storable_fields_impl + .push(format!("{}: dep::aztec::prelude::Storable {{ slot: 0 }}", field_ident,)); }); let storage_fields_source = format!( " - struct StorageLayout<{}> {{ + struct StorageLayout {{ {} }} @@ -529,7 +524,6 @@ pub fn generate_storage_layout( {} }}; ", - generic_args.join(", "), storable_fields.join(",\n"), storable_fields_impl.join(",\n") ); diff --git a/noir/noir-repo/compiler/noirc_frontend/src/monomorphization/mod.rs b/noir/noir-repo/compiler/noirc_frontend/src/monomorphization/mod.rs index 2e74eb87e60d..c7dbd526d48e 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/monomorphization/mod.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/monomorphization/mod.rs @@ -246,7 +246,12 @@ impl<'interner> Monomorphizer<'interner> { } } FunctionKind::Recursive => { - unreachable!("Only main can be specified as recursive, which should already be checked"); + // let func = self.interner.function_meta(&id); + // println!("{:#?}", func.name); + let id = + self.queue_function(id, expr_id, typ, turbofish_generics, trait_method); + Definition::Function(id) + //unreachable!("wtf"); } } } diff --git a/yarn-project/archiver/src/archiver/index.ts b/yarn-project/archiver/src/archiver/index.ts index a7294537624a..81aa8727e172 100644 --- a/yarn-project/archiver/src/archiver/index.ts +++ b/yarn-project/archiver/src/archiver/index.ts @@ -3,3 +3,4 @@ export * from './config.js'; export { MemoryArchiverStore } from './memory_archiver_store/memory_archiver_store.js'; export { ArchiverDataStore } from './archiver_store.js'; export { KVArchiverDataStore } from './kv_archiver_store/kv_archiver_store.js'; +export { ContractInstanceStore } from './kv_archiver_store/contract_instance_store.js'; diff --git a/yarn-project/builder/src/contract-interface-gen/typescript.ts b/yarn-project/builder/src/contract-interface-gen/typescript.ts index 4c2911fba97b..e4519b9e0af2 100644 --- a/yarn-project/builder/src/contract-interface-gen/typescript.ts +++ b/yarn-project/builder/src/contract-interface-gen/typescript.ts @@ -194,10 +194,9 @@ function generateStorageLayoutGetter(input: ContractArtifact) { const storageFieldsUnionType = entries.map(([name]) => `'${name}'`).join(' | '); const layout = entries .map( - ([name, { slot, typ }]) => + ([name, { slot }]) => `${name}: { slot: new Fr(${slot.toBigInt()}n), - typ: "${typ}", }`, ) .join(',\n'); diff --git a/yarn-project/circuits.js/src/contract/contract_instance.ts b/yarn-project/circuits.js/src/contract/contract_instance.ts index 1d45791cdab0..4c047461664d 100644 --- a/yarn-project/circuits.js/src/contract/contract_instance.ts +++ b/yarn-project/circuits.js/src/contract/contract_instance.ts @@ -27,7 +27,6 @@ export 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 initializationHash = computeInitializationHash(constructorArtifact, args); diff --git a/yarn-project/foundation/src/abi/abi.ts b/yarn-project/foundation/src/abi/abi.ts index 200e7b0088c9..2b6080866042 100644 --- a/yarn-project/foundation/src/abi/abi.ts +++ b/yarn-project/foundation/src/abi/abi.ts @@ -290,10 +290,6 @@ export type FieldLayout = { * Slot in which the field is stored. */ slot: Fr; - /** - * Type being stored at the slot (e.g., 'Map>') - */ - typ: string; }; /** diff --git a/yarn-project/txe/package.json b/yarn-project/txe/package.json index 19aa02bf07c5..fc5a4bd87a80 100644 --- a/yarn-project/txe/package.json +++ b/yarn-project/txe/package.json @@ -48,6 +48,8 @@ ] }, "dependencies": { + "@aztec/archiver": "workspace:^", + "@aztec/aztec.js": "workspace:^", "@aztec/circuit-types": "workspace:^", "@aztec/circuits.js": "workspace:^", "@aztec/foundation": "workspace:^", diff --git a/yarn-project/txe/src/oracle/txe_oracle.ts b/yarn-project/txe/src/oracle/txe_oracle.ts index fae81f7375cf..cb9245455682 100644 --- a/yarn-project/txe/src/oracle/txe_oracle.ts +++ b/yarn-project/txe/src/oracle/txe_oracle.ts @@ -1,3 +1,4 @@ +import { type ContractInstanceStore } from '@aztec/archiver'; import { MerkleTreeId, Note, @@ -8,7 +9,7 @@ import { type UnencryptedL2Log, } from '@aztec/circuit-types'; import { - type CompleteAddress, + CompleteAddress, type Header, type KeyValidationRequest, NULLIFIER_SUBTREE_HEIGHT, @@ -42,6 +43,7 @@ export class TXE implements TypedOracle { private trees: MerkleTrees, private packedValuesCache: PackedValuesCache, private noteCache: ExecutionNoteCache, + private contractInstanceStore: ContractInstanceStore, private contractAddress: AztecAddress, ) { this.packedValuesCache = packedValuesCache; @@ -68,8 +70,12 @@ export class TXE implements TypedOracle { throw new Error('Method not implemented.'); } - getContractInstance(_address: AztecAddress): Promise { - throw new Error('Method not implemented.'); + getContractInstance(address: AztecAddress): Promise { + const contractInstance = this.contractInstanceStore.getContractInstance(address); + if (!contractInstance) { + throw new Error(`Contract instance not found for address ${address}`); + } + return Promise.resolve(contractInstance); } getMembershipWitness(_blockNumber: number, _treeId: MerkleTreeId, _leafValue: Fr): Promise { @@ -115,8 +121,8 @@ export class TXE implements TypedOracle { throw new Error('Method not implemented.'); } - getCompleteAddress(_account: AztecAddress): Promise { - throw new Error('Method not implemented.'); + getCompleteAddress(account: AztecAddress): Promise { + return Promise.resolve(CompleteAddress.fromSecretKeyAndPartialAddress(Fr.ZERO, account)); } getAuthWitness(_messageHash: Fr): Promise { diff --git a/yarn-project/txe/src/txe_service/txe_service.ts b/yarn-project/txe/src/txe_service/txe_service.ts index 3aef9ab03019..1f0d9f45a942 100644 --- a/yarn-project/txe/src/txe_service/txe_service.ts +++ b/yarn-project/txe/src/txe_service/txe_service.ts @@ -1,5 +1,14 @@ -import { L2Block, MerkleTreeId } from '@aztec/circuit-types'; -import { Fr, Header, PrivateContextInputs } from '@aztec/circuits.js'; +import { ContractInstanceStore } from '@aztec/archiver'; +import { L2Block, MerkleTreeId, PublicDataWrite } from '@aztec/circuit-types'; +import { + Fr, + Header, + PUBLIC_DATA_SUBTREE_HEIGHT, + PrivateContextInputs, + PublicDataTreeLeaf, + getContractInstanceFromDeployParams, +} from '@aztec/circuits.js'; +import { computePublicDataTreeLeafSlot } from '@aztec/circuits.js/hash'; import { AztecAddress } from '@aztec/foundation/aztec-address'; import { type Logger } from '@aztec/foundation/log'; import { type AztecKVStore } from '@aztec/kv-store'; @@ -26,6 +35,7 @@ export class TXEService { private typedOracle: TypedOracle, private store: AztecKVStore, private trees: MerkleTrees, + private contractInstanceStore: ContractInstanceStore, private contractAddress: AztecAddress, ) {} @@ -34,13 +44,16 @@ export class TXEService { const trees = await MerkleTrees.new(store, logger); const packedValuesCache = new PackedValuesCache(); const noteCache = new ExecutionNoteCache(); + const contractInstanceStore = new ContractInstanceStore(store); logger.info(`TXE service initialized`); - const txe = new TXE(logger, trees, packedValuesCache, noteCache, contractAddress); - const service = new TXEService(logger, txe, store, trees, contractAddress); + const txe = new TXE(logger, trees, packedValuesCache, noteCache, contractInstanceStore, contractAddress); + const service = new TXEService(logger, txe, store, trees, contractInstanceStore, contractAddress); await service.timeTravel(toSingle(new Fr(1n))); return service; } + // Cheatcodes + async getPrivateContextInputs(blockNumber: ForeignCallSingle) { const inputs = PrivateContextInputs.empty(); const stateReference = await this.trees.getStateReference(true); @@ -51,13 +64,10 @@ export class TXEService { return toForeignCallResult(inputs.toFields().map(toSingle)); } - timeTravel(blocks: ForeignCallSingle) { - return this.#timeTravelInner(fromSingle(blocks).toNumber()); - } - - async #timeTravelInner(blocks: number) { - this.logger.info(`time traveling ${blocks} blocks`); - for (let i = 0; i < blocks; i++) { + async timeTravel(blocks: ForeignCallSingle) { + const nBlocks = fromSingle(blocks).toNumber(); + this.logger.info(`time traveling ${nBlocks} blocks`); + for (let i = 0; i < nBlocks; i++) { const header = Header.empty(); const l2Block = L2Block.empty(); header.state = await this.trees.getStateReference(true); @@ -82,11 +92,86 @@ export class TXEService { return toForeignCallResult([]); } + async reset() { + this.blockNumber = 0; + this.store = openTmpStore(true); + this.trees = await MerkleTrees.new(this.store, this.logger); + this.contractInstanceStore = new ContractInstanceStore(this.store); + this.typedOracle = new TXE( + this.logger, + this.trees, + new PackedValuesCache(), + new ExecutionNoteCache(), + this.contractInstanceStore, + this.contractAddress, + ); + await this.timeTravel(toSingle(new Fr(1))); + return toForeignCallResult([]); + } + setContractAddress(address = AztecAddress.random()) { this.contractAddress = address; return toForeignCallResult([]); } + async deploy( + path: ForeignCallArray, + initializer: ForeignCallArray, + _length: ForeignCallSingle, + args: ForeignCallArray, + ) { + const pathStr = fromArray(path) + .map(char => String.fromCharCode(char.toNumber())) + .join(''); + const initializerStr = fromArray(initializer) + .map(char => String.fromCharCode(char.toNumber())) + .join(''); + const decodedArgs = fromArray(args); + this.logger.debug(`Deploy ${pathStr} with ${initializerStr} and ${decodedArgs}`); + const contractModule = await import(pathStr); + // Hacky way of getting the class, the name of the Artifact is always longer + const contractClass = contractModule[Object.keys(contractModule).sort((a, b) => a.length - b.length)[0]]; + const instance = getContractInstanceFromDeployParams(contractClass.artifact, { + constructorArgs: decodedArgs, + salt: Fr.ONE, + publicKeysHash: Fr.ZERO, + constructorArtifact: initializerStr, + deployer: AztecAddress.ZERO, + }); + this.logger.debug(`Deployed ${contractClass.artifact.name} at ${instance.address}`); + await this.contractInstanceStore.addContractInstance(instance); + return toForeignCallResult([toSingle(instance.address)]); + } + + async directStorageWrite( + contractAddress: ForeignCallSingle, + startStorageSlot: ForeignCallSingle, + values: ForeignCallArray, + ) { + const startStorageSlotFr = fromSingle(startStorageSlot); + const valuesFr = fromArray(values); + const contractAddressFr = fromSingle(contractAddress); + const db = this.trees.asLatest(); + + 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 PublicDataWrite(computePublicDataTreeLeafSlot(contractAddressFr, storageSlot), value); + }); + await db.batchInsert( + MerkleTreeId.PUBLIC_DATA_TREE, + publicDataWrites.map(write => new PublicDataTreeLeaf(write.leafIndex, write.newValue).toBuffer()), + PUBLIC_DATA_SUBTREE_HEIGHT, + ); + return toForeignCallResult([toArray(publicDataWrites.map(write => write.newValue))]); + } + + // PXE oracles + + getRandomField() { + return toForeignCallResult([toSingle(this.typedOracle.getRandomField())]); + } + getContractAddress() { return toForeignCallResult([toSingle(this.contractAddress.toField())]); } @@ -103,21 +188,6 @@ export class TXEService { return toForeignCallResult([toSingle(new Fr(this.blockNumber))]); } - async reset() { - this.blockNumber = 0; - this.store = openTmpStore(true); - this.trees = await MerkleTrees.new(this.store, this.logger); - this.typedOracle = new TXE( - this.logger, - this.trees, - new PackedValuesCache(), - new ExecutionNoteCache(), - this.contractAddress, - ); - await this.#timeTravelInner(1); - return toForeignCallResult([]); - } - async packArgumentsArray(args: ForeignCallArray) { const packed = await this.typedOracle.packArgumentsArray(fromArray(args)); return toForeignCallResult([toSingle(packed)]); @@ -277,4 +347,24 @@ export class TXEService { const exists = await this.typedOracle.checkNullifierExists(fromSingle(innerNullifier)); return toForeignCallResult([toSingle(new Fr(exists))]); } + + async getContractInstance(address: ForeignCallSingle) { + const instance = await this.typedOracle.getContractInstance(fromSingle(address)); + return toForeignCallResult([ + toArray([ + instance.salt, + instance.deployer, + instance.contractClassId, + instance.initializationHash, + instance.publicKeysHash, + ]), + ]); + } + + async getPublicKeysAndPartialAddress(address: ForeignCallSingle) { + const parsedAddress = AztecAddress.fromField(fromSingle(address)); + const { publicKeys, partialAddress } = await this.typedOracle.getCompleteAddress(parsedAddress); + + return toForeignCallResult([toArray([...publicKeys.toFields(), partialAddress])]); + } } diff --git a/yarn-project/types/src/abi/contract_artifact.ts b/yarn-project/types/src/abi/contract_artifact.ts index 8ca72aa68a75..9fff2b21b6a4 100644 --- a/yarn-project/types/src/abi/contract_artifact.ts +++ b/yarn-project/types/src/abi/contract_artifact.ts @@ -2,7 +2,6 @@ import { type ABIParameter, type ABIParameterVisibility, type AbiType, - type BasicValue, type ContractArtifact, type ContractNote, type FieldLayout, @@ -227,10 +226,8 @@ function getStorageLayout(input: NoirCompiledContract) { return storageFields.reduce((acc: Record, field) => { const name = field.name; const slot = field.value.fields[0].value as IntegerValue; - const typ = field.value.fields[1].value as BasicValue<'string', string>; acc[name] = { slot: new Fr(BigInt(slot.value)), - typ: typ.value, }; return acc; }, {}); diff --git a/yarn-project/yarn.lock b/yarn-project/yarn.lock index e02b14abb9fc..a7a16340361d 100644 --- a/yarn-project/yarn.lock +++ b/yarn-project/yarn.lock @@ -934,6 +934,8 @@ __metadata: version: 0.0.0-use.local resolution: "@aztec/txe@workspace:txe" dependencies: + "@aztec/archiver": "workspace:^" + "@aztec/aztec.js": "workspace:^" "@aztec/circuit-types": "workspace:^" "@aztec/circuits.js": "workspace:^" "@aztec/foundation": "workspace:^" From 562e92ed1a9cfd41be860107d09837da73f0a7f7 Mon Sep 17 00:00:00 2001 From: thunkar Date: Mon, 10 Jun 2024 10:40:49 +0000 Subject: [PATCH 30/60] initial txe version --- .../src/state_vars/shared_mutable/test.nr | 2 +- .../src/test/helpers/test_environment.nr | 25 ++++++++++--------- .../aztec-nr/aztec/src/test/helpers/types.nr | 12 ++++----- .../contracts/counter_contract/src/main.nr | 11 ++++---- .../tooling/nargo_cli/src/cli/test_cmd.rs | 4 +-- yarn-project/txe/src/oracle/txe_oracle.ts | 2 +- .../txe/src/txe_service/txe_service.ts | 14 +++++------ 7 files changed, 34 insertions(+), 36 deletions(-) diff --git a/noir-projects/aztec-nr/aztec/src/state_vars/shared_mutable/test.nr b/noir-projects/aztec-nr/aztec/src/state_vars/shared_mutable/test.nr index d16344201ce1..42f432a55340 100644 --- a/noir-projects/aztec-nr/aztec/src/state_vars/shared_mutable/test.nr +++ b/noir-projects/aztec-nr/aztec/src/state_vars/shared_mutable/test.nr @@ -92,7 +92,7 @@ fn test_get_current_value_in_private_before_change() { let (_, block_of_change) = public_state_var.get_scheduled_value_in_public(); - let schedule_block_number = env.block_number; + let schedule_block_number = env.block_number(); let private_state_var = in_private(&mut env, schedule_block_number); assert_eq(private_state_var.get_current_value_in_private(), 0); diff --git a/noir-projects/aztec-nr/aztec/src/test/helpers/test_environment.nr b/noir-projects/aztec-nr/aztec/src/test/helpers/test_environment.nr index 865ff9482860..5dd18c6871c9 100644 --- a/noir-projects/aztec-nr/aztec/src/test/helpers/test_environment.nr +++ b/noir-projects/aztec-nr/aztec/src/test/helpers/test_environment.nr @@ -5,11 +5,10 @@ use dep::protocol_types::{ }; use crate::context::{PrivateContext, PublicContext, PrivateVoidCallInterface}; -use crate::test::helpers::cheatcodes; +use crate::test::helpers::{cheatcodes, types::Deployer}; use crate::keys::{public_keys::PublicKeys, constants::{NULLIFIER_INDEX, INCOMING_INDEX, OUTGOING_INDEX, TAGGING_INDEX}}; struct TestEnvironment { - block_number: u32, contract_address: Option, args_hash: Option, function_selector: Option @@ -19,12 +18,11 @@ impl TestEnvironment { fn new() -> Self { cheatcodes::reset(); - Self { - block_number: 1, - contract_address: Option::none(), - args_hash: Option::none(), - function_selector: Option::none() - } + Self { contract_address: Option::none(), args_hash: Option::none(), function_selector: Option::none() } + } + + fn block_number(self) -> u32 { + cheatcodes::get_block_number() } fn contract_address(&mut self, contract_address: AztecAddress) -> Self { @@ -43,12 +41,11 @@ impl TestEnvironment { } fn advance_block_to(&mut self, block_number: u32) { - let difference = block_number - self.block_number; + let difference = block_number - cheatcodes::get_block_number(); self.advance_block_by(difference); } fn advance_block_by(&mut self, blocks: u32) { - self.block_number += blocks; cheatcodes::advance_blocks(blocks); } @@ -61,11 +58,11 @@ impl TestEnvironment { } fn private(&mut self) -> PrivateContext { - self.private_at(self.block_number) + self.private_at(cheatcodes::get_block_number()) } fn private_at(&mut self, historical_block_number: u32) -> PrivateContext { - if historical_block_number >= self.block_number { + if historical_block_number >= cheatcodes::get_block_number() { self.advance_block_to(historical_block_number + 1); } @@ -122,4 +119,8 @@ impl TestEnvironment { AztecAddress::compute(keys.hash(), PartialAddress::from_field(1)) } + + fn deploy(self, path: str) -> Deployer { + cheatcodes::deploy(path) + } } diff --git a/noir-projects/aztec-nr/aztec/src/test/helpers/types.nr b/noir-projects/aztec-nr/aztec/src/test/helpers/types.nr index d443e0072245..e4b43538c341 100644 --- a/noir-projects/aztec-nr/aztec/src/test/helpers/types.nr +++ b/noir-projects/aztec-nr/aztec/src/test/helpers/types.nr @@ -2,25 +2,23 @@ use dep::protocol_types::address::AztecAddress; use crate::context::inputs::{PublicContextInputs, PrivateContextInputs}; use crate::context::call_interfaces::CallInterface; -use crate::test::helpers::cheatcodes::{get_private_context_inputs, get_public_context_inputs, deploy_inner}; +use crate::test::helpers::cheatcodes::{get_private_context_inputs, get_public_context_inputs, deploy_inner, advance_blocks, get_block_number}; struct Deployer { path: str, } impl Deployer { - pub fn with_private_initializer( - self, - call_interface: C, - historical_block_number: u32 - ) -> AztecAddress where C: CallInterface { + pub fn with_private_initializer(self, call_interface: C) -> AztecAddress where C: CallInterface { let address = deploy_inner( self.path, call_interface.get_name(), call_interface.get_args() ); + advance_blocks(1); + let block_number = get_block_number(); let original_fn = call_interface.get_original(); - let mut inputs = get_private_context_inputs(historical_block_number); + let mut inputs = get_private_context_inputs(block_number - 1); inputs.call_context.storage_contract_address = address; inputs.call_context.function_selector = call_interface.get_selector(); let _result = original_fn(inputs); diff --git a/noir-projects/noir-contracts/contracts/counter_contract/src/main.nr b/noir-projects/noir-contracts/contracts/counter_contract/src/main.nr index 9ae4c64db4c9..c3c8cc6981ff 100644 --- a/noir-projects/noir-contracts/contracts/counter_contract/src/main.nr +++ b/noir-projects/noir-contracts/contracts/counter_contract/src/main.nr @@ -41,25 +41,24 @@ contract Counter { use dep::aztec::test::{helpers::{cheatcodes, test_environment::TestEnvironment}}; use dep::aztec::protocol_types::storage::map::derive_storage_slot_in_map; use dep::aztec::oracle::{storage::{storage_read, storage_write}}; - use dep::aztec::keys::PublicKeys; - use dep::aztec::protocol_types::grumpkin_point::GrumpkinPoint; - use dep::aztec::protocol_types::address::PartialAddress; use dep::aztec::note::note_getter::{MAX_NOTES_PER_PAGE, view_notes}; use dep::aztec::note::note_viewer_options::NoteViewerOptions; #[test] fn test_initialize() { + // Setup env, generate keys let mut env = TestEnvironment::new(); let owner = env.get_address_with_keys(AztecAddress::from_field(13)); let outgoing_viewer = env.get_address_with_keys(AztecAddress::from_field(14)); - cheatcodes::advance_blocks(2); + + // Deploy contract and initialize let initializer = Counter::interface().initialize(5, owner, outgoing_viewer); - let _contract_address = cheatcodes::deploy("@aztec/noir-contracts.js/Counter").with_private_initializer(initializer, 1); + let _contract_address = env.deploy("@aztec/noir-contracts.js/Counter").with_private_initializer(initializer); + // Read the stored value in the note let counter_slot = Counter::storage().counters.slot; let owner_slot = derive_storage_slot_in_map(counter_slot, owner); let mut options = NoteViewerOptions::new(); let opt_notes: [Option; MAX_NOTES_PER_PAGE] = view_notes(owner_slot, options); - assert(opt_notes[0].unwrap().value == 5); } } diff --git a/noir/noir-repo/tooling/nargo_cli/src/cli/test_cmd.rs b/noir/noir-repo/tooling/nargo_cli/src/cli/test_cmd.rs index 99c284e5019b..838642b587d7 100644 --- a/noir/noir-repo/tooling/nargo_cli/src/cli/test_cmd.rs +++ b/noir/noir-repo/tooling/nargo_cli/src/cli/test_cmd.rs @@ -82,7 +82,7 @@ pub(crate) fn run(args: TestCommand, config: NargoConfig) -> Result<(), CliError let test_reports: Vec> = workspace .into_iter() - .par_bridge() + //.par_bridge() .map(|package| { run_tests::( &workspace_file_manager, @@ -137,7 +137,7 @@ fn run_tests + Default>( println!("[{}] Running {count_all} test function{plural}", package.name); let test_report: Vec<(String, TestStatus)> = test_functions - .into_par_iter() + .into_iter() .map(|test_name| { let status = run_test::( file_manager, diff --git a/yarn-project/txe/src/oracle/txe_oracle.ts b/yarn-project/txe/src/oracle/txe_oracle.ts index ec7f09ec6a56..59ae24e756d4 100644 --- a/yarn-project/txe/src/oracle/txe_oracle.ts +++ b/yarn-project/txe/src/oracle/txe_oracle.ts @@ -128,7 +128,7 @@ export class TXE implements TypedOracle { } getCompleteAddress(account: AztecAddress): Promise { - return Promise.resolve(CompleteAddress.fromSecretKeyAndPartialAddress(Fr.ZERO, account)); + return Promise.resolve(CompleteAddress.fromSecretKeyAndPartialAddress(Fr.ONE, account)); } getAuthWitness(_messageHash: Fr): Promise { diff --git a/yarn-project/txe/src/txe_service/txe_service.ts b/yarn-project/txe/src/txe_service/txe_service.ts index fc7cd9bb5584..7da9912a06d7 100644 --- a/yarn-project/txe/src/txe_service/txe_service.ts +++ b/yarn-project/txe/src/txe_service/txe_service.ts @@ -413,18 +413,18 @@ export class TXEService { } emitEncryptedLog( - contractAddress: ForeignCallSingle, - randomandomness: ForeignCallSingle, - encryptedLog: ForeignCallSingle, - counter: ForeignCallSingle, + _contractAddress: ForeignCallSingle, + _randomandomness: ForeignCallSingle, + _encryptedLog: ForeignCallSingle, + _counter: ForeignCallSingle, ) { return toForeignCallResult([]); } emitEncryptedNoteLog( - noteHashCounter: ForeignCallSingle, - encryptedNote: ForeignCallArray, - counter: ForeignCallSingle, + _noteHashCounter: ForeignCallSingle, + _encryptedNote: ForeignCallArray, + _counter: ForeignCallSingle, ) { return toForeignCallResult([]); } From 21f3178a73ae10b99bc4eca178168bae2ff94ffa Mon Sep 17 00:00:00 2001 From: thunkar Date: Mon, 10 Jun 2024 10:50:33 +0000 Subject: [PATCH 31/60] removed file --- .vscode/settings.json | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/.vscode/settings.json b/.vscode/settings.json index 03168efe258b..ab8056e8a7c3 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -158,16 +158,14 @@ "avm-transpiler/Cargo.toml" ], "files.watcherExclude": { - "**/.git/**": true, + "**/.git/objects/**": true, + "**/.git/subtree-cache/**": true, "**/node_modules/**": true, "**/.hg/store/**": true, "**/target/**": true, "**/l1-contracts/lib/**": true, - "**/barretenberg/cpp/build*/**": true, - "**/barretenberg/cpp/src/wasi-sdk*/**": true, - "**/dest/**": true, - "**/noir/noir-repo/docs/versioned_docs/**": true + "**/barretenberg/cpp/build*/**": true }, "cmake.sourceDirectory": "${workspaceFolder}/barretenberg/cpp", - "noir.nargoPath": "/mnt/user-data/grego/.nargo/bin/nargo" + "noir.nargoPath": "./noir/noir-repo/target/release/nargo" } From c543a2cca50e5db2ea8496591bc3d82f11e67b23 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicol=C3=A1s=20Venturo?= Date: Mon, 10 Jun 2024 11:07:27 +0000 Subject: [PATCH 32/60] Add TXE --- .vscode/settings.json | 8 +- .../aztec/src/context/call_interfaces.nr | 135 ++++-- .../aztec/src/context/private_context.nr | 2 +- .../aztec-nr/aztec/src/keys/getters.nr | 2 +- .../aztec/src/note/note_getter/test.nr | 41 +- .../src/state_vars/private_mutable/test.nr | 14 +- .../src/state_vars/public_immutable/test.nr | 53 +++ .../aztec-nr/aztec/src/state_vars/storage.nr | 5 +- .../aztec-nr/aztec/src/test/helpers.nr | 4 +- .../aztec/src/test/helpers/cheatcodes.nr | 82 ++++ .../aztec/src/test/helpers/context_builder.nr | 52 --- .../src/test/helpers/test_environment.nr | 126 +++++ .../aztec-nr/aztec/src/test/helpers/types.nr | 42 ++ .../aztec-nr/aztec/src/test/mocks.nr | 1 + .../aztec/src/test/mocks/mock_struct.nr | 36 ++ .../contracts/counter_contract/src/main.nr | 24 + .../crates/types/src/traits.nr | 8 +- noir/noir-repo/aztec_macros/src/lib.rs | 2 +- .../src/transforms/contract_interface.rs | 214 ++++++--- .../aztec_macros/src/transforms/storage.rs | 16 +- .../src/monomorphization/mod.rs | 7 +- .../tooling/nargo_cli/src/cli/test_cmd.rs | 4 +- yarn-project/archiver/src/archiver/index.ts | 1 + .../src/contract-interface-gen/typescript.ts | 3 +- .../src/contract/contract_instance.ts | 1 - yarn-project/foundation/src/abi/abi.ts | 4 - yarn-project/package.json | 1 + .../src/client/execution_note_cache.ts | 3 + yarn-project/simulator/src/client/index.ts | 2 + yarn-project/txe/.eslintrc.cjs | 1 + yarn-project/txe/package.json | 80 ++++ yarn-project/txe/src/bin/index.ts | 27 ++ yarn-project/txe/src/http_rpc_server/index.ts | 29 ++ yarn-project/txe/src/index.ts | 1 + yarn-project/txe/src/oracle/txe_oracle.ts | 356 +++++++++++++++ .../txe/src/txe_service/txe_service.ts | 431 ++++++++++++++++++ yarn-project/txe/src/util/encoding.ts | 29 ++ yarn-project/txe/tsconfig.json | 41 ++ .../types/src/abi/contract_artifact.ts | 3 - .../world-state/src/world-state-db/index.ts | 2 + yarn-project/yarn.lock | 26 ++ 41 files changed, 1701 insertions(+), 218 deletions(-) create mode 100644 noir-projects/aztec-nr/aztec/src/state_vars/public_immutable/test.nr create mode 100644 noir-projects/aztec-nr/aztec/src/test/helpers/cheatcodes.nr delete mode 100644 noir-projects/aztec-nr/aztec/src/test/helpers/context_builder.nr create mode 100644 noir-projects/aztec-nr/aztec/src/test/helpers/test_environment.nr create mode 100644 noir-projects/aztec-nr/aztec/src/test/helpers/types.nr create mode 100644 noir-projects/aztec-nr/aztec/src/test/mocks/mock_struct.nr create mode 100644 yarn-project/txe/.eslintrc.cjs create mode 100644 yarn-project/txe/package.json create mode 100644 yarn-project/txe/src/bin/index.ts create mode 100644 yarn-project/txe/src/http_rpc_server/index.ts create mode 100644 yarn-project/txe/src/index.ts create mode 100644 yarn-project/txe/src/oracle/txe_oracle.ts create mode 100644 yarn-project/txe/src/txe_service/txe_service.ts create mode 100644 yarn-project/txe/src/util/encoding.ts create mode 100644 yarn-project/txe/tsconfig.json diff --git a/.vscode/settings.json b/.vscode/settings.json index a8ab844ebe13..ab8056e8a7c3 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -158,15 +158,13 @@ "avm-transpiler/Cargo.toml" ], "files.watcherExclude": { - "**/.git/**": true, + "**/.git/objects/**": true, + "**/.git/subtree-cache/**": true, "**/node_modules/**": true, "**/.hg/store/**": true, "**/target/**": true, "**/l1-contracts/lib/**": true, - "**/barretenberg/cpp/build*/**": true, - "**/barretenberg/cpp/src/wasi-sdk*/**": true, - "**/dest/**": true, - "**/noir/noir-repo/docs/versioned_docs/**": true + "**/barretenberg/cpp/build*/**": true }, "cmake.sourceDirectory": "${workspaceFolder}/barretenberg/cpp", "noir.nargoPath": "./noir/noir-repo/target/release/nargo" diff --git a/noir-projects/aztec-nr/aztec/src/context/call_interfaces.nr b/noir-projects/aztec-nr/aztec/src/context/call_interfaces.nr index 69904595c964..d57a4eb3b3d7 100644 --- a/noir-projects/aztec-nr/aztec/src/context/call_interfaces.nr +++ b/noir-projects/aztec-nr/aztec/src/context/call_interfaces.nr @@ -1,20 +1,51 @@ -use dep::protocol_types::{abis::function_selector::FunctionSelector, address::AztecAddress, traits::Deserialize}; +use dep::protocol_types::{ + abis::{function_selector::FunctionSelector, private_circuit_public_inputs::PrivateCircuitPublicInputs}, + address::AztecAddress, traits::Deserialize +}; -use crate::context::private_context::PrivateContext; -use crate::context::public_context::PublicContext; -use crate::context::gas::GasOpts; -use crate::context::public_context::FunctionReturns; +use crate::context::{ + private_context::PrivateContext, public_context::PublicContext, gas::GasOpts, + public_context::FunctionReturns, inputs::{PrivateContextInputs, PublicContextInputs} +}; use crate::oracle::arguments; -struct PrivateCallInterface { +trait CallInterface { + fn get_args(self) -> [Field]; + fn get_original(self) -> fn[Env](T) -> P; + fn get_selector(self) -> FunctionSelector; + fn get_name(self) -> str; +} + +impl CallInterface for PrivateCallInterface { + fn get_args(self) -> [Field] { + self.args + } + + fn get_original(self) -> fn[Env](PrivateContextInputs) -> PrivateCircuitPublicInputs { + self.original + } + + fn get_selector(self) -> FunctionSelector { + self.selector + } + + fn get_name(self) -> str { + self.name + } +} + +struct PrivateCallInterface { target_contract: AztecAddress, selector: FunctionSelector, + name: str, args_hash: Field, + args: [Field], + original: fn[Env](PrivateContextInputs) -> PrivateCircuitPublicInputs } -impl PrivateCallInterface { - pub fn call(self, context: &mut PrivateContext) -> T where T: Deserialize { +impl PrivateCallInterface { + pub fn call(self, context: &mut PrivateContext) -> T where T: Deserialize { let returns = context.call_private_function_with_packed_args( self.target_contract, self.selector, @@ -26,24 +57,45 @@ impl PrivateCallInterface { unpacked } - pub fn view(self, context: &mut PrivateContext) -> T where T: Deserialize { + pub fn view(self, context: &mut PrivateContext) -> T where T: Deserialize { let returns = context.call_private_function_with_packed_args(self.target_contract, self.selector, self.args_hash, true, false); returns.unpack_into() } - pub fn delegate_call(self, context: &mut PrivateContext) -> T where T: Deserialize { + pub fn delegate_call(self, context: &mut PrivateContext) -> T where T: Deserialize { let returns = context.call_private_function_with_packed_args(self.target_contract, self.selector, self.args_hash, false, true); returns.unpack_into() } } -struct PrivateVoidCallInterface { +impl CallInterface for PrivateVoidCallInterface { + fn get_args(self) -> [Field] { + self.args + } + + fn get_original(self) -> fn[Env](PrivateContextInputs) -> PrivateCircuitPublicInputs { + self.original + } + + fn get_selector(self) -> FunctionSelector { + self.selector + } + + fn get_name(self) -> str { + self.name + } +} + +struct PrivateVoidCallInterface { target_contract: AztecAddress, selector: FunctionSelector, + name: str, args_hash: Field, + args: [Field], + original: fn[Env](PrivateContextInputs) -> PrivateCircuitPublicInputs } -impl PrivateVoidCallInterface { +impl PrivateVoidCallInterface { pub fn call(self, context: &mut PrivateContext) { context.call_private_function_with_packed_args( self.target_contract, @@ -63,55 +115,63 @@ impl PrivateVoidCallInterface { } } -struct PrivateStaticCallInterface { +struct PrivateStaticCallInterface { target_contract: AztecAddress, selector: FunctionSelector, + name: str, args_hash: Field, + args: [Field], + original: fn[Env](PrivateContextInputs) -> PrivateCircuitPublicInputs } -impl PrivateStaticCallInterface { - pub fn view(self, context: &mut PrivateContext) -> T where T: Deserialize { +impl PrivateStaticCallInterface { + pub fn view(self, context: &mut PrivateContext) -> T where T: Deserialize { let returns = context.call_private_function_with_packed_args(self.target_contract, self.selector, self.args_hash, true, false); returns.unpack_into() } } -struct PrivateStaticVoidCallInterface { +struct PrivateStaticVoidCallInterface { target_contract: AztecAddress, selector: FunctionSelector, + name: str, args_hash: Field, + args: [Field], + original: fn[Env](PrivateContextInputs) -> PrivateCircuitPublicInputs } -impl PrivateStaticVoidCallInterface { +impl PrivateStaticVoidCallInterface { pub fn view(self, context: &mut PrivateContext) { context.call_private_function_with_packed_args(self.target_contract, self.selector, self.args_hash, true, false).assert_empty(); } } -struct PublicCallInterface { +struct PublicCallInterface { target_contract: AztecAddress, selector: FunctionSelector, + name: str, args: [Field], gas_opts: GasOpts, + original: fn[Env](PublicContextInputs) -> T } -impl PublicCallInterface { +impl PublicCallInterface { pub fn with_gas(self: &mut Self, gas_opts: GasOpts) -> &mut Self { self.gas_opts = gas_opts; self } - pub fn call(self, context: &mut PublicContext) -> T where T: Deserialize { + pub fn call(self, context: &mut PublicContext) -> T where T: Deserialize { let returns = context.call_public_function(self.target_contract, self.selector, self.args, self.gas_opts); returns.deserialize_into() } - pub fn view(self, context: &mut PublicContext) -> T where T: Deserialize { + pub fn view(self, context: &mut PublicContext) -> T where T: Deserialize { let returns = context.static_call_public_function(self.target_contract, self.selector, self.args, self.gas_opts); returns.deserialize_into() } - pub fn delegate_call(self, context: &mut PublicContext) -> T where T: Deserialize { + pub fn delegate_call(self, context: &mut PublicContext) -> T where T: Deserialize { let returns = context.delegate_call_public_function(self.target_contract, self.selector, self.args); returns.deserialize_into() } @@ -153,30 +213,32 @@ impl PublicCallInterface { } } -struct PublicVoidCallInterface { +struct PublicVoidCallInterface { target_contract: AztecAddress, selector: FunctionSelector, + name: str, args: [Field], gas_opts: GasOpts, + original: fn[Env](PublicContextInputs) -> () } -impl PublicVoidCallInterface { +impl PublicVoidCallInterface { pub fn with_gas(self: &mut Self, gas_opts: GasOpts) -> &mut Self { self.gas_opts = gas_opts; self } - pub fn call(self, context: &mut PublicContext) { + pub fn call(self, context: &mut PublicContext) { let returns = context.call_public_function(self.target_contract, self.selector, self.args, self.gas_opts); returns.assert_empty() } - pub fn view(self, context: &mut PublicContext) { + pub fn view(self, context: &mut PublicContext) { let returns = context.static_call_public_function(self.target_contract, self.selector, self.args, self.gas_opts); returns.assert_empty() } - pub fn delegate_call(self, context: &mut PublicContext) { + pub fn delegate_call(self, context: &mut PublicContext) { let returns = context.delegate_call_public_function(self.target_contract, self.selector, self.args); returns.assert_empty() } @@ -218,22 +280,25 @@ impl PublicVoidCallInterface { } } -struct PublicStaticCallInterface { +struct PublicStaticCallInterface { target_contract: AztecAddress, selector: FunctionSelector, + name: str, args: [Field], gas_opts: GasOpts, + original: fn[Env](PublicContextInputs) -> T } -impl PublicStaticCallInterface { +impl PublicStaticCallInterface { pub fn with_gas(self: &mut Self, gas_opts: GasOpts) -> &mut Self { self.gas_opts = gas_opts; self } - pub fn view(self, context: &mut PublicContext) -> T where T: Deserialize { + pub fn view(self, context: &mut PublicContext) -> T where T: Deserialize { let returns = context.static_call_public_function(self.target_contract, self.selector, self.args, self.gas_opts); - returns.deserialize_into() + let unpacked: T = returns.deserialize_into(); + unpacked } pub fn enqueue_view(self, context: &mut PrivateContext) { @@ -249,20 +314,22 @@ impl PublicStaticCallInterface { } } -struct PublicStaticVoidCallInterface { +struct PublicStaticVoidCallInterface { target_contract: AztecAddress, selector: FunctionSelector, + name: str, args: [Field], gas_opts: GasOpts, + original: fn[Env](PublicContextInputs) -> () } -impl PublicStaticVoidCallInterface { +impl PublicStaticVoidCallInterface { pub fn with_gas(self: &mut Self, gas_opts: GasOpts) -> &mut Self { self.gas_opts = gas_opts; self } - pub fn view(self, context: &mut PublicContext) { + pub fn view(self, context: &mut PublicContext) { let returns = context.static_call_public_function(self.target_contract, self.selector, self.args, self.gas_opts); returns.assert_empty() } diff --git a/noir-projects/aztec-nr/aztec/src/context/private_context.nr b/noir-projects/aztec-nr/aztec/src/context/private_context.nr index aeba73e0554a..e0f31144ed28 100644 --- a/noir-projects/aztec-nr/aztec/src/context/private_context.nr +++ b/noir-projects/aztec-nr/aztec/src/context/private_context.nr @@ -237,7 +237,7 @@ impl PrivateContext { let request_and_generator = KeyValidationRequestAndGenerator { request, sk_app_generator: sk_generators[key_index] }; // We constrain that the pk_m_hash matches the one in the request (otherwise we could get an arbitrary // valid key request and not the one corresponding to pk_m_hash). - assert(request.pk_m.hash() == pk_m_hash); + //assert(request.pk_m.hash() == pk_m_hash); self.key_validation_requests_and_generators.push(request_and_generator); self.last_key_validation_requests[key_index] = Option::some(request); request.sk_app diff --git a/noir-projects/aztec-nr/aztec/src/keys/getters.nr b/noir-projects/aztec-nr/aztec/src/keys/getters.nr index 8e56174ac835..8a2c7c440fb2 100644 --- a/noir-projects/aztec-nr/aztec/src/keys/getters.nr +++ b/noir-projects/aztec-nr/aztec/src/keys/getters.nr @@ -95,7 +95,7 @@ fn fetch_and_constrain_keys(address: AztecAddress) -> PublicKeys { let computed_address = AztecAddress::compute(public_keys.hash(), partial_address); - assert(computed_address.eq(address)); + //assert(computed_address.eq(address)); public_keys } diff --git a/noir-projects/aztec-nr/aztec/src/note/note_getter/test.nr b/noir-projects/aztec-nr/aztec/src/note/note_getter/test.nr index 7229268f1e55..0ba1c1c08bb5 100644 --- a/noir-projects/aztec-nr/aztec/src/note/note_getter/test.nr +++ b/noir-projects/aztec-nr/aztec/src/note/note_getter/test.nr @@ -9,13 +9,13 @@ use crate::{ }; use dep::protocol_types::address::AztecAddress; -use crate::test::{helpers::context_builder::ContextBuilder, mocks::mock_note::MockNote}; +use crate::test::{helpers::{test_environment::TestEnvironment, cheatcodes}, mocks::mock_note::MockNote}; global contract_address = AztecAddress::from_field(69); global storage_slot: Field = 42; -fn setup() -> PrivateContext { - ContextBuilder::new().contract_address(contract_address).private() +fn setup() -> TestEnvironment { + TestEnvironment::new().contract_address(contract_address) } fn build_valid_note(value: Field) -> MockNote { @@ -24,7 +24,8 @@ fn build_valid_note(value: Field) -> MockNote { #[test] fn processes_single_note() { - let mut context = setup(); + let mut env = setup(); + let mut context = env.private(); let mut notes_to_constrain = [Option::none(); MAX_NOTE_HASH_READ_REQUESTS_PER_CALL]; notes_to_constrain[0] = Option::some(build_valid_note(13)); @@ -38,7 +39,8 @@ fn processes_single_note() { #[test] fn processes_many_notes() { - let mut context = setup(); + let mut env = setup(); + let mut context = env.private(); let mut notes_to_constrain = [Option::none(); MAX_NOTE_HASH_READ_REQUESTS_PER_CALL]; notes_to_constrain[0] = Option::some(build_valid_note(13)); @@ -53,7 +55,8 @@ fn processes_many_notes() { #[test] fn collapses_notes_at_the_beginning_of_the_array() { - let mut context = setup(); + let mut env = setup(); + let mut context = env.private(); let mut opt_notes = [Option::none(); MAX_NOTE_HASH_READ_REQUESTS_PER_CALL]; opt_notes[1] = Option::some(build_valid_note(0)); @@ -80,8 +83,9 @@ fn collapses_notes_at_the_beginning_of_the_array() { } #[test(should_fail_with="Cannot return zero notes")] - fn rejects_zero_notes() { - let mut context = setup(); +fn rejects_zero_notes() { + let mut env = setup(); + let mut context = env.private(); let opt_notes: [Option; MAX_NOTE_HASH_READ_REQUESTS_PER_CALL] = [Option::none(); MAX_NOTE_HASH_READ_REQUESTS_PER_CALL]; @@ -91,7 +95,8 @@ fn collapses_notes_at_the_beginning_of_the_array() { #[test(should_fail_with="Got more notes than limit.")] fn rejects_mote_notes_than_limit() { - let mut context = setup(); + let mut env = setup(); + let mut context = env.private(); let mut opt_notes: [Option; MAX_NOTE_HASH_READ_REQUESTS_PER_CALL] = [Option::none(); MAX_NOTE_HASH_READ_REQUESTS_PER_CALL]; opt_notes[1] = Option::some(build_valid_note(0)); @@ -105,7 +110,8 @@ fn rejects_mote_notes_than_limit() { #[test] fn applies_filter_before_constraining() { - let mut context = setup(); + let mut env = setup(); + let mut context = env.private(); let mut notes_to_constrain = [Option::none(); MAX_NOTE_HASH_READ_REQUESTS_PER_CALL]; let invalid_note = MockNote::new(13).build(); // This note does not have the correct address or storage slot @@ -138,7 +144,8 @@ fn applies_filter_before_constraining() { #[test(should_fail_with="Mismatch note header contract address.")] fn rejects_mismatched_address() { - let mut context = setup(); + let mut env = setup(); + let mut context = env.private(); let note = MockNote::new(1).storage_slot(storage_slot).build(); // We're not setting the right contract address @@ -151,7 +158,8 @@ fn rejects_mismatched_address() { #[test(should_fail_with="Mismatch note header storage slot.")] fn rejects_mismatched_storage_slot() { - let mut context = setup(); + let mut env = setup(); + let mut context = env.private(); let note = MockNote::new(1).contract_address(contract_address).build(); // We're not setting the right storage slot @@ -164,7 +172,8 @@ fn rejects_mismatched_storage_slot() { #[test(should_fail_with="Mismatch return note field.")] fn rejects_mismatched_selector() { - let mut context = setup(); + let mut env = setup(); + let mut context = env.private(); let value = 10; let note = build_valid_note(value); @@ -184,7 +193,8 @@ fn rejects_mismatched_selector() { #[test(should_fail_with="Return notes not sorted in descending order.")] fn rejects_mismatched_desc_sort_order() { - let mut context = setup(); + let mut env = setup(); + let mut context = env.private(); let mut opt_notes = [Option::none(); MAX_NOTE_HASH_READ_REQUESTS_PER_CALL]; // Notes in ascending order @@ -201,7 +211,8 @@ fn rejects_mismatched_desc_sort_order() { #[test(should_fail_with="Return notes not sorted in ascending order.")] fn rejects_mismatched_asc_sort_order() { - let mut context = setup(); + let mut env = setup(); + let mut context = env.private(); let mut opt_notes = [Option::none(); MAX_NOTE_HASH_READ_REQUESTS_PER_CALL]; // Notes in descending order diff --git a/noir-projects/aztec-nr/aztec/src/state_vars/private_mutable/test.nr b/noir-projects/aztec-nr/aztec/src/state_vars/private_mutable/test.nr index f0272af161f1..3db22ca38e24 100644 --- a/noir-projects/aztec-nr/aztec/src/state_vars/private_mutable/test.nr +++ b/noir-projects/aztec-nr/aztec/src/state_vars/private_mutable/test.nr @@ -1,14 +1,17 @@ use dep::protocol_types::{address::AztecAddress, grumpkin_point::GrumpkinPoint}; use crate::{context::PrivateContext, state_vars::private_mutable::PrivateMutable}; -use crate::test::{mocks::mock_note::MockNote, helpers::context_builder::ContextBuilder}; +use crate::test::{mocks::mock_note::MockNote, helpers::test_environment::TestEnvironment}; use dep::std::{unsafe::zeroed, test::OracleMock}; global contract_address = AztecAddress::from_field(13); global storage_slot = 17; -fn setup() -> PrivateMutable { - let mut context = ContextBuilder::new().contract_address(contract_address).private(); - let state_var = PrivateMutable::new(&mut context, storage_slot); +fn setup() -> TestEnvironment { + TestEnvironment::new().contract_address(contract_address) +} + +fn in_private(env: &mut TestEnvironment) -> PrivateMutable { + let state_var = PrivateMutable::new(&mut env.private(), storage_slot); // This oracle is called for its side effects alone - it's always expected to return 0. OracleMock::mock("notifyCreatedNote").returns(0); @@ -18,7 +21,8 @@ fn setup() -> PrivateMutable { #[test] fn test_initialize_or_replace_without_nullifier() { - let state_var = setup(); + let mut env = setup(); + let state_var = in_private(&mut env); let ovpk_m: GrumpkinPoint = zeroed(); let ivpk_m: GrumpkinPoint = zeroed(); diff --git a/noir-projects/aztec-nr/aztec/src/state_vars/public_immutable/test.nr b/noir-projects/aztec-nr/aztec/src/state_vars/public_immutable/test.nr new file mode 100644 index 000000000000..7614ffff1d10 --- /dev/null +++ b/noir-projects/aztec-nr/aztec/src/state_vars/public_immutable/test.nr @@ -0,0 +1,53 @@ +use crate::{context::PublicContext, state_vars::public_immutable::PublicImmutable}; +use crate::test::{helpers::test_environment::TestEnvironment, mocks::mock_struct::MockStruct}; +use dep::protocol_types::traits::Serialize; + +global storage_slot = 7; + +fn setup() -> TestEnvironment { + TestEnvironment::new() +} + +fn in_public(env: TestEnvironment) -> PublicImmutable { + PublicImmutable::new(&mut env.public(), storage_slot) +} + +#[test] +fn test_uninitialized_by_default() { + let env = setup(); + let state_var = in_public(env); + + assert_eq(state_var.is_initialized(), false); +} + +#[test] +fn test_initialize_uninitialized() { + let env = setup(); + let state_var = in_public(env); + + let value = MockStruct::new(5, 6); + + state_var.initialize(value); + + assert(state_var.is_initialized()); + assert(state_var.read() == value); +} + +#[test(should_fail_with="PublicImmutable already initialized")] +fn test_initialize_already_initialized() { + let env = setup(); + let state_var = in_public(env); + + let value = MockStruct::new(5, 6); + + state_var.initialize(value); + state_var.initialize(value); +} + +#[test(should_fail_with="PublicImmutable not initialized")] +fn test_read_uninitialized() { + let env = setup(); + let state_var = in_public(env); + + let _ = state_var.read(); +} diff --git a/noir-projects/aztec-nr/aztec/src/state_vars/storage.nr b/noir-projects/aztec-nr/aztec/src/state_vars/storage.nr index 0f8cce2323cc..8ae9faf228c4 100644 --- a/noir-projects/aztec-nr/aztec/src/state_vars/storage.nr +++ b/noir-projects/aztec-nr/aztec/src/state_vars/storage.nr @@ -8,9 +8,8 @@ trait Storage where T: Serialize + Deserialize { // Struct representing an exportable storage variable in the contract // Every entry in the storage struct will be exported in the compilation artifact as a -// Storable entity, containing the storage slot and the type of the variable -struct Storable { +// Storable entity, containing the storage slot +struct Storable { slot: Field, - typ: str } diff --git a/noir-projects/aztec-nr/aztec/src/test/helpers.nr b/noir-projects/aztec-nr/aztec/src/test/helpers.nr index c5c7a4b5f311..80045b39a642 100644 --- a/noir-projects/aztec-nr/aztec/src/test/helpers.nr +++ b/noir-projects/aztec-nr/aztec/src/test/helpers.nr @@ -1 +1,3 @@ -mod context_builder; +mod test_environment; +mod cheatcodes; +mod types; diff --git a/noir-projects/aztec-nr/aztec/src/test/helpers/cheatcodes.nr b/noir-projects/aztec-nr/aztec/src/test/helpers/cheatcodes.nr new file mode 100644 index 000000000000..033e2823574d --- /dev/null +++ b/noir-projects/aztec-nr/aztec/src/test/helpers/cheatcodes.nr @@ -0,0 +1,82 @@ +use dep::protocol_types::address::AztecAddress; +use crate::context::inputs::{PublicContextInputs, PrivateContextInputs}; +use crate::test::helpers::types::Deployer; + +unconstrained pub fn reset() { + oracle_reset(); +} + +unconstrained pub fn get_contract_address() -> AztecAddress { + oracle_get_contract_address() +} + +unconstrained pub fn set_contract_address(address: AztecAddress) { + oracle_set_contract_address(address); +} + +unconstrained pub fn get_block_number() -> u32 { + oracle_get_block_number() +} + +unconstrained pub fn advance_blocks(blocks: u32) { + oracle_time_travel(blocks); +} + +unconstrained pub fn get_private_context_inputs(historical_block_number: u32) -> PrivateContextInputs { + oracle_get_private_context_inputs(historical_block_number) +} + +unconstrained pub fn get_public_context_inputs() -> PublicContextInputs { + oracle_get_public_context_inputs() +} + +unconstrained pub fn deploy_inner( + path: str, + initializer: str, + args: [Field] +) -> AztecAddress { + oracle_deploy(path, initializer, args) +} + +pub fn deploy(path: str) -> Deployer { + Deployer { path } +} + +unconstrained pub fn direct_storage_write( + contract_address: AztecAddress, + storage_slot: Field, + fields: [Field; N] +) { + let _hash = direct_storage_write_oracle(contract_address, storage_slot, fields); +} + +#[oracle(reset)] +fn oracle_reset() {} + +#[oracle(getContractAddress)] +fn oracle_get_contract_address() -> AztecAddress {} + +#[oracle(setContractAddress)] +fn oracle_set_contract_address(address: AztecAddress) {} + +#[oracle(getBlockNumber)] +fn oracle_get_block_number() -> u32 {} + +#[oracle(timeTravel)] +fn oracle_time_travel(blocks: u32) {} + +#[oracle(getPrivateContextInputs)] +fn oracle_get_private_context_inputs(historical_block_number: u32) -> PrivateContextInputs {} + +#[oracle(getPublicContextInputs)] +fn oracle_get_public_context_inputs() -> PublicContextInputs {} + +#[oracle(deploy)] +fn oracle_deploy(path: str, initializer: str, args: [Field]) -> AztecAddress {} + +#[oracle(directStorageWrite)] +fn direct_storage_write_oracle( + _contract_address: AztecAddress, + _storage_slot: Field, + _values: [Field; N] +) -> [Field; N] {} diff --git a/noir-projects/aztec-nr/aztec/src/test/helpers/context_builder.nr b/noir-projects/aztec-nr/aztec/src/test/helpers/context_builder.nr deleted file mode 100644 index 0b04ac52ccbd..000000000000 --- a/noir-projects/aztec-nr/aztec/src/test/helpers/context_builder.nr +++ /dev/null @@ -1,52 +0,0 @@ -use crate::context::{PrivateContext, PublicContext}; -use dep::protocol_types::address::AztecAddress; -use dep::std::test::OracleMock; - -struct ContextBuilder { - block_number: Option, - contract_address: Option, -} - -impl ContextBuilder { - fn new() -> Self { - Self { block_number: Option::none(), contract_address: Option::none() } - } - - fn block_number(&mut self, block_number: Field) -> &mut Self { - self.block_number = Option::some(block_number); - self - } - - fn contract_address(&mut self, contract_address: AztecAddress) -> &mut Self { - self.contract_address = Option::some(contract_address); - self - } - - fn private(&mut self) -> PrivateContext { - let mut context = PrivateContext::empty(); - - if self.block_number.is_some() { - context.inputs.historical_header.global_variables.block_number = self.block_number.unwrap_unchecked(); - } - - if self.contract_address.is_some() { - context.inputs.call_context.storage_contract_address = self.contract_address.unwrap_unchecked(); - } - - context - } - - fn public(&mut self) -> PublicContext { - let mut context = PublicContext::empty(); - - if self.block_number.is_some() { - let _ = OracleMock::mock("avmOpcodeBlockNumber").returns(self.block_number.unwrap()); - } - - if self.contract_address.is_some() { - let _ = OracleMock::mock("avmOpcodeAddress").returns(self.contract_address.unwrap()); - } - - context - } -} diff --git a/noir-projects/aztec-nr/aztec/src/test/helpers/test_environment.nr b/noir-projects/aztec-nr/aztec/src/test/helpers/test_environment.nr new file mode 100644 index 000000000000..5dd18c6871c9 --- /dev/null +++ b/noir-projects/aztec-nr/aztec/src/test/helpers/test_environment.nr @@ -0,0 +1,126 @@ +use dep::protocol_types::{ + abis::function_selector::FunctionSelector, address::{AztecAddress, PartialAddress}, + storage::map::derive_storage_slot_in_map, constants::CANONICAL_KEY_REGISTRY_ADDRESS, + grumpkin_point::GrumpkinPoint +}; + +use crate::context::{PrivateContext, PublicContext, PrivateVoidCallInterface}; +use crate::test::helpers::{cheatcodes, types::Deployer}; +use crate::keys::{public_keys::PublicKeys, constants::{NULLIFIER_INDEX, INCOMING_INDEX, OUTGOING_INDEX, TAGGING_INDEX}}; + +struct TestEnvironment { + contract_address: Option, + args_hash: Option, + function_selector: Option +} + +impl TestEnvironment { + fn new() -> Self { + cheatcodes::reset(); + + Self { contract_address: Option::none(), args_hash: Option::none(), function_selector: Option::none() } + } + + fn block_number(self) -> u32 { + cheatcodes::get_block_number() + } + + fn contract_address(&mut self, contract_address: AztecAddress) -> Self { + self.contract_address = Option::some(contract_address); + *self + } + + fn function_selector(&mut self, function_selector: FunctionSelector) -> Self { + self.function_selector = Option::some(function_selector); + *self + } + + fn args_hash(&mut self, args_hash: Field) -> Self { + self.args_hash = Option::some(args_hash); + *self + } + + fn advance_block_to(&mut self, block_number: u32) { + let difference = block_number - cheatcodes::get_block_number(); + self.advance_block_by(difference); + } + + fn advance_block_by(&mut self, blocks: u32) { + cheatcodes::advance_blocks(blocks); + } + + fn public(self) -> PublicContext { + if (self.contract_address.is_some()) { + cheatcodes::set_contract_address(self.contract_address.unwrap_unchecked()); + } + + PublicContext::empty() + } + + fn private(&mut self) -> PrivateContext { + self.private_at(cheatcodes::get_block_number()) + } + + fn private_at(&mut self, historical_block_number: u32) -> PrivateContext { + if historical_block_number >= cheatcodes::get_block_number() { + self.advance_block_to(historical_block_number + 1); + } + + let mut inputs = cheatcodes::get_private_context_inputs(historical_block_number); + + if (self.contract_address.is_some()) { + inputs.call_context.storage_contract_address = self.contract_address.unwrap_unchecked(); + } + + if (self.function_selector.is_some()) { + inputs.call_context.function_selector = self.function_selector.unwrap_unchecked(); + } + + let mut args_hash = 0; + + if (self.args_hash.is_some()) { + args_hash = self.args_hash.unwrap_unchecked(); + } + + PrivateContext::new(inputs, args_hash) + } + + fn store_master_key(self, key_index: Field, address: AztecAddress) -> GrumpkinPoint { + let x_coordinate_map_slot = key_index * 2 + 1; + let y_coordinate_map_slot = x_coordinate_map_slot + 1; + let x_coordinate_derived_slot = derive_storage_slot_in_map(x_coordinate_map_slot, address); + let y_coordinate_derived_slot = derive_storage_slot_in_map(y_coordinate_map_slot, address); + + let canonical_registry_address = AztecAddress::from_field(CANONICAL_KEY_REGISTRY_ADDRESS); + + let fake_key = GrumpkinPoint { x: 1, y: 2 }; + + cheatcodes::direct_storage_write( + canonical_registry_address, + x_coordinate_derived_slot, + [fake_key.x] + ); + + cheatcodes::direct_storage_write( + canonical_registry_address, + y_coordinate_derived_slot, + [fake_key.y] + ); + fake_key + } + + fn get_address_with_keys(self, address: AztecAddress) -> AztecAddress { + let keys = PublicKeys { + npk_m: self.store_master_key(NULLIFIER_INDEX, address), + ivpk_m: self.store_master_key(INCOMING_INDEX, address), + ovpk_m: self.store_master_key(OUTGOING_INDEX, address), + tpk_m: self.store_master_key(TAGGING_INDEX, address) + }; + + AztecAddress::compute(keys.hash(), PartialAddress::from_field(1)) + } + + fn deploy(self, path: str) -> Deployer { + cheatcodes::deploy(path) + } +} diff --git a/noir-projects/aztec-nr/aztec/src/test/helpers/types.nr b/noir-projects/aztec-nr/aztec/src/test/helpers/types.nr new file mode 100644 index 000000000000..e4b43538c341 --- /dev/null +++ b/noir-projects/aztec-nr/aztec/src/test/helpers/types.nr @@ -0,0 +1,42 @@ +use dep::protocol_types::address::AztecAddress; + +use crate::context::inputs::{PublicContextInputs, PrivateContextInputs}; +use crate::context::call_interfaces::CallInterface; +use crate::test::helpers::cheatcodes::{get_private_context_inputs, get_public_context_inputs, deploy_inner, advance_blocks, get_block_number}; + +struct Deployer { + path: str, + } + +impl Deployer { + pub fn with_private_initializer(self, call_interface: C) -> AztecAddress where C: CallInterface { + let address = deploy_inner( + self.path, + call_interface.get_name(), + call_interface.get_args() + ); + advance_blocks(1); + let block_number = get_block_number(); + let original_fn = call_interface.get_original(); + let mut inputs = get_private_context_inputs(block_number - 1); + inputs.call_context.storage_contract_address = address; + inputs.call_context.function_selector = call_interface.get_selector(); + let _result = original_fn(inputs); + address + } + + pub fn with_public_initializer( + self, + call_interface: C + ) -> AztecAddress where C: CallInterface { + let address = deploy_inner( + self.path, + call_interface.get_name(), + call_interface.get_args() + ); + let original_fn = call_interface.get_original(); + let mut inputs = get_public_context_inputs(); + let _result = original_fn(inputs); + address + } +} diff --git a/noir-projects/aztec-nr/aztec/src/test/mocks.nr b/noir-projects/aztec-nr/aztec/src/test/mocks.nr index fb8ef27c7bf9..148cc284a9f0 100644 --- a/noir-projects/aztec-nr/aztec/src/test/mocks.nr +++ b/noir-projects/aztec-nr/aztec/src/test/mocks.nr @@ -1 +1,2 @@ mod mock_note; +mod mock_struct; diff --git a/noir-projects/aztec-nr/aztec/src/test/mocks/mock_struct.nr b/noir-projects/aztec-nr/aztec/src/test/mocks/mock_struct.nr new file mode 100644 index 000000000000..1a0ed1a304ee --- /dev/null +++ b/noir-projects/aztec-nr/aztec/src/test/mocks/mock_struct.nr @@ -0,0 +1,36 @@ +use dep::protocol_types::traits::{Eq, Serialize, Deserialize}; + +struct MockStruct { + a: Field, + b: Field, +} + +impl MockStruct { + fn new(a: Field, b: Field) -> Self { + Self { a, b } + } +} + +impl Eq for MockStruct { + fn eq(self, other: Self) -> bool { + (self.a == other.a) & (self.b == other.b) + } +} + +impl Serialize<2> for MockStruct { + fn serialize(self) -> [Field; 2] { + [self.a, self.b] + } +} + +impl Deserialize<2> for MockStruct { + fn deserialize(fields: [Field; 2]) -> Self { + Self { a: fields[0], b: fields[1] } + } +} + +#[test] +fn test_serde() { + let val = MockStruct::new(5, 6); + assert_eq(val, MockStruct::deserialize(val.serialize())); +} diff --git a/noir-projects/noir-contracts/contracts/counter_contract/src/main.nr b/noir-projects/noir-contracts/contracts/counter_contract/src/main.nr index cf2a235e7987..c3c8cc6981ff 100644 --- a/noir-projects/noir-contracts/contracts/counter_contract/src/main.nr +++ b/noir-projects/noir-contracts/contracts/counter_contract/src/main.nr @@ -37,4 +37,28 @@ contract Counter { balance_utils::get_balance(counters.at(owner).set) } // docs:end:get_counter + + use dep::aztec::test::{helpers::{cheatcodes, test_environment::TestEnvironment}}; + use dep::aztec::protocol_types::storage::map::derive_storage_slot_in_map; + use dep::aztec::oracle::{storage::{storage_read, storage_write}}; + use dep::aztec::note::note_getter::{MAX_NOTES_PER_PAGE, view_notes}; + use dep::aztec::note::note_viewer_options::NoteViewerOptions; + + #[test] + fn test_initialize() { + // Setup env, generate keys + let mut env = TestEnvironment::new(); + let owner = env.get_address_with_keys(AztecAddress::from_field(13)); + let outgoing_viewer = env.get_address_with_keys(AztecAddress::from_field(14)); + + // Deploy contract and initialize + let initializer = Counter::interface().initialize(5, owner, outgoing_viewer); + let _contract_address = env.deploy("@aztec/noir-contracts.js/Counter").with_private_initializer(initializer); + // Read the stored value in the note + let counter_slot = Counter::storage().counters.slot; + let owner_slot = derive_storage_slot_in_map(counter_slot, owner); + let mut options = NoteViewerOptions::new(); + let opt_notes: [Option; MAX_NOTES_PER_PAGE] = view_notes(owner_slot, options); + assert(opt_notes[0].unwrap().value == 5); + } } diff --git a/noir-projects/noir-protocol-circuits/crates/types/src/traits.nr b/noir-projects/noir-protocol-circuits/crates/types/src/traits.nr index bce2078479a6..01af1206fecc 100644 --- a/noir-projects/noir-protocol-circuits/crates/types/src/traits.nr +++ b/noir-projects/noir-protocol-circuits/crates/types/src/traits.nr @@ -110,4 +110,10 @@ impl Serialize for str { trait Deserialize { fn deserialize(fields: [Field; N]) -> Self; } -// docs:end:deserialize \ No newline at end of file +// docs:end:deserialize + +impl Deserialize for [Field; N] { + fn deserialize(fields: [Field; N]) -> Self { + fields + } +} diff --git a/noir/noir-repo/aztec_macros/src/lib.rs b/noir/noir-repo/aztec_macros/src/lib.rs index 2daf86dc6432..217f86c4d9a9 100644 --- a/noir/noir-repo/aztec_macros/src/lib.rs +++ b/noir/noir-repo/aztec_macros/src/lib.rs @@ -160,7 +160,7 @@ fn transform_module( // Apply transformations to the function based on collected attributes if is_private || is_public { let fn_type = if is_private { "Private" } else { "Public" }; - let stub_src = stub_function(fn_type, func, is_static); + let stub_src = stub_function(fn_type, func, is_static, is_initializer); stubs.push((stub_src, Location { file: *file_id, span: func.name_ident().span() })); export_fn_abi(&mut module.types, func)?; diff --git a/noir/noir-repo/aztec_macros/src/transforms/contract_interface.rs b/noir/noir-repo/aztec_macros/src/transforms/contract_interface.rs index 90f9ce6164a7..c63538e36011 100644 --- a/noir/noir-repo/aztec_macros/src/transforms/contract_interface.rs +++ b/noir/noir-repo/aztec_macros/src/transforms/contract_interface.rs @@ -40,7 +40,12 @@ use crate::utils::{ // } // // The selector placeholder has to be replaced with the actual function signature after type checking in the next macro pass -pub fn stub_function(aztec_visibility: &str, func: &NoirFunction, is_static_call: bool) -> String { +pub fn stub_function( + aztec_visibility: &str, + func: &NoirFunction, + is_static_call: bool, + is_initializer: bool, +) -> (String, bool) { let fn_name = func.name().to_string(); let fn_parameters = func .parameters() @@ -61,11 +66,7 @@ pub fn stub_function(aztec_visibility: &str, func: &NoirFunction, is_static_call let parameters = func.parameters(); let is_void = if matches!(fn_return_type.typ, UnresolvedTypeData::Unit) { "Void" } else { "" }; let is_static = if is_static_call { "Static" } else { "" }; - let return_type_hint = if is_void == "Void" { - "".to_string() - } else { - format!("<{}>", fn_return_type.typ.to_string().replace("plain::", "")) - }; + let return_type_hint = fn_return_type.typ.to_string().replace("plain::", ""); let call_args = parameters .iter() .map(|arg| { @@ -74,21 +75,64 @@ pub fn stub_function(aztec_visibility: &str, func: &NoirFunction, is_static_call UnresolvedTypeData::Array(_, typ) => { format!( "let hash_{0} = {0}.map(|x: {1}| x.serialize()); - for i in 0..{0}.len() {{ - args_acc = args_acc.append(hash_{0}[i].as_slice()); - }}\n", + for i in 0..{0}.len() {{ + args_acc = args_acc.append(hash_{0}[i].as_slice()); + }}\n", param_name, typ.typ.to_string().replace("plain::", "") ) } - _ => { + UnresolvedTypeData::Named(_, _, _) | UnresolvedTypeData::String(_) => { format!("args_acc = args_acc.append({}.serialize().as_slice());\n", param_name) } + _ => { + format!("args_acc = args_acc.append(&[{}.to_field()]);\n", param_name) + } } }) .collect::>() .join(""); - if aztec_visibility != "Public" { + + let param_types = if parameters.len() > 0 { + parameters + .iter() + .map(|param| param.pattern.name_ident().0.contents.clone()) + .collect::>() + .join(", ") + } else { + "".to_string() + }; + + let original = format!( + "| inputs: dep::aztec::context::inputs::{}ContextInputs | -> {} {{ + let _ = failing_env_workaround; + {}(inputs{}) + }}", + aztec_visibility, + if aztec_visibility == "Private" { + "dep::aztec::protocol_types::abis::private_circuit_public_inputs::PrivateCircuitPublicInputs".to_string() + } else { + return_type_hint.clone() + }, + fn_name, + if param_types.is_empty() { "".to_string() } else { format!(" ,{} ", param_types) } + ); + let arg_types = format!( + "(Field, {})", + parameters + .iter() + .map(|param| param.typ.typ.to_string().replace("plain::", "")) + .collect::>() + .join(",") + ); + + let generics = if is_void == "Void" { + format!("{}>", arg_types) + } else { + format!("{}, {}>", return_type_hint, arg_types) + }; + + let fn_body = if aztec_visibility != "Public" { let args_hash = if !parameters.is_empty() { format!( "let mut args_acc: [Field] = &[]; @@ -101,20 +145,19 @@ pub fn stub_function(aztec_visibility: &str, func: &NoirFunction, is_static_call "let args_hash = 0;".to_string() }; - let fn_body = format!( - "{} - dep::aztec::context::{}{}{}CallInterface {{ - target_contract: self.target_contract, - selector: {}, - args_hash, - }}", - args_hash, aztec_visibility, is_static, is_void, fn_selector, - ); format!( - "pub fn {}(self, {}) -> dep::aztec::context::{}{}{}CallInterface{} {{ - {} - }}", - fn_name, fn_parameters, aztec_visibility, is_static, is_void, return_type_hint, fn_body + "{} + let failing_env_workaround = 0; + let selector = {}; + dep::aztec::context::{}{}{}CallInterface {{ + target_contract: self.target_contract, + selector, + name: \"{}\", + args_hash, + args: args_acc, + original: {} + }}", + args_hash, fn_selector, aztec_visibility, is_static, is_void, fn_name, original ) } else { let args = format!( @@ -123,23 +166,37 @@ pub fn stub_function(aztec_visibility: &str, func: &NoirFunction, is_static_call ", call_args ); - let fn_body = format!( + format!( "{} - dep::aztec::context::Public{}{}CallInterface {{ + let failing_env_workaround = 0; + let selector = {}; + dep::aztec::context::{}{}{}CallInterface {{ target_contract: self.target_contract, - selector: {}, + selector, + name: \"{}\", args: args_acc, gas_opts: dep::aztec::context::gas::GasOpts::default(), + original: {} }}", - args, is_static, is_void, fn_selector, - ); - format!( - "pub fn {}(self, {}) -> dep::aztec::context::Public{}{}CallInterface{} {{ - {} - }}", - fn_name, fn_parameters, is_static, is_void, return_type_hint, fn_body + args, fn_selector, aztec_visibility, is_static, is_void, fn_name, original ) - } + }; + ( + format!( + "pub fn {}(self, {}) -> dep::aztec::context::{}{}{}CallInterface<{},{} {{ + {} + }}", + fn_name, + fn_parameters, + aztec_visibility, + is_static, + is_void, + fn_name.len(), + generics, + fn_body + ), + is_initializer, + ) } // Generates the contract interface as a struct with an `at` function that holds the stubbed functions and provides @@ -148,7 +205,7 @@ pub fn stub_function(aztec_visibility: &str, func: &NoirFunction, is_static_call pub fn generate_contract_interface( module: &mut SortedModule, module_name: &str, - stubs: &[(String, Location)], + stubs: &[((String, bool), Location)], ) -> Result<(), AztecMacroError> { let contract_interface = format!( " @@ -164,6 +221,14 @@ pub fn generate_contract_interface( ) -> Self {{ Self {{ target_contract }} }} + + pub fn interface() -> Self {{ + Self {{ target_contract: dep::aztec::protocol_types::address::AztecAddress::zero() }} + }} + + pub fn storage() -> StorageLayout {{ + STORAGE_LAYOUT + }} }} #[contract_library_method] @@ -172,9 +237,19 @@ pub fn generate_contract_interface( ) -> {0} {{ {0} {{ target_contract }} }} + + #[contract_library_method] + pub fn interface() -> {0} {{ + {0} {{ target_contract: dep::aztec::protocol_types::address::AztecAddress::zero() }} + }} + + #[contract_library_method] + pub fn storage() -> StorageLayout {{ + STORAGE_LAYOUT + }} ", module_name, - stubs.iter().map(|(src, _)| src.to_owned()).collect::>().join("\n"), + stubs.iter().map(|((src, _), _)| src.to_owned()).collect::>().join("\n"), ); let (contract_interface_ast, errors) = parse_program(&contract_interface); @@ -191,7 +266,7 @@ pub fn generate_contract_interface( .iter() .enumerate() .map(|(i, (method, orig_span))| { - if method.name() == "at" { + if method.name() == "at" || method.name() == "interface" || method.name() == "storage" { (method.clone(), *orig_span) } else { let (_, new_location) = stubs[i]; @@ -205,7 +280,9 @@ pub fn generate_contract_interface( module.types.push(contract_interface_ast.types.pop().unwrap()); module.impls.push(impl_with_locations); - module.functions.push(contract_interface_ast.functions.pop().unwrap()); + for function in contract_interface_ast.functions { + module.functions.push(function); + } Ok(()) } @@ -244,7 +321,7 @@ pub fn update_fn_signatures_in_contract_interface( let name = context.def_interner.function_name(func_id); let fn_parameters = &context.def_interner.function_meta(func_id).parameters.clone(); - if name == "at" { + if name == "at" || name == "interface" || name == "storage" { continue; } @@ -257,42 +334,29 @@ pub fn update_fn_signatures_in_contract_interface( .collect::>(), ); let hir_func = context.def_interner.function(func_id).block(&context.def_interner); - let call_interface_constructor_statement = context.def_interner.statement( - hir_func - .statements() - .last() - .ok_or((AztecMacroError::AztecDepNotFound, file_id))?, + + let function_selector_statement = context.def_interner.statement( + hir_func.statements().get(hir_func.statements().len() - 2).ok_or(( + AztecMacroError::CouldNotGenerateContractInterface { + secondary_message: Some( + "Function signature statement not found, invalid body length" + .to_string(), + ), + }, + file_id, + ))?, ); - let call_interface_constructor_expression = - match call_interface_constructor_statement { - HirStatement::Expression(expression_id) => { - match context.def_interner.expression(&expression_id) { - HirExpression::Constructor(hir_constructor_expression) => { - Ok(hir_constructor_expression) - } - _ => Err(( - AztecMacroError::CouldNotGenerateContractInterface { - secondary_message: Some( - "CallInterface constructor statement must be a constructor expression" - .to_string(), - ), - }, - file_id, - )), - } - } - _ => Err(( - AztecMacroError::CouldNotGenerateContractInterface { - secondary_message: Some( - "CallInterface constructor statement must be an expression" - .to_string(), - ), - }, - file_id, - )), - }?; - let (_, function_selector_expression_id) = - call_interface_constructor_expression.fields[1]; + let function_selector_expression_id = match function_selector_statement { + HirStatement::Let(let_statement) => Ok(let_statement.expression), + _ => Err(( + AztecMacroError::CouldNotGenerateContractInterface { + secondary_message: Some( + "Function selector statement must be an expression".to_string(), + ), + }, + file_id, + )), + }?; let function_selector_expression = context.def_interner.expression(&function_selector_expression_id); diff --git a/noir/noir-repo/aztec_macros/src/transforms/storage.rs b/noir/noir-repo/aztec_macros/src/transforms/storage.rs index a1c21c7efcf3..a46754d02fa3 100644 --- a/noir/noir-repo/aztec_macros/src/transforms/storage.rs +++ b/noir/noir-repo/aztec_macros/src/transforms/storage.rs @@ -504,23 +504,18 @@ pub fn generate_storage_layout( .find(|r#struct| r#struct.name.0.contents == *storage_struct_name) .unwrap(); - let mut generic_args = vec![]; let mut storable_fields = vec![]; let mut storable_fields_impl = vec![]; - definition.fields.iter().enumerate().for_each(|(index, (field_ident, field_type))| { - storable_fields.push(format!("{}: dep::aztec::prelude::Storable", field_ident, index)); - generic_args.push(format!("N{}", index)); - storable_fields_impl.push(format!( - "{}: dep::aztec::prelude::Storable {{ slot: 0, typ: \"{}\" }}", - field_ident, - field_type.to_string().replace("plain::", "") - )); + definition.fields.iter().for_each(|(field_ident, _)| { + storable_fields.push(format!("{}: dep::aztec::prelude::Storable", field_ident)); + storable_fields_impl + .push(format!("{}: dep::aztec::prelude::Storable {{ slot: 0 }}", field_ident,)); }); let storage_fields_source = format!( " - struct StorageLayout<{}> {{ + struct StorageLayout {{ {} }} @@ -529,7 +524,6 @@ pub fn generate_storage_layout( {} }}; ", - generic_args.join(", "), storable_fields.join(",\n"), storable_fields_impl.join(",\n") ); diff --git a/noir/noir-repo/compiler/noirc_frontend/src/monomorphization/mod.rs b/noir/noir-repo/compiler/noirc_frontend/src/monomorphization/mod.rs index a25d6488c831..239f644faff4 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/monomorphization/mod.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/monomorphization/mod.rs @@ -246,7 +246,12 @@ impl<'interner> Monomorphizer<'interner> { } } FunctionKind::Recursive => { - unreachable!("Only main can be specified as recursive, which should already be checked"); + // let func = self.interner.function_meta(&id); + // println!("{:#?}", func.name); + let id = + self.queue_function(id, expr_id, typ, turbofish_generics, trait_method); + Definition::Function(id) + //unreachable!("wtf"); } } } diff --git a/noir/noir-repo/tooling/nargo_cli/src/cli/test_cmd.rs b/noir/noir-repo/tooling/nargo_cli/src/cli/test_cmd.rs index 99c284e5019b..838642b587d7 100644 --- a/noir/noir-repo/tooling/nargo_cli/src/cli/test_cmd.rs +++ b/noir/noir-repo/tooling/nargo_cli/src/cli/test_cmd.rs @@ -82,7 +82,7 @@ pub(crate) fn run(args: TestCommand, config: NargoConfig) -> Result<(), CliError let test_reports: Vec> = workspace .into_iter() - .par_bridge() + //.par_bridge() .map(|package| { run_tests::( &workspace_file_manager, @@ -137,7 +137,7 @@ fn run_tests + Default>( println!("[{}] Running {count_all} test function{plural}", package.name); let test_report: Vec<(String, TestStatus)> = test_functions - .into_par_iter() + .into_iter() .map(|test_name| { let status = run_test::( file_manager, diff --git a/yarn-project/archiver/src/archiver/index.ts b/yarn-project/archiver/src/archiver/index.ts index a7294537624a..81aa8727e172 100644 --- a/yarn-project/archiver/src/archiver/index.ts +++ b/yarn-project/archiver/src/archiver/index.ts @@ -3,3 +3,4 @@ export * from './config.js'; export { MemoryArchiverStore } from './memory_archiver_store/memory_archiver_store.js'; export { ArchiverDataStore } from './archiver_store.js'; export { KVArchiverDataStore } from './kv_archiver_store/kv_archiver_store.js'; +export { ContractInstanceStore } from './kv_archiver_store/contract_instance_store.js'; diff --git a/yarn-project/builder/src/contract-interface-gen/typescript.ts b/yarn-project/builder/src/contract-interface-gen/typescript.ts index 4c2911fba97b..e4519b9e0af2 100644 --- a/yarn-project/builder/src/contract-interface-gen/typescript.ts +++ b/yarn-project/builder/src/contract-interface-gen/typescript.ts @@ -194,10 +194,9 @@ function generateStorageLayoutGetter(input: ContractArtifact) { const storageFieldsUnionType = entries.map(([name]) => `'${name}'`).join(' | '); const layout = entries .map( - ([name, { slot, typ }]) => + ([name, { slot }]) => `${name}: { slot: new Fr(${slot.toBigInt()}n), - typ: "${typ}", }`, ) .join(',\n'); diff --git a/yarn-project/circuits.js/src/contract/contract_instance.ts b/yarn-project/circuits.js/src/contract/contract_instance.ts index 1d45791cdab0..4c047461664d 100644 --- a/yarn-project/circuits.js/src/contract/contract_instance.ts +++ b/yarn-project/circuits.js/src/contract/contract_instance.ts @@ -27,7 +27,6 @@ export 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 initializationHash = computeInitializationHash(constructorArtifact, args); diff --git a/yarn-project/foundation/src/abi/abi.ts b/yarn-project/foundation/src/abi/abi.ts index 200e7b0088c9..2b6080866042 100644 --- a/yarn-project/foundation/src/abi/abi.ts +++ b/yarn-project/foundation/src/abi/abi.ts @@ -290,10 +290,6 @@ export type FieldLayout = { * Slot in which the field is stored. */ slot: Fr; - /** - * Type being stored at the slot (e.g., 'Map>') - */ - typ: string; }; /** diff --git a/yarn-project/package.json b/yarn-project/package.json index fa08f05b063d..388c8f4d6df4 100644 --- a/yarn-project/package.json +++ b/yarn-project/package.json @@ -51,6 +51,7 @@ "sequencer-client", "scripts", "types", + "txe", "world-state" ], "prettier": "@aztec/foundation/prettier", diff --git a/yarn-project/simulator/src/client/execution_note_cache.ts b/yarn-project/simulator/src/client/execution_note_cache.ts index 325e0b8f80b1..e74123138e69 100644 --- a/yarn-project/simulator/src/client/execution_note_cache.ts +++ b/yarn-project/simulator/src/client/execution_note_cache.ts @@ -54,6 +54,7 @@ export class ExecutionNoteCache { let nullifiedNoteHashCounter: number | undefined = undefined; // Find and remove the matching new note and log(s) if the emitted innerNoteHash is not empty. if (!innerNoteHash.equals(Fr.ZERO)) { + console.log('DELETING A NOTE'); const notes = this.newNotes.get(contractAddress.toBigInt()) ?? []; const noteIndexToRemove = notes.findIndex(n => n.note.innerNoteHash.equals(innerNoteHash)); if (noteIndexToRemove === -1) { @@ -62,6 +63,8 @@ export class ExecutionNoteCache { const note = notes.splice(noteIndexToRemove, 1)[0]; nullifiedNoteHashCounter = note.counter; this.newNotes.set(contractAddress.toBigInt(), notes); + } else { + console.log('NOT DELETING A NOTE'); } return nullifiedNoteHashCounter; diff --git a/yarn-project/simulator/src/client/index.ts b/yarn-project/simulator/src/client/index.ts index 60fcf15d4a98..bb30eff13125 100644 --- a/yarn-project/simulator/src/client/index.ts +++ b/yarn-project/simulator/src/client/index.ts @@ -1,3 +1,5 @@ export * from './simulator.js'; export * from './db_oracle.js'; export * from './execution_result.js'; +export * from './pick_notes.js'; +export * from './execution_note_cache.js'; diff --git a/yarn-project/txe/.eslintrc.cjs b/yarn-project/txe/.eslintrc.cjs new file mode 100644 index 000000000000..e659927475c0 --- /dev/null +++ b/yarn-project/txe/.eslintrc.cjs @@ -0,0 +1 @@ +module.exports = require('@aztec/foundation/eslint'); diff --git a/yarn-project/txe/package.json b/yarn-project/txe/package.json new file mode 100644 index 000000000000..871593212404 --- /dev/null +++ b/yarn-project/txe/package.json @@ -0,0 +1,80 @@ +{ + "name": "@aztec/txe", + "version": "0.0.0", + "type": "module", + "exports": "./dest/index.js", + "bin": "./dest/bin/index.js", + "typedocOptions": { + "entryPoints": [ + "./src/index.ts" + ], + "name": "TXE", + "tsconfig": "./tsconfig.json" + }, + "scripts": { + "build": "yarn clean && tsc -b", + "build:dev": "tsc -b --watch", + "clean": "rm -rf ./dest .tsbuildinfo", + "formatting": "run -T prettier --check ./src && run -T eslint ./src", + "formatting:fix": "run -T eslint --fix ./src && run -T prettier -w ./src", + "test": "NODE_NO_WARNINGS=1 node --experimental-vm-modules ../node_modules/.bin/jest --passWithNoTests", + "start": "DEBUG='aztec:*' && node ./dest/bin/index.js" + }, + "inherits": [ + "../package.common.json" + ], + "jest": { + "moduleNameMapper": { + "^(\\.{1,2}/.*)\\.[cm]?js$": "$1" + }, + "testRegex": "./src/.*\\.test\\.(js|mjs|ts)$", + "rootDir": "./src", + "workerThreads": true, + "transform": { + "^.+\\.tsx?$": [ + "@swc/jest" + ] + }, + "extensionsToTreatAsEsm": [ + ".ts" + ], + "reporters": [ + [ + "default", + { + "summaryThreshold": 9999 + } + ] + ] + }, + "dependencies": { + "@aztec/archiver": "workspace:^", + "@aztec/aztec.js": "workspace:^", + "@aztec/circuit-types": "workspace:^", + "@aztec/circuits.js": "workspace:^", + "@aztec/foundation": "workspace:^", + "@aztec/key-store": "workspace:^", + "@aztec/kv-store": "workspace:^", + "@aztec/simulator": "workspace:^", + "@aztec/types": "workspace:^", + "@aztec/world-state": "workspace:^" + }, + "devDependencies": { + "@jest/globals": "^29.5.0", + "@types/jest": "^29.5.0", + "@types/node": "^18.7.23", + "jest": "^29.5.0", + "jest-mock-extended": "^3.0.3", + "ts-node": "^10.9.1", + "typescript": "^5.0.4" + }, + "files": [ + "dest", + "src", + "!*.test.*" + ], + "types": "./dest/index.d.ts", + "engines": { + "node": ">=18" + } +} diff --git a/yarn-project/txe/src/bin/index.ts b/yarn-project/txe/src/bin/index.ts new file mode 100644 index 000000000000..365e2054be7b --- /dev/null +++ b/yarn-project/txe/src/bin/index.ts @@ -0,0 +1,27 @@ +#!/usr/bin/env -S node --no-warnings +import { createDebugLogger } from '@aztec/foundation/log'; + +import { startTXEHttpServer } from '../index.js'; +import { TXEService } from '../txe_service/txe_service.js'; + +const { TXE_PORT = 8080 } = process.env; + +const logger = createDebugLogger('aztec:txe_service'); + +/** + * Create and start a new PXE HTTP Server + */ +async function main() { + logger.info(`Setting up TXE...`); + + const txeService = await TXEService.init(logger); + + startTXEHttpServer(txeService, TXE_PORT); + + logger.info(`TXE listening on port ${TXE_PORT}`); +} + +main().catch(err => { + logger.error(err); + process.exit(1); +}); diff --git a/yarn-project/txe/src/http_rpc_server/index.ts b/yarn-project/txe/src/http_rpc_server/index.ts new file mode 100644 index 000000000000..69d9e08464b9 --- /dev/null +++ b/yarn-project/txe/src/http_rpc_server/index.ts @@ -0,0 +1,29 @@ +import { JsonRpcServer } from '@aztec/foundation/json-rpc/server'; + +import http from 'http'; + +import { type TXEService } from '../txe_service/txe_service.js'; + +/** + * Wraps an instance of Private eXecution Environment (TXE) implementation to a JSON RPC HTTP interface. + * @returns A new instance of the HTTP server. + */ +export function createTXERpcServer(txeService: TXEService): JsonRpcServer { + return new JsonRpcServer(txeService, {}, {}, ['init']); +} + +/** + * Creates an http server that forwards calls to the TXE and starts it on the given port. + * @param txeService - TXE that answers queries to the created HTTP server. + * @param port - Port to listen in. + * @returns A running http server. + */ +export function startTXEHttpServer(txeService: TXEService, port: string | number): http.Server { + const txeServer = createTXERpcServer(txeService); + + const app = txeServer.getApp(); + const httpServer = http.createServer(app.callback()); + httpServer.listen(port); + + return httpServer; +} diff --git a/yarn-project/txe/src/index.ts b/yarn-project/txe/src/index.ts new file mode 100644 index 000000000000..6f83d1f63b2e --- /dev/null +++ b/yarn-project/txe/src/index.ts @@ -0,0 +1 @@ +export * from './http_rpc_server/index.js'; diff --git a/yarn-project/txe/src/oracle/txe_oracle.ts b/yarn-project/txe/src/oracle/txe_oracle.ts new file mode 100644 index 000000000000..59ae24e756d4 --- /dev/null +++ b/yarn-project/txe/src/oracle/txe_oracle.ts @@ -0,0 +1,356 @@ +import { type ContractInstanceStore } from '@aztec/archiver'; +import { + L1NotePayload, + MerkleTreeId, + Note, + type NoteStatus, + type NullifierMembershipWitness, + PublicDataWitness, + PublicDataWrite, + TaggedNote, + type UnencryptedL2Log, +} from '@aztec/circuit-types'; +import { + CompleteAddress, + type Header, + KeyValidationRequest, + NULLIFIER_SUBTREE_HEIGHT, + PUBLIC_DATA_SUBTREE_HEIGHT, + type PUBLIC_DATA_TREE_HEIGHT, + type PrivateCallStackItem, + type PublicCallRequest, + PublicDataTreeLeaf, + type PublicDataTreeLeafPreimage, +} from '@aztec/circuits.js'; +import { Aes128 } from '@aztec/circuits.js/barretenberg'; +import { computePublicDataTreeLeafSlot, siloNoteHash, siloNullifier } from '@aztec/circuits.js/hash'; +import { type FunctionSelector } from '@aztec/foundation/abi'; +import { AztecAddress } from '@aztec/foundation/aztec-address'; +import { Fr, GrumpkinScalar, type Point } from '@aztec/foundation/fields'; +import { type Logger, applyStringFormatting } from '@aztec/foundation/log'; +import { KeyStore } from '@aztec/key-store'; +import { + type ExecutionNoteCache, + type MessageLoadOracleInputs, + type NoteData, + type PackedValuesCache, + type TypedOracle, + pickNotes, +} from '@aztec/simulator'; +import { type ContractInstance } from '@aztec/types/contracts'; +import { MerkleTreeSnapshotOperationsFacade, type MerkleTrees } from '@aztec/world-state'; + +export class TXE implements TypedOracle { + constructor( + private logger: Logger, + private trees: MerkleTrees, + private packedValuesCache: PackedValuesCache, + private noteCache: ExecutionNoteCache, + private contractInstanceStore: ContractInstanceStore, + private keyStore: KeyStore, + private contractAddress: AztecAddress, + ) {} + + setContractAddress(contractAddress: AztecAddress) { + this.contractAddress = contractAddress; + } + + getRandomField() { + return Fr.random(); + } + + packArgumentsArray(args: Fr[]): Promise { + return Promise.resolve(this.packedValuesCache.pack(args)); + } + + packReturns(returns: Fr[]): Promise { + return Promise.resolve(this.packedValuesCache.pack(returns)); + } + + unpackReturns(returnsHash: Fr): Promise { + return Promise.resolve(this.packedValuesCache.unpack(returnsHash)); + } + + getKeyValidationRequest(pkMHash: Fr): Promise { + //return this.keyStore.getKeyValidationRequest(pkMHash, this.contractAddress); + return Promise.resolve(KeyValidationRequest.empty()); + } + + getContractInstance(address: AztecAddress): Promise { + const contractInstance = this.contractInstanceStore.getContractInstance(address); + if (!contractInstance) { + throw new Error(`Contract instance not found for address ${address}`); + } + return Promise.resolve(contractInstance); + } + + getMembershipWitness(_blockNumber: number, _treeId: MerkleTreeId, _leafValue: Fr): Promise { + throw new Error('Method not implemented.'); + } + + async getSiblingPath(blockNumber: number, treeId: MerkleTreeId, leafIndex: Fr) { + const committedDb = new MerkleTreeSnapshotOperationsFacade(this.trees, blockNumber); + const result = await committedDb.getSiblingPath(treeId, leafIndex.toBigInt()); + return result.toFields(); + } + + getNullifierMembershipWitness(_blockNumber: number, _nullifier: Fr): Promise { + throw new Error('Method not implemented.'); + } + + async getPublicDataTreeWitness(blockNumber: number, leafSlot: Fr): Promise { + const committedDb = new MerkleTreeSnapshotOperationsFacade(this.trees, blockNumber); + const lowLeafResult = await committedDb.getPreviousValueIndex(MerkleTreeId.PUBLIC_DATA_TREE, leafSlot.toBigInt()); + if (!lowLeafResult) { + return undefined; + } else { + const preimage = (await committedDb.getLeafPreimage( + MerkleTreeId.PUBLIC_DATA_TREE, + lowLeafResult.index, + )) as PublicDataTreeLeafPreimage; + const path = await committedDb.getSiblingPath( + MerkleTreeId.PUBLIC_DATA_TREE, + lowLeafResult.index, + ); + return new PublicDataWitness(lowLeafResult.index, preimage, path); + } + } + + getLowNullifierMembershipWitness( + _blockNumber: number, + _nullifier: Fr, + ): Promise { + throw new Error('Method not implemented.'); + } + + getHeader(_blockNumber: number): Promise
{ + throw new Error('Method not implemented.'); + } + + getCompleteAddress(account: AztecAddress): Promise { + return Promise.resolve(CompleteAddress.fromSecretKeyAndPartialAddress(Fr.ONE, account)); + } + + getAuthWitness(_messageHash: Fr): Promise { + throw new Error('Method not implemented.'); + } + + popCapsule(): Promise { + throw new Error('Method not implemented.'); + } + + getNotes( + storageSlot: Fr, + numSelects: number, + selectByIndexes: number[], + selectByOffsets: number[], + selectByLengths: number[], + selectValues: Fr[], + selectComparators: number[], + sortByIndexes: number[], + sortByOffsets: number[], + sortByLengths: number[], + sortOrder: number[], + limit: number, + offset: number, + _status: NoteStatus, + ) { + // Nullified pending notes are already removed from the list. + const pendingNotes = this.noteCache.getNotes(this.contractAddress, storageSlot); + + // const pendingNullifiers = this.noteCache.getNullifiers(this.contractAddress); + // const dbNotes = await this.db.getNotes(this.contractAddress, storageSlot, status); + // const dbNotesFiltered = dbNotes.filter(n => !pendingNullifiers.has((n.siloedNullifier as Fr).value)); + + const notes = pickNotes(pendingNotes, { + selects: selectByIndexes.slice(0, numSelects).map((index, i) => ({ + selector: { index, offset: selectByOffsets[i], length: selectByLengths[i] }, + value: selectValues[i], + comparator: selectComparators[i], + })), + sorts: sortByIndexes.map((index, i) => ({ + selector: { index, offset: sortByOffsets[i], length: sortByLengths[i] }, + order: sortOrder[i], + })), + limit, + offset, + }); + + this.logger.debug( + `Returning ${notes.length} notes for ${this.contractAddress} at ${storageSlot}: ${notes + .map(n => `${n.nonce.toString()}:[${n.note.items.map(i => i.toString()).join(',')}]`) + .join(', ')}`, + ); + + return Promise.resolve(notes); + } + + async notifyCreatedNote(storageSlot: Fr, noteTypeId: Fr, noteItems: Fr[], innerNoteHash: Fr, counter: number) { + const note = new Note(noteItems); + this.noteCache.addNewNote( + { + contractAddress: this.contractAddress, + storageSlot, + nonce: Fr.ZERO, // Nonce cannot be known during private execution. + note, + siloedNullifier: undefined, // Siloed nullifier cannot be known for newly created note. + innerNoteHash, + }, + counter, + ); + const db = this.trees.asLatest(); + const noteHash = siloNoteHash(this.contractAddress, innerNoteHash); + await db.appendLeaves(MerkleTreeId.NOTE_HASH_TREE, [noteHash]); + } + + async notifyNullifiedNote(innerNullifier: Fr, innerNoteHash: Fr, _counter: number) { + this.noteCache.nullifyNote(this.contractAddress, innerNullifier, innerNoteHash); + const db = this.trees.asLatest(); + const siloedNullifier = siloNullifier(this.contractAddress, innerNullifier); + await db.batchInsert(MerkleTreeId.NULLIFIER_TREE, [siloedNullifier.toBuffer()], NULLIFIER_SUBTREE_HEIGHT); + return Promise.resolve(); + } + + async checkNullifierExists(innerNullifier: Fr): Promise { + const nullifier = siloNullifier(this.contractAddress, innerNullifier!); + const db = this.trees.asLatest(); + const index = await db.findLeafIndex(MerkleTreeId.NULLIFIER_TREE, nullifier.toBuffer()); + return index !== undefined; + } + + getL1ToL2MembershipWitness( + _contractAddress: AztecAddress, + _messageHash: Fr, + _secret: Fr, + ): Promise> { + throw new Error('Method not implemented.'); + } + + async storageRead(startStorageSlot: Fr, numberOfElements: number): Promise { + const db = this.trees.asLatest(); + + const values = []; + for (let i = 0n; i < numberOfElements; i++) { + const storageSlot = startStorageSlot.add(new Fr(i)); + const leafSlot = computePublicDataTreeLeafSlot(this.contractAddress, storageSlot).toBigInt(); + + const lowLeafResult = await db.getPreviousValueIndex(MerkleTreeId.PUBLIC_DATA_TREE, leafSlot); + + let value = Fr.ZERO; + if (lowLeafResult && lowLeafResult.alreadyPresent) { + const preimage = (await db.getLeafPreimage( + MerkleTreeId.PUBLIC_DATA_TREE, + lowLeafResult.index, + )) as PublicDataTreeLeafPreimage; + value = preimage.value; + } + this.logger.debug(`Oracle storage read: slot=${storageSlot.toString()} value=${value}`); + values.push(value); + } + return values; + } + + async storageWrite(startStorageSlot: Fr, values: Fr[]): Promise { + const db = this.trees.asLatest(); + + 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 PublicDataWrite(computePublicDataTreeLeafSlot(this.contractAddress, storageSlot), value); + }); + await db.batchInsert( + MerkleTreeId.PUBLIC_DATA_TREE, + publicDataWrites.map(write => new PublicDataTreeLeaf(write.leafIndex, write.newValue).toBuffer()), + PUBLIC_DATA_SUBTREE_HEIGHT, + ); + return publicDataWrites.map(write => write.newValue); + } + + emitEncryptedLog(_contractAddress: AztecAddress, _randomness: Fr, _encryptedNote: Buffer, _counter: number): void { + throw new Error('Method not implemented.'); + } + + emitEncryptedNoteLog(_noteHashCounter: number, _encryptedNote: Buffer, _counter: number): void { + throw new Error('Method not implemented.'); + } + + computeEncryptedLog( + contractAddress: AztecAddress, + storageSlot: Fr, + noteTypeId: Fr, + ovKeys: KeyValidationRequest, + ivpkM: Point, + preimage: Fr[], + ): Buffer { + const note = new Note(preimage); + const l1NotePayload = new L1NotePayload(note, contractAddress, storageSlot, noteTypeId); + const taggedNote = new TaggedNote(l1NotePayload); + + const ephSk = GrumpkinScalar.random(); + + const recipient = AztecAddress.random(); + + return taggedNote.encrypt(ephSk, recipient, ivpkM, ovKeys); + } + + emitUnencryptedLog(_log: UnencryptedL2Log, _counter: number): void { + throw new Error('Method not implemented.'); + } + + emitContractClassUnencryptedLog(_log: UnencryptedL2Log, _counter: number): Fr { + throw new Error('Method not implemented.'); + } + + callPrivateFunction( + _targetContractAddress: AztecAddress, + _functionSelector: FunctionSelector, + _argsHash: Fr, + _sideEffectCounter: number, + _isStaticCall: boolean, + _isDelegateCall: boolean, + ): Promise { + throw new Error('Method not implemented.'); + } + + callPublicFunction( + _targetContractAddress: AztecAddress, + _functionSelector: FunctionSelector, + _argsHash: Fr, + _sideEffectCounter: number, + _isStaticCall: boolean, + _isDelegateCall: boolean, + ): Promise { + throw new Error('Method not implemented.'); + } + + enqueuePublicFunctionCall( + _targetContractAddress: AztecAddress, + _functionSelector: FunctionSelector, + _argsHash: Fr, + _sideEffectCounter: number, + _isStaticCall: boolean, + _isDelegateCall: boolean, + ): Promise { + throw new Error('Method not implemented.'); + } + + setPublicTeardownFunctionCall( + _targetContractAddress: AztecAddress, + _functionSelector: FunctionSelector, + _argsHash: Fr, + _sideEffectCounter: number, + _isStaticCall: boolean, + _isDelegateCall: boolean, + ): Promise { + throw new Error('Method not implemented.'); + } + + aes128Encrypt(input: Buffer, initializationVector: Buffer, key: Buffer): Buffer { + const aes128 = new Aes128(); + return aes128.encryptBufferCBC(input, initializationVector, key); + } + + debugLog(message: string, fields: Fr[]): void { + this.logger.verbose(`debug_log ${applyStringFormatting(message, fields)}`); + } +} diff --git a/yarn-project/txe/src/txe_service/txe_service.ts b/yarn-project/txe/src/txe_service/txe_service.ts new file mode 100644 index 000000000000..7da9912a06d7 --- /dev/null +++ b/yarn-project/txe/src/txe_service/txe_service.ts @@ -0,0 +1,431 @@ +import { ContractInstanceStore } from '@aztec/archiver'; +import { L2Block, MerkleTreeId, PublicDataWrite } from '@aztec/circuit-types'; +import { + Fr, + Header, + KeyValidationRequest, + PUBLIC_DATA_SUBTREE_HEIGHT, + Point, + PrivateContextInputs, + PublicDataTreeLeaf, + getContractInstanceFromDeployParams, +} from '@aztec/circuits.js'; +import { computePublicDataTreeLeafSlot } from '@aztec/circuits.js/hash'; +import { AztecAddress } from '@aztec/foundation/aztec-address'; +import { type Logger } from '@aztec/foundation/log'; +import { KeyStore } from '@aztec/key-store'; +import { type AztecKVStore } from '@aztec/kv-store'; +import { openTmpStore } from '@aztec/kv-store/utils'; +import { ExecutionNoteCache, PackedValuesCache, type TypedOracle } from '@aztec/simulator'; +import { MerkleTrees } from '@aztec/world-state'; + +import { TXE } from '../oracle/txe_oracle.js'; +import { + type ForeignCallArray, + type ForeignCallSingle, + fromArray, + fromSingle, + toArray, + toForeignCallResult, + toSingle, +} from '../util/encoding.js'; + +export class TXEService { + private blockNumber = 0; + + constructor( + private logger: Logger, + private typedOracle: TypedOracle, + private store: AztecKVStore, + private trees: MerkleTrees, + private contractInstanceStore: ContractInstanceStore, + private keyStore: KeyStore, + private contractAddress: AztecAddress, + ) {} + + static async init(logger: Logger, contractAddress = AztecAddress.random()) { + const store = openTmpStore(true); + const trees = await MerkleTrees.new(store, logger); + const packedValuesCache = new PackedValuesCache(); + const noteCache = new ExecutionNoteCache(); + const contractInstanceStore = new ContractInstanceStore(store); + const keyStore = new KeyStore(store); + logger.info(`TXE service initialized`); + const txe = new TXE(logger, trees, packedValuesCache, noteCache, contractInstanceStore, keyStore, contractAddress); + const service = new TXEService(logger, txe, store, trees, contractInstanceStore, keyStore, contractAddress); + await service.timeTravel(toSingle(new Fr(1n))); + return service; + } + + // Cheatcodes + + async getPrivateContextInputs(blockNumber: ForeignCallSingle) { + const inputs = PrivateContextInputs.empty(); + const stateReference = await this.trees.getStateReference(true); + inputs.historicalHeader.globalVariables.blockNumber = fromSingle(blockNumber); + inputs.historicalHeader.state = stateReference; + inputs.callContext.msgSender = AztecAddress.random(); + inputs.callContext.storageContractAddress = this.contractAddress; + return toForeignCallResult(inputs.toFields().map(toSingle)); + } + + async timeTravel(blocks: ForeignCallSingle) { + const nBlocks = fromSingle(blocks).toNumber(); + this.logger.info(`time traveling ${nBlocks} blocks`); + for (let i = 0; i < nBlocks; i++) { + const header = Header.empty(); + const l2Block = L2Block.empty(); + header.state = await this.trees.getStateReference(true); + header.globalVariables.blockNumber = new Fr(this.blockNumber); + header.state.partial.nullifierTree.root = Fr.fromBuffer( + (await this.trees.getTreeInfo(MerkleTreeId.NULLIFIER_TREE, true)).root, + ); + header.state.partial.noteHashTree.root = Fr.fromBuffer( + (await this.trees.getTreeInfo(MerkleTreeId.NOTE_HASH_TREE, true)).root, + ); + header.state.partial.publicDataTree.root = Fr.fromBuffer( + (await this.trees.getTreeInfo(MerkleTreeId.PUBLIC_DATA_TREE, true)).root, + ); + header.state.l1ToL2MessageTree.root = Fr.fromBuffer( + (await this.trees.getTreeInfo(MerkleTreeId.L1_TO_L2_MESSAGE_TREE, true)).root, + ); + l2Block.archive.root = Fr.fromBuffer((await this.trees.getTreeInfo(MerkleTreeId.ARCHIVE, true)).root); + l2Block.header = header; + await this.trees.handleL2BlockAndMessages(l2Block, []); + this.blockNumber++; + } + return toForeignCallResult([]); + } + + async reset() { + this.blockNumber = 0; + this.store = openTmpStore(true); + this.trees = await MerkleTrees.new(this.store, this.logger); + this.contractInstanceStore = new ContractInstanceStore(this.store); + this.keyStore = new KeyStore(this.store); + this.typedOracle = new TXE( + this.logger, + this.trees, + new PackedValuesCache(), + new ExecutionNoteCache(), + this.contractInstanceStore, + this.keyStore, + this.contractAddress, + ); + await this.timeTravel(toSingle(new Fr(1))); + return toForeignCallResult([]); + } + + setContractAddress(address: ForeignCallSingle) { + const typedAddress = AztecAddress.fromField(fromSingle(address)); + this.contractAddress = typedAddress; + (this.typedOracle as TXE).setContractAddress(typedAddress); + return toForeignCallResult([]); + } + + async deploy( + path: ForeignCallArray, + initializer: ForeignCallArray, + _length: ForeignCallSingle, + args: ForeignCallArray, + ) { + const pathStr = fromArray(path) + .map(char => String.fromCharCode(char.toNumber())) + .join(''); + const initializerStr = fromArray(initializer) + .map(char => String.fromCharCode(char.toNumber())) + .join(''); + const decodedArgs = fromArray(args); + this.logger.debug(`Deploy ${pathStr} with ${initializerStr} and ${decodedArgs}`); + const contractModule = await import(pathStr); + // Hacky way of getting the class, the name of the Artifact is always longer + const contractClass = contractModule[Object.keys(contractModule).sort((a, b) => a.length - b.length)[0]]; + const instance = getContractInstanceFromDeployParams(contractClass.artifact, { + constructorArgs: decodedArgs, + salt: Fr.ONE, + publicKeysHash: Fr.ZERO, + constructorArtifact: initializerStr, + deployer: AztecAddress.ZERO, + }); + this.logger.debug(`Deployed ${contractClass.artifact.name} at ${instance.address}`); + await this.contractInstanceStore.addContractInstance(instance); + return toForeignCallResult([toSingle(instance.address)]); + } + + async directStorageWrite( + contractAddress: ForeignCallSingle, + startStorageSlot: ForeignCallSingle, + values: ForeignCallArray, + ) { + const startStorageSlotFr = fromSingle(startStorageSlot); + const valuesFr = fromArray(values); + const contractAddressFr = fromSingle(contractAddress); + const db = this.trees.asLatest(); + + 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 PublicDataWrite(computePublicDataTreeLeafSlot(contractAddressFr, storageSlot), value); + }); + await db.batchInsert( + MerkleTreeId.PUBLIC_DATA_TREE, + publicDataWrites.map(write => new PublicDataTreeLeaf(write.leafIndex, write.newValue).toBuffer()), + PUBLIC_DATA_SUBTREE_HEIGHT, + ); + return toForeignCallResult([toArray(publicDataWrites.map(write => write.newValue))]); + } + + // PXE oracles + + getRandomField() { + return toForeignCallResult([toSingle(this.typedOracle.getRandomField())]); + } + + getContractAddress() { + return toForeignCallResult([toSingle(this.contractAddress.toField())]); + } + + getBlockNumber() { + return toForeignCallResult([toSingle(new Fr(this.blockNumber))]); + } + + avmOpcodeAddress() { + return toForeignCallResult([toSingle(this.contractAddress.toField())]); + } + + avmOpcodeBlockNumber() { + return toForeignCallResult([toSingle(new Fr(this.blockNumber))]); + } + + async packArgumentsArray(args: ForeignCallArray) { + const packed = await this.typedOracle.packArgumentsArray(fromArray(args)); + return toForeignCallResult([toSingle(packed)]); + } + + async packArguments(_length: ForeignCallSingle, values: ForeignCallArray) { + const packed = await this.typedOracle.packArgumentsArray(fromArray(values)); + return toForeignCallResult([toSingle(packed)]); + } + + // Since the argument is a slice, noir automatically adds a length field to oracle call. + async packReturns(_length: ForeignCallSingle, values: ForeignCallArray) { + const packed = await this.typedOracle.packReturns(fromArray(values)); + return toForeignCallResult([toSingle(packed)]); + } + + async unpackReturns(returnsHash: ForeignCallSingle) { + const unpacked = await this.typedOracle.unpackReturns(fromSingle(returnsHash)); + return toForeignCallResult([toArray(unpacked)]); + } + + // Since the argument is a slice, noir automatically adds a length field to oracle call. + debugLog(message: ForeignCallArray, _length: ForeignCallSingle, fields: ForeignCallArray) { + const messageStr = fromArray(message) + .map(field => String.fromCharCode(field.toNumber())) + .join(''); + const fieldsFr = fromArray(fields); + this.typedOracle.debugLog(messageStr, fieldsFr); + return toForeignCallResult([]); + } + + async storageRead(startStorageSlot: ForeignCallSingle, numberOfElements: ForeignCallSingle) { + const values = await this.typedOracle.storageRead( + fromSingle(startStorageSlot), + fromSingle(numberOfElements).toNumber(), + ); + return toForeignCallResult([toArray(values)]); + } + + async storageWrite(startStorageSlot: ForeignCallSingle, values: ForeignCallArray) { + const newValues = await this.typedOracle.storageWrite(fromSingle(startStorageSlot), fromArray(values)); + return toForeignCallResult([toArray(newValues)]); + } + + async getPublicDataTreeWitness(blockNumber: ForeignCallSingle, leafSlot: ForeignCallSingle) { + const parsedBlockNumber = fromSingle(blockNumber).toNumber(); + const parsedLeafSlot = fromSingle(leafSlot); + + const witness = await this.typedOracle.getPublicDataTreeWitness(parsedBlockNumber, parsedLeafSlot); + if (!witness) { + throw new Error(`Public data witness not found for slot ${parsedLeafSlot} at block ${parsedBlockNumber}.`); + } + return toForeignCallResult([toArray(witness.toFields())]); + } + + async getSiblingPath(blockNumber: ForeignCallSingle, treeId: ForeignCallSingle, leafIndex: ForeignCallSingle) { + const result = await this.typedOracle.getSiblingPath( + fromSingle(blockNumber).toNumber(), + fromSingle(treeId).toNumber(), + fromSingle(leafIndex), + ); + return toForeignCallResult([toArray(result)]); + } + + async getNotes( + storageSlot: ForeignCallSingle, + numSelects: ForeignCallSingle, + selectByIndexes: ForeignCallArray, + selectByOffsets: ForeignCallArray, + selectByLengths: ForeignCallArray, + selectValues: ForeignCallArray, + selectComparators: ForeignCallArray, + sortByIndexes: ForeignCallArray, + sortByOffsets: ForeignCallArray, + sortByLengths: ForeignCallArray, + sortOrder: ForeignCallArray, + limit: ForeignCallSingle, + offset: ForeignCallSingle, + status: ForeignCallSingle, + returnSize: ForeignCallSingle, + ) { + const noteDatas = await this.typedOracle.getNotes( + fromSingle(storageSlot), + fromSingle(numSelects).toNumber(), + fromArray(selectByIndexes).map(fr => fr.toNumber()), + fromArray(selectByOffsets).map(fr => fr.toNumber()), + fromArray(selectByLengths).map(fr => fr.toNumber()), + fromArray(selectValues), + fromArray(selectComparators).map(fr => fr.toNumber()), + fromArray(sortByIndexes).map(fr => fr.toNumber()), + fromArray(sortByOffsets).map(fr => fr.toNumber()), + fromArray(sortByLengths).map(fr => fr.toNumber()), + fromArray(sortOrder).map(fr => fr.toNumber()), + fromSingle(limit).toNumber(), + fromSingle(offset).toNumber(), + fromSingle(status).toNumber(), + ); + const noteLength = noteDatas?.[0]?.note.items.length ?? 0; + if (!noteDatas.every(({ note }) => noteLength === note.items.length)) { + throw new Error('Notes should all be the same length.'); + } + + const contractAddress = noteDatas[0]?.contractAddress ?? Fr.ZERO; + + // Values indicates whether the note is settled or transient. + const noteTypes = { + isSettled: new Fr(0), + isTransient: new Fr(1), + }; + const flattenData = noteDatas.flatMap(({ nonce, note, index }) => [ + nonce, + index === undefined ? noteTypes.isTransient : noteTypes.isSettled, + ...note.items, + ]); + + const returnFieldSize = fromSingle(returnSize).toNumber(); + const returnData = [noteDatas.length, contractAddress, ...flattenData].map(v => new Fr(v)); + if (returnData.length > returnFieldSize) { + throw new Error(`Return data size too big. Maximum ${returnFieldSize} fields. Got ${flattenData.length}.`); + } + + const paddedZeros = Array(returnFieldSize - returnData.length).fill(new Fr(0)); + return toForeignCallResult([toArray([...returnData, ...paddedZeros])]); + } + + notifyCreatedNote( + storageSlot: ForeignCallSingle, + noteTypeId: ForeignCallSingle, + note: ForeignCallArray, + innerNoteHash: ForeignCallSingle, + counter: ForeignCallSingle, + ) { + this.typedOracle.notifyCreatedNote( + fromSingle(storageSlot), + fromSingle(noteTypeId), + fromArray(note), + fromSingle(innerNoteHash), + fromSingle(counter).toNumber(), + ); + return toForeignCallResult([toSingle(new Fr(0))]); + } + + async notifyNullifiedNote( + innerNullifier: ForeignCallSingle, + innerNoteHash: ForeignCallSingle, + counter: ForeignCallSingle, + ) { + await this.typedOracle.notifyNullifiedNote( + fromSingle(innerNullifier), + fromSingle(innerNoteHash), + fromSingle(counter).toNumber(), + ); + return toForeignCallResult([toSingle(new Fr(0))]); + } + + async checkNullifierExists(innerNullifier: ForeignCallSingle) { + const exists = await this.typedOracle.checkNullifierExists(fromSingle(innerNullifier)); + return toForeignCallResult([toSingle(new Fr(exists))]); + } + + async getContractInstance(address: ForeignCallSingle) { + const instance = await this.typedOracle.getContractInstance(fromSingle(address)); + return toForeignCallResult([ + toArray([ + instance.salt, + instance.deployer, + instance.contractClassId, + instance.initializationHash, + instance.publicKeysHash, + ]), + ]); + } + + async getPublicKeysAndPartialAddress(address: ForeignCallSingle) { + const parsedAddress = AztecAddress.fromField(fromSingle(address)); + const { publicKeys, partialAddress } = await this.typedOracle.getCompleteAddress(parsedAddress); + + return toForeignCallResult([toArray([...publicKeys.toFields(), partialAddress])]); + } + + async getKeyValidationRequest(pkMHash: ForeignCallSingle) { + const keyValidationRequest = await this.typedOracle.getKeyValidationRequest(fromSingle(pkMHash)); + return toForeignCallResult([toArray(keyValidationRequest.toFields())]); + } + + computeEncryptedLog( + contractAddress: ForeignCallSingle, + storageSlot: ForeignCallSingle, + noteTypeId: ForeignCallSingle, + ovskApp: ForeignCallSingle, + ovpkMX: ForeignCallSingle, + ovpkMY: ForeignCallSingle, + ivpkMX: ForeignCallSingle, + ivpkMY: ForeignCallSingle, + preimage: ForeignCallArray, + ) { + const ovpkM = new Point(fromSingle(ovpkMX), fromSingle(ovpkMY)); + const ovKeys = new KeyValidationRequest(ovpkM, Fr.fromString(fromSingle(ovskApp).toString())); + const ivpkM = new Point(fromSingle(ivpkMX), fromSingle(ivpkMY)); + const encLog = this.typedOracle.computeEncryptedLog( + AztecAddress.fromString(fromSingle(contractAddress).toString()), + Fr.fromString(fromSingle(storageSlot).toString()), + Fr.fromString(fromSingle(noteTypeId).toString()), + ovKeys, + ivpkM, + fromArray(preimage), + ); + const bytes: Fr[] = []; + + encLog.forEach(v => { + bytes.push(new Fr(v)); + }); + return toForeignCallResult([toArray(bytes)]); + } + + emitEncryptedLog( + _contractAddress: ForeignCallSingle, + _randomandomness: ForeignCallSingle, + _encryptedLog: ForeignCallSingle, + _counter: ForeignCallSingle, + ) { + return toForeignCallResult([]); + } + + emitEncryptedNoteLog( + _noteHashCounter: ForeignCallSingle, + _encryptedNote: ForeignCallArray, + _counter: ForeignCallSingle, + ) { + return toForeignCallResult([]); + } +} diff --git a/yarn-project/txe/src/util/encoding.ts b/yarn-project/txe/src/util/encoding.ts new file mode 100644 index 000000000000..42dc32b88b4c --- /dev/null +++ b/yarn-project/txe/src/util/encoding.ts @@ -0,0 +1,29 @@ +import { Fr } from '@aztec/foundation/fields'; + +export type ForeignCallSingle = { + Single: string; +}; + +export type ForeignCallArray = { + Array: string[]; +}; + +export function fromSingle(obj: ForeignCallSingle) { + return Fr.fromBuffer(Buffer.from(obj.Single, 'hex')); +} + +export function fromArray(obj: ForeignCallArray) { + return obj.Array.map(str => Fr.fromBuffer(Buffer.from(str, 'hex'))); +} + +export function toSingle(obj: Fr) { + return { Single: obj.toString().slice(2) }; +} + +export function toArray(objs: Fr[]) { + return { Array: objs.map(obj => obj.toString()) }; +} + +export function toForeignCallResult(obj: (ForeignCallSingle | ForeignCallArray)[]) { + return { values: obj }; +} diff --git a/yarn-project/txe/tsconfig.json b/yarn-project/txe/tsconfig.json new file mode 100644 index 000000000000..effb5a7151c9 --- /dev/null +++ b/yarn-project/txe/tsconfig.json @@ -0,0 +1,41 @@ +{ + "extends": "..", + "compilerOptions": { + "outDir": "dest", + "rootDir": "src", + "tsBuildInfoFile": ".tsbuildinfo" + }, + "references": [ + { + "path": "../circuit-types" + }, + { + "path": "../circuits.js" + }, + { + "path": "../foundation" + }, + { + "path": "../noir-protocol-circuits-types" + }, + { + "path": "../protocol-contracts" + }, + { + "path": "../types" + }, + { + "path": "../world-state" + }, + { + "path": "../kv-store" + }, + { + "path": "../merkle-tree" + }, + { + "path": "../noir-contracts.js" + } + ], + "include": ["src"] +} diff --git a/yarn-project/types/src/abi/contract_artifact.ts b/yarn-project/types/src/abi/contract_artifact.ts index 8ca72aa68a75..9fff2b21b6a4 100644 --- a/yarn-project/types/src/abi/contract_artifact.ts +++ b/yarn-project/types/src/abi/contract_artifact.ts @@ -2,7 +2,6 @@ import { type ABIParameter, type ABIParameterVisibility, type AbiType, - type BasicValue, type ContractArtifact, type ContractNote, type FieldLayout, @@ -227,10 +226,8 @@ function getStorageLayout(input: NoirCompiledContract) { return storageFields.reduce((acc: Record, field) => { const name = field.name; const slot = field.value.fields[0].value as IntegerValue; - const typ = field.value.fields[1].value as BasicValue<'string', string>; acc[name] = { slot: new Fr(BigInt(slot.value)), - typ: typ.value, }; return acc; }, {}); diff --git a/yarn-project/world-state/src/world-state-db/index.ts b/yarn-project/world-state/src/world-state-db/index.ts index 9d72e0991e84..f4c20a567f9e 100644 --- a/yarn-project/world-state/src/world-state-db/index.ts +++ b/yarn-project/world-state/src/world-state-db/index.ts @@ -1,3 +1,5 @@ export * from './merkle_trees.js'; export * from './merkle_tree_db.js'; export * from './merkle_tree_operations.js'; +export * from './merkle_tree_operations_facade.js'; +export * from './merkle_tree_snapshot_operations_facade.js'; diff --git a/yarn-project/yarn.lock b/yarn-project/yarn.lock index d1fe44fd9dfd..321559b00b05 100644 --- a/yarn-project/yarn.lock +++ b/yarn-project/yarn.lock @@ -933,6 +933,32 @@ __metadata: languageName: unknown linkType: soft +"@aztec/txe@workspace:txe": + version: 0.0.0-use.local + resolution: "@aztec/txe@workspace:txe" + dependencies: + "@aztec/archiver": "workspace:^" + "@aztec/aztec.js": "workspace:^" + "@aztec/circuit-types": "workspace:^" + "@aztec/circuits.js": "workspace:^" + "@aztec/foundation": "workspace:^" + "@aztec/key-store": "workspace:^" + "@aztec/kv-store": "workspace:^" + "@aztec/simulator": "workspace:^" + "@aztec/types": "workspace:^" + "@aztec/world-state": "workspace:^" + "@jest/globals": ^29.5.0 + "@types/jest": ^29.5.0 + "@types/node": ^18.7.23 + jest: ^29.5.0 + jest-mock-extended: ^3.0.3 + ts-node: ^10.9.1 + typescript: ^5.0.4 + bin: + txe: ./dest/bin/index.js + languageName: unknown + linkType: soft + "@aztec/types@workspace:^, @aztec/types@workspace:types": version: 0.0.0-use.local resolution: "@aztec/types@workspace:types" From 6083adea78b753e440041fd121bb55589d30821f Mon Sep 17 00:00:00 2001 From: thunkar Date: Mon, 10 Jun 2024 13:52:50 +0000 Subject: [PATCH 33/60] fixes --- .vscode/settings.json | 8 +++--- noir/noir-repo/aztec_macros/src/lib.rs | 10 ++++---- .../src/transforms/contract_interface.rs | 25 +++++++++++++------ .../aztec_macros/src/transforms/storage.rs | 4 ++- 4 files changed, 30 insertions(+), 17 deletions(-) diff --git a/.vscode/settings.json b/.vscode/settings.json index ab8056e8a7c3..a8ab844ebe13 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -158,13 +158,15 @@ "avm-transpiler/Cargo.toml" ], "files.watcherExclude": { - "**/.git/objects/**": true, - "**/.git/subtree-cache/**": true, + "**/.git/**": true, "**/node_modules/**": true, "**/.hg/store/**": true, "**/target/**": true, "**/l1-contracts/lib/**": true, - "**/barretenberg/cpp/build*/**": true + "**/barretenberg/cpp/build*/**": true, + "**/barretenberg/cpp/src/wasi-sdk*/**": true, + "**/dest/**": true, + "**/noir/noir-repo/docs/versioned_docs/**": true }, "cmake.sourceDirectory": "${workspaceFolder}/barretenberg/cpp", "noir.nargoPath": "./noir/noir-repo/target/release/nargo" diff --git a/noir/noir-repo/aztec_macros/src/lib.rs b/noir/noir-repo/aztec_macros/src/lib.rs index 217f86c4d9a9..46eb95604a32 100644 --- a/noir/noir-repo/aztec_macros/src/lib.rs +++ b/noir/noir-repo/aztec_macros/src/lib.rs @@ -13,7 +13,7 @@ use transforms::{ }, note_interface::{generate_note_interface_impl, inject_note_exports}, storage::{ - assign_storage_slots, check_for_storage_definition, check_for_storage_implementation, + self, assign_storage_slots, check_for_storage_definition, check_for_storage_implementation, generate_storage_implementation, generate_storage_layout, inject_context_in_storage, }, }; @@ -109,9 +109,9 @@ fn transform_module( // Make sure we're only generating the storage layout for the root crate // In case we got a contract importing other contracts for their interface, we // don't want to generate the storage layout for them - if crate_id == context.root_crate_id() { - generate_storage_layout(module, storage_struct_name.clone())?; - } + // if crate_id == context.root_crate_id() { + generate_storage_layout(module, storage_struct_name.clone(), module_name)?; + //} } for structure in module.types.iter_mut() { @@ -219,7 +219,7 @@ fn transform_module( }); } - generate_contract_interface(module, module_name, &stubs)?; + generate_contract_interface(module, module_name, &stubs, storage_defined)?; } Ok(has_transformed_module) diff --git a/noir/noir-repo/aztec_macros/src/transforms/contract_interface.rs b/noir/noir-repo/aztec_macros/src/transforms/contract_interface.rs index c63538e36011..da89cbe608ec 100644 --- a/noir/noir-repo/aztec_macros/src/transforms/contract_interface.rs +++ b/noir/noir-repo/aztec_macros/src/transforms/contract_interface.rs @@ -142,7 +142,11 @@ pub fn stub_function( call_args ) } else { - "let args_hash = 0;".to_string() + " + let mut args_acc: [Field] = &[]; + let args_hash = 0; + " + .to_string() }; format!( @@ -206,7 +210,15 @@ pub fn generate_contract_interface( module: &mut SortedModule, module_name: &str, stubs: &[((String, bool), Location)], + has_storage_layout: bool, ) -> Result<(), AztecMacroError> { + let storage_layout_getter = format!( + "#[contract_library_method] + pub fn storage() -> StorageLayout {{ + {}_STORAGE_LAYOUT + }}", + module_name, + ); let contract_interface = format!( " struct {0} {{ @@ -226,9 +238,7 @@ pub fn generate_contract_interface( Self {{ target_contract: dep::aztec::protocol_types::address::AztecAddress::zero() }} }} - pub fn storage() -> StorageLayout {{ - STORAGE_LAYOUT - }} + {2} }} #[contract_library_method] @@ -243,13 +253,12 @@ pub fn generate_contract_interface( {0} {{ target_contract: dep::aztec::protocol_types::address::AztecAddress::zero() }} }} - #[contract_library_method] - pub fn storage() -> StorageLayout {{ - STORAGE_LAYOUT - }} + {3} ", module_name, stubs.iter().map(|((src, _), _)| src.to_owned()).collect::>().join("\n"), + if has_storage_layout { storage_layout_getter.clone() } else { "".to_string() }, + if has_storage_layout { format!("#[contract_library_method]\n{}", storage_layout_getter) } else { "".to_string() } ); let (contract_interface_ast, errors) = parse_program(&contract_interface); diff --git a/noir/noir-repo/aztec_macros/src/transforms/storage.rs b/noir/noir-repo/aztec_macros/src/transforms/storage.rs index a46754d02fa3..bac87502c7d1 100644 --- a/noir/noir-repo/aztec_macros/src/transforms/storage.rs +++ b/noir/noir-repo/aztec_macros/src/transforms/storage.rs @@ -497,6 +497,7 @@ pub fn assign_storage_slots( pub fn generate_storage_layout( module: &mut SortedModule, storage_struct_name: String, + module_name: &str, ) -> Result<(), AztecMacroError> { let definition = module .types @@ -520,11 +521,12 @@ pub fn generate_storage_layout( }} #[abi(storage)] - global STORAGE_LAYOUT = StorageLayout {{ + global {}_STORAGE_LAYOUT = StorageLayout {{ {} }}; ", storable_fields.join(",\n"), + module_name, storable_fields_impl.join(",\n") ); From b80f6c7fc0a0f9217db73f8d978fc1fc1d954d3f Mon Sep 17 00:00:00 2001 From: thunkar Date: Mon, 10 Jun 2024 15:21:32 +0000 Subject: [PATCH 34/60] fixes --- .vscode/settings.json | 1 - .../aztec/src/state_vars/private_immutable.nr | 13 +++---- .../aztec/src/state_vars/public_immutable.nr | 37 ++++++++++--------- .../shared_mutable/shared_mutable.nr | 15 +++++--- .../contracts/test_contract/src/main.nr | 6 +-- noir/noir-repo/aztec_macros/src/lib.rs | 21 ++--------- .../src/transforms/contract_interface.rs | 37 ++++++++----------- 7 files changed, 55 insertions(+), 75 deletions(-) diff --git a/.vscode/settings.json b/.vscode/settings.json index a8ab844ebe13..86a0427f7901 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -169,5 +169,4 @@ "**/noir/noir-repo/docs/versioned_docs/**": true }, "cmake.sourceDirectory": "${workspaceFolder}/barretenberg/cpp", - "noir.nargoPath": "./noir/noir-repo/target/release/nargo" } diff --git a/noir-projects/aztec-nr/aztec/src/state_vars/private_immutable.nr b/noir-projects/aztec-nr/aztec/src/state_vars/private_immutable.nr index 6e1d4f6ccd45..967df14e52f0 100644 --- a/noir-projects/aztec-nr/aztec/src/state_vars/private_immutable.nr +++ b/noir-projects/aztec-nr/aztec/src/state_vars/private_immutable.nr @@ -50,8 +50,7 @@ impl PrivateImmutable { ovpk_m: GrumpkinPoint, ivpk_m: GrumpkinPoint ) where Note: NoteInterface { - // Nullify the storage slot - since we always push the same nullifier, this can only be done once, as further - // attempts to do it will result in the transaction being reverted by the sequencer. + // Nullify the storage slot. let nullifier = self.compute_initialization_nullifier(); self.context.push_new_nullifier(nullifier, 0); @@ -61,10 +60,8 @@ impl PrivateImmutable { // docs:start:get_note pub fn get_note(self) -> Note where Note: NoteInterface { - // We do not need to check for initialization because we'll only be able to read a note and prove its inclusion - // if one was actually created, and this can be done during initialization. Therefore, a successful read implies - // initialization. - get_note(self.context, self.storage_slot) + let storage_slot = self.storage_slot; + get_note(self.context, storage_slot) } // docs:end:get_note } @@ -80,8 +77,8 @@ impl PrivateImmutable { // view_note does not actually use the context, but it calls oracles that are only available in private // docs:start:view_note unconstrained pub fn view_note(self) -> Note where Note: NoteInterface { - let options = NoteViewerOptions::new(); - view_notes(self.storage_slot, options)[0].unwrap() + let mut options = NoteViewerOptions::new(); + view_notes(self.storage_slot, options.set_limit(1))[0].unwrap() } // docs:end:view_note } diff --git a/noir-projects/aztec-nr/aztec/src/state_vars/public_immutable.nr b/noir-projects/aztec-nr/aztec/src/state_vars/public_immutable.nr index f6cb509f432c..b8f2c1d2dde6 100644 --- a/noir-projects/aztec-nr/aztec/src/state_vars/public_immutable.nr +++ b/noir-projects/aztec-nr/aztec/src/state_vars/public_immutable.nr @@ -4,8 +4,6 @@ use crate::{ }; use dep::protocol_types::{constants::INITIALIZATION_SLOT_SEPARATOR, traits::{Deserialize, Serialize}}; -mod test; - // Just like SharedImmutable but without the ability to read from private functions. // docs:start:public_immutable_struct struct PublicImmutable { @@ -18,42 +16,45 @@ impl Storage for PublicImmutable {} impl PublicImmutable { // docs:start:public_immutable_struct_new - pub fn new(context: Context, storage_slot: Field) -> Self { + pub fn new( + // Note: Passing the contexts to new(...) just to have an interface compatible with a Map. + context: Context, + storage_slot: Field + ) -> Self { assert(storage_slot != 0, "Storage slot 0 not allowed. Storage slots must start from 1."); PublicImmutable { context, storage_slot } } // docs:end:public_immutable_struct_new - - fn get_initialization_slot(self) -> Field { - INITIALIZATION_SLOT_SEPARATOR + self.storage_slot - } } impl PublicImmutable { // docs:start:public_immutable_struct_write pub fn initialize(self, value: T) where T: Serialize { - assert(!self.is_initialized(), "PublicImmutable already initialized"); + // TODO(#4738): Uncomment the following assert + // assert( + // self.context.public.unwrap_unchecked().is_deployment(), "PublicImmutable can only be initialized during contract deployment" + // ); + + // We check that the struct is not yet initialized by checking if the initialization slot is 0 + let initialization_slot = INITIALIZATION_SLOT_SEPARATOR + self.storage_slot; + let fields_read: [Field; 1] = storage_read(initialization_slot); + assert(fields_read[0] == 0, "PublicImmutable already initialized"); // We populate the initialization slot with a non-zero value to indicate that the struct is initialized - public_storage::write(self.get_initialization_slot(), 0xdead); + storage_write(initialization_slot, [0xdead]); - // And we then store the actual value - public_storage::write(self.storage_slot, value); + let fields_write = T::serialize(value); + storage_write(self.storage_slot, fields_write); } // docs:end:public_immutable_struct_write // Note that we don't access the context, but we do call oracles that are only available in public // docs:start:public_immutable_struct_read pub fn read(self) -> T where T: Deserialize { - assert(self.is_initialized(), "PublicImmutable not initialized"); - public_storage::read(self.storage_slot) + let fields = storage_read(self.storage_slot); + T::deserialize(fields) } // docs:end:public_immutable_struct_read - - pub fn is_initialized(self) -> bool { - let init_slot_contents: Field = public_storage::read(self.get_initialization_slot()); - init_slot_contents != 0 - } } impl PublicImmutable { diff --git a/noir-projects/aztec-nr/aztec/src/state_vars/shared_mutable/shared_mutable.nr b/noir-projects/aztec-nr/aztec/src/state_vars/shared_mutable/shared_mutable.nr index 38f36ae41db3..91c864a03b23 100644 --- a/noir-projects/aztec-nr/aztec/src/state_vars/shared_mutable/shared_mutable.nr +++ b/noir-projects/aztec-nr/aztec/src/state_vars/shared_mutable/shared_mutable.nr @@ -121,7 +121,7 @@ impl SharedMutable { // will only be valid for however many blocks we can ensure the value will not change, which will depend on the // current delay and any scheduled delay changes. - let (value_change, delay_change, historical_block_number) = self.historical_read_from_public_storage(); + let (value_change, delay_change, historical_block_number) = self.historical_read_from_public_storage(*self.context); // We use the effective minimum delay as opposed to the current delay at the historical block as this one also // takes into consideration any scheduled delay changes. @@ -139,26 +139,29 @@ impl SharedMutable { value_change.get_current_at(historical_block_number) } - fn historical_read_from_public_storage(self) -> (ScheduledValueChange, ScheduledDelayChange, u32) where T: FromField { - let header = self.context.get_header(); + fn historical_read_from_public_storage( + self, + context: PrivateContext + ) -> (ScheduledValueChange, ScheduledDelayChange, u32) where T: FromField { + let header = context.get_header(); // Ideally the following would be simply public_storage::read_historical, but we can't implement that yet. let value_change_slot = self.get_value_change_storage_slot(); let mut raw_value_change_fields = [0; 3]; for i in 0..3 { raw_value_change_fields[i] = header.public_storage_historical_read( value_change_slot + i as Field, - self.context.this_address() + context.this_address() ); } // Ideally the following would be simply public_storage::read_historical, but we can't implement that yet. let delay_change_slot = self.get_delay_change_storage_slot(); - let raw_delay_change_fields = [header.public_storage_historical_read(delay_change_slot, self.context.this_address())]; + let raw_delay_change_fields = [header.public_storage_historical_read(delay_change_slot, context.this_address())]; let value_change = ScheduledValueChange::deserialize(raw_value_change_fields); let delay_change = ScheduledDelayChange::deserialize(raw_delay_change_fields); - let historical_block_number = self.context.historical_header.global_variables.block_number as u32; + let historical_block_number = context.historical_header.global_variables.block_number as u32; (value_change, delay_change, historical_block_number) } diff --git a/noir-projects/noir-contracts/contracts/test_contract/src/main.nr b/noir-projects/noir-contracts/contracts/test_contract/src/main.nr index d1329c33dd04..5f45f2b1a75e 100644 --- a/noir-projects/noir-contracts/contracts/test_contract/src/main.nr +++ b/noir-projects/noir-contracts/contracts/test_contract/src/main.nr @@ -506,12 +506,12 @@ contract Test { // This function is used in the e2e_state_vars to test the SharedMutablePrivateGetter in isolation #[aztec(private)] - fn test_shared_mutable_private_getter( + fn test_shared_mutable_private_getter( contract_address_to_read: AztecAddress, storage_slot_of_shared_mutable: Field - ) -> Field where T: FromField, T: ToField { + ) -> Field { // It's a bit wonky because we need to know the delay for get_current_value_in_private to work correctly - let test: SharedMutablePrivateGetter = SharedMutablePrivateGetter::new( + let test: SharedMutablePrivateGetter = SharedMutablePrivateGetter::new( &mut context, contract_address_to_read, storage_slot_of_shared_mutable diff --git a/noir/noir-repo/aztec_macros/src/lib.rs b/noir/noir-repo/aztec_macros/src/lib.rs index 46eb95604a32..2f4244981a45 100644 --- a/noir/noir-repo/aztec_macros/src/lib.rs +++ b/noir/noir-repo/aztec_macros/src/lib.rs @@ -13,7 +13,7 @@ use transforms::{ }, note_interface::{generate_note_interface_impl, inject_note_exports}, storage::{ - self, assign_storage_slots, check_for_storage_definition, check_for_storage_implementation, + assign_storage_slots, check_for_storage_definition, check_for_storage_implementation, generate_storage_implementation, generate_storage_layout, inject_context_in_storage, }, }; @@ -65,14 +65,8 @@ fn transform( // Usage -> mut ast -> aztec_library::transform(&mut ast) // Covers all functions in the ast for submodule in ast.submodules.iter_mut().filter(|submodule| submodule.is_contract) { - if transform_module( - crate_id, - &file_id, - context, - &mut submodule.contents, - submodule.name.0.contents.as_str(), - ) - .map_err(|err| (err.into(), file_id))? + if transform_module(&file_id, &mut submodule.contents, submodule.name.0.contents.as_str()) + .map_err(|err| (err.into(), file_id))? { check_for_aztec_dependency(crate_id, context)?; } @@ -87,9 +81,7 @@ fn transform( /// For annotated functions it calls the `transform` function which will perform the required transformations. /// Returns true if an annotated node is found, false otherwise fn transform_module( - crate_id: &CrateId, file_id: &FileId, - context: &HirContext, module: &mut SortedModule, module_name: &str, ) -> Result { @@ -106,12 +98,7 @@ fn transform_module( if !check_for_storage_implementation(module, storage_struct_name) { generate_storage_implementation(module, storage_struct_name)?; } - // Make sure we're only generating the storage layout for the root crate - // In case we got a contract importing other contracts for their interface, we - // don't want to generate the storage layout for them - // if crate_id == context.root_crate_id() { generate_storage_layout(module, storage_struct_name.clone(), module_name)?; - //} } for structure in module.types.iter_mut() { @@ -160,7 +147,7 @@ fn transform_module( // Apply transformations to the function based on collected attributes if is_private || is_public { let fn_type = if is_private { "Private" } else { "Public" }; - let stub_src = stub_function(fn_type, func, is_static, is_initializer); + let stub_src = stub_function(fn_type, func, is_static); stubs.push((stub_src, Location { file: *file_id, span: func.name_ident().span() })); export_fn_abi(&mut module.types, func)?; diff --git a/noir/noir-repo/aztec_macros/src/transforms/contract_interface.rs b/noir/noir-repo/aztec_macros/src/transforms/contract_interface.rs index da89cbe608ec..5e3daced44dd 100644 --- a/noir/noir-repo/aztec_macros/src/transforms/contract_interface.rs +++ b/noir/noir-repo/aztec_macros/src/transforms/contract_interface.rs @@ -40,12 +40,7 @@ use crate::utils::{ // } // // The selector placeholder has to be replaced with the actual function signature after type checking in the next macro pass -pub fn stub_function( - aztec_visibility: &str, - func: &NoirFunction, - is_static_call: bool, - is_initializer: bool, -) -> (String, bool) { +pub fn stub_function(aztec_visibility: &str, func: &NoirFunction, is_static_call: bool) -> String { let fn_name = func.name().to_string(); let fn_parameters = func .parameters() @@ -93,7 +88,7 @@ pub fn stub_function( .collect::>() .join(""); - let param_types = if parameters.len() > 0 { + let param_types = if !parameters.is_empty() { parameters .iter() .map(|param| param.pattern.name_ident().0.contents.clone()) @@ -185,21 +180,19 @@ pub fn stub_function( args, fn_selector, aztec_visibility, is_static, is_void, fn_name, original ) }; - ( - format!( - "pub fn {}(self, {}) -> dep::aztec::context::{}{}{}CallInterface<{},{} {{ + + format!( + "pub fn {}(self, {}) -> dep::aztec::context::{}{}{}CallInterface<{},{} {{ {} }}", - fn_name, - fn_parameters, - aztec_visibility, - is_static, - is_void, - fn_name.len(), - generics, - fn_body - ), - is_initializer, + fn_name, + fn_parameters, + aztec_visibility, + is_static, + is_void, + fn_name.len(), + generics, + fn_body ) } @@ -209,7 +202,7 @@ pub fn stub_function( pub fn generate_contract_interface( module: &mut SortedModule, module_name: &str, - stubs: &[((String, bool), Location)], + stubs: &[(String, Location)], has_storage_layout: bool, ) -> Result<(), AztecMacroError> { let storage_layout_getter = format!( @@ -256,7 +249,7 @@ pub fn generate_contract_interface( {3} ", module_name, - stubs.iter().map(|((src, _), _)| src.to_owned()).collect::>().join("\n"), + stubs.iter().map(|(src, _)| src.to_owned()).collect::>().join("\n"), if has_storage_layout { storage_layout_getter.clone() } else { "".to_string() }, if has_storage_layout { format!("#[contract_library_method]\n{}", storage_layout_getter) } else { "".to_string() } ); From ca42523b5807cdce1e463f651a6b8107772e6b7d Mon Sep 17 00:00:00 2001 From: thunkar Date: Tue, 11 Jun 2024 08:38:29 +0000 Subject: [PATCH 35/60] account abstraction --- .../aztec/src/context/private_context.nr | 2 +- .../aztec-nr/aztec/src/keys/getters.nr | 2 +- .../aztec/src/test/helpers/cheatcodes.nr | 19 ++-- .../src/test/helpers/test_environment.nr | 43 +++---- .../aztec-nr/aztec/src/test/helpers/types.nr | 56 +++++++-- .../contracts/counter_contract/src/main.nr | 4 +- yarn-project/txe/src/oracle/txe_oracle.ts | 50 +++++++- .../txe/src/txe_service/txe_service.ts | 107 +++++++++++------- yarn-project/txe/src/util/account_store.ts | 19 ++++ 9 files changed, 205 insertions(+), 97 deletions(-) create mode 100644 yarn-project/txe/src/util/account_store.ts diff --git a/noir-projects/aztec-nr/aztec/src/context/private_context.nr b/noir-projects/aztec-nr/aztec/src/context/private_context.nr index e0f31144ed28..aeba73e0554a 100644 --- a/noir-projects/aztec-nr/aztec/src/context/private_context.nr +++ b/noir-projects/aztec-nr/aztec/src/context/private_context.nr @@ -237,7 +237,7 @@ impl PrivateContext { let request_and_generator = KeyValidationRequestAndGenerator { request, sk_app_generator: sk_generators[key_index] }; // We constrain that the pk_m_hash matches the one in the request (otherwise we could get an arbitrary // valid key request and not the one corresponding to pk_m_hash). - //assert(request.pk_m.hash() == pk_m_hash); + assert(request.pk_m.hash() == pk_m_hash); self.key_validation_requests_and_generators.push(request_and_generator); self.last_key_validation_requests[key_index] = Option::some(request); request.sk_app diff --git a/noir-projects/aztec-nr/aztec/src/keys/getters.nr b/noir-projects/aztec-nr/aztec/src/keys/getters.nr index 8a2c7c440fb2..8e56174ac835 100644 --- a/noir-projects/aztec-nr/aztec/src/keys/getters.nr +++ b/noir-projects/aztec-nr/aztec/src/keys/getters.nr @@ -95,7 +95,7 @@ fn fetch_and_constrain_keys(address: AztecAddress) -> PublicKeys { let computed_address = AztecAddress::compute(public_keys.hash(), partial_address); - //assert(computed_address.eq(address)); + assert(computed_address.eq(address)); public_keys } diff --git a/noir-projects/aztec-nr/aztec/src/test/helpers/cheatcodes.nr b/noir-projects/aztec-nr/aztec/src/test/helpers/cheatcodes.nr index 033e2823574d..648acba82e50 100644 --- a/noir-projects/aztec-nr/aztec/src/test/helpers/cheatcodes.nr +++ b/noir-projects/aztec-nr/aztec/src/test/helpers/cheatcodes.nr @@ -1,6 +1,6 @@ use dep::protocol_types::address::AztecAddress; use crate::context::inputs::{PublicContextInputs, PrivateContextInputs}; -use crate::test::helpers::types::Deployer; +use crate::test::helpers::types::{Deployer, TestAccount}; unconstrained pub fn reset() { oracle_reset(); @@ -30,18 +30,10 @@ unconstrained pub fn get_public_context_inputs() -> PublicContextInputs { oracle_get_public_context_inputs() } -unconstrained pub fn deploy_inner( - path: str, - initializer: str, - args: [Field] -) -> AztecAddress { +unconstrained pub fn deploy(path: str, initializer: str, args: [Field]) -> AztecAddress { oracle_deploy(path, initializer, args) } -pub fn deploy(path: str) -> Deployer { - Deployer { path } -} - unconstrained pub fn direct_storage_write( contract_address: AztecAddress, storage_slot: Field, @@ -50,6 +42,10 @@ unconstrained pub fn direct_storage_write( let _hash = direct_storage_write_oracle(contract_address, storage_slot, fields); } +unconstrained pub fn add_account(secret: Field) -> TestAccount { + oracle_add_account(secret) +} + #[oracle(reset)] fn oracle_reset() {} @@ -80,3 +76,6 @@ fn direct_storage_write_oracle( _storage_slot: Field, _values: [Field; N] ) -> [Field; N] {} + +#[oracle(addAccount)] +fn oracle_add_account(secret: Field) -> TestAccount {} diff --git a/noir-projects/aztec-nr/aztec/src/test/helpers/test_environment.nr b/noir-projects/aztec-nr/aztec/src/test/helpers/test_environment.nr index 5dd18c6871c9..39ba83a9cd0b 100644 --- a/noir-projects/aztec-nr/aztec/src/test/helpers/test_environment.nr +++ b/noir-projects/aztec-nr/aztec/src/test/helpers/test_environment.nr @@ -4,9 +4,11 @@ use dep::protocol_types::{ grumpkin_point::GrumpkinPoint }; +use crate::oracle::unsafe_rand::unsafe_rand; + use crate::context::{PrivateContext, PublicContext, PrivateVoidCallInterface}; -use crate::test::helpers::{cheatcodes, types::Deployer}; -use crate::keys::{public_keys::PublicKeys, constants::{NULLIFIER_INDEX, INCOMING_INDEX, OUTGOING_INDEX, TAGGING_INDEX}}; +use crate::test::helpers::{cheatcodes, types::{Deployer, TestAccount}}; +use crate::keys::constants::{NULLIFIER_INDEX, INCOMING_INDEX, OUTGOING_INDEX, TAGGING_INDEX}; struct TestEnvironment { contract_address: Option, @@ -85,7 +87,7 @@ impl TestEnvironment { PrivateContext::new(inputs, args_hash) } - fn store_master_key(self, key_index: Field, address: AztecAddress) -> GrumpkinPoint { + fn store_master_key(self, key_index: Field, address: AztecAddress, key: GrumpkinPoint) { let x_coordinate_map_slot = key_index * 2 + 1; let y_coordinate_map_slot = x_coordinate_map_slot + 1; let x_coordinate_derived_slot = derive_storage_slot_in_map(x_coordinate_map_slot, address); @@ -93,34 +95,25 @@ impl TestEnvironment { let canonical_registry_address = AztecAddress::from_field(CANONICAL_KEY_REGISTRY_ADDRESS); - let fake_key = GrumpkinPoint { x: 1, y: 2 }; - - cheatcodes::direct_storage_write( - canonical_registry_address, - x_coordinate_derived_slot, - [fake_key.x] - ); + cheatcodes::direct_storage_write(canonical_registry_address, x_coordinate_derived_slot, [key.x]); - cheatcodes::direct_storage_write( - canonical_registry_address, - y_coordinate_derived_slot, - [fake_key.y] - ); - fake_key + cheatcodes::direct_storage_write(canonical_registry_address, y_coordinate_derived_slot, [key.y]); } - fn get_address_with_keys(self, address: AztecAddress) -> AztecAddress { - let keys = PublicKeys { - npk_m: self.store_master_key(NULLIFIER_INDEX, address), - ivpk_m: self.store_master_key(INCOMING_INDEX, address), - ovpk_m: self.store_master_key(OUTGOING_INDEX, address), - tpk_m: self.store_master_key(TAGGING_INDEX, address) - }; + fn get_account(self) -> AztecAddress { + let test_account = cheatcodes::add_account(unsafe_rand()); + let address = test_account.address; + let keys = test_account.keys; + + self.store_master_key(NULLIFIER_INDEX, address, keys.npk_m); + self.store_master_key(INCOMING_INDEX, address, keys.ivpk_m); + self.store_master_key(OUTGOING_INDEX, address, keys.ovpk_m); + self.store_master_key(TAGGING_INDEX, address, keys.tpk_m); - AztecAddress::compute(keys.hash(), PartialAddress::from_field(1)) + test_account.address } fn deploy(self, path: str) -> Deployer { - cheatcodes::deploy(path) + Deployer { path } } } diff --git a/noir-projects/aztec-nr/aztec/src/test/helpers/types.nr b/noir-projects/aztec-nr/aztec/src/test/helpers/types.nr index e4b43538c341..3ce27bdade02 100644 --- a/noir-projects/aztec-nr/aztec/src/test/helpers/types.nr +++ b/noir-projects/aztec-nr/aztec/src/test/helpers/types.nr @@ -1,24 +1,28 @@ -use dep::protocol_types::address::AztecAddress; +use dep::protocol_types::{traits::{Deserialize, Serialize}, address::AztecAddress}; use crate::context::inputs::{PublicContextInputs, PrivateContextInputs}; use crate::context::call_interfaces::CallInterface; -use crate::test::helpers::cheatcodes::{get_private_context_inputs, get_public_context_inputs, deploy_inner, advance_blocks, get_block_number}; +use crate::test::helpers::cheatcodes; +use crate::keys::public_keys::{PUBLIC_KEYS_LENGTH, PublicKeys}; struct Deployer { path: str, } impl Deployer { - pub fn with_private_initializer(self, call_interface: C) -> AztecAddress where C: CallInterface { - let address = deploy_inner( + pub fn with_private_initializer( + self, + call_interface: C + ) -> AztecAddress where C: CallInterface { + let address = cheatcodes::deploy( self.path, call_interface.get_name(), call_interface.get_args() ); - advance_blocks(1); - let block_number = get_block_number(); + cheatcodes::advance_blocks(1); + let block_number = cheatcodes::get_block_number(); let original_fn = call_interface.get_original(); - let mut inputs = get_private_context_inputs(block_number - 1); + let mut inputs = cheatcodes::get_private_context_inputs(block_number - 1); inputs.call_context.storage_contract_address = address; inputs.call_context.function_selector = call_interface.get_selector(); let _result = original_fn(inputs); @@ -29,14 +33,48 @@ impl Deployer { self, call_interface: C ) -> AztecAddress where C: CallInterface { - let address = deploy_inner( + let address = cheatcodes::deploy( self.path, call_interface.get_name(), call_interface.get_args() ); let original_fn = call_interface.get_original(); - let mut inputs = get_public_context_inputs(); + let mut inputs = cheatcodes::get_public_context_inputs(); let _result = original_fn(inputs); address } } + +// Keys length + address +global TEST_ACCOUNT_LENGTH = PUBLIC_KEYS_LENGTH + 1; + +struct TestAccount { + address: AztecAddress, + keys: PublicKeys +} + +impl Serialize for TestAccount { + fn serialize(self) -> [Field; TEST_ACCOUNT_LENGTH] { + let mut output = [0; TEST_ACCOUNT_LENGTH]; + + output[0] = self.address.to_field(); + + for i in 0..PUBLIC_KEYS_LENGTH { + output[i+1] = self.keys.serialize()[i]; + } + output + } +} + +impl Deserialize for TestAccount { + fn deserialize(input: [Field; TEST_ACCOUNT_LENGTH]) -> Self { + let address = AztecAddress::from_field(input[0]); + let mut key_buffer = [0; PUBLIC_KEYS_LENGTH]; + for i in 0..PUBLIC_KEYS_LENGTH { + key_buffer[i] = input[i+1]; + } + let keys = PublicKeys::deserialize(key_buffer); + + Self { address, keys } + } +} diff --git a/noir-projects/noir-contracts/contracts/counter_contract/src/main.nr b/noir-projects/noir-contracts/contracts/counter_contract/src/main.nr index c3c8cc6981ff..3ae2f399d779 100644 --- a/noir-projects/noir-contracts/contracts/counter_contract/src/main.nr +++ b/noir-projects/noir-contracts/contracts/counter_contract/src/main.nr @@ -48,8 +48,8 @@ contract Counter { fn test_initialize() { // Setup env, generate keys let mut env = TestEnvironment::new(); - let owner = env.get_address_with_keys(AztecAddress::from_field(13)); - let outgoing_viewer = env.get_address_with_keys(AztecAddress::from_field(14)); + let owner = env.get_account(); + let outgoing_viewer = env.get_account(); // Deploy contract and initialize let initializer = Counter::interface().initialize(5, owner, outgoing_viewer); diff --git a/yarn-project/txe/src/oracle/txe_oracle.ts b/yarn-project/txe/src/oracle/txe_oracle.ts index 59ae24e756d4..8891b2c27188 100644 --- a/yarn-project/txe/src/oracle/txe_oracle.ts +++ b/yarn-project/txe/src/oracle/txe_oracle.ts @@ -11,7 +11,7 @@ import { type UnencryptedL2Log, } from '@aztec/circuit-types'; import { - CompleteAddress, + type CompleteAddress, type Header, KeyValidationRequest, NULLIFIER_SUBTREE_HEIGHT, @@ -28,7 +28,7 @@ import { type FunctionSelector } from '@aztec/foundation/abi'; import { AztecAddress } from '@aztec/foundation/aztec-address'; import { Fr, GrumpkinScalar, type Point } from '@aztec/foundation/fields'; import { type Logger, applyStringFormatting } from '@aztec/foundation/log'; -import { KeyStore } from '@aztec/key-store'; +import { type KeyStore } from '@aztec/key-store'; import { type ExecutionNoteCache, type MessageLoadOracleInputs, @@ -37,10 +37,12 @@ import { type TypedOracle, pickNotes, } from '@aztec/simulator'; -import { type ContractInstance } from '@aztec/types/contracts'; +import { type ContractInstance, type ContractInstanceWithAddress } from '@aztec/types/contracts'; import { MerkleTreeSnapshotOperationsFacade, type MerkleTrees } from '@aztec/world-state'; export class TXE implements TypedOracle { + private blockNumber = 0; + constructor( private logger: Logger, private trees: MerkleTrees, @@ -48,13 +50,50 @@ export class TXE implements TypedOracle { private noteCache: ExecutionNoteCache, private contractInstanceStore: ContractInstanceStore, private keyStore: KeyStore, + private accountStore: any, private contractAddress: AztecAddress, ) {} + // Utils + setContractAddress(contractAddress: AztecAddress) { this.contractAddress = contractAddress; } + setBlockNumber(blockNumber: number) { + this.blockNumber = blockNumber; + } + + getTrees() { + return this.trees; + } + + getContractInstanceStore() { + return this.contractInstanceStore; + } + + getKeyStore() { + return this.keyStore; + } + + getAccountStore() { + return this.accountStore; + } + + async addContractInstance(contractInstance: ContractInstanceWithAddress) { + await this.contractInstanceStore.addContractInstance(contractInstance); + } + + // TypedOracle + + getBlockNumber(): Promise { + return Promise.resolve(this.blockNumber); + } + + getContractAddress(): Promise { + return Promise.resolve(this.contractAddress); + } + getRandomField() { return Fr.random(); } @@ -72,8 +111,7 @@ export class TXE implements TypedOracle { } getKeyValidationRequest(pkMHash: Fr): Promise { - //return this.keyStore.getKeyValidationRequest(pkMHash, this.contractAddress); - return Promise.resolve(KeyValidationRequest.empty()); + return this.keyStore.getKeyValidationRequest(pkMHash, this.contractAddress); } getContractInstance(address: AztecAddress): Promise { @@ -128,7 +166,7 @@ export class TXE implements TypedOracle { } getCompleteAddress(account: AztecAddress): Promise { - return Promise.resolve(CompleteAddress.fromSecretKeyAndPartialAddress(Fr.ONE, account)); + return Promise.resolve(this.accountStore.getAccount(account)); } getAuthWitness(_messageHash: Fr): Promise { diff --git a/yarn-project/txe/src/txe_service/txe_service.ts b/yarn-project/txe/src/txe_service/txe_service.ts index 7da9912a06d7..ea47b17044cf 100644 --- a/yarn-project/txe/src/txe_service/txe_service.ts +++ b/yarn-project/txe/src/txe_service/txe_service.ts @@ -20,6 +20,7 @@ import { ExecutionNoteCache, PackedValuesCache, type TypedOracle } from '@aztec/ import { MerkleTrees } from '@aztec/world-state'; import { TXE } from '../oracle/txe_oracle.js'; +import { AccountStore } from '../util/account_store.js'; import { type ForeignCallArray, type ForeignCallSingle, @@ -31,17 +32,7 @@ import { } from '../util/encoding.js'; export class TXEService { - private blockNumber = 0; - - constructor( - private logger: Logger, - private typedOracle: TypedOracle, - private store: AztecKVStore, - private trees: MerkleTrees, - private contractInstanceStore: ContractInstanceStore, - private keyStore: KeyStore, - private contractAddress: AztecAddress, - ) {} + constructor(private logger: Logger, private typedOracle: TypedOracle, private store: AztecKVStore) {} static async init(logger: Logger, contractAddress = AztecAddress.random()) { const store = openTmpStore(true); @@ -50,9 +41,19 @@ export class TXEService { const noteCache = new ExecutionNoteCache(); const contractInstanceStore = new ContractInstanceStore(store); const keyStore = new KeyStore(store); + const accountStore = new AccountStore(store); logger.info(`TXE service initialized`); - const txe = new TXE(logger, trees, packedValuesCache, noteCache, contractInstanceStore, keyStore, contractAddress); - const service = new TXEService(logger, txe, store, trees, contractInstanceStore, keyStore, contractAddress); + const txe = new TXE( + logger, + trees, + packedValuesCache, + noteCache, + contractInstanceStore, + keyStore, + accountStore, + contractAddress, + ); + const service = new TXEService(logger, txe, store); await service.timeTravel(toSingle(new Fr(1n))); return service; } @@ -61,56 +62,60 @@ export class TXEService { async getPrivateContextInputs(blockNumber: ForeignCallSingle) { const inputs = PrivateContextInputs.empty(); - const stateReference = await this.trees.getStateReference(true); + const trees = (this.typedOracle as TXE).getTrees(); + const stateReference = await trees.getStateReference(true); inputs.historicalHeader.globalVariables.blockNumber = fromSingle(blockNumber); inputs.historicalHeader.state = stateReference; inputs.callContext.msgSender = AztecAddress.random(); - inputs.callContext.storageContractAddress = this.contractAddress; + inputs.callContext.storageContractAddress = await this.typedOracle.getContractAddress(); return toForeignCallResult(inputs.toFields().map(toSingle)); } async timeTravel(blocks: ForeignCallSingle) { const nBlocks = fromSingle(blocks).toNumber(); this.logger.info(`time traveling ${nBlocks} blocks`); + const trees = (this.typedOracle as TXE).getTrees(); + const blockNumber = await this.typedOracle.getBlockNumber(); for (let i = 0; i < nBlocks; i++) { const header = Header.empty(); const l2Block = L2Block.empty(); - header.state = await this.trees.getStateReference(true); - header.globalVariables.blockNumber = new Fr(this.blockNumber); + header.state = await trees.getStateReference(true); + header.globalVariables.blockNumber = new Fr(blockNumber); header.state.partial.nullifierTree.root = Fr.fromBuffer( - (await this.trees.getTreeInfo(MerkleTreeId.NULLIFIER_TREE, true)).root, + (await trees.getTreeInfo(MerkleTreeId.NULLIFIER_TREE, true)).root, ); header.state.partial.noteHashTree.root = Fr.fromBuffer( - (await this.trees.getTreeInfo(MerkleTreeId.NOTE_HASH_TREE, true)).root, + (await trees.getTreeInfo(MerkleTreeId.NOTE_HASH_TREE, true)).root, ); header.state.partial.publicDataTree.root = Fr.fromBuffer( - (await this.trees.getTreeInfo(MerkleTreeId.PUBLIC_DATA_TREE, true)).root, + (await trees.getTreeInfo(MerkleTreeId.PUBLIC_DATA_TREE, true)).root, ); header.state.l1ToL2MessageTree.root = Fr.fromBuffer( - (await this.trees.getTreeInfo(MerkleTreeId.L1_TO_L2_MESSAGE_TREE, true)).root, + (await trees.getTreeInfo(MerkleTreeId.L1_TO_L2_MESSAGE_TREE, true)).root, ); - l2Block.archive.root = Fr.fromBuffer((await this.trees.getTreeInfo(MerkleTreeId.ARCHIVE, true)).root); + l2Block.archive.root = Fr.fromBuffer((await trees.getTreeInfo(MerkleTreeId.ARCHIVE, true)).root); l2Block.header = header; - await this.trees.handleL2BlockAndMessages(l2Block, []); - this.blockNumber++; + await trees.handleL2BlockAndMessages(l2Block, []); + (this.typedOracle as TXE).setBlockNumber(blockNumber + 1); } return toForeignCallResult([]); } async reset() { - this.blockNumber = 0; this.store = openTmpStore(true); - this.trees = await MerkleTrees.new(this.store, this.logger); - this.contractInstanceStore = new ContractInstanceStore(this.store); - this.keyStore = new KeyStore(this.store); + const trees = await MerkleTrees.new(this.store, this.logger); + const contractInstanceStore = new ContractInstanceStore(this.store); + const keyStore = new KeyStore(this.store); + const accountStore = new AccountStore(this.store); this.typedOracle = new TXE( this.logger, - this.trees, + trees, new PackedValuesCache(), new ExecutionNoteCache(), - this.contractInstanceStore, - this.keyStore, - this.contractAddress, + contractInstanceStore, + keyStore, + accountStore, + AztecAddress.random(), ); await this.timeTravel(toSingle(new Fr(1))); return toForeignCallResult([]); @@ -118,7 +123,6 @@ export class TXEService { setContractAddress(address: ForeignCallSingle) { const typedAddress = AztecAddress.fromField(fromSingle(address)); - this.contractAddress = typedAddress; (this.typedOracle as TXE).setContractAddress(typedAddress); return toForeignCallResult([]); } @@ -148,7 +152,7 @@ export class TXEService { deployer: AztecAddress.ZERO, }); this.logger.debug(`Deployed ${contractClass.artifact.name} at ${instance.address}`); - await this.contractInstanceStore.addContractInstance(instance); + await (this.typedOracle as TXE).addContractInstance(instance); return toForeignCallResult([toSingle(instance.address)]); } @@ -157,10 +161,11 @@ export class TXEService { startStorageSlot: ForeignCallSingle, values: ForeignCallArray, ) { + const trees = (this.typedOracle as TXE).getTrees(); const startStorageSlotFr = fromSingle(startStorageSlot); const valuesFr = fromArray(values); const contractAddressFr = fromSingle(contractAddress); - const db = this.trees.asLatest(); + const db = trees.asLatest(); const publicDataWrites = valuesFr.map((value, i) => { const storageSlot = startStorageSlotFr.add(new Fr(i)); @@ -175,26 +180,42 @@ export class TXEService { return toForeignCallResult([toArray(publicDataWrites.map(write => write.newValue))]); } + async addAccount(secret: ForeignCallSingle) { + const secretKey = fromSingle(secret); + const keyStore = (this.typedOracle as TXE).getKeyStore(); + const completeAddress = await keyStore.addAccount(secretKey, Fr.ONE); + const accountStore = (this.typedOracle as TXE).getAccountStore(); + await accountStore.setAccount(completeAddress.address, completeAddress); + return toForeignCallResult([ + toSingle(completeAddress.address), + ...completeAddress.publicKeys.toFields().map(toSingle), + ]); + } + // PXE oracles getRandomField() { return toForeignCallResult([toSingle(this.typedOracle.getRandomField())]); } - getContractAddress() { - return toForeignCallResult([toSingle(this.contractAddress.toField())]); + async getContractAddress() { + const contractAddress = await this.typedOracle.getContractAddress(); + return toForeignCallResult([toSingle(contractAddress.toField())]); } - getBlockNumber() { - return toForeignCallResult([toSingle(new Fr(this.blockNumber))]); + async getBlockNumber() { + const blockNumber = await this.typedOracle.getBlockNumber(); + return toForeignCallResult([toSingle(new Fr(blockNumber))]); } - avmOpcodeAddress() { - return toForeignCallResult([toSingle(this.contractAddress.toField())]); + async avmOpcodeAddress() { + const contractAddress = await this.typedOracle.getContractAddress(); + return toForeignCallResult([toSingle(contractAddress.toField())]); } - avmOpcodeBlockNumber() { - return toForeignCallResult([toSingle(new Fr(this.blockNumber))]); + async avmOpcodeBlockNumber() { + const blockNumber = await this.typedOracle.getBlockNumber(); + return toForeignCallResult([toSingle(new Fr(blockNumber))]); } async packArgumentsArray(args: ForeignCallArray) { diff --git a/yarn-project/txe/src/util/account_store.ts b/yarn-project/txe/src/util/account_store.ts new file mode 100644 index 000000000000..f35589812dc9 --- /dev/null +++ b/yarn-project/txe/src/util/account_store.ts @@ -0,0 +1,19 @@ +import { type AztecAddress, CompleteAddress } from '@aztec/circuits.js'; +import { type AztecKVStore, type AztecMap } from '@aztec/kv-store'; + +export class AccountStore { + #accounts: AztecMap; + + constructor(database: AztecKVStore) { + this.#accounts = database.openMap('accounts'); + } + + getAccount(key: AztecAddress) { + const completeAddress = this.#accounts.get(key.toString()); + return CompleteAddress.fromBuffer(completeAddress!); + } + + async setAccount(key: AztecAddress, value: CompleteAddress) { + await this.#accounts.set(key.toString(), value.toBuffer()); + } +} From 38fb77e681d420bef33f049a06a9538a98a239aa Mon Sep 17 00:00:00 2001 From: thunkar Date: Tue, 11 Jun 2024 08:55:33 +0000 Subject: [PATCH 36/60] reverted parallel changes since we have session ids now --- .../compiler/noirc_frontend/src/monomorphization/mod.rs | 3 --- noir/noir-repo/tooling/nargo_cli/src/cli/test_cmd.rs | 4 ++-- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/noir/noir-repo/compiler/noirc_frontend/src/monomorphization/mod.rs b/noir/noir-repo/compiler/noirc_frontend/src/monomorphization/mod.rs index 239f644faff4..451a884d59c2 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/monomorphization/mod.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/monomorphization/mod.rs @@ -246,12 +246,9 @@ impl<'interner> Monomorphizer<'interner> { } } FunctionKind::Recursive => { - // let func = self.interner.function_meta(&id); - // println!("{:#?}", func.name); let id = self.queue_function(id, expr_id, typ, turbofish_generics, trait_method); Definition::Function(id) - //unreachable!("wtf"); } } } diff --git a/noir/noir-repo/tooling/nargo_cli/src/cli/test_cmd.rs b/noir/noir-repo/tooling/nargo_cli/src/cli/test_cmd.rs index 838642b587d7..99c284e5019b 100644 --- a/noir/noir-repo/tooling/nargo_cli/src/cli/test_cmd.rs +++ b/noir/noir-repo/tooling/nargo_cli/src/cli/test_cmd.rs @@ -82,7 +82,7 @@ pub(crate) fn run(args: TestCommand, config: NargoConfig) -> Result<(), CliError let test_reports: Vec> = workspace .into_iter() - //.par_bridge() + .par_bridge() .map(|package| { run_tests::( &workspace_file_manager, @@ -137,7 +137,7 @@ fn run_tests + Default>( println!("[{}] Running {count_all} test function{plural}", package.name); let test_report: Vec<(String, TestStatus)> = test_functions - .into_iter() + .into_par_iter() .map(|test_name| { let status = run_test::( file_manager, From 85b7decab5b81080b64da7ea2dcc129fe84cbe86 Mon Sep 17 00:00:00 2001 From: thunkar Date: Tue, 11 Jun 2024 09:40:52 +0000 Subject: [PATCH 37/60] updated tsconfig --- yarn-project/txe/tsconfig.json | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/yarn-project/txe/tsconfig.json b/yarn-project/txe/tsconfig.json index effb5a7151c9..1db338e8540f 100644 --- a/yarn-project/txe/tsconfig.json +++ b/yarn-project/txe/tsconfig.json @@ -6,32 +6,41 @@ "tsBuildInfoFile": ".tsbuildinfo" }, "references": [ + { + "path": "../bb-prover" + }, + { + "path": "../builder" + }, { "path": "../circuit-types" }, { "path": "../circuits.js" }, + { + "path": "../ethereum" + }, { "path": "../foundation" }, { - "path": "../noir-protocol-circuits-types" + "path": "../key-store" }, { - "path": "../protocol-contracts" + "path": "../kv-store" }, { - "path": "../types" + "path": "../noir-protocol-circuits-types" }, { - "path": "../world-state" + "path": "../protocol-contracts" }, { - "path": "../kv-store" + "path": "../simulator" }, { - "path": "../merkle-tree" + "path": "../types" }, { "path": "../noir-contracts.js" From 907e26d49c685e0e9f3cfe8a345f40f01bedd338 Mon Sep 17 00:00:00 2001 From: thunkar Date: Wed, 12 Jun 2024 10:36:33 +0000 Subject: [PATCH 38/60] external calls --- .../src/state_vars/shared_mutable/test.nr | 170 ++++++++-------- .../aztec-nr/aztec/src/test/helpers.nr | 1 + .../aztec/src/test/helpers/cheatcodes.nr | 15 +- .../aztec-nr/aztec/src/test/helpers/keys.nr | 18 ++ .../src/test/helpers/test_environment.nr | 46 ++--- .../aztec-nr/aztec/src/test/helpers/types.nr | 18 +- .../contracts/child_contract/src/main.nr | 2 +- .../contracts/counter_contract/src/main.nr | 5 +- .../contracts/parent_contract/Nargo.toml | 2 + .../contracts/parent_contract/src/main.nr | 33 +++ yarn-project/pxe/src/database/index.ts | 1 + yarn-project/pxe/src/index.ts | 2 + yarn-project/txe/package.json | 1 + yarn-project/txe/src/oracle/txe_oracle.ts | 190 +++++++++++++++--- .../txe/src/txe_service/txe_service.ts | 77 ++++--- .../{account_store.ts => txe_database.ts} | 13 +- yarn-project/yarn.lock | 1 + 17 files changed, 411 insertions(+), 184 deletions(-) create mode 100644 noir-projects/aztec-nr/aztec/src/test/helpers/keys.nr rename yarn-project/txe/src/util/{account_store.ts => txe_database.ts} (55%) diff --git a/noir-projects/aztec-nr/aztec/src/state_vars/shared_mutable/test.nr b/noir-projects/aztec-nr/aztec/src/state_vars/shared_mutable/test.nr index 42f432a55340..b193640a54b2 100644 --- a/noir-projects/aztec-nr/aztec/src/state_vars/shared_mutable/test.nr +++ b/noir-projects/aztec-nr/aztec/src/state_vars/shared_mutable/test.nr @@ -29,124 +29,124 @@ fn in_private( SharedMutable::new(&mut env.private_at(historical_block_number), storage_slot) } -#[test] -fn test_get_current_value_in_public_initial() { - let env = setup(); - let state_var = in_public(env); +// #[test] +// fn test_get_current_value_in_public_initial() { +// let env = setup(); +// let state_var = in_public(env); - // 0 is the default empty value for a Field - assert_eq(state_var.get_current_value_in_public(), 0); -} +// // 0 is the default empty value for a Field +// assert_eq(state_var.get_current_value_in_public(), 0); +// } -#[test] -fn test_get_current_value_in_public_before_scheduled_change() { - let mut env = setup(); - let state_var = in_public(env); +// #[test] +// fn test_get_current_value_in_public_before_scheduled_change() { +// let mut env = setup(); +// let state_var = in_public(env); - state_var.schedule_value_change(new_value); +// state_var.schedule_value_change(new_value); - let (_, block_of_change) = state_var.get_scheduled_value_in_public(); +// let (_, block_of_change) = state_var.get_scheduled_value_in_public(); - let original_value = 0; +// let original_value = 0; - // The current value has not changed - assert_eq(state_var.get_current_value_in_public(), original_value); +// // The current value has not changed +// assert_eq(state_var.get_current_value_in_public(), original_value); - // The current value still does not change right before the block of change - env.advance_block_to(block_of_change - 1); - assert_eq(state_var.get_current_value_in_public(), original_value); -} +// // The current value still does not change right before the block of change +// env.advance_block_to(block_of_change - 1); +// assert_eq(state_var.get_current_value_in_public(), original_value); +// } -#[test] -fn test_get_current_value_in_public_at_scheduled_change() { - let mut env = setup(); - let state_var = in_public(env); +// #[test] +// fn test_get_current_value_in_public_at_scheduled_change() { +// let mut env = setup(); +// let state_var = in_public(env); - state_var.schedule_value_change(new_value); +// state_var.schedule_value_change(new_value); - let (_, block_of_change) = state_var.get_scheduled_value_in_public(); +// let (_, block_of_change) = state_var.get_scheduled_value_in_public(); - env.advance_block_to(block_of_change); - assert_eq(state_var.get_current_value_in_public(), new_value); -} +// env.advance_block_to(block_of_change); +// assert_eq(state_var.get_current_value_in_public(), new_value); +// } -#[test] -fn test_get_current_value_in_public_after_scheduled_change() { - let mut env = setup(); - let state_var = in_public(env); +// #[test] +// fn test_get_current_value_in_public_after_scheduled_change() { +// let mut env = setup(); +// let state_var = in_public(env); - state_var.schedule_value_change(new_value); +// state_var.schedule_value_change(new_value); - let (_, block_of_change) = state_var.get_scheduled_value_in_public(); +// let (_, block_of_change) = state_var.get_scheduled_value_in_public(); - env.advance_block_to(block_of_change + 10); - assert_eq(state_var.get_current_value_in_public(), new_value); -} +// env.advance_block_to(block_of_change + 10); +// assert_eq(state_var.get_current_value_in_public(), new_value); +// } -#[test] -fn test_get_current_value_in_private_before_change() { - let mut env = setup(); +// #[test] +// fn test_get_current_value_in_private_before_change() { +// let mut env = setup(); - let public_state_var = in_public(env); - public_state_var.schedule_value_change(new_value); +// let public_state_var = in_public(env); +// public_state_var.schedule_value_change(new_value); - let (_, block_of_change) = public_state_var.get_scheduled_value_in_public(); +// let (_, block_of_change) = public_state_var.get_scheduled_value_in_public(); - let schedule_block_number = env.block_number(); +// let schedule_block_number = env.block_number(); - let private_state_var = in_private(&mut env, schedule_block_number); - assert_eq(private_state_var.get_current_value_in_private(), 0); - assert_eq(private_state_var.context.max_block_number.unwrap(), block_of_change - 1); -} +// let private_state_var = in_private(&mut env, schedule_block_number); +// assert_eq(private_state_var.get_current_value_in_private(), 0); +// assert_eq(private_state_var.context.max_block_number.unwrap(), block_of_change - 1); +// } -#[test] -fn test_get_current_value_in_private_immediately_before_change() { - let mut env = setup(); +// #[test] +// fn test_get_current_value_in_private_immediately_before_change() { +// let mut env = setup(); - let public_state_var = in_public(env); - public_state_var.schedule_value_change(new_value); +// let public_state_var = in_public(env); +// public_state_var.schedule_value_change(new_value); - let (_, block_of_change) = public_state_var.get_scheduled_value_in_public(); +// let (_, block_of_change) = public_state_var.get_scheduled_value_in_public(); - let private_state_var = in_private(&mut env, block_of_change - 1); +// let private_state_var = in_private(&mut env, block_of_change - 1); - assert_eq(private_state_var.get_current_value_in_private(), 0); - assert_eq(private_state_var.context.max_block_number.unwrap(), block_of_change - 1); -} +// assert_eq(private_state_var.get_current_value_in_private(), 0); +// assert_eq(private_state_var.context.max_block_number.unwrap(), block_of_change - 1); +// } -#[test] -fn test_get_current_value_in_private_at_change() { - let mut env = setup(); +// #[test] +// fn test_get_current_value_in_private_at_change() { +// let mut env = setup(); - let public_state_var = in_public(env); - public_state_var.schedule_value_change(new_value); +// let public_state_var = in_public(env); +// public_state_var.schedule_value_change(new_value); - let (_, block_of_change) = public_state_var.get_scheduled_value_in_public(); +// let (_, block_of_change) = public_state_var.get_scheduled_value_in_public(); - let historical_block_number = block_of_change; - let private_state_var = in_private(&mut env, historical_block_number); - assert_eq(private_state_var.get_current_value_in_private(), new_value); - assert_eq( - private_state_var.context.max_block_number.unwrap(), historical_block_number + TEST_INITIAL_DELAY - ); -} +// let historical_block_number = block_of_change; +// let private_state_var = in_private(&mut env, historical_block_number); +// assert_eq(private_state_var.get_current_value_in_private(), new_value); +// assert_eq( +// private_state_var.context.max_block_number.unwrap(), historical_block_number + TEST_INITIAL_DELAY +// ); +// } -#[test] -fn test_get_current_value_in_private_after_change() { - let mut env = setup(); +// #[test] +// fn test_get_current_value_in_private_after_change() { +// let mut env = setup(); - let public_state_var = in_public(env); - public_state_var.schedule_value_change(new_value); +// let public_state_var = in_public(env); +// public_state_var.schedule_value_change(new_value); - let (_, block_of_change) = public_state_var.get_scheduled_value_in_public(); +// let (_, block_of_change) = public_state_var.get_scheduled_value_in_public(); - let historical_block_number = block_of_change + 10; - let private_state_var = in_private(&mut env, historical_block_number); - assert_eq(private_state_var.get_current_value_in_private(), new_value); - assert_eq( - private_state_var.context.max_block_number.unwrap(), historical_block_number + TEST_INITIAL_DELAY - ); -} +// let historical_block_number = block_of_change + 10; +// let private_state_var = in_private(&mut env, historical_block_number); +// assert_eq(private_state_var.get_current_value_in_private(), new_value); +// assert_eq( +// private_state_var.context.max_block_number.unwrap(), historical_block_number + TEST_INITIAL_DELAY +// ); +// } // #[test] // fn test_get_current_delay_in_public() { diff --git a/noir-projects/aztec-nr/aztec/src/test/helpers.nr b/noir-projects/aztec-nr/aztec/src/test/helpers.nr index 80045b39a642..b28a85add1cb 100644 --- a/noir-projects/aztec-nr/aztec/src/test/helpers.nr +++ b/noir-projects/aztec-nr/aztec/src/test/helpers.nr @@ -1,3 +1,4 @@ mod test_environment; mod cheatcodes; mod types; +mod keys; diff --git a/noir-projects/aztec-nr/aztec/src/test/helpers/cheatcodes.nr b/noir-projects/aztec-nr/aztec/src/test/helpers/cheatcodes.nr index 648acba82e50..44c22ef574ac 100644 --- a/noir-projects/aztec-nr/aztec/src/test/helpers/cheatcodes.nr +++ b/noir-projects/aztec-nr/aztec/src/test/helpers/cheatcodes.nr @@ -1,4 +1,4 @@ -use dep::protocol_types::address::AztecAddress; +use dep::protocol_types::address::{AztecAddress, PartialAddress}; use crate::context::inputs::{PublicContextInputs, PrivateContextInputs}; use crate::test::helpers::types::{Deployer, TestAccount}; @@ -42,8 +42,12 @@ unconstrained pub fn direct_storage_write( let _hash = direct_storage_write_oracle(contract_address, storage_slot, fields); } -unconstrained pub fn add_account(secret: Field) -> TestAccount { - oracle_add_account(secret) +unconstrained pub fn create_account() -> TestAccount { + oracle_create_account() +} + +unconstrained pub fn add_account(secret: Field, partial_address: PartialAddress) -> TestAccount { + oracle_add_account(secret, partial_address) } #[oracle(reset)] @@ -77,5 +81,8 @@ fn direct_storage_write_oracle( _values: [Field; N] ) -> [Field; N] {} +#[oracle(createAccount)] +fn oracle_create_account() -> TestAccount {} + #[oracle(addAccount)] -fn oracle_add_account(secret: Field) -> TestAccount {} +fn oracle_add_account(secret: Field, partial_address: PartialAddress) -> TestAccount {} diff --git a/noir-projects/aztec-nr/aztec/src/test/helpers/keys.nr b/noir-projects/aztec-nr/aztec/src/test/helpers/keys.nr new file mode 100644 index 000000000000..f4c53c95c20c --- /dev/null +++ b/noir-projects/aztec-nr/aztec/src/test/helpers/keys.nr @@ -0,0 +1,18 @@ +use dep::protocol_types::{ + address::AztecAddress, storage::map::derive_storage_slot_in_map, + constants::CANONICAL_KEY_REGISTRY_ADDRESS, grumpkin_point::GrumpkinPoint +}; + +use crate::test::helpers::cheatcodes; + +pub fn store_master_key(key_index: Field, address: AztecAddress, key: GrumpkinPoint) { + let x_coordinate_map_slot = key_index * 2 + 1; + let y_coordinate_map_slot = x_coordinate_map_slot + 1; + let x_coordinate_derived_slot = derive_storage_slot_in_map(x_coordinate_map_slot, address); + let y_coordinate_derived_slot = derive_storage_slot_in_map(y_coordinate_map_slot, address); + + let canonical_registry_address = AztecAddress::from_field(CANONICAL_KEY_REGISTRY_ADDRESS); + + cheatcodes::direct_storage_write(canonical_registry_address, x_coordinate_derived_slot, [key.x]); + cheatcodes::direct_storage_write(canonical_registry_address, y_coordinate_derived_slot, [key.y]); +} diff --git a/noir-projects/aztec-nr/aztec/src/test/helpers/test_environment.nr b/noir-projects/aztec-nr/aztec/src/test/helpers/test_environment.nr index 39ba83a9cd0b..4a837a918dff 100644 --- a/noir-projects/aztec-nr/aztec/src/test/helpers/test_environment.nr +++ b/noir-projects/aztec-nr/aztec/src/test/helpers/test_environment.nr @@ -1,13 +1,14 @@ use dep::protocol_types::{ - abis::function_selector::FunctionSelector, address::{AztecAddress, PartialAddress}, - storage::map::derive_storage_slot_in_map, constants::CANONICAL_KEY_REGISTRY_ADDRESS, - grumpkin_point::GrumpkinPoint + abis::{function_selector::FunctionSelector, private_circuit_public_inputs::PrivateCircuitPublicInputs}, + address::{AztecAddress, PartialAddress}, storage::map::derive_storage_slot_in_map, + constants::CANONICAL_KEY_REGISTRY_ADDRESS, grumpkin_point::GrumpkinPoint, traits::Deserialize }; -use crate::oracle::unsafe_rand::unsafe_rand; +use crate::context::inputs::{PublicContextInputs, PrivateContextInputs}; +use crate::context::{packed_returns::PackedReturns, call_interfaces::CallInterface}; use crate::context::{PrivateContext, PublicContext, PrivateVoidCallInterface}; -use crate::test::helpers::{cheatcodes, types::{Deployer, TestAccount}}; +use crate::test::helpers::{cheatcodes, types::{Deployer, TestAccount}, keys}; use crate::keys::constants::{NULLIFIER_INDEX, INCOMING_INDEX, OUTGOING_INDEX, TAGGING_INDEX}; struct TestEnvironment { @@ -87,28 +88,15 @@ impl TestEnvironment { PrivateContext::new(inputs, args_hash) } - fn store_master_key(self, key_index: Field, address: AztecAddress, key: GrumpkinPoint) { - let x_coordinate_map_slot = key_index * 2 + 1; - let y_coordinate_map_slot = x_coordinate_map_slot + 1; - let x_coordinate_derived_slot = derive_storage_slot_in_map(x_coordinate_map_slot, address); - let y_coordinate_derived_slot = derive_storage_slot_in_map(y_coordinate_map_slot, address); - - let canonical_registry_address = AztecAddress::from_field(CANONICAL_KEY_REGISTRY_ADDRESS); - - cheatcodes::direct_storage_write(canonical_registry_address, x_coordinate_derived_slot, [key.x]); - - cheatcodes::direct_storage_write(canonical_registry_address, y_coordinate_derived_slot, [key.y]); - } - - fn get_account(self) -> AztecAddress { - let test_account = cheatcodes::add_account(unsafe_rand()); + fn create_account(self) -> AztecAddress { + let test_account = cheatcodes::create_account(); let address = test_account.address; let keys = test_account.keys; - self.store_master_key(NULLIFIER_INDEX, address, keys.npk_m); - self.store_master_key(INCOMING_INDEX, address, keys.ivpk_m); - self.store_master_key(OUTGOING_INDEX, address, keys.ovpk_m); - self.store_master_key(TAGGING_INDEX, address, keys.tpk_m); + keys::store_master_key(NULLIFIER_INDEX, address, keys.npk_m); + keys::store_master_key(INCOMING_INDEX, address, keys.ivpk_m); + keys::store_master_key(OUTGOING_INDEX, address, keys.ovpk_m); + keys::store_master_key(TAGGING_INDEX, address, keys.tpk_m); test_account.address } @@ -116,4 +104,14 @@ impl TestEnvironment { fn deploy(self, path: str) -> Deployer { Deployer { path } } + + fn call_private( + self, + call_interface: C + ) -> T where C: CallInterface, T: Deserialize { + let original_fn = call_interface.get_original(); + let mut inputs = cheatcodes::get_private_context_inputs(cheatcodes::get_block_number() - 1); + let public_inputs = original_fn(inputs); + PackedReturns::new(public_inputs.returns_hash).unpack_into() + } } diff --git a/noir-projects/aztec-nr/aztec/src/test/helpers/types.nr b/noir-projects/aztec-nr/aztec/src/test/helpers/types.nr index 3ce27bdade02..9ce08939e8d5 100644 --- a/noir-projects/aztec-nr/aztec/src/test/helpers/types.nr +++ b/noir-projects/aztec-nr/aztec/src/test/helpers/types.nr @@ -1,4 +1,7 @@ -use dep::protocol_types::{traits::{Deserialize, Serialize}, address::AztecAddress}; +use dep::protocol_types::{ + traits::{Deserialize, Serialize}, address::AztecAddress, + abis::{function_selector::FunctionSelector, private_circuit_public_inputs::PrivateCircuitPublicInputs} +}; use crate::context::inputs::{PublicContextInputs, PrivateContextInputs}; use crate::context::call_interfaces::CallInterface; @@ -10,10 +13,10 @@ struct Deployer { } impl Deployer { - pub fn with_private_initializer( + pub fn with_private_initializer( self, call_interface: C - ) -> AztecAddress where C: CallInterface { + ) -> AztecAddress where C: CallInterface { let address = cheatcodes::deploy( self.path, call_interface.get_name(), @@ -29,10 +32,10 @@ impl Deployer { address } - pub fn with_public_initializer( + pub fn with_public_initializer( self, call_interface: C - ) -> AztecAddress where C: CallInterface { + ) -> AztecAddress where C: CallInterface { let address = cheatcodes::deploy( self.path, call_interface.get_name(), @@ -43,6 +46,11 @@ impl Deployer { let _result = original_fn(inputs); address } + + pub fn without_initializer(self) -> AztecAddress { + let address = cheatcodes::deploy(self.path, "", &[]); + address + } } // Keys length + address diff --git a/noir-projects/noir-contracts/contracts/child_contract/src/main.nr b/noir-projects/noir-contracts/contracts/child_contract/src/main.nr index 8542d7a0ffd0..35ad62063625 100644 --- a/noir-projects/noir-contracts/contracts/child_contract/src/main.nr +++ b/noir-projects/noir-contracts/contracts/child_contract/src/main.nr @@ -53,7 +53,7 @@ contract Child { fn private_set_value(new_value: Field, owner: AztecAddress) -> Field { let header = context.get_header(); let owner_npk_m_hash = header.get_npk_m_hash(&mut context, owner); - let msg_sender_ovpk_m = header.get_ovpk_m(&mut context, context.msg_sender()); + let msg_sender_ovpk_m = header.get_ovpk_m(&mut context, owner); let owner_ivpk_m = header.get_ivpk_m(&mut context, owner); let mut note = ValueNote::new(new_value, owner_npk_m_hash); diff --git a/noir-projects/noir-contracts/contracts/counter_contract/src/main.nr b/noir-projects/noir-contracts/contracts/counter_contract/src/main.nr index 3ae2f399d779..dc20ba272db3 100644 --- a/noir-projects/noir-contracts/contracts/counter_contract/src/main.nr +++ b/noir-projects/noir-contracts/contracts/counter_contract/src/main.nr @@ -40,7 +40,6 @@ contract Counter { use dep::aztec::test::{helpers::{cheatcodes, test_environment::TestEnvironment}}; use dep::aztec::protocol_types::storage::map::derive_storage_slot_in_map; - use dep::aztec::oracle::{storage::{storage_read, storage_write}}; use dep::aztec::note::note_getter::{MAX_NOTES_PER_PAGE, view_notes}; use dep::aztec::note::note_viewer_options::NoteViewerOptions; @@ -48,8 +47,8 @@ contract Counter { fn test_initialize() { // Setup env, generate keys let mut env = TestEnvironment::new(); - let owner = env.get_account(); - let outgoing_viewer = env.get_account(); + let owner = env.create_account(); + let outgoing_viewer = env.create_account(); // Deploy contract and initialize let initializer = Counter::interface().initialize(5, owner, outgoing_viewer); diff --git a/noir-projects/noir-contracts/contracts/parent_contract/Nargo.toml b/noir-projects/noir-contracts/contracts/parent_contract/Nargo.toml index 53a4d6145648..a91d3bd758c9 100644 --- a/noir-projects/noir-contracts/contracts/parent_contract/Nargo.toml +++ b/noir-projects/noir-contracts/contracts/parent_contract/Nargo.toml @@ -6,3 +6,5 @@ type = "contract" [dependencies] aztec = { path = "../../../aztec-nr/aztec" } +child_contract = { path = "../child_contract" } +value_note = { path = "../../../aztec-nr/value-note" } diff --git a/noir-projects/noir-contracts/contracts/parent_contract/src/main.nr b/noir-projects/noir-contracts/contracts/parent_contract/src/main.nr index fb066de5ad6d..d2163b7f8ce6 100644 --- a/noir-projects/noir-contracts/contracts/parent_contract/src/main.nr +++ b/noir-projects/noir-contracts/contracts/parent_contract/src/main.nr @@ -242,4 +242,37 @@ contract Parent { // Call the target private function context.static_call_public_function(target_contract, target_selector, args); } + + use dep::aztec::test::{helpers::{cheatcodes, test_environment::TestEnvironment}}; + use dep::aztec::protocol_types::storage::map::derive_storage_slot_in_map; + use dep::aztec::oracle::{storage::{storage_read, storage_write}}; + use dep::aztec::note::note_getter::{MAX_NOTES_PER_PAGE, view_notes}; + use dep::aztec::note::note_viewer_options::NoteViewerOptions; + use dep::child_contract::Child; + use dep::value_note::value_note::ValueNote; + + #[test] + fn test_private_call() { + // Setup env, generate keys + let mut env = TestEnvironment::new(); + let owner = env.create_account(); + + // Deploy child contract + let child_contract_address = env.deploy("@aztec/noir-contracts.js/Child").without_initializer(); + cheatcodes::advance_blocks(1); + // Set value in child through parent + let call_interface = Parent::interface().private_call( + child_contract_address, + FunctionSelector::from_signature("private_set_value(Field,(Field))"), + [7, owner.to_field()] + ); + let result: Field = env.call_private(call_interface); + // Read the stored value in the note + //cheatcodes::set_contract_address(child_contract_address); + let counter_slot = Child::storage().a_map_with_private_values.slot; + let owner_slot = derive_storage_slot_in_map(counter_slot, owner); + let mut options = NoteViewerOptions::new(); + let opt_notes: [Option; MAX_NOTES_PER_PAGE] = view_notes(owner_slot, options); + assert(opt_notes[0].unwrap().value == result); + } } diff --git a/yarn-project/pxe/src/database/index.ts b/yarn-project/pxe/src/database/index.ts index 4685cc2f7a8f..e01c18032417 100644 --- a/yarn-project/pxe/src/database/index.ts +++ b/yarn-project/pxe/src/database/index.ts @@ -1 +1,2 @@ export * from './pxe_database.js'; +export * from './kv_pxe_database.js'; diff --git a/yarn-project/pxe/src/index.ts b/yarn-project/pxe/src/index.ts index d7cf6d57253a..7c62b24d3ff1 100644 --- a/yarn-project/pxe/src/index.ts +++ b/yarn-project/pxe/src/index.ts @@ -9,3 +9,5 @@ export * from '@aztec/foundation/fields'; export * from '@aztec/foundation/eth-address'; export * from '@aztec/foundation/aztec-address'; export * from '@aztec/key-store'; +export * from './database/index.js'; +export { ContractDataOracle } from './contract_data_oracle/index.js'; diff --git a/yarn-project/txe/package.json b/yarn-project/txe/package.json index 871593212404..c5548db696df 100644 --- a/yarn-project/txe/package.json +++ b/yarn-project/txe/package.json @@ -55,6 +55,7 @@ "@aztec/foundation": "workspace:^", "@aztec/key-store": "workspace:^", "@aztec/kv-store": "workspace:^", + "@aztec/pxe": "workspace:^", "@aztec/simulator": "workspace:^", "@aztec/types": "workspace:^", "@aztec/world-state": "workspace:^" diff --git a/yarn-project/txe/src/oracle/txe_oracle.ts b/yarn-project/txe/src/oracle/txe_oracle.ts index 8891b2c27188..cd15873be1e0 100644 --- a/yarn-project/txe/src/oracle/txe_oracle.ts +++ b/yarn-project/txe/src/oracle/txe_oracle.ts @@ -1,4 +1,3 @@ -import { type ContractInstanceStore } from '@aztec/archiver'; import { L1NotePayload, MerkleTreeId, @@ -7,55 +6,87 @@ import { type NullifierMembershipWitness, PublicDataWitness, PublicDataWrite, - TaggedNote, + TaggedLog, type UnencryptedL2Log, } from '@aztec/circuit-types'; +import { type CircuitWitnessGenerationStats } from '@aztec/circuit-types/stats'; import { type CompleteAddress, + FunctionData, type Header, - KeyValidationRequest, + type KeyValidationRequest, NULLIFIER_SUBTREE_HEIGHT, PUBLIC_DATA_SUBTREE_HEIGHT, type PUBLIC_DATA_TREE_HEIGHT, - type PrivateCallStackItem, + PrivateCallStackItem, + PrivateCircuitPublicInputs, + PrivateContextInputs, type PublicCallRequest, PublicDataTreeLeaf, type PublicDataTreeLeafPreimage, + computeContractClassId, + getContractClassFromArtifact, } from '@aztec/circuits.js'; import { Aes128 } from '@aztec/circuits.js/barretenberg'; import { computePublicDataTreeLeafSlot, siloNoteHash, siloNullifier } from '@aztec/circuits.js/hash'; -import { type FunctionSelector } from '@aztec/foundation/abi'; +import { + type ContractArtifact, + type FunctionAbi, + type FunctionSelector, + countArgumentsSize, +} from '@aztec/foundation/abi'; import { AztecAddress } from '@aztec/foundation/aztec-address'; import { Fr, GrumpkinScalar, type Point } from '@aztec/foundation/fields'; import { type Logger, applyStringFormatting } from '@aztec/foundation/log'; +import { Timer } from '@aztec/foundation/timer'; import { type KeyStore } from '@aztec/key-store'; +import { ContractDataOracle } from '@aztec/pxe'; import { + ExecutionError, type ExecutionNoteCache, type MessageLoadOracleInputs, type NoteData, + Oracle, type PackedValuesCache, type TypedOracle, + acvm, + extractCallStack, pickNotes, + toACVMWitness, + witnessMapToFields, } from '@aztec/simulator'; import { type ContractInstance, type ContractInstanceWithAddress } from '@aztec/types/contracts'; import { MerkleTreeSnapshotOperationsFacade, type MerkleTrees } from '@aztec/world-state'; +import { type TXEDatabase } from '../util/txe_database.js'; + export class TXE implements TypedOracle { private blockNumber = 0; + private sideEffectCounter = 0; + private contractDataOracle: ContractDataOracle; constructor( private logger: Logger, private trees: MerkleTrees, private packedValuesCache: PackedValuesCache, private noteCache: ExecutionNoteCache, - private contractInstanceStore: ContractInstanceStore, private keyStore: KeyStore, - private accountStore: any, + private txeDatabase: TXEDatabase, private contractAddress: AztecAddress, - ) {} + ) { + this.contractDataOracle = new ContractDataOracle(txeDatabase); + } // Utils + getSideEffectCounter() { + return this.sideEffectCounter; + } + + setSideEffectCounter(sideEffectCounter: number) { + this.sideEffectCounter = sideEffectCounter; + } + setContractAddress(contractAddress: AztecAddress) { this.contractAddress = contractAddress; } @@ -68,20 +99,37 @@ export class TXE implements TypedOracle { return this.trees; } - getContractInstanceStore() { - return this.contractInstanceStore; + getTXEDatabase() { + return this.txeDatabase; } getKeyStore() { return this.keyStore; } - getAccountStore() { - return this.accountStore; + async addContractInstance(contractInstance: ContractInstanceWithAddress) { + await this.txeDatabase.addContractInstance(contractInstance); } - async addContractInstance(contractInstance: ContractInstanceWithAddress) { - await this.contractInstanceStore.addContractInstance(contractInstance); + async addContractArtifact(artifact: ContractArtifact) { + const contractClass = getContractClassFromArtifact(artifact); + await this.txeDatabase.addContractArtifact(computeContractClassId(contractClass), artifact); + } + + async getPrivateContextInputs( + blockNumber: number, + msgSender = AztecAddress.random(), + contractAddress = this.contractAddress, + ) { + const trees = this.getTrees(); + const stateReference = await trees.getStateReference(true); + const inputs = PrivateContextInputs.empty(); + inputs.historicalHeader.globalVariables.blockNumber = new Fr(blockNumber); + inputs.historicalHeader.state = stateReference; + inputs.callContext.msgSender = msgSender; + inputs.callContext.storageContractAddress = contractAddress; + inputs.callContext.sideEffectCounter = this.getSideEffectCounter(); + return inputs; } // TypedOracle @@ -114,8 +162,8 @@ export class TXE implements TypedOracle { return this.keyStore.getKeyValidationRequest(pkMHash, this.contractAddress); } - getContractInstance(address: AztecAddress): Promise { - const contractInstance = this.contractInstanceStore.getContractInstance(address); + async getContractInstance(address: AztecAddress): Promise { + const contractInstance = await this.txeDatabase.getContractInstance(address); if (!contractInstance) { throw new Error(`Contract instance not found for address ${address}`); } @@ -166,7 +214,7 @@ export class TXE implements TypedOracle { } getCompleteAddress(account: AztecAddress): Promise { - return Promise.resolve(this.accountStore.getAccount(account)); + return Promise.resolve(this.txeDatabase.getAccount(account)); } getAuthWitness(_messageHash: Fr): Promise { @@ -305,14 +353,14 @@ export class TXE implements TypedOracle { } emitEncryptedLog(_contractAddress: AztecAddress, _randomness: Fr, _encryptedNote: Buffer, _counter: number): void { - throw new Error('Method not implemented.'); + return; } emitEncryptedNoteLog(_noteHashCounter: number, _encryptedNote: Buffer, _counter: number): void { - throw new Error('Method not implemented.'); + return; } - computeEncryptedLog( + computeEncryptedNoteLog( contractAddress: AztecAddress, storageSlot: Fr, noteTypeId: Fr, @@ -322,7 +370,7 @@ export class TXE implements TypedOracle { ): Buffer { const note = new Note(preimage); const l1NotePayload = new L1NotePayload(note, contractAddress, storageSlot, noteTypeId); - const taggedNote = new TaggedNote(l1NotePayload); + const taggedNote = new TaggedLog(l1NotePayload); const ephSk = GrumpkinScalar.random(); @@ -339,15 +387,85 @@ export class TXE implements TypedOracle { throw new Error('Method not implemented.'); } - callPrivateFunction( - _targetContractAddress: AztecAddress, - _functionSelector: FunctionSelector, - _argsHash: Fr, + async callPrivateFunction( + targetContractAddress: AztecAddress, + functionSelector: FunctionSelector, + argsHash: Fr, _sideEffectCounter: number, _isStaticCall: boolean, _isDelegateCall: boolean, ): Promise { - throw new Error('Method not implemented.'); + this.logger.debug( + `Calling private function ${targetContractAddress}:${functionSelector} from ${this.contractAddress}`, + ); + const artifact = await this.contractDataOracle.getFunctionArtifact(targetContractAddress, functionSelector); + + const acir = artifact.bytecode; + const initialWitness = await this.getInitialWitness(artifact, argsHash, targetContractAddress); + const acvmCallback = new Oracle(this); + const timer = new Timer(); + const acirExecutionResult = await acvm(acir, initialWitness, acvmCallback).catch((err: Error) => { + const execError = new ExecutionError( + err.message, + { + contractAddress: targetContractAddress, + functionSelector, + }, + extractCallStack(err, artifact.debug), + { cause: err }, + ); + this.logger.debug( + `Error executing private function ${targetContractAddress}:${functionSelector}\n${JSON.stringify( + execError, + null, + 4, + )}`, + ); + throw execError; + }); + const duration = timer.ms(); + const returnWitness = witnessMapToFields(acirExecutionResult.returnWitness); + const publicInputs = PrivateCircuitPublicInputs.fromFields(returnWitness); + + // TODO (alexg) estimate this size + const initialWitnessSize = witnessMapToFields(initialWitness).length * Fr.SIZE_IN_BYTES; + this.logger.debug(`Ran external function ${targetContractAddress.toString()}:${functionSelector}`, { + circuitName: 'app-circuit', + duration, + eventName: 'circuit-witness-generation', + inputSize: initialWitnessSize, + outputSize: publicInputs.toBuffer().length, + appCircuitName: 'noname', + } satisfies CircuitWitnessGenerationStats); + + const callStackItem = new PrivateCallStackItem( + targetContractAddress, + new FunctionData(functionSelector, true), + publicInputs, + ); + this.sideEffectCounter += publicInputs.callContext.sideEffectCounter; + + return callStackItem; + } + + async getInitialWitness(abi: FunctionAbi, argsHash: Fr, targetContractAddress: AztecAddress) { + const argumentsSize = countArgumentsSize(abi); + + const args = this.packedValuesCache.unpack(argsHash); + + if (args.length !== argumentsSize) { + throw new Error('Invalid arguments size'); + } + + const privateContextInputs = await this.getPrivateContextInputs( + this.blockNumber - 1, + this.contractAddress, + targetContractAddress, + ); + + const fields = [...privateContextInputs.toFields(), ...args]; + + return toACVMWitness(0, fields); } callPublicFunction( @@ -391,4 +509,24 @@ export class TXE implements TypedOracle { debugLog(message: string, fields: Fr[]): void { this.logger.verbose(`debug_log ${applyStringFormatting(message, fields)}`); } + + emitEncryptedEventLog( + _contractAddress: AztecAddress, + _randomness: Fr, + _encryptedEvent: Buffer, + _counter: number, + ): void { + return; + } + + computeEncryptedEventLog( + _contractAddress: AztecAddress, + _randomness: Fr, + _eventTypeId: Fr, + _ovKeys: KeyValidationRequest, + _ivpkM: Point, + _preimage: Fr[], + ): Buffer { + throw new Error('Method not implemented.'); + } } diff --git a/yarn-project/txe/src/txe_service/txe_service.ts b/yarn-project/txe/src/txe_service/txe_service.ts index ea47b17044cf..9783829661d4 100644 --- a/yarn-project/txe/src/txe_service/txe_service.ts +++ b/yarn-project/txe/src/txe_service/txe_service.ts @@ -1,7 +1,7 @@ -import { ContractInstanceStore } from '@aztec/archiver'; import { L2Block, MerkleTreeId, PublicDataWrite } from '@aztec/circuit-types'; import { Fr, + FunctionSelector, Header, KeyValidationRequest, PUBLIC_DATA_SUBTREE_HEIGHT, @@ -20,7 +20,6 @@ import { ExecutionNoteCache, PackedValuesCache, type TypedOracle } from '@aztec/ import { MerkleTrees } from '@aztec/world-state'; import { TXE } from '../oracle/txe_oracle.js'; -import { AccountStore } from '../util/account_store.js'; import { type ForeignCallArray, type ForeignCallSingle, @@ -30,6 +29,7 @@ import { toForeignCallResult, toSingle, } from '../util/encoding.js'; +import { TXEDatabase } from '../util/txe_database.js'; export class TXEService { constructor(private logger: Logger, private typedOracle: TypedOracle, private store: AztecKVStore) {} @@ -39,20 +39,10 @@ export class TXEService { const trees = await MerkleTrees.new(store, logger); const packedValuesCache = new PackedValuesCache(); const noteCache = new ExecutionNoteCache(); - const contractInstanceStore = new ContractInstanceStore(store); const keyStore = new KeyStore(store); - const accountStore = new AccountStore(store); + const txeDatabase = new TXEDatabase(store); logger.info(`TXE service initialized`); - const txe = new TXE( - logger, - trees, - packedValuesCache, - noteCache, - contractInstanceStore, - keyStore, - accountStore, - contractAddress, - ); + const txe = new TXE(logger, trees, packedValuesCache, noteCache, keyStore, txeDatabase, contractAddress); const service = new TXEService(logger, txe, store); await service.timeTravel(toSingle(new Fr(1n))); return service; @@ -61,13 +51,7 @@ export class TXEService { // Cheatcodes async getPrivateContextInputs(blockNumber: ForeignCallSingle) { - const inputs = PrivateContextInputs.empty(); - const trees = (this.typedOracle as TXE).getTrees(); - const stateReference = await trees.getStateReference(true); - inputs.historicalHeader.globalVariables.blockNumber = fromSingle(blockNumber); - inputs.historicalHeader.state = stateReference; - inputs.callContext.msgSender = AztecAddress.random(); - inputs.callContext.storageContractAddress = await this.typedOracle.getContractAddress(); + const inputs = await (this.typedOracle as TXE).getPrivateContextInputs(fromSingle(blockNumber).toNumber()); return toForeignCallResult(inputs.toFields().map(toSingle)); } @@ -104,17 +88,15 @@ export class TXEService { async reset() { this.store = openTmpStore(true); const trees = await MerkleTrees.new(this.store, this.logger); - const contractInstanceStore = new ContractInstanceStore(this.store); const keyStore = new KeyStore(this.store); - const accountStore = new AccountStore(this.store); + const txeDatabase = new TXEDatabase(this.store); this.typedOracle = new TXE( this.logger, trees, new PackedValuesCache(), new ExecutionNoteCache(), - contractInstanceStore, keyStore, - accountStore, + txeDatabase, AztecAddress.random(), ); await this.timeTravel(toSingle(new Fr(1))); @@ -148,11 +130,13 @@ export class TXEService { constructorArgs: decodedArgs, salt: Fr.ONE, publicKeysHash: Fr.ZERO, - constructorArtifact: initializerStr, + constructorArtifact: initializerStr ? initializerStr : undefined, deployer: AztecAddress.ZERO, }); + this.logger.debug(`Deployed ${contractClass.artifact.name} at ${instance.address}`); await (this.typedOracle as TXE).addContractInstance(instance); + await (this.typedOracle as TXE).addContractArtifact(contractClass.artifact); return toForeignCallResult([toSingle(instance.address)]); } @@ -180,11 +164,21 @@ export class TXEService { return toForeignCallResult([toArray(publicDataWrites.map(write => write.newValue))]); } - async addAccount(secret: ForeignCallSingle) { - const secretKey = fromSingle(secret); + async createAccount() { const keyStore = (this.typedOracle as TXE).getKeyStore(); - const completeAddress = await keyStore.addAccount(secretKey, Fr.ONE); - const accountStore = (this.typedOracle as TXE).getAccountStore(); + const completeAddress = await keyStore.createAccount(); + const accountStore = (this.typedOracle as TXE).getTXEDatabase(); + await accountStore.setAccount(completeAddress.address, completeAddress); + return toForeignCallResult([ + toSingle(completeAddress.address), + ...completeAddress.publicKeys.toFields().map(toSingle), + ]); + } + + async addAccount(secret: ForeignCallSingle, partialAddress: ForeignCallSingle) { + const keyStore = (this.typedOracle as TXE).getKeyStore(); + const completeAddress = await keyStore.addAccount(fromSingle(secret), fromSingle(partialAddress)); + const accountStore = (this.typedOracle as TXE).getTXEDatabase(); await accountStore.setAccount(completeAddress.address, completeAddress); return toForeignCallResult([ toSingle(completeAddress.address), @@ -403,7 +397,7 @@ export class TXEService { return toForeignCallResult([toArray(keyValidationRequest.toFields())]); } - computeEncryptedLog( + computeEncryptedNoteLog( contractAddress: ForeignCallSingle, storageSlot: ForeignCallSingle, noteTypeId: ForeignCallSingle, @@ -417,7 +411,7 @@ export class TXEService { const ovpkM = new Point(fromSingle(ovpkMX), fromSingle(ovpkMY)); const ovKeys = new KeyValidationRequest(ovpkM, Fr.fromString(fromSingle(ovskApp).toString())); const ivpkM = new Point(fromSingle(ivpkMX), fromSingle(ivpkMY)); - const encLog = this.typedOracle.computeEncryptedLog( + const encLog = this.typedOracle.computeEncryptedNoteLog( AztecAddress.fromString(fromSingle(contractAddress).toString()), Fr.fromString(fromSingle(storageSlot).toString()), Fr.fromString(fromSingle(noteTypeId).toString()), @@ -449,4 +443,23 @@ export class TXEService { ) { return toForeignCallResult([]); } + + async callPrivateFunction( + targetContractAddress: ForeignCallSingle, + functionSelector: ForeignCallSingle, + argsHash: ForeignCallSingle, + sideEffectCounter: ForeignCallSingle, + isStaticCall: ForeignCallSingle, + isDelegateCall: ForeignCallSingle, + ) { + const result = await this.typedOracle.callPrivateFunction( + fromSingle(targetContractAddress), + FunctionSelector.fromField(fromSingle(functionSelector)), + fromSingle(argsHash), + fromSingle(sideEffectCounter).toNumber(), + fromSingle(isStaticCall).toBool(), + fromSingle(isDelegateCall).toBool(), + ); + return toForeignCallResult([toArray(result.toFields())]); + } } diff --git a/yarn-project/txe/src/util/account_store.ts b/yarn-project/txe/src/util/txe_database.ts similarity index 55% rename from yarn-project/txe/src/util/account_store.ts rename to yarn-project/txe/src/util/txe_database.ts index f35589812dc9..b154fd8702a2 100644 --- a/yarn-project/txe/src/util/account_store.ts +++ b/yarn-project/txe/src/util/txe_database.ts @@ -1,16 +1,21 @@ import { type AztecAddress, CompleteAddress } from '@aztec/circuits.js'; import { type AztecKVStore, type AztecMap } from '@aztec/kv-store'; +import { KVPxeDatabase } from '@aztec/pxe'; -export class AccountStore { +export class TXEDatabase extends KVPxeDatabase { #accounts: AztecMap; - constructor(database: AztecKVStore) { - this.#accounts = database.openMap('accounts'); + constructor(db: AztecKVStore) { + super(db); + this.#accounts = db.openMap('accounts'); } getAccount(key: AztecAddress) { const completeAddress = this.#accounts.get(key.toString()); - return CompleteAddress.fromBuffer(completeAddress!); + if (!completeAddress) { + throw new Error(`Account not found: ${key.toString()}`); + } + return CompleteAddress.fromBuffer(completeAddress); } async setAccount(key: AztecAddress, value: CompleteAddress) { diff --git a/yarn-project/yarn.lock b/yarn-project/yarn.lock index 321559b00b05..c09a315a1b0e 100644 --- a/yarn-project/yarn.lock +++ b/yarn-project/yarn.lock @@ -944,6 +944,7 @@ __metadata: "@aztec/foundation": "workspace:^" "@aztec/key-store": "workspace:^" "@aztec/kv-store": "workspace:^" + "@aztec/pxe": "workspace:^" "@aztec/simulator": "workspace:^" "@aztec/types": "workspace:^" "@aztec/world-state": "workspace:^" From 5a925532f5fee78486de63dfdd60e27ebc2a8c5a Mon Sep 17 00:00:00 2001 From: thunkar Date: Wed, 12 Jun 2024 16:13:42 +0000 Subject: [PATCH 39/60] private calls --- .../aztec/src/note/note_getter/test.nr | 7 ++-- .../src/state_vars/private_mutable/test.nr | 7 ++-- .../src/test/helpers/test_environment.nr | 35 +------------------ .../contracts/parent_contract/src/main.nr | 27 +++++++++++--- .../contracts/token_contract/src/main.nr | 3 ++ yarn-project/txe/src/oracle/txe_oracle.ts | 33 ++++++++--------- 6 files changed, 49 insertions(+), 63 deletions(-) diff --git a/noir-projects/aztec-nr/aztec/src/note/note_getter/test.nr b/noir-projects/aztec-nr/aztec/src/note/note_getter/test.nr index 0ba1c1c08bb5..9ee0738312ef 100644 --- a/noir-projects/aztec-nr/aztec/src/note/note_getter/test.nr +++ b/noir-projects/aztec-nr/aztec/src/note/note_getter/test.nr @@ -11,15 +11,14 @@ use dep::protocol_types::address::AztecAddress; use crate::test::{helpers::{test_environment::TestEnvironment, cheatcodes}, mocks::mock_note::MockNote}; -global contract_address = AztecAddress::from_field(69); global storage_slot: Field = 42; fn setup() -> TestEnvironment { - TestEnvironment::new().contract_address(contract_address) + TestEnvironment::new() } fn build_valid_note(value: Field) -> MockNote { - MockNote::new(value).contract_address(contract_address).storage_slot(storage_slot).build() + MockNote::new(value).contract_address(cheatcodes::get_contract_address()).storage_slot(storage_slot).build() } #[test] @@ -161,7 +160,7 @@ fn rejects_mismatched_storage_slot() { let mut env = setup(); let mut context = env.private(); - let note = MockNote::new(1).contract_address(contract_address).build(); // We're not setting the right storage slot + let note = MockNote::new(1).contract_address(cheatcodes::get_contract_address()).build(); // We're not setting the right storage slot let mut opt_notes = [Option::none(); MAX_NOTE_HASH_READ_REQUESTS_PER_CALL]; opt_notes[0] = Option::some(note); diff --git a/noir-projects/aztec-nr/aztec/src/state_vars/private_mutable/test.nr b/noir-projects/aztec-nr/aztec/src/state_vars/private_mutable/test.nr index 3db22ca38e24..6c1b3569fa76 100644 --- a/noir-projects/aztec-nr/aztec/src/state_vars/private_mutable/test.nr +++ b/noir-projects/aztec-nr/aztec/src/state_vars/private_mutable/test.nr @@ -1,13 +1,12 @@ use dep::protocol_types::{address::AztecAddress, grumpkin_point::GrumpkinPoint}; use crate::{context::PrivateContext, state_vars::private_mutable::PrivateMutable}; -use crate::test::{mocks::mock_note::MockNote, helpers::test_environment::TestEnvironment}; +use crate::test::{mocks::mock_note::MockNote, helpers::{cheatcodes, test_environment::TestEnvironment}}; use dep::std::{unsafe::zeroed, test::OracleMock}; -global contract_address = AztecAddress::from_field(13); global storage_slot = 17; fn setup() -> TestEnvironment { - TestEnvironment::new().contract_address(contract_address) + TestEnvironment::new() } fn in_private(env: &mut TestEnvironment) -> PrivateMutable { @@ -28,7 +27,7 @@ fn test_initialize_or_replace_without_nullifier() { let ivpk_m: GrumpkinPoint = zeroed(); let value = 42; - let mut note = MockNote::new(value).contract_address(contract_address).storage_slot(storage_slot).build(); + let mut note = MockNote::new(value).contract_address(cheatcodes::get_contract_address()).storage_slot(storage_slot).build(); OracleMock::mock("checkNullifierExists").returns(0); state_var.initialize_or_replace(&mut note, ovpk_m, ivpk_m); diff --git a/noir-projects/aztec-nr/aztec/src/test/helpers/test_environment.nr b/noir-projects/aztec-nr/aztec/src/test/helpers/test_environment.nr index 4a837a918dff..28485ae60a5c 100644 --- a/noir-projects/aztec-nr/aztec/src/test/helpers/test_environment.nr +++ b/noir-projects/aztec-nr/aztec/src/test/helpers/test_environment.nr @@ -28,21 +28,6 @@ impl TestEnvironment { cheatcodes::get_block_number() } - fn contract_address(&mut self, contract_address: AztecAddress) -> Self { - self.contract_address = Option::some(contract_address); - *self - } - - fn function_selector(&mut self, function_selector: FunctionSelector) -> Self { - self.function_selector = Option::some(function_selector); - *self - } - - fn args_hash(&mut self, args_hash: Field) -> Self { - self.args_hash = Option::some(args_hash); - *self - } - fn advance_block_to(&mut self, block_number: u32) { let difference = block_number - cheatcodes::get_block_number(); self.advance_block_by(difference); @@ -53,10 +38,6 @@ impl TestEnvironment { } fn public(self) -> PublicContext { - if (self.contract_address.is_some()) { - cheatcodes::set_contract_address(self.contract_address.unwrap_unchecked()); - } - PublicContext::empty() } @@ -71,21 +52,7 @@ impl TestEnvironment { let mut inputs = cheatcodes::get_private_context_inputs(historical_block_number); - if (self.contract_address.is_some()) { - inputs.call_context.storage_contract_address = self.contract_address.unwrap_unchecked(); - } - - if (self.function_selector.is_some()) { - inputs.call_context.function_selector = self.function_selector.unwrap_unchecked(); - } - - let mut args_hash = 0; - - if (self.args_hash.is_some()) { - args_hash = self.args_hash.unwrap_unchecked(); - } - - PrivateContext::new(inputs, args_hash) + PrivateContext::new(inputs, 0) } fn create_account(self) -> AztecAddress { diff --git a/noir-projects/noir-contracts/contracts/parent_contract/src/main.nr b/noir-projects/noir-contracts/contracts/parent_contract/src/main.nr index d2163b7f8ce6..75239c195375 100644 --- a/noir-projects/noir-contracts/contracts/parent_contract/src/main.nr +++ b/noir-projects/noir-contracts/contracts/parent_contract/src/main.nr @@ -256,23 +256,40 @@ contract Parent { // Setup env, generate keys let mut env = TestEnvironment::new(); let owner = env.create_account(); + let parent_contract_address = cheatcodes::get_contract_address(); // Deploy child contract let child_contract_address = env.deploy("@aztec/noir-contracts.js/Child").without_initializer(); cheatcodes::advance_blocks(1); + // Set value in child through parent - let call_interface = Parent::interface().private_call( + let value_to_set = 7; + let parent_private_set_call_interface = Parent::interface().private_call( child_contract_address, FunctionSelector::from_signature("private_set_value(Field,(Field))"), - [7, owner.to_field()] + [value_to_set, owner.to_field()] ); - let result: Field = env.call_private(call_interface); + let result: Field = env.call_private(parent_private_set_call_interface); + assert(result == value_to_set); + // Read the stored value in the note - //cheatcodes::set_contract_address(child_contract_address); + cheatcodes::set_contract_address(child_contract_address); let counter_slot = Child::storage().a_map_with_private_values.slot; let owner_slot = derive_storage_slot_in_map(counter_slot, owner); let mut options = NoteViewerOptions::new(); let opt_notes: [Option; MAX_NOTES_PER_PAGE] = view_notes(owner_slot, options); - assert(opt_notes[0].unwrap().value == result); + let note_value = opt_notes[0].unwrap().value; + assert(note_value == value_to_set); + assert(note_value == result); + + // Get value from child through parent + cheatcodes::set_contract_address(parent_contract_address); + let parent_private_get_call_interface = Parent::interface().private_call( + child_contract_address, + FunctionSelector::from_signature("private_get_value(Field,(Field))"), + [7, owner.to_field()] + ); + let read_result: Field = env.call_private(parent_private_get_call_interface); + assert(note_value == read_result); } } diff --git a/noir-projects/noir-contracts/contracts/token_contract/src/main.nr b/noir-projects/noir-contracts/contracts/token_contract/src/main.nr index 6360cbc442eb..f53f18b8cdd2 100644 --- a/noir-projects/noir-contracts/contracts/token_contract/src/main.nr +++ b/noir-projects/noir-contracts/contracts/token_contract/src/main.nr @@ -370,5 +370,8 @@ contract Token { storage.balances.balance_of(owner).to_field() } // docs:end:balance_of_private + + #[test] + fn test_private_transfer() {} } // docs:end:token_all diff --git a/yarn-project/txe/src/oracle/txe_oracle.ts b/yarn-project/txe/src/oracle/txe_oracle.ts index cd15873be1e0..ca81dc05ea3b 100644 --- a/yarn-project/txe/src/oracle/txe_oracle.ts +++ b/yarn-project/txe/src/oracle/txe_oracle.ts @@ -50,6 +50,7 @@ import { type PackedValuesCache, type TypedOracle, acvm, + createSimulationError, extractCallStack, pickNotes, toACVMWitness, @@ -119,7 +120,7 @@ export class TXE implements TypedOracle { async getPrivateContextInputs( blockNumber: number, msgSender = AztecAddress.random(), - contractAddress = this.contractAddress, + sideEffectCounter = this.sideEffectCounter, ) { const trees = this.getTrees(); const stateReference = await trees.getStateReference(true); @@ -127,8 +128,9 @@ export class TXE implements TypedOracle { inputs.historicalHeader.globalVariables.blockNumber = new Fr(blockNumber); inputs.historicalHeader.state = stateReference; inputs.callContext.msgSender = msgSender; - inputs.callContext.storageContractAddress = contractAddress; - inputs.callContext.sideEffectCounter = this.getSideEffectCounter(); + inputs.callContext.storageContractAddress = this.contractAddress; + inputs.callContext.sideEffectCounter = sideEffectCounter; + inputs.startSideEffectCounter = sideEffectCounter; return inputs; } @@ -391,17 +393,21 @@ export class TXE implements TypedOracle { targetContractAddress: AztecAddress, functionSelector: FunctionSelector, argsHash: Fr, - _sideEffectCounter: number, + sideEffectCounter: number, _isStaticCall: boolean, _isDelegateCall: boolean, ): Promise { this.logger.debug( `Calling private function ${targetContractAddress}:${functionSelector} from ${this.contractAddress}`, ); + // Modify env + const from = AztecAddress.fromField(this.contractAddress); + this.setContractAddress(targetContractAddress); + const artifact = await this.contractDataOracle.getFunctionArtifact(targetContractAddress, functionSelector); const acir = artifact.bytecode; - const initialWitness = await this.getInitialWitness(artifact, argsHash, targetContractAddress); + const initialWitness = await this.getInitialWitness(artifact, argsHash, from, sideEffectCounter); const acvmCallback = new Oracle(this); const timer = new Timer(); const acirExecutionResult = await acvm(acir, initialWitness, acvmCallback).catch((err: Error) => { @@ -415,10 +421,8 @@ export class TXE implements TypedOracle { { cause: err }, ); this.logger.debug( - `Error executing private function ${targetContractAddress}:${functionSelector}\n${JSON.stringify( + `Error executing private function ${targetContractAddress}:${functionSelector}\n${createSimulationError( execError, - null, - 4, )}`, ); throw execError; @@ -427,7 +431,6 @@ export class TXE implements TypedOracle { const returnWitness = witnessMapToFields(acirExecutionResult.returnWitness); const publicInputs = PrivateCircuitPublicInputs.fromFields(returnWitness); - // TODO (alexg) estimate this size const initialWitnessSize = witnessMapToFields(initialWitness).length * Fr.SIZE_IN_BYTES; this.logger.debug(`Ran external function ${targetContractAddress.toString()}:${functionSelector}`, { circuitName: 'app-circuit', @@ -443,12 +446,14 @@ export class TXE implements TypedOracle { new FunctionData(functionSelector, true), publicInputs, ); - this.sideEffectCounter += publicInputs.callContext.sideEffectCounter; + // Apply side effects + this.sideEffectCounter += publicInputs.endSideEffectCounter.toNumber(); + this.setContractAddress(from); return callStackItem; } - async getInitialWitness(abi: FunctionAbi, argsHash: Fr, targetContractAddress: AztecAddress) { + async getInitialWitness(abi: FunctionAbi, argsHash: Fr, msgSender: AztecAddress, sideEffectCounter: number) { const argumentsSize = countArgumentsSize(abi); const args = this.packedValuesCache.unpack(argsHash); @@ -457,11 +462,7 @@ export class TXE implements TypedOracle { throw new Error('Invalid arguments size'); } - const privateContextInputs = await this.getPrivateContextInputs( - this.blockNumber - 1, - this.contractAddress, - targetContractAddress, - ); + const privateContextInputs = await this.getPrivateContextInputs(this.blockNumber - 1, msgSender, sideEffectCounter); const fields = [...privateContextInputs.toFields(), ...args]; From 5e9486eb417d3dcb30efc0b7c9ac7f5c4a53e8fd Mon Sep 17 00:00:00 2001 From: thunkar Date: Thu, 13 Jun 2024 14:04:54 +0000 Subject: [PATCH 40/60] public initializers --- .../aztec/src/context/call_interfaces.nr | 141 ++++++++++++++++++ .../aztec/src/test/helpers/cheatcodes.nr | 40 ++++- .../src/test/helpers/test_environment.nr | 73 ++++++++- .../aztec-nr/aztec/src/test/helpers/types.nr | 32 +++- .../contracts/child_contract/src/main.nr | 2 +- .../contracts/counter_contract/src/main.nr | 4 +- .../contracts/parent_contract/src/main.nr | 11 +- .../contracts/token_contract/src/main.nr | 42 +++++- .../src/transforms/contract_interface.rs | 4 +- .../src/contract/contract_instance.ts | 22 ++- yarn-project/txe/src/oracle/txe_oracle.ts | 111 +++++++++++--- .../txe/src/txe_service/txe_service.ts | 81 +++++++++- 12 files changed, 510 insertions(+), 53 deletions(-) diff --git a/noir-projects/aztec-nr/aztec/src/context/call_interfaces.nr b/noir-projects/aztec-nr/aztec/src/context/call_interfaces.nr index d57a4eb3b3d7..dd1374f9eb01 100644 --- a/noir-projects/aztec-nr/aztec/src/context/call_interfaces.nr +++ b/noir-projects/aztec-nr/aztec/src/context/call_interfaces.nr @@ -15,6 +15,7 @@ trait CallInterface { fn get_original(self) -> fn[Env](T) -> P; fn get_selector(self) -> FunctionSelector; fn get_name(self) -> str; + fn get_contract_address(self) -> AztecAddress; } impl CallInterface for PrivateCallInterface { @@ -33,6 +34,10 @@ impl CallInterface str { self.name } + + fn get_contract_address(self) -> AztecAddress { + self.target_contract + } } struct PrivateCallInterface { @@ -84,6 +89,10 @@ impl CallInterface str { self.name } + + fn get_contract_address(self) -> AztecAddress { + self.target_contract + } } struct PrivateVoidCallInterface { @@ -115,6 +124,28 @@ impl PrivateVoidCallInterface { } } +impl CallInterface for PrivateStaticCallInterface { + fn get_args(self) -> [Field] { + self.args + } + + fn get_original(self) -> fn[Env](PrivateContextInputs) -> PrivateCircuitPublicInputs { + self.original + } + + fn get_selector(self) -> FunctionSelector { + self.selector + } + + fn get_name(self) -> str { + self.name + } + + fn get_contract_address(self) -> AztecAddress { + self.target_contract + } +} + struct PrivateStaticCallInterface { target_contract: AztecAddress, selector: FunctionSelector, @@ -131,6 +162,28 @@ impl PrivateStaticCallInterface { } } +impl CallInterface for PrivateStaticVoidCallInterface { + fn get_args(self) -> [Field] { + self.args + } + + fn get_original(self) -> fn[Env](PrivateContextInputs) -> PrivateCircuitPublicInputs { + self.original + } + + fn get_selector(self) -> FunctionSelector { + self.selector + } + + fn get_name(self) -> str { + self.name + } + + fn get_contract_address(self) -> AztecAddress { + self.target_contract + } +} + struct PrivateStaticVoidCallInterface { target_contract: AztecAddress, selector: FunctionSelector, @@ -146,6 +199,28 @@ impl PrivateStaticVoidCallInterface { } } +impl CallInterface for PublicCallInterface { + fn get_args(self) -> [Field] { + self.args + } + + fn get_original(self) -> fn[Env](PublicContextInputs) -> T { + self.original + } + + fn get_selector(self) -> FunctionSelector { + self.selector + } + + fn get_name(self) -> str { + self.name + } + + fn get_contract_address(self) -> AztecAddress { + self.target_contract + } +} + struct PublicCallInterface { target_contract: AztecAddress, selector: FunctionSelector, @@ -213,6 +288,28 @@ impl PublicCallInterface { } } +impl CallInterface for PublicVoidCallInterface { + fn get_args(self) -> [Field] { + self.args + } + + fn get_original(self) -> fn[Env](PublicContextInputs) -> () { + self.original + } + + fn get_selector(self) -> FunctionSelector { + self.selector + } + + fn get_name(self) -> str { + self.name + } + + fn get_contract_address(self) -> AztecAddress { + self.target_contract + } +} + struct PublicVoidCallInterface { target_contract: AztecAddress, selector: FunctionSelector, @@ -280,6 +377,28 @@ impl PublicVoidCallInterface { } } +impl CallInterface for PublicStaticCallInterface { + fn get_args(self) -> [Field] { + self.args + } + + fn get_original(self) -> fn[Env](PublicContextInputs) -> T { + self.original + } + + fn get_selector(self) -> FunctionSelector { + self.selector + } + + fn get_name(self) -> str { + self.name + } + + fn get_contract_address(self) -> AztecAddress { + self.target_contract + } +} + struct PublicStaticCallInterface { target_contract: AztecAddress, selector: FunctionSelector, @@ -314,6 +433,28 @@ impl PublicStaticCallInterface { } } +impl CallInterface for PublicStaticVoidCallInterface { + fn get_args(self) -> [Field] { + self.args + } + + fn get_original(self) -> fn[Env](PublicContextInputs) -> () { + self.original + } + + fn get_selector(self) -> FunctionSelector { + self.selector + } + + fn get_name(self) -> str { + self.name + } + + fn get_contract_address(self) -> AztecAddress { + self.target_contract + } +} + struct PublicStaticVoidCallInterface { target_contract: AztecAddress, selector: FunctionSelector, diff --git a/noir-projects/aztec-nr/aztec/src/test/helpers/cheatcodes.nr b/noir-projects/aztec-nr/aztec/src/test/helpers/cheatcodes.nr index 44c22ef574ac..55f05b1554a4 100644 --- a/noir-projects/aztec-nr/aztec/src/test/helpers/cheatcodes.nr +++ b/noir-projects/aztec-nr/aztec/src/test/helpers/cheatcodes.nr @@ -1,6 +1,7 @@ -use dep::protocol_types::address::{AztecAddress, PartialAddress}; +use dep::protocol_types::{abis::function_selector::FunctionSelector, address::{AztecAddress, PartialAddress}}; use crate::context::inputs::{PublicContextInputs, PrivateContextInputs}; use crate::test::helpers::types::{Deployer, TestAccount}; +use crate::keys::public_keys::PublicKeys; unconstrained pub fn reset() { oracle_reset(); @@ -30,8 +31,13 @@ unconstrained pub fn get_public_context_inputs() -> PublicContextInputs { oracle_get_public_context_inputs() } -unconstrained pub fn deploy(path: str, initializer: str, args: [Field]) -> AztecAddress { - oracle_deploy(path, initializer, args) +unconstrained pub fn deploy( + path: str, + initializer: str, + args: [Field], + public_keys_hash: Field +) -> AztecAddress { + oracle_deploy(path, initializer, args, public_keys_hash) } unconstrained pub fn direct_storage_write( @@ -50,6 +56,18 @@ unconstrained pub fn add_account(secret: Field, partial_address: PartialAddress) oracle_add_account(secret, partial_address) } +unconstrained pub fn derive_keys(secret: Field) -> PublicKeys { + oracle_derive_keys(secret) +} + +unconstrained pub fn set_msg_sender(msg_sender: AztecAddress) { + oracle_set_msg_sender(msg_sender) +} + +unconstrained pub fn get_msg_sender() -> AztecAddress { + oracle_get_msg_sender() +} + #[oracle(reset)] fn oracle_reset() {} @@ -72,7 +90,12 @@ fn oracle_get_private_context_inputs(historical_block_number: u32) -> PrivateCon fn oracle_get_public_context_inputs() -> PublicContextInputs {} #[oracle(deploy)] -fn oracle_deploy(path: str, initializer: str, args: [Field]) -> AztecAddress {} +fn oracle_deploy( + path: str, + initializer: str, + args: [Field], + public_keys_hash: Field +) -> AztecAddress {} #[oracle(directStorageWrite)] fn direct_storage_write_oracle( @@ -86,3 +109,12 @@ fn oracle_create_account() -> TestAccount {} #[oracle(addAccount)] fn oracle_add_account(secret: Field, partial_address: PartialAddress) -> TestAccount {} + +#[oracle(deriveKeys)] +fn oracle_derive_keys(secret: Field) -> PublicKeys {} + +#[oracle(getMsgSender)] +fn oracle_get_msg_sender() -> AztecAddress {} + +#[oracle(setMsgSender)] +fn oracle_set_msg_sender(msg_sender: AztecAddress) {} diff --git a/noir-projects/aztec-nr/aztec/src/test/helpers/test_environment.nr b/noir-projects/aztec-nr/aztec/src/test/helpers/test_environment.nr index 28485ae60a5c..d3db6178f17b 100644 --- a/noir-projects/aztec-nr/aztec/src/test/helpers/test_environment.nr +++ b/noir-projects/aztec-nr/aztec/src/test/helpers/test_environment.nr @@ -10,6 +10,7 @@ use crate::context::{packed_returns::PackedReturns, call_interfaces::CallInterfa use crate::context::{PrivateContext, PublicContext, PrivateVoidCallInterface}; use crate::test::helpers::{cheatcodes, types::{Deployer, TestAccount}, keys}; use crate::keys::constants::{NULLIFIER_INDEX, INCOMING_INDEX, OUTGOING_INDEX, TAGGING_INDEX}; +use crate::hash::hash_args; struct TestEnvironment { contract_address: Option, @@ -68,8 +69,30 @@ impl TestEnvironment { test_account.address } + fn create_account_contract(self, secret: Field) -> AztecAddress { + let public_keys = cheatcodes::derive_keys(secret); + let args = &[public_keys.ivpk_m.x, public_keys.ivpk_m.y]; + let address = cheatcodes::deploy( + "@aztec/noir-contracts.js/SchnorrAccount", + "constructor", + args, + public_keys.hash().to_field() + ); + cheatcodes::advance_blocks(1); + let test_account = cheatcodes::add_account(secret, PartialAddress::from_field(address.to_field())); + let address = test_account.address; + let keys = test_account.keys; + + keys::store_master_key(NULLIFIER_INDEX, address, keys.npk_m); + keys::store_master_key(INCOMING_INDEX, address, keys.ivpk_m); + keys::store_master_key(OUTGOING_INDEX, address, keys.ovpk_m); + keys::store_master_key(TAGGING_INDEX, address, keys.tpk_m); + + test_account.address + } + fn deploy(self, path: str) -> Deployer { - Deployer { path } + Deployer { path, public_keys_hash: 0 } } fn call_private( @@ -77,8 +100,56 @@ impl TestEnvironment { call_interface: C ) -> T where C: CallInterface, T: Deserialize { let original_fn = call_interface.get_original(); + let original_msg_sender = cheatcodes::get_msg_sender(); + let original_contract_address = cheatcodes::get_contract_address(); + let target_address = call_interface.get_contract_address(); + + cheatcodes::set_contract_address(target_address); + cheatcodes::set_msg_sender(original_contract_address); let mut inputs = cheatcodes::get_private_context_inputs(cheatcodes::get_block_number() - 1); + inputs.call_context.function_selector = call_interface.get_selector(); let public_inputs = original_fn(inputs); + + cheatcodes::set_contract_address(original_contract_address); + cheatcodes::set_msg_sender(original_msg_sender); PackedReturns::new(public_inputs.returns_hash).unpack_into() } + + fn call_private_void( + self, + call_interface: C + ) where C: CallInterface { + let original_fn = call_interface.get_original(); + let original_msg_sender = cheatcodes::get_msg_sender(); + let original_contract_address = cheatcodes::get_contract_address(); + let target_address = call_interface.get_contract_address(); + + cheatcodes::set_contract_address(target_address); + cheatcodes::set_msg_sender(original_contract_address); + let mut inputs = cheatcodes::get_private_context_inputs(cheatcodes::get_block_number() - 1); + inputs.call_context.function_selector = call_interface.get_selector(); + let public_inputs = original_fn(inputs); + + cheatcodes::set_contract_address(original_contract_address); + cheatcodes::set_msg_sender(original_msg_sender); + PackedReturns::new(public_inputs.returns_hash).assert_empty(); + } + + fn call_public(self, call_interface: C) -> T where C: CallInterface { + let original_fn = call_interface.get_original(); + let original_msg_sender = cheatcodes::get_msg_sender(); + let original_contract_address = cheatcodes::get_contract_address(); + let target_address = call_interface.get_contract_address(); + + cheatcodes::set_contract_address(target_address); + cheatcodes::set_msg_sender(original_contract_address); + let mut inputs = cheatcodes::get_public_context_inputs(); + inputs.selector = call_interface.get_selector().to_field(); + inputs.args_hash = hash_args(call_interface.get_args()); + let result = original_fn(inputs); + + cheatcodes::set_contract_address(original_contract_address); + cheatcodes::set_msg_sender(original_msg_sender); + result + } } diff --git a/noir-projects/aztec-nr/aztec/src/test/helpers/types.nr b/noir-projects/aztec-nr/aztec/src/test/helpers/types.nr index 9ce08939e8d5..0274a5c15ea7 100644 --- a/noir-projects/aztec-nr/aztec/src/test/helpers/types.nr +++ b/noir-projects/aztec-nr/aztec/src/test/helpers/types.nr @@ -7,9 +7,11 @@ use crate::context::inputs::{PublicContextInputs, PrivateContextInputs}; use crate::context::call_interfaces::CallInterface; use crate::test::helpers::cheatcodes; use crate::keys::public_keys::{PUBLIC_KEYS_LENGTH, PublicKeys}; +use crate::hash::hash_args; struct Deployer { path: str, + public_keys_hash: Field } impl Deployer { @@ -20,15 +22,23 @@ impl Deployer { let address = cheatcodes::deploy( self.path, call_interface.get_name(), - call_interface.get_args() + call_interface.get_args(), + self.public_keys_hash ); cheatcodes::advance_blocks(1); let block_number = cheatcodes::get_block_number(); let original_fn = call_interface.get_original(); + let original_msg_sender = cheatcodes::get_msg_sender(); + let original_contract_address = cheatcodes::get_contract_address(); + + cheatcodes::set_contract_address(address); + cheatcodes::set_msg_sender(original_contract_address); let mut inputs = cheatcodes::get_private_context_inputs(block_number - 1); - inputs.call_context.storage_contract_address = address; inputs.call_context.function_selector = call_interface.get_selector(); let _result = original_fn(inputs); + + cheatcodes::set_contract_address(original_contract_address); + cheatcodes::set_msg_sender(original_msg_sender); address } @@ -39,16 +49,28 @@ impl Deployer { let address = cheatcodes::deploy( self.path, call_interface.get_name(), - call_interface.get_args() + call_interface.get_args(), + self.public_keys_hash ); + cheatcodes::advance_blocks(1); let original_fn = call_interface.get_original(); + let original_msg_sender = cheatcodes::get_msg_sender(); + let original_contract_address = cheatcodes::get_contract_address(); + + cheatcodes::set_contract_address(address); + cheatcodes::set_msg_sender(original_contract_address); let mut inputs = cheatcodes::get_public_context_inputs(); - let _result = original_fn(inputs); + inputs.selector = call_interface.get_selector().to_field(); + inputs.args_hash = hash_args(call_interface.get_args()); + let _result: T = original_fn(inputs); + + cheatcodes::set_contract_address(original_contract_address); + cheatcodes::set_msg_sender(original_msg_sender); address } pub fn without_initializer(self) -> AztecAddress { - let address = cheatcodes::deploy(self.path, "", &[]); + let address = cheatcodes::deploy(self.path, "", &[], self.public_keys_hash); address } } diff --git a/noir-projects/noir-contracts/contracts/child_contract/src/main.nr b/noir-projects/noir-contracts/contracts/child_contract/src/main.nr index fea13f3b3462..bdf9390d1e47 100644 --- a/noir-projects/noir-contracts/contracts/child_contract/src/main.nr +++ b/noir-projects/noir-contracts/contracts/child_contract/src/main.nr @@ -56,7 +56,7 @@ contract Child { let owner_npk_m_hash = header.get_npk_m_hash(&mut context, owner); let mut note = ValueNote::new(new_value, owner_npk_m_hash); - storage.a_map_with_private_values.at(owner).insert(&mut note).emit(encode_and_encrypt(&mut context, context.msg_sender(), owner)); + storage.a_map_with_private_values.at(owner).insert(&mut note).emit(encode_and_encrypt(&mut context, owner, owner)); new_value } diff --git a/noir-projects/noir-contracts/contracts/counter_contract/src/main.nr b/noir-projects/noir-contracts/contracts/counter_contract/src/main.nr index dc20ba272db3..e296d211aabc 100644 --- a/noir-projects/noir-contracts/contracts/counter_contract/src/main.nr +++ b/noir-projects/noir-contracts/contracts/counter_contract/src/main.nr @@ -52,8 +52,10 @@ contract Counter { // Deploy contract and initialize let initializer = Counter::interface().initialize(5, owner, outgoing_viewer); - let _contract_address = env.deploy("@aztec/noir-contracts.js/Counter").with_private_initializer(initializer); + let contract_address = env.deploy("@aztec/noir-contracts.js/Counter").with_private_initializer(initializer); // Read the stored value in the note + + cheatcodes::set_contract_address(contract_address); let counter_slot = Counter::storage().counters.slot; let owner_slot = derive_storage_slot_in_map(counter_slot, owner); let mut options = NoteViewerOptions::new(); diff --git a/noir-projects/noir-contracts/contracts/parent_contract/src/main.nr b/noir-projects/noir-contracts/contracts/parent_contract/src/main.nr index 75239c195375..4247d25da6f8 100644 --- a/noir-projects/noir-contracts/contracts/parent_contract/src/main.nr +++ b/noir-projects/noir-contracts/contracts/parent_contract/src/main.nr @@ -245,7 +245,6 @@ contract Parent { use dep::aztec::test::{helpers::{cheatcodes, test_environment::TestEnvironment}}; use dep::aztec::protocol_types::storage::map::derive_storage_slot_in_map; - use dep::aztec::oracle::{storage::{storage_read, storage_write}}; use dep::aztec::note::note_getter::{MAX_NOTES_PER_PAGE, view_notes}; use dep::aztec::note::note_viewer_options::NoteViewerOptions; use dep::child_contract::Child; @@ -256,6 +255,7 @@ contract Parent { // Setup env, generate keys let mut env = TestEnvironment::new(); let owner = env.create_account(); + // Initial address, since we don't have to deploy Parent in order to test it let parent_contract_address = cheatcodes::get_contract_address(); // Deploy child contract @@ -264,15 +264,14 @@ contract Parent { // Set value in child through parent let value_to_set = 7; - let parent_private_set_call_interface = Parent::interface().private_call( + let parent_private_set_call_interface = Parent::at(parent_contract_address).private_call( child_contract_address, FunctionSelector::from_signature("private_set_value(Field,(Field))"), [value_to_set, owner.to_field()] ); let result: Field = env.call_private(parent_private_set_call_interface); assert(result == value_to_set); - - // Read the stored value in the note + // Read the stored value in the note. We have to change the contract address to the child contract in order to read its notes cheatcodes::set_contract_address(child_contract_address); let counter_slot = Child::storage().a_map_with_private_values.slot; let owner_slot = derive_storage_slot_in_map(counter_slot, owner); @@ -281,10 +280,8 @@ contract Parent { let note_value = opt_notes[0].unwrap().value; assert(note_value == value_to_set); assert(note_value == result); - // Get value from child through parent - cheatcodes::set_contract_address(parent_contract_address); - let parent_private_get_call_interface = Parent::interface().private_call( + let parent_private_get_call_interface = Parent::at(parent_contract_address).private_call( child_contract_address, FunctionSelector::from_signature("private_get_value(Field,(Field))"), [7, owner.to_field()] diff --git a/noir-projects/noir-contracts/contracts/token_contract/src/main.nr b/noir-projects/noir-contracts/contracts/token_contract/src/main.nr index 21223cb8e538..73340ddb4662 100644 --- a/noir-projects/noir-contracts/contracts/token_contract/src/main.nr +++ b/noir-projects/noir-contracts/contracts/token_contract/src/main.nr @@ -396,7 +396,47 @@ contract Token { } // docs:end:balance_of_private + use dep::aztec::test::{helpers::{cheatcodes, test_environment::TestEnvironment}}; + use dep::aztec::protocol_types::storage::map::derive_storage_slot_in_map; + use dep::aztec::note::note_getter::{MAX_NOTES_PER_PAGE, view_notes}; + use dep::aztec::note::note_viewer_options::NoteViewerOptions; + #[test] - fn test_private_transfer() {} + fn test_private_transfer() { + // Setup env, generate keys + let mut env = TestEnvironment::new(); + let owner = env.create_account(); + let recipient = env.create_account(); + let mint_amount = 10000; + + cheatcodes::set_contract_address(owner); + + // Deploy token contract + let initializer_call_interface = Token::interface().constructor( + owner, + "TestToken0000000000000000000000", + "TT00000000000000000000000000000", + 18 + ); + let token_contract_address = env.deploy("@aztec/noir-contracts.js/Token").with_public_initializer(initializer_call_interface); + env.advance_block_by(1); + + // Mint some tokens + let secret = 1; + let secret_hash = compute_secret_hash(secret); + let mint_private_call_interface = Token::at(token_contract_address).mint_private(mint_amount, secret_hash); + env.call_public(mint_private_call_interface); + + // Time travel so we can read keys from the registry + env.advance_block_by(6); + + // Redeem our shielded tokens + let redeem_shield_call_interface = Token::at(token_contract_address).redeem_shield(owner, mint_amount, secret); + env.call_private_void(redeem_shield_call_interface); + + // Transfer tokens + let private_token_transfer_call_interface = Token::at(token_contract_address).transfer(recipient, 1000); + env.call_private_void(private_token_transfer_call_interface); + } } // docs:end:token_all \ No newline at end of file diff --git a/noir/noir-repo/aztec_macros/src/transforms/contract_interface.rs b/noir/noir-repo/aztec_macros/src/transforms/contract_interface.rs index 5e3daced44dd..a8babdc944db 100644 --- a/noir/noir-repo/aztec_macros/src/transforms/contract_interface.rs +++ b/noir/noir-repo/aztec_macros/src/transforms/contract_interface.rs @@ -69,9 +69,9 @@ pub fn stub_function(aztec_visibility: &str, func: &NoirFunction, is_static_call match &arg.typ.typ { UnresolvedTypeData::Array(_, typ) => { format!( - "let hash_{0} = {0}.map(|x: {1}| x.serialize()); + "let serialized_{0} = {0}.map(|x: {1}| x.serialize()); for i in 0..{0}.len() {{ - args_acc = args_acc.append(hash_{0}[i].as_slice()); + args_acc = args_acc.append(serialized_{0}[i].as_slice()); }}\n", param_name, typ.typ.to_string().replace("plain::", "") diff --git a/yarn-project/circuits.js/src/contract/contract_instance.ts b/yarn-project/circuits.js/src/contract/contract_instance.ts index 4c047461664d..3168be0ce0c7 100644 --- a/yarn-project/circuits.js/src/contract/contract_instance.ts +++ b/yarn-project/circuits.js/src/contract/contract_instance.ts @@ -1,11 +1,20 @@ -import { type ContractArtifact, type FunctionArtifact, getDefaultInitializer } from '@aztec/foundation/abi'; +import { + type ContractArtifact, + type FunctionArtifact, + FunctionSelector, + getDefaultInitializer, +} from '@aztec/foundation/abi'; import { AztecAddress } from '@aztec/foundation/aztec-address'; import { Fr } from '@aztec/foundation/fields'; import { type ContractInstance, type ContractInstanceWithAddress } from '@aztec/types/contracts'; import { getContractClassFromArtifact } from '../contract/contract_class.js'; import { computeContractClassId } from '../contract/contract_class_id.js'; -import { computeContractAddressFromInstance, computeInitializationHash } from './contract_address.js'; +import { + computeContractAddressFromInstance, + computeInitializationHash, + computeInitializationHashFromEncodedArgs, +} from './contract_address.js'; /** * Generates a Contract Instance from the deployment params. @@ -18,6 +27,7 @@ export function getContractInstanceFromDeployParams( opts: { constructorArtifact?: FunctionArtifact | string; constructorArgs?: any[]; + skipArgsDecoding?: boolean; salt?: Fr; publicKeysHash?: Fr; deployer?: AztecAddress; @@ -29,7 +39,13 @@ export function getContractInstanceFromDeployParams( const deployer = opts.deployer ?? AztecAddress.ZERO; const contractClass = getContractClassFromArtifact(artifact); const contractClassId = computeContractClassId(contractClass); - const initializationHash = computeInitializationHash(constructorArtifact, args); + const initializationHash = + constructorArtifact && opts?.skipArgsDecoding + ? computeInitializationHashFromEncodedArgs( + FunctionSelector.fromNameAndParameters(constructorArtifact?.name, constructorArtifact?.parameters), + args, + ) + : computeInitializationHash(constructorArtifact, args); const publicKeysHash = opts.publicKeysHash ?? Fr.ZERO; const instance: ContractInstance = { diff --git a/yarn-project/txe/src/oracle/txe_oracle.ts b/yarn-project/txe/src/oracle/txe_oracle.ts index ca81dc05ea3b..90b521bfffcc 100644 --- a/yarn-project/txe/src/oracle/txe_oracle.ts +++ b/yarn-project/txe/src/oracle/txe_oracle.ts @@ -3,7 +3,7 @@ import { MerkleTreeId, Note, type NoteStatus, - type NullifierMembershipWitness, + NullifierMembershipWitness, PublicDataWitness, PublicDataWrite, TaggedLog, @@ -16,6 +16,8 @@ import { type Header, type KeyValidationRequest, NULLIFIER_SUBTREE_HEIGHT, + type NULLIFIER_TREE_HEIGHT, + type NullifierLeafPreimage, PUBLIC_DATA_SUBTREE_HEIGHT, type PUBLIC_DATA_TREE_HEIGHT, PrivateCallStackItem, @@ -25,16 +27,12 @@ import { PublicDataTreeLeaf, type PublicDataTreeLeafPreimage, computeContractClassId, + deriveKeys, getContractClassFromArtifact, } from '@aztec/circuits.js'; import { Aes128 } from '@aztec/circuits.js/barretenberg'; import { computePublicDataTreeLeafSlot, siloNoteHash, siloNullifier } from '@aztec/circuits.js/hash'; -import { - type ContractArtifact, - type FunctionAbi, - type FunctionSelector, - countArgumentsSize, -} from '@aztec/foundation/abi'; +import { type ContractArtifact, type FunctionAbi, FunctionSelector, countArgumentsSize } from '@aztec/foundation/abi'; import { AztecAddress } from '@aztec/foundation/aztec-address'; import { Fr, GrumpkinScalar, type Point } from '@aztec/foundation/fields'; import { type Logger, applyStringFormatting } from '@aztec/foundation/log'; @@ -64,6 +62,9 @@ import { type TXEDatabase } from '../util/txe_database.js'; export class TXE implements TypedOracle { private blockNumber = 0; private sideEffectCounter = 0; + private contractAddress: AztecAddress; + private msgSender: AztecAddress; + private contractDataOracle: ContractDataOracle; constructor( @@ -73,13 +74,22 @@ export class TXE implements TypedOracle { private noteCache: ExecutionNoteCache, private keyStore: KeyStore, private txeDatabase: TXEDatabase, - private contractAddress: AztecAddress, ) { this.contractDataOracle = new ContractDataOracle(txeDatabase); + this.contractAddress = AztecAddress.random(); + this.msgSender = AztecAddress.fromField(new Fr(0)); } // Utils + getMsgSender() { + return this.msgSender; + } + + setMsgSender(msgSender: Fr) { + this.msgSender = msgSender; + } + getSideEffectCounter() { return this.sideEffectCounter; } @@ -117,23 +127,56 @@ export class TXE implements TypedOracle { await this.txeDatabase.addContractArtifact(computeContractClassId(contractClass), artifact); } - async getPrivateContextInputs( - blockNumber: number, - msgSender = AztecAddress.random(), - sideEffectCounter = this.sideEffectCounter, - ) { + async getPrivateContextInputs(blockNumber: number, sideEffectCounter = this.sideEffectCounter) { const trees = this.getTrees(); const stateReference = await trees.getStateReference(true); const inputs = PrivateContextInputs.empty(); inputs.historicalHeader.globalVariables.blockNumber = new Fr(blockNumber); inputs.historicalHeader.state = stateReference; - inputs.callContext.msgSender = msgSender; + inputs.callContext.msgSender = this.msgSender; inputs.callContext.storageContractAddress = this.contractAddress; inputs.callContext.sideEffectCounter = sideEffectCounter; inputs.startSideEffectCounter = sideEffectCounter; return inputs; } + getPublicContextInputs() { + const inputs = { + functionSelector: FunctionSelector.fromField(new Fr(0)), + argsHash: new Fr(0), + isStaticCall: false, + toFields: function () { + return [this.functionSelector.toField(), this.argsHash, new Fr(this.isStaticCall)]; + }, + }; + return inputs; + } + + async avmOpcodeNullifierExists(innerNullifier: Fr, targetAddress: AztecAddress): Promise { + const nullifier = siloNullifier(targetAddress, innerNullifier!); + const db = this.trees.asLatest(); + const index = await db.findLeafIndex(MerkleTreeId.NULLIFIER_TREE, nullifier.toBuffer()); + return index !== undefined; + } + + async avmOpcodeEmitNullifier(nullifier: Fr) { + const db = this.trees.asLatest(); + const siloedNullifier = siloNullifier(this.contractAddress, nullifier); + await db.batchInsert(MerkleTreeId.NULLIFIER_TREE, [siloedNullifier.toBuffer()], NULLIFIER_SUBTREE_HEIGHT); + return Promise.resolve(); + } + + async avmOpcodeEmitNoteHash(innerNoteHash: Fr) { + const db = this.trees.asLatest(); + const noteHash = siloNoteHash(this.contractAddress, innerNoteHash); + await db.appendLeaves(MerkleTreeId.NOTE_HASH_TREE, [noteHash]); + return Promise.resolve(); + } + + deriveKeys(secret: Fr) { + return deriveKeys(secret); + } + // TypedOracle getBlockNumber(): Promise { @@ -182,8 +225,29 @@ export class TXE implements TypedOracle { return result.toFields(); } - getNullifierMembershipWitness(_blockNumber: number, _nullifier: Fr): Promise { - throw new Error('Method not implemented.'); + async getNullifierMembershipWitness( + blockNumber: number, + nullifier: Fr, + ): Promise { + const committedDb = new MerkleTreeSnapshotOperationsFacade(this.trees, blockNumber); + const index = await committedDb.findLeafIndex(MerkleTreeId.NULLIFIER_TREE, nullifier.toBuffer()); + if (!index) { + return undefined; + } + + const leafPreimagePromise = committedDb.getLeafPreimage(MerkleTreeId.NULLIFIER_TREE, index); + const siblingPathPromise = committedDb.getSiblingPath( + MerkleTreeId.NULLIFIER_TREE, + BigInt(index), + ); + + const [leafPreimage, siblingPath] = await Promise.all([leafPreimagePromise, siblingPathPromise]); + + if (!leafPreimage) { + return undefined; + } + + return new NullifierMembershipWitness(BigInt(index), leafPreimage as NullifierLeafPreimage, siblingPath); } async getPublicDataTreeWitness(blockNumber: number, leafSlot: Fr): Promise { @@ -400,14 +464,16 @@ export class TXE implements TypedOracle { this.logger.debug( `Calling private function ${targetContractAddress}:${functionSelector} from ${this.contractAddress}`, ); - // Modify env - const from = AztecAddress.fromField(this.contractAddress); + // Store and modify env + const currentContractAddress = AztecAddress.fromField(this.contractAddress); + const currentMessageSender = AztecAddress.fromField(this.msgSender); + this.setMsgSender(this.contractAddress); this.setContractAddress(targetContractAddress); const artifact = await this.contractDataOracle.getFunctionArtifact(targetContractAddress, functionSelector); const acir = artifact.bytecode; - const initialWitness = await this.getInitialWitness(artifact, argsHash, from, sideEffectCounter); + const initialWitness = await this.getInitialWitness(artifact, argsHash, sideEffectCounter); const acvmCallback = new Oracle(this); const timer = new Timer(); const acirExecutionResult = await acvm(acir, initialWitness, acvmCallback).catch((err: Error) => { @@ -448,12 +514,13 @@ export class TXE implements TypedOracle { ); // Apply side effects this.sideEffectCounter += publicInputs.endSideEffectCounter.toNumber(); - this.setContractAddress(from); + this.setContractAddress(currentContractAddress); + this.setMsgSender(currentMessageSender); return callStackItem; } - async getInitialWitness(abi: FunctionAbi, argsHash: Fr, msgSender: AztecAddress, sideEffectCounter: number) { + async getInitialWitness(abi: FunctionAbi, argsHash: Fr, sideEffectCounter: number) { const argumentsSize = countArgumentsSize(abi); const args = this.packedValuesCache.unpack(argsHash); @@ -462,7 +529,7 @@ export class TXE implements TypedOracle { throw new Error('Invalid arguments size'); } - const privateContextInputs = await this.getPrivateContextInputs(this.blockNumber - 1, msgSender, sideEffectCounter); + const privateContextInputs = await this.getPrivateContextInputs(this.blockNumber - 1, sideEffectCounter); const fields = [...privateContextInputs.toFields(), ...args]; diff --git a/yarn-project/txe/src/txe_service/txe_service.ts b/yarn-project/txe/src/txe_service/txe_service.ts index 9783829661d4..e312ff817f7b 100644 --- a/yarn-project/txe/src/txe_service/txe_service.ts +++ b/yarn-project/txe/src/txe_service/txe_service.ts @@ -6,7 +6,6 @@ import { KeyValidationRequest, PUBLIC_DATA_SUBTREE_HEIGHT, Point, - PrivateContextInputs, PublicDataTreeLeaf, getContractInstanceFromDeployParams, } from '@aztec/circuits.js'; @@ -34,7 +33,7 @@ import { TXEDatabase } from '../util/txe_database.js'; export class TXEService { constructor(private logger: Logger, private typedOracle: TypedOracle, private store: AztecKVStore) {} - static async init(logger: Logger, contractAddress = AztecAddress.random()) { + static async init(logger: Logger) { const store = openTmpStore(true); const trees = await MerkleTrees.new(store, logger); const packedValuesCache = new PackedValuesCache(); @@ -42,7 +41,7 @@ export class TXEService { const keyStore = new KeyStore(store); const txeDatabase = new TXEDatabase(store); logger.info(`TXE service initialized`); - const txe = new TXE(logger, trees, packedValuesCache, noteCache, keyStore, txeDatabase, contractAddress); + const txe = new TXE(logger, trees, packedValuesCache, noteCache, keyStore, txeDatabase); const service = new TXEService(logger, txe, store); await service.timeTravel(toSingle(new Fr(1n))); return service; @@ -55,15 +54,21 @@ export class TXEService { return toForeignCallResult(inputs.toFields().map(toSingle)); } + getPublicContextInputs() { + const inputs = (this.typedOracle as TXE).getPublicContextInputs(); + return toForeignCallResult(inputs.toFields().map(toSingle)); + } + async timeTravel(blocks: ForeignCallSingle) { const nBlocks = fromSingle(blocks).toNumber(); this.logger.info(`time traveling ${nBlocks} blocks`); const trees = (this.typedOracle as TXE).getTrees(); - const blockNumber = await this.typedOracle.getBlockNumber(); for (let i = 0; i < nBlocks; i++) { const header = Header.empty(); const l2Block = L2Block.empty(); header.state = await trees.getStateReference(true); + const blockNumber = await this.typedOracle.getBlockNumber(); + header.globalVariables.blockNumber = new Fr(blockNumber); header.state.partial.nullifierTree.root = Fr.fromBuffer( (await trees.getTreeInfo(MerkleTreeId.NULLIFIER_TREE, true)).root, @@ -97,7 +102,6 @@ export class TXEService { new ExecutionNoteCache(), keyStore, txeDatabase, - AztecAddress.random(), ); await this.timeTravel(toSingle(new Fr(1))); return toForeignCallResult([]); @@ -109,11 +113,17 @@ export class TXEService { return toForeignCallResult([]); } + deriveKeys(secret: ForeignCallSingle) { + const keys = (this.typedOracle as TXE).deriveKeys(fromSingle(secret)); + return toForeignCallResult(keys.publicKeys.toFields().map(toSingle)); + } + async deploy( path: ForeignCallArray, initializer: ForeignCallArray, _length: ForeignCallSingle, args: ForeignCallArray, + publicKeysHash: ForeignCallSingle, ) { const pathStr = fromArray(path) .map(char => String.fromCharCode(char.toNumber())) @@ -128,8 +138,9 @@ export class TXEService { const contractClass = contractModule[Object.keys(contractModule).sort((a, b) => a.length - b.length)[0]]; const instance = getContractInstanceFromDeployParams(contractClass.artifact, { constructorArgs: decodedArgs, + skipArgsDecoding: true, salt: Fr.ONE, - publicKeysHash: Fr.ZERO, + publicKeysHash: fromSingle(publicKeysHash), constructorArtifact: initializerStr ? initializerStr : undefined, deployer: AztecAddress.ZERO, }); @@ -169,6 +180,7 @@ export class TXEService { const completeAddress = await keyStore.createAccount(); const accountStore = (this.typedOracle as TXE).getTXEDatabase(); await accountStore.setAccount(completeAddress.address, completeAddress); + this.logger.debug(`Created account ${completeAddress.address}`); return toForeignCallResult([ toSingle(completeAddress.address), ...completeAddress.publicKeys.toFields().map(toSingle), @@ -186,6 +198,16 @@ export class TXEService { ]); } + setMsgSender(msgSender: ForeignCallSingle) { + (this.typedOracle as TXE).setMsgSender(fromSingle(msgSender)); + return toForeignCallResult([]); + } + + getMsgSender() { + const msgSender = (this.typedOracle as TXE).getMsgSender(); + return toForeignCallResult([toSingle(msgSender)]); + } + // PXE oracles getRandomField() { @@ -385,6 +407,44 @@ export class TXEService { ]); } + async avmOpcodeGetContractInstance(address: ForeignCallSingle) { + const instance = await this.typedOracle.getContractInstance(fromSingle(address)); + return toForeignCallResult([ + toArray([ + // AVM requires an extra boolean indicating the instance was found + new Fr(1), + instance.salt, + instance.deployer, + instance.contractClassId, + instance.initializationHash, + instance.publicKeysHash, + ]), + ]); + } + + avmOpcodeSender() { + const sender = (this.typedOracle as TXE).getMsgSender(); + return toForeignCallResult([toSingle(sender)]); + } + + async avmOpcodeEmitNullifier(nullifier: ForeignCallSingle) { + await (this.typedOracle as TXE).avmOpcodeEmitNullifier(fromSingle(nullifier)); + return toForeignCallResult([]); + } + + async avmOpcodeEmitNoteHash(innerNoteHash: ForeignCallSingle) { + await (this.typedOracle as TXE).avmOpcodeEmitNoteHash(fromSingle(innerNoteHash)); + return toForeignCallResult([]); + } + + async avmOpcodeNullifierExists(innerNullifier: ForeignCallSingle, targetAddress: ForeignCallSingle) { + const exists = await (this.typedOracle as TXE).avmOpcodeNullifierExists( + fromSingle(innerNullifier), + AztecAddress.fromField(fromSingle(targetAddress)), + ); + return toForeignCallResult([toSingle(new Fr(exists))]); + } + async getPublicKeysAndPartialAddress(address: ForeignCallSingle) { const parsedAddress = AztecAddress.fromField(fromSingle(address)); const { publicKeys, partialAddress } = await this.typedOracle.getCompleteAddress(parsedAddress); @@ -462,4 +522,13 @@ export class TXEService { ); return toForeignCallResult([toArray(result.toFields())]); } + + async getNullifierMembershipWitness(blockNumber: ForeignCallSingle, nullifier: ForeignCallSingle) { + const parsedBlockNumber = fromSingle(blockNumber).toNumber(); + const witness = await this.typedOracle.getNullifierMembershipWitness(parsedBlockNumber, fromSingle(nullifier)); + if (!witness) { + throw new Error(`Nullifier membership witness not found at block ${parsedBlockNumber}.`); + } + return toForeignCallResult([toArray(witness.toFields())]); + } } From bbeaa00ac2bf88e4f35d5041aecab0fefaf33ba4 Mon Sep 17 00:00:00 2001 From: thunkar Date: Thu, 13 Jun 2024 14:59:01 +0000 Subject: [PATCH 41/60] 34 token transfer --- .../aztec/src/test/helpers/cheatcodes.nr | 41 +++++++++++++++++++ .../contracts/token_contract/src/main.nr | 28 ++++++++++++- yarn-project/txe/src/oracle/txe_oracle.ts | 18 ++++---- .../txe/src/txe_service/txe_service.ts | 5 +++ 4 files changed, 82 insertions(+), 10 deletions(-) diff --git a/noir-projects/aztec-nr/aztec/src/test/helpers/cheatcodes.nr b/noir-projects/aztec-nr/aztec/src/test/helpers/cheatcodes.nr index 55f05b1554a4..0fd8270f263e 100644 --- a/noir-projects/aztec-nr/aztec/src/test/helpers/cheatcodes.nr +++ b/noir-projects/aztec-nr/aztec/src/test/helpers/cheatcodes.nr @@ -2,6 +2,11 @@ use dep::protocol_types::{abis::function_selector::FunctionSelector, address::{A use crate::context::inputs::{PublicContextInputs, PrivateContextInputs}; use crate::test::helpers::types::{Deployer, TestAccount}; use crate::keys::public_keys::PublicKeys; +use crate::note::{ + note_header::NoteHeader, note_interface::NoteInterface, + utils::{compute_note_hash_for_insertion, compute_note_hash_for_consumption} +}; +use crate::oracle::notes::notify_created_note; unconstrained pub fn reset() { oracle_reset(); @@ -68,6 +73,39 @@ unconstrained pub fn get_msg_sender() -> AztecAddress { oracle_get_msg_sender() } +unconstrained fn get_side_effects_counter() -> u32 { + oracle_get_side_effects_counter() +} + +pub fn store_note_in_cache( + note: &mut Note, + storage_slot: Field, + contract_address: AztecAddress +) where Note: NoteInterface { + let original_contract_address = get_contract_address(); + set_contract_address(contract_address); + let note_hash_counter = get_side_effects_counter(); + + let header = NoteHeader { contract_address, storage_slot, nonce: 0, note_hash_counter }; + // TODO: change this to note.set_header(header) once https://github.com/noir-lang/noir/issues/4095 is fixed + Note::set_header(note, header); + let inner_note_hash = compute_note_hash_for_insertion(*note); + + // TODO: Strong typing required because of https://github.com/noir-lang/noir/issues/4088 + let serialized_note: [Field; N] = Note::serialize_content(*note); + assert( + notify_created_note( + storage_slot, + Note::get_note_type_id(), + serialized_note, + inner_note_hash, + note_hash_counter + ) + == 0 + ); + set_contract_address(original_contract_address); +} + #[oracle(reset)] fn oracle_reset() {} @@ -118,3 +156,6 @@ fn oracle_get_msg_sender() -> AztecAddress {} #[oracle(setMsgSender)] fn oracle_set_msg_sender(msg_sender: AztecAddress) {} + +#[oracle(getSideEffectsCounter)] +fn oracle_get_side_effects_counter() -> u32 {} diff --git a/noir-projects/noir-contracts/contracts/token_contract/src/main.nr b/noir-projects/noir-contracts/contracts/token_contract/src/main.nr index 73340ddb4662..4a95b79b116c 100644 --- a/noir-projects/noir-contracts/contracts/token_contract/src/main.nr +++ b/noir-projects/noir-contracts/contracts/token_contract/src/main.nr @@ -409,6 +409,7 @@ contract Token { let recipient = env.create_account(); let mint_amount = 10000; + // Start the test in the account contract address cheatcodes::set_contract_address(owner); // Deploy token contract @@ -430,13 +431,38 @@ contract Token { // Time travel so we can read keys from the registry env.advance_block_by(6); + // Store a note in the cache so we can redeem it + cheatcodes::store_note_in_cache( + &mut TransparentNote::new(mint_amount, secret_hash), + Token::storage().pending_shields.slot, + token_contract_address + ); + // Redeem our shielded tokens let redeem_shield_call_interface = Token::at(token_contract_address).redeem_shield(owner, mint_amount, secret); env.call_private_void(redeem_shield_call_interface); + // Not really sure why this is needed? + env.advance_block_by(1); + // Transfer tokens - let private_token_transfer_call_interface = Token::at(token_contract_address).transfer(recipient, 1000); + let transfer_amount = 1000; + let private_token_transfer_call_interface = Token::at(token_contract_address).transfer(recipient, transfer_amount); env.call_private_void(private_token_transfer_call_interface); + + // Check balances + cheatcodes::set_contract_address(token_contract_address); + + let balances_slot = Token::storage().balances.slot; + let recipient_slot = derive_storage_slot_in_map(balances_slot, recipient); + let mut options = NoteViewerOptions::new(); + let opt_notes: [Option; MAX_NOTES_PER_PAGE] = view_notes(recipient_slot, options); + assert(opt_notes[0].unwrap().amount.to_field() == transfer_amount); + + let owner_slot = derive_storage_slot_in_map(balances_slot, owner); + let mut options = NoteViewerOptions::new(); + let opt_notes: [Option; MAX_NOTES_PER_PAGE] = view_notes(owner_slot, options); + assert(opt_notes[0].unwrap().amount.to_field() == mint_amount - transfer_amount); } } // docs:end:token_all \ No newline at end of file diff --git a/yarn-project/txe/src/oracle/txe_oracle.ts b/yarn-project/txe/src/oracle/txe_oracle.ts index 90b521bfffcc..9cbb375f0962 100644 --- a/yarn-project/txe/src/oracle/txe_oracle.ts +++ b/yarn-project/txe/src/oracle/txe_oracle.ts @@ -61,7 +61,7 @@ import { type TXEDatabase } from '../util/txe_database.js'; export class TXE implements TypedOracle { private blockNumber = 0; - private sideEffectCounter = 0; + private sideEffectsCounter = 0; private contractAddress: AztecAddress; private msgSender: AztecAddress; @@ -90,12 +90,12 @@ export class TXE implements TypedOracle { this.msgSender = msgSender; } - getSideEffectCounter() { - return this.sideEffectCounter; + getSideEffectsCounter() { + return this.sideEffectsCounter; } - setSideEffectCounter(sideEffectCounter: number) { - this.sideEffectCounter = sideEffectCounter; + setSideEffectsCounter(sideEffectsCounter: number) { + this.sideEffectsCounter = sideEffectsCounter; } setContractAddress(contractAddress: AztecAddress) { @@ -127,7 +127,7 @@ export class TXE implements TypedOracle { await this.txeDatabase.addContractArtifact(computeContractClassId(contractClass), artifact); } - async getPrivateContextInputs(blockNumber: number, sideEffectCounter = this.sideEffectCounter) { + async getPrivateContextInputs(blockNumber: number, sideEffectsCounter = this.sideEffectsCounter) { const trees = this.getTrees(); const stateReference = await trees.getStateReference(true); const inputs = PrivateContextInputs.empty(); @@ -135,8 +135,8 @@ export class TXE implements TypedOracle { inputs.historicalHeader.state = stateReference; inputs.callContext.msgSender = this.msgSender; inputs.callContext.storageContractAddress = this.contractAddress; - inputs.callContext.sideEffectCounter = sideEffectCounter; - inputs.startSideEffectCounter = sideEffectCounter; + inputs.callContext.sideEffectCounter = sideEffectsCounter; + inputs.startSideEffectCounter = sideEffectsCounter; return inputs; } @@ -513,7 +513,7 @@ export class TXE implements TypedOracle { publicInputs, ); // Apply side effects - this.sideEffectCounter += publicInputs.endSideEffectCounter.toNumber(); + this.sideEffectsCounter += publicInputs.endSideEffectCounter.toNumber(); this.setContractAddress(currentContractAddress); this.setMsgSender(currentMessageSender); diff --git a/yarn-project/txe/src/txe_service/txe_service.ts b/yarn-project/txe/src/txe_service/txe_service.ts index e312ff817f7b..a93b80ce1451 100644 --- a/yarn-project/txe/src/txe_service/txe_service.ts +++ b/yarn-project/txe/src/txe_service/txe_service.ts @@ -208,6 +208,11 @@ export class TXEService { return toForeignCallResult([toSingle(msgSender)]); } + getSideEffectsCounter() { + const counter = (this.typedOracle as TXE).getSideEffectsCounter(); + return toForeignCallResult([toSingle(new Fr(counter))]); + } + // PXE oracles getRandomField() { From 963990a4769c02923b68648a78b15aeb513e79af Mon Sep 17 00:00:00 2001 From: thunkar Date: Fri, 14 Jun 2024 07:41:23 +0000 Subject: [PATCH 42/60] cleanup --- .../aztec/src/test/helpers/cheatcodes.nr | 36 +------------ .../src/test/helpers/test_environment.nr | 53 +++++++++++++++++++ .../contracts/token_contract/src/main.nr | 2 +- 3 files changed, 55 insertions(+), 36 deletions(-) diff --git a/noir-projects/aztec-nr/aztec/src/test/helpers/cheatcodes.nr b/noir-projects/aztec-nr/aztec/src/test/helpers/cheatcodes.nr index 0fd8270f263e..014757cf9b00 100644 --- a/noir-projects/aztec-nr/aztec/src/test/helpers/cheatcodes.nr +++ b/noir-projects/aztec-nr/aztec/src/test/helpers/cheatcodes.nr @@ -2,11 +2,6 @@ use dep::protocol_types::{abis::function_selector::FunctionSelector, address::{A use crate::context::inputs::{PublicContextInputs, PrivateContextInputs}; use crate::test::helpers::types::{Deployer, TestAccount}; use crate::keys::public_keys::PublicKeys; -use crate::note::{ - note_header::NoteHeader, note_interface::NoteInterface, - utils::{compute_note_hash_for_insertion, compute_note_hash_for_consumption} -}; -use crate::oracle::notes::notify_created_note; unconstrained pub fn reset() { oracle_reset(); @@ -73,39 +68,10 @@ unconstrained pub fn get_msg_sender() -> AztecAddress { oracle_get_msg_sender() } -unconstrained fn get_side_effects_counter() -> u32 { +unconstrained pub fn get_side_effects_counter() -> u32 { oracle_get_side_effects_counter() } -pub fn store_note_in_cache( - note: &mut Note, - storage_slot: Field, - contract_address: AztecAddress -) where Note: NoteInterface { - let original_contract_address = get_contract_address(); - set_contract_address(contract_address); - let note_hash_counter = get_side_effects_counter(); - - let header = NoteHeader { contract_address, storage_slot, nonce: 0, note_hash_counter }; - // TODO: change this to note.set_header(header) once https://github.com/noir-lang/noir/issues/4095 is fixed - Note::set_header(note, header); - let inner_note_hash = compute_note_hash_for_insertion(*note); - - // TODO: Strong typing required because of https://github.com/noir-lang/noir/issues/4088 - let serialized_note: [Field; N] = Note::serialize_content(*note); - assert( - notify_created_note( - storage_slot, - Note::get_note_type_id(), - serialized_note, - inner_note_hash, - note_hash_counter - ) - == 0 - ); - set_contract_address(original_contract_address); -} - #[oracle(reset)] fn oracle_reset() {} diff --git a/noir-projects/aztec-nr/aztec/src/test/helpers/test_environment.nr b/noir-projects/aztec-nr/aztec/src/test/helpers/test_environment.nr index d3db6178f17b..3f14fb0e5fa7 100644 --- a/noir-projects/aztec-nr/aztec/src/test/helpers/test_environment.nr +++ b/noir-projects/aztec-nr/aztec/src/test/helpers/test_environment.nr @@ -12,6 +12,12 @@ use crate::test::helpers::{cheatcodes, types::{Deployer, TestAccount}, keys}; use crate::keys::constants::{NULLIFIER_INDEX, INCOMING_INDEX, OUTGOING_INDEX, TAGGING_INDEX}; use crate::hash::hash_args; +use crate::note::{ + note_header::NoteHeader, note_interface::NoteInterface, + utils::{compute_note_hash_for_insertion, compute_note_hash_for_consumption} +}; +use crate::oracle::notes::notify_created_note; + struct TestEnvironment { contract_address: Option, args_hash: Option, @@ -152,4 +158,51 @@ impl TestEnvironment { cheatcodes::set_msg_sender(original_msg_sender); result } + + fn call_public_void(self, call_interface: C) where C: CallInterface { + let original_fn = call_interface.get_original(); + let original_msg_sender = cheatcodes::get_msg_sender(); + let original_contract_address = cheatcodes::get_contract_address(); + let target_address = call_interface.get_contract_address(); + + cheatcodes::set_contract_address(target_address); + cheatcodes::set_msg_sender(original_contract_address); + let mut inputs = cheatcodes::get_public_context_inputs(); + inputs.selector = call_interface.get_selector().to_field(); + inputs.args_hash = hash_args(call_interface.get_args()); + original_fn(inputs); + + cheatcodes::set_contract_address(original_contract_address); + cheatcodes::set_msg_sender(original_msg_sender); + } + + pub fn store_note_in_cache( + self, + note: &mut Note, + storage_slot: Field, + contract_address: AztecAddress + ) where Note: NoteInterface { + let original_contract_address = cheatcodes::get_contract_address(); + cheatcodes::set_contract_address(contract_address); + let note_hash_counter = cheatcodes::get_side_effects_counter(); + + let header = NoteHeader { contract_address, storage_slot, nonce: 0, note_hash_counter }; + // TODO: change this to note.set_header(header) once https://github.com/noir-lang/noir/issues/4095 is fixed + Note::set_header(note, header); + let inner_note_hash = compute_note_hash_for_insertion(*note); + + // TODO: Strong typing required because of https://github.com/noir-lang/noir/issues/4088 + let serialized_note: [Field; N] = Note::serialize_content(*note); + assert( + notify_created_note( + storage_slot, + Note::get_note_type_id(), + serialized_note, + inner_note_hash, + note_hash_counter + ) + == 0 + ); + cheatcodes::set_contract_address(original_contract_address); + } } diff --git a/noir-projects/noir-contracts/contracts/token_contract/src/main.nr b/noir-projects/noir-contracts/contracts/token_contract/src/main.nr index 4a95b79b116c..0d9571448855 100644 --- a/noir-projects/noir-contracts/contracts/token_contract/src/main.nr +++ b/noir-projects/noir-contracts/contracts/token_contract/src/main.nr @@ -432,7 +432,7 @@ contract Token { env.advance_block_by(6); // Store a note in the cache so we can redeem it - cheatcodes::store_note_in_cache( + env.store_note_in_cache( &mut TransparentNote::new(mint_amount, secret_hash), Token::storage().pending_shields.slot, token_contract_address From 44d63aad426afabe86d8ff38049ace4dd649fb93 Mon Sep 17 00:00:00 2001 From: thunkar Date: Fri, 14 Jun 2024 10:51:59 +0000 Subject: [PATCH 43/60] sessionId compatibility --- .../src/test/helpers/test_environment.nr | 1 - yarn-project/txe/src/bin/index.ts | 81 ++++++++++++++++--- yarn-project/txe/src/http_rpc_server/index.ts | 29 ------- yarn-project/txe/src/index.ts | 1 - .../txe/src/txe_service/txe_service.ts | 17 ---- yarn-project/txe/src/util/encoding.ts | 18 ++--- 6 files changed, 79 insertions(+), 68 deletions(-) delete mode 100644 yarn-project/txe/src/http_rpc_server/index.ts delete mode 100644 yarn-project/txe/src/index.ts diff --git a/noir-projects/aztec-nr/aztec/src/test/helpers/test_environment.nr b/noir-projects/aztec-nr/aztec/src/test/helpers/test_environment.nr index 3f14fb0e5fa7..142f6fd58f8f 100644 --- a/noir-projects/aztec-nr/aztec/src/test/helpers/test_environment.nr +++ b/noir-projects/aztec-nr/aztec/src/test/helpers/test_environment.nr @@ -27,7 +27,6 @@ struct TestEnvironment { impl TestEnvironment { fn new() -> Self { cheatcodes::reset(); - Self { contract_address: Option::none(), args_hash: Option::none(), function_selector: Option::none() } } diff --git a/yarn-project/txe/src/bin/index.ts b/yarn-project/txe/src/bin/index.ts index 365e2054be7b..149347621599 100644 --- a/yarn-project/txe/src/bin/index.ts +++ b/yarn-project/txe/src/bin/index.ts @@ -1,27 +1,86 @@ #!/usr/bin/env -S node --no-warnings -import { createDebugLogger } from '@aztec/foundation/log'; +import { JsonRpcServer } from '@aztec/foundation/json-rpc/server'; +import { type Logger, createDebugLogger } from '@aztec/foundation/log'; + +import http from 'http'; -import { startTXEHttpServer } from '../index.js'; import { TXEService } from '../txe_service/txe_service.js'; +import { type ForeignCallResult, toForeignCallResult } from '../util/encoding.js'; const { TXE_PORT = 8080 } = process.env; const logger = createDebugLogger('aztec:txe_service'); +const TXESessions = new Map(); + +type MethodNames = { + [K in keyof T]: T[K] extends (...args: any[]) => any ? K : never; +}[keyof T]; + +type TXEForeignCallInput = { + session_id: number; + function: MethodNames | 'reset'; + inputs: any[]; +}; + +class TXEDispatcher { + constructor(private logger: Logger) {} + + // eslint-disable-next-line camelcase + async resolve_foreign_call({ + session_id: sessionId, + function: functionName, + inputs, + }: TXEForeignCallInput): Promise { + this.logger.debug( + `Calling ${functionName} with inputs: ${JSON.stringify(inputs, null, 2)} on session ${sessionId}`, + ); + + if (!TXESessions.has(sessionId) && functionName != 'reset') { + this.logger.debug(`Creating new session ${sessionId}`); + TXESessions.set(sessionId, await TXEService.init(logger)); + } + + if (functionName === 'reset') { + TXESessions.delete(sessionId) && + this.logger.debug(`Called reset on session ${sessionId}, yeeting it out of existence`); + return toForeignCallResult([]); + } else { + const txeService = TXESessions.get(sessionId); + const response = await (txeService as any)[functionName](...inputs); + this.logger.debug( + `${sessionId}:${functionName}(${JSON.stringify(inputs, null, 2)}) -> ${JSON.stringify(response, null, 2)}`, + ); + return response; + } + } +} + /** - * Create and start a new PXE HTTP Server + * Creates an http server that forwards calls to the TXE and starts it on the given port. + * @param txeService - TXE that answers queries to the created HTTP server. + * @param port - Port to listen in. + * @returns A running http server. */ -async function main() { - logger.info(`Setting up TXE...`); +export function startTXEHttpServer(dispatcher: TXEDispatcher, port: string | number): http.Server { + const txeServer = new JsonRpcServer(dispatcher, {}, {}, ['init']); + + const app = txeServer.getApp(); + const httpServer = http.createServer(app.callback()); + httpServer.listen(port); + + return httpServer; +} - const txeService = await TXEService.init(logger); +/** + * Create and start a new TXE HTTP Server + */ +function main() { + logger.info(`Setting up TXE...`); - startTXEHttpServer(txeService, TXE_PORT); + startTXEHttpServer(new TXEDispatcher(logger), TXE_PORT); logger.info(`TXE listening on port ${TXE_PORT}`); } -main().catch(err => { - logger.error(err); - process.exit(1); -}); +main(); diff --git a/yarn-project/txe/src/http_rpc_server/index.ts b/yarn-project/txe/src/http_rpc_server/index.ts deleted file mode 100644 index 69d9e08464b9..000000000000 --- a/yarn-project/txe/src/http_rpc_server/index.ts +++ /dev/null @@ -1,29 +0,0 @@ -import { JsonRpcServer } from '@aztec/foundation/json-rpc/server'; - -import http from 'http'; - -import { type TXEService } from '../txe_service/txe_service.js'; - -/** - * Wraps an instance of Private eXecution Environment (TXE) implementation to a JSON RPC HTTP interface. - * @returns A new instance of the HTTP server. - */ -export function createTXERpcServer(txeService: TXEService): JsonRpcServer { - return new JsonRpcServer(txeService, {}, {}, ['init']); -} - -/** - * Creates an http server that forwards calls to the TXE and starts it on the given port. - * @param txeService - TXE that answers queries to the created HTTP server. - * @param port - Port to listen in. - * @returns A running http server. - */ -export function startTXEHttpServer(txeService: TXEService, port: string | number): http.Server { - const txeServer = createTXERpcServer(txeService); - - const app = txeServer.getApp(); - const httpServer = http.createServer(app.callback()); - httpServer.listen(port); - - return httpServer; -} diff --git a/yarn-project/txe/src/index.ts b/yarn-project/txe/src/index.ts deleted file mode 100644 index 6f83d1f63b2e..000000000000 --- a/yarn-project/txe/src/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from './http_rpc_server/index.js'; diff --git a/yarn-project/txe/src/txe_service/txe_service.ts b/yarn-project/txe/src/txe_service/txe_service.ts index a93b80ce1451..6b53c0de1ebd 100644 --- a/yarn-project/txe/src/txe_service/txe_service.ts +++ b/yarn-project/txe/src/txe_service/txe_service.ts @@ -90,23 +90,6 @@ export class TXEService { return toForeignCallResult([]); } - async reset() { - this.store = openTmpStore(true); - const trees = await MerkleTrees.new(this.store, this.logger); - const keyStore = new KeyStore(this.store); - const txeDatabase = new TXEDatabase(this.store); - this.typedOracle = new TXE( - this.logger, - trees, - new PackedValuesCache(), - new ExecutionNoteCache(), - keyStore, - txeDatabase, - ); - await this.timeTravel(toSingle(new Fr(1))); - return toForeignCallResult([]); - } - setContractAddress(address: ForeignCallSingle) { const typedAddress = AztecAddress.fromField(fromSingle(address)); (this.typedOracle as TXE).setContractAddress(typedAddress); diff --git a/yarn-project/txe/src/util/encoding.ts b/yarn-project/txe/src/util/encoding.ts index 42dc32b88b4c..0d4a78ba61ad 100644 --- a/yarn-project/txe/src/util/encoding.ts +++ b/yarn-project/txe/src/util/encoding.ts @@ -1,27 +1,27 @@ import { Fr } from '@aztec/foundation/fields'; -export type ForeignCallSingle = { - Single: string; -}; +export type ForeignCallSingle = string; + +export type ForeignCallArray = string[]; -export type ForeignCallArray = { - Array: string[]; +export type ForeignCallResult = { + values: (ForeignCallSingle | ForeignCallArray)[]; }; export function fromSingle(obj: ForeignCallSingle) { - return Fr.fromBuffer(Buffer.from(obj.Single, 'hex')); + return Fr.fromBuffer(Buffer.from(obj, 'hex')); } export function fromArray(obj: ForeignCallArray) { - return obj.Array.map(str => Fr.fromBuffer(Buffer.from(str, 'hex'))); + return obj.map(str => Fr.fromBuffer(Buffer.from(str, 'hex'))); } export function toSingle(obj: Fr) { - return { Single: obj.toString().slice(2) }; + return obj.toString().slice(2); } export function toArray(objs: Fr[]) { - return { Array: objs.map(obj => obj.toString()) }; + return objs.map(obj => obj.toString()); } export function toForeignCallResult(obj: (ForeignCallSingle | ForeignCallArray)[]) { From 9f1dbe6e8ab0171296e9e051dcb4f3c0796c7da6 Mon Sep 17 00:00:00 2001 From: thunkar Date: Fri, 14 Jun 2024 10:52:09 +0000 Subject: [PATCH 44/60] corrected docs --- noir/noir-repo/docs/docs/how_to/how-to-oracles.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/noir/noir-repo/docs/docs/how_to/how-to-oracles.md b/noir/noir-repo/docs/docs/how_to/how-to-oracles.md index 62ead1f534f3..64d984c8cf99 100644 --- a/noir/noir-repo/docs/docs/how_to/how-to-oracles.md +++ b/noir/noir-repo/docs/docs/how_to/how-to-oracles.md @@ -141,10 +141,10 @@ server.addMethod("resolve_function_call", async (params) => { if params.function !== "getSqrt" { throw Error("Unexpected foreign call") }; - const values = params.inputs[0].Array.map((field) => { + const values = params.inputs[0].map((field) => { return `${Math.sqrt(parseInt(field, 16))}`; }); - return { values: [{ Array: values }] }; + return { values }; }); ``` @@ -232,9 +232,9 @@ const foreignCallHandler = async (name, input) => { // notice that the "inputs" parameter contains *all* the inputs // in this case we to make the RPC request with the first parameter "numbers", which would be input[0] const oracleReturn = await client.request(name, [ - { Array: input[0].map((i) => i.toString("hex")) }, + input[0].map((i) => i.toString("hex")), ]); - return [oracleReturn.values[0].Array]; + return { values: oracleReturn }; }; // the rest of your NoirJS code From 93f32d0a655ec9bfbf960d12788cd5118c4105f3 Mon Sep 17 00:00:00 2001 From: thunkar Date: Fri, 14 Jun 2024 11:07:29 +0000 Subject: [PATCH 45/60] cleanup --- yarn-project/simulator/src/client/execution_note_cache.ts | 4 ---- 1 file changed, 4 deletions(-) diff --git a/yarn-project/simulator/src/client/execution_note_cache.ts b/yarn-project/simulator/src/client/execution_note_cache.ts index e74123138e69..3615b012c99a 100644 --- a/yarn-project/simulator/src/client/execution_note_cache.ts +++ b/yarn-project/simulator/src/client/execution_note_cache.ts @@ -54,7 +54,6 @@ export class ExecutionNoteCache { let nullifiedNoteHashCounter: number | undefined = undefined; // Find and remove the matching new note and log(s) if the emitted innerNoteHash is not empty. if (!innerNoteHash.equals(Fr.ZERO)) { - console.log('DELETING A NOTE'); const notes = this.newNotes.get(contractAddress.toBigInt()) ?? []; const noteIndexToRemove = notes.findIndex(n => n.note.innerNoteHash.equals(innerNoteHash)); if (noteIndexToRemove === -1) { @@ -63,10 +62,7 @@ export class ExecutionNoteCache { const note = notes.splice(noteIndexToRemove, 1)[0]; nullifiedNoteHashCounter = note.counter; this.newNotes.set(contractAddress.toBigInt(), notes); - } else { - console.log('NOT DELETING A NOTE'); } - return nullifiedNoteHashCounter; } From 19123bd39e67046a579c273a32c35fceacb7675a Mon Sep 17 00:00:00 2001 From: thunkar Date: Fri, 14 Jun 2024 11:20:22 +0000 Subject: [PATCH 46/60] formatting --- .../aztec-nr/aztec/src/test/helpers/types.nr | 4 ++-- yarn-project/txe/tsconfig.json | 14 ++++---------- 2 files changed, 6 insertions(+), 12 deletions(-) diff --git a/noir-projects/aztec-nr/aztec/src/test/helpers/types.nr b/noir-projects/aztec-nr/aztec/src/test/helpers/types.nr index 0274a5c15ea7..7baec3523d8b 100644 --- a/noir-projects/aztec-nr/aztec/src/test/helpers/types.nr +++ b/noir-projects/aztec-nr/aztec/src/test/helpers/types.nr @@ -38,7 +38,7 @@ impl Deployer { let _result = original_fn(inputs); cheatcodes::set_contract_address(original_contract_address); - cheatcodes::set_msg_sender(original_msg_sender); + cheatcodes::set_msg_sender(original_msg_sender); address } @@ -65,7 +65,7 @@ impl Deployer { let _result: T = original_fn(inputs); cheatcodes::set_contract_address(original_contract_address); - cheatcodes::set_msg_sender(original_msg_sender); + cheatcodes::set_msg_sender(original_msg_sender); address } diff --git a/yarn-project/txe/tsconfig.json b/yarn-project/txe/tsconfig.json index 1db338e8540f..a57cb2cd9946 100644 --- a/yarn-project/txe/tsconfig.json +++ b/yarn-project/txe/tsconfig.json @@ -7,10 +7,10 @@ }, "references": [ { - "path": "../bb-prover" + "path": "../archiver" }, { - "path": "../builder" + "path": "../aztec.js" }, { "path": "../circuit-types" @@ -18,9 +18,6 @@ { "path": "../circuits.js" }, - { - "path": "../ethereum" - }, { "path": "../foundation" }, @@ -31,10 +28,7 @@ "path": "../kv-store" }, { - "path": "../noir-protocol-circuits-types" - }, - { - "path": "../protocol-contracts" + "path": "../pxe" }, { "path": "../simulator" @@ -43,7 +37,7 @@ "path": "../types" }, { - "path": "../noir-contracts.js" + "path": "../world-state" } ], "include": ["src"] From 35fe257127e6fe3fa4924a9a65c70a499a2624fe Mon Sep 17 00:00:00 2001 From: thunkar Date: Fri, 14 Jun 2024 11:31:17 +0000 Subject: [PATCH 47/60] better comment --- .../noir-contracts/contracts/token_contract/src/main.nr | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/noir-projects/noir-contracts/contracts/token_contract/src/main.nr b/noir-projects/noir-contracts/contracts/token_contract/src/main.nr index 0d9571448855..fca6ed10b8f3 100644 --- a/noir-projects/noir-contracts/contracts/token_contract/src/main.nr +++ b/noir-projects/noir-contracts/contracts/token_contract/src/main.nr @@ -442,7 +442,8 @@ contract Token { let redeem_shield_call_interface = Token::at(token_contract_address).redeem_shield(owner, mint_amount, secret); env.call_private_void(redeem_shield_call_interface); - // Not really sure why this is needed? + // Not really sure why this is needed? Nullifier inclusion in contract initializer fails otherwise. + // If it were to fail, it should do it at line 443, investigation required env.advance_block_by(1); // Transfer tokens From cca4e8c90700f8476e86784df4b74ec1296175cc Mon Sep 17 00:00:00 2001 From: thunkar Date: Mon, 17 Jun 2024 11:18:58 +0000 Subject: [PATCH 48/60] test ci --- .github/workflows/ci.yml | 2 +- noir-projects/Dockerfile | 13 +++++++++++-- noir-projects/Earthfile | 6 ++++++ 3 files changed, 18 insertions(+), 3 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 671ce7a2acbc..196a9efb59a8 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -418,7 +418,7 @@ jobs: run: earthly-ci --no-output ./noir+packages-test noir-projects: - needs: [setup, changes] + needs: [setup, changes, build] runs-on: ${{ github.event.pull_request.user.login || github.actor }}-x86 if: ${{ needs.changes.outputs.barretenberg == 'true' || needs.changes.outputs.noir == 'true' || needs.changes.outputs.noir-projects == 'true' }} steps: diff --git a/noir-projects/Dockerfile b/noir-projects/Dockerfile index ab4c6270e97b..4404a9e5accf 100644 --- a/noir-projects/Dockerfile +++ b/noir-projects/Dockerfile @@ -1,5 +1,6 @@ FROM aztecprotocol/noir as noir FROM aztecprotocol/avm-transpiler as transpiler +FROM aztecprotocol/yarn-project AS yarn-project FROM ubuntu:lunar AS builder RUN apt-get update && apt-get install -y parallel nodejs npm @@ -13,13 +14,21 @@ ENV PATH="/usr/src/avm-transpiler/target/release:${PATH}" # Copy in noir projects WORKDIR /usr/src/noir-projects COPY . . + +# Launch TXE +WORKDIR /usr/src/yarn-project/txe +RUN yarn start & echo $! > /tmp/txe.pid + # Build WORKDIR /usr/src/noir-projects/noir-contracts -RUN ./bootstrap.sh && nargo test --silence-warnings +RUN ./bootstrap.sh && nargo test --silence-warnings --oracle-resolver http://localhost:8080 WORKDIR /usr/src/noir-projects/noir-protocol-circuits RUN ./bootstrap.sh && nargo test --silence-warnings WORKDIR /usr/src/noir-projects/aztec-nr -RUN nargo test --silence-warnings +RUN nargo test --silence-warnings --oracle-resolver http://localhost:8080 + +# Kill TXE +RUN kill $(cat /tmp/txe.pid) FROM scratch COPY --from=builder /usr/src/noir-projects /usr/src/noir-projects \ No newline at end of file diff --git a/noir-projects/Earthfile b/noir-projects/Earthfile index 2a7c32ab568e..ad06dddc45c1 100644 --- a/noir-projects/Earthfile +++ b/noir-projects/Earthfile @@ -42,9 +42,15 @@ build: test: FROM +build + # Install and start TXE + COPY ../yarn-project/+build/usr/src/yarn-project /usr/src/yarn-project + + RUN cd /usr/src/yarn-project/txe && yarn start & echo $! > /tmp/txe.pid RUN cd noir-protocol-circuits && nargo test --silence-warnings RUN cd aztec-nr && nargo test --silence-warnings RUN cd noir-contracts && nargo test --silence-warnings + # Kill TXE + RUN kill $(cat /tmp/txe.pid) format: FROM +build From f564d92f4ff8bd7d666771b7464ac0e0f0e0cd2e Mon Sep 17 00:00:00 2001 From: thunkar Date: Mon, 17 Jun 2024 14:24:40 +0000 Subject: [PATCH 49/60] working CI --- noir-projects/Earthfile | 29 +++++++++++++++++++---------- yarn-project/Earthfile | 13 +++++++++++++ yarn-project/txe/package.json | 1 + yarn-project/txe/tsconfig.json | 3 +++ yarn-project/yarn.lock | 1 + 5 files changed, 37 insertions(+), 10 deletions(-) diff --git a/noir-projects/Earthfile b/noir-projects/Earthfile index ad06dddc45c1..6ca6b1e296e2 100644 --- a/noir-projects/Earthfile +++ b/noir-projects/Earthfile @@ -41,16 +41,25 @@ build: SAVE ARTIFACT noir-protocol-circuits test: - FROM +build - # Install and start TXE - COPY ../yarn-project/+build/usr/src/yarn-project /usr/src/yarn-project - - RUN cd /usr/src/yarn-project/txe && yarn start & echo $! > /tmp/txe.pid - RUN cd noir-protocol-circuits && nargo test --silence-warnings - RUN cd aztec-nr && nargo test --silence-warnings - RUN cd noir-contracts && nargo test --silence-warnings - # Kill TXE - RUN kill $(cat /tmp/txe.pid) + FROM ../yarn-project/+txe + + # Install nargo + COPY ../noir/+nargo/nargo /usr/bin/nargo + + COPY +build/. /usr/src/noir-projects + + RUN cd /usr/src/noir-projects/noir-protocol-circuits && nargo test --silence-warnings + RUN cd /usr/src/yarn-project/txe && yarn start & echo $! > /tmp/txe.pid && \ + # Wait for TXE to initialize + sleep 5 && \ + cd /usr/src/noir-projects/aztec-nr && nargo test --silence-warnings --oracle-resolver http://localhost:8080 && \ + kill $(cat /tmp/txe.pid) + + RUN cd /usr/src/yarn-project/txe && yarn start & echo $! > /tmp/txe.pid && \ + # Wait for TXE to initialize + sleep 5 && \ + cd /usr/src/noir-projects/noir-contracts && nargo test --silence-warnings --oracle-resolver http://localhost:8080 && \ + kill $(cat /tmp/txe.pid) format: FROM +build diff --git a/yarn-project/Earthfile b/yarn-project/Earthfile index 14b724a28b56..f39379e20a18 100644 --- a/yarn-project/Earthfile +++ b/yarn-project/Earthfile @@ -115,6 +115,19 @@ rollup-verifier-contract: RUN --entrypoint write-contract -c RootRollupArtifact -n UltraVerifier.sol SAVE ARTIFACT /usr/src/bb /usr/src/bb +txe: + FROM +build + RUN yarn workspaces focus @aztec/txe --production && yarn cache clean + # Remove a bunch of stuff that we don't need that takes up space. + RUN rm -rf \ + ../noir-projects \ + ../l1-contracts \ + ../barretenberg/ts/src \ + ../barretenberg/ts/dest/node-cjs \ + ../barretenberg/ts/dest/browser \ + **/artifacts + SAVE ARTIFACT /usr/src /usr/src + aztec-prod: FROM +build RUN yarn workspaces focus @aztec/aztec @aztec/builder --production && yarn cache clean diff --git a/yarn-project/txe/package.json b/yarn-project/txe/package.json index c5548db696df..5658cd3b454e 100644 --- a/yarn-project/txe/package.json +++ b/yarn-project/txe/package.json @@ -55,6 +55,7 @@ "@aztec/foundation": "workspace:^", "@aztec/key-store": "workspace:^", "@aztec/kv-store": "workspace:^", + "@aztec/noir-contracts.js": "workspace:^", "@aztec/pxe": "workspace:^", "@aztec/simulator": "workspace:^", "@aztec/types": "workspace:^", diff --git a/yarn-project/txe/tsconfig.json b/yarn-project/txe/tsconfig.json index a57cb2cd9946..7db2bf797787 100644 --- a/yarn-project/txe/tsconfig.json +++ b/yarn-project/txe/tsconfig.json @@ -27,6 +27,9 @@ { "path": "../kv-store" }, + { + "path": "../noir-contracts.js" + }, { "path": "../pxe" }, diff --git a/yarn-project/yarn.lock b/yarn-project/yarn.lock index c09a315a1b0e..cc328c7abd12 100644 --- a/yarn-project/yarn.lock +++ b/yarn-project/yarn.lock @@ -944,6 +944,7 @@ __metadata: "@aztec/foundation": "workspace:^" "@aztec/key-store": "workspace:^" "@aztec/kv-store": "workspace:^" + "@aztec/noir-contracts.js": "workspace:^" "@aztec/pxe": "workspace:^" "@aztec/simulator": "workspace:^" "@aztec/types": "workspace:^" From 083b1f86dd684270496dc1bf3e6fb72912c1134e Mon Sep 17 00:00:00 2001 From: thunkar Date: Mon, 17 Jun 2024 14:32:22 +0000 Subject: [PATCH 50/60] better dockerfile --- noir-projects/Dockerfile | 26 +++++++++++++++----------- 1 file changed, 15 insertions(+), 11 deletions(-) diff --git a/noir-projects/Dockerfile b/noir-projects/Dockerfile index 4404a9e5accf..c4eaa46b39ff 100644 --- a/noir-projects/Dockerfile +++ b/noir-projects/Dockerfile @@ -15,20 +15,24 @@ ENV PATH="/usr/src/avm-transpiler/target/release:${PATH}" WORKDIR /usr/src/noir-projects COPY . . -# Launch TXE -WORKDIR /usr/src/yarn-project/txe -RUN yarn start & echo $! > /tmp/txe.pid # Build -WORKDIR /usr/src/noir-projects/noir-contracts -RUN ./bootstrap.sh && nargo test --silence-warnings --oracle-resolver http://localhost:8080 -WORKDIR /usr/src/noir-projects/noir-protocol-circuits -RUN ./bootstrap.sh && nargo test --silence-warnings -WORKDIR /usr/src/noir-projects/aztec-nr -RUN nargo test --silence-warnings --oracle-resolver http://localhost:8080 +WORKDIR /usr/src/noir-projects +RUN cd noir-protocol-circuits && ./bootstrap.sh && nargo test --silence-warnings + +RUN cd /usr/src/yarn-project/txe && yarn start & echo $! > /tmp/txe.pid && \ + # Wait for TXE to initialize + sleep 5 && \ + cd /usr/src/noir-projects/noir-contracts && \ + ./bootstrap.sh && nargo test --silence-warnings --oracle-resolver http://localhost:8080 && \ + kill $(cat /tmp/txe.pid) + +RUN cd /usr/src/yarn-project/txe && yarn start & echo $! > /tmp/txe.pid && \ + # Wait for TXE to initialize + sleep 5 && \ + cd /usr/src/noir-projects/aztec-nr && \ + nargo test --silence-warnings --oracle-resolver http://localhost:8080 -# Kill TXE -RUN kill $(cat /tmp/txe.pid) FROM scratch COPY --from=builder /usr/src/noir-projects /usr/src/noir-projects \ No newline at end of file From ab5e0e9c07a2cb51458471f5c0a9966703e811f5 Mon Sep 17 00:00:00 2001 From: thunkar Date: Tue, 18 Jun 2024 10:42:26 +0000 Subject: [PATCH 51/60] nargo fmt --- .../noir-contracts/contracts/uniswap_contract/src/main.nr | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/noir-projects/noir-contracts/contracts/uniswap_contract/src/main.nr b/noir-projects/noir-contracts/contracts/uniswap_contract/src/main.nr index d00374bb4e47..3bf3f00fb1bc 100644 --- a/noir-projects/noir-contracts/contracts/uniswap_contract/src/main.nr +++ b/noir-projects/noir-contracts/contracts/uniswap_contract/src/main.nr @@ -21,14 +21,14 @@ contract Uniswap { struct Storage { portal_address: SharedImmutable, } - + #[aztec(public)] #[aztec(initializer)] fn constructor(portal_address: EthAddress) { storage.portal_address.initialize(portal_address); } // docs:end:uniswap_setup - + // docs:start:swap_public #[aztec(public)] fn swap_public( From 39b5f76618059b388b4042612a2be1ca9e0e2013 Mon Sep 17 00:00:00 2001 From: thunkar Date: Tue, 18 Jun 2024 14:49:38 +0000 Subject: [PATCH 52/60] comments from PR --- .../contracts/parent_contract/src/main.nr | 6 ++---- .../aztec_macros/src/transforms/contract_interface.rs | 10 +++++----- yarn-project/deploy_npm.sh | 1 + yarn-project/txe/src/oracle/txe_oracle.ts | 4 ---- 4 files changed, 8 insertions(+), 13 deletions(-) diff --git a/noir-projects/noir-contracts/contracts/parent_contract/src/main.nr b/noir-projects/noir-contracts/contracts/parent_contract/src/main.nr index 4247d25da6f8..e6901b1a3191 100644 --- a/noir-projects/noir-contracts/contracts/parent_contract/src/main.nr +++ b/noir-projects/noir-contracts/contracts/parent_contract/src/main.nr @@ -255,8 +255,6 @@ contract Parent { // Setup env, generate keys let mut env = TestEnvironment::new(); let owner = env.create_account(); - // Initial address, since we don't have to deploy Parent in order to test it - let parent_contract_address = cheatcodes::get_contract_address(); // Deploy child contract let child_contract_address = env.deploy("@aztec/noir-contracts.js/Child").without_initializer(); @@ -264,7 +262,7 @@ contract Parent { // Set value in child through parent let value_to_set = 7; - let parent_private_set_call_interface = Parent::at(parent_contract_address).private_call( + let parent_private_set_call_interface = Parent::interface().private_call( child_contract_address, FunctionSelector::from_signature("private_set_value(Field,(Field))"), [value_to_set, owner.to_field()] @@ -281,7 +279,7 @@ contract Parent { assert(note_value == value_to_set); assert(note_value == result); // Get value from child through parent - let parent_private_get_call_interface = Parent::at(parent_contract_address).private_call( + let parent_private_get_call_interface = Parent::interface().private_call( child_contract_address, FunctionSelector::from_signature("private_get_value(Field,(Field))"), [7, owner.to_field()] diff --git a/noir/noir-repo/aztec_macros/src/transforms/contract_interface.rs b/noir/noir-repo/aztec_macros/src/transforms/contract_interface.rs index a8babdc944db..1875ab0b2521 100644 --- a/noir/noir-repo/aztec_macros/src/transforms/contract_interface.rs +++ b/noir/noir-repo/aztec_macros/src/transforms/contract_interface.rs @@ -100,7 +100,6 @@ pub fn stub_function(aztec_visibility: &str, func: &NoirFunction, is_static_call let original = format!( "| inputs: dep::aztec::context::inputs::{}ContextInputs | -> {} {{ - let _ = failing_env_workaround; {}(inputs{}) }}", aztec_visibility, @@ -113,12 +112,15 @@ pub fn stub_function(aztec_visibility: &str, func: &NoirFunction, is_static_call if param_types.is_empty() { "".to_string() } else { format!(" ,{} ", param_types) } ); let arg_types = format!( - "(Field, {})", + "({}{})", parameters .iter() .map(|param| param.typ.typ.to_string().replace("plain::", "")) .collect::>() - .join(",") + .join(","), + // In order to distinguish between a single element Tuple (Type,) and a single type with unnecessary parenthesis around it (Type), + // The latter gets simplified to Type, that is NOT a valid env + if parameters.len() == 1 { "," } else { "" } ); let generics = if is_void == "Void" { @@ -146,7 +148,6 @@ pub fn stub_function(aztec_visibility: &str, func: &NoirFunction, is_static_call format!( "{} - let failing_env_workaround = 0; let selector = {}; dep::aztec::context::{}{}{}CallInterface {{ target_contract: self.target_contract, @@ -167,7 +168,6 @@ pub fn stub_function(aztec_visibility: &str, func: &NoirFunction, is_static_call ); format!( "{} - let failing_env_workaround = 0; let selector = {}; dep::aztec::context::{}{}{}CallInterface {{ target_contract: self.target_contract, diff --git a/yarn-project/deploy_npm.sh b/yarn-project/deploy_npm.sh index f8d18abdac76..b7154d1226c6 100755 --- a/yarn-project/deploy_npm.sh +++ b/yarn-project/deploy_npm.sh @@ -105,3 +105,4 @@ deploy_package p2p deploy_package prover-client deploy_package sequencer-client deploy_package aztec-node +deploy_package txe diff --git a/yarn-project/txe/src/oracle/txe_oracle.ts b/yarn-project/txe/src/oracle/txe_oracle.ts index 9cbb375f0962..09115f3ebab2 100644 --- a/yarn-project/txe/src/oracle/txe_oracle.ts +++ b/yarn-project/txe/src/oracle/txe_oracle.ts @@ -310,10 +310,6 @@ export class TXE implements TypedOracle { // Nullified pending notes are already removed from the list. const pendingNotes = this.noteCache.getNotes(this.contractAddress, storageSlot); - // const pendingNullifiers = this.noteCache.getNullifiers(this.contractAddress); - // const dbNotes = await this.db.getNotes(this.contractAddress, storageSlot, status); - // const dbNotesFiltered = dbNotes.filter(n => !pendingNullifiers.has((n.siloedNullifier as Fr).value)); - const notes = pickNotes(pendingNotes, { selects: selectByIndexes.slice(0, numSelects).map((index, i) => ({ selector: { index, offset: selectByOffsets[i], length: selectByLengths[i] }, From 9e0ac91f81f65483a69d2068fd4addb68b3805ce Mon Sep 17 00:00:00 2001 From: thunkar Date: Tue, 18 Jun 2024 16:04:37 +0000 Subject: [PATCH 53/60] kill txe even if nargo test fails --- noir-projects/Dockerfile | 2 +- noir-projects/Earthfile | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/noir-projects/Dockerfile b/noir-projects/Dockerfile index c4eaa46b39ff..b8abf566f844 100644 --- a/noir-projects/Dockerfile +++ b/noir-projects/Dockerfile @@ -24,7 +24,7 @@ RUN cd /usr/src/yarn-project/txe && yarn start & echo $! > /tmp/txe.pid && \ # Wait for TXE to initialize sleep 5 && \ cd /usr/src/noir-projects/noir-contracts && \ - ./bootstrap.sh && nargo test --silence-warnings --oracle-resolver http://localhost:8080 && \ + ./bootstrap.sh && nargo test --silence-warnings --oracle-resolver http://localhost:8080 ; \ kill $(cat /tmp/txe.pid) RUN cd /usr/src/yarn-project/txe && yarn start & echo $! > /tmp/txe.pid && \ diff --git a/noir-projects/Earthfile b/noir-projects/Earthfile index 6ca6b1e296e2..df8db2aa0469 100644 --- a/noir-projects/Earthfile +++ b/noir-projects/Earthfile @@ -52,13 +52,13 @@ test: RUN cd /usr/src/yarn-project/txe && yarn start & echo $! > /tmp/txe.pid && \ # Wait for TXE to initialize sleep 5 && \ - cd /usr/src/noir-projects/aztec-nr && nargo test --silence-warnings --oracle-resolver http://localhost:8080 && \ + cd /usr/src/noir-projects/aztec-nr && nargo test --silence-warnings --oracle-resolver http://localhost:8080 ; \ kill $(cat /tmp/txe.pid) RUN cd /usr/src/yarn-project/txe && yarn start & echo $! > /tmp/txe.pid && \ # Wait for TXE to initialize sleep 5 && \ - cd /usr/src/noir-projects/noir-contracts && nargo test --silence-warnings --oracle-resolver http://localhost:8080 && \ + cd /usr/src/noir-projects/noir-contracts && nargo test --silence-warnings --oracle-resolver http://localhost:8080 ; \ kill $(cat /tmp/txe.pid) format: From 2dab518733679a895adbec47dc3e13e338f4fab1 Mon Sep 17 00:00:00 2001 From: thunkar Date: Tue, 18 Jun 2024 20:00:49 +0000 Subject: [PATCH 54/60] fix circleCI --- build_manifest.yml | 3 ++- noir-projects/Dockerfile | 4 ++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/build_manifest.yml b/build_manifest.yml index 0824765b52b6..15de111f658f 100644 --- a/build_manifest.yml +++ b/build_manifest.yml @@ -64,12 +64,13 @@ aztec-nargo: - noir multiarch: buildx -# Compiles all aztec noir projects using nargo and the avm-transpiler. +# Compiles all aztec noir projects using nargo and the avm-transpiler. Tests them using the TXE. noir-projects: buildDir: noir-projects dependencies: - noir - avm-transpiler + - yarn-project # Uses nargo to compile all the noir test programs, used to test bb and bb.js. noir-compile-acir-tests: diff --git a/noir-projects/Dockerfile b/noir-projects/Dockerfile index b8abf566f844..a7582e2a508e 100644 --- a/noir-projects/Dockerfile +++ b/noir-projects/Dockerfile @@ -11,6 +11,10 @@ ENV PATH="/usr/src/noir/noir-repo/target/release:${PATH}" # Copy in transpiler COPY --from=transpiler /usr/src/avm-transpiler/target/release/avm-transpiler /usr/src/avm-transpiler/target/release/avm-transpiler ENV PATH="/usr/src/avm-transpiler/target/release:${PATH}" +# Copy in yarn project +COPY --from=yarn-project /usr/src/yarn-project/txe /usr/src/yarn-project/txe +COPY --from=yarn-project /usr/src/yarn-project/node_modules /usr/src/yarn-project/node_modules + # Copy in noir projects WORKDIR /usr/src/noir-projects COPY . . From 9e41eb1b89598d9bafd5a9d64cb5bda570e17541 Mon Sep 17 00:00:00 2001 From: thunkar Date: Tue, 18 Jun 2024 22:17:41 +0000 Subject: [PATCH 55/60] fixed? --- build_manifest.yml | 12 +++++++++++- noir-projects/Dockerfile | 29 ++++++---------------------- noir-projects/Dockerfile.test | 36 +++++++++++++++++++++++++++++++++++ 3 files changed, 53 insertions(+), 24 deletions(-) create mode 100644 noir-projects/Dockerfile.test diff --git a/build_manifest.yml b/build_manifest.yml index 15de111f658f..1d728b031002 100644 --- a/build_manifest.yml +++ b/build_manifest.yml @@ -64,12 +64,22 @@ aztec-nargo: - noir multiarch: buildx -# Compiles all aztec noir projects using nargo and the avm-transpiler. Tests them using the TXE. +# Compiles all aztec noir projects using nargo and the avm-transpiler. noir-projects: buildDir: noir-projects dependencies: - noir - avm-transpiler + +# Compiles all aztec noir projects using nargo and the avm-transpiler, then tests them using the TXE. +noir-projects-tests: + buildDir: noir-projects + dockerfile: Dockerfile.test + rebuildPatterns: + - ^noir-projects/Dockerfile$ + dependencies: + - noir + - avm-transpiler - yarn-project # Uses nargo to compile all the noir test programs, used to test bb and bb.js. diff --git a/noir-projects/Dockerfile b/noir-projects/Dockerfile index a7582e2a508e..dc2ecf463622 100644 --- a/noir-projects/Dockerfile +++ b/noir-projects/Dockerfile @@ -1,6 +1,5 @@ FROM aztecprotocol/noir as noir FROM aztecprotocol/avm-transpiler as transpiler -FROM aztecprotocol/yarn-project AS yarn-project FROM ubuntu:lunar AS builder RUN apt-get update && apt-get install -y parallel nodejs npm @@ -11,32 +10,16 @@ ENV PATH="/usr/src/noir/noir-repo/target/release:${PATH}" # Copy in transpiler COPY --from=transpiler /usr/src/avm-transpiler/target/release/avm-transpiler /usr/src/avm-transpiler/target/release/avm-transpiler ENV PATH="/usr/src/avm-transpiler/target/release:${PATH}" -# Copy in yarn project -COPY --from=yarn-project /usr/src/yarn-project/txe /usr/src/yarn-project/txe -COPY --from=yarn-project /usr/src/yarn-project/node_modules /usr/src/yarn-project/node_modules - # Copy in noir projects WORKDIR /usr/src/noir-projects COPY . . - - # Build -WORKDIR /usr/src/noir-projects -RUN cd noir-protocol-circuits && ./bootstrap.sh && nargo test --silence-warnings - -RUN cd /usr/src/yarn-project/txe && yarn start & echo $! > /tmp/txe.pid && \ - # Wait for TXE to initialize - sleep 5 && \ - cd /usr/src/noir-projects/noir-contracts && \ - ./bootstrap.sh && nargo test --silence-warnings --oracle-resolver http://localhost:8080 ; \ - kill $(cat /tmp/txe.pid) - -RUN cd /usr/src/yarn-project/txe && yarn start & echo $! > /tmp/txe.pid && \ - # Wait for TXE to initialize - sleep 5 && \ - cd /usr/src/noir-projects/aztec-nr && \ - nargo test --silence-warnings --oracle-resolver http://localhost:8080 - +WORKDIR /usr/src/noir-projects/noir-contracts +RUN ./bootstrap.sh +WORKDIR /usr/src/noir-projects/noir-protocol-circuits +RUN ./bootstrap.sh +WORKDIR /usr/src/noir-projects/aztec-nr +RUN nargo compile --silence-warnings FROM scratch COPY --from=builder /usr/src/noir-projects /usr/src/noir-projects \ No newline at end of file diff --git a/noir-projects/Dockerfile.test b/noir-projects/Dockerfile.test new file mode 100644 index 000000000000..0436e22d5c1b --- /dev/null +++ b/noir-projects/Dockerfile.test @@ -0,0 +1,36 @@ +FROM aztecprotocol/noir as noir +FROM aztecprotocol/avm-transpiler as transpiler +FROM aztecprotocol/yarn-project as yarn-project + +# Copy in nargo +COPY --from=noir /usr/src/noir/noir-repo/target/release/nargo /usr/src/noir/noir-repo/target/release/nargo +ENV PATH="/usr/src/noir/noir-repo/target/release:${PATH}" +# Copy in transpiler +COPY --from=transpiler /usr/src/avm-transpiler/target/release/avm-transpiler /usr/src/avm-transpiler/target/release/avm-transpiler +ENV PATH="/usr/src/avm-transpiler/target/release:${PATH}" + +# All built files are ignored, but this build is fast and we're moving to earthly, so we don't bother +WORKDIR /usr/src/yarn-project +RUN yarn workspaces focus @aztec/txe +RUN cd ./txe && yarn build + +# Copy in noir projects +WORKDIR /usr/src/noir-projects +COPY . . + +# Build & test +WORKDIR /usr/src/noir-projects +RUN cd noir-protocol-circuits && ./bootstrap.sh && nargo test --silence-warnings + +RUN cd /usr/src/yarn-project/txe && yarn start & echo $! > /tmp/txe.pid && \ + # Wait for TXE to initialize + sleep 5 && \ + cd /usr/src/noir-projects/noir-contracts && \ + ./bootstrap.sh && nargo test --silence-warnings --oracle-resolver http://localhost:8080 ; \ + kill $(cat /tmp/txe.pid) + +RUN cd /usr/src/yarn-project/txe && yarn start & echo $! > /tmp/txe.pid && \ + # Wait for TXE to initialize + sleep 5 && \ + cd /usr/src/noir-projects/aztec-nr && \ + nargo test --silence-warnings --oracle-resolver http://localhost:8080 \ No newline at end of file From 4a80d7bea807c4d9edfa2b0ee78842cedf4fadab Mon Sep 17 00:00:00 2001 From: thunkar Date: Tue, 18 Jun 2024 22:19:23 +0000 Subject: [PATCH 56/60] better test --- noir-projects/Dockerfile.test | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/noir-projects/Dockerfile.test b/noir-projects/Dockerfile.test index 0436e22d5c1b..6ecaff3a2df4 100644 --- a/noir-projects/Dockerfile.test +++ b/noir-projects/Dockerfile.test @@ -20,13 +20,13 @@ COPY . . # Build & test WORKDIR /usr/src/noir-projects -RUN cd noir-protocol-circuits && ./bootstrap.sh && nargo test --silence-warnings +RUN cd noir-protocol-circuits && nargo test --silence-warnings RUN cd /usr/src/yarn-project/txe && yarn start & echo $! > /tmp/txe.pid && \ # Wait for TXE to initialize sleep 5 && \ cd /usr/src/noir-projects/noir-contracts && \ - ./bootstrap.sh && nargo test --silence-warnings --oracle-resolver http://localhost:8080 ; \ + nargo test --silence-warnings --oracle-resolver http://localhost:8080 ; \ kill $(cat /tmp/txe.pid) RUN cd /usr/src/yarn-project/txe && yarn start & echo $! > /tmp/txe.pid && \ From 9fcfe12f9b12eddfa02412d383b9751d303987ca Mon Sep 17 00:00:00 2001 From: thunkar Date: Tue, 18 Jun 2024 22:21:16 +0000 Subject: [PATCH 57/60] improvements --- build_manifest.yml | 3 +-- noir-projects/Dockerfile.test | 5 +++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/build_manifest.yml b/build_manifest.yml index 1d728b031002..3847e0fbf849 100644 --- a/build_manifest.yml +++ b/build_manifest.yml @@ -75,9 +75,8 @@ noir-projects: noir-projects-tests: buildDir: noir-projects dockerfile: Dockerfile.test - rebuildPatterns: - - ^noir-projects/Dockerfile$ dependencies: + - noir-projects - noir - avm-transpiler - yarn-project diff --git a/noir-projects/Dockerfile.test b/noir-projects/Dockerfile.test index 6ecaff3a2df4..c9144fe11a6d 100644 --- a/noir-projects/Dockerfile.test +++ b/noir-projects/Dockerfile.test @@ -2,6 +2,8 @@ FROM aztecprotocol/noir as noir FROM aztecprotocol/avm-transpiler as transpiler FROM aztecprotocol/yarn-project as yarn-project +RUN apt-get update && apt-get install -y parallel + # Copy in nargo COPY --from=noir /usr/src/noir/noir-repo/target/release/nargo /usr/src/noir/noir-repo/target/release/nargo ENV PATH="/usr/src/noir/noir-repo/target/release:${PATH}" @@ -15,8 +17,7 @@ RUN yarn workspaces focus @aztec/txe RUN cd ./txe && yarn build # Copy in noir projects -WORKDIR /usr/src/noir-projects -COPY . . +COPY --from=transpiler /usr/src/noir-projects /usr/src/noir-projects # Build & test WORKDIR /usr/src/noir-projects From 82c3860883301b1a534060875cd6447857cad9b4 Mon Sep 17 00:00:00 2001 From: thunkar Date: Tue, 18 Jun 2024 22:21:49 +0000 Subject: [PATCH 58/60] typo+ --- noir-projects/Dockerfile.test | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/noir-projects/Dockerfile.test b/noir-projects/Dockerfile.test index c9144fe11a6d..b80298761889 100644 --- a/noir-projects/Dockerfile.test +++ b/noir-projects/Dockerfile.test @@ -17,7 +17,7 @@ RUN yarn workspaces focus @aztec/txe RUN cd ./txe && yarn build # Copy in noir projects -COPY --from=transpiler /usr/src/noir-projects /usr/src/noir-projects +COPY --from=noir-contracts /usr/src/noir-projects /usr/src/noir-projects # Build & test WORKDIR /usr/src/noir-projects From e3b1cc446de1c1dfa80b55db3e527a2678b5dcb8 Mon Sep 17 00:00:00 2001 From: thunkar Date: Tue, 18 Jun 2024 23:00:59 +0000 Subject: [PATCH 59/60] no way around it --- build_manifest.yml | 1 - noir-projects/Dockerfile.test | 12 ++++++------ 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/build_manifest.yml b/build_manifest.yml index 3847e0fbf849..b26e4e84d968 100644 --- a/build_manifest.yml +++ b/build_manifest.yml @@ -76,7 +76,6 @@ noir-projects-tests: buildDir: noir-projects dockerfile: Dockerfile.test dependencies: - - noir-projects - noir - avm-transpiler - yarn-project diff --git a/noir-projects/Dockerfile.test b/noir-projects/Dockerfile.test index b80298761889..3a794f4955f4 100644 --- a/noir-projects/Dockerfile.test +++ b/noir-projects/Dockerfile.test @@ -17,21 +17,21 @@ RUN yarn workspaces focus @aztec/txe RUN cd ./txe && yarn build # Copy in noir projects -COPY --from=noir-contracts /usr/src/noir-projects /usr/src/noir-projects +WORKDIR /usr/src/noir-projects +COPY . . # Build & test -WORKDIR /usr/src/noir-projects -RUN cd noir-protocol-circuits && nargo test --silence-warnings +RUN cd ./noir-protocol-circuits && ./bootstrap.sh && nargo test --silence-warnings RUN cd /usr/src/yarn-project/txe && yarn start & echo $! > /tmp/txe.pid && \ # Wait for TXE to initialize sleep 5 && \ - cd /usr/src/noir-projects/noir-contracts && \ - nargo test --silence-warnings --oracle-resolver http://localhost:8080 ; \ + cd ./noir-contracts && \ + ./bootstrap.sh && nargo test --silence-warnings --oracle-resolver http://localhost:8080 ; \ kill $(cat /tmp/txe.pid) RUN cd /usr/src/yarn-project/txe && yarn start & echo $! > /tmp/txe.pid && \ # Wait for TXE to initialize sleep 5 && \ - cd /usr/src/noir-projects/aztec-nr && \ + cd ./aztec-nr && \ nargo test --silence-warnings --oracle-resolver http://localhost:8080 \ No newline at end of file From 168e1cdb22e285fdd07f95a894e393ec5e65f270 Mon Sep 17 00:00:00 2001 From: thunkar Date: Tue, 18 Jun 2024 23:07:48 +0000 Subject: [PATCH 60/60] better comment --- noir-projects/Dockerfile.test | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/noir-projects/Dockerfile.test b/noir-projects/Dockerfile.test index 3a794f4955f4..40edcbaaf35e 100644 --- a/noir-projects/Dockerfile.test +++ b/noir-projects/Dockerfile.test @@ -11,7 +11,8 @@ ENV PATH="/usr/src/noir/noir-repo/target/release:${PATH}" COPY --from=transpiler /usr/src/avm-transpiler/target/release/avm-transpiler /usr/src/avm-transpiler/target/release/avm-transpiler ENV PATH="/usr/src/avm-transpiler/target/release:${PATH}" -# All built files are ignored, but this build is fast and we're moving to earthly, so we don't bother +# All built files are ignored so we have to repeat the txe build here. +# It is fast (compared to the tests) and we're moving to earthly, so we don't bother with something more sophisticated. WORKDIR /usr/src/yarn-project RUN yarn workspaces focus @aztec/txe RUN cd ./txe && yarn build