From 5d9e254c253730c595152d35f73f443e16bbcd95 Mon Sep 17 00:00:00 2001 From: Santiago Palladino Date: Wed, 12 Jul 2023 05:11:13 -0300 Subject: [PATCH] Add oracle call to retrieve public key for a given address (#1027) * Adds oracle call to fetch the public key for an address * Placeholder for address validation * Temporarily remove sanity check --- .../cpp/src/aztec3/circuits/abis/c_bind.cpp | 27 ++++++++++++ circuits/cpp/src/aztec3/circuits/hash.hpp | 18 +++++--- yarn-project/acir-simulator/src/acvm/acvm.ts | 1 + .../acir-simulator/src/client/db_oracle.ts | 2 + .../src/client/private_execution.test.ts | 27 ++++++++++-- .../src/client/private_execution.ts | 5 +++ .../src/client/unconstrained_execution.ts | 8 +++- .../src/account_state/account_state.ts | 1 - .../aztec_rpc_server/aztec_rpc_server.test.ts | 44 +++++++++++++++++++ .../src/aztec_rpc_server/aztec_rpc_server.ts | 10 +++++ .../aztec-rpc/src/database/database.ts | 6 ++- .../aztec-rpc/src/database/memory_db.ts | 13 +++++- .../aztec-rpc/src/simulator_oracle/index.ts | 14 +++++- yarn-project/circuits.js/src/abis/abis.ts | 21 ++++++++- .../end-to-end/src/e2e_p2p_network.test.ts | 10 ++++- .../foundation/src/aztec-address/index.ts | 8 ++++ .../src/contracts/test_contract/src/main.nr | 13 +++++- .../src/examples/test_contract.json | 20 ++++++++- .../src/libs/noir-aztec/src/generators.nr | 3 +- .../src/libs/noir-aztec/src/oracle.nr | 1 + .../noir-aztec/src/oracle/get_public_key.nr | 22 ++++++++++ 21 files changed, 251 insertions(+), 23 deletions(-) create mode 100644 yarn-project/aztec-rpc/src/aztec_rpc_server/aztec_rpc_server.test.ts create mode 100644 yarn-project/noir-contracts/src/libs/noir-aztec/src/oracle/get_public_key.nr diff --git a/circuits/cpp/src/aztec3/circuits/abis/c_bind.cpp b/circuits/cpp/src/aztec3/circuits/abis/c_bind.cpp index fd7259fd268..adb07e4786b 100644 --- a/circuits/cpp/src/aztec3/circuits/abis/c_bind.cpp +++ b/circuits/cpp/src/aztec3/circuits/abis/c_bind.cpp @@ -320,6 +320,33 @@ WASM_EXPORT void abis__compute_contract_address(uint8_t const* point_data_buf, NT::fr::serialize_to_buffer(contract_address, output); } +/** + * @brief Compute a contract address from its partial contract address + * This is a WASM-export that can be called from Typescript. + * + * @details Computes a contract address by hashing the deployers public key along with the previously computed partial + * address Return the serialized results in the `output` buffer. + * + * @param point_data_buf point data struct as a buffer of bytes + * @param partial_address_data_buf partial contract address + * @param output buffer that will contain the output. The serialized contract address. + */ +WASM_EXPORT void abis__compute_contract_address_from_partial(uint8_t const* point_data_buf, + uint8_t const* partial_address_data_buf, + uint8_t* output) +{ + Point deployer_public_key; + NT::fr partial_address; + + read(point_data_buf, deployer_public_key); + read(partial_address_data_buf, partial_address); + + NT::fr const contract_address = + aztec3::circuits::compute_contract_address_from_partial(deployer_public_key, partial_address); + + NT::fr::serialize_to_buffer(contract_address, output); +} + /** * @brief Compute a partial contract address * This is a WASM-export that can be called from Typescript. diff --git a/circuits/cpp/src/aztec3/circuits/hash.hpp b/circuits/cpp/src/aztec3/circuits/hash.hpp index 29986cc3dee..4974c5e9ca4 100644 --- a/circuits/cpp/src/aztec3/circuits/hash.hpp +++ b/circuits/cpp/src/aztec3/circuits/hash.hpp @@ -57,21 +57,29 @@ template typename NCT::fr compute_partial_contract_address(typena return NCT::compress(inputs, aztec3::GeneratorIndex::PARTIAL_CONTRACT_ADDRESS); } +template +typename NCT::address compute_contract_address_from_partial(Point const& point, typename NCT::fr partial_address) +{ + using fr = typename NCT::fr; + using address = typename NCT::address; + + std::vector const inputs = { + point.x.fields[0], point.x.fields[1], point.y.fields[0], point.y.fields[1], partial_address, + }; + return address(NCT::compress(inputs, aztec3::GeneratorIndex::CONTRACT_ADDRESS)); +} + template typename NCT::address compute_contract_address(Point const& point, typename NCT::fr contract_address_salt, typename NCT::fr function_tree_root, typename NCT::fr constructor_hash) { using fr = typename NCT::fr; - using address = typename NCT::address; const fr partial_address = compute_partial_contract_address(contract_address_salt, function_tree_root, constructor_hash); - std::vector const inputs = { - point.x.fields[0], point.x.fields[1], point.y.fields[0], point.y.fields[1], partial_address, - }; - return address(NCT::compress(inputs, aztec3::GeneratorIndex::CONTRACT_ADDRESS)); + return compute_contract_address_from_partial(point, partial_address); } template diff --git a/yarn-project/acir-simulator/src/acvm/acvm.ts b/yarn-project/acir-simulator/src/acvm/acvm.ts index fdbb1b27167..db29d457247 100644 --- a/yarn-project/acir-simulator/src/acvm/acvm.ts +++ b/yarn-project/acir-simulator/src/acvm/acvm.ts @@ -38,6 +38,7 @@ type ORACLE_NAMES = | 'getL1ToL2Message' | 'emitEncryptedLog' | 'emitUnencryptedLog' + | 'getPublicKey' | 'debugLog'; /** diff --git a/yarn-project/acir-simulator/src/client/db_oracle.ts b/yarn-project/acir-simulator/src/client/db_oracle.ts index 26a857bab43..d3b43af3328 100644 --- a/yarn-project/acir-simulator/src/client/db_oracle.ts +++ b/yarn-project/acir-simulator/src/client/db_oracle.ts @@ -3,6 +3,7 @@ import { EthAddress } from '@aztec/foundation/eth-address'; import { Fr, Point } from '@aztec/foundation/fields'; import { FunctionAbi } from '@aztec/foundation/abi'; import { CommitmentsDB } from '../index.js'; +import { PartialContractAddress } from '@aztec/types'; /** * The format that noir contracts use to get notes. @@ -57,6 +58,7 @@ export interface CommitmentDataOracleInputs { * The database oracle interface. */ export interface DBOracle extends CommitmentsDB { + getPublicKey(address: AztecAddress): Promise<[Point, PartialContractAddress]>; getSecretKey(contractAddress: AztecAddress, pubKey: Point): Promise; getNotes( contractAddress: AztecAddress, diff --git a/yarn-project/acir-simulator/src/client/private_execution.test.ts b/yarn-project/acir-simulator/src/client/private_execution.test.ts index 463ca599100..c89e53b7fdd 100644 --- a/yarn-project/acir-simulator/src/client/private_execution.test.ts +++ b/yarn-project/acir-simulator/src/client/private_execution.test.ts @@ -10,13 +10,13 @@ import { PublicCallRequest, TxContext, } from '@aztec/circuits.js'; -import { computeSecretMessageHash, siloCommitment } from '@aztec/circuits.js/abis'; +import { computeContractAddressFromPartial, computeSecretMessageHash, siloCommitment } from '@aztec/circuits.js/abis'; import { Grumpkin, pedersenPlookupCommitInputs } from '@aztec/circuits.js/barretenberg'; import { FunctionAbi } from '@aztec/foundation/abi'; import { AztecAddress } from '@aztec/foundation/aztec-address'; import { toBufferBE } from '@aztec/foundation/bigint-buffer'; import { EthAddress } from '@aztec/foundation/eth-address'; -import { Fr } from '@aztec/foundation/fields'; +import { Coordinate, Fr, Point } from '@aztec/foundation/fields'; import { DebugLogger, createDebugLogger } from '@aztec/foundation/log'; import { AppendOnlyTree, Pedersen, StandardTree, newTree } from '@aztec/merkle-tree'; import { @@ -27,7 +27,7 @@ import { ZkTokenContractAbi, } from '@aztec/noir-contracts/examples'; import { PackedArguments, TxExecutionRequest } from '@aztec/types'; -import { mock } from 'jest-mock-extended'; +import { MockProxy, mock } from 'jest-mock-extended'; import { default as levelup } from 'levelup'; import { default as memdown, type MemDown } from 'memdown'; import { encodeArguments } from '../abi_coder/index.js'; @@ -40,7 +40,7 @@ const createMemDown = () => (memdown as any)() as MemDown; describe('Private Execution test suite', () => { let circuitsWasm: CircuitsWasm; - let oracle: ReturnType>; + let oracle: MockProxy; let acirSimulator: AcirSimulator; let historicRoots = PrivateHistoricTreeRoots.empty(); let logger: DebugLogger; @@ -470,4 +470,23 @@ describe('Private Execution test suite', () => { ); }); }); + + describe('get public key', () => { + it('gets the public key for an address', async () => { + // Tweak the contract ABI so we can extract return values + const abi = TestContractAbi.functions.find(f => f.name === 'getPublicKey')!; + abi.returnTypes = [{ kind: 'field' }, { kind: 'field' }]; + + // Generate a partial address, pubkey, and resulting address + const partialAddress = Fr.random(); + const pubKey = new Point(new Coordinate([Fr.random(), Fr.ZERO]), new Coordinate([Fr.random(), Fr.ZERO])); + const wasm = await CircuitsWasm.get(); + const address = computeContractAddressFromPartial(wasm, pubKey, partialAddress); + const args = [address]; + + oracle.getPublicKey.mockResolvedValue([pubKey, partialAddress]); + const result = await runSimulator({ origin: AztecAddress.random(), abi, args }); + expect(result.returnValues).toEqual([pubKey.x.toBigInt(), pubKey.y.toBigInt()]); + }); + }); }); diff --git a/yarn-project/acir-simulator/src/client/private_execution.ts b/yarn-project/acir-simulator/src/client/private_execution.ts index 8487afcac88..69b4c0e0497 100644 --- a/yarn-project/acir-simulator/src/client/private_execution.ts +++ b/yarn-project/acir-simulator/src/client/private_execution.ts @@ -81,6 +81,11 @@ export class PrivateFunctionExecution { ), ), ), + getPublicKey: async ([acvmAddress]) => { + const address = frToAztecAddress(fromACVMField(acvmAddress)); + const [pubKey, partialContractAddress] = await this.context.db.getPublicKey(address); + return [pubKey.x.toBigInt(), pubKey.y.toBigInt(), partialContractAddress].map(toACVMField); + }, getNotes: ([slot], sortBy, sortOrder, [limit], [offset], [returnSize]) => this.context.getNotes(this.contractAddress, slot, sortBy, sortOrder, limit, offset, returnSize), getRandomField: () => Promise.resolve(toACVMField(Fr.random())), diff --git a/yarn-project/acir-simulator/src/client/unconstrained_execution.ts b/yarn-project/acir-simulator/src/client/unconstrained_execution.ts index d2d30e1fd4d..780ec1e5258 100644 --- a/yarn-project/acir-simulator/src/client/unconstrained_execution.ts +++ b/yarn-project/acir-simulator/src/client/unconstrained_execution.ts @@ -4,7 +4,7 @@ import { AztecAddress } from '@aztec/foundation/aztec-address'; import { Coordinate, Fr, Point } from '@aztec/foundation/fields'; import { createDebugLogger } from '@aztec/foundation/log'; import { decodeReturnValues } from '../abi_coder/decoder.js'; -import { extractReturnWitness } from '../acvm/deserialize.js'; +import { extractReturnWitness, frToAztecAddress } from '../acvm/deserialize.js'; import { ACVMField, ZERO_ACVM_FIELD, acvm, fromACVMField, toACVMField, toACVMWitness } from '../acvm/index.js'; import { ClientTxExecutionContext } from './client_execution_context.js'; import { oracleDebugCallToFormattedStr } from './debug.js'; @@ -49,7 +49,11 @@ export class UnconstrainedFunctionExecution { ), ), ), - + getPublicKey: async ([acvmAddress]) => { + const address = frToAztecAddress(fromACVMField(acvmAddress)); + const [pubKey, partialContractAddress] = await this.context.db.getPublicKey(address); + return [pubKey.x.toBigInt(), pubKey.y.toBigInt(), partialContractAddress].map(toACVMField); + }, getNotes: ([slot], sortBy, sortOrder, [limit], [offset], [returnSize]) => this.context.getNotes(this.contractAddress, slot, sortBy, sortOrder, limit, offset, returnSize), getRandomField: () => Promise.resolve(toACVMField(Fr.random())), diff --git a/yarn-project/aztec-rpc/src/account_state/account_state.ts b/yarn-project/aztec-rpc/src/account_state/account_state.ts index 1e68e55a4d3..be87bee4485 100644 --- a/yarn-project/aztec-rpc/src/account_state/account_state.ts +++ b/yarn-project/aztec-rpc/src/account_state/account_state.ts @@ -489,7 +489,6 @@ export class AccountState { contractDataOracle ?? new ContractDataOracle(this.db, this.node), this.db, keyPair, - this.address, this.node, ); return new AcirSimulator(simulatorOracle); diff --git a/yarn-project/aztec-rpc/src/aztec_rpc_server/aztec_rpc_server.test.ts b/yarn-project/aztec-rpc/src/aztec_rpc_server/aztec_rpc_server.test.ts new file mode 100644 index 00000000000..34de01357d8 --- /dev/null +++ b/yarn-project/aztec-rpc/src/aztec_rpc_server/aztec_rpc_server.test.ts @@ -0,0 +1,44 @@ +import { AztecNode } from '@aztec/aztec-node'; +import { AztecAddress, CircuitsWasm, Fr } from '@aztec/circuits.js'; +import { computeContractAddressFromPartial } from '@aztec/circuits.js/abis'; +import { Grumpkin } from '@aztec/circuits.js/barretenberg'; +import { ConstantKeyPair, TestKeyStore } from '@aztec/key-store'; +import { randomBytes } from 'crypto'; +import { MockProxy, mock } from 'jest-mock-extended'; +import { MemoryDB } from '../database/memory_db.js'; +import { AztecRPCServer } from './aztec_rpc_server.js'; + +describe('AztecRpcServer', function () { + let wasm: CircuitsWasm; + let keyStore: TestKeyStore; + let db: MemoryDB; + let node: MockProxy; + let rpcServer: AztecRPCServer; + + beforeEach(async () => { + keyStore = new TestKeyStore(await Grumpkin.new()); + node = mock(); + db = new MemoryDB(); + rpcServer = new AztecRPCServer(keyStore, node, db); + wasm = await CircuitsWasm.get(); + }); + + it('registers a public key in the db when adding a new account', async () => { + const keyPair = ConstantKeyPair.random(await Grumpkin.new()); + const pubKey = keyPair.getPublicKey(); + const partialAddress = Fr.random(); + const address = computeContractAddressFromPartial(wasm, pubKey, partialAddress); + + await rpcServer.addAccount(await keyPair.getPrivateKey(), address, partialAddress); + expect(await db.getPublicKey(address)).toEqual([pubKey, partialAddress]); + }); + + // TODO(#1007) + it.skip('refuses to add an account with incorrect address for given partial address and pubkey', async () => { + const privateKey = randomBytes(32); + const partialAddress = Fr.random(); + const address = AztecAddress.random(); + + await expect(rpcServer.addAccount(privateKey, address, partialAddress)).rejects.toThrowError(/cannot be derived/); + }); +}); diff --git a/yarn-project/aztec-rpc/src/aztec_rpc_server/aztec_rpc_server.ts b/yarn-project/aztec-rpc/src/aztec_rpc_server/aztec_rpc_server.ts index e769b26330f..2236d9e111d 100644 --- a/yarn-project/aztec-rpc/src/aztec_rpc_server/aztec_rpc_server.ts +++ b/yarn-project/aztec-rpc/src/aztec_rpc_server/aztec_rpc_server.ts @@ -79,6 +79,16 @@ export class AztecRPCServer implements AztecRPC { abi = SchnorrAccountContractAbi, ) { const pubKey = this.keyStore.addAccount(privKey); + // TODO(#1007): ECDSA contract breaks this check, since the ecdsa public key does not match the one derived from the keystore. + // Once we decouple the ecdsa contract signing and encryption keys, we can re-enable this check. + // const wasm = await CircuitsWasm.get(); + // const expectedAddress = computeContractAddressFromPartial(wasm, pubKey, partialContractAddress); + // if (!expectedAddress.equals(address)) { + // throw new Error( + // `Address cannot be derived from pubkey and partial address (received ${address.toString()}, derived ${expectedAddress.toString()})`, + // ); + // } + await this.db.addPublicKey(address, pubKey, partialContractAddress); await this.#initAccountState(pubKey, address, partialContractAddress, abi); return address; } diff --git a/yarn-project/aztec-rpc/src/database/database.ts b/yarn-project/aztec-rpc/src/database/database.ts index 57f15a664a7..eb1744553db 100644 --- a/yarn-project/aztec-rpc/src/database/database.ts +++ b/yarn-project/aztec-rpc/src/database/database.ts @@ -1,10 +1,11 @@ -import { TxHash } from '@aztec/types'; +import { PartialContractAddress, TxHash } from '@aztec/types'; import { ContractDatabase } from '../contract_database/index.js'; import { NoteSpendingInfoDao } from './note_spending_info_dao.js'; import { TxDao } from './tx_dao.js'; import { AztecAddress } from '@aztec/foundation/aztec-address'; import { Fr, Point } from '@aztec/foundation/fields'; import { MerkleTreeId } from '@aztec/types'; +import { PublicKey } from '@aztec/key-store'; /** * Options for selecting items from the database. @@ -49,4 +50,7 @@ export interface Database extends ContractDatabase { getTreeRoots(): Record; setTreeRoots(roots: Record): Promise; + + addPublicKey(address: AztecAddress, publicKey: PublicKey, partialAddress: PartialContractAddress): Promise; + getPublicKey(address: AztecAddress): Promise<[Point, PartialContractAddress] | undefined>; } diff --git a/yarn-project/aztec-rpc/src/database/memory_db.ts b/yarn-project/aztec-rpc/src/database/memory_db.ts index 6bbf9f7a6f1..2230ec6ac76 100644 --- a/yarn-project/aztec-rpc/src/database/memory_db.ts +++ b/yarn-project/aztec-rpc/src/database/memory_db.ts @@ -1,4 +1,4 @@ -import { TxHash } from '@aztec/types'; +import { PartialContractAddress, TxHash } from '@aztec/types'; import { MemoryContractDatabase } from '../contract_database/index.js'; import { Database, GetOptions } from './database.js'; import { NoteSpendingInfoDao } from './note_spending_info_dao.js'; @@ -6,6 +6,7 @@ import { TxDao } from './tx_dao.js'; import { AztecAddress } from '@aztec/foundation/aztec-address'; import { Fr, Point } from '@aztec/foundation/fields'; import { MerkleTreeId } from '@aztec/types'; +import { PublicKey } from '@aztec/key-store'; const sortNotes = (notes: NoteSpendingInfoDao[], sortBy: number[], sortOrder: number[]) => { const sortNotesLevel = (a: Fr[], b: Fr[], level = 0): number => { @@ -36,6 +37,7 @@ export class MemoryDB extends MemoryContractDatabase implements Database { private txTable: TxDao[] = []; private noteSpendingInfoTable: NoteSpendingInfoDao[] = []; private treeRoots: Record | undefined; + private publicKeys: Map = new Map(); /** * Retrieve a transaction from the MemoryDB using its transaction hash. @@ -196,4 +198,13 @@ export class MemoryDB extends MemoryContractDatabase implements Database { this.treeRoots = roots; return Promise.resolve(); } + + addPublicKey(address: AztecAddress, publicKey: Point, partialAddress: PartialContractAddress): Promise { + this.publicKeys.set(address.toBigInt(), [publicKey, partialAddress]); + return Promise.resolve(); + } + + getPublicKey(address: AztecAddress): Promise<[Point, Fr] | undefined> { + return Promise.resolve(this.publicKeys.get(address.toBigInt())); + } } diff --git a/yarn-project/aztec-rpc/src/simulator_oracle/index.ts b/yarn-project/aztec-rpc/src/simulator_oracle/index.ts index b9a10244e41..0eab6337b80 100644 --- a/yarn-project/aztec-rpc/src/simulator_oracle/index.ts +++ b/yarn-project/aztec-rpc/src/simulator_oracle/index.ts @@ -6,7 +6,7 @@ import { FunctionAbi } from '@aztec/foundation/abi'; import { ContractDataOracle } from '../contract_data_oracle/index.js'; import { Database } from '../database/index.js'; import { siloCommitment } from '@aztec/circuits.js/abis'; -import { MerkleTreeId } from '@aztec/types'; +import { MerkleTreeId, PartialContractAddress } from '@aztec/types'; /** * A data oracle that provides information needed for simulating a transaction. @@ -16,7 +16,6 @@ export class SimulatorOracle implements DBOracle { private contractDataOracle: ContractDataOracle, private db: Database, private keyPair: KeyPair, - private address: AztecAddress, private node: AztecNode, ) {} @@ -40,6 +39,17 @@ export class SimulatorOracle implements DBOracle { return this.keyPair.getPrivateKey(); } + /** + * Retrieve the public key associated to a given address. + * @param address - Address to fetch the pubkey for. + * @returns A public key and the corresponding partial contract address, such that the hash of the two resolves to the input address. + */ + async getPublicKey(address: AztecAddress): Promise<[Point, PartialContractAddress]> { + const result = await this.db.getPublicKey(address); + if (!result) throw new Error(`Unknown public key for address ${address.toString()}`); + return result; + } + /** * Retrieves a set of notes stored in the database for a given contract address and storage slot. * The query result is paginated using 'limit' and 'offset' values. diff --git a/yarn-project/circuits.js/src/abis/abis.ts b/yarn-project/circuits.js/src/abis/abis.ts index 11c86d9ba15..a87691f1905 100644 --- a/yarn-project/circuits.js/src/abis/abis.ts +++ b/yarn-project/circuits.js/src/abis/abis.ts @@ -170,7 +170,7 @@ export function hashConstructor( /** * Computes a contract address. * @param wasm - A module providing low-level wasm access. - * @param deployerAddr - The address of the contract deployer. + * @param deployerPubKey - The pubkey of the contract deployer. * @param contractAddrSalt - The salt used as 1 one of the inputs of the contract address computation. * @param fnTreeRoot - The function tree root of the contract being deployed. * @param constructorHash - The hash of the constructor. @@ -217,6 +217,25 @@ export function computePartialContractAddress( return Fr.fromBuffer(result); } +/** + * Computes a contract address from its partial address and the pubkey. + * @param wasm - A module providing low-level wasm access. + * @param partial - The salt used as 1 one of the inputs of the contract address computation. + * @param fnTreeRoot - The function tree root of the contract being deployed. + * @param constructorHash - The hash of the constructor. + * @returns The partially constructed contract address. + */ +export function computeContractAddressFromPartial(wasm: IWasmModule, pubKey: Point, partialAddress: Fr): AztecAddress { + wasm.call('pedersen__init'); + const result = inputBuffersToOutputBuffer( + wasm, + 'abis__compute_contract_address_from_partial', + [pubKey.toFieldsBuffer(), partialAddress.toBuffer()], + 32, + ); + return new AztecAddress(result); +} + /** * Computes a siloed commitment, given the contract address and the commitment itself. * A siloed commitment effectively namespaces a commitment to a specific contract. diff --git a/yarn-project/end-to-end/src/e2e_p2p_network.test.ts b/yarn-project/end-to-end/src/e2e_p2p_network.test.ts index 551f4a7c0b7..e0ec8fd6ce8 100644 --- a/yarn-project/end-to-end/src/e2e_p2p_network.test.ts +++ b/yarn-project/end-to-end/src/e2e_p2p_network.test.ts @@ -2,6 +2,8 @@ import { AztecNodeConfig, AztecNodeService } from '@aztec/aztec-node'; import { AztecAddress, AztecRPCServer, + CircuitsWasm, + ConstantKeyPair, ContractDeployer, Fr, SentTx, @@ -12,8 +14,9 @@ import { DebugLogger } from '@aztec/foundation/log'; import { TestContractAbi } from '@aztec/noir-contracts/examples'; import { BootstrapNode, P2PConfig, createLibP2PPeerId, exportLibP2PPeerIdToString } from '@aztec/p2p'; +import { computeContractAddressFromPartial } from '@aztec/circuits.js/abis'; +import { Grumpkin } from '@aztec/circuits.js/barretenberg'; import { setup } from './utils.js'; -import { randomBytes } from 'crypto'; const NUM_NODES = 4; const NUM_TXS_PER_BLOCK = 4; @@ -149,7 +152,10 @@ describe('e2e_p2p_network', () => { numTxs: number, ): Promise => { const aztecRpcServer = await createAztecRPCServer(node); - const account = await aztecRpcServer.addAccount(randomBytes(32), AztecAddress.random(), Fr.random()); + const keyPair = ConstantKeyPair.random(await Grumpkin.new()); + const partialAddress = Fr.random(); + const address = computeContractAddressFromPartial(await CircuitsWasm.get(), keyPair.getPublicKey(), partialAddress); + const account = await aztecRpcServer.addAccount(await keyPair.getPrivateKey(), address, partialAddress); const txs = await submitTxsTo(aztecRpcServer, account, numTxs); return { diff --git a/yarn-project/foundation/src/aztec-address/index.ts b/yarn-project/foundation/src/aztec-address/index.ts index e77f5a0b58c..e069a0dea7e 100644 --- a/yarn-project/foundation/src/aztec-address/index.ts +++ b/yarn-project/foundation/src/aztec-address/index.ts @@ -104,6 +104,14 @@ export class AztecAddress { return Fr.fromBuffer(this.toBuffer()); } + /** + * Returns this address as a bigint. Useful for creating maps indexed by addresses. + * @returns A bigint with the same value as the address. + */ + toBigInt() { + return toBigIntBE(this.buffer); + } + /** * Determines if this AztecAddress instance is equal to the given AztecAddress instance. * Equality is based on the content of their respective buffers. diff --git a/yarn-project/noir-contracts/src/contracts/test_contract/src/main.nr b/yarn-project/noir-contracts/src/contracts/test_contract/src/main.nr index c0d09471b41..e6d8f5ea292 100644 --- a/yarn-project/noir-contracts/src/contracts/test_contract/src/main.nr +++ b/yarn-project/noir-contracts/src/contracts/test_contract/src/main.nr @@ -11,7 +11,8 @@ contract Test { use dep::aztec::oracle::{ create_l2_to_l1_message::create_l2_to_l1_message, - create_nullifier::create_nullifier + create_nullifier::create_nullifier, + get_public_key::get_public_key }; fn constructor( @@ -21,6 +22,16 @@ contract Test { Context::new(inputs, 0).finish() } + fn getPublicKey( + inputs: PrivateContextInputs, + address: Field, + ) -> distinct pub abi::PrivateCircuitPublicInputs { + let mut context = Context::new(inputs, abi::hash_args([address])); + let pub_key = get_public_key(address); + context.return_values = context.return_values.push_array([pub_key.x, pub_key.y]); + context.finish() + } + // Purely exists for testing open fn createL2ToL1MessagePublic( _inputs: PublicContextInputs, diff --git a/yarn-project/noir-contracts/src/examples/test_contract.json b/yarn-project/noir-contracts/src/examples/test_contract.json index 54b34da7651..b135197822f 100644 --- a/yarn-project/noir-contracts/src/examples/test_contract.json +++ b/yarn-project/noir-contracts/src/examples/test_contract.json @@ -29,7 +29,7 @@ } ], "returnTypes": [null], - "bytecode": "ad90414ac3401885639b08aeb47a91d613684a23955625ed0586e477181c67e2cc44ea726e303369376e4da32005c10b78805ec0cb14e9c24d29dda46ff93e787cbce9d94c7f8514250f219f44394bba88523d8f2f6fae7ad6f9073b60b00b1e6e831f77908290c00ae78e972b77b25c79ae75aadf4341282578667535220c53288c75bf17ed7ae9b46a4fb48d79d56f43feec1a9e9e773993ca79b6feac2ed78758bf1c299ee9cf880b2098adbbe23b1180140ccec77cd01982940883d58b1830910a449fa530696e168d7fb9601f728b9030245e220234bdcda67ed57bca1195cd4057d7f963d6bf77fe51391628ab6250b9603fc6fc01", + "bytecode": "ad90414ac340188593360aaeb47a91d613684a23955625ed0586e477181c67e2cc44ea726e303369376e4da32005c10b78805ec0cb14e9c24d29dda46ff93e787cbce9d94c7f8514250f219f44394bba88523d8f2f6fae7ad605fe0e78b00b1e6e831f77908290c00ae78e972b77b25c79ae75aadf4341282578667535220c53288c75bf17ed7ae9b46a4fb48d79d56f43feec7c4fcfbb9c49e53c5b7f5697eb436c508e14cff467c40510ccd65df19d08400a06e7633ee80c414a84c1ea450c984805a2cf529834360bff5f2ed887dc22240c899788004d6fb369b3ea3de588ca46a0abebfc31ebdfbbe651391628ab6250b9603fc6fc01", "verificationKey": "0000000200000800000000740000000f00000003515f3109623eb3c25aa5b16a1a79fd558bac7a7ce62c4560a8c537c77ce80dd339128d1d37b6582ee9e6df9567efb64313471dfa18f520f9ce53161b50dbf7731bc5f900000003515f322bc4cce83a486a92c92fd59bd84e0f92595baa639fc2ed86b00ffa0dfded2a092a669a3bdb7a273a015eda494457cc7ed5236f26cee330c290d45a33b9daa94800000003515f332729426c008c085a81bd34d8ef12dd31e80130339ef99d50013a89e4558eee6d0fa4ffe2ee7b7b62eb92608b2251ac31396a718f9b34978888789042b790a30100000003515f342be6b6824a913eb7a57b03cb1ee7bfb4de02f2f65fe8a4e97baa7766ddb353a82a8a25c49dc63778cd9fe96173f12a2bc77f3682f4c4448f98f1df82c75234a100000003515f351f85760d6ab567465aadc2f180af9eae3800e6958fec96aef53fd8a7b195d7c000c6267a0dd5cfc22b3fe804f53e266069c0e36f51885baec1e7e67650c62e170000000c515f41524954484d455449430d9d0f8ece2aa12012fa21e6e5c859e97bd5704e5c122064a66051294bc5e04213f61f54a0ebdf6fee4d4a6ecf693478191de0c2899bcd8e86a636c8d3eff43400000003515f43224a99d02c86336737c8dd5b746c40d2be6aead8393889a76a18d664029096e90f7fe81adcc92a74350eada9622ac453f49ebac24a066a1f83b394df54dfa0130000000c515f46495845445f42415345060e8a013ed289c2f9fd7473b04f6594b138ddb4b4cf6b901622a14088f04b8d2c83ff74fce56e3d5573b99c7b26d85d5046ce0c6559506acb7a675e7713eb3a00000007515f4c4f4749430721a91cb8da4b917e054f72147e1760cfe0ef3d45090ac0f4961d84ec1996961a25e787b26bd8b50b1a99450f77a424a83513c2b33af268cd253b0587ff50c700000003515f4d05dbd8623b8652511e1eb38d38887a69eceb082f807514f09e127237c5213b401b9325b48c6c225968002318095f89d0ef9cf629b2b7f0172e03bc39aacf6ed800000007515f52414e474504b57a3805e41df328f5ca9aefa40fad5917391543b7b65c6476e60b8f72e9ad07c92f3b3e11c8feae96dedc4b14a6226ef3201244f37cfc1ee5b96781f48d2b000000075349474d415f3125001d1954a18571eaa007144c5a567bb0d2be4def08a8be918b8c05e3b27d312c59ed41e09e144eab5de77ca89a2fd783be702a47c951d3112e3de02ce6e47c000000075349474d415f3223994e6a23618e60fa01c449a7ab88378709197e186d48d604bfb6931ffb15ad11c5ec7a0700570f80088fd5198ab5d5c227f2ad2a455a6edeec024156bb7beb000000075349474d415f3300cda5845f23468a13275d18bddae27c6bb189cf9aa95b6a03a0cb6688c7e8d829639b45cf8607c525cc400b55ebf90205f2f378626dc3406cc59b2d1b474fba000000075349474d415f342d299e7928496ea2d37f10b43afd6a80c90a33b483090d18069ffa275eedb2fc2f82121e8de43dc036d99b478b6227ceef34248939987a19011f065d8b5cef5c0000000010000000000000000100000002000000030000000400000005000000060000000700000008000000090000000a0000000b0000000c0000000d0000000e0000000f" }, { @@ -56,7 +56,23 @@ "kind": "field" } ], - "bytecode": "ad91414ec2401885118a892b453c484b5b687752020613d1801728eddf3a7198d6696b703937600674e156a84643f4081e800b7819a244dd18c3a6bce57bc9cb97f7260777eccdc2b6736905c356429c868d319b75eb9da32617d2d69ab0b82edcfe2f7c3a03176804642cc4ee6229f616cb9c28edb3478b228c917fcb59da43c4c7301e71f17128679352ca5c218f46f7ece124b816f91c9b350212c522c7b3d7b2e96a102e4d7b7110b297564001f964e58d5f1d0a760c9de46b110f01e56cde051f4531d036716158f86be47fc18a9b009b5b88d8f4a68500bba7e1444a9b57898da34291a5c7c9206c7b42da999e533b4cbb102794bcb3e73a45f1c50062e47cdfa6ca554d835a051455b1e58ad9377459d3fb55433114ddd0dd8aa1aa606846cdec9b35d9543415144f3755ef87a1bc81db78f913", + "bytecode": "ad914d4ec2401cc5f9a826ae14f1202d6da1dd4909184c44035ea0b4ffd689c3b44e5b83cbb90133a00bb742351aa247f0005cc0cb1025eac61836e52ddf4b5e7e796f7270c7de2c6c3b9756306c25c469d818b359b7de396a7221e5d7845bebc2edffc2a73370814640c642ec2e96626fb1cc89d23e7bb428c218f9b79ca53d447c0ce311171f87723629a5cc15f26874cf1e4e826b91cfb1592320512c723c7b2d9bae06e1d2b41707217b690514904f56def8d5a160c7d049be16f11050cee65df05114036d13178685bf46fe174cda04d8dc42c4a6372d04d83d0d27c5b47995d8382a482c3d4e0661db13c59de939b5c3b40b7142c93b7bae53145f0c2046cef76daa5cd534a8554051155bae987d439735bd5f351443d10dddad18aa0a8666d4ccbe59934d455341f17453f57e18ca1bb88d973f01", + "verificationKey": "0000000200000800000000740000000f00000003515f3109623eb3c25aa5b16a1a79fd558bac7a7ce62c4560a8c537c77ce80dd339128d1d37b6582ee9e6df9567efb64313471dfa18f520f9ce53161b50dbf7731bc5f900000003515f322bc4cce83a486a92c92fd59bd84e0f92595baa639fc2ed86b00ffa0dfded2a092a669a3bdb7a273a015eda494457cc7ed5236f26cee330c290d45a33b9daa94800000003515f332729426c008c085a81bd34d8ef12dd31e80130339ef99d50013a89e4558eee6d0fa4ffe2ee7b7b62eb92608b2251ac31396a718f9b34978888789042b790a30100000003515f342be6b6824a913eb7a57b03cb1ee7bfb4de02f2f65fe8a4e97baa7766ddb353a82a8a25c49dc63778cd9fe96173f12a2bc77f3682f4c4448f98f1df82c75234a100000003515f351f85760d6ab567465aadc2f180af9eae3800e6958fec96aef53fd8a7b195d7c000c6267a0dd5cfc22b3fe804f53e266069c0e36f51885baec1e7e67650c62e170000000c515f41524954484d455449430d9d0f8ece2aa12012fa21e6e5c859e97bd5704e5c122064a66051294bc5e04213f61f54a0ebdf6fee4d4a6ecf693478191de0c2899bcd8e86a636c8d3eff43400000003515f43224a99d02c86336737c8dd5b746c40d2be6aead8393889a76a18d664029096e90f7fe81adcc92a74350eada9622ac453f49ebac24a066a1f83b394df54dfa0130000000c515f46495845445f42415345060e8a013ed289c2f9fd7473b04f6594b138ddb4b4cf6b901622a14088f04b8d2c83ff74fce56e3d5573b99c7b26d85d5046ce0c6559506acb7a675e7713eb3a00000007515f4c4f4749430721a91cb8da4b917e054f72147e1760cfe0ef3d45090ac0f4961d84ec1996961a25e787b26bd8b50b1a99450f77a424a83513c2b33af268cd253b0587ff50c700000003515f4d05dbd8623b8652511e1eb38d38887a69eceb082f807514f09e127237c5213b401b9325b48c6c225968002318095f89d0ef9cf629b2b7f0172e03bc39aacf6ed800000007515f52414e474504b57a3805e41df328f5ca9aefa40fad5917391543b7b65c6476e60b8f72e9ad07c92f3b3e11c8feae96dedc4b14a6226ef3201244f37cfc1ee5b96781f48d2b000000075349474d415f3125001d1954a18571eaa007144c5a567bb0d2be4def08a8be918b8c05e3b27d312c59ed41e09e144eab5de77ca89a2fd783be702a47c951d3112e3de02ce6e47c000000075349474d415f3223994e6a23618e60fa01c449a7ab88378709197e186d48d604bfb6931ffb15ad11c5ec7a0700570f80088fd5198ab5d5c227f2ad2a455a6edeec024156bb7beb000000075349474d415f3300cda5845f23468a13275d18bddae27c6bb189cf9aa95b6a03a0cb6688c7e8d829639b45cf8607c525cc400b55ebf90205f2f378626dc3406cc59b2d1b474fba000000075349474d415f342d299e7928496ea2d37f10b43afd6a80c90a33b483090d18069ffa275eedb2fc2f82121e8de43dc036d99b478b6227ceef34248939987a19011f065d8b5cef5c0000000010000000000000000100000002000000030000000400000005000000060000000700000008000000090000000a0000000b0000000c0000000d0000000e0000000f" + }, + { + "name": "getPublicKey", + "functionType": "secret", + "parameters": [ + { + "name": "address", + "type": { + "kind": "field" + }, + "visibility": "private" + } + ], + "returnTypes": [], + "bytecode": "cdd8d76ed36e18c7f1a4a5fc277befb25781b8491a87d9a41b6829297b67bc494d5da7384e69d966954decb485c221948dc4257001ef0d20f55a2a9e52ce9038c983f4b3f43bb2647d3f4e2c591e942fc73c03f697a81e4f7645b37d8d79235917d7757b3416696b6a70dc29dedf9cacf8ddc9a95efb43c4d4acce6e6169c962c171bfd5fa7d35818008550bc5afc47dd5e1841af40582891a555195a01a4c55ab7ebf50036a289c08877c6125e0174a3a18f6a77d93c7ec6fb5be120ffea83988517311a3e62146cd478c5a8018b510316a1162d462c4a82588514b11a39621462d478c5a8118558918b512316a1562d46ac4a83588516b11a3d62146ad478cda8018b511316a1362d466c4a82ac4a82d88515b11a3b62146f918a27efd4ef4ae5da48499134671cc53e9ce92e3ee6cda1cda5cda3cda7cda02da42da22da62da12da52da32da72da0a5a256d256d156d356d0d6d2d6d1d6d3d6d036d236d136d33ad8ab685b695b68de693e355ae52cd7fe3fc88bf6600312a881855831815428c5211a3c28851db11a3762046ed448cda8518b51b316a0f62542d6254e48fbf5ecd74157acdf1d302b420ad8616a2a9b4306d3b6d076d276d176d376d0fad961699783d8ad6d96fa3a6a6eb5a66c8b1df746846461793fe12db9559a5fb1d7b34629af1fe627d436361c47ed59aed75bd1e7bb42e6be42cd7e330dce1d71377d5a998bcb6c7fbbac3caf6d81f1bb3a6d032c6c4b9e2a78cb0daf3095d4bee13fd8efdbe59c47b7e54b965e58efd3926325ace12668b91127ddec9eb9497fd6c2d7f131356de34bef2ffbd9a4ac6a7f9a39a111fc4963ffc200ebb4df438d5d39a690db4165aa31cf7b87bf7f173f623dee356c4a836c4a8038851ed88510711a36288511d88518710a30e23461d418c3a8a18750c31ea3862d409c4a8938851a710a34e23469d418c3a8b18750e312a8e1895408c4a2246a510a30462541a312a8318d58918a57147b90c5f33bd0cb0f388b03206581722ac9c01a623c2a630c0ba1161150c3003113695019645844519603d88b07a06d80544580303cc4484ed6780e51061ad0c300b11d6c600cb23c20e30c07a1161ed0cb08b88b0830cb03e44588c01d68f08eb60805d42841d62805d46841d66805d41841d61805d45841d65805d43841d63805d47841d6780c91b88b2131c321b51769243761351768a43760b51769a43761b51768643760751769643761751768e433680288b73c8ee21ca121cb2fb88b22487ec01a22cc5217b8828131cb24788b23487ec31a22cc3217b8228ebe4903d4594691cb202a2ec2f0e998328fb9b43e622cafee190151165ff72c8061165ff71c8861065ff73c8861165d33864cf1065d33964cf11653338642388b2991cb217a5cb0a631ee57c97de6d647b2e98392bdf7bb1afffd2e52b57af5d9737a42d6fca5bf2b6bc23efca01794fde970fe443f9483e964fe45359908e7465510eca21392c9fc9e77244bef80e", "verificationKey": "0000000200000800000000740000000f00000003515f3109623eb3c25aa5b16a1a79fd558bac7a7ce62c4560a8c537c77ce80dd339128d1d37b6582ee9e6df9567efb64313471dfa18f520f9ce53161b50dbf7731bc5f900000003515f322bc4cce83a486a92c92fd59bd84e0f92595baa639fc2ed86b00ffa0dfded2a092a669a3bdb7a273a015eda494457cc7ed5236f26cee330c290d45a33b9daa94800000003515f332729426c008c085a81bd34d8ef12dd31e80130339ef99d50013a89e4558eee6d0fa4ffe2ee7b7b62eb92608b2251ac31396a718f9b34978888789042b790a30100000003515f342be6b6824a913eb7a57b03cb1ee7bfb4de02f2f65fe8a4e97baa7766ddb353a82a8a25c49dc63778cd9fe96173f12a2bc77f3682f4c4448f98f1df82c75234a100000003515f351f85760d6ab567465aadc2f180af9eae3800e6958fec96aef53fd8a7b195d7c000c6267a0dd5cfc22b3fe804f53e266069c0e36f51885baec1e7e67650c62e170000000c515f41524954484d455449430d9d0f8ece2aa12012fa21e6e5c859e97bd5704e5c122064a66051294bc5e04213f61f54a0ebdf6fee4d4a6ecf693478191de0c2899bcd8e86a636c8d3eff43400000003515f43224a99d02c86336737c8dd5b746c40d2be6aead8393889a76a18d664029096e90f7fe81adcc92a74350eada9622ac453f49ebac24a066a1f83b394df54dfa0130000000c515f46495845445f42415345060e8a013ed289c2f9fd7473b04f6594b138ddb4b4cf6b901622a14088f04b8d2c83ff74fce56e3d5573b99c7b26d85d5046ce0c6559506acb7a675e7713eb3a00000007515f4c4f4749430721a91cb8da4b917e054f72147e1760cfe0ef3d45090ac0f4961d84ec1996961a25e787b26bd8b50b1a99450f77a424a83513c2b33af268cd253b0587ff50c700000003515f4d05dbd8623b8652511e1eb38d38887a69eceb082f807514f09e127237c5213b401b9325b48c6c225968002318095f89d0ef9cf629b2b7f0172e03bc39aacf6ed800000007515f52414e474504b57a3805e41df328f5ca9aefa40fad5917391543b7b65c6476e60b8f72e9ad07c92f3b3e11c8feae96dedc4b14a6226ef3201244f37cfc1ee5b96781f48d2b000000075349474d415f3125001d1954a18571eaa007144c5a567bb0d2be4def08a8be918b8c05e3b27d312c59ed41e09e144eab5de77ca89a2fd783be702a47c951d3112e3de02ce6e47c000000075349474d415f3223994e6a23618e60fa01c449a7ab88378709197e186d48d604bfb6931ffb15ad11c5ec7a0700570f80088fd5198ab5d5c227f2ad2a455a6edeec024156bb7beb000000075349474d415f3300cda5845f23468a13275d18bddae27c6bb189cf9aa95b6a03a0cb6688c7e8d829639b45cf8607c525cc400b55ebf90205f2f378626dc3406cc59b2d1b474fba000000075349474d415f342d299e7928496ea2d37f10b43afd6a80c90a33b483090d18069ffa275eedb2fc2f82121e8de43dc036d99b478b6227ceef34248939987a19011f065d8b5cef5c0000000010000000000000000100000002000000030000000400000005000000060000000700000008000000090000000a0000000b0000000c0000000d0000000e0000000f" } ] diff --git a/yarn-project/noir-contracts/src/libs/noir-aztec/src/generators.nr b/yarn-project/noir-contracts/src/libs/noir-aztec/src/generators.nr index 5a3093c950d..bd4bfb75372 100644 --- a/yarn-project/noir-contracts/src/libs/noir-aztec/src/generators.nr +++ b/yarn-project/noir-contracts/src/libs/noir-aztec/src/generators.nr @@ -5,4 +5,5 @@ global MAPPING_SLORAGE_SLOT = 4; global Nullifier = 5; global SINGLETON_INITIALISATION = 6; global MessageSecret = 20; -global FUNCTION_ARGS = 44; \ No newline at end of file +global FUNCTION_ARGS = 44; +global ContractAddress = 15; \ No newline at end of file diff --git a/yarn-project/noir-contracts/src/libs/noir-aztec/src/oracle.nr b/yarn-project/noir-contracts/src/libs/noir-aztec/src/oracle.nr index 515f4c79768..b99008a6c1f 100644 --- a/yarn-project/noir-contracts/src/libs/noir-aztec/src/oracle.nr +++ b/yarn-project/noir-contracts/src/libs/noir-aztec/src/oracle.nr @@ -6,6 +6,7 @@ mod create_nullifier; mod debug_log; mod get_commitment; mod get_l1_to_l2_message; +mod get_public_key; mod get_secret_key; mod rand; mod enqueue_public_function_call; diff --git a/yarn-project/noir-contracts/src/libs/noir-aztec/src/oracle/get_public_key.nr b/yarn-project/noir-contracts/src/libs/noir-aztec/src/oracle/get_public_key.nr new file mode 100644 index 00000000000..cbe6416adc7 --- /dev/null +++ b/yarn-project/noir-contracts/src/libs/noir-aztec/src/oracle/get_public_key.nr @@ -0,0 +1,22 @@ +use crate::types::point::Point; +use crate::generators; + +#[oracle(getPublicKey)] +fn get_public_key_oracle(_address: Field) -> [Field; 3] {} + +unconstrained fn get_public_key_internal(address: Field) -> [Field; 3] { + get_public_key_oracle(address) +} + +fn get_public_key(address: Field) -> Point { + let result = get_public_key_internal(address); + let pub_key_x = result[0]; + let pub_key_y = result[1]; + let partial_address = result[2]; + + // TODO: Remove zeroes when each point coordinate is converted to a single field + let calculated_address = dep::std::hash::pedersen([generators::ContractAddress, pub_key_x, 0, pub_key_y, 0, partial_address])[0]; + // assert(calculated_address == address); + + Point::new(pub_key_x, pub_key_y) +} \ No newline at end of file