Skip to content

Commit

Permalink
Track instance deployed events in node
Browse files Browse the repository at this point in the history
  • Loading branch information
spalladino committed Feb 5, 2024
1 parent 6557c5e commit 22fc411
Show file tree
Hide file tree
Showing 8 changed files with 148 additions and 8 deletions.
44 changes: 42 additions & 2 deletions yarn-project/archiver/src/archiver/archiver.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import {
NUMBER_OF_L1_L2_MESSAGES_PER_ROLLUP,
REGISTERER_CONTRACT_CLASS_REGISTERED_MAGIC_VALUE,
} from '@aztec/circuits.js';
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';
Expand Down Expand Up @@ -73,12 +74,19 @@ export class Archiver implements ArchiveSource {
// TODO(@spalladino): Calculate this on the fly somewhere else.
// Today this is printed in the logs for end-to-end test at
// end-to-end/src/e2e_deploy_contract.test.ts -t 'registering a new contract class'
// as "Added contract ContractClassRegisterer ADDRESS"
// "Added contract ContractClassRegisterer ADDRESS"
// "Added contract ContractInstanceDeployer ADDRESS"

/** Address of the ClassRegisterer contract with a salt=1 */
private classRegistererAddress = AztecAddress.fromString(
'0x29c0cd0000951bba8af520ad5513cc53d9f0413c5a24a72a4ba8c17894c0bef9',
);

/** Address of the InstanceDeployer contract with a salt=1 */
private instanceDeployerAddress = AztecAddress.fromString(
'0x1799c61aa10430bf6fec46679c4cb76c3ed12cd8b6e73ed7389d5ae296ad1b97',
);

/**
* Creates a new instance of the Archiver.
* @param publicClient - A client for interacting with the Ethereum node.
Expand Down Expand Up @@ -288,13 +296,14 @@ export class Archiver implements ArchiveSource {
),
);

// Unroll all logs emitted during the retrieved blocks and extract any contract classes from them
// Unroll all logs emitted during the retrieved blocks and extract any contract classes and instances from them
await Promise.all(
retrievedBlocks.retrievedData.map(async block => {
const blockLogs = (block.newUnencryptedLogs?.txLogs ?? [])
.flatMap(txLog => txLog.unrollLogs())
.map(log => UnencryptedL2Log.fromBuffer(log));
await this.storeRegisteredContractClasses(blockLogs, block.number);
await this.storeDeployedContractInstances(blockLogs, block.number);
}),
);

Expand Down Expand Up @@ -355,6 +364,33 @@ export class Archiver implements ArchiveSource {
}
}

/**
* Extracts and stores contract instances out of ContractInstanceDeployed events emitted by the canonical deployer contract.
* @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}`);
}
}

if (contractInstances.length > 0) {
contractInstances.forEach(c => this.log(`Storing contract instance at ${c.address.toString()}`));
await this.store.addContractInstances(contractInstances, blockNum);
}
}

/**
* Stores extended contract data as classes and instances.
* Temporary solution until we source this data from the contract class registerer and instance deployer.
Expand Down Expand Up @@ -505,6 +541,10 @@ export class Archiver implements ArchiveSource {
return this.store.getContractClass(id);
}

public getContract(address: AztecAddress): Promise<ContractInstanceWithAddress | undefined> {
return this.store.getContractInstance(address);
}

/**
* Gets up to `limit` amount of pending L1 to L2 messages.
* @param limit - The number of messages to return.
Expand Down
6 changes: 5 additions & 1 deletion yarn-project/aztec-node/src/aztec-node/server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ import {
SequencerClient,
getGlobalVariableBuilder,
} from '@aztec/sequencer-client';
import { ContractClassPublic } from '@aztec/types/contracts';
import { ContractClassPublic, ContractInstanceWithAddress } from '@aztec/types/contracts';
import {
MerkleTrees,
ServerWorldStateSynchronizer,
Expand Down Expand Up @@ -242,6 +242,10 @@ export class AztecNodeService implements AztecNode {
return this.contractDataSource.getContractClass(id);
}

public getContract(address: AztecAddress): Promise<ContractInstanceWithAddress | undefined> {
return this.contractDataSource.getContract(address);
}

/**
* Gets up to `limit` amount of logs starting from `from`.
* @param from - Number of the L2 block to which corresponds the first logs to be returned.
Expand Down
8 changes: 7 additions & 1 deletion yarn-project/circuit-types/src/contract_data.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import {
serializeBufferArrayToVector,
serializeToBuffer,
} from '@aztec/foundation/serialize';
import { ContractClassPublic } from '@aztec/types/contracts';
import { ContractClassPublic, ContractInstanceWithAddress } from '@aztec/types/contracts';

/**
* Used for retrieval of contract data (A3 address, portal contract address, bytecode).
Expand Down Expand Up @@ -62,6 +62,12 @@ export interface ContractDataSource {
* @param id - Contract class id.
*/
getContractClass(id: Fr): Promise<ContractClassPublic | undefined>;

/**
* Returns a publicly deployed contract instance given its address.
* @param address - Address of the deployed contract.
*/
getContract(address: AztecAddress): Promise<ContractInstanceWithAddress | undefined>;
}

