Skip to content

Commit

Permalink
Introduce classes and instances and populate in node
Browse files Browse the repository at this point in the history
  • Loading branch information
spalladino committed Jan 23, 2024
1 parent 5006228 commit 9a98343
Show file tree
Hide file tree
Showing 26 changed files with 650 additions and 7 deletions.
3 changes: 2 additions & 1 deletion yarn-project/archiver/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@
"@aztec/foundation": "workspace:^",
"@aztec/kv-store": "workspace:^",
"@aztec/l1-artifacts": "workspace:^",
"@types/lodash.omit": "^4.5.7",
"@aztec/types": "workspace:^",
"debug": "^4.3.4",
"lmdb": "^2.9.1",
"lodash.omit": "^4.5.0",
Expand All @@ -54,6 +54,7 @@
"@jest/globals": "^29.5.0",
"@types/debug": "^4.1.7",
"@types/jest": "^29.5.0",
"@types/lodash.omit": "^4.5.7",
"@types/node": "^18.15.11",
"@types/ws": "^8.5.4",
"concurrently": "^8.0.1",
Expand Down
58 changes: 57 additions & 1 deletion yarn-project/archiver/src/archiver/archiver.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,14 +15,20 @@ import {
LogType,
TxHash,
} from '@aztec/circuit-types';
import { FunctionSelector, NUMBER_OF_L1_L2_MESSAGES_PER_ROLLUP } from '@aztec/circuits.js';
import { FunctionSelector, NUMBER_OF_L1_L2_MESSAGES_PER_ROLLUP, getContractClassId } from '@aztec/circuits.js';
import { createEthereumChain } from '@aztec/ethereum';
import { AztecAddress } from '@aztec/foundation/aztec-address';
import { padArrayEnd } from '@aztec/foundation/collection';
import { EthAddress } from '@aztec/foundation/eth-address';
import { Fr } from '@aztec/foundation/fields';
import { DebugLogger, createDebugLogger } from '@aztec/foundation/log';
import { RunningPromise } from '@aztec/foundation/running-promise';
import {
ContractClass,
ContractClassWithId,
ContractInstance,
ContractInstanceWithAddress,
} from '@aztec/types/contracts';

import omit from 'lodash.omit';
import { Chain, HttpTransport, PublicClient, createPublicClient, http } from 'viem';
Expand Down Expand Up @@ -268,6 +274,7 @@ export class Archiver implements L2BlockSource, L2LogsSource, ContractDataSource
this.log(`Retrieved extended contract data for l2 block number: ${l2BlockNum}`);
if (l2BlockNum <= lastKnownL2BlockNum) {
await this.store.addExtendedContractData(contracts, l2BlockNum);
await this.storeContractDataAsClassesAndInstances(contracts, l2BlockNum);
}
}),
);
Expand All @@ -294,6 +301,24 @@ export class Archiver implements L2BlockSource, L2LogsSource, ContractDataSource
);
}

/**
* Stores extended contract data as classes and instances.
* Temporary solution until we source this data from the contract class registerer and instance deployer.
* @param contracts - The extended contract data to be stored.
* @param l2BlockNum - The L2 block number to which the contract data corresponds.
*/
async storeContractDataAsClassesAndInstances(contracts: ExtendedContractData[], l2BlockNum: number) {
const classesAndInstances = contracts.map(extendedContractDataToContractClassAndInstance);
await this.store.addContractClasses(
classesAndInstances.map(([c, _]) => c),
l2BlockNum,
);
await this.store.addContractInstances(
classesAndInstances.map(([_, i]) => i),
l2BlockNum,
);
}

