-
Notifications
You must be signed in to change notification settings - Fork 316
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: Aztec.js API for registering a contract class #4469
Merged
Merged
Changes from all commits
Commits
Show all changes
2 commits
Select commit
Hold shift + click to select a range
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
export { registerContractClass } from '../deployment/register_class.js'; | ||
export { broadcastPrivateFunction, broadcastUnconstrainedFunction } from '../deployment/broadcast_function.js'; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
import { ContractArtifact } from '@aztec/foundation/abi'; | ||
import { ContractInstanceWithAddress } from '@aztec/types/contracts'; | ||
|
||
import { Wallet } from '../wallet/index.js'; | ||
import { ContractBase } from './contract_base.js'; | ||
|
||
/** Unsafe constructor for ContractBase that bypasses the check that the instance is registered in the wallet. */ | ||
export class UnsafeContract extends ContractBase { | ||
constructor( | ||
/** The deployed contract instance definition. */ | ||
instance: ContractInstanceWithAddress, | ||
/** The Application Binary Interface for the contract. */ | ||
artifact: ContractArtifact, | ||
/** The wallet used for interacting with this contract. */ | ||
wallet: Wallet, | ||
) { | ||
super(instance, artifact, wallet); | ||
} | ||
} |
118 changes: 118 additions & 0 deletions
118
yarn-project/aztec.js/src/deployment/broadcast_function.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,118 @@ | ||
import { | ||
ARTIFACT_FUNCTION_TREE_MAX_HEIGHT, | ||
MAX_PACKED_BYTECODE_SIZE_PER_PRIVATE_FUNCTION_IN_FIELDS, | ||
computeArtifactFunctionTree, | ||
computeArtifactFunctionTreeRoot, | ||
computeArtifactMetadataHash, | ||
computeFunctionArtifactHash, | ||
computePrivateFunctionsTree, | ||
getContractClassFromArtifact, | ||
} from '@aztec/circuits.js'; | ||
import { ContractArtifact, FunctionSelector, FunctionType, bufferAsFields } from '@aztec/foundation/abi'; | ||
import { padArrayEnd } from '@aztec/foundation/collection'; | ||
import { Fr } from '@aztec/foundation/fields'; | ||
|
||
import { ContractFunctionInteraction } from '../contract/contract_function_interaction.js'; | ||
import { Wallet } from '../wallet/index.js'; | ||
import { getRegistererContract } from './protocol_contracts.js'; | ||
|
||
/** | ||
* Sets up a call to broadcast a private function's bytecode via the ClassRegisterer contract. | ||
* Note that this is not required for users to call the function, but is rather a convenience to make | ||
* this code publicly available so dapps or wallets do not need to redistribute it. | ||
* @param wallet - Wallet to send the transaction. | ||
* @param artifact - Contract artifact that contains the function to be broadcast. | ||
* @param selector - Selector of the function to be broadcast. | ||
* @returns A ContractFunctionInteraction object that can be used to send the transaction. | ||
*/ | ||
export function broadcastPrivateFunction( | ||
wallet: Wallet, | ||
artifact: ContractArtifact, | ||
selector: FunctionSelector, | ||
): ContractFunctionInteraction { | ||
const contractClass = getContractClassFromArtifact(artifact); | ||
const privateFunction = contractClass.privateFunctions.find(fn => fn.selector.equals(selector)); | ||
if (!privateFunction) { | ||
throw new Error(`Private function with selector ${selector.toString()} not found`); | ||
} | ||
const privateFunctionArtifact = artifact.functions.find(fn => | ||
FunctionSelector.fromNameAndParameters(fn).equals(selector), | ||
)!; | ||
|
||
// TODO(@spalladino): The following is computing the unconstrained root hash twice. | ||
// Feels like we need a nicer API for returning a hash along with all its preimages, | ||
// since it's common to provide all hash preimages to a function that verifies them. | ||
const artifactMetadataHash = computeArtifactMetadataHash(artifact); | ||
const unconstrainedArtifactFunctionTreeRoot = computeArtifactFunctionTreeRoot(artifact, FunctionType.OPEN); | ||
|
||
// We need two sibling paths because private function information is split across two trees: | ||
// The "private function tree" captures the selectors and verification keys, and is used in the kernel circuit for verifying the proof generated by the app circuit. | ||
// The "artifact tree" captures function bytecode and metadata, and is used by the pxe to check that its executing the code it's supposed to be executing, but it never goes into circuits. | ||
const privateFunctionTreePath = computePrivateFunctionsTree(contractClass.privateFunctions).getSiblingPath(0); | ||
const artifactFunctionTreePath = computeArtifactFunctionTree(artifact, FunctionType.SECRET)!.getSiblingPath(0); | ||
|
||
const vkHash = privateFunction.vkHash; | ||
const metadataHash = computeFunctionArtifactHash(privateFunctionArtifact); | ||
const bytecode = bufferAsFields( | ||
Buffer.from(privateFunctionArtifact.bytecode, 'hex'), | ||
MAX_PACKED_BYTECODE_SIZE_PER_PRIVATE_FUNCTION_IN_FIELDS, | ||
); | ||
|
||
const registerer = getRegistererContract(wallet); | ||
return registerer.methods.broadcast_private_function( | ||
contractClass.id, | ||
Fr.fromBufferReduce(artifactMetadataHash), | ||
Fr.fromBufferReduce(unconstrainedArtifactFunctionTreeRoot), | ||
privateFunctionTreePath.map(Fr.fromBufferReduce), | ||
padArrayEnd(artifactFunctionTreePath.map(Fr.fromBufferReduce), Fr.ZERO, ARTIFACT_FUNCTION_TREE_MAX_HEIGHT), | ||
// eslint-disable-next-line camelcase | ||
{ selector, metadata_hash: Fr.fromBufferReduce(metadataHash), bytecode, vk_hash: vkHash }, | ||
); | ||
} | ||
|
||
/** | ||
* Sets up a call to broadcast an unconstrained function's bytecode via the ClassRegisterer contract. | ||
* Note that this is not required for users to call the function, but is rather a convenience to make | ||
* this code publicly available so dapps or wallets do not need to redistribute it. | ||
* @param wallet - Wallet to send the transaction. | ||
* @param artifact - Contract artifact that contains the function to be broadcast. | ||
* @param selector - Selector of the function to be broadcast. | ||
* @returns A ContractFunctionInteraction object that can be used to send the transaction. | ||
*/ | ||
export function broadcastUnconstrainedFunction( | ||
wallet: Wallet, | ||
artifact: ContractArtifact, | ||
selector: FunctionSelector, | ||
): ContractFunctionInteraction { | ||
const functionArtifactIndex = artifact.functions.findIndex( | ||
fn => fn.functionType === FunctionType.UNCONSTRAINED && FunctionSelector.fromNameAndParameters(fn).equals(selector), | ||
); | ||
if (functionArtifactIndex < 0) { | ||
throw new Error(`Unconstrained function with selector ${selector.toString()} not found`); | ||
} | ||
const functionArtifact = artifact.functions[functionArtifactIndex]; | ||
|
||
// TODO(@spalladino): Same comment as above on computing duplicated hashes. | ||
const artifactMetadataHash = computeArtifactMetadataHash(artifact); | ||
const privateArtifactFunctionTreeRoot = computeArtifactFunctionTreeRoot(artifact, FunctionType.SECRET); | ||
const functionTreePath = computeArtifactFunctionTree(artifact, FunctionType.UNCONSTRAINED)!.getSiblingPath( | ||
functionArtifactIndex, | ||
); | ||
|
||
const contractClassId = getContractClassFromArtifact(artifact).id; | ||
const metadataHash = computeFunctionArtifactHash(functionArtifact); | ||
const bytecode = bufferAsFields( | ||
Buffer.from(functionArtifact.bytecode, 'hex'), | ||
MAX_PACKED_BYTECODE_SIZE_PER_PRIVATE_FUNCTION_IN_FIELDS, | ||
); | ||
|
||
const registerer = getRegistererContract(wallet); | ||
return registerer.methods.broadcast_unconstrained_function( | ||
contractClassId, | ||
Fr.fromBufferReduce(artifactMetadataHash), | ||
Fr.fromBufferReduce(privateArtifactFunctionTreeRoot), | ||
padArrayEnd(functionTreePath.map(Fr.fromBufferReduce), Fr.ZERO, ARTIFACT_FUNCTION_TREE_MAX_HEIGHT), | ||
// eslint-disable-next-line camelcase | ||
{ selector, metadata_hash: Fr.fromBufferReduce(metadataHash), bytecode }, | ||
); | ||
} |
File renamed without changes.
File renamed without changes.
File renamed without changes.
10 changes: 10 additions & 0 deletions
10
yarn-project/aztec.js/src/deployment/protocol_contracts.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
import { getCanonicalClassRegisterer } from '@aztec/protocol-contracts/class-registerer'; | ||
|
||
import { UnsafeContract } from '../contract/unsafe_contract.js'; | ||
import { Wallet } from '../wallet/index.js'; | ||
|
||
/** Returns a Contract wrapper for the class registerer. */ | ||
export function getRegistererContract(wallet: Wallet) { | ||
const { artifact, instance } = getCanonicalClassRegisterer(); | ||
return new UnsafeContract(instance, artifact, wallet); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
import { MAX_PACKED_PUBLIC_BYTECODE_SIZE_IN_FIELDS, getContractClassFromArtifact } from '@aztec/circuits.js'; | ||
import { ContractArtifact, bufferAsFields } from '@aztec/foundation/abi'; | ||
|
||
import { ContractFunctionInteraction } from '../contract/contract_function_interaction.js'; | ||
import { Wallet } from '../wallet/index.js'; | ||
import { getRegistererContract } from './protocol_contracts.js'; | ||
|
||
/** Sets up a call to register a contract class given its artifact. */ | ||
export function registerContractClass(wallet: Wallet, artifact: ContractArtifact): ContractFunctionInteraction { | ||
const { artifactHash, privateFunctionsRoot, publicBytecodeCommitment, packedBytecode } = | ||
getContractClassFromArtifact(artifact); | ||
const encodedBytecode = bufferAsFields(packedBytecode, MAX_PACKED_PUBLIC_BYTECODE_SIZE_IN_FIELDS); | ||
const registerer = getRegistererContract(wallet); | ||
return registerer.methods.register(artifactHash, privateFunctionsRoot, publicBytecodeCommitment, encodedBytecode); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -18,6 +18,9 @@ | |
{ | ||
"path": "../foundation" | ||
}, | ||
{ | ||
"path": "../protocol-contracts" | ||
}, | ||
{ | ||
"path": "../types" | ||
} | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't understand how/where any bypass takes place since we're just calling
super
?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The constructor in the base class is protected, this class makes it public.