Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
9 changes: 9 additions & 0 deletions yarn-project/acir-simulator/src/client/db_oracle.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,15 @@ export class ContractNotFoundError extends Error {
}
}

/**
* Error thrown when a contract class is not found in the database.
*/
export class ContractClassNotFoundError extends Error {
constructor(contractClassId: string) {
super(`DB has no contract class with id ${contractClassId}`);
}
}

/**
* The database oracle interface.
*/
Expand Down
3 changes: 2 additions & 1 deletion yarn-project/aztec-nr/aztec/src/context.nr
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ use dep::protocol_types::{
NUM_FIELDS_PER_SHA256,
RETURN_VALUES_LENGTH,
},
contract_class::ContractClassId,
contrakt::{
deployment_data::ContractDeploymentData,
storage_read::StorageRead,
Expand Down Expand Up @@ -348,7 +349,7 @@ impl PrivateContext {
y: reader.read()
},
initialization_hash : reader.read(),
contract_class_id : reader.read(),
contract_class_id : ContractClassId::from_field(reader.read()),
contract_address_salt : reader.read(),
portal_contract_address : EthAddress::from_field(reader.read()),
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ use dep::protocol_types::{
new_contract_data::NewContractData as ContractLeafPreimage,
},
address::{AztecAddress, EthAddress},
contract_class::ContractClassId,
grumpkin_point::GrumpkinPoint,
};
use dep::std::merkle::compute_merkle_root;
Expand All @@ -23,7 +24,7 @@ use crate::{
pub fn prove_contract_inclusion(
public_key: GrumpkinPoint,
contract_address_salt: Field,
contract_class_id: Field,
contract_class_id: ContractClassId,
initialization_hash: Field,
portal_contract_address: EthAddress,
block_number: u32, // The block at which we'll prove that the public value exists
Expand Down
1 change: 1 addition & 0 deletions yarn-project/aztec.js/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ export {
CheatCodes,
AztecAddressLike,
FunctionSelectorLike,
WrappedFieldLike,
isContractDeployed,
EthCheatCodes,
computeAuthWitMessageHash,
Expand Down
3 changes: 3 additions & 0 deletions yarn-project/aztec.js/src/utils/abi_types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,6 @@ export type AztecAddressLike = { /** Wrapped address */ address: FieldLike } | A

/** Any type that can be converted into an FunctionSelector Aztec.nr struct. */
export type FunctionSelectorLike = FieldLike | FunctionSelector;

/** Any type that can be converted into a struct with a single `inner` field. */
export type WrappedFieldLike = { /** Wrapped value */ inner: FieldLike } | FieldLike;
3 changes: 2 additions & 1 deletion yarn-project/circuits.js/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,8 @@
"./utils": "./dest/utils/index.js",
"./types": "./dest/types/index.js",
"./constants": "./dest/constants.gen.js",
"./contract": "./dest/contract/index.js"
"./contract": "./dest/contract/index.js",
"./merkle": "./dest/merkle/index.js"
},
"typedocOptions": {
"entryPoints": [
Expand Down
4 changes: 2 additions & 2 deletions yarn-project/circuits.js/src/abis/abis.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import {
PRIVATE_CIRCUIT_PUBLIC_INPUTS_HASH_INPUT_LENGTH,
PUBLIC_CIRCUIT_PUBLIC_INPUTS_HASH_INPUT_LENGTH,
} from '../constants.gen.js';
import { MerkleTreeCalculator } from '../merkle/merkle_tree_calculator.js';
import {
CallContext,
ContractDeploymentData,
Expand All @@ -32,7 +33,6 @@ import {
TxRequest,
VerificationKey,
} from '../structs/index.js';
import { MerkleTreeCalculator } from './merkle_tree_calculator.js';

/**
* Computes a hash of a transaction request.
Expand Down Expand Up @@ -136,7 +136,7 @@ export function computeFunctionTree(fnLeaves: Fr[]) {
const leaves = fnLeaves.map(fr => fr.toBuffer());
return getFunctionTreeRootCalculator()
.computeTree(leaves)
.map(b => Fr.fromBuffer(b));
.nodes.map(b => Fr.fromBuffer(b));
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,25 +25,25 @@ exports[`ContractClass creates a contract class from a contract compilation arti
"selector": {
"value": 2432309179
},
"vkHash": "0x038021824fbd98bb0e388b0efe18f72e9350f7456481714539ba583de37113ce",
"vkHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
"isInternal": false
},
{
"selector": {
"value": 283286945
},
"vkHash": "0x038021824fbd98bb0e388b0efe18f72e9350f7456481714539ba583de37113ce",
"vkHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
"isInternal": false
},
{
"selector": {
"value": 332459554
},
"vkHash": "0x038021824fbd98bb0e388b0efe18f72e9350f7456481714539ba583de37113ce",
"vkHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
"isInternal": false
}
],
"packedBytecode": "0x",
"id": "0x0710cd0d58fbc487f87fb17855d50ecdc46d3df58b724044f1a35eee815becf5"
"id": "0x034c098fd12d17ec1ecb116e91d01ddc76748569790835f91c866f3e8ec8466a"
}"
`;
Binary file not shown.
2 changes: 1 addition & 1 deletion yarn-project/circuits.js/src/contract/artifact_hash.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { sha256 } from '@aztec/foundation/crypto';
import { Fr } from '@aztec/foundation/fields';
import { numToUInt8 } from '@aztec/foundation/serialize';

import { MerkleTreeCalculator } from '../abis/merkle_tree_calculator.js';
import { MerkleTreeCalculator } from '../merkle/merkle_tree_calculator.js';

const VERSION = 1;

Expand Down
20 changes: 17 additions & 3 deletions yarn-project/circuits.js/src/contract/contract_address.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,11 @@ import { AztecAddress, GeneratorIndex, PublicKey } from '../index.js';
* ```
* @param instance - A contract instance for which to calculate the deployment address.
*/
export function computeContractAddressFromInstance(instance: ContractInstance): AztecAddress {
export function computeContractAddressFromInstance(
instance:
| ContractInstance
| ({ contractClassId: Fr; saltedInitializationHash: Fr } & Pick<ContractInstance, 'publicKeysHash'>),
): AztecAddress {
const partialAddress = computePartialAddress(instance);
const publicKeyHash = instance.publicKeysHash;
return computeContractAddressFromPartial({ partialAddress, publicKeyHash });
Expand Down Expand Up @@ -97,6 +101,16 @@ export function computePublicKeysHash(publicKey: PublicKey | undefined): Fr {
export function computeInitializationHash(initFn: FunctionAbi, args: any[]): Fr {
const selector = FunctionSelector.fromNameAndParameters(initFn.name, initFn.parameters);
const flatArgs = encodeArguments(initFn, args);
const argsHash = computeVarArgsHash(flatArgs);
return Fr.fromBuffer(pedersenHash([selector.toBuffer(), argsHash.toBuffer()], GeneratorIndex.CONSTRUCTOR));
return computeInitializationHashFromEncodedArgs(selector, flatArgs);
}

/**
* Computes the initialization hash for an instance given its constructor function selector and encoded arguments.
* @param initFn - Constructor function selector.
* @param args - Encoded arguments.
* @returns The hash.
*/
export function computeInitializationHashFromEncodedArgs(initFn: FunctionSelector, encodedArgs: Fr[]): Fr {
const argsHash = computeVarArgsHash(encodedArgs);
return Fr.fromBuffer(pedersenHash([initFn.toBuffer(), argsHash.toBuffer()], GeneratorIndex.CONSTRUCTOR));
}
13 changes: 7 additions & 6 deletions yarn-project/circuits.js/src/contract/contract_class.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,7 @@ import { Fr } from '@aztec/foundation/fields';
import { ContractClass, ContractClassWithId } from '@aztec/types/contracts';

import { getArtifactHash } from './artifact_hash.js';
import { getContractClassId } from './contract_class_id.js';
import { hashVKStr } from './contract_tree/index.js';
import { computeContractClassId } from './contract_class_id.js';

/** Contract artifact including its artifact hash */
type ContractArtifactWithHash = ContractArtifact & { artifactHash: Fr };
Expand Down Expand Up @@ -33,13 +32,15 @@ export function getContractClassFromArtifact(
})),
packedBytecode: Buffer.alloc(0),
};
const id = getContractClassId(contractClass);
const id = computeContractClassId(contractClass);
return { ...contractClass, id };
}

/**
* Calculates the hash of a verification key.
* */
function getVerificationKeyHash(verificationKeyInBase64: string) {
return Fr.fromBuffer(hashVKStr(verificationKeyInBase64));
* Returns zero for consistency with Noir.
*/
function getVerificationKeyHash(_verificationKeyInBase64: string) {
// return Fr.fromBuffer(hashVKStr(verificationKeyInBase64));
return Fr.ZERO;
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { Fr } from '@aztec/foundation/fields';
import { ContractClass } from '@aztec/types/contracts';

import { FunctionSelector, getContractClassId } from '../index.js';
import { FunctionSelector, computeContractClassId } from '../index.js';

describe('ContractClass', () => {
describe('getContractClassId', () => {
Expand All @@ -26,8 +26,8 @@ describe('ContractClass', () => {
],
};

expect(getContractClassId(contractClass).toString()).toMatchInlineSnapshot(
`"0x1b436781f84669144ec383d6ea5f49b05ccba5c6221ebeb86085443c2a859202"`,
expect(computeContractClassId(contractClass).toString()).toMatchInlineSnapshot(
`"0x2f4c56801b35e01081aeb1b2bd07eba0f8d55de625ec1e957347eedaea1669bb"`,
);
});
});
Expand Down
70 changes: 20 additions & 50 deletions yarn-project/circuits.js/src/contract/contract_class_id.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
import { pedersenHash, sha256 } from '@aztec/foundation/crypto';
import { Fr } from '@aztec/foundation/fields';
import { numToUInt8 } from '@aztec/foundation/serialize';
import { ContractClass, PrivateFunction, PublicFunction } from '@aztec/types/contracts';
import { ContractClass } from '@aztec/types/contracts';

import { MerkleTreeCalculator } from '../abis/merkle_tree_calculator.js';
import { FUNCTION_TREE_HEIGHT, GeneratorIndex } from '../constants.gen.js';
import { GeneratorIndex } from '../constants.gen.js';
import { computePrivateFunctionsRoot } from './private_function.js';

/**
* Returns the id of a contract class computed as its hash.
Expand All @@ -19,60 +18,31 @@ import { FUNCTION_TREE_HEIGHT, GeneratorIndex } from '../constants.gen.js';
* @param contractClass - Contract class.
* @returns The identifier.
*/
export function getContractClassId(contractClass: ContractClass): Fr {
const privateFunctionsRoot = getPrivateFunctionsRoot(contractClass.privateFunctions);
const publicFunctionsRoot = getPublicFunctionsRoot(contractClass.publicFunctions); // This should be removed once we drop public functions as first class citizens in the protocol
const bytecodeCommitment = getBytecodeCommitment(contractClass.packedBytecode);
export function computeContractClassId(contractClass: ContractClass): Fr {
const { privateFunctionsRoot, publicBytecodeCommitment } = computeContractClassIdPreimage(contractClass);
return Fr.fromBuffer(
pedersenHash(
[
numToUInt8(contractClass.version),
contractClass.artifactHash.toBuffer(),
privateFunctionsRoot.toBuffer(),
publicFunctionsRoot.toBuffer(),
bytecodeCommitment.toBuffer(),
],
[contractClass.artifactHash.toBuffer(), privateFunctionsRoot.toBuffer(), publicBytecodeCommitment.toBuffer()],
GeneratorIndex.CONTRACT_LEAF, // TODO(@spalladino): Review all generator indices in this file
),
);
}

// TODO(@spalladino): Replace with actual implementation
function getBytecodeCommitment(bytecode: Buffer) {
return Fr.fromBufferReduce(sha256(bytecode));
/** Returns the preimage of a contract class id given a contract class. */
export function computeContractClassIdPreimage(contractClass: ContractClass): ContractClassIdPreimage {
const privateFunctionsRoot = computePrivateFunctionsRoot(contractClass.privateFunctions);
const publicBytecodeCommitment = computeBytecodeCommitment(contractClass.packedBytecode);
return { artifactHash: contractClass.artifactHash, privateFunctionsRoot, publicBytecodeCommitment };
}

// Memoize the merkle tree calculators to avoid re-computing the zero-hash for each level in each call
let privateFunctionTreeCalculator: MerkleTreeCalculator | undefined;
let publicFunctionTreeCalculator: MerkleTreeCalculator | undefined;
/** Preimage of a contract class id. */
export type ContractClassIdPreimage = {
artifactHash: Fr;
privateFunctionsRoot: Fr;
publicBytecodeCommitment: Fr;
};

const PRIVATE_FUNCTION_SIZE = 2;
const PUBLIC_FUNCTION_SIZE = 2;

function getPrivateFunctionsRoot(fns: PrivateFunction[]): Fr {
const privateFunctionLeaves = fns.map(fn =>
pedersenHash(
[fn.selector, fn.vkHash].map(x => x.toBuffer()),
GeneratorIndex.FUNCTION_LEAF,
),
);
if (!privateFunctionTreeCalculator) {
const functionTreeZeroLeaf = pedersenHash(new Array(PRIVATE_FUNCTION_SIZE).fill(Buffer.alloc(32)));
privateFunctionTreeCalculator = new MerkleTreeCalculator(FUNCTION_TREE_HEIGHT, functionTreeZeroLeaf);
}
return Fr.fromBuffer(privateFunctionTreeCalculator.computeTreeRoot(privateFunctionLeaves));
}

function getPublicFunctionsRoot(fns: PublicFunction[]): Fr {
const publicFunctionLeaves = fns.map(fn =>
pedersenHash(
[fn.selector, getBytecodeCommitment(fn.bytecode)].map(x => x.toBuffer()),
GeneratorIndex.FUNCTION_LEAF,
),
);
if (!publicFunctionTreeCalculator) {
const functionTreeZeroLeaf = pedersenHash(new Array(PUBLIC_FUNCTION_SIZE).fill(Buffer.alloc(32)));
publicFunctionTreeCalculator = new MerkleTreeCalculator(FUNCTION_TREE_HEIGHT, functionTreeZeroLeaf);
}
return Fr.fromBuffer(publicFunctionTreeCalculator.computeTreeRoot(publicFunctionLeaves));
// TODO(@spalladino): Replace with actual implementation
function computeBytecodeCommitment(bytecode: Buffer) {
return Fr.fromBufferReduce(sha256(bytecode));
}
4 changes: 2 additions & 2 deletions yarn-project/circuits.js/src/contract/contract_instance.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { ContractArtifact } from '@aztec/foundation/abi';
import { ContractInstance, ContractInstanceWithAddress } from '@aztec/types/contracts';

import { EthAddress, Fr, PublicKey, getContractClassFromArtifact, getContractClassId } from '../index.js';
import { EthAddress, Fr, PublicKey, computeContractClassId, getContractClassFromArtifact } from '../index.js';
import {
computeContractAddressFromInstance,
computeInitializationHash,
Expand Down Expand Up @@ -34,7 +34,7 @@ export function getContractInstanceFromDeployParams(
}

const contractClass = getContractClassFromArtifact(artifact);
const contractClassId = getContractClassId(contractClass);
const contractClassId = computeContractClassId(contractClass);
const initializationHash = computeInitializationHash(constructorArtifact, args);
const publicKeysHash = computePublicKeysHash(publicKey);

Expand Down
1 change: 1 addition & 0 deletions yarn-project/circuits.js/src/contract/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,4 @@ export * from './contract_class_id.js';
export * from './contract_class.js';
export * from './artifact_hash.js';
export * from './contract_address.js';
export * from './private_function.js';
34 changes: 34 additions & 0 deletions yarn-project/circuits.js/src/contract/private_function.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import { Fr } from '@aztec/foundation/fields';
import { PrivateFunction } from '@aztec/types/contracts';

import { fr, makeSelector } from '../tests/factories.js';
import { computePrivateFunctionsRoot, computePrivateFunctionsTree } from './private_function.js';

describe('PrivateFunction', () => {
const privateFunctions: PrivateFunction[] = [
{ selector: makeSelector(1), vkHash: fr(2), isInternal: false },
{ selector: makeSelector(3), vkHash: fr(4), isInternal: false },
];

it('computes merkle tree', () => {
const tree = computePrivateFunctionsTree(privateFunctions);
expect(tree.nodes.map(node => node.toString())).toMatchSnapshot();
});

it('computes merkle tree root', () => {
const root = computePrivateFunctionsRoot(privateFunctions);
expect(root.toString()).toMatchSnapshot();
});

it('tree and root methods agree', () => {
const tree = computePrivateFunctionsTree(privateFunctions);
const root = computePrivateFunctionsRoot(privateFunctions);
expect(Fr.fromBuffer(tree.root).equals(root)).toBe(true);
});

it('sorts functions before computing tree', () => {
const root = computePrivateFunctionsRoot(privateFunctions);
const rootReversed = computePrivateFunctionsRoot([...privateFunctions].reverse());
expect(root.equals(rootReversed)).toBe(true);
});
});
Loading