/**
* Stops the archiver.
* @returns A promise signalling completion of the stop process.
Expand Down Expand Up @@ -440,3 +465,34 @@ export class Archiver implements L2BlockSource, L2LogsSource, ContractDataSource
return this.store.getConfirmedL1ToL2Message(messageKey);
}
}

/** Converts ExtendedContractData into contract classes and instances. */
function extendedContractDataToContractClassAndInstance(
data: ExtendedContractData,
): [ContractClassWithId, ContractInstanceWithAddress] {
const contractClass: ContractClass = {
version: 1,
artifactHash: Fr.ZERO,
publicFunctions: data.publicFunctions.map(f => ({
selector: f.selector,
bytecode: f.bytecode,
isInternal: f.isInternal,
})),
privateFunctions: [],
packedBytecode: data.bytecode,
};
const contractClassId = getContractClassId(contractClass);
const contractInstance: ContractInstance = {
version: 1,
salt: Fr.ZERO,
contractClassId,
initializationHash: Fr.ZERO,
portalContractAddress: data.contractData.portalContractAddress,
publicKeysHash: data.partialAddress,
};
const address = data.contractData.contractAddress;
return [
{ ...contractClass, id: contractClassId },
{ ...contractInstance, address },
];
}
29 changes: 29 additions & 0 deletions yarn-project/archiver/src/archiver/archiver_store.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import {
} from '@aztec/circuit-types';
import { Fr } from '@aztec/circuits.js';
import { AztecAddress } from '@aztec/foundation/aztec-address';
import { ContractClassWithId, ContractInstanceWithAddress } from '@aztec/types/contracts';

/**
* Represents the latest L1 block processed by the archiver for various objects in L2.
Expand Down Expand Up @@ -167,4 +168,32 @@ export interface ArchiverDataStore {
* Gets the last L1 block number processed by the archiver
*/
getL1BlockNumber(): Promise<ArchiverL1SynchPoint>;

/**
* Add new contract classes from an L2 block to the store's list.
* @param data - List of contract classes to be added.
* @param blockNumber - Number of the L2 block the contracts were registered in.
* @returns True if the operation is successful.
*/
addContractClasses(data: ContractClassWithId[], blockNumber: number): Promise<boolean>;

/**
* Returns a contract class given its id, or undefined if not exists.
* @param id - Id of the contract class.
*/
getContractClass(id: Fr): Promise<ContractClassWithId | undefined>;

/**
* Add new contract instances from an L2 block to the store's list.
* @param data - List of contract instances to be added.
* @param blockNumber - Number of the L2 block the instances were deployed in.
* @returns True if the operation is successful.
*/
addContractInstances(data: ContractInstanceWithAddress[], blockNumber: number): Promise<boolean>;

/**
* Returns a contract instance given its address, or undefined if not exists.
* @param address - Address of the contract.
*/
getContractInstance(address: AztecAddress): Promise<ContractInstanceWithAddress | undefined>;
}
42 changes: 42 additions & 0 deletions yarn-project/archiver/src/archiver/archiver_store_test_suite.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,12 @@ import {
import '@aztec/circuit-types/jest';
import { AztecAddress, Fr } from '@aztec/circuits.js';
import { randomBytes } from '@aztec/foundation/crypto';
import {
ContractClassWithId,
ContractInstanceWithAddress,
SerializableContractClass,
SerializableContractInstance,
} from '@aztec/types/contracts';

import { ArchiverDataStore } from './archiver_store.js';

Expand Down Expand Up @@ -320,6 +326,42 @@ export function describeArchiverDataStore(testName: string, getStore: () => Arch
});
});

describe('contractInstances', () => {
let contractInstance: ContractInstanceWithAddress;
const blockNum = 10;

beforeEach(async () => {
contractInstance = { ...SerializableContractInstance.random(), address: AztecAddress.random() };
await store.addContractInstances([contractInstance], blockNum);
});

it('returns previously stored contract instances', async () => {
await expect(store.getContractInstance(contractInstance.address)).resolves.toMatchObject(contractInstance);
});

it('returns undefined if contract instance is not found', async () => {
await expect(store.getContractInstance(AztecAddress.random())).resolves.toBeUndefined();
});
});

