@aztec/pxe 4.0.0-nightly.20260113 → 4.0.0-nightly.20260114
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/dest/contract_function_simulator/oracle/interfaces.d.ts +3 -3
- package/dest/contract_function_simulator/oracle/interfaces.d.ts.map +1 -1
- package/dest/contract_function_simulator/oracle/note_packing_utils.d.ts +4 -4
- package/dest/contract_function_simulator/oracle/note_packing_utils.d.ts.map +1 -1
- package/dest/contract_function_simulator/oracle/note_packing_utils.js +5 -5
- package/dest/contract_function_simulator/oracle/oracle.js +1 -1
- package/dest/contract_function_simulator/oracle/private_execution_oracle.d.ts +1 -1
- package/dest/contract_function_simulator/oracle/private_execution_oracle.d.ts.map +1 -1
- package/dest/contract_function_simulator/oracle/private_execution_oracle.js +2 -1
- package/dest/events/event_service.d.ts +1 -1
- package/dest/events/event_service.d.ts.map +1 -1
- package/dest/events/event_service.js +8 -12
- package/dest/notes/note_service.d.ts +2 -2
- package/dest/notes/note_service.d.ts.map +1 -1
- package/dest/notes/note_service.js +14 -22
- package/dest/private_kernel/private_kernel_oracle.d.ts +23 -28
- package/dest/private_kernel/private_kernel_oracle.d.ts.map +1 -1
- package/dest/private_kernel/private_kernel_oracle.js +92 -2
- package/dest/pxe.d.ts +7 -36
- package/dest/pxe.d.ts.map +1 -1
- package/dest/pxe.js +8 -58
- package/dest/storage/note_store/note_store.d.ts +3 -4
- package/dest/storage/note_store/note_store.d.ts.map +1 -1
- package/dest/storage/note_store/note_store.js +63 -70
- package/dest/storage/private_event_store/private_event_store.d.ts +10 -5
- package/dest/storage/private_event_store/private_event_store.d.ts.map +1 -1
- package/dest/storage/private_event_store/private_event_store.js +55 -41
- package/package.json +16 -16
- package/src/contract_function_simulator/oracle/interfaces.ts +2 -2
- package/src/contract_function_simulator/oracle/note_packing_utils.ts +6 -6
- package/src/contract_function_simulator/oracle/oracle.ts +1 -1
- package/src/contract_function_simulator/oracle/private_execution_oracle.ts +1 -0
- package/src/events/event_service.ts +12 -26
- package/src/notes/note_service.ts +14 -23
- package/src/private_kernel/private_kernel_oracle.ts +119 -37
- package/src/pxe.ts +8 -81
- package/src/storage/note_store/note_store.ts +66 -65
- package/src/storage/private_event_store/private_event_store.ts +72 -45
- package/dest/private_kernel/private_kernel_oracle_impl.d.ts +0 -46
- package/dest/private_kernel/private_kernel_oracle_impl.d.ts.map +0 -1
- package/dest/private_kernel/private_kernel_oracle_impl.js +0 -85
- package/src/private_kernel/private_kernel_oracle_impl.ts +0 -127
|
@@ -8,10 +8,10 @@ import { TxHash } from '@aztec/stdlib/tx';
|
|
|
8
8
|
* Stores decrypted private event logs.
|
|
9
9
|
*/ export class PrivateEventStore {
|
|
10
10
|
#store;
|
|
11
|
-
/** Map storing the actual private event log entries, keyed by
|
|
12
|
-
/** Map from contractAddress_scope_eventSelector to
|
|
13
|
-
/** Map from block number to
|
|
14
|
-
/** Map from
|
|
11
|
+
/** Map storing the actual private event log entries, keyed by siloedEventCommitment */ #eventLogs;
|
|
12
|
+
/** Map from contractAddress_scope_eventSelector to siloedEventCommitment[] for efficient lookup */ #eventsByContractScopeSelector;
|
|
13
|
+
/** Map from block number to siloedEventCommitment[] for rollback support */ #eventsByBlockNumber;
|
|
14
|
+
/** Map from siloedEventCommitment to boolean indicating if log has been seen. */ #seenLogs;
|
|
15
15
|
logger = createLogger('private_event_store');
|
|
16
16
|
constructor(store){
|
|
17
17
|
this.#store = store;
|
|
@@ -26,23 +26,26 @@ import { TxHash } from '@aztec/stdlib/tx';
|
|
|
26
26
|
/**
|
|
27
27
|
* Store a private event log.
|
|
28
28
|
* @param eventSelector - The event selector of the event.
|
|
29
|
+
* @param randomness - The randomness used for the event commitment.
|
|
29
30
|
* @param msgContent - The content of the event.
|
|
30
|
-
* @param
|
|
31
|
+
* @param siloedEventCommitment - The siloed event commitment (used as unique identifier).
|
|
31
32
|
* @param metadata
|
|
32
33
|
* contractAddress - The address of the contract that emitted the event.
|
|
33
34
|
* scope - The address to which the event is scoped.
|
|
34
35
|
* txHash - The transaction hash of the event log.
|
|
35
36
|
* blockNumber - The block number in which the event was emitted.
|
|
36
|
-
*/ storePrivateEventLog(eventSelector, randomness, msgContent,
|
|
37
|
-
const { contractAddress, scope, txHash, l2BlockNumber, l2BlockHash } = metadata;
|
|
37
|
+
*/ storePrivateEventLog(eventSelector, randomness, msgContent, siloedEventCommitment, metadata) {
|
|
38
|
+
const { contractAddress, scope, txHash, l2BlockNumber, l2BlockHash, txIndexInBlock, eventIndexInTx } = metadata;
|
|
38
39
|
return this.#store.transactionAsync(async ()=>{
|
|
39
40
|
const key = this.#keyFor(contractAddress, scope, eventSelector);
|
|
40
|
-
//
|
|
41
|
-
|
|
41
|
+
// The siloed event commitment is guaranteed to be unique as it's inserted into the nullifier tree. For this
|
|
42
|
+
// reason we use it as id.
|
|
43
|
+
const eventId = siloedEventCommitment.toString();
|
|
44
|
+
const hasBeenSeen = await this.#seenLogs.getAsync(eventId);
|
|
42
45
|
if (hasBeenSeen) {
|
|
43
46
|
this.logger.verbose('Ignoring duplicate event log', {
|
|
44
47
|
txHash: txHash.toString(),
|
|
45
|
-
|
|
48
|
+
siloedEventCommitment
|
|
46
49
|
});
|
|
47
50
|
return;
|
|
48
51
|
}
|
|
@@ -52,27 +55,28 @@ import { TxHash } from '@aztec/stdlib/tx';
|
|
|
52
55
|
msgContent,
|
|
53
56
|
l2BlockNumber
|
|
54
57
|
});
|
|
55
|
-
await this.#eventLogs.set(
|
|
58
|
+
await this.#eventLogs.set(eventId, {
|
|
56
59
|
randomness,
|
|
57
60
|
msgContent: serializeToBuffer(msgContent),
|
|
58
61
|
l2BlockNumber,
|
|
59
62
|
l2BlockHash: l2BlockHash.toBuffer(),
|
|
60
|
-
eventCommitmentIndex,
|
|
61
63
|
txHash: txHash.toBuffer(),
|
|
64
|
+
txIndexInBlock,
|
|
65
|
+
eventIndexInTx,
|
|
62
66
|
lookupKey: key
|
|
63
67
|
});
|
|
64
|
-
const
|
|
68
|
+
const existingIds = await this.#eventsByContractScopeSelector.getAsync(key) || [];
|
|
65
69
|
await this.#eventsByContractScopeSelector.set(key, [
|
|
66
|
-
...
|
|
67
|
-
|
|
70
|
+
...existingIds,
|
|
71
|
+
eventId
|
|
68
72
|
]);
|
|
69
|
-
const
|
|
73
|
+
const existingBlockIds = await this.#eventsByBlockNumber.getAsync(l2BlockNumber) || [];
|
|
70
74
|
await this.#eventsByBlockNumber.set(l2BlockNumber, [
|
|
71
|
-
...
|
|
72
|
-
|
|
75
|
+
...existingBlockIds,
|
|
76
|
+
eventId
|
|
73
77
|
]);
|
|
74
|
-
// Mark this log as seen
|
|
75
|
-
await this.#seenLogs.set(
|
|
78
|
+
// Mark this log as seen
|
|
79
|
+
await this.#seenLogs.set(eventId, true);
|
|
76
80
|
});
|
|
77
81
|
}
|
|
78
82
|
/**
|
|
@@ -83,15 +87,15 @@ import { TxHash } from '@aztec/stdlib/tx';
|
|
|
83
87
|
* fromBlock: The block number to search from (inclusive).
|
|
84
88
|
* toBlock: The block number to search upto (exclusive).
|
|
85
89
|
* scope: - The addresses that decrypted the logs.
|
|
86
|
-
* @returns - The event log contents, augmented with metadata about
|
|
87
|
-
*
|
|
90
|
+
* @returns - The event log contents, augmented with metadata about the transaction and block in which the event was
|
|
91
|
+
* included.
|
|
88
92
|
*/ async getPrivateEvents(eventSelector, filter) {
|
|
89
93
|
const events = [];
|
|
90
94
|
for (const scope of filter.scopes){
|
|
91
95
|
const key = this.#keyFor(filter.contractAddress, scope, eventSelector);
|
|
92
|
-
const
|
|
93
|
-
for (const
|
|
94
|
-
const entry = await this.#eventLogs.getAsync(
|
|
96
|
+
const eventIds = await this.#eventsByContractScopeSelector.getAsync(key) || [];
|
|
97
|
+
for (const eventId of eventIds){
|
|
98
|
+
const entry = await this.#eventLogs.getAsync(eventId);
|
|
95
99
|
if (!entry || entry.l2BlockNumber < filter.fromBlock || entry.l2BlockNumber >= filter.toBlock) {
|
|
96
100
|
continue;
|
|
97
101
|
}
|
|
@@ -105,7 +109,9 @@ import { TxHash } from '@aztec/stdlib/tx';
|
|
|
105
109
|
continue;
|
|
106
110
|
}
|
|
107
111
|
events.push({
|
|
108
|
-
|
|
112
|
+
l2BlockNumber: entry.l2BlockNumber,
|
|
113
|
+
txIndexInBlock: entry.txIndexInBlock,
|
|
114
|
+
eventIndexInTx: entry.eventIndexInTx,
|
|
109
115
|
event: {
|
|
110
116
|
packedEvent: msgContent,
|
|
111
117
|
l2BlockNumber: BlockNumber(entry.l2BlockNumber),
|
|
@@ -116,8 +122,16 @@ import { TxHash } from '@aztec/stdlib/tx';
|
|
|
116
122
|
});
|
|
117
123
|
}
|
|
118
124
|
}
|
|
119
|
-
// Sort by
|
|
120
|
-
events.sort((a, b)=>
|
|
125
|
+
// Sort by block number, then by tx index within block, then by event index within tx
|
|
126
|
+
events.sort((a, b)=>{
|
|
127
|
+
if (a.l2BlockNumber !== b.l2BlockNumber) {
|
|
128
|
+
return a.l2BlockNumber - b.l2BlockNumber;
|
|
129
|
+
}
|
|
130
|
+
if (a.txIndexInBlock !== b.txIndexInBlock) {
|
|
131
|
+
return a.txIndexInBlock - b.txIndexInBlock;
|
|
132
|
+
}
|
|
133
|
+
return a.eventIndexInTx - b.eventIndexInTx;
|
|
134
|
+
});
|
|
121
135
|
return events.map((ev)=>ev.event);
|
|
122
136
|
}
|
|
123
137
|
/**
|
|
@@ -128,26 +142,26 @@ import { TxHash } from '@aztec/stdlib/tx';
|
|
|
128
142
|
*/ async rollback(blockNumber, synchedBlockNumber) {
|
|
129
143
|
let removedCount = 0;
|
|
130
144
|
for(let block = blockNumber + 1; block <= synchedBlockNumber; block++){
|
|
131
|
-
const
|
|
132
|
-
if (
|
|
145
|
+
const eventIds = await this.#eventsByBlockNumber.getAsync(block);
|
|
146
|
+
if (eventIds) {
|
|
133
147
|
await this.#eventsByBlockNumber.delete(block);
|
|
134
|
-
for (const
|
|
135
|
-
const entry = await this.#eventLogs.getAsync(
|
|
148
|
+
for (const eventId of eventIds){
|
|
149
|
+
const entry = await this.#eventLogs.getAsync(eventId);
|
|
136
150
|
if (!entry) {
|
|
137
|
-
throw new Error(`Event log not found for
|
|
151
|
+
throw new Error(`Event log not found for eventId ${eventId}`);
|
|
138
152
|
}
|
|
139
|
-
await this.#eventLogs.delete(
|
|
140
|
-
await this.#seenLogs.delete(
|
|
153
|
+
await this.#eventLogs.delete(eventId);
|
|
154
|
+
await this.#seenLogs.delete(eventId);
|
|
141
155
|
// Update #eventsByContractScopeSelector using the stored lookupKey
|
|
142
|
-
const
|
|
143
|
-
if (!
|
|
144
|
-
throw new Error(`No
|
|
156
|
+
const existingIds = await this.#eventsByContractScopeSelector.getAsync(entry.lookupKey);
|
|
157
|
+
if (!existingIds || existingIds.length === 0) {
|
|
158
|
+
throw new Error(`No ids found in #eventsByContractScopeSelector for key ${entry.lookupKey}`);
|
|
145
159
|
}
|
|
146
|
-
const
|
|
147
|
-
if (
|
|
160
|
+
const filteredIds = existingIds.filter((id)=>id !== eventId);
|
|
161
|
+
if (filteredIds.length === 0) {
|
|
148
162
|
await this.#eventsByContractScopeSelector.delete(entry.lookupKey);
|
|
149
163
|
} else {
|
|
150
|
-
await this.#eventsByContractScopeSelector.set(entry.lookupKey,
|
|
164
|
+
await this.#eventsByContractScopeSelector.set(entry.lookupKey, filteredIds);
|
|
151
165
|
}
|
|
152
166
|
removedCount++;
|
|
153
167
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@aztec/pxe",
|
|
3
|
-
"version": "4.0.0-nightly.
|
|
3
|
+
"version": "4.0.0-nightly.20260114",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"exports": {
|
|
6
6
|
"./server": "./dest/entrypoints/server/index.js",
|
|
@@ -61,19 +61,19 @@
|
|
|
61
61
|
]
|
|
62
62
|
},
|
|
63
63
|
"dependencies": {
|
|
64
|
-
"@aztec/bb-prover": "4.0.0-nightly.
|
|
65
|
-
"@aztec/bb.js": "4.0.0-nightly.
|
|
66
|
-
"@aztec/builder": "4.0.0-nightly.
|
|
67
|
-
"@aztec/constants": "4.0.0-nightly.
|
|
68
|
-
"@aztec/ethereum": "4.0.0-nightly.
|
|
69
|
-
"@aztec/foundation": "4.0.0-nightly.
|
|
70
|
-
"@aztec/key-store": "4.0.0-nightly.
|
|
71
|
-
"@aztec/kv-store": "4.0.0-nightly.
|
|
72
|
-
"@aztec/noir-protocol-circuits-types": "4.0.0-nightly.
|
|
73
|
-
"@aztec/noir-types": "4.0.0-nightly.
|
|
74
|
-
"@aztec/protocol-contracts": "4.0.0-nightly.
|
|
75
|
-
"@aztec/simulator": "4.0.0-nightly.
|
|
76
|
-
"@aztec/stdlib": "4.0.0-nightly.
|
|
64
|
+
"@aztec/bb-prover": "4.0.0-nightly.20260114",
|
|
65
|
+
"@aztec/bb.js": "4.0.0-nightly.20260114",
|
|
66
|
+
"@aztec/builder": "4.0.0-nightly.20260114",
|
|
67
|
+
"@aztec/constants": "4.0.0-nightly.20260114",
|
|
68
|
+
"@aztec/ethereum": "4.0.0-nightly.20260114",
|
|
69
|
+
"@aztec/foundation": "4.0.0-nightly.20260114",
|
|
70
|
+
"@aztec/key-store": "4.0.0-nightly.20260114",
|
|
71
|
+
"@aztec/kv-store": "4.0.0-nightly.20260114",
|
|
72
|
+
"@aztec/noir-protocol-circuits-types": "4.0.0-nightly.20260114",
|
|
73
|
+
"@aztec/noir-types": "4.0.0-nightly.20260114",
|
|
74
|
+
"@aztec/protocol-contracts": "4.0.0-nightly.20260114",
|
|
75
|
+
"@aztec/simulator": "4.0.0-nightly.20260114",
|
|
76
|
+
"@aztec/stdlib": "4.0.0-nightly.20260114",
|
|
77
77
|
"koa": "^2.16.1",
|
|
78
78
|
"koa-router": "^13.1.1",
|
|
79
79
|
"lodash.omit": "^4.5.0",
|
|
@@ -82,8 +82,8 @@
|
|
|
82
82
|
"viem": "npm:@aztec/viem@2.38.2"
|
|
83
83
|
},
|
|
84
84
|
"devDependencies": {
|
|
85
|
-
"@aztec/merkle-tree": "4.0.0-nightly.
|
|
86
|
-
"@aztec/noir-test-contracts.js": "4.0.0-nightly.
|
|
85
|
+
"@aztec/merkle-tree": "4.0.0-nightly.20260114",
|
|
86
|
+
"@aztec/noir-test-contracts.js": "4.0.0-nightly.20260114",
|
|
87
87
|
"@jest/globals": "^30.0.0",
|
|
88
88
|
"@types/jest": "^30.0.0",
|
|
89
89
|
"@types/lodash.omit": "^4.5.7",
|
|
@@ -32,10 +32,10 @@ export interface NoteData {
|
|
|
32
32
|
noteNonce: Fr;
|
|
33
33
|
/** A hash of the note as it gets stored in the note hash tree. */
|
|
34
34
|
noteHash: Fr;
|
|
35
|
+
/** True if the note is pending, false if settled. */
|
|
36
|
+
isPending: boolean;
|
|
35
37
|
/** The corresponding nullifier of the note. Undefined for pending notes. */
|
|
36
38
|
siloedNullifier?: Fr;
|
|
37
|
-
/** The note's leaf index in the note hash tree. Undefined for pending notes. */
|
|
38
|
-
index?: bigint;
|
|
39
39
|
}
|
|
40
40
|
|
|
41
41
|
// These interfaces contain the list of oracles required by aztec-nr in order to simulate and execute transactions, i.e.
|
|
@@ -28,7 +28,7 @@ function fromRawData(nonzeroNoteHashCounter: boolean, maybeNoteNonce: Fr): { sta
|
|
|
28
28
|
* @param randomness - The randomness injected into the note to get the hiding property of commitments
|
|
29
29
|
* @param storageSlot - The storage slot of the note
|
|
30
30
|
* @param noteNonce - The nonce injected into the note hash preimage by kernels.
|
|
31
|
-
* @param
|
|
31
|
+
* @param isPending - True if the note is pending, false if settled
|
|
32
32
|
* @param note - The note content containing the actual note data
|
|
33
33
|
* @returns The packed note as an array of field elements
|
|
34
34
|
*/
|
|
@@ -38,7 +38,7 @@ export function packAsRetrievedNote({
|
|
|
38
38
|
randomness,
|
|
39
39
|
storageSlot,
|
|
40
40
|
noteNonce,
|
|
41
|
-
|
|
41
|
+
isPending,
|
|
42
42
|
note,
|
|
43
43
|
}: {
|
|
44
44
|
contractAddress: AztecAddress;
|
|
@@ -46,14 +46,14 @@ export function packAsRetrievedNote({
|
|
|
46
46
|
randomness: Fr;
|
|
47
47
|
storageSlot: Fr;
|
|
48
48
|
noteNonce: Fr;
|
|
49
|
-
|
|
49
|
+
isPending: boolean;
|
|
50
50
|
note: Note;
|
|
51
51
|
}) {
|
|
52
|
-
// If
|
|
53
|
-
const
|
|
52
|
+
// If the note is pending it means it has a non-zero note hash counter associated with it.
|
|
53
|
+
const nonZeroNoteHashCounter = isPending;
|
|
54
54
|
|
|
55
55
|
// To pack the note as retrieved note we first need to reconstruct the note metadata.
|
|
56
|
-
const noteMetadata = fromRawData(
|
|
56
|
+
const noteMetadata = fromRawData(nonZeroNoteHashCounter, noteNonce);
|
|
57
57
|
|
|
58
58
|
// Pack in order: note, contract_address, owner, randomness, storage_slot, metadata (stage, maybe_note_nonce)
|
|
59
59
|
return [
|
|
@@ -428,6 +428,7 @@ export class PrivateExecutionOracle extends UtilityExecutionOracle implements IP
|
|
|
428
428
|
note,
|
|
429
429
|
siloedNullifier: undefined, // Siloed nullifier cannot be known for newly created note.
|
|
430
430
|
noteHash,
|
|
431
|
+
isPending: true, // This note has just been created and hence is not settled yet.
|
|
431
432
|
},
|
|
432
433
|
counter,
|
|
433
434
|
);
|
|
@@ -3,7 +3,6 @@ import type { EventSelector } from '@aztec/stdlib/abi';
|
|
|
3
3
|
import type { AztecAddress } from '@aztec/stdlib/aztec-address';
|
|
4
4
|
import { siloNullifier } from '@aztec/stdlib/hash';
|
|
5
5
|
import type { AztecNode } from '@aztec/stdlib/interfaces/server';
|
|
6
|
-
import { MerkleTreeId } from '@aztec/stdlib/trees';
|
|
7
6
|
import type { TxHash } from '@aztec/stdlib/tx';
|
|
8
7
|
|
|
9
8
|
import { AnchorBlockStore } from '../storage/anchor_block_store/anchor_block_store.js';
|
|
@@ -45,35 +44,22 @@ export class EventService {
|
|
|
45
44
|
throw new Error(`Could not find tx effect for tx hash ${txHash} as of block number ${syncedBlockNumber}`);
|
|
46
45
|
}
|
|
47
46
|
|
|
48
|
-
|
|
49
|
-
|
|
47
|
+
// Find the index of the event commitment in the nullifiers array to determine event ordering within the tx
|
|
48
|
+
const eventIndexInTx = txEffect.data.nullifiers.findIndex(n => n.equals(siloedEventCommitment));
|
|
49
|
+
if (eventIndexInTx === -1) {
|
|
50
50
|
throw new Error(
|
|
51
51
|
`Event commitment ${eventCommitment} (siloed as ${siloedEventCommitment}) is not present in tx ${txHash}`,
|
|
52
52
|
);
|
|
53
53
|
}
|
|
54
54
|
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
return this.privateEventStore.storePrivateEventLog(
|
|
66
|
-
selector,
|
|
67
|
-
randomness,
|
|
68
|
-
content,
|
|
69
|
-
Number(nullifierIndex.data), // Index of the event commitment in the nullifier tree
|
|
70
|
-
{
|
|
71
|
-
contractAddress,
|
|
72
|
-
scope,
|
|
73
|
-
txHash,
|
|
74
|
-
l2BlockNumber: nullifierIndex.l2BlockNumber, // Block number in which the event was emitted
|
|
75
|
-
l2BlockHash: nullifierIndex.l2BlockHash, // Block hash in which the event was emitted
|
|
76
|
-
},
|
|
77
|
-
);
|
|
55
|
+
return this.privateEventStore.storePrivateEventLog(selector, randomness, content, siloedEventCommitment, {
|
|
56
|
+
contractAddress,
|
|
57
|
+
scope,
|
|
58
|
+
txHash,
|
|
59
|
+
l2BlockNumber: txEffect.l2BlockNumber,
|
|
60
|
+
l2BlockHash: txEffect.l2BlockHash,
|
|
61
|
+
txIndexInBlock: txEffect.txIndexInBlock,
|
|
62
|
+
eventIndexInTx,
|
|
63
|
+
});
|
|
78
64
|
}
|
|
79
65
|
}
|
|
@@ -41,7 +41,7 @@ export class NoteService {
|
|
|
41
41
|
scopes,
|
|
42
42
|
});
|
|
43
43
|
return noteDaos.map(
|
|
44
|
-
({ contractAddress, owner, storageSlot, randomness, noteNonce, note, noteHash, siloedNullifier
|
|
44
|
+
({ contractAddress, owner, storageSlot, randomness, noteNonce, note, noteHash, siloedNullifier }) => ({
|
|
45
45
|
contractAddress,
|
|
46
46
|
owner,
|
|
47
47
|
storageSlot,
|
|
@@ -49,9 +49,8 @@ export class NoteService {
|
|
|
49
49
|
noteNonce,
|
|
50
50
|
note,
|
|
51
51
|
noteHash,
|
|
52
|
+
isPending: false, // Note service deals only with settled notes
|
|
52
53
|
siloedNullifier,
|
|
53
|
-
// PXE can use this index to get full MembershipWitness
|
|
54
|
-
index,
|
|
55
54
|
}),
|
|
56
55
|
);
|
|
57
56
|
}
|
|
@@ -146,7 +145,10 @@ export class NoteService {
|
|
|
146
145
|
const uniqueNoteHash = await computeUniqueNoteHash(noteNonce, await siloNoteHash(contractAddress, noteHash));
|
|
147
146
|
const siloedNullifier = await siloNullifier(contractAddress, nullifier);
|
|
148
147
|
|
|
149
|
-
const txEffect = await
|
|
148
|
+
const [txEffect, [nullifierIndex]] = await Promise.all([
|
|
149
|
+
this.aztecNode.getTxEffect(txHash),
|
|
150
|
+
this.aztecNode.findLeavesIndexes(syncedBlockNumber, MerkleTreeId.NULLIFIER_TREE, [siloedNullifier]),
|
|
151
|
+
]);
|
|
150
152
|
if (!txEffect) {
|
|
151
153
|
throw new Error(`Could not find tx effect for tx hash ${txHash}`);
|
|
152
154
|
}
|
|
@@ -155,25 +157,12 @@ export class NoteService {
|
|
|
155
157
|
throw new Error(`Could not find tx effect for tx hash ${txHash} as of block number ${syncedBlockNumber}`);
|
|
156
158
|
}
|
|
157
159
|
|
|
158
|
-
|
|
159
|
-
|
|
160
|
+
// Find the index of the note hash in the noteHashes array to determine note ordering within the tx
|
|
161
|
+
const noteIndexInTx = txEffect.data.noteHashes.findIndex(nh => nh.equals(uniqueNoteHash));
|
|
162
|
+
if (noteIndexInTx === -1) {
|
|
160
163
|
throw new Error(`Note hash ${noteHash} (uniqued as ${uniqueNoteHash}) is not present in tx ${txHash}`);
|
|
161
164
|
}
|
|
162
165
|
|
|
163
|
-
// We store notes by their index in the global note hash tree, which has the convenient side effect of validating
|
|
164
|
-
// note existence in said tree. We concurrently also check if the note's nullifier exists, performing all node
|
|
165
|
-
// queries in a single round-trip.
|
|
166
|
-
const [[uniqueNoteHashTreeIndexInBlock], [nullifierIndex]] = await Promise.all([
|
|
167
|
-
this.aztecNode.findLeavesIndexes(syncedBlockNumber, MerkleTreeId.NOTE_HASH_TREE, [uniqueNoteHash]),
|
|
168
|
-
this.aztecNode.findLeavesIndexes(syncedBlockNumber, MerkleTreeId.NULLIFIER_TREE, [siloedNullifier]),
|
|
169
|
-
]);
|
|
170
|
-
|
|
171
|
-
if (uniqueNoteHashTreeIndexInBlock === undefined) {
|
|
172
|
-
throw new Error(
|
|
173
|
-
`Note hash ${noteHash} (uniqued as ${uniqueNoteHash}) is not present on the tree at block ${syncedBlockNumber} (from tx ${txHash})`,
|
|
174
|
-
);
|
|
175
|
-
}
|
|
176
|
-
|
|
177
166
|
const noteDao = new NoteDao(
|
|
178
167
|
new Note(content),
|
|
179
168
|
contractAddress,
|
|
@@ -184,15 +173,17 @@ export class NoteService {
|
|
|
184
173
|
noteHash,
|
|
185
174
|
siloedNullifier,
|
|
186
175
|
txHash,
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
176
|
+
txEffect.l2BlockNumber,
|
|
177
|
+
txEffect.l2BlockHash.toString(),
|
|
178
|
+
txEffect.txIndexInBlock,
|
|
179
|
+
noteIndexInTx,
|
|
190
180
|
);
|
|
191
181
|
|
|
192
182
|
// The note was found by `recipient`, so we use that as the scope when storing the note.
|
|
193
183
|
await this.noteStore.addNotes([noteDao], recipient);
|
|
194
184
|
|
|
195
185
|
if (nullifierIndex !== undefined) {
|
|
186
|
+
// We found nullifier index which implies that the note has already been nullified.
|
|
196
187
|
const { data: _, ...blockHashAndNum } = nullifierIndex;
|
|
197
188
|
await this.noteStore.applyNullifiers([{ data: siloedNullifier, ...blockHashAndNum }]);
|
|
198
189
|
}
|
|
@@ -1,73 +1,155 @@
|
|
|
1
|
-
import { FUNCTION_TREE_HEIGHT, NOTE_HASH_TREE_HEIGHT, VK_TREE_HEIGHT } from '@aztec/constants';
|
|
1
|
+
import { FUNCTION_TREE_HEIGHT, NOTE_HASH_TREE_HEIGHT, PUBLIC_DATA_TREE_HEIGHT, VK_TREE_HEIGHT } from '@aztec/constants';
|
|
2
2
|
import type { Fr } from '@aztec/foundation/curves/bn254';
|
|
3
3
|
import type { GrumpkinScalar, Point } from '@aztec/foundation/curves/grumpkin';
|
|
4
4
|
import { MembershipWitness } from '@aztec/foundation/trees';
|
|
5
|
+
import type { KeyStore } from '@aztec/key-store';
|
|
6
|
+
import { getVKIndex, getVKSiblingPath } from '@aztec/noir-protocol-circuits-types/vk-tree';
|
|
7
|
+
import { ProtocolContractAddress } from '@aztec/protocol-contracts';
|
|
5
8
|
import type { FunctionSelector } from '@aztec/stdlib/abi';
|
|
6
9
|
import type { AztecAddress } from '@aztec/stdlib/aztec-address';
|
|
10
|
+
import type { BlockParameter } from '@aztec/stdlib/block';
|
|
11
|
+
import {
|
|
12
|
+
type ContractInstanceWithAddress,
|
|
13
|
+
computeContractClassIdPreimage,
|
|
14
|
+
computeSaltedInitializationHash,
|
|
15
|
+
} from '@aztec/stdlib/contract';
|
|
16
|
+
import { DelayedPublicMutableValues, DelayedPublicMutableValuesWithHash } from '@aztec/stdlib/delayed-public-mutable';
|
|
17
|
+
import { computePublicDataTreeLeafSlot } from '@aztec/stdlib/hash';
|
|
18
|
+
import type { AztecNode } from '@aztec/stdlib/interfaces/client';
|
|
7
19
|
import { UpdatedClassIdHints } from '@aztec/stdlib/kernel';
|
|
8
|
-
import type { PublicKeys } from '@aztec/stdlib/keys';
|
|
9
20
|
import type { NullifierMembershipWitness } from '@aztec/stdlib/trees';
|
|
10
21
|
import type { VerificationKeyAsFields } from '@aztec/stdlib/vks';
|
|
11
22
|
|
|
23
|
+
import type { ContractStore } from '../storage/contract_store/contract_store.js';
|
|
24
|
+
|
|
25
|
+
// TODO: Block number should not be "latest".
|
|
26
|
+
// It should be fixed at the time the proof is being simulated. I.e., it should be the same as the value defined in the constant data.
|
|
27
|
+
|
|
12
28
|
/**
|
|
13
29
|
* Provides functionality needed by the private kernel for interacting with our state trees.
|
|
14
|
-
* This is either PrivateKernelOracleImpl, or a mocked test implementation.
|
|
15
30
|
*/
|
|
16
|
-
export
|
|
31
|
+
export class PrivateKernelOracle {
|
|
32
|
+
constructor(
|
|
33
|
+
private contractStore: ContractStore,
|
|
34
|
+
private keyStore: KeyStore,
|
|
35
|
+
private node: AztecNode,
|
|
36
|
+
private blockNumber: BlockParameter = 'latest',
|
|
37
|
+
) {}
|
|
38
|
+
|
|
17
39
|
/** Retrieves the preimage of a contract address from the registered contract instances db. */
|
|
18
|
-
getContractAddressPreimage(
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
40
|
+
public async getContractAddressPreimage(
|
|
41
|
+
address: AztecAddress,
|
|
42
|
+
): Promise<ContractInstanceWithAddress & { saltedInitializationHash: Fr }> {
|
|
43
|
+
const instance = await this.contractStore.getContractInstance(address);
|
|
44
|
+
if (!instance) {
|
|
45
|
+
throw new Error(`Contract instance not found when getting address preimage. Contract address: ${address}.`);
|
|
46
|
+
}
|
|
47
|
+
return {
|
|
48
|
+
saltedInitializationHash: await computeSaltedInitializationHash(instance),
|
|
49
|
+
...instance,
|
|
50
|
+
};
|
|
51
|
+
}
|
|
24
52
|
|
|
25
53
|
/** Retrieves the preimage of a contract class id from the contract classes db. */
|
|
26
|
-
getContractClassIdPreimage(
|
|
27
|
-
contractClassId
|
|
28
|
-
|
|
54
|
+
public async getContractClassIdPreimage(contractClassId: Fr) {
|
|
55
|
+
const contractClass = await this.contractStore.getContractClass(contractClassId);
|
|
56
|
+
if (!contractClass) {
|
|
57
|
+
throw new Error(`Contract class not found when getting class id preimage. Class id: ${contractClassId}.`);
|
|
58
|
+
}
|
|
59
|
+
return computeContractClassIdPreimage(contractClass);
|
|
60
|
+
}
|
|
29
61
|
|
|
30
|
-
/**
|
|
31
|
-
|
|
32
|
-
*/
|
|
33
|
-
getFunctionMembershipWitness(
|
|
62
|
+
/** Returns a membership witness with the sibling path and leaf index in our private functions tree. */
|
|
63
|
+
public async getFunctionMembershipWitness(
|
|
34
64
|
contractClassId: Fr,
|
|
35
65
|
selector: FunctionSelector,
|
|
36
|
-
): Promise<MembershipWitness<typeof FUNCTION_TREE_HEIGHT
|
|
66
|
+
): Promise<MembershipWitness<typeof FUNCTION_TREE_HEIGHT>> {
|
|
67
|
+
const membershipWitness = await this.contractStore.getFunctionMembershipWitness(contractClassId, selector);
|
|
68
|
+
if (!membershipWitness) {
|
|
69
|
+
throw new Error(
|
|
70
|
+
`Membership witness not found for contract class id ${contractClassId} and selector ${selector}.`,
|
|
71
|
+
);
|
|
72
|
+
}
|
|
73
|
+
return membershipWitness;
|
|
74
|
+
}
|
|
37
75
|
|
|
38
76
|
/**
|
|
39
77
|
* Returns a membership witness with the sibling path and leaf index in our protocol VK indexed merkle tree.
|
|
40
78
|
* Used to validate the previous kernel's verification key.
|
|
41
79
|
*/
|
|
42
|
-
getVkMembershipWitness(vk: VerificationKeyAsFields): Promise<MembershipWitness<typeof VK_TREE_HEIGHT
|
|
80
|
+
public getVkMembershipWitness(vk: VerificationKeyAsFields): Promise<MembershipWitness<typeof VK_TREE_HEIGHT>> {
|
|
81
|
+
const leafIndex = getVKIndex(vk);
|
|
82
|
+
return Promise.resolve(new MembershipWitness(VK_TREE_HEIGHT, BigInt(leafIndex), getVKSiblingPath(leafIndex)));
|
|
83
|
+
}
|
|
43
84
|
|
|
44
|
-
/**
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
85
|
+
/** Returns a membership witness with the sibling path and leaf index in our note hash tree. */
|
|
86
|
+
getNoteHashMembershipWitness(noteHash: Fr): Promise<MembershipWitness<typeof NOTE_HASH_TREE_HEIGHT> | undefined> {
|
|
87
|
+
return this.node.getNoteHashMembershipWitness(this.blockNumber, noteHash);
|
|
88
|
+
}
|
|
48
89
|
|
|
49
|
-
/**
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
90
|
+
/** Returns a membership witness with the sibling path and leaf index in our nullifier indexed merkle tree. */
|
|
91
|
+
getNullifierMembershipWitness(nullifier: Fr): Promise<NullifierMembershipWitness | undefined> {
|
|
92
|
+
return this.node.getNullifierMembershipWitness(this.blockNumber, nullifier);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/** Returns the root of our note hash merkle tree. */
|
|
96
|
+
async getNoteHashTreeRoot(): Promise<Fr> {
|
|
97
|
+
const header = await this.node.getBlockHeader(this.blockNumber);
|
|
98
|
+
if (!header) {
|
|
99
|
+
throw new Error(`No block header found for block number ${this.blockNumber}`);
|
|
100
|
+
}
|
|
101
|
+
return header.state.partial.noteHashTree.root;
|
|
102
|
+
}
|
|
57
103
|
|
|
58
104
|
/**
|
|
59
105
|
* Retrieves the sk_m corresponding to the pk_m.
|
|
60
106
|
* @throws If the provided public key is not associated with any of the registered accounts.
|
|
61
|
-
* @param
|
|
107
|
+
* @param masterPublicKey - The master public key to get secret key for.
|
|
62
108
|
* @returns A Promise that resolves to sk_m.
|
|
63
109
|
* @dev Used when feeding the sk_m to the kernel circuit for keys verification.
|
|
64
110
|
*/
|
|
65
|
-
getMasterSecretKey(masterPublicKey: Point): Promise<GrumpkinScalar
|
|
111
|
+
public getMasterSecretKey(masterPublicKey: Point): Promise<GrumpkinScalar> {
|
|
112
|
+
return this.keyStore.getMasterSecretKey(masterPublicKey);
|
|
113
|
+
}
|
|
66
114
|
|
|
67
115
|
/** Use debug data to get the function name corresponding to a selector. */
|
|
68
|
-
getDebugFunctionName(contractAddress: AztecAddress, selector: FunctionSelector): Promise<string | undefined
|
|
116
|
+
public getDebugFunctionName(contractAddress: AztecAddress, selector: FunctionSelector): Promise<string | undefined> {
|
|
117
|
+
return this.contractStore.getDebugFunctionName(contractAddress, selector);
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* Returns a membership witness and leaf index to our public data indexed merkle tree,
|
|
122
|
+
* along with an associated DelayedPublicMutable containing the class ID to update.
|
|
123
|
+
*/
|
|
124
|
+
public async getUpdatedClassIdHints(contractAddress: AztecAddress): Promise<UpdatedClassIdHints> {
|
|
125
|
+
const { delayedPublicMutableSlot, delayedPublicMutableHashSlot } =
|
|
126
|
+
await DelayedPublicMutableValuesWithHash.getContractUpdateSlots(contractAddress);
|
|
127
|
+
|
|
128
|
+
const hashLeafSlot = await computePublicDataTreeLeafSlot(
|
|
129
|
+
ProtocolContractAddress.ContractInstanceRegistry,
|
|
130
|
+
delayedPublicMutableHashSlot,
|
|
131
|
+
);
|
|
132
|
+
const updatedClassIdWitness = await this.node.getPublicDataWitness(this.blockNumber, hashLeafSlot);
|
|
133
|
+
|
|
134
|
+
if (!updatedClassIdWitness) {
|
|
135
|
+
throw new Error(`No public data tree witness found for ${hashLeafSlot}`);
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
const readStorage = (storageSlot: Fr) =>
|
|
139
|
+
this.node.getPublicStorageAt(this.blockNumber, ProtocolContractAddress.ContractInstanceRegistry, storageSlot);
|
|
140
|
+
const delayedPublicMutableValues = await DelayedPublicMutableValues.readFromTree(
|
|
141
|
+
delayedPublicMutableSlot,
|
|
142
|
+
readStorage,
|
|
143
|
+
);
|
|
69
144
|
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
145
|
+
return new UpdatedClassIdHints(
|
|
146
|
+
new MembershipWitness(
|
|
147
|
+
PUBLIC_DATA_TREE_HEIGHT,
|
|
148
|
+
updatedClassIdWitness.index,
|
|
149
|
+
updatedClassIdWitness.siblingPath.toTuple(),
|
|
150
|
+
),
|
|
151
|
+
updatedClassIdWitness.leafPreimage,
|
|
152
|
+
delayedPublicMutableValues,
|
|
153
|
+
);
|
|
154
|
+
}
|
|
73
155
|
}
|