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: add the storage layout to the contract artifact #5952

Merged
merged 6 commits into from
Apr 24, 2024
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
2 changes: 1 addition & 1 deletion yarn-project/aztec.js/src/api/abi.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
export { ContractArtifact, FunctionArtifact, FunctionSelector } from '@aztec/foundation/abi';
export { loadContractArtifact } from '@aztec/types/abi';
export { loadContractArtifact, contractArtifactToBuffer, contractArtifactFromBuffer } from '@aztec/types/abi';
export { NoirCompiledContract } from '@aztec/types/noir';
2 changes: 2 additions & 0 deletions yarn-project/aztec.js/src/contract/contract.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,8 @@ describe('Contract Class', () => {
globals: {},
},
fileMap: {},
storageLayout: {},
notes: {},
};

beforeEach(() => {
Expand Down
38 changes: 8 additions & 30 deletions yarn-project/aztec.js/src/contract/contract_base.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
import { type Fr, computePartialAddress } from '@aztec/circuits.js';
import { type ContractArtifact, type FunctionArtifact, FunctionSelector } from '@aztec/foundation/abi';
import { computePartialAddress } from '@aztec/circuits.js';
import {
type ContractArtifact,
type ContractNote,
type FieldLayout,
type FunctionArtifact,
FunctionSelector,
} from '@aztec/foundation/abi';
import { type ContractInstanceWithAddress } from '@aztec/types/contracts';

import { type Wallet } from '../account/index.js';
Expand All @@ -16,34 +22,6 @@ export type ContractMethod = ((...args: any[]) => ContractFunctionInteraction) &
readonly selector: FunctionSelector;
};

/**
* Type representing a field layout in the storage of a contract.
*/
type FieldLayout = {
/**
* Slot in which the field is stored.
*/
slot: Fr;
/**
* Type being stored at the slot
*/
typ: string;
};

/**
* Type representing a note in use in the contract.
*/
type ContractNote = {
/**
* Note identifier
*/
id: Fr;
/**
* Type of the note
*/
typ: string;
};

/**
* Type representing the storage layout of a contract.
*/
Expand Down
2 changes: 0 additions & 2 deletions yarn-project/builder/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,6 @@
"fs-extra": "^11.1.1",
"lodash.camelcase": "^4.3.0",
"lodash.capitalize": "^4.2.1",
"lodash.uniqby": "^4.7.0",
"memfs": "^4.6.0",
"pako": "^2.1.0",
"semver": "^7.5.4",
Expand All @@ -71,7 +70,6 @@
"@types/jest": "^29.5.0",
"@types/lodash.camelcase": "^4.3.7",
"@types/lodash.capitalize": "^4.2.7",
"@types/lodash.uniqby": "^4.7.9",
"@types/node": "^18.7.23",
"@types/pako": "^2.0.0",
"@types/semver": "^7.5.4",
Expand Down
71 changes: 29 additions & 42 deletions yarn-project/builder/src/contract-interface-gen/typescript.ts
Original file line number Diff line number Diff line change
@@ -1,21 +1,14 @@
import {
type ABIParameter,
type BasicValue,
type ContractArtifact,
type FunctionArtifact,
type IntegerValue,
type StructValue,
type TupleValue,
type TypedStructFieldValue,
getDefaultInitializer,
isAztecAddressStruct,
isEthAddressStruct,
isFunctionSelectorStruct,
isWrappedFieldStruct,
} from '@aztec/foundation/abi';

import uniqBy from 'lodash.uniqby';

/**
* Returns the corresponding typescript type for a given Noir type.
* @param type - The input Noir type.
Expand Down Expand Up @@ -192,64 +185,58 @@ function generateAbiStatement(name: string, artifactImportPath: string) {
* @param input - The contract artifact.
*/
function generateStorageLayoutGetter(input: ContractArtifact) {
const storage = input.outputs.globals.storage ? (input.outputs.globals.storage[0] as StructValue) : { fields: [] };
const storageFields = storage.fields as TypedStructFieldValue<StructValue>[];
const storageFieldsUnionType = storageFields.map(f => `'${f.name}'`).join(' | ');
const layout = storageFields
const entries = Object.entries(input.storageLayout);

if (entries.length === 0) {
return '';
}

const storageFieldsUnionType = entries.map(([name]) => `'${name}'`).join(' | ');
const layout = entries
.map(
({
name,
value: {
fields: [slot, typ],
},
}) =>
([name, { slot, typ }]) =>
`${name}: {
slot: new Fr(${(slot.value as IntegerValue).value}n),
typ: "${(typ.value as BasicValue<'string', string>).value}",
}
`,
slot: new Fr(${slot.toBigInt()}n),
typ: "${typ}",
}`,
)
.join(',\n');
return storageFields.length > 0
? `
public static get storage(): ContractStorageLayout<${storageFieldsUnionType}> {

return `public static get storage(): ContractStorageLayout<${storageFieldsUnionType}> {
return {
${layout}
} as ContractStorageLayout<${storageFieldsUnionType}>;
}
`
: '';
`;
}

/**
* Generates a getter for the contract notes
* @param input - The contract artifact.
*/
function generateNotesGetter(input: ContractArtifact) {
const notes = input.outputs.globals.notes
? uniqBy(input.outputs.globals.notes as TupleValue[], n => (n.fields[1] as BasicValue<'string', string>).value)
: [];
const notesUnionType = notes.map(n => `'${(n.fields[1] as BasicValue<'string', string>).value}'`).join(' | ');
const entries = Object.entries(input.notes);

const noteMetadata = notes
if (entries.length === 0) {
return '';
}

const notesUnionType = entries.map(([name]) => `'${name}'`).join(' | ');
const noteMetadata = entries
.map(
({ fields: [id, typ] }) =>
`${(typ as BasicValue<'string', string>).value}: {
id: new Fr(${(id as IntegerValue).value}n),
}
`,
([name, { id }]) =>
`${name}: {
id: new Fr(${id.toBigInt()}n),
}`,
)
.join(',\n');
return notes.length > 0
? `
public static get notes(): ContractNotes<${notesUnionType}> {
const notes = this.artifact.outputs.globals.notes ? (this.artifact.outputs.globals.notes as any) : [];

return `public static get notes(): ContractNotes<${notesUnionType}> {
return {
${noteMetadata}
} as ContractNotes<${notesUnionType}>;
}
`
: '';
`;
}

/**
Expand Down
2 changes: 2 additions & 0 deletions yarn-project/circuit-types/src/mocks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,8 @@ export const randomContractArtifact = (): ContractArtifact => ({
globals: {},
},
fileMap: {},
storageLayout: {},
notes: {},
});

export const randomContractInstanceWithAddress = (opts: { contractClassId?: Fr } = {}): ContractInstanceWithAddress =>
Expand Down
2 changes: 2 additions & 0 deletions yarn-project/circuits.js/src/contract/artifact_hash.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ describe('ArtifactHash', () => {
globals: {},
structs: {},
},
storageLayout: {},
notes: {},
};
expect(computeArtifactHash(emptyArtifact).toString()).toMatchInlineSnapshot(
`"0x0dea64e7fa0688017f77bcb7075485485afb4a5f1f8508483398869439f82fdf"`,
Expand Down
4 changes: 2 additions & 2 deletions yarn-project/end-to-end/src/e2e_account_init_fees.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -333,8 +333,8 @@ describe('e2e_fees_account_init', () => {
});

async function addTransparentNoteToPxe(owner: AztecAddress, amount: bigint, secretHash: Fr, txHash: TxHash) {
const storageSlot = new Fr(5); // The storage slot of `pending_shields` is 5.
const noteTypeId = new Fr(84114971101151129711410111011678111116101n); // TransparentNote
const storageSlot = bananaCoin.artifact.storageLayout['pending_shields'].slot;
const noteTypeId = bananaCoin.artifact.notes['TransparentNote'].id;

const note = new Note([new Fr(amount), secretHash]);
// this note isn't encrypted but we need to provide a registered public key
Expand Down
4 changes: 2 additions & 2 deletions yarn-project/end-to-end/src/sample-dapp/index.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,8 @@ async function mintPrivateFunds(pxe) {
const secretHash = await computeSecretHash(secret);
const receipt = await token.methods.mint_private(mintAmount, secretHash).send().wait();

const storageSlot = new Fr(5);
const noteTypeId = new Fr(84114971101151129711410111011678111116101n); // TransparentNote
const storageSlot = token.artifact.storageLayout['pending_shields'].slot;
const noteTypeId = token.artifact.notes['TransparentNote'].id;

const note = new Note([new Fr(mintAmount), secretHash]);
const extendedNote = new ExtendedNote(
Expand Down
4 changes: 2 additions & 2 deletions yarn-project/end-to-end/src/sample-dapp/index.test.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,8 @@ describe('token', () => {
const secretHash = await computeSecretHash(secret);
const receipt = await token.methods.mint_private(initialBalance, secretHash).send().wait();

const storageSlot = new Fr(5);
const noteTypeId = new Fr(84114971101151129711410111011678111116101n); // TransparentNote
const storageSlot = token.artifact.storageLayout['pending_shields'].slot;
const noteTypeId = token.artifact.notes['TransparentNote'].id;
const note = new Note([new Fr(initialBalance), secretHash]);
const extendedNote = new ExtendedNote(
note,
Expand Down
10 changes: 5 additions & 5 deletions yarn-project/end-to-end/src/shared/browser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -227,11 +227,11 @@ export const browserTestSuite = (
INITIAL_TEST_SIGNING_KEYS,
INITIAL_TEST_ACCOUNT_SALTS,
Buffer,
contractArtifactFromBuffer,
} = window.AztecJs;
// We serialize the artifact since buffers (used for bytecode) do not cross well from one realm to another
const TokenContractArtifact = JSON.parse(
Buffer.from(serializedTokenContractArtifact, 'base64').toString('utf-8'),
(key, value) => (key === 'bytecode' && typeof value === 'string' ? Buffer.from(value, 'base64') : value),
const TokenContractArtifact = contractArtifactFromBuffer(
Buffer.from(serializedTokenContractArtifact, 'base64'),
);
const pxe = createPXEClient(rpcUrl!);

Expand Down Expand Up @@ -264,9 +264,9 @@ export const browserTestSuite = (
const secretHash = computeSecretHash(secret);
const mintPrivateReceipt = await token.methods.mint_private(initialBalance, secretHash).send().wait();

const storageSlot = new Fr(5);
const storageSlot = token.artifact.storageLayout['pending_shields'].slot;

const noteTypeId = new Fr(84114971101151129711410111011678111116101n);
const noteTypeId = token.artifact.notes['TransparentNote'].id;
const note = new Note([new Fr(initialBalance), secretHash]);
const extendedNote = new ExtendedNote(
note,
Expand Down
37 changes: 37 additions & 0 deletions yarn-project/foundation/src/abi/abi.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { inflate } from 'pako';

import { type Fr } from '../fields/fields.js';
import { type FunctionSelector } from './function_selector.js';

/**
Expand Down Expand Up @@ -267,6 +268,34 @@ export type DebugFileMap = Record<
}
>;

/**
* Type representing a note in use in the contract.
*/
export type ContractNote = {
/**
* Note identifier
*/
id: Fr;
/**
* Type of the note (e.g., 'TransparentNote')
*/
typ: string;
};

/**
* Type representing a field layout in the storage of a contract.
*/
export type FieldLayout = {
/**
* Slot in which the field is stored.
*/
slot: Fr;
/**
* Type being stored at the slot (e.g., 'Map<AztecAddress, PublicMutable<U128>>')
*/
typ: string;
};

/**
* Defines artifact of a contract.
*/
Expand All @@ -292,6 +321,14 @@ export interface ContractArtifact {
structs: Record<string, AbiType[]>;
globals: Record<string, AbiValue[]>;
};
/**
* Storage layout
*/
storageLayout: Record<string, FieldLayout>;
/**
* The notes used in the contract.
*/
notes: Record<string, ContractNote>;

/**
* The map of file ID to the source code and path of the file.
Expand Down
3 changes: 3 additions & 0 deletions yarn-project/p2p/src/service/discv5_service.test.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { jest } from '@jest/globals';
import type { PeerId } from '@libp2p/interface';

import { BootstrapNode } from '../bootstrap/bootstrap.js';
Expand All @@ -21,6 +22,8 @@ const waitForPeers = (node: DiscV5Service, expectedCount: number): Promise<void>
};

describe('Discv5Service', () => {
jest.setTimeout(10_000);

let bootNode: BootstrapNode;
let bootNodePeerId: PeerId;
let port = 1234;
Expand Down
Loading
Loading