/**
Expand Down
8 changes: 7 additions & 1 deletion yarn-project/circuit-types/src/interfaces/aztec-node.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import {
import { L1ContractAddresses } from '@aztec/ethereum';
import { AztecAddress } from '@aztec/foundation/aztec-address';
import { Fr } from '@aztec/foundation/fields';
import { ContractClassPublic } from '@aztec/types/contracts';
import { ContractClassPublic, ContractInstanceWithAddress } from '@aztec/types/contracts';

import { ContractData, ExtendedContractData } from '../contract_data.js';
import { L1ToL2MessageAndIndex } from '../l1_to_l2_message.js';
Expand Down Expand Up @@ -286,4 +286,10 @@ export interface AztecNode {
* @param id - Id of the contract class.
*/
getContractClass(id: Fr): Promise<ContractClassPublic | undefined>;

/**
* Returns a publicly deployed contract instance given its address.
* @param address - Address of the deployed contract.
*/
getContract(address: AztecAddress): Promise<ContractInstanceWithAddress | undefined>;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
import { toBigIntBE } from '@aztec/foundation/bigint-buffer';
import { Fr } from '@aztec/foundation/fields';
import { BufferReader } from '@aztec/foundation/serialize';
import { ContractInstanceWithAddress } from '@aztec/types/contracts';

import { DEPLOYER_CONTRACT_INSTANCE_DEPLOYED_MAGIC_VALUE } from '../constants.gen.js';
import { AztecAddress, EthAddress } from '../index.js';

/** Event emitted from the ContractInstanceDeployer. */
export class ContractInstanceDeployedEvent {
constructor(
public readonly address: AztecAddress,
public readonly version: number,
public readonly salt: Fr,
public readonly contractClassId: Fr,
public readonly initializationHash: Fr,
public readonly portalContractAddress: EthAddress,
public readonly publicKeysHash: Fr,
public readonly universalDeploy: boolean,
) {}

static isContractInstanceDeployedEvent(log: Buffer) {
return toBigIntBE(log.subarray(0, 32)) == DEPLOYER_CONTRACT_INSTANCE_DEPLOYED_MAGIC_VALUE;
}

static fromLogData(log: Buffer) {
if (!this.isContractInstanceDeployedEvent(log)) {
const magicValue = DEPLOYER_CONTRACT_INSTANCE_DEPLOYED_MAGIC_VALUE.toString(16);
throw new Error(`Log data for ContractInstanceDeployedEvent is not prefixed with magic value 0x${magicValue}`);
}
const reader = new BufferReader(log.subarray(32));
const address = reader.readObject(AztecAddress);
const version = reader.readObject(Fr).toNumber();
const salt = reader.readObject(Fr);
const contractClassId = reader.readObject(Fr);
const initializationHash = reader.readObject(Fr);
const portalContractAddress = reader.readObject(EthAddress);
const publicKeysHash = reader.readObject(Fr);
const universalDeploy = reader.readObject(Fr).toBool();

return new ContractInstanceDeployedEvent(
address,
version,
salt,
contractClassId,
initializationHash,
portalContractAddress,
publicKeysHash,
universalDeploy,
);
}

toContractInstance(): ContractInstanceWithAddress {
if (this.version !== 1) {
throw new Error(`Unexpected contract instance version ${this.version}`);
}

return {
address: this.address,
version: this.version,
contractClassId: this.contractClassId,
initializationHash: this.initializationHash,
portalContractAddress: this.portalContractAddress,
publicKeysHash: this.publicKeysHash,
salt: this.salt,
};
}
}
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 @@ -7,3 +7,4 @@ export * from './contract_address.js';
export * from './private_function.js';
export * from './public_bytecode.js';
export * from './contract_class_registered_event.js';
export * from './contract_instance_deployed_event.js';
17 changes: 14 additions & 3 deletions yarn-project/end-to-end/src/e2e_deploy_contract.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -388,18 +388,29 @@ describe('e2e_deploy_contract', () => {
const deployer = await registerContract(wallet, ContractInstanceDeployerContract, [], new Fr(1));

const salt = Fr.random();
const initArgs = Fr.ZERO;
const portalAddress = EthAddress.random();
const publicKeysHash = computePublicKeysHash(Point.random());
const publicKey = Point.random();
const publicKeysHash = computePublicKeysHash(publicKey);

const expected = getContractInstanceFromDeployParams(artifact, [], salt, publicKey, portalAddress);

const tx = await deployer.methods
.deploy(salt, contractClass.id, initArgs, portalAddress, publicKeysHash, false)
.deploy(salt, contractClass.id, expected.initializationHash, portalAddress, publicKeysHash, false)
.send()
.wait();

const logs = await pxe.getUnencryptedLogs({ txHash: tx.txHash });
const deployedLog = logs.logs[0].log;
expect(deployedLog.contractAddress).toEqual(deployer.address);

const instance = await aztecNode.getContract(expected.address);
expect(instance).toBeDefined();
expect(instance!.address).toEqual(expected.address);
expect(instance!.contractClassId).toEqual(contractClass.id);
expect(instance!.initializationHash).toEqual(expected.initializationHash);
expect(instance!.portalContractAddress).toEqual(portalAddress);
expect(instance!.publicKeysHash).toEqual(publicKeysHash);
expect(instance!.salt).toEqual(salt);
});
});
});
Expand Down
4 changes: 4 additions & 0 deletions yarn-project/foundation/src/fields/fields.ts
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,10 @@ abstract class BaseField {
return this.asBigInt;
}

toBool(): boolean {
return Boolean(this.toBigInt());
}

toNumber(): number {
const value = this.toBigInt();
if (value > Number.MAX_SAFE_INTEGER) {
Expand Down

0 comments on commit 22fc411

Please sign in to comment.