describe('contractClasses', () => {
let contractClass: ContractClassWithId;
const blockNum = 10;

beforeEach(async () => {
contractClass = { ...SerializableContractClass.random(), id: Fr.random() };
await store.addContractClasses([contractClass], blockNum);
});

it('returns previously stored contract class', async () => {
await expect(store.getContractClass(contractClass.id)).resolves.toMatchObject(contractClass);
});

it('returns undefined if contract class is not found', async () => {
await expect(store.getContractClass(Fr.random())).resolves.toBeUndefined();
});
});

describe('getContractData', () => {
let block: L2Block;
beforeEach(async () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ import { AztecAddress } from '@aztec/circuits.js';
import { createDebugLogger } from '@aztec/foundation/log';
import { AztecKVStore, AztecMap, Range } from '@aztec/kv-store';

/* eslint-disable */
type BlockIndexValue = [blockNumber: number, index: number];

type BlockContext = {
Expand All @@ -12,7 +11,6 @@ type BlockContext = {
block: Buffer;
blockHash: Buffer;
};
/* eslint-enable */

/**
* LMDB implementation of the ArchiverDataStore interface.
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import { Fr } from '@aztec/foundation/fields';
import { AztecKVStore, AztecMap } from '@aztec/kv-store';
import { ContractClassWithId, SerializableContractClass } from '@aztec/types/contracts';

/**
* LMDB implementation of the ArchiverDataStore interface.
*/
export class ContractClassStore {
#contractClasses: AztecMap<string, Buffer>;

constructor(db: AztecKVStore) {
this.#contractClasses = db.createMap('archiver_contract_classes');
}

addContractClass(contractClass: ContractClassWithId): Promise<boolean> {
return this.#contractClasses.set(
contractClass.id.toString(),
new SerializableContractClass(contractClass).toBuffer(),
);
}

getContractClass(id: Fr): ContractClassWithId | undefined {
const contractClass = this.#contractClasses.get(id.toString());
if (!contractClass) {
return undefined;
}
return { ...SerializableContractClass.fromBuffer(contractClass), id };
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import { AztecAddress } from '@aztec/circuits.js';
import { AztecKVStore, AztecMap } from '@aztec/kv-store';
import { ContractInstanceWithAddress, SerializableContractInstance } from '@aztec/types/contracts';

/**
* LMDB implementation of the ArchiverDataStore interface.
*/
export class ContractInstanceStore {
#contractInstances: AztecMap<string, Buffer>;

constructor(db: AztecKVStore) {
this.#contractInstances = db.createMap('archiver_contract_instances');
}

addContractInstance(contractInstance: ContractInstanceWithAddress): Promise<boolean> {
return this.#contractInstances.set(
contractInstance.address.toString(),
new SerializableContractInstance(contractInstance).toBuffer(),
);
}

getContractInstance(address: AztecAddress): ContractInstanceWithAddress | undefined {
const contractInstance = this.#contractInstances.get(address.toString());
if (!contractInstance) {
return undefined;
}
return { ...SerializableContractInstance.fromBuffer(contractInstance), address };
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,12 @@ import { Fr } from '@aztec/circuits.js';
import { AztecAddress } from '@aztec/foundation/aztec-address';
import { createDebugLogger } from '@aztec/foundation/log';
import { AztecKVStore } from '@aztec/kv-store';
import { ContractClassWithId, ContractInstanceWithAddress } from '@aztec/types/contracts';

import { ArchiverDataStore, ArchiverL1SynchPoint } from '../archiver_store.js';
import { BlockStore } from './block_store.js';
import { ContractClassStore } from './contract_class_store.js';
import { ContractInstanceStore } from './contract_instance_store.js';
import { ContractStore } from './contract_store.js';
import { LogStore } from './log_store.js';
import { MessageStore } from './message_store.js';
Expand All @@ -29,6 +32,8 @@ export class KVArchiverDataStore implements ArchiverDataStore {
#logStore: LogStore;
#contractStore: ContractStore;
#messageStore: MessageStore;
#contractClassStore: ContractClassStore;
#contractInstanceStore: ContractInstanceStore;

#log = createDebugLogger('aztec:archiver:lmdb');

Expand All @@ -37,6 +42,24 @@ export class KVArchiverDataStore implements ArchiverDataStore {
this.#logStore = new LogStore(db, this.#blockStore, logsMaxPageSize);
this.#contractStore = new ContractStore(db, this.#blockStore);
this.#messageStore = new MessageStore(db);
this.#contractClassStore = new ContractClassStore(db);
this.#contractInstanceStore = new ContractInstanceStore(db);
}

getContractClass(id: Fr): Promise<ContractClassWithId | undefined> {
return Promise.resolve(this.#contractClassStore.getContractClass(id));
}

getContractInstance(address: AztecAddress): Promise<ContractInstanceWithAddress | undefined> {
return Promise.resolve(this.#contractInstanceStore.getContractInstance(address));
}

async addContractClasses(data: ContractClassWithId[], _blockNumber: number): Promise<boolean> {
return (await Promise.all(data.map(c => this.#contractClassStore.addContractClass(c)))).every(Boolean);
}

async addContractInstances(data: ContractInstanceWithAddress[], _blockNumber: number): Promise<boolean> {
return (await Promise.all(data.map(c => this.#contractInstanceStore.addContractInstance(c)))).every(Boolean);
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import {
} from '@aztec/circuit-types';
import { Fr, NUMBER_OF_L1_L2_MESSAGES_PER_ROLLUP } from '@aztec/circuits.js';
import { AztecAddress } from '@aztec/foundation/aztec-address';
import { ContractClassWithId, ContractInstanceWithAddress } from '@aztec/types/contracts';

import { ArchiverDataStore } from '../archiver_store.js';
import { L1ToL2MessageStore, PendingL1ToL2MessageStore } from './l1_to_l2_message_store.js';
Expand Down Expand Up @@ -68,6 +69,10 @@ export class MemoryArchiverStore implements ArchiverDataStore {
*/
private pendingL1ToL2Messages: PendingL1ToL2MessageStore = new PendingL1ToL2MessageStore();

private contractClasses: Map<string, ContractClassWithId> = new Map();

private contractInstances: Map<string, ContractInstanceWithAddress> = new Map();

private lastL1BlockAddedMessages: bigint = 0n;
private lastL1BlockCancelledMessages: bigint = 0n;

Expand All @@ -76,6 +81,28 @@ export class MemoryArchiverStore implements ArchiverDataStore {
public readonly maxLogs: number,
) {}

public getContractClass(id: Fr): Promise<ContractClassWithId | undefined> {
return Promise.resolve(this.contractClasses.get(id.toString()));
}

public getContractInstance(address: AztecAddress): Promise<ContractInstanceWithAddress | undefined> {
return Promise.resolve(this.contractInstances.get(address.toString()));
}

public addContractClasses(data: ContractClassWithId[], _blockNumber: number): Promise<boolean> {
for (const contractClass of data) {
this.contractClasses.set(contractClass.id.toString(), contractClass);
}
return Promise.resolve(true);
}

public addContractInstances(data: ContractInstanceWithAddress[], _blockNumber: number): Promise<boolean> {
for (const contractInstance of data) {
this.contractInstances.set(contractInstance.address.toString(), contractInstance);
}
return Promise.resolve(true);
}

/**
* Append new blocks to the store's list.
* @param blocks - The L2 blocks to be added to the store.
Expand Down
3 changes: 3 additions & 0 deletions yarn-project/archiver/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,9 @@
},
{
"path": "../l1-artifacts"
},
{
"path": "../types"
}
],
"include": ["src"]
Expand Down
2 changes: 1 addition & 1 deletion yarn-project/circuit-types/src/contract_data.ts
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,7 @@ export class ExtendedContractData {
/** The base contract data: aztec & portal addresses. */
public contractData: ContractData,
/** Artifacts of public functions. */
private publicFunctions: EncodedContractFunction[],
public readonly publicFunctions: EncodedContractFunction[],
/** Partial addresses of the contract. */
public readonly partialAddress: PartialAddress,
/** Public key of the contract. */
Expand Down
Loading

0 comments on commit 9a98343

Please sign in to comment.