@aztec/pxe 0.8.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +13 -0
- package/dest/bin/index.d.ts +3 -0
- package/dest/bin/index.d.ts.map +1 -0
- package/dest/bin/index.js +31 -0
- package/dest/config/index.d.ts +21 -0
- package/dest/config/index.d.ts.map +1 -0
- package/dest/config/index.js +21 -0
- package/dest/contract_data_oracle/index.d.ts +99 -0
- package/dest/contract_data_oracle/index.d.ts.map +1 -0
- package/dest/contract_data_oracle/index.js +137 -0
- package/dest/contract_database/index.d.ts +2 -0
- package/dest/contract_database/index.d.ts.map +1 -0
- package/dest/contract_database/index.js +2 -0
- package/dest/contract_database/memory_contract_database.d.ts +43 -0
- package/dest/contract_database/memory_contract_database.d.ts.map +1 -0
- package/dest/contract_database/memory_contract_database.js +51 -0
- package/dest/contract_tree/index.d.ts +107 -0
- package/dest/contract_tree/index.d.ts.map +1 -0
- package/dest/contract_tree/index.js +181 -0
- package/dest/database/database.d.ts +118 -0
- package/dest/database/database.d.ts.map +1 -0
- package/dest/database/database.js +2 -0
- package/dest/database/index.d.ts +4 -0
- package/dest/database/index.d.ts.map +1 -0
- package/dest/database/index.js +4 -0
- package/dest/database/memory_db.d.ts +45 -0
- package/dest/database/memory_db.d.ts.map +1 -0
- package/dest/database/memory_db.js +111 -0
- package/dest/database/note_spending_info_dao.d.ts +44 -0
- package/dest/database/note_spending_info_dao.d.ts.map +1 -0
- package/dest/database/note_spending_info_dao.js +14 -0
- package/dest/index.d.ts +10 -0
- package/dest/index.d.ts.map +1 -0
- package/dest/index.js +10 -0
- package/dest/kernel_oracle/index.d.ts +18 -0
- package/dest/kernel_oracle/index.d.ts.map +1 -0
- package/dest/kernel_oracle/index.js +29 -0
- package/dest/kernel_prover/index.d.ts +3 -0
- package/dest/kernel_prover/index.d.ts.map +1 -0
- package/dest/kernel_prover/index.js +3 -0
- package/dest/kernel_prover/kernel_prover.d.ts +89 -0
- package/dest/kernel_prover/kernel_prover.d.ts.map +1 -0
- package/dest/kernel_prover/kernel_prover.js +175 -0
- package/dest/kernel_prover/proof_creator.d.ts +84 -0
- package/dest/kernel_prover/proof_creator.d.ts.map +1 -0
- package/dest/kernel_prover/proof_creator.js +83 -0
- package/dest/kernel_prover/proving_data_oracle.d.ts +53 -0
- package/dest/kernel_prover/proving_data_oracle.d.ts.map +1 -0
- package/dest/kernel_prover/proving_data_oracle.js +2 -0
- package/dest/note_processor/index.d.ts +2 -0
- package/dest/note_processor/index.d.ts.map +1 -0
- package/dest/note_processor/index.js +2 -0
- package/dest/note_processor/note_processor.d.ts +78 -0
- package/dest/note_processor/note_processor.d.ts.map +1 -0
- package/dest/note_processor/note_processor.js +206 -0
- package/dest/pxe_http/index.d.ts +2 -0
- package/dest/pxe_http/index.d.ts.map +1 -0
- package/dest/pxe_http/index.js +2 -0
- package/dest/pxe_http/pxe_http_server.d.ts +40 -0
- package/dest/pxe_http/pxe_http_server.d.ts.map +1 -0
- package/dest/pxe_http/pxe_http_server.js +44 -0
- package/dest/pxe_service/create_pxe_service.d.ts +30 -0
- package/dest/pxe_service/create_pxe_service.d.ts.map +1 -0
- package/dest/pxe_service/create_pxe_service.js +27 -0
- package/dest/pxe_service/index.d.ts +4 -0
- package/dest/pxe_service/index.d.ts.map +1 -0
- package/dest/pxe_service/index.js +4 -0
- package/dest/pxe_service/pxe_service.d.ts +70 -0
- package/dest/pxe_service/pxe_service.d.ts.map +1 -0
- package/dest/pxe_service/pxe_service.js +470 -0
- package/dest/pxe_service/test/pxe_test_suite.d.ts +3 -0
- package/dest/pxe_service/test/pxe_test_suite.d.ts.map +1 -0
- package/dest/pxe_service/test/pxe_test_suite.js +100 -0
- package/dest/simulator/index.d.ts +9 -0
- package/dest/simulator/index.d.ts.map +1 -0
- package/dest/simulator/index.js +11 -0
- package/dest/simulator_oracle/index.d.ts +53 -0
- package/dest/simulator_oracle/index.d.ts.map +1 -0
- package/dest/simulator_oracle/index.js +91 -0
- package/dest/synchronizer/index.d.ts +2 -0
- package/dest/synchronizer/index.d.ts.map +1 -0
- package/dest/synchronizer/index.js +2 -0
- package/dest/synchronizer/synchronizer.d.ts +82 -0
- package/dest/synchronizer/synchronizer.d.ts.map +1 -0
- package/dest/synchronizer/synchronizer.js +241 -0
- package/package.json +74 -0
- package/src/bin/index.ts +39 -0
- package/src/config/index.ts +34 -0
- package/src/contract_data_oracle/index.ts +151 -0
- package/src/contract_database/index.ts +1 -0
- package/src/contract_database/memory_contract_database.ts +58 -0
- package/src/contract_tree/index.ts +245 -0
- package/src/database/database.ts +131 -0
- package/src/database/index.ts +3 -0
- package/src/database/memory_db.ts +147 -0
- package/src/database/note_spending_info_dao.ts +64 -0
- package/src/index.ts +11 -0
- package/src/kernel_oracle/index.ts +39 -0
- package/src/kernel_prover/index.ts +2 -0
- package/src/kernel_prover/kernel_prover.ts +317 -0
- package/src/kernel_prover/proof_creator.ts +176 -0
- package/src/kernel_prover/proving_data_oracle.ts +69 -0
- package/src/note_processor/index.ts +1 -0
- package/src/note_processor/note_processor.ts +267 -0
- package/src/pxe_http/index.ts +1 -0
- package/src/pxe_http/pxe_http_server.ts +70 -0
- package/src/pxe_service/create_pxe_service.ts +52 -0
- package/src/pxe_service/index.ts +3 -0
- package/src/pxe_service/pxe_service.ts +650 -0
- package/src/pxe_service/test/pxe_test_suite.ts +138 -0
- package/src/simulator/index.ts +24 -0
- package/src/simulator_oracle/index.ts +121 -0
- package/src/synchronizer/index.ts +1 -0
- package/src/synchronizer/synchronizer.ts +285 -0
package/src/bin/index.ts
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
#!/usr/bin/env -S node --no-warnings
|
|
2
|
+
import { createDebugLogger } from '@aztec/foundation/log';
|
|
3
|
+
import { createAztecNodeRpcClient } from '@aztec/types';
|
|
4
|
+
|
|
5
|
+
import { getPXEServiceConfig } from '../config/index.js';
|
|
6
|
+
import { startPXEHttpServer } from '../pxe_http/index.js';
|
|
7
|
+
import { createPXEService } from '../pxe_service/index.js';
|
|
8
|
+
|
|
9
|
+
const { PXE_PORT = 8080, AZTEC_NODE_URL = 'http://localhost:8079' } = process.env;
|
|
10
|
+
|
|
11
|
+
const logger = createDebugLogger('aztec:pxe_service');
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Create and start a new PXE HTTP Server
|
|
15
|
+
*/
|
|
16
|
+
async function main() {
|
|
17
|
+
logger.info(`Setting up PXE...`);
|
|
18
|
+
|
|
19
|
+
const pxeConfig = getPXEServiceConfig();
|
|
20
|
+
const nodeRpcClient = createAztecNodeRpcClient(AZTEC_NODE_URL);
|
|
21
|
+
const pxeService = await createPXEService(nodeRpcClient, pxeConfig);
|
|
22
|
+
|
|
23
|
+
const shutdown = async () => {
|
|
24
|
+
logger.info('Shutting down...');
|
|
25
|
+
await pxeService.stop();
|
|
26
|
+
process.exit(0);
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
process.once('SIGINT', shutdown);
|
|
30
|
+
process.once('SIGTERM', shutdown);
|
|
31
|
+
|
|
32
|
+
startPXEHttpServer(pxeService, PXE_PORT);
|
|
33
|
+
logger.info(`PXE listening on port ${PXE_PORT}`);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
main().catch(err => {
|
|
37
|
+
logger.error(err);
|
|
38
|
+
process.exit(1);
|
|
39
|
+
});
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { readFileSync } from 'fs';
|
|
2
|
+
import { dirname, resolve } from 'path';
|
|
3
|
+
import { fileURLToPath } from 'url';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Configuration settings for the PXE Service.
|
|
7
|
+
*/
|
|
8
|
+
export interface PXEServiceConfig {
|
|
9
|
+
/**
|
|
10
|
+
* The interval to wait between polling for new blocks.
|
|
11
|
+
*/
|
|
12
|
+
l2BlockPollingIntervalMS: number;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Creates an instance of PXEServiceConfig out of environment variables using sensible defaults for integration testing if not set.
|
|
17
|
+
*/
|
|
18
|
+
export function getPXEServiceConfig(): PXEServiceConfig {
|
|
19
|
+
const { PXE_BLOCK_POLLING_INTERVAL_MS } = process.env;
|
|
20
|
+
|
|
21
|
+
return {
|
|
22
|
+
l2BlockPollingIntervalMS: PXE_BLOCK_POLLING_INTERVAL_MS ? +PXE_BLOCK_POLLING_INTERVAL_MS : 1000,
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Returns package name and version.
|
|
28
|
+
*/
|
|
29
|
+
export function getPackageInfo() {
|
|
30
|
+
const packageJsonPath = resolve(dirname(fileURLToPath(import.meta.url)), '../../package.json');
|
|
31
|
+
const { version, name } = JSON.parse(readFileSync(packageJsonPath).toString());
|
|
32
|
+
|
|
33
|
+
return { version, name };
|
|
34
|
+
}
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
import { AztecAddress, CircuitsWasm, MembershipWitness, VK_TREE_HEIGHT } from '@aztec/circuits.js';
|
|
2
|
+
import { FunctionDebugMetadata, FunctionSelector, getFunctionDebugMetadata } from '@aztec/foundation/abi';
|
|
3
|
+
import { ContractDatabase, StateInfoProvider } from '@aztec/types';
|
|
4
|
+
|
|
5
|
+
import { ContractTree } from '../contract_tree/index.js';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* ContractDataOracle serves as a data manager and retriever for Aztec.nr contracts.
|
|
9
|
+
* It provides methods to obtain contract addresses, function ABI, bytecode, and membership witnesses
|
|
10
|
+
* from a given contract address and function selector. The class maintains a cache of ContractTree instances
|
|
11
|
+
* to efficiently serve the requested data. It interacts with the ContractDatabase and AztecNode to fetch
|
|
12
|
+
* the required information and facilitate cryptographic proof generation.
|
|
13
|
+
*/
|
|
14
|
+
export class ContractDataOracle {
|
|
15
|
+
private trees: ContractTree[] = [];
|
|
16
|
+
|
|
17
|
+
constructor(private db: ContractDatabase, private stateProvider: StateInfoProvider) {}
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Retrieve the portal contract address associated with the given contract address.
|
|
21
|
+
* This function searches for the corresponding contract tree in the local cache and returns the portal contract address.
|
|
22
|
+
* If the contract tree is not found in the cache, it fetches the contract data from the database and creates a new ContractTree instance.
|
|
23
|
+
* Throws an error if the contract address is not found in the database.
|
|
24
|
+
*
|
|
25
|
+
* @param contractAddress - The AztecAddress of the contract whose portal contract address needs to be retrieved.
|
|
26
|
+
* @returns A Promise that resolves to the portal contract address.
|
|
27
|
+
*/
|
|
28
|
+
public async getPortalContractAddress(contractAddress: AztecAddress) {
|
|
29
|
+
const tree = await this.getTree(contractAddress);
|
|
30
|
+
return tree.contract.portalContract;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Retrieves the ABI of a specified function within a given contract.
|
|
35
|
+
* The function is identified by its selector, which is a unique code generated from the function's signature.
|
|
36
|
+
* Throws an error if the contract address or function selector are invalid or not found.
|
|
37
|
+
*
|
|
38
|
+
* @param contractAddress - The AztecAddress representing the contract containing the function.
|
|
39
|
+
* @param selector - The function selector.
|
|
40
|
+
* @returns The corresponding function's ABI as an object.
|
|
41
|
+
*/
|
|
42
|
+
public async getFunctionAbi(contractAddress: AztecAddress, selector: FunctionSelector) {
|
|
43
|
+
const tree = await this.getTree(contractAddress);
|
|
44
|
+
return tree.getFunctionAbi(selector);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Retrieves the debug metadata of a specified function within a given contract.
|
|
49
|
+
* The function is identified by its selector, which is a unique code generated from the function's signature.
|
|
50
|
+
* Returns undefined if the debug metadata for the given function is not found.
|
|
51
|
+
*
|
|
52
|
+
* @param contractAddress - The AztecAddress representing the contract containing the function.
|
|
53
|
+
* @param selector - The function selector.
|
|
54
|
+
* @returns The corresponding function's ABI as an object.
|
|
55
|
+
*/
|
|
56
|
+
public async getFunctionDebugMetadata(
|
|
57
|
+
contractAddress: AztecAddress,
|
|
58
|
+
selector: FunctionSelector,
|
|
59
|
+
): Promise<FunctionDebugMetadata | undefined> {
|
|
60
|
+
const contract = await this.db.getContract(contractAddress);
|
|
61
|
+
const functionAbi = contract?.functions.find(f => f.selector.equals(selector));
|
|
62
|
+
|
|
63
|
+
if (!contract || !functionAbi) {
|
|
64
|
+
return undefined;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
return getFunctionDebugMetadata(contract, functionAbi.name);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Retrieve the bytecode of a specific function in a contract at the given address.
|
|
72
|
+
* The returned bytecode is required for executing and verifying the function's behavior
|
|
73
|
+
* in the Aztec network. Throws an error if the contract or function cannot be found.
|
|
74
|
+
*
|
|
75
|
+
* @param contractAddress - The contract's address.
|
|
76
|
+
* @param selector - The function selector.
|
|
77
|
+
* @returns A Promise that resolves to a Buffer containing the bytecode of the specified function.
|
|
78
|
+
*/
|
|
79
|
+
public async getBytecode(contractAddress: AztecAddress, selector: FunctionSelector) {
|
|
80
|
+
const tree = await this.getTree(contractAddress);
|
|
81
|
+
return tree.getBytecode(selector);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Retrieves the contract membership witness for a given contract address.
|
|
86
|
+
* A contract membership witness is a cryptographic proof that the contract exists in the Aztec network.
|
|
87
|
+
* This function will search for an existing contract tree associated with the contract address and obtain its
|
|
88
|
+
* membership witness. If no such contract tree exists, it will throw an error.
|
|
89
|
+
*
|
|
90
|
+
* @param contractAddress - The contract address.
|
|
91
|
+
* @returns A promise that resolves to a MembershipWitness instance representing the contract membership witness.
|
|
92
|
+
* @throws Error if the contract address is unknown or not found.
|
|
93
|
+
*/
|
|
94
|
+
public async getContractMembershipWitness(contractAddress: AztecAddress) {
|
|
95
|
+
const tree = await this.getTree(contractAddress);
|
|
96
|
+
return tree.getContractMembershipWitness();
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* Retrieve the function membership witness for the given contract address and function selector.
|
|
101
|
+
* The function membership witness represents a proof that the function belongs to the specified contract.
|
|
102
|
+
* Throws an error if the contract address or function selector is unknown.
|
|
103
|
+
*
|
|
104
|
+
* @param contractAddress - The contract address.
|
|
105
|
+
* @param selector - The function selector.
|
|
106
|
+
* @returns A promise that resolves with the MembershipWitness instance for the specified contract's function.
|
|
107
|
+
*/
|
|
108
|
+
public async getFunctionMembershipWitness(contractAddress: AztecAddress, selector: FunctionSelector) {
|
|
109
|
+
const tree = await this.getTree(contractAddress);
|
|
110
|
+
return tree.getFunctionMembershipWitness(selector);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* Retrieve the membership witness corresponding to a verification key.
|
|
115
|
+
* This function currently returns a random membership witness of the specified height,
|
|
116
|
+
* which is a placeholder implementation until a concrete membership witness calculation
|
|
117
|
+
* is implemented.
|
|
118
|
+
*
|
|
119
|
+
* @param vk - The VerificationKey for which the membership witness is needed.
|
|
120
|
+
* @returns A Promise that resolves to the MembershipWitness instance.
|
|
121
|
+
*/
|
|
122
|
+
public async getVkMembershipWitness() {
|
|
123
|
+
// TODO
|
|
124
|
+
return await Promise.resolve(MembershipWitness.random(VK_TREE_HEIGHT));
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* Retrieve or create a ContractTree instance based on the provided AztecAddress.
|
|
129
|
+
* If an existing tree with the same contract address is found in the cache, it will be returned.
|
|
130
|
+
* Otherwise, a new ContractTree instance will be created using the contract data from the database
|
|
131
|
+
* and added to the cache before returning.
|
|
132
|
+
*
|
|
133
|
+
* @param contractAddress - The AztecAddress of the contract for which the ContractTree is required.
|
|
134
|
+
* @returns A ContractTree instance associated with the specified contract address.
|
|
135
|
+
* @throws An Error if the contract is not found in the ContractDatabase.
|
|
136
|
+
*/
|
|
137
|
+
private async getTree(contractAddress: AztecAddress) {
|
|
138
|
+
let tree = this.trees.find(t => t.contract.completeAddress.address.equals(contractAddress));
|
|
139
|
+
if (!tree) {
|
|
140
|
+
const contract = await this.db.getContract(contractAddress);
|
|
141
|
+
if (!contract) {
|
|
142
|
+
throw new Error(`Unknown contract: ${contractAddress}`);
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
const wasm = await CircuitsWasm.get();
|
|
146
|
+
tree = new ContractTree(contract, this.stateProvider, wasm);
|
|
147
|
+
this.trees.push(tree);
|
|
148
|
+
}
|
|
149
|
+
return tree;
|
|
150
|
+
}
|
|
151
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './memory_contract_database.js';
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { FunctionSelector } from '@aztec/circuits.js';
|
|
2
|
+
import { AztecAddress } from '@aztec/foundation/aztec-address';
|
|
3
|
+
import { DebugLogger } from '@aztec/foundation/log';
|
|
4
|
+
import { ContractDao, ContractDatabase } from '@aztec/types';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* The MemoryContractDatabase class serves as an in-memory implementation of the ContractDatabase interface.
|
|
8
|
+
* It allows for storing and retrieving contract data, such as ContractDao objects and associated function bytecodes,
|
|
9
|
+
* within a contracts array. This class is particularly useful for testing and development purposes where a
|
|
10
|
+
* persistent storage may not be required.
|
|
11
|
+
*/
|
|
12
|
+
export class MemoryContractDatabase implements ContractDatabase {
|
|
13
|
+
private contracts: ContractDao[] = [];
|
|
14
|
+
|
|
15
|
+
constructor(protected log: DebugLogger) {}
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Adds a new ContractDao instance to the memory-based contract database.
|
|
19
|
+
* The function stores the contract in an array and returns a resolved promise indicating successful addition.
|
|
20
|
+
*
|
|
21
|
+
* @param contract - The ContractDao instance to be added to the memory database.
|
|
22
|
+
* @returns A Promise that resolves when the contract is successfully added.
|
|
23
|
+
*/
|
|
24
|
+
public addContract(contract: ContractDao) {
|
|
25
|
+
this.log(`Adding contract ${contract.completeAddress.address.toString()}`);
|
|
26
|
+
this.contracts.push(contract);
|
|
27
|
+
return Promise.resolve();
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Retrieve a ContractDao instance with the specified AztecAddress from the in-memory contracts list.
|
|
32
|
+
* Returns the first match found or undefined if no contract with the given address is found.
|
|
33
|
+
*
|
|
34
|
+
* @param address - The AztecAddress to search for in the stored contracts.
|
|
35
|
+
* @returns A Promise resolving to the ContractDao instance matching the given address or undefined.
|
|
36
|
+
*/
|
|
37
|
+
public getContract(address: AztecAddress): Promise<ContractDao | undefined> {
|
|
38
|
+
return Promise.resolve(this.contracts.find(c => c.completeAddress.address.equals(address)));
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
public getContracts(): Promise<ContractDao[]> {
|
|
42
|
+
return Promise.resolve(this.contracts);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Retrieve the bytecode associated with a given contract address and function selector.
|
|
47
|
+
* This function searches through the stored contracts to find a matching contract and function,
|
|
48
|
+
* then returns the corresponding bytecode. If no match is found, it returns undefined.
|
|
49
|
+
*
|
|
50
|
+
* @param contractAddress - The AztecAddress representing the contract address to look for.
|
|
51
|
+
* @param selector - The function selector.
|
|
52
|
+
* @returns A Promise that resolves to the bytecode of the matching function or undefined if not found.
|
|
53
|
+
*/
|
|
54
|
+
public async getCode(contractAddress: AztecAddress, selector: FunctionSelector) {
|
|
55
|
+
const contract = await this.getContract(contractAddress);
|
|
56
|
+
return contract?.functions.find(f => f.selector.equals(selector))?.bytecode;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
@@ -0,0 +1,245 @@
|
|
|
1
|
+
import {
|
|
2
|
+
CONTRACT_TREE_HEIGHT,
|
|
3
|
+
CircuitsWasm,
|
|
4
|
+
EthAddress,
|
|
5
|
+
FUNCTION_TREE_HEIGHT,
|
|
6
|
+
Fr,
|
|
7
|
+
FunctionData,
|
|
8
|
+
MembershipWitness,
|
|
9
|
+
NewContractConstructor,
|
|
10
|
+
NewContractData,
|
|
11
|
+
computeFunctionTree,
|
|
12
|
+
computeFunctionTreeData,
|
|
13
|
+
generateFunctionLeaves,
|
|
14
|
+
hashVKStr,
|
|
15
|
+
isConstrained,
|
|
16
|
+
isConstructor,
|
|
17
|
+
} from '@aztec/circuits.js';
|
|
18
|
+
import {
|
|
19
|
+
computeCompleteAddress,
|
|
20
|
+
computeContractLeaf,
|
|
21
|
+
computeFunctionTreeRoot,
|
|
22
|
+
computeVarArgsHash,
|
|
23
|
+
hashConstructor,
|
|
24
|
+
} from '@aztec/circuits.js/abis';
|
|
25
|
+
import { ContractAbi, FunctionSelector } from '@aztec/foundation/abi';
|
|
26
|
+
import { assertLength } from '@aztec/foundation/serialize';
|
|
27
|
+
import { AztecNode, ContractDao, MerkleTreeId, PublicKey, StateInfoProvider } from '@aztec/types';
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* The ContractTree class represents a Merkle tree of functions for a particular contract.
|
|
31
|
+
* It manages the construction of the function tree, computes its root, and generates membership witnesses
|
|
32
|
+
* for constrained functions. This class also enables lookup of specific function ABI and bytecode using selectors.
|
|
33
|
+
* It is used in combination with the AztecNode to compute various data for executing private transactions.
|
|
34
|
+
*/
|
|
35
|
+
export class ContractTree {
|
|
36
|
+
private functionLeaves?: Fr[];
|
|
37
|
+
private functionTree?: Fr[];
|
|
38
|
+
private functionTreeRoot?: Fr;
|
|
39
|
+
private contractIndex?: bigint;
|
|
40
|
+
|
|
41
|
+
constructor(
|
|
42
|
+
/**
|
|
43
|
+
* The contract data object containing the ABI and contract address.
|
|
44
|
+
*/
|
|
45
|
+
public readonly contract: ContractDao,
|
|
46
|
+
private stateInfoProvider: StateInfoProvider,
|
|
47
|
+
private wasm: CircuitsWasm,
|
|
48
|
+
/**
|
|
49
|
+
* Data associated with the contract constructor for a new contract.
|
|
50
|
+
*/
|
|
51
|
+
public readonly newContractConstructor?: NewContractConstructor,
|
|
52
|
+
) {}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Create a new ContractTree instance from the provided contract ABI, constructor arguments, and related data.
|
|
56
|
+
* The function generates function leaves for constrained functions, computes the function tree root,
|
|
57
|
+
* and hashes the constructor's verification key. It then computes the contract address using the contract
|
|
58
|
+
* and portal contract addresses, contract address salt, and generated data. Finally, it returns a new
|
|
59
|
+
* ContractTree instance containing the contract data and computed values.
|
|
60
|
+
*
|
|
61
|
+
* @param abi - The contract's ABI containing the functions and their metadata.
|
|
62
|
+
* @param args - An array of Fr elements representing the constructor's arguments.
|
|
63
|
+
* @param portalContract - The Ethereum address of the portal smart contract.
|
|
64
|
+
* @param contractAddressSalt - An Fr element representing the salt used to compute the contract address.
|
|
65
|
+
* @param from - The public key of the contract deployer.
|
|
66
|
+
* @param node - An instance of the AztecNode class representing the current node.
|
|
67
|
+
* @returns A new ContractTree instance containing the contract data and computed values.
|
|
68
|
+
*/
|
|
69
|
+
public static async new(
|
|
70
|
+
abi: ContractAbi,
|
|
71
|
+
args: Fr[],
|
|
72
|
+
portalContract: EthAddress,
|
|
73
|
+
contractAddressSalt: Fr,
|
|
74
|
+
from: PublicKey,
|
|
75
|
+
node: AztecNode,
|
|
76
|
+
) {
|
|
77
|
+
const wasm = await CircuitsWasm.get();
|
|
78
|
+
const constructorAbi = abi.functions.find(isConstructor);
|
|
79
|
+
if (!constructorAbi) {
|
|
80
|
+
throw new Error('Constructor not found.');
|
|
81
|
+
}
|
|
82
|
+
if (!constructorAbi.verificationKey) {
|
|
83
|
+
throw new Error('Missing verification key for the constructor.');
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
const functions = abi.functions.map(f => ({
|
|
87
|
+
...f,
|
|
88
|
+
selector: FunctionSelector.fromNameAndParameters(f.name, f.parameters),
|
|
89
|
+
}));
|
|
90
|
+
const leaves = generateFunctionLeaves(functions, wasm);
|
|
91
|
+
const root = computeFunctionTreeRoot(wasm, leaves);
|
|
92
|
+
const functionData = FunctionData.fromAbi(constructorAbi);
|
|
93
|
+
const vkHash = hashVKStr(constructorAbi.verificationKey, wasm);
|
|
94
|
+
const argsHash = await computeVarArgsHash(wasm, args);
|
|
95
|
+
const constructorHash = hashConstructor(wasm, functionData, argsHash, vkHash);
|
|
96
|
+
|
|
97
|
+
const completeAddress = computeCompleteAddress(wasm, from, contractAddressSalt, root, constructorHash);
|
|
98
|
+
|
|
99
|
+
const contractDao: ContractDao = {
|
|
100
|
+
...abi,
|
|
101
|
+
completeAddress,
|
|
102
|
+
functions,
|
|
103
|
+
portalContract,
|
|
104
|
+
};
|
|
105
|
+
const NewContractConstructor = {
|
|
106
|
+
functionData,
|
|
107
|
+
vkHash,
|
|
108
|
+
};
|
|
109
|
+
return new ContractTree(contractDao, node, wasm, NewContractConstructor);
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* Retrieve the ABI of a given function.
|
|
114
|
+
* The function is identified by its selector, which represents a unique identifier for the function's signature.
|
|
115
|
+
* Throws an error if the function with the provided selector is not found in the contract.
|
|
116
|
+
*
|
|
117
|
+
* @param selector - The function selector.
|
|
118
|
+
* @returns The ABI object containing relevant information about the targeted function.
|
|
119
|
+
*/
|
|
120
|
+
public getFunctionAbi(selector: FunctionSelector) {
|
|
121
|
+
const abi = this.contract.functions.find(f => f.selector.equals(selector));
|
|
122
|
+
if (!abi) {
|
|
123
|
+
throw new Error(
|
|
124
|
+
`Unknown function. Selector ${selector.toString()} not found in the ABI of contract ${this.contract.completeAddress.address.toString()}. Expected one of: ${this.contract.functions
|
|
125
|
+
.map(f => f.selector.toString())
|
|
126
|
+
.join(', ')}`,
|
|
127
|
+
);
|
|
128
|
+
}
|
|
129
|
+
return abi;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
/**
|
|
133
|
+
* Retrieve the bytecode of a function in the contract by its function selector.
|
|
134
|
+
* The function selector is a unique identifier for each function in a contract.
|
|
135
|
+
* Throws an error if the function with the given selector is not found in the contract.
|
|
136
|
+
*
|
|
137
|
+
* @param selector - The selector of a function to get bytecode for.
|
|
138
|
+
* @returns The bytecode of the function as a string.
|
|
139
|
+
*/
|
|
140
|
+
public getBytecode(selector: FunctionSelector) {
|
|
141
|
+
return this.getFunctionAbi(selector).bytecode;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
/**
|
|
145
|
+
* Retrieves the contract membership witness for the current contract tree instance.
|
|
146
|
+
* The contract membership witness is a proof that demonstrates the existence of the contract
|
|
147
|
+
* in the global contract merkle tree. This proof contains the index of the contract's leaf
|
|
148
|
+
* in the tree and the sibling path needed to construct the root of the merkle tree.
|
|
149
|
+
* If the witness hasn't been previously computed, this function will request the contract node
|
|
150
|
+
* to find the contract's index and path in order to create the membership witness.
|
|
151
|
+
*
|
|
152
|
+
* @returns A Promise that resolves to the MembershipWitness object for the given contract tree.
|
|
153
|
+
*/
|
|
154
|
+
public async getContractMembershipWitness() {
|
|
155
|
+
const index = await this.getContractIndex();
|
|
156
|
+
|
|
157
|
+
const siblingPath = await this.stateInfoProvider.getContractPath(index);
|
|
158
|
+
return new MembershipWitness<typeof CONTRACT_TREE_HEIGHT>(
|
|
159
|
+
CONTRACT_TREE_HEIGHT,
|
|
160
|
+
index,
|
|
161
|
+
assertLength(siblingPath.toFieldArray(), CONTRACT_TREE_HEIGHT),
|
|
162
|
+
);
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
/**
|
|
166
|
+
* Calculate and return the root of the function tree for the current contract.
|
|
167
|
+
* This root is a cryptographic commitment to the set of constrained functions within the contract,
|
|
168
|
+
* which is used in the Aztec node's proof system. The root will be cached after the first call.
|
|
169
|
+
*
|
|
170
|
+
* @returns A promise that resolves to the Fr (finite field element) representation of the function tree root.
|
|
171
|
+
*/
|
|
172
|
+
public getFunctionTreeRoot() {
|
|
173
|
+
if (!this.functionTreeRoot) {
|
|
174
|
+
const leaves = this.getFunctionLeaves();
|
|
175
|
+
this.functionTreeRoot = computeFunctionTreeRoot(this.wasm, leaves);
|
|
176
|
+
}
|
|
177
|
+
return Promise.resolve(this.functionTreeRoot);
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
/**
|
|
181
|
+
* Retrieve the membership witness of a function within a contract's function tree.
|
|
182
|
+
* A membership witness represents the position and authentication path of a target function
|
|
183
|
+
* in the Merkle tree of constrained functions. It is required to prove the existence of the
|
|
184
|
+
* function within the contract during execution.
|
|
185
|
+
*
|
|
186
|
+
* @param selector - The function selector.
|
|
187
|
+
* @returns A MembershipWitness instance representing the position and authentication path of the function in the function tree.
|
|
188
|
+
*/
|
|
189
|
+
public getFunctionMembershipWitness(
|
|
190
|
+
selector: FunctionSelector,
|
|
191
|
+
): Promise<MembershipWitness<typeof FUNCTION_TREE_HEIGHT>> {
|
|
192
|
+
const targetFunctions = this.contract.functions.filter(isConstrained);
|
|
193
|
+
const functionIndex = targetFunctions.findIndex(f => f.selector.equals(selector));
|
|
194
|
+
if (functionIndex < 0) {
|
|
195
|
+
return Promise.resolve(MembershipWitness.empty(FUNCTION_TREE_HEIGHT, 0n));
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
if (!this.functionTree) {
|
|
199
|
+
const leaves = this.getFunctionLeaves();
|
|
200
|
+
this.functionTree = computeFunctionTree(this.wasm, leaves);
|
|
201
|
+
}
|
|
202
|
+
const functionTreeData = computeFunctionTreeData(this.functionTree, functionIndex);
|
|
203
|
+
return Promise.resolve(
|
|
204
|
+
new MembershipWitness<typeof FUNCTION_TREE_HEIGHT>(
|
|
205
|
+
FUNCTION_TREE_HEIGHT,
|
|
206
|
+
BigInt(functionIndex),
|
|
207
|
+
assertLength(functionTreeData.siblingPath, FUNCTION_TREE_HEIGHT),
|
|
208
|
+
),
|
|
209
|
+
);
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
/**
|
|
213
|
+
* Retrieve the function leaves for the contract tree.
|
|
214
|
+
* Function leaves are computed based on constrained functions present in the contract.
|
|
215
|
+
* It caches the computed function leaves and returns them if already calculated.
|
|
216
|
+
*
|
|
217
|
+
* @returns An array of Fr representing the function leaves.
|
|
218
|
+
*/
|
|
219
|
+
private getFunctionLeaves() {
|
|
220
|
+
if (!this.functionLeaves) {
|
|
221
|
+
this.functionLeaves = generateFunctionLeaves(this.contract.functions, this.wasm);
|
|
222
|
+
}
|
|
223
|
+
return this.functionLeaves;
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
private async getContractIndex() {
|
|
227
|
+
if (this.contractIndex === undefined) {
|
|
228
|
+
const { completeAddress, portalContract } = this.contract;
|
|
229
|
+
const root = await this.getFunctionTreeRoot();
|
|
230
|
+
const newContractData = new NewContractData(completeAddress.address, portalContract, root);
|
|
231
|
+
const commitment = computeContractLeaf(this.wasm, newContractData);
|
|
232
|
+
this.contractIndex = await this.stateInfoProvider.findLeafIndex(
|
|
233
|
+
MerkleTreeId.CONTRACT_TREE,
|
|
234
|
+
commitment.toBuffer(),
|
|
235
|
+
);
|
|
236
|
+
if (this.contractIndex === undefined) {
|
|
237
|
+
throw new Error(
|
|
238
|
+
`Failed to find contract at ${completeAddress.address} with portal ${portalContract} resulting in commitment ${commitment}.`,
|
|
239
|
+
);
|
|
240
|
+
}
|
|
241
|
+
return this.contractIndex;
|
|
242
|
+
}
|
|
243
|
+
return this.contractIndex;
|
|
244
|
+
}
|
|
245
|
+
}
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
import { CompleteAddress, HistoricBlockData } from '@aztec/circuits.js';
|
|
2
|
+
import { AztecAddress } from '@aztec/foundation/aztec-address';
|
|
3
|
+
import { Fr } from '@aztec/foundation/fields';
|
|
4
|
+
import { ContractDatabase, MerkleTreeId, PublicKey } from '@aztec/types';
|
|
5
|
+
|
|
6
|
+
import { NoteSpendingInfoDao } from './note_spending_info_dao.js';
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* A database interface that provides methods for retrieving, adding, and removing transactional data related to Aztec
|
|
10
|
+
* addresses, storage slots, and nullifiers.
|
|
11
|
+
*/
|
|
12
|
+
export interface Database extends ContractDatabase {
|
|
13
|
+
/**
|
|
14
|
+
* Add a auth witness to the database.
|
|
15
|
+
* @param messageHash - The message hash.
|
|
16
|
+
* @param witness - An array of field elements representing the auth witness.
|
|
17
|
+
*/
|
|
18
|
+
addAuthWitness(messageHash: Fr, witness: Fr[]): Promise<void>;
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Fetching the auth witness for a given message hash.
|
|
22
|
+
* @param messageHash - The message hash.
|
|
23
|
+
* @returns A Promise that resolves to an array of field elements representing the auth witness.
|
|
24
|
+
*/
|
|
25
|
+
getAuthWitness(messageHash: Fr): Promise<Fr[]>;
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Get auxiliary transaction data based on contract address and storage slot.
|
|
29
|
+
* It searches for matching NoteSpendingInfoDao objects in the MemoryDB's noteSpendingInfoTable
|
|
30
|
+
* where both the contractAddress and storageSlot properties match the given inputs.
|
|
31
|
+
*
|
|
32
|
+
* @param contract - The contract address.
|
|
33
|
+
* @param storageSlot - A Fr object representing the storage slot to search for in the auxiliary data.
|
|
34
|
+
* @returns An array of NoteSpendingInfoDao objects that fulfill the contract address and storage slot criteria.
|
|
35
|
+
*/
|
|
36
|
+
getNoteSpendingInfo(contract: AztecAddress, storageSlot: Fr): Promise<NoteSpendingInfoDao[]>;
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Add a NoteSpendingInfoDao instance to the noteSpendingInfoTable.
|
|
40
|
+
* This function is used to store auxiliary data related to a transaction,
|
|
41
|
+
* such as contract address and storage slot, in the database.
|
|
42
|
+
*
|
|
43
|
+
* @param noteSpendingInfoDao - The NoteSpendingInfoDao instance containing the auxiliary data of a transaction.
|
|
44
|
+
* @returns A promise that resolves when the auxiliary data is added to the database.
|
|
45
|
+
*/
|
|
46
|
+
addNoteSpendingInfo(noteSpendingInfoDao: NoteSpendingInfoDao): Promise<void>;
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Adds an array of NoteSpendingInfoDaos to the noteSpendingInfoTable.
|
|
50
|
+
* This function is used to insert multiple transaction auxiliary data objects into the database at once,
|
|
51
|
+
* which can improve performance when dealing with large numbers of transactions.
|
|
52
|
+
*
|
|
53
|
+
* @param noteSpendingInfoDaos - An array of NoteSpendingInfoDao instances representing the auxiliary data of transactions.
|
|
54
|
+
* @returns A Promise that resolves when all NoteSpendingInfoDaos have been successfully added to the noteSpendingInfoTable.
|
|
55
|
+
*/
|
|
56
|
+
addNoteSpendingInfoBatch(noteSpendingInfoDaos: NoteSpendingInfoDao[]): Promise<void>;
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Remove nullified transaction auxiliary data records associated with the given account and nullifiers.
|
|
60
|
+
* The function filters the records based on matching account and nullifier values, and updates the
|
|
61
|
+
* noteSpendingInfoTable with the remaining records. It returns an array of removed NoteSpendingInfoDao instances.
|
|
62
|
+
*
|
|
63
|
+
* @param nullifiers - An array of Fr instances representing nullifiers to be matched.
|
|
64
|
+
* @param account - A PublicKey instance representing the account for which the records are being removed.
|
|
65
|
+
* @returns A Promise resolved with an array of removed NoteSpendingInfoDao instances.
|
|
66
|
+
*/
|
|
67
|
+
removeNullifiedNoteSpendingInfo(nullifiers: Fr[], account: PublicKey): Promise<NoteSpendingInfoDao[]>;
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Retrieve the stored Merkle tree roots from the database.
|
|
71
|
+
* The function returns a Promise that resolves to an object containing the MerkleTreeId as keys
|
|
72
|
+
* and their corresponding Fr values as roots. Throws an error if the tree roots are not set in the
|
|
73
|
+
* memory database.
|
|
74
|
+
*
|
|
75
|
+
* @returns An object containing the Merkle tree roots for each merkle tree id.
|
|
76
|
+
*/
|
|
77
|
+
getTreeRoots(): Record<MerkleTreeId, Fr>;
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Set the tree roots for the Merkle trees in the database.
|
|
81
|
+
* This function updates the 'treeRoots' property of the instance
|
|
82
|
+
* with the provided 'roots' object containing MerkleTreeId and Fr pairs.
|
|
83
|
+
* Note that this will overwrite any existing tree roots in the database.
|
|
84
|
+
*
|
|
85
|
+
* @param roots - A Record object mapping MerkleTreeIds to their corresponding Fr root values.
|
|
86
|
+
* @returns A Promise that resolves when the tree roots have been successfully updated in the database.
|
|
87
|
+
*/
|
|
88
|
+
setTreeRoots(roots: Record<MerkleTreeId, Fr>): Promise<void>;
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* Retrieve the stored Historic Block Data from the database.
|
|
92
|
+
* The function returns a Promise that resolves to the Historic Block Data.
|
|
93
|
+
* This data is required to reproduce block attestations.
|
|
94
|
+
* Throws an error if the historic block data is not available within the database.
|
|
95
|
+
*
|
|
96
|
+
* note: this data is a combination of the tree roots and the global variables hash.
|
|
97
|
+
*/
|
|
98
|
+
getHistoricBlockData(): HistoricBlockData;
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* Set the latest Historic Block Data.
|
|
102
|
+
* This function updates the 'global variables hash' and `tree roots` property of the instance
|
|
103
|
+
* Note that this will overwrite any existing hash or roots in the database.
|
|
104
|
+
*
|
|
105
|
+
* @param historicBlockData - An object containing the most recent historic block data.
|
|
106
|
+
* @returns A Promise that resolves when the hash has been successfully updated in the database.
|
|
107
|
+
*/
|
|
108
|
+
setHistoricBlockData(historicBlockData: HistoricBlockData): Promise<void>;
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* Adds complete address to the database.
|
|
112
|
+
* @param address - The complete address to add.
|
|
113
|
+
* @returns A promise resolving to true if the address was added, false if it already exists.
|
|
114
|
+
* @throws If we try to add a CompleteAddress with the same AztecAddress but different public key or partial
|
|
115
|
+
* address.
|
|
116
|
+
*/
|
|
117
|
+
addCompleteAddress(address: CompleteAddress): Promise<boolean>;
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* Retrieves the complete address corresponding to the provided aztec address.
|
|
121
|
+
* @param address - The aztec address of the complete address contract.
|
|
122
|
+
* @returns A promise that resolves to a CompleteAddress instance if the address is found, or undefined if not found.
|
|
123
|
+
*/
|
|
124
|
+
getCompleteAddress(address: AztecAddress): Promise<CompleteAddress | undefined>;
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* Retrieves the list of complete address added to this database
|
|
128
|
+
* @returns A promise that resolves to an array of AztecAddress instances.
|
|
129
|
+
*/
|
|
130
|
+
getCompleteAddresses(): Promise<CompleteAddress[]>;
|
|
131
|
+
}
|