@aztec/stdlib 0.77.0-testnet-ignition.30 → 0.77.1
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/interfaces/contract_instance_update.js +1 -1
- package/dest/contract/interfaces/node-info.js +2 -2
- package/dest/database-version/index.d.ts +2 -0
- package/dest/database-version/index.d.ts.map +1 -0
- package/dest/database-version/index.js +1 -0
- package/dest/database-version/version_manager.d.ts +99 -0
- package/dest/database-version/version_manager.d.ts.map +1 -0
- package/dest/database-version/version_manager.js +188 -0
- package/dest/errors/simulation_error.js +2 -2
- package/dest/hash/hash.d.ts +0 -2
- package/dest/hash/hash.d.ts.map +1 -1
- package/dest/hash/hash.js +0 -11
- package/dest/interfaces/world_state.js +3 -3
- package/dest/kernel/private_kernel_tail_circuit_public_inputs.d.ts +2 -1
- package/dest/kernel/private_kernel_tail_circuit_public_inputs.d.ts.map +1 -1
- package/dest/kernel/private_kernel_tail_circuit_public_inputs.js +4 -1
- package/dest/logs/contract_class_log.d.ts +4 -0
- package/dest/logs/contract_class_log.d.ts.map +1 -1
- package/dest/logs/contract_class_log.js +30 -9
- package/dest/logs/private_log.d.ts +2 -0
- package/dest/logs/private_log.d.ts.map +1 -1
- package/dest/logs/private_log.js +9 -0
- package/dest/logs/public_log.d.ts +2 -0
- package/dest/logs/public_log.d.ts.map +1 -1
- package/dest/logs/public_log.js +10 -0
- package/dest/messaging/l2_to_l1_message.js +1 -1
- package/dest/tx/tx.d.ts.map +1 -1
- package/dest/tx/tx.js +4 -6
- package/dest/tx/tx_effect.d.ts.map +1 -1
- package/dest/tx/tx_effect.js +26 -10
- package/dest/tx/tx_receipt.js +1 -1
- package/package.json +8 -7
- package/src/contract/interfaces/contract_instance_update.ts +1 -1
- package/src/contract/interfaces/node-info.ts +2 -2
- package/src/database-version/README.md +63 -0
- package/src/database-version/index.ts +1 -0
- package/src/database-version/version_manager.ts +207 -0
- package/src/errors/simulation_error.ts +2 -2
- package/src/hash/hash.ts +0 -10
- package/src/interfaces/world_state.ts +3 -3
- package/src/kernel/private_kernel_tail_circuit_public_inputs.ts +5 -1
- package/src/logs/contract_class_log.ts +28 -9
- package/src/logs/private_log.ts +11 -0
- package/src/logs/public_log.ts +12 -0
- package/src/messaging/l2_to_l1_message.ts +1 -1
- package/src/tx/tx.ts +7 -7
- package/src/tx/tx_effect.ts +29 -9
- package/src/tx/tx_receipt.ts +1 -1
package/dest/tx/tx.js
CHANGED
|
@@ -3,10 +3,8 @@ import { arraySerializedSizeOfNonEmpty } from '@aztec/foundation/collection';
|
|
|
3
3
|
import { Fr } from '@aztec/foundation/fields';
|
|
4
4
|
import { BufferReader, serializeArrayOfBufferableToVector, serializeToBuffer } from '@aztec/foundation/serialize';
|
|
5
5
|
import { z } from 'zod';
|
|
6
|
-
import { siloContractClassLog } from '../hash/hash.js';
|
|
7
6
|
import { PrivateKernelTailCircuitPublicInputs } from '../kernel/private_kernel_tail_circuit_public_inputs.js';
|
|
8
7
|
import { ContractClassLog } from '../logs/contract_class_log.js';
|
|
9
|
-
import { PrivateLog } from '../logs/private_log.js';
|
|
10
8
|
import { Gossipable } from '../p2p/gossipable.js';
|
|
11
9
|
import { TopicType, createTopicString } from '../p2p/topic_type.js';
|
|
12
10
|
import { ClientIvcProof } from '../proofs/client_ivc_proof.js';
|
|
@@ -135,9 +133,9 @@ import { TxHash } from './tx_hash.js';
|
|
|
135
133
|
const contractClassLogs = [];
|
|
136
134
|
for (const log of this.contractClassLogs){
|
|
137
135
|
const hashedLog = await log.hash();
|
|
138
|
-
const logHash = logHashes.find((hash)=>hash.value.equals(hashedLog));
|
|
136
|
+
const logHash = logHashes.find((hash)=>hash.value.equals(hashedLog) && hash.contractAddress.equals(log.contractAddress));
|
|
139
137
|
if (logHash) {
|
|
140
|
-
contractClassLogs.push(silo ? await
|
|
138
|
+
contractClassLogs.push(silo ? await log.silo() : log);
|
|
141
139
|
}
|
|
142
140
|
}
|
|
143
141
|
return contractClassLogs;
|
|
@@ -167,7 +165,7 @@ import { TxHash } from './tx_hash.js';
|
|
|
167
165
|
nullifierCount: this.data.getNonEmptyNullifiers().length,
|
|
168
166
|
privateLogCount: this.data.getNonEmptyPrivateLogs().length,
|
|
169
167
|
classRegisteredCount: this.data.getNonEmptyContractClassLogsHashes().length,
|
|
170
|
-
contractClassLogSize: this.data.
|
|
168
|
+
contractClassLogSize: this.data.getEmittedContractClassLogsLength(),
|
|
171
169
|
proofSize: this.clientIvcProof.clientIvcProofBuffer.length,
|
|
172
170
|
size: this.toBuffer().length,
|
|
173
171
|
feePaymentMethod: // needsSetup? then we pay through a fee payment contract
|
|
@@ -183,7 +181,7 @@ import { TxHash } from './tx_hash.js';
|
|
|
183
181
|
* Estimates the tx size based on its private effects. Note that the actual size of the tx
|
|
184
182
|
* after processing will probably be larger, as public execution would generate more data.
|
|
185
183
|
*/ getEstimatedPrivateTxEffectsSize() {
|
|
186
|
-
return this.data.getNonEmptyNoteHashes().length * Fr.SIZE_IN_BYTES + this.data.getNonEmptyNullifiers().length * Fr.SIZE_IN_BYTES + this.data.
|
|
184
|
+
return this.data.getNonEmptyNoteHashes().length * Fr.SIZE_IN_BYTES + this.data.getNonEmptyNullifiers().length * Fr.SIZE_IN_BYTES + this.data.getEmittedPrivateLogsLength() * Fr.SIZE_IN_BYTES + this.data.getEmittedContractClassLogsLength() * Fr.SIZE_IN_BYTES;
|
|
187
185
|
}
|
|
188
186
|
/**
|
|
189
187
|
* Convenience function to get a hash out of a tx or a tx-like.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tx_effect.d.ts","sourceRoot":"","sources":["../../src/tx/tx_effect.ts"],"names":[],"mappings":";;;AAoBA,OAAO,EAAE,KAAK,QAAQ,EAA6B,MAAM,yBAAyB,CAAC;AAInF,OAAO,EAAE,EAAE,EAAE,MAAM,0BAA0B,CAAC;AAC9C,OAAO,EAAE,KAAK,MAAM,EAAW,MAAM,2BAA2B,CAAC;AACjE,OAAO,EACL,YAAY,EACZ,WAAW,EAGZ,MAAM,6BAA6B,CAAC;AAGrC,OAAO,EAAE,OAAO,EAAE,MAAM,MAAM,CAAC;AAG/B,OAAO,EAAE,eAAe,EAAE,MAAM,6BAA6B,CAAC;AAC9D,OAAO,EAAE,UAAU,EAAE,MAAM,uBAAuB,CAAC;AACnD,OAAO,EAAE,gBAAgB,EAAE,MAAM,+BAA+B,CAAC;AACjE,OAAO,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAC;AACpD,OAAO,EAAE,SAAS,EAAE,MAAM,uBAAuB,CAAC;AAClD,OAAO,EAAE,MAAM,EAAE,MAAM,cAAc,CAAC;AAGtC,eAAO,MAAM,eAAe,uBAAuB,CAAC;AAEpD,eAAO,MAAM,4BAA4B,QAA0C,CAAC;AAEpF,eAAO,MAAM,4BAA4B,QAAmC,CAAC;AAE7E,qBAAa,QAAQ;IAEjB;;OAEG;IACI,UAAU,EAAE,UAAU;IAC7B;;OAEG;IACI,MAAM,EAAE,MAAM;IACrB;;OAEG;IACI,cAAc,EAAE,EAAE;IACzB;;OAEG;IACI,UAAU,EAAE,EAAE,EAAE;IACvB;;OAEG;IACI,UAAU,EAAE,EAAE,EAAE;IACvB;;;OAGG;IACI,UAAU,EAAE,EAAE,EAAE;IACvB;;OAEG;IACI,gBAAgB,EAAE,eAAe,EAAE;IAC1C;;OAEG;IACI,WAAW,EAAE,UAAU,EAAE;IAChC;;OAEG;IACI,UAAU,EAAE,SAAS,EAAE;IAC9B;;OAEG;IACI,iBAAiB,EAAE,gBAAgB,EAAE;;IAxC5C;;OAEG;IACI,UAAU,EAAE,UAAU;IAC7B;;OAEG;IACI,MAAM,EAAE,MAAM;IACrB;;OAEG;IACI,cAAc,EAAE,EAAE;IACzB;;OAEG;IACI,UAAU,EAAE,EAAE,EAAE;IACvB;;OAEG;IACI,UAAU,EAAE,EAAE,EAAE;IACvB;;;OAGG;IACI,UAAU,EAAE,EAAE,EAAE;IACvB;;OAEG;IACI,gBAAgB,EAAE,eAAe,EAAE;IAC1C;;OAEG;IACI,WAAW,EAAE,UAAU,EAAE;IAChC;;OAEG;IACI,UAAU,EAAE,SAAS,EAAE;IAC9B;;OAEG;IACI,iBAAiB,EAAE,gBAAgB,EAAE;IAoD9C,QAAQ,IAAI,MAAM;IAelB,yEAAyE;IACzE,SAAS;IAIT;;;;OAIG;IACH,MAAM,CAAC,UAAU,CAAC,MAAM,EAAE,MAAM,GAAG,YAAY,GAAG,QAAQ;IAiB1D;;;;OAIG;IACH,SAAS;WAuBI,MAAM,CAAC,mBAAmB,SAAI,EAAE,oBAAoB,SAAI,GAAG,OAAO,CAAC,QAAQ,CAAC;IAezF,MAAM,CAAC,KAAK,IAAI,QAAQ;IAIxB,OAAO,IAAI,OAAO;IAIlB,2DAA2D;IAC3D,QAAQ;IAIR;;;OAGG;IACH,OAAO,CAAC,QAAQ;IAOhB;;OAEG;IACH,MAAM,CAAC,UAAU,CAAC,MAAM,EAAE,EAAE;;;;IAK5B;;;OAGG;IACH,OAAO,CAAC,gBAAgB;IAgBxB;;;;OAIG;IACH,MAAM,CAAC,gBAAgB,CAAC,KAAK,EAAE,EAAE;;;;IAQjC;;OAEG;IACH,MAAM,CAAC,YAAY,CAAC,KAAK,EAAE,EAAE;IAuB7B;;OAEG;IACH,YAAY,IAAI,EAAE,EAAE;
|
|
1
|
+
{"version":3,"file":"tx_effect.d.ts","sourceRoot":"","sources":["../../src/tx/tx_effect.ts"],"names":[],"mappings":"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"}
|
package/dest/tx/tx_effect.js
CHANGED
|
@@ -270,15 +270,26 @@ export class TxEffect {
|
|
|
270
270
|
]).flat());
|
|
271
271
|
}
|
|
272
272
|
if (this.privateLogs.length) {
|
|
273
|
-
|
|
274
|
-
|
|
273
|
+
const totalLogLen = this.privateLogs.reduce(// +1 for length prefix
|
|
274
|
+
(total, log)=>total + (log.getEmittedLength() == 0 ? 0 : log.getEmittedLength() + 1), 0);
|
|
275
|
+
flattened.push(this.toPrefix(PRIVATE_LOGS_PREFIX, totalLogLen));
|
|
276
|
+
flattened.push(...this.privateLogs.flatMap((l)=>[
|
|
277
|
+
new Fr(l.getEmittedLength()),
|
|
278
|
+
...l.getEmittedFields()
|
|
279
|
+
]));
|
|
275
280
|
}
|
|
276
281
|
if (this.publicLogs.length) {
|
|
277
|
-
|
|
278
|
-
|
|
282
|
+
const totalLogLen = this.publicLogs.reduce(// +1 for length prefix
|
|
283
|
+
(total, log)=>total + (log.getEmittedLength() == 0 ? 0 : log.getEmittedLength() + 1), 0);
|
|
284
|
+
flattened.push(this.toPrefix(PUBLIC_LOGS_PREFIX, totalLogLen));
|
|
285
|
+
flattened.push(...this.publicLogs.flatMap((l)=>[
|
|
286
|
+
new Fr(l.getEmittedLength()),
|
|
287
|
+
...l.getEmittedFields()
|
|
288
|
+
]));
|
|
279
289
|
}
|
|
280
290
|
if (this.contractClassLogs.length) {
|
|
281
|
-
const totalLogLen = this.contractClassLogs.reduce(
|
|
291
|
+
const totalLogLen = this.contractClassLogs.reduce(// +2 for length prefix and contract address
|
|
292
|
+
(total, log)=>total + (log.getEmittedLength() == 0 ? 0 : log.getEmittedLength() + 2), 0);
|
|
282
293
|
flattened.push(this.toPrefix(CONTRACT_CLASS_LOGS_PREFIX, totalLogLen));
|
|
283
294
|
flattened.push(...this.contractClassLogs.flatMap((l)=>[
|
|
284
295
|
new Fr(l.getEmittedLength()),
|
|
@@ -347,11 +358,13 @@ export class TxEffect {
|
|
|
347
358
|
}
|
|
348
359
|
case PRIVATE_LOGS_PREFIX:
|
|
349
360
|
{
|
|
350
|
-
// TODO(Miranda): squash log 0s in a nested loop and add len prefix?
|
|
351
361
|
ensureEmpty(effect.privateLogs);
|
|
352
362
|
const flatPrivateLogs = reader.readFieldArray(length);
|
|
353
|
-
|
|
354
|
-
|
|
363
|
+
let i = 0;
|
|
364
|
+
while(i < length){
|
|
365
|
+
const logLen = flatPrivateLogs[i++].toNumber();
|
|
366
|
+
const logFields = flatPrivateLogs.slice(i, i += logLen);
|
|
367
|
+
effect.privateLogs.push(PrivateLog.fromFields(logFields.concat(new Array(PRIVATE_LOG_SIZE_IN_FIELDS - logLen).fill(Fr.ZERO))));
|
|
355
368
|
}
|
|
356
369
|
break;
|
|
357
370
|
}
|
|
@@ -359,8 +372,11 @@ export class TxEffect {
|
|
|
359
372
|
{
|
|
360
373
|
ensureEmpty(effect.publicLogs);
|
|
361
374
|
const flatPublicLogs = reader.readFieldArray(length);
|
|
362
|
-
|
|
363
|
-
|
|
375
|
+
let i = 0;
|
|
376
|
+
while(i < length){
|
|
377
|
+
const logLen = flatPublicLogs[i++].toNumber();
|
|
378
|
+
const logFields = flatPublicLogs.slice(i, i += logLen);
|
|
379
|
+
effect.publicLogs.push(PublicLog.fromFields(logFields.concat(new Array(PUBLIC_LOG_SIZE_IN_FIELDS - logLen).fill(Fr.ZERO))));
|
|
364
380
|
}
|
|
365
381
|
break;
|
|
366
382
|
}
|
package/dest/tx/tx_receipt.js
CHANGED
|
@@ -46,7 +46,7 @@ import { TxHash } from './tx_hash.js';
|
|
|
46
46
|
status: z.nativeEnum(TxStatus),
|
|
47
47
|
error: z.string(),
|
|
48
48
|
blockHash: L2BlockHash.schema.optional(),
|
|
49
|
-
blockNumber: z.number().optional(),
|
|
49
|
+
blockNumber: z.number().int().nonnegative().optional(),
|
|
50
50
|
transactionFee: schemas.BigInt.optional(),
|
|
51
51
|
debugInfo: DebugInfoSchema.optional()
|
|
52
52
|
}).transform(TxReceipt.from);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@aztec/stdlib",
|
|
3
|
-
"version": "0.77.
|
|
3
|
+
"version": "0.77.1",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"inherits": [
|
|
6
6
|
"../package.common.json",
|
|
@@ -46,7 +46,8 @@
|
|
|
46
46
|
"./interfaces/server": "./dest/interfaces/server.js",
|
|
47
47
|
"./epoch-helpers": "./dest/epoch-helpers/index.js",
|
|
48
48
|
"./config": "./dest/config/index.js",
|
|
49
|
-
"./testing/jest": "./dest/tests/jest.js"
|
|
49
|
+
"./testing/jest": "./dest/tests/jest.js",
|
|
50
|
+
"./database-version": "./dest/database-version/index.js"
|
|
50
51
|
},
|
|
51
52
|
"typedocOptions": {
|
|
52
53
|
"entryPoints": [
|
|
@@ -65,11 +66,11 @@
|
|
|
65
66
|
"test": "NODE_NO_WARNINGS=1 node --experimental-vm-modules ../node_modules/.bin/jest --passWithNoTests --maxWorkers=${JEST_MAX_WORKERS:-8}"
|
|
66
67
|
},
|
|
67
68
|
"dependencies": {
|
|
68
|
-
"@aztec/bb.js": "0.77.
|
|
69
|
-
"@aztec/blob-lib": "0.77.
|
|
70
|
-
"@aztec/constants": "0.77.
|
|
71
|
-
"@aztec/ethereum": "0.77.
|
|
72
|
-
"@aztec/foundation": "0.77.
|
|
69
|
+
"@aztec/bb.js": "0.77.1",
|
|
70
|
+
"@aztec/blob-lib": "0.77.1",
|
|
71
|
+
"@aztec/constants": "0.77.1",
|
|
72
|
+
"@aztec/ethereum": "0.77.1",
|
|
73
|
+
"@aztec/foundation": "0.77.1",
|
|
73
74
|
"lodash.chunk": "^4.2.0",
|
|
74
75
|
"lodash.isequal": "^4.5.0",
|
|
75
76
|
"lodash.omit": "^4.5.0",
|
|
@@ -22,7 +22,7 @@ export type ContractInstanceUpdateWithAddress = ContractInstanceUpdate & { addre
|
|
|
22
22
|
export const ContractInstanceUpdateSchema = z.object({
|
|
23
23
|
prevContractClassId: schemas.Fr,
|
|
24
24
|
newContractClassId: schemas.Fr,
|
|
25
|
-
blockOfChange: z.number(),
|
|
25
|
+
blockOfChange: z.number().int().nonnegative(),
|
|
26
26
|
}) satisfies ZodFor<ContractInstanceUpdate>;
|
|
27
27
|
|
|
28
28
|
export const ContractInstanceUpdateWithAddressSchema = ContractInstanceUpdateSchema.and(
|
|
@@ -24,8 +24,8 @@ export interface NodeInfo {
|
|
|
24
24
|
export const NodeInfoSchema: ZodFor<NodeInfo> = z
|
|
25
25
|
.object({
|
|
26
26
|
nodeVersion: z.string(),
|
|
27
|
-
l1ChainId: z.number(),
|
|
28
|
-
protocolVersion: z.number(),
|
|
27
|
+
l1ChainId: z.number().int().nonnegative(),
|
|
28
|
+
protocolVersion: z.number().int().nonnegative(),
|
|
29
29
|
enr: z.string().optional(),
|
|
30
30
|
l1ContractAddresses: L1ContractAddressesSchema,
|
|
31
31
|
protocolContractAddresses: ProtocolContractAddressesSchema,
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
# Version Manager
|
|
2
|
+
|
|
3
|
+
The Version Manager helps manage database migrations and version compatibility across different versions of the software.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- Track database schema versions
|
|
8
|
+
- Handle migrations between versions
|
|
9
|
+
- Reset database when necessary (incompatible versions, rollup address change)
|
|
10
|
+
- Simple, clean API for version management
|
|
11
|
+
|
|
12
|
+
## Usage
|
|
13
|
+
|
|
14
|
+
```typescript
|
|
15
|
+
import { EthAddress } from '@aztec/foundation/eth-address';
|
|
16
|
+
import { version } from '@aztec/foundation';
|
|
17
|
+
|
|
18
|
+
// Define your current database version
|
|
19
|
+
const DB_VERSION = 3;
|
|
20
|
+
const rollupAddress = EthAddress.fromString('0x1234567890123456789012345678901234567890');
|
|
21
|
+
|
|
22
|
+
// Create version manager for your service
|
|
23
|
+
const versionManager = new version.VersionManager(DB_VERSION, rollupAddress, {
|
|
24
|
+
dataDir: '/path/to/data',
|
|
25
|
+
serviceName: 'my-database',
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
// When initializing your database
|
|
29
|
+
await versionManager.checkVersionAndHandle(
|
|
30
|
+
// Called when a reset is needed
|
|
31
|
+
async () => {
|
|
32
|
+
// Initialize a fresh database
|
|
33
|
+
await initializeFreshDatabase();
|
|
34
|
+
},
|
|
35
|
+
// Called when an upgrade is needed (optional)
|
|
36
|
+
async (oldVersion, newVersion) => {
|
|
37
|
+
if (oldVersion === 1 && newVersion === 2) {
|
|
38
|
+
// Migrate from version 1 to 2
|
|
39
|
+
await migrateV1ToV2();
|
|
40
|
+
} else if (oldVersion === 2 && newVersion === 3) {
|
|
41
|
+
// Migrate from version 2 to 3
|
|
42
|
+
await migrateV2ToV3();
|
|
43
|
+
} else {
|
|
44
|
+
// Unsupported migration path, will fall back to reset
|
|
45
|
+
throw new Error(`Cannot upgrade from ${oldVersion} to ${newVersion}`);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
);
|
|
49
|
+
|
|
50
|
+
// Get the data directory for your service
|
|
51
|
+
const dataDir = versionManager.getDataDirectory();
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
## Automatic Reset Conditions
|
|
55
|
+
|
|
56
|
+
The database will be reset in the following conditions:
|
|
57
|
+
|
|
58
|
+
1. No version information exists (first run)
|
|
59
|
+
2. Rollup address has changed
|
|
60
|
+
3. Version has changed and no upgrade callback is provided
|
|
61
|
+
4. Upgrade callback throws an error
|
|
62
|
+
|
|
63
|
+
When a reset occurs, the data directory is deleted and recreated, and the reset callback is called to initialize a fresh database.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './version_manager.js';
|
|
@@ -0,0 +1,207 @@
|
|
|
1
|
+
import { EthAddress } from '@aztec/foundation/eth-address';
|
|
2
|
+
import { jsonParseWithSchemaSync, jsonStringify } from '@aztec/foundation/json-rpc';
|
|
3
|
+
import { createLogger } from '@aztec/foundation/log';
|
|
4
|
+
|
|
5
|
+
import fs from 'fs/promises';
|
|
6
|
+
import { join } from 'path';
|
|
7
|
+
import { z } from 'zod';
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Represents a version record for storing in a version file.
|
|
11
|
+
*/
|
|
12
|
+
export class DatabaseVersion {
|
|
13
|
+
constructor(public readonly schemaVersion: number, public readonly rollupAddress: EthAddress) {}
|
|
14
|
+
|
|
15
|
+
public toBuffer(): Buffer {
|
|
16
|
+
return Buffer.from(jsonStringify(this));
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
public static fromBuffer(buf: Buffer): DatabaseVersion {
|
|
20
|
+
try {
|
|
21
|
+
return jsonParseWithSchemaSync(buf.toString('utf-8'), DatabaseVersion.schema);
|
|
22
|
+
} catch (err) {
|
|
23
|
+
throw new Error(`Failed to deserialize version information: ${err}`, { cause: err });
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Compares two versions. If the rollups addresses are different then it returns undefined
|
|
29
|
+
*/
|
|
30
|
+
public cmp(other: DatabaseVersion): undefined | -1 | 0 | 1 {
|
|
31
|
+
if (this.rollupAddress.equals(other.rollupAddress)) {
|
|
32
|
+
if (this.schemaVersion < other.schemaVersion) {
|
|
33
|
+
return -1;
|
|
34
|
+
} else if (this.schemaVersion > other.schemaVersion) {
|
|
35
|
+
return 1;
|
|
36
|
+
} else {
|
|
37
|
+
return 0;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
return undefined;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Checks if two versions exactly match
|
|
45
|
+
*/
|
|
46
|
+
public equals(other: DatabaseVersion): boolean {
|
|
47
|
+
return this.cmp(other) === 0;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Returns the schema for this class
|
|
52
|
+
*/
|
|
53
|
+
static get schema() {
|
|
54
|
+
return z
|
|
55
|
+
.object({
|
|
56
|
+
schemaVersion: z.number(),
|
|
57
|
+
rollupAddress: EthAddress.schema,
|
|
58
|
+
})
|
|
59
|
+
.transform(({ schemaVersion, rollupAddress }) => new DatabaseVersion(schemaVersion, rollupAddress));
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Returns an empty instance
|
|
64
|
+
*/
|
|
65
|
+
static empty() {
|
|
66
|
+
return new DatabaseVersion(0, EthAddress.ZERO);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
export type DatabaseVersionManagerFs = Pick<typeof fs, 'readFile' | 'writeFile' | 'rm' | 'mkdir'>;
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* A manager for handling database versioning and migrations.
|
|
74
|
+
* This class will check the version of data in a directory and either
|
|
75
|
+
* reset or upgrade based on version compatibility.
|
|
76
|
+
*/
|
|
77
|
+
export class DatabaseVersionManager<T> {
|
|
78
|
+
public static readonly VERSION_FILE = 'db_version';
|
|
79
|
+
|
|
80
|
+
private readonly versionFile: string;
|
|
81
|
+
private readonly currentVersion: DatabaseVersion;
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Create a new version manager
|
|
85
|
+
*
|
|
86
|
+
* @param schemaVersion - The current version of the application
|
|
87
|
+
* @param rollupAddress - The rollup contract address
|
|
88
|
+
* @param dataDirectory - The directory where version information will be stored
|
|
89
|
+
* @param onOpen - A callback to the open the database at the given location
|
|
90
|
+
* @param onUpgrade - An optional callback to upgrade the database before opening. If not provided it will reset the database
|
|
91
|
+
* @param fileSystem - An interface to access the filesystem
|
|
92
|
+
* @param log - Optional custom logger
|
|
93
|
+
* @param options - Configuration options
|
|
94
|
+
*/
|
|
95
|
+
constructor(
|
|
96
|
+
schemaVersion: number,
|
|
97
|
+
rollupAddress: EthAddress,
|
|
98
|
+
private dataDirectory: string,
|
|
99
|
+
private onOpen: (dataDir: string) => Promise<T>,
|
|
100
|
+
private onUpgrade?: (dataDir: string, currentVersion: number, latestVersion: number) => Promise<void>,
|
|
101
|
+
private fileSystem: DatabaseVersionManagerFs = fs,
|
|
102
|
+
private log = createLogger(`foundation:version-manager`),
|
|
103
|
+
) {
|
|
104
|
+
if (schemaVersion < 1) {
|
|
105
|
+
throw new TypeError(`Invalid schema version received: ${schemaVersion}`);
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
this.versionFile = join(this.dataDirectory, DatabaseVersionManager.VERSION_FILE);
|
|
109
|
+
this.currentVersion = new DatabaseVersion(schemaVersion, rollupAddress);
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* Checks the stored version against the current version and handles the outcome
|
|
114
|
+
* by either resetting the data directory or calling an upgrade function
|
|
115
|
+
*
|
|
116
|
+
* @param onReset - Function to call when a full reset is needed
|
|
117
|
+
* @param onUpgrade - Function to call when an upgrade is needed
|
|
118
|
+
* @returns True if data was reset, false if upgraded or no change needed
|
|
119
|
+
*/
|
|
120
|
+
public async open(): Promise<[T, boolean]> {
|
|
121
|
+
// const storedVersion = await DatabaseVersion.readVersion(this.versionFile);
|
|
122
|
+
let storedVersion: DatabaseVersion;
|
|
123
|
+
|
|
124
|
+
try {
|
|
125
|
+
const versionBuf = await this.fileSystem.readFile(this.versionFile);
|
|
126
|
+
storedVersion = DatabaseVersion.fromBuffer(versionBuf);
|
|
127
|
+
} catch (err) {
|
|
128
|
+
if (err && (err as Error & { code: string }).code === 'ENOENT') {
|
|
129
|
+
storedVersion = DatabaseVersion.empty();
|
|
130
|
+
} else {
|
|
131
|
+
this.log.warn(`Failed to read stored version information: ${err}. Defaulting to empty version`);
|
|
132
|
+
storedVersion = DatabaseVersion.empty();
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
const cmp = storedVersion.cmp(this.currentVersion);
|
|
137
|
+
let needsReset = false;
|
|
138
|
+
|
|
139
|
+
if (typeof cmp === 'number') {
|
|
140
|
+
// only allow forward upgrades
|
|
141
|
+
if (cmp === -1 && this.onUpgrade) {
|
|
142
|
+
this.log.info(`Upgrading from version ${storedVersion.schemaVersion} to ${this.currentVersion.schemaVersion}`);
|
|
143
|
+
try {
|
|
144
|
+
await this.onUpgrade(this.dataDirectory, storedVersion.schemaVersion, this.currentVersion.schemaVersion);
|
|
145
|
+
} catch (error) {
|
|
146
|
+
this.log.error(`Failed to upgrade: ${error}. Falling back to reset.`);
|
|
147
|
+
needsReset = true;
|
|
148
|
+
}
|
|
149
|
+
} else if (cmp !== 0) {
|
|
150
|
+
this.log.info(
|
|
151
|
+
`Can't upgrade from version ${storedVersion.schemaVersion} to ${this.currentVersion}. Resetting database at ${this.dataDirectory}`,
|
|
152
|
+
);
|
|
153
|
+
needsReset = true;
|
|
154
|
+
}
|
|
155
|
+
} else {
|
|
156
|
+
this.log.warn('Rollup address changed, resetting data directory');
|
|
157
|
+
needsReset = true;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
// Handle reset if needed
|
|
161
|
+
if (needsReset) {
|
|
162
|
+
await this.resetDataDirectory();
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
// Write the current version to disk
|
|
166
|
+
await this.writeVersion();
|
|
167
|
+
|
|
168
|
+
return [await this.onOpen(this.dataDirectory), needsReset];
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
/**
|
|
172
|
+
* Writes the current version to the version file
|
|
173
|
+
*/
|
|
174
|
+
private async writeVersion(): Promise<void> {
|
|
175
|
+
// Ensure the directory exists
|
|
176
|
+
await this.fileSystem.mkdir(this.dataDirectory, { recursive: true });
|
|
177
|
+
// Write the version file
|
|
178
|
+
await this.fileSystem.writeFile(this.versionFile, this.currentVersion.toBuffer());
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
/**
|
|
182
|
+
* Resets the data directory by deleting it and recreating it
|
|
183
|
+
*/
|
|
184
|
+
private async resetDataDirectory(): Promise<void> {
|
|
185
|
+
try {
|
|
186
|
+
await this.fileSystem.rm(this.dataDirectory, { recursive: true, force: true, maxRetries: 3 });
|
|
187
|
+
await this.fileSystem.mkdir(this.dataDirectory, { recursive: true });
|
|
188
|
+
} catch (err) {
|
|
189
|
+
this.log.error(`Failed to reset data directory: ${err}`);
|
|
190
|
+
throw new Error(`Failed to reset data directory: ${err}`, { cause: err });
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
/**
|
|
195
|
+
* Get the data directory path
|
|
196
|
+
*/
|
|
197
|
+
public getDataDirectory(): string {
|
|
198
|
+
return this.dataDirectory;
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
/**
|
|
202
|
+
* Get the current version number
|
|
203
|
+
*/
|
|
204
|
+
public getSchemaVersion(): number {
|
|
205
|
+
return this.currentVersion.schemaVersion;
|
|
206
|
+
}
|
|
207
|
+
}
|
|
@@ -57,8 +57,8 @@ export interface SourceCodeLocation {
|
|
|
57
57
|
|
|
58
58
|
const SourceCodeLocationSchema = z.object({
|
|
59
59
|
filePath: z.string(),
|
|
60
|
-
line: z.number(),
|
|
61
|
-
column: z.number(),
|
|
60
|
+
line: z.number().int().nonnegative(),
|
|
61
|
+
column: z.number().int().nonnegative(),
|
|
62
62
|
fileSource: z.string(),
|
|
63
63
|
locationText: z.string(),
|
|
64
64
|
});
|
package/src/hash/hash.ts
CHANGED
|
@@ -3,7 +3,6 @@ import { poseidon2Hash, poseidon2HashWithSeparator, sha256Trunc } from '@aztec/f
|
|
|
3
3
|
import { Fr } from '@aztec/foundation/fields';
|
|
4
4
|
|
|
5
5
|
import type { AztecAddress } from '../aztec-address/index.js';
|
|
6
|
-
import type { ContractClassLog } from '../logs/index.js';
|
|
7
6
|
import type { ScopedL2ToL1Message } from '../messaging/l2_to_l1_message.js';
|
|
8
7
|
|
|
9
8
|
/**
|
|
@@ -132,12 +131,3 @@ export function siloL2ToL1Message(l2ToL1Message: ScopedL2ToL1Message, version: F
|
|
|
132
131
|
]);
|
|
133
132
|
return Fr.fromBuffer(sha256Trunc(preimage));
|
|
134
133
|
}
|
|
135
|
-
|
|
136
|
-
export async function siloContractClassLog(log: ContractClassLog, contract: AztecAddress): Promise<ContractClassLog> {
|
|
137
|
-
const innerLog = log.clone();
|
|
138
|
-
if (contract.isZero()) {
|
|
139
|
-
return innerLog;
|
|
140
|
-
}
|
|
141
|
-
innerLog.fields[0] = await poseidon2Hash([contract, innerLog.fields[0]]);
|
|
142
|
-
return innerLog;
|
|
143
|
-
}
|
|
@@ -76,9 +76,9 @@ export interface WorldStateSynchronizer extends ForkMerkleTreeOperations {
|
|
|
76
76
|
}
|
|
77
77
|
|
|
78
78
|
export const WorldStateSyncStatusSchema = z.object({
|
|
79
|
-
finalisedBlockNumber: z.number(),
|
|
80
|
-
latestBlockNumber: z.number(),
|
|
79
|
+
finalisedBlockNumber: z.number().int().nonnegative(),
|
|
80
|
+
latestBlockNumber: z.number().int().nonnegative(),
|
|
81
81
|
latestBlockHash: z.string(),
|
|
82
|
-
oldestHistoricBlockNumber: z.number(),
|
|
82
|
+
oldestHistoricBlockNumber: z.number().int().nonnegative(),
|
|
83
83
|
treesAreSynched: z.boolean(),
|
|
84
84
|
}) satisfies z.ZodType<WorldStateSyncStatus>;
|
|
@@ -253,10 +253,14 @@ export class PrivateKernelTailCircuitPublicInputs {
|
|
|
253
253
|
return contractClassLogsHashes.filter(h => !h.isEmpty());
|
|
254
254
|
}
|
|
255
255
|
|
|
256
|
-
|
|
256
|
+
getEmittedContractClassLogsLength() {
|
|
257
257
|
return this.getNonEmptyContractClassLogsHashes().reduce((total, log) => total + log.logHash.length, 0);
|
|
258
258
|
}
|
|
259
259
|
|
|
260
|
+
getEmittedPrivateLogsLength() {
|
|
261
|
+
return this.getNonEmptyPrivateLogs().reduce((total, log) => total + log.getEmittedLength(), 0);
|
|
262
|
+
}
|
|
263
|
+
|
|
260
264
|
static fromBuffer(buffer: Buffer | BufferReader): PrivateKernelTailCircuitPublicInputs {
|
|
261
265
|
const reader = BufferReader.asReader(buffer);
|
|
262
266
|
const isForPublic = reader.readBoolean();
|
|
@@ -11,6 +11,8 @@ import { AztecAddress } from '../aztec-address/index.js';
|
|
|
11
11
|
|
|
12
12
|
export class ContractClassLog {
|
|
13
13
|
static SIZE_IN_BYTES = Fr.SIZE_IN_BYTES * CONTRACT_CLASS_LOG_SIZE_IN_FIELDS;
|
|
14
|
+
// Keeps original first field pre-siloing. Only set by silo().
|
|
15
|
+
public unsiloedFirstField?: Fr | undefined;
|
|
14
16
|
|
|
15
17
|
// Below line gives error 'Type instantiation is excessively deep and possibly infinite. ts(2589)'
|
|
16
18
|
// public fields: Tuple<Fr, typeof CONTRACT_CLASS_LOG_DATA_SIZE_IN_FIELDS>
|
|
@@ -76,23 +78,40 @@ export class ContractClassLog {
|
|
|
76
78
|
|
|
77
79
|
getEmittedLength() {
|
|
78
80
|
// This assumes that we cut trailing zeroes from the end of the log. In ts, these will always be added back.
|
|
79
|
-
// Does not include address
|
|
81
|
+
// Does not include address or length prefix.
|
|
82
|
+
// Note: Unlike public logs, address is not included here because it is not included in the log itself.
|
|
80
83
|
return this.getEmittedFields().length;
|
|
81
84
|
}
|
|
82
85
|
|
|
83
86
|
getEmittedFields() {
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
87
|
+
const lastNonZeroIndex = this.fields.findLastIndex(f => !f.isZero());
|
|
88
|
+
return this.fields.slice(0, lastNonZeroIndex + 1);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
setUnsiloedFirstField(field: Fr) {
|
|
92
|
+
this.unsiloedFirstField = field;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
toUnsiloed() {
|
|
96
|
+
if (this.unsiloedFirstField) {
|
|
97
|
+
return new ContractClassLog(this.contractAddress, [this.unsiloedFirstField].concat(this.fields.slice(1)));
|
|
98
|
+
} else {
|
|
99
|
+
return this;
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
async silo() {
|
|
104
|
+
const innerLog = this.clone();
|
|
105
|
+
if (innerLog.contractAddress.isZero()) {
|
|
106
|
+
return innerLog;
|
|
90
107
|
}
|
|
91
|
-
|
|
108
|
+
innerLog.setUnsiloedFirstField(innerLog.fields[0]);
|
|
109
|
+
innerLog.fields[0] = await poseidon2Hash([innerLog.contractAddress, innerLog.fields[0]]);
|
|
110
|
+
return innerLog;
|
|
92
111
|
}
|
|
93
112
|
|
|
94
113
|
async hash() {
|
|
95
|
-
return await poseidon2Hash(this.
|
|
114
|
+
return await poseidon2Hash(this.fields);
|
|
96
115
|
}
|
|
97
116
|
|
|
98
117
|
static get schema() {
|
package/src/logs/private_log.ts
CHANGED
|
@@ -42,6 +42,17 @@ export class PrivateLog {
|
|
|
42
42
|
return new PrivateLog(makeTuple(PRIVATE_LOG_SIZE_IN_FIELDS, Fr.random));
|
|
43
43
|
}
|
|
44
44
|
|
|
45
|
+
getEmittedLength() {
|
|
46
|
+
// This assumes that we cut trailing zeroes from the end of the log. In ts, these will always be added back.
|
|
47
|
+
// Does not include length prefix.
|
|
48
|
+
return this.getEmittedFields().length;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
getEmittedFields() {
|
|
52
|
+
const lastNonZeroIndex = this.fields.findLastIndex(f => !f.isZero());
|
|
53
|
+
return this.fields.slice(0, lastNonZeroIndex + 1);
|
|
54
|
+
}
|
|
55
|
+
|
|
45
56
|
static get schema() {
|
|
46
57
|
return z
|
|
47
58
|
.object({
|
package/src/logs/public_log.ts
CHANGED
|
@@ -54,6 +54,18 @@ export class PublicLog {
|
|
|
54
54
|
return new PublicLog(await AztecAddress.random(), makeTuple(PUBLIC_LOG_DATA_SIZE_IN_FIELDS, Fr.random));
|
|
55
55
|
}
|
|
56
56
|
|
|
57
|
+
getEmittedLength() {
|
|
58
|
+
// This assumes that we cut trailing zeroes from the end of the log. In ts, these will always be added back.
|
|
59
|
+
// Does not include length prefix.
|
|
60
|
+
return this.getEmittedFields().length;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
getEmittedFields() {
|
|
64
|
+
const fields = this.toFields();
|
|
65
|
+
const lastNonZeroIndex = fields.findLastIndex(f => !f.isZero());
|
|
66
|
+
return fields.slice(0, lastNonZeroIndex + 1);
|
|
67
|
+
}
|
|
68
|
+
|
|
57
69
|
equals(other: this) {
|
|
58
70
|
return (
|
|
59
71
|
this.contractAddress.equals(other.contractAddress) &&
|
|
@@ -17,7 +17,7 @@ export class L2ToL1Message {
|
|
|
17
17
|
.object({
|
|
18
18
|
recipient: schemas.EthAddress,
|
|
19
19
|
content: schemas.Fr,
|
|
20
|
-
counter: z.number(),
|
|
20
|
+
counter: z.number().int().nonnegative(),
|
|
21
21
|
})
|
|
22
22
|
.transform(({ recipient, content, counter }) => new L2ToL1Message(recipient, content, counter));
|
|
23
23
|
}
|