From 15ce01dcc163fbef71d6eec8090bff9371af45bd Mon Sep 17 00:00:00 2001 From: Santiago Palladino Date: Mon, 5 Feb 2024 19:15:44 -0300 Subject: [PATCH] feat: Instance deployer contract Implementation for the initial contract instance deployer contract. Skips validations for class id and eth address for now. Pending to listen to these events on the node. Builds on #4429 Fixes #4071 --- .../src/core/libraries/ConstantsGen.sol | 2 + yarn-project/circuits.js/src/constants.gen.ts | 2 + .../src/e2e_deploy_contract.test.ts | 29 ++++++++- yarn-project/noir-contracts/Nargo.toml | 1 + .../Nargo.toml | 8 +++ .../src/events.nr | 1 + .../src/events/instance_deployed.nr | 36 +++++++++++ .../src/main.nr | 63 +++++++++++++++++++ .../src/crates/types/src/address.nr | 12 ++++ .../src/crates/types/src/constants.nr | 4 ++ 10 files changed, 156 insertions(+), 2 deletions(-) create mode 100644 yarn-project/noir-contracts/contracts/contract_instance_deployer_contract/Nargo.toml create mode 100644 yarn-project/noir-contracts/contracts/contract_instance_deployer_contract/src/events.nr create mode 100644 yarn-project/noir-contracts/contracts/contract_instance_deployer_contract/src/events/instance_deployed.nr create mode 100644 yarn-project/noir-contracts/contracts/contract_instance_deployer_contract/src/main.nr diff --git a/l1-contracts/src/core/libraries/ConstantsGen.sol b/l1-contracts/src/core/libraries/ConstantsGen.sol index 67e3d526332..9cf2512b141 100644 --- a/l1-contracts/src/core/libraries/ConstantsGen.sol +++ b/l1-contracts/src/core/libraries/ConstantsGen.sol @@ -76,6 +76,8 @@ library Constants { 0x1b70e95fde0b70adc30496b90a327af6a5e383e028e7a43211a07bcd; uint256 internal constant REGISTERER_UNCONSTRAINED_FUNCTION_BROADCASTED_MAGIC_VALUE = 0xe7af816635466f128568edb04c9fa024f6c87fb9010fdbffa68b3d99; + uint256 internal constant DEPLOYER_CONTRACT_INSTANCE_DEPLOYED_MAGIC_VALUE = + 0x85864497636cf755ae7bde03f267ce01a520981c21c3682aaf82a631; uint256 internal constant L1_TO_L2_MESSAGE_LENGTH = 8; uint256 internal constant L1_TO_L2_MESSAGE_ORACLE_CALL_LENGTH = 25; uint256 internal constant MAX_NOTE_FIELDS_LENGTH = 20; diff --git a/yarn-project/circuits.js/src/constants.gen.ts b/yarn-project/circuits.js/src/constants.gen.ts index 56dd6070b8c..c00ab8a57f1 100644 --- a/yarn-project/circuits.js/src/constants.gen.ts +++ b/yarn-project/circuits.js/src/constants.gen.ts @@ -62,6 +62,8 @@ export const REGISTERER_PRIVATE_FUNCTION_BROADCASTED_MAGIC_VALUE = 0x1b70e95fde0b70adc30496b90a327af6a5e383e028e7a43211a07bcdn; export const REGISTERER_UNCONSTRAINED_FUNCTION_BROADCASTED_MAGIC_VALUE = 0xe7af816635466f128568edb04c9fa024f6c87fb9010fdbffa68b3d99n; +export const DEPLOYER_CONTRACT_INSTANCE_DEPLOYED_MAGIC_VALUE = + 0x85864497636cf755ae7bde03f267ce01a520981c21c3682aaf82a631n; export const L1_TO_L2_MESSAGE_LENGTH = 8; export const L1_TO_L2_MESSAGE_ORACLE_CALL_LENGTH = 25; export const MAX_NOTE_FIELDS_LENGTH = 20; diff --git a/yarn-project/end-to-end/src/e2e_deploy_contract.test.ts b/yarn-project/end-to-end/src/e2e_deploy_contract.test.ts index cddb33e1dec..cbe2aeab39d 100644 --- a/yarn-project/end-to-end/src/e2e_deploy_contract.test.ts +++ b/yarn-project/end-to-end/src/e2e_deploy_contract.test.ts @@ -24,6 +24,7 @@ import { ARTIFACT_FUNCTION_TREE_MAX_HEIGHT, MAX_PACKED_BYTECODE_SIZE_PER_PRIVATE_FUNCTION_IN_FIELDS, MAX_PACKED_PUBLIC_BYTECODE_SIZE_IN_FIELDS, + Point, computeArtifactFunctionTree, computeArtifactFunctionTreeRoot, computeArtifactMetadataHash, @@ -31,11 +32,17 @@ import { computePrivateFunctionsRoot, computePrivateFunctionsTree, computePublicBytecodeCommitment, + computePublicKeysHash, } from '@aztec/circuits.js'; import { siloNullifier } from '@aztec/circuits.js/abis'; import { FunctionSelector, FunctionType, bufferAsFields } from '@aztec/foundation/abi'; import { padArrayEnd } from '@aztec/foundation/collection'; -import { ContractClassRegistererContract, ReaderContractArtifact, StatefulTestContract } from '@aztec/noir-contracts'; +import { + ContractClassRegistererContract, + ContractInstanceDeployerContract, + ReaderContractArtifact, + StatefulTestContract, +} from '@aztec/noir-contracts'; import { TestContract, TestContractArtifact } from '@aztec/noir-contracts/Test'; import { TokenContractArtifact } from '@aztec/noir-contracts/Token'; import { SequencerClient } from '@aztec/sequencer-client'; @@ -263,7 +270,7 @@ describe('e2e_deploy_contract', () => { // Tests registering a new contract class on a node // All this dance will be hidden behind a nicer API in the near future! - describe('registering a new contract class', () => { + describe('public registration and deployment', () => { let registerer: ContractClassRegistererContract; let artifact: ContractArtifact; let contractClass: ContractClassWithId; @@ -376,6 +383,24 @@ describe('e2e_deploy_contract', () => { .send() .wait(); }, 60_000); + + it('deploys a new instance of the registered class via the deployer', async () => { + const deployer = await registerContract(wallet, ContractInstanceDeployerContract, [], new Fr(1)); + + const salt = Fr.random(); + const initArgs = Fr.ZERO; + const portalAddress = EthAddress.random(); + const publicKeysHash = computePublicKeysHash(Point.random()); + + const tx = await deployer.methods + .deploy(salt, contractClass.id, initArgs, portalAddress, publicKeysHash, false) + .send() + .wait(); + + const logs = await pxe.getUnencryptedLogs({ txHash: tx.txHash }); + const deployedLog = logs.logs[0].log; + expect(deployedLog.contractAddress).toEqual(deployer.address); + }); }); }); diff --git a/yarn-project/noir-contracts/Nargo.toml b/yarn-project/noir-contracts/Nargo.toml index d5db8034622..d55caac75d5 100644 --- a/yarn-project/noir-contracts/Nargo.toml +++ b/yarn-project/noir-contracts/Nargo.toml @@ -5,6 +5,7 @@ members = [ "contracts/card_game_contract", "contracts/child_contract", "contracts/contract_class_registerer_contract", + "contracts/contract_instance_deployer_contract", "contracts/counter_contract", "contracts/docs_example_contract", "contracts/easy_private_token_contract", diff --git a/yarn-project/noir-contracts/contracts/contract_instance_deployer_contract/Nargo.toml b/yarn-project/noir-contracts/contracts/contract_instance_deployer_contract/Nargo.toml new file mode 100644 index 00000000000..aeca13c16a4 --- /dev/null +++ b/yarn-project/noir-contracts/contracts/contract_instance_deployer_contract/Nargo.toml @@ -0,0 +1,8 @@ +[package] +name = "contract_instance_deployer_contract" +authors = [""] +compiler_version = ">=0.18.0" +type = "contract" + +[dependencies] +aztec = { path = "../../../aztec-nr/aztec" } diff --git a/yarn-project/noir-contracts/contracts/contract_instance_deployer_contract/src/events.nr b/yarn-project/noir-contracts/contracts/contract_instance_deployer_contract/src/events.nr new file mode 100644 index 00000000000..d2b6ed6033f --- /dev/null +++ b/yarn-project/noir-contracts/contracts/contract_instance_deployer_contract/src/events.nr @@ -0,0 +1 @@ +mod instance_deployed; diff --git a/yarn-project/noir-contracts/contracts/contract_instance_deployer_contract/src/events/instance_deployed.nr b/yarn-project/noir-contracts/contracts/contract_instance_deployer_contract/src/events/instance_deployed.nr new file mode 100644 index 00000000000..932973d45b1 --- /dev/null +++ b/yarn-project/noir-contracts/contracts/contract_instance_deployer_contract/src/events/instance_deployed.nr @@ -0,0 +1,36 @@ +use dep::aztec::protocol_types::{ + contract_class::ContractClassId, + address::{ AztecAddress, EthAddress, PublicKeysHash, PartialAddress }, + constants::{DEPLOYER_CONTRACT_INSTANCE_DEPLOYED_MAGIC_VALUE}, + traits::{Serialize} +}; + +// #[event] +struct ContractInstanceDeployed { + address: AztecAddress, + version: u8, + salt: Field, + contract_class_id: ContractClassId, + initialization_hash: Field, + portal_contract_address: EthAddress, + public_keys_hash: PublicKeysHash, + universal_deploy: bool, +} + +global CONTRACT_INSTANCE_DEPLOYED_SERIALIZED_SIZE: Field = 9; + +impl Serialize for ContractInstanceDeployed { + fn serialize(self: Self) -> [Field; CONTRACT_INSTANCE_DEPLOYED_SERIALIZED_SIZE] { + [ + DEPLOYER_CONTRACT_INSTANCE_DEPLOYED_MAGIC_VALUE, + self.address.to_field(), + self.version as Field, + self.salt, + self.contract_class_id.to_field(), + self.initialization_hash, + self.portal_contract_address.to_field(), + self.public_keys_hash.to_field(), + self.universal_deploy as Field, + ] + } +} diff --git a/yarn-project/noir-contracts/contracts/contract_instance_deployer_contract/src/main.nr b/yarn-project/noir-contracts/contracts/contract_instance_deployer_contract/src/main.nr new file mode 100644 index 00000000000..679085f994c --- /dev/null +++ b/yarn-project/noir-contracts/contracts/contract_instance_deployer_contract/src/main.nr @@ -0,0 +1,63 @@ +mod events; + +contract ContractInstanceDeployer { + use dep::std::option::Option; + use dep::aztec::protocol_types::{ + address::{ AztecAddress, EthAddress, PublicKeysHash, PartialAddress }, + contract_class::ContractClassId, + constants::{DEPLOYER_CONTRACT_INSTANCE_DEPLOYED_MAGIC_VALUE}, + traits::{Serialize} + }; + + use dep::aztec::log::{ emit_unencrypted_log, emit_unencrypted_log_from_private}; + + use crate::events::{ + instance_deployed::ContractInstanceDeployed, + }; + + #[aztec(private)] + fn constructor() {} + + #[aztec(private)] + fn deploy( + salt: Field, + contract_class_id: ContractClassId, + initialization_hash: Field, + portal_contract_address: EthAddress, + public_keys_hash: PublicKeysHash, + universal_deploy: bool + ) { + // TODO(@spalladino): assert nullifier_exists silo(contract_class_id, ContractClassRegisterer) + // TODO(@spalladino): assert is_valid_eth_address(portal_contract_address) + + // TODO(#4434) Add deployer field to instance calculation + // let deployer = if universal_deploy { Field::zero() } else { context.msg_sender() }; + + let partial_address = PartialAddress::compute( + contract_class_id, + salt, + initialization_hash, + portal_contract_address + ); + + let address = AztecAddress::compute(public_keys_hash, partial_address); + + // Emit the address as a nullifier to be able to prove that this instance has been (not) deployed + context.push_new_nullifier(address.to_field(), 0); + + // Broadcast the event + let event = ContractInstanceDeployed { + contract_class_id, + address, + public_keys_hash, + portal_contract_address, + initialization_hash, + salt, + universal_deploy, + version: 1 + }; + let event_payload = event.serialize(); + dep::aztec::oracle::debug_log::debug_log_array_with_prefix("ContractInstanceDeployed", event_payload); + emit_unencrypted_log_from_private(&mut context, event_payload); + } +} diff --git a/yarn-project/noir-protocol-circuits/src/crates/types/src/address.nr b/yarn-project/noir-protocol-circuits/src/crates/types/src/address.nr index 33e56273756..d4dc79fd9bf 100644 --- a/yarn-project/noir-protocol-circuits/src/crates/types/src/address.nr +++ b/yarn-project/noir-protocol-circuits/src/crates/types/src/address.nr @@ -248,6 +248,18 @@ impl ToField for PublicKeysHash { } } +impl Serialize<1> for PublicKeysHash { + fn serialize(self: Self) -> [Field; 1] { + [self.to_field()] + } +} + +impl Deserialize<1> for PublicKeysHash { + fn deserialize(fields: [Field; 1]) -> Self { + PublicKeysHash::from_field(fields[0]) + } +} + impl PublicKeysHash { pub fn from_field(field : Field) -> Self { Self { diff --git a/yarn-project/noir-protocol-circuits/src/crates/types/src/constants.nr b/yarn-project/noir-protocol-circuits/src/crates/types/src/constants.nr index 406b0842f5c..893a3a596fc 100644 --- a/yarn-project/noir-protocol-circuits/src/crates/types/src/constants.nr +++ b/yarn-project/noir-protocol-circuits/src/crates/types/src/constants.nr @@ -108,6 +108,10 @@ global REGISTERER_PRIVATE_FUNCTION_BROADCASTED_MAGIC_VALUE = 0x1b70e95fde0b70adc // sha224sum 'struct ClassUnconstrainedFunctionBroadcasted' global REGISTERER_UNCONSTRAINED_FUNCTION_BROADCASTED_MAGIC_VALUE = 0xe7af816635466f128568edb04c9fa024f6c87fb9010fdbffa68b3d99; +// CONTRACT INSTANCE CONSTANTS +// sha224sum 'struct ContractInstanceDeployed' +global DEPLOYER_CONTRACT_INSTANCE_DEPLOYED_MAGIC_VALUE = 0x85864497636cf755ae7bde03f267ce01a520981c21c3682aaf82a631; + // NOIR CONSTANTS - constants used only in yarn-packages/noir-contracts // Some are defined here because Noir doesn't yet support globals referencing other globals yet. // Move these constants to a noir file once the issue below is resolved: