Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat!: Use new deployment flow in ContractDeployer #4497

Merged
merged 38 commits into from
Feb 27, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
38 commits
Select commit Hold shift + click to select a range
fe61ecd
feat: Use new deployment flow in ContractDeployer
spalladino Feb 7, 2024
82ceae3
Update max bytecode size to 5k fields
spalladino Feb 14, 2024
36016d5
Skip max args check in args hasher on ts
spalladino Feb 14, 2024
f420a89
Add contract class info on inspect-contract cli
spalladino Feb 14, 2024
92cb2d3
Fall back to getContractInstance to check if contract is deployed
spalladino Feb 14, 2024
74f6cc5
Remove contractAddress from tx receipt
spalladino Feb 14, 2024
9d83bd7
Compress public bytecode
spalladino Feb 14, 2024
e781c68
Revert "Compress public bytecode"
spalladino Feb 14, 2024
01279bd
Slice max args during hashing
spalladino Feb 15, 2024
ff88f5b
Fix snapshot
spalladino Feb 15, 2024
1068d9b
Changes to circuits testing exports and deploy method
spalladino Feb 15, 2024
cb7d034
Add contracts deployed on each tx to the public executor cache
spalladino Feb 15, 2024
1a6a90c
Create fake ContractData in node archiver
spalladino Feb 16, 2024
93677a5
List internal fns in inspect-contract
spalladino Feb 16, 2024
67e72e2
Fix e2e test
spalladino Feb 16, 2024
fa94327
Add logging to public execution contract data source
spalladino Feb 16, 2024
5b63eb5
Increase e2e test timeout
spalladino Feb 16, 2024
ccf4015
Update yarn-project to try and trigger a rebuild
spalladino Feb 16, 2024
19878a1
Less noisy class registerer debug stmts
spalladino Feb 16, 2024
a1360bb
Add ad-hoc method to pxe to check for public class registrations
spalladino Feb 16, 2024
efe68bd
Use capsules for registering class bytecode
spalladino Feb 16, 2024
5cd598b
Increase e2e tests timeout
spalladino Feb 16, 2024
c25c93a
Fix import post rebase
spalladino Feb 16, 2024
3063528
Bump max bytecode size to 8k fields
spalladino Feb 16, 2024
fc1388f
Try to catch errors for not pushing contract classes
spalladino Feb 16, 2024
95e9a09
Moar logging
spalladino Feb 16, 2024
ccd3cbe
Run yarn format
spalladino Feb 16, 2024
52fb66f
Add method for debugging existing class ids
spalladino Feb 17, 2024
173f0b0
Add TODO to review test
spalladino Feb 17, 2024
d43fa13
Fix the goddamn bug
spalladino Feb 17, 2024
b6b3839
Fix boxes
spalladino Feb 17, 2024
f38bf75
Fix CLI tests
spalladino Feb 17, 2024
0c6cbbf
Fix cli_docs_sandbox test
spalladino Feb 17, 2024
94a81e6
Format
spalladino Feb 23, 2024
a8123c2
Update registerer snapshot
spalladino Feb 23, 2024
4d5c124
Fix boxes (again)
spalladino Feb 23, 2024
26f3b60
Address feedback from @PhilWindle
spalladino Feb 27, 2024
73b70e5
Merge branch 'master' into palla/contract-deploy-12
PhilWindle Feb 27, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions boxes/react/src/hooks/useContract.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,17 +14,17 @@ export function useContract() {
e.preventDefault();

setWait(true);
const contractDeployer = new ContractDeployer(artifact, deployerEnv.pxe);
const wallet = await deployerEnv.getWallet();
const contractDeployer = new ContractDeployer(artifact, wallet);

const salt = Fr.random();
const tx = contractDeployer
.deploy(Fr.random(), wallet.getCompleteAddress().address)
.send({ contractAddressSalt: salt });
const { contractAddress } = await toast.promise(tx.wait(), {
const { address: contractAddress } = await toast.promise(tx.deployed(), {
pending: 'Deploying contract...',
success: {
render: ({ data }) => `Address: ${data.contractAddress}`,
render: ({ data }) => `Address: ${data.address}`,
},
error: 'Error deploying contract',
});
Expand Down
12 changes: 5 additions & 7 deletions boxes/react/src/index.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,9 @@
import { Home } from "./pages/home";
import "react-toastify/dist/ReactToastify.css";
import * as ReactDOM from "react-dom/client";
import { ToastContainer } from "react-toastify";
import { Home } from './pages/home';
import 'react-toastify/dist/ReactToastify.css';
import * as ReactDOM from 'react-dom/client';
import { ToastContainer } from 'react-toastify';

const root = ReactDOM.createRoot(
document.getElementById("root") as HTMLElement,
);
const root = ReactDOM.createRoot(document.getElementById('root') as HTMLElement);
root.render(
<>
<Home />
Expand Down
25 changes: 8 additions & 17 deletions boxes/react/src/pages/contract.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { useState } from "react";
import { Contract } from "@aztec/aztec.js";
import { useNumber } from "../hooks/useNumber";
import { filteredInterface } from "../config";
import { useState } from 'react';
import { Contract } from '@aztec/aztec.js';
import { useNumber } from '../hooks/useNumber';
import { filteredInterface } from '../config';

export function ContractComponent({ contract }: { contract: Contract }) {
const [showInput, setShowInput] = useState(true);
Expand All @@ -15,7 +15,7 @@ export function ContractComponent({ contract }: { contract: Contract }) {
<select name="viewFunctions" id="viewFunctions">
{filteredInterface.map(
(fn, index) =>
fn.functionType === "unconstrained" && (
fn.functionType === 'unconstrained' && (
<option key={index} value={index}>
{fn.name}
</option>
Expand All @@ -29,26 +29,17 @@ export function ContractComponent({ contract }: { contract: Contract }) {

<form onSubmit={setNumber}>
<label htmlFor="functions">Functions:</label>
<select
name="functions"
id="functions"
onChange={() => setShowInput(true)}
>
<select name="functions" id="functions" onChange={() => setShowInput(true)}>
{filteredInterface.map(
(fn, index) =>
fn.functionType !== "unconstrained" && (
fn.functionType !== 'unconstrained' && (
<option key={index} value={index}>
{fn.name}
</option>
),
)}
</select>
<input
type="number"
name="numberToSet"
id="numberToSet"
hidden={!showInput}
/>
<input type="number" name="numberToSet" id="numberToSet" hidden={!showInput} />
<button type="submit" disabled={wait}>
Write
</button>
Expand Down
4 changes: 2 additions & 2 deletions boxes/react/src/pages/home.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { ContractComponent } from "./contract";
import { useContract } from "../hooks/useContract";
import { ContractComponent } from './contract';
import { useContract } from '../hooks/useContract';

export function Home() {
const { contract, deploy, wait } = useContract();
Expand Down
6 changes: 2 additions & 4 deletions boxes/react/tests/blank.contract.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,9 @@ describe('BoxReact Contract Tests', () => {
beforeAll(async () => {
wallet = await deployerEnv.getWallet();
const pxe = deployerEnv.pxe;
const deployer = new ContractDeployer(artifact, pxe);
const deployer = new ContractDeployer(artifact, wallet);
const salt = Fr.random();
const tx = deployer.deploy(Fr.random(), wallet.getCompleteAddress().address).send({ contractAddressSalt: salt });
await tx.wait();
const { contractAddress } = await tx.getReceipt();
const { address: contractAddress } = await deployer.deploy(Fr.random(), wallet.getCompleteAddress().address).send({ contractAddressSalt: salt }).deployed();
contract = await BoxReactContract.at(contractAddress!, wallet);

logger(`L2 contract deployed at ${contractAddress}`);
Expand Down
6 changes: 3 additions & 3 deletions boxes/vanilla-js/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,11 +27,11 @@ document.querySelector('#deploy').addEventListener('click', async ({ target }: a
wallet = await account.register();

const { artifact, at } = VanillaContract;
const contractDeployer = new ContractDeployer(artifact, pxe);
const { contractAddress } = await contractDeployer
const contractDeployer = new ContractDeployer(artifact, wallet);
const { address: contractAddress } = await contractDeployer
.deploy(Fr.random(), wallet.getCompleteAddress().address)
.send({ contractAddressSalt: Fr.random() })
.wait();
.deployed();
contract = await at(contractAddress, wallet);
alert(`Contract deployed at ${contractAddress}`);

Expand Down
2 changes: 1 addition & 1 deletion l1-contracts/src/core/libraries/ConstantsGen.sol
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ library Constants {
uint256 internal constant NUM_FIELDS_PER_SHA256 = 2;
uint256 internal constant ARGS_HASH_CHUNK_LENGTH = 32;
uint256 internal constant ARGS_HASH_CHUNK_COUNT = 32;
uint256 internal constant MAX_PACKED_PUBLIC_BYTECODE_SIZE_IN_FIELDS = 1000;
uint256 internal constant MAX_PACKED_PUBLIC_BYTECODE_SIZE_IN_FIELDS = 8000;
uint256 internal constant MAX_PACKED_BYTECODE_SIZE_PER_PRIVATE_FUNCTION_IN_FIELDS = 500;
uint256 internal constant MAX_PACKED_BYTECODE_SIZE_PER_UNCONSTRAINED_FUNCTION_IN_FIELDS = 500;
uint256 internal constant REGISTERER_CONTRACT_CLASS_REGISTERED_MAGIC_VALUE =
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
// Copied from noir-contracts/contracts/slow_tree_contract/src/capsule.nr
// We should extract this to a shared lib in aztec-nr once we settle on a design for capsules

#[oracle(popCapsule)]
fn pop_capsule_oracle<N>() -> [Field; N] {}

// A capsule is a "blob" of data that is passed to the contract through an oracle.
unconstrained pub fn pop_capsule<N>() -> [Field; N] {
pop_capsule_oracle()
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
mod events;
mod capsule;

contract ContractClassRegisterer {
use dep::std::option::Option;
Expand All @@ -19,19 +20,18 @@ contract ContractClassRegisterer {
unconstrained_function_broadcasted::{ClassUnconstrainedFunctionBroadcasted, UnconstrainedFunction}
};

use crate::capsule::pop_capsule;

#[aztec(private)]
fn constructor() {}

#[aztec(private)]
fn register(
artifact_hash: Field,
private_functions_root: Field,
public_bytecode_commitment: Field,
packed_public_bytecode: [Field; MAX_PACKED_PUBLIC_BYTECODE_SIZE_IN_FIELDS]
) {
fn register(artifact_hash: Field, private_functions_root: Field, public_bytecode_commitment: Field) {
// TODO: Validate public_bytecode_commitment is the correct commitment of packed_public_bytecode
// TODO: Validate packed_public_bytecode is legit public bytecode

let packed_public_bytecode: [Field; MAX_PACKED_PUBLIC_BYTECODE_SIZE_IN_FIELDS] = pop_capsule();

// Compute contract class id from preimage
let contract_class_id = ContractClassId::compute(
artifact_hash,
Expand All @@ -44,9 +44,16 @@ contract ContractClassRegisterer {
context.push_new_nullifier(contract_class_id.to_field(), 0);

// Broadcast class info including public bytecode
let event_payload = event.serialize();
dep::aztec::oracle::debug_log::debug_log_array_with_prefix("ContractClassRegistered", event_payload);
emit_unencrypted_log_from_private(&mut context, event_payload);
dep::aztec::oracle::debug_log::debug_log_array_with_prefix(
"ContractClassRegistered",
[
contract_class_id.to_field(),
artifact_hash,
private_functions_root,
public_bytecode_commitment
]
);
emit_unencrypted_log_from_private(&mut context, event.serialize());
}

#[aztec(private)]
Expand All @@ -66,9 +73,18 @@ contract ContractClassRegisterer {
artifact_function_tree_sibling_path,
function: function_data
};
let event_payload = event.serialize();
dep::aztec::oracle::debug_log::debug_log_array_with_prefix("ClassPrivateFunctionBroadcasted", event_payload);
emit_unencrypted_log_from_private(&mut context, event_payload);
dep::aztec::oracle::debug_log::debug_log_array_with_prefix(
"ClassPrivateFunctionBroadcasted",
[
contract_class_id.to_field(),
artifact_metadata_hash,
unconstrained_functions_artifact_tree_root,
function_data.selector.to_field(),
function_data.vk_hash,
function_data.metadata_hash
]
);
emit_unencrypted_log_from_private(&mut context, event.serialize());
}

#[aztec(private)]
Expand All @@ -86,8 +102,16 @@ contract ContractClassRegisterer {
artifact_function_tree_sibling_path,
function: function_data
};
let event_payload = event.serialize();
dep::aztec::oracle::debug_log::debug_log_array_with_prefix("ClassUnconstrainedFunctionBroadcasted", event_payload);
emit_unencrypted_log_from_private(&mut context, event_payload);
dep::aztec::oracle::debug_log::debug_log_array_with_prefix(
"ClassUnconstrainedFunctionBroadcasted",
[
contract_class_id.to_field(),
artifact_metadata_hash,
private_functions_artifact_tree_root,
function_data.selector.to_field(),
function_data.metadata_hash
]
);
emit_unencrypted_log_from_private(&mut context, event.serialize());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -104,11 +104,7 @@ global ARGS_HASH_CHUNK_COUNT: u32 = 32;
global INITIALIZATION_SLOT_SEPARATOR: Field = 1000_000_000;

// CONTRACT CLASS CONSTANTS
// This should be around 8192 (assuming 2**15 instructions packed at 8 bytes each),
// but it's reduced to speed up build times, otherwise the ClassRegisterer takes over 5 mins to compile.
// We are not using 1024 so we can squeeze in a few more args to methods that consume packed public bytecode,
// such as the ClassRegisterer.register, and still land below the 32 * 32 max args limit for hashing.
global MAX_PACKED_PUBLIC_BYTECODE_SIZE_IN_FIELDS: Field = 1000;
global MAX_PACKED_PUBLIC_BYTECODE_SIZE_IN_FIELDS: Field = 8000;
// Bytecode size for private functions is per function, not for the entire contract.
// Note that private functions bytecode includes a mix of acir and brillig.
global MAX_PACKED_BYTECODE_SIZE_PER_PRIVATE_FUNCTION_IN_FIELDS: Field = 500;
Expand Down
81 changes: 28 additions & 53 deletions yarn-project/archiver/src/archiver/archiver.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,10 @@ import {
ContractClassRegisteredEvent,
FunctionSelector,
NUMBER_OF_L1_L2_MESSAGES_PER_ROLLUP,
REGISTERER_CONTRACT_CLASS_REGISTERED_MAGIC_VALUE,
} from '@aztec/circuits.js';
import { ContractInstanceDeployedEvent, computeSaltedInitializationHash } from '@aztec/circuits.js/contract';
import { ContractInstanceDeployedEvent } from '@aztec/circuits.js/contract';
import { createEthereumChain } from '@aztec/ethereum';
import { AztecAddress } from '@aztec/foundation/aztec-address';
import { toBigIntBE } from '@aztec/foundation/bigint-buffer';
import { padArrayEnd } from '@aztec/foundation/collection';
import { EthAddress } from '@aztec/foundation/eth-address';
import { Fr } from '@aztec/foundation/fields';
Expand Down Expand Up @@ -72,12 +70,6 @@ export class Archiver implements ArchiveSource {
*/
private lastLoggedL1BlockNumber = 0n;

/** Address of the ClassRegisterer contract with a salt=1 */
private classRegistererAddress = ClassRegistererAddress;

/** Address of the InstanceDeployer contract with a salt=1 */
private instanceDeployerAddress = InstanceDeployerAddress;

/**
* Creates a new instance of the Archiver.
* @param publicClient - A client for interacting with the Ethereum node.
Expand Down Expand Up @@ -342,22 +334,9 @@ export class Archiver implements ArchiveSource {
* @param allLogs - All logs emitted in a bunch of blocks.
*/
private async storeRegisteredContractClasses(allLogs: UnencryptedL2Log[], blockNum: number) {
const contractClasses: ContractClassPublic[] = [];
for (const log of allLogs) {
try {
if (
!log.contractAddress.equals(this.classRegistererAddress) ||
toBigIntBE(log.data.subarray(0, 32)) !== REGISTERER_CONTRACT_CLASS_REGISTERED_MAGIC_VALUE
) {
continue;
}
const event = ContractClassRegisteredEvent.fromLogData(log.data);
contractClasses.push(event.toContractClassPublic());
} catch (err) {
this.log.warn(`Error processing log ${log.toHumanReadable()}: ${err}`);
}
}

const contractClasses = ContractClassRegisteredEvent.fromLogs(allLogs, ClassRegistererAddress).map(e =>
e.toContractClassPublic(),
);
if (contractClasses.length > 0) {
contractClasses.forEach(c => this.log(`Registering contract class ${c.id.toString()}`));
await this.store.addContractClasses(contractClasses, blockNum);
Expand All @@ -369,22 +348,9 @@ export class Archiver implements ArchiveSource {
* @param allLogs - All logs emitted in a bunch of blocks.
*/
private async storeDeployedContractInstances(allLogs: UnencryptedL2Log[], blockNum: number) {
const contractInstances: ContractInstanceWithAddress[] = [];
for (const log of allLogs) {
try {
if (
!log.contractAddress.equals(this.instanceDeployerAddress) ||
!ContractInstanceDeployedEvent.isContractInstanceDeployedEvent(log.data)
) {
continue;
}
const event = ContractInstanceDeployedEvent.fromLogData(log.data);
contractInstances.push(event.toContractInstance());
} catch (err) {
this.log.warn(`Error processing log ${log.toHumanReadable()}: ${err}`);
}
}

const contractInstances = ContractInstanceDeployedEvent.fromLogs(allLogs, InstanceDeployerAddress).map(e =>
e.toContractInstance(),
);
if (contractInstances.length > 0) {
contractInstances.forEach(c => this.log(`Storing contract instance at ${c.address.toString()}`));
await this.store.addContractInstances(contractInstances, blockNum);
Expand Down Expand Up @@ -480,19 +446,11 @@ export class Archiver implements ArchiveSource {

const contractClass = await this.store.getContractClass(instance.contractClassId);
if (!contractClass) {
this.log.warn(
`Contract class ${instance.contractClassId.toString()} for address ${address.toString()} not found`,
);
this.log.warn(`Class ${instance.contractClassId.toString()} for address ${address.toString()} not found`);
return undefined;
}

return new ExtendedContractData(
new ContractData(address, instance.portalContractAddress),
contractClass.publicFunctions.map(f => new EncodedContractFunction(f.selector, f.isInternal, f.bytecode)),
contractClass.id,
computeSaltedInitializationHash(instance),
instance.publicKeysHash,
);
return ExtendedContractData.fromClassAndInstance(contractClass, instance);
}

/**
Expand All @@ -510,8 +468,21 @@ export class Archiver implements ArchiveSource {
* @param contractAddress - The contract data address.
* @returns ContractData with the portal address (if we didn't throw an error).
*/
public getContractData(contractAddress: AztecAddress): Promise<ContractData | undefined> {
return this.store.getContractData(contractAddress);
public async getContractData(contractAddress: AztecAddress): Promise<ContractData | undefined> {
return (await this.store.getContractData(contractAddress)) ?? this.makeContractDataFor(contractAddress);
}

/**
* Temporary method for creating a fake contract data out of classes and instances registered in the node.
* Used as a fallback if the extended contract data is not found.
*/
private async makeContractDataFor(address: AztecAddress): Promise<ContractData | undefined> {
const instance = await this.store.getContractInstance(address);
if (!instance) {
return undefined;
}

return new ContractData(address, instance.portalContractAddress);
}

/**
Expand Down Expand Up @@ -591,6 +562,10 @@ export class Archiver implements ArchiveSource {
getConfirmedL1ToL2Message(messageKey: Fr): Promise<L1ToL2Message> {
return this.store.getConfirmedL1ToL2Message(messageKey);
}

getContractClassIds(): Promise<Fr[]> {
return this.store.getContractClassIds();
}
}

/**
Expand Down
3 changes: 3 additions & 0 deletions yarn-project/archiver/src/archiver/archiver_store.ts
Original file line number Diff line number Diff line change
Expand Up @@ -196,4 +196,7 @@ export interface ArchiverDataStore {
* @param address - Address of the contract.
*/
getContractInstance(address: AztecAddress): Promise<ContractInstanceWithAddress | undefined>;

/** Returns the list of all class ids known by the archiver. */
getContractClassIds(): Promise<Fr[]>;
}
Loading
Loading