@aztec-labs/archiver 6.0.0-rc.1 → 6.0.0-rc.3

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.
Files changed (42) hide show
  1. package/README.md +7 -1
  2. package/dest/errors.d.ts +20 -1
  3. package/dest/errors.d.ts.map +1 -1
  4. package/dest/errors.js +22 -0
  5. package/dest/l1/bin/retrieve-calldata.js +14 -4
  6. package/dest/l1/calldata_retriever.d.ts +15 -17
  7. package/dest/l1/calldata_retriever.d.ts.map +1 -1
  8. package/dest/l1/calldata_retriever.js +2 -9
  9. package/dest/l1/data_retrieval.d.ts +7 -3
  10. package/dest/l1/data_retrieval.d.ts.map +1 -1
  11. package/dest/l1/data_retrieval.js +14 -9
  12. package/dest/modules/data_store_updater.js +5 -5
  13. package/dest/modules/l1_synchronizer.d.ts +1 -1
  14. package/dest/modules/l1_synchronizer.d.ts.map +1 -1
  15. package/dest/modules/l1_synchronizer.js +228 -191
  16. package/dest/modules/validation.d.ts +14 -8
  17. package/dest/modules/validation.d.ts.map +1 -1
  18. package/dest/modules/validation.js +72 -22
  19. package/dest/store/block_store.d.ts +2 -1
  20. package/dest/store/block_store.d.ts.map +1 -1
  21. package/dest/store/block_store.js +34 -14
  22. package/dest/store/contract_class_store.d.ts +1 -1
  23. package/dest/store/contract_class_store.d.ts.map +1 -1
  24. package/dest/store/contract_class_store.js +4 -2
  25. package/dest/store/contract_instance_store.d.ts +5 -4
  26. package/dest/store/contract_instance_store.d.ts.map +1 -1
  27. package/dest/store/contract_instance_store.js +20 -9
  28. package/dest/test/fake_l1_state.d.ts +3 -1
  29. package/dest/test/fake_l1_state.d.ts.map +1 -1
  30. package/dest/test/fake_l1_state.js +4 -4
  31. package/package.json +13 -13
  32. package/src/errors.ts +32 -0
  33. package/src/l1/bin/retrieve-calldata.ts +15 -4
  34. package/src/l1/calldata_retriever.ts +14 -20
  35. package/src/l1/data_retrieval.ts +22 -14
  36. package/src/modules/data_store_updater.ts +4 -4
  37. package/src/modules/l1_synchronizer.ts +294 -261
  38. package/src/modules/validation.ts +87 -28
  39. package/src/store/block_store.ts +41 -13
  40. package/src/store/contract_class_store.ts +4 -4
  41. package/src/store/contract_instance_store.ts +24 -17
  42. package/src/test/fake_l1_state.ts +9 -3
@@ -391,7 +391,7 @@ import { InitialCheckpointNumberNotSequentialError } from '../errors.js';
391
391
  import { getCheckpointBlobDataFromBlobs, retrieveCheckpointCalldataFromRollup, retrievedToPublishedCheckpoint } from '../l1/data_retrieval.js';
392
392
  import { ArchiverDataStoreUpdater, blockLeafCount } from './data_store_updater.js';
393
393
  import { InboxMessageSynchronizer } from './inbox_message_synchronizer.js';
394
- import { validateCheckpointAttestationsFromCalldata } from './validation.js';
394
+ import { resolveCheckpointAttestationsFromCalldata } from './validation.js';
395
395
  /** Bounded Inbox message sync passes (a recovery step each) taken within one archiver sync iteration. */ const MAX_MESSAGE_SYNC_PASSES_PER_ITERATION = 3;
396
396
  _dec = trackSpan('Archiver.syncFromL1'), _dec1 = trackSpan('Archiver.handleEpochPrune'), _dec2 = trackSpan('Archiver.syncL1ToL2Messages'), _dec3 = trackSpan('Archiver.handleCheckpoints');
397
397
  /**
@@ -1059,209 +1059,246 @@ _dec = trackSpan('Archiver.syncFromL1'), _dec1 = trackSpan('Archiver.handleEpoch
1059
1059
  searchStartBlock,
1060
1060
  searchEndBlock
1061
1061
  });
1062
- // Check if the last checkpoint matches a local pending entry (so we can skip blob fetch).
1063
- // We only check the last one; if it matches, the blob fetch is skipped for that entry.
1064
- // TODO(palla/pipelining): We may have more than a single checkpoint to promote
1065
- const lastCalldataCheckpoint = calldataCheckpoints[calldataCheckpoints.length - 1];
1066
- const promoteResult = await this.tryBuildPublishedCheckpointFromProposed(lastCalldataCheckpoint);
1067
- const checkpointToPromote = promoteResult && !('diverged' in promoteResult) ? promoteResult : undefined;
1068
- const evictProposedFrom = promoteResult && 'diverged' in promoteResult ? promoteResult.fromCheckpointNumber : undefined;
1069
- // Validate attestations from CALLDATA before fetching any blobs. A checkpoint with invalid
1070
- // attestations (or one descending from a rejected ancestor) is rejected here without fetching its
1071
- // blobs, so a malformed blob does not throw during decode before the rejection path runs and
1072
- // stall sync. The signed consensus payload (header, archive root, fee asset price
1073
- // modifier) is fully available from calldata.
1074
- const checkpointsToIngest = [];
1075
- for (const calldataCheckpoint of calldataCheckpoints){
1076
- // Check the attestations uploaded by the publisher to L1 are correct.
1077
- // Rollup contract does not validate attestations to save on gas, so this
1078
- // falls on the nodes to verify offchain and skip those checkpoints.
1079
- const validationResult = this.config.skipValidateCheckpointAttestations ? {
1080
- valid: true
1081
- } : await validateCheckpointAttestationsFromCalldata(calldataCheckpoint, this.epochCache, this.l1Constants, this.getSignatureContext(), this.log);
1082
- // Also skip the checkpoint if it builds on a previously-rejected ancestor. Without
1083
- // this, addCheckpoints would throw InitialCheckpointNumberNotSequentialError when the
1084
- // ancestor was skipped earlier (e.g. due to invalid attestations), the catch handler
1085
- // would roll back the L1 sync point, and the next iteration would re-fetch and re-throw.
1086
- const rejectedAncestor = await this.stores.blocks.getRejectedCheckpointByArchiveRoot(calldataCheckpoint.header.lastArchiveRoot);
1087
- // Update the validation result if it has changed, so we can keep track of the first invalid checkpoint
1088
- // in case there is a sequence of more than one invalid checkpoint, as we need to invalidate the first one.
1089
- // There is an exception though: if a checkpoint is invalidated and replaced with another invalid checkpoint,
1090
- // we need to update the validation result, since we need to be able to invalidate the new one.
1091
- // See test 'chain progresses if an invalid checkpoint is invalidated with an invalid one' for more info.
1092
- // Do not update the validation result if there is a rejected ancestor, since in that case we want to keep the
1093
- // original invalidation, as the new checkpoint is extending from a previous invalid one.
1094
- const validStatusChanged = rollupStatus.validationResult?.valid !== validationResult.valid;
1095
- const invalidStatusWithSameCheckpointNumber = !validationResult.valid && rollupStatus.validationResult && !rollupStatus.validationResult.valid && rollupStatus.validationResult.checkpoint.checkpointNumber === validationResult.checkpoint.checkpointNumber;
1096
- if (!rejectedAncestor && (validStatusChanged || invalidStatusWithSameCheckpointNumber)) {
1097
- rollupStatus.validationResult = validationResult;
1098
- }
1099
- if (!validationResult.valid) {
1100
- this.log.warn(`Skipping checkpoint ${calldataCheckpoint.checkpointNumber} due to invalid attestations`, {
1101
- checkpointNumber: calldataCheckpoint.checkpointNumber,
1102
- l1BlockNumber: calldataCheckpoint.l1.blockNumber,
1103
- ...pick(validationResult, 'reason')
1104
- });
1105
- // Emit event for invalid checkpoint detection
1106
- this.events.emit(L2BlockSourceEvents.InvalidAttestationsCheckpointDetected, {
1107
- type: L2BlockSourceEvents.InvalidAttestationsCheckpointDetected,
1062
+ // Screening below records rejected checkpoints as it goes, and each such write advances the L1 sync point past
1063
+ // the rejected checkpoint's L1 block before the batch's valid checkpoints are persisted. If anything in the batch
1064
+ // throws before the batch is persisted, the sync point is restored to where it stood before the batch, so the
1065
+ // next iteration processes the whole batch again instead of skipping its valid checkpoints. Once the batch is
1066
+ // persisted the sync point is left alone: replaying it would refetch blobs for checkpoints already stored,
1067
+ // including promoted ones this node never fetched blobs for, and stall if those blobs are unavailable.
1068
+ const syncPointBeforeBatch = await this.stores.blocks.getSynchedL1BlockNumber();
1069
+ let batchPersisted = false;
1070
+ try {
1071
+ // Decode and validate attestations from CALLDATA before fetching any blobs, so a checkpoint with invalid
1072
+ // attestations is rejected without decoding a possibly malformed blob. Runs even when validation is
1073
+ // disabled, since promotion and persistence need the decoded attestations.
1074
+ const resolvedCheckpoints = await asyncPool(10, calldataCheckpoints, async (calldataCheckpoint)=>{
1075
+ const { attestations, validationResult } = await resolveCheckpointAttestationsFromCalldata(calldataCheckpoint, this.epochCache, this.l1Constants, this.getSignatureContext(), this.log);
1076
+ return {
1077
+ checkpoint: {
1078
+ ...calldataCheckpoint,
1079
+ attestations
1080
+ },
1108
1081
  validationResult
1109
- });
1110
- // Persist a rejected-ancestor entry so any later checkpoint that builds on this one
1111
- // is detected and skipped (rather than tripping the addCheckpoints consecutive-number
1112
- // check and causing the sync point to roll back in a loop).
1113
- await this.stores.blocks.addRejectedCheckpoint({
1114
- checkpointNumber: calldataCheckpoint.checkpointNumber,
1115
- archiveRoot: calldataCheckpoint.archiveRoot,
1116
- parentArchiveRoot: calldataCheckpoint.header.lastArchiveRoot,
1117
- slotNumber: calldataCheckpoint.header.slotNumber,
1118
- l1: calldataCheckpoint.l1,
1119
- reason: 'invalid-attestations'
1120
- });
1121
- continue;
1122
- }
1123
- if (rejectedAncestor) {
1124
- const descendantInfo = {
1125
- archive: calldataCheckpoint.archiveRoot,
1126
- lastArchive: calldataCheckpoint.header.lastArchiveRoot,
1127
- slotNumber: calldataCheckpoint.header.slotNumber,
1128
- checkpointNumber: calldataCheckpoint.checkpointNumber,
1129
- timestamp: calldataCheckpoint.header.timestamp
1130
1082
  };
1131
- this.log.warn(`Skipping checkpoint ${calldataCheckpoint.checkpointNumber} as it is a descendant of ` + `rejected checkpoint ${rejectedAncestor.checkpointNumber} (${rejectedAncestor.reason})`, {
1132
- checkpointNumber: calldataCheckpoint.checkpointNumber,
1133
- l1BlockNumber: calldataCheckpoint.l1.blockNumber,
1134
- l1BlockHash: calldataCheckpoint.l1.blockHash,
1135
- ancestorCheckpointNumber: rejectedAncestor.checkpointNumber,
1136
- ancestorArchiveRoot: rejectedAncestor.archiveRoot.toString(),
1137
- ancestorReason: rejectedAncestor.reason
1138
- });
1139
- this.events.emit(L2BlockSourceEvents.DescendentOfInvalidAttestationsCheckpointDetected, {
1140
- type: L2BlockSourceEvents.DescendentOfInvalidAttestationsCheckpointDetected,
1141
- checkpoint: descendantInfo,
1142
- ancestorArchiveRoot: rejectedAncestor.archiveRoot,
1143
- ancestorCheckpointNumber: rejectedAncestor.checkpointNumber
1083
+ });
1084
+ // Check if the last checkpoint matches a local pending entry (so we can skip blob fetch).
1085
+ // We only check the last one; if it matches, the blob fetch is skipped for that entry.
1086
+ // TODO(palla/pipelining): We may have more than a single checkpoint to promote
1087
+ const lastCalldataCheckpoint = resolvedCheckpoints[resolvedCheckpoints.length - 1].checkpoint;
1088
+ const promoteResult = await this.tryBuildPublishedCheckpointFromProposed(lastCalldataCheckpoint);
1089
+ const checkpointToPromote = promoteResult && !('diverged' in promoteResult) ? promoteResult : undefined;
1090
+ const evictProposedFrom = promoteResult && 'diverged' in promoteResult ? promoteResult.fromCheckpointNumber : undefined;
1091
+ const checkpointsToIngest = [];
1092
+ for (const { checkpoint: calldataCheckpoint, validationResult: resolvedValidationResult } of resolvedCheckpoints){
1093
+ // Check the attestations uploaded by the publisher to L1 are correct.
1094
+ // Rollup contract does not validate attestations to save on gas, so this
1095
+ // falls on the nodes to verify offchain and skip those checkpoints.
1096
+ const validationResult = this.config.skipValidateCheckpointAttestations ? {
1097
+ valid: true
1098
+ } : resolvedValidationResult;
1099
+ // Also skip the checkpoint if it builds on a previously-rejected ancestor. Without
1100
+ // this, addCheckpoints would throw InitialCheckpointNumberNotSequentialError when the
1101
+ // ancestor was skipped earlier (e.g. due to invalid attestations), the catch handler
1102
+ // would roll back the L1 sync point, and the next iteration would re-fetch and re-throw.
1103
+ const rejectedAncestor = await this.stores.blocks.getRejectedCheckpointByArchiveRoot(calldataCheckpoint.header.lastArchiveRoot);
1104
+ // Update the validation result if it has changed, so we can keep track of the first invalid checkpoint
1105
+ // in case there is a sequence of more than one invalid checkpoint, as we need to invalidate the first one.
1106
+ // There is an exception though: if a checkpoint is invalidated and replaced with another invalid checkpoint,
1107
+ // we need to update the validation result, since we need to be able to invalidate the new one.
1108
+ // See test 'chain progresses if an invalid checkpoint is invalidated with an invalid one' for more info.
1109
+ // Do not update the validation result if there is a rejected ancestor, since in that case we want to keep the
1110
+ // original invalidation, as the new checkpoint is extending from a previous invalid one.
1111
+ const validStatusChanged = rollupStatus.validationResult?.valid !== validationResult.valid;
1112
+ const invalidStatusWithSameCheckpointNumber = !validationResult.valid && rollupStatus.validationResult && !rollupStatus.validationResult.valid && rollupStatus.validationResult.checkpoint.checkpointNumber === validationResult.checkpoint.checkpointNumber;
1113
+ if (!rejectedAncestor && (validStatusChanged || invalidStatusWithSameCheckpointNumber)) {
1114
+ rollupStatus.validationResult = validationResult;
1115
+ }
1116
+ if (!validationResult.valid) {
1117
+ this.log.warn(`Skipping checkpoint ${calldataCheckpoint.checkpointNumber} due to invalid attestations`, {
1118
+ checkpointNumber: calldataCheckpoint.checkpointNumber,
1119
+ l1BlockNumber: calldataCheckpoint.l1.blockNumber,
1120
+ ...pick(validationResult, 'reason')
1121
+ });
1122
+ // Emit event for invalid checkpoint detection
1123
+ this.events.emit(L2BlockSourceEvents.InvalidAttestationsCheckpointDetected, {
1124
+ type: L2BlockSourceEvents.InvalidAttestationsCheckpointDetected,
1125
+ validationResult
1126
+ });
1127
+ // Persist a rejected-ancestor entry so any later checkpoint that builds on this one
1128
+ // is detected and skipped (rather than tripping the addCheckpoints consecutive-number
1129
+ // check and causing the sync point to roll back in a loop).
1130
+ await this.stores.blocks.addRejectedCheckpoint({
1131
+ checkpointNumber: calldataCheckpoint.checkpointNumber,
1132
+ archiveRoot: calldataCheckpoint.archiveRoot,
1133
+ parentArchiveRoot: calldataCheckpoint.header.lastArchiveRoot,
1134
+ slotNumber: calldataCheckpoint.header.slotNumber,
1135
+ l1: calldataCheckpoint.l1,
1136
+ reason: 'invalid-attestations'
1137
+ });
1138
+ continue;
1139
+ }
1140
+ if (rejectedAncestor) {
1141
+ const descendantInfo = {
1142
+ archive: calldataCheckpoint.archiveRoot,
1143
+ lastArchive: calldataCheckpoint.header.lastArchiveRoot,
1144
+ slotNumber: calldataCheckpoint.header.slotNumber,
1145
+ checkpointNumber: calldataCheckpoint.checkpointNumber,
1146
+ timestamp: calldataCheckpoint.header.timestamp
1147
+ };
1148
+ this.log.warn(`Skipping checkpoint ${calldataCheckpoint.checkpointNumber} as it is a descendant of ` + `rejected checkpoint ${rejectedAncestor.checkpointNumber} (${rejectedAncestor.reason})`, {
1149
+ checkpointNumber: calldataCheckpoint.checkpointNumber,
1150
+ l1BlockNumber: calldataCheckpoint.l1.blockNumber,
1151
+ l1BlockHash: calldataCheckpoint.l1.blockHash,
1152
+ ancestorCheckpointNumber: rejectedAncestor.checkpointNumber,
1153
+ ancestorArchiveRoot: rejectedAncestor.archiveRoot.toString(),
1154
+ ancestorReason: rejectedAncestor.reason
1155
+ });
1156
+ this.events.emit(L2BlockSourceEvents.DescendentOfInvalidAttestationsCheckpointDetected, {
1157
+ type: L2BlockSourceEvents.DescendentOfInvalidAttestationsCheckpointDetected,
1158
+ checkpoint: descendantInfo,
1159
+ ancestorArchiveRoot: rejectedAncestor.archiveRoot,
1160
+ ancestorCheckpointNumber: rejectedAncestor.checkpointNumber
1161
+ });
1162
+ // Persist this chainpoint as rejected as well, so we can construct a chain of
1163
+ // skipped checkpoints starting from the first one with invalid attestations.
1164
+ await this.stores.blocks.addRejectedCheckpoint({
1165
+ checkpointNumber: calldataCheckpoint.checkpointNumber,
1166
+ archiveRoot: calldataCheckpoint.archiveRoot,
1167
+ parentArchiveRoot: calldataCheckpoint.header.lastArchiveRoot,
1168
+ slotNumber: calldataCheckpoint.header.slotNumber,
1169
+ l1: calldataCheckpoint.l1,
1170
+ reason: 'descends-from-invalid-attestations'
1171
+ });
1172
+ continue;
1173
+ }
1174
+ checkpointsToIngest.push(calldataCheckpoint);
1175
+ }
1176
+ // Fetch blobs in parallel only for the surviving (attestation-valid, non-descendant) checkpoints,
1177
+ // then build the full published checkpoints. The last calldata checkpoint may be promotable from a
1178
+ // local proposed block (checkpointToPromote), in which case it carries no blob to fetch. A missing or
1179
+ // undecodable blob throws and propagates, and the batch guard restores the L1 sync point so the fetch is retried.
1180
+ const toFetchBlobs = checkpointToPromote ? checkpointsToIngest.filter((c)=>c.checkpointNumber !== checkpointToPromote.checkpoint.number) : checkpointsToIngest;
1181
+ const blobFetched = await asyncPool(10, toFetchBlobs, async (checkpoint)=>retrievedToPublishedCheckpoint({
1182
+ ...checkpoint,
1183
+ checkpointBlobData: await getCheckpointBlobDataFromBlobs(this.blobClient, checkpoint.l1.blockHash, checkpoint.blobHashes, checkpoint.checkpointNumber, this.log, !initialSyncComplete, checkpoint.parentBeaconBlockRoot, checkpoint.l1.timestamp)
1184
+ }));
1185
+ // Index the built checkpoints by number so we can ingest them in calldata order, slotting in the
1186
+ // promoted checkpoint (built from a local proposed block rather than blobs).
1187
+ const publishedByNumber = new Map(blobFetched.map((publishedCheckpoint)=>[
1188
+ publishedCheckpoint.checkpoint.number,
1189
+ publishedCheckpoint
1190
+ ]));
1191
+ if (checkpointToPromote) {
1192
+ publishedByNumber.set(checkpointToPromote.checkpoint.number, checkpointToPromote);
1193
+ }
1194
+ const validCheckpoints = [];
1195
+ for (const calldataCheckpoint of checkpointsToIngest){
1196
+ const published = publishedByNumber.get(calldataCheckpoint.checkpointNumber);
1197
+ validCheckpoints.push(published);
1198
+ this.log.debug(`Ingesting new checkpoint ${published.checkpoint.number} with ${published.checkpoint.blocks.length} blocks`, {
1199
+ checkpointHash: published.checkpoint.hash(),
1200
+ l1BlockNumber: published.l1.blockNumber,
1201
+ ...published.checkpoint.header.toInspect(),
1202
+ blocks: published.checkpoint.blocks.map((b)=>b.getStats())
1144
1203
  });
1145
- // Persist this chainpoint as rejected as well, so we can construct a chain of
1146
- // skipped checkpoints starting from the first one with invalid attestations.
1147
- await this.stores.blocks.addRejectedCheckpoint({
1148
- checkpointNumber: calldataCheckpoint.checkpointNumber,
1149
- archiveRoot: calldataCheckpoint.archiveRoot,
1150
- parentArchiveRoot: calldataCheckpoint.header.lastArchiveRoot,
1151
- slotNumber: calldataCheckpoint.header.slotNumber,
1152
- l1: calldataCheckpoint.l1,
1153
- reason: 'descends-from-invalid-attestations'
1204
+ }
1205
+ for (const published of validCheckpoints){
1206
+ this.instrumentation.processCheckpointL1Timing({
1207
+ slotNumber: published.checkpoint.header.slotNumber,
1208
+ l1Timestamp: published.l1.timestamp,
1209
+ l1Constants: this.l1Constants
1154
1210
  });
1155
- continue;
1156
1211
  }
1157
- checkpointsToIngest.push(calldataCheckpoint);
1158
- }
1159
- // Fetch blobs in parallel only for the surviving (attestation-valid, non-descendant) checkpoints,
1160
- // then build the full published checkpoints. The last calldata checkpoint may be promotable from a
1161
- // local proposed block (checkpointToPromote), in which case it carries no blob to fetch. A missing or
1162
- // undecodable blob throws and propagates, rolling back the L1 sync point so the fetch is retried.
1163
- const toFetchBlobs = checkpointToPromote ? checkpointsToIngest.filter((c)=>c.checkpointNumber !== checkpointToPromote.checkpoint.number) : checkpointsToIngest;
1164
- const blobFetched = await asyncPool(10, toFetchBlobs, async (checkpoint)=>retrievedToPublishedCheckpoint({
1165
- ...checkpoint,
1166
- checkpointBlobData: await getCheckpointBlobDataFromBlobs(this.blobClient, checkpoint.l1.blockHash, checkpoint.blobHashes, checkpoint.checkpointNumber, this.log, !initialSyncComplete, checkpoint.parentBeaconBlockRoot, checkpoint.l1.timestamp)
1167
- }));
1168
- // Index the built checkpoints by number so we can ingest them in calldata order, slotting in the
1169
- // promoted checkpoint (built from a local proposed block rather than blobs).
1170
- const publishedByNumber = new Map(blobFetched.map((publishedCheckpoint)=>[
1171
- publishedCheckpoint.checkpoint.number,
1172
- publishedCheckpoint
1173
- ]));
1174
- if (checkpointToPromote) {
1175
- publishedByNumber.set(checkpointToPromote.checkpoint.number, checkpointToPromote);
1176
- }
1177
- const validCheckpoints = [];
1178
- for (const calldataCheckpoint of checkpointsToIngest){
1179
- const published = publishedByNumber.get(calldataCheckpoint.checkpointNumber);
1180
- validCheckpoints.push(published);
1181
- this.log.debug(`Ingesting new checkpoint ${published.checkpoint.number} with ${published.checkpoint.blocks.length} blocks`, {
1182
- checkpointHash: published.checkpoint.hash(),
1183
- l1BlockNumber: published.l1.blockNumber,
1184
- ...published.checkpoint.header.toInspect(),
1185
- blocks: published.checkpoint.blocks.map((b)=>b.getStats())
1186
- });
1187
- }
1188
- for (const published of validCheckpoints){
1189
- this.instrumentation.processCheckpointL1Timing({
1190
- slotNumber: published.checkpoint.header.slotNumber,
1191
- l1Timestamp: published.l1.timestamp,
1192
- l1Constants: this.l1Constants
1193
- });
1194
- }
1195
- try {
1196
- const updatedValidationResult = rollupStatus.validationResult === initialValidationResult ? undefined : rollupStatus.validationResult;
1197
- // Split valid checkpoints: the promoted one (if any) is persisted via the proposed-promotion path,
1198
- // the rest via addCheckpoints. Both paths run within the same store transaction for atomicity.
1199
- const [[maybeValidCheckpointToPromote], checkpointsToAdd] = partition(validCheckpoints, (c)=>c.checkpoint.number === checkpointToPromote?.checkpoint.number);
1200
- const [processDuration, result] = await elapsed(()=>execInSpan(this.tracer, 'Archiver.addCheckpoints', ()=>this.updater.addCheckpoints(checkpointsToAdd, updatedValidationResult, maybeValidCheckpointToPromote && {
1201
- l1: lastCalldataCheckpoint.l1,
1202
- attestations: lastCalldataCheckpoint.attestations,
1203
- verbatimAttestations: lastCalldataCheckpoint.verbatimAttestations,
1204
- checkpoint: maybeValidCheckpointToPromote
1205
- }, evictProposedFrom)));
1206
- if (validCheckpoints.length > 0) {
1207
- this.instrumentation.processNewCheckpointedBlocks(processDuration / validCheckpoints.length, validCheckpoints.flatMap((c)=>c.checkpoint.blocks));
1212
+ try {
1213
+ const updatedValidationResult = rollupStatus.validationResult === initialValidationResult ? undefined : rollupStatus.validationResult;
1214
+ // Split valid checkpoints: the promoted one (if any) is persisted via the proposed-promotion path,
1215
+ // the rest via addCheckpoints. Both paths run within the same store transaction for atomicity.
1216
+ const [[maybeValidCheckpointToPromote], checkpointsToAdd] = partition(validCheckpoints, (c)=>c.checkpoint.number === checkpointToPromote?.checkpoint.number);
1217
+ const [processDuration, result] = await elapsed(()=>execInSpan(this.tracer, 'Archiver.addCheckpoints', async ()=>{
1218
+ const addResult = await this.updater.addCheckpoints(checkpointsToAdd, updatedValidationResult, maybeValidCheckpointToPromote && {
1219
+ l1: lastCalldataCheckpoint.l1,
1220
+ attestations: lastCalldataCheckpoint.attestations,
1221
+ verbatimAttestations: lastCalldataCheckpoint.verbatimAttestations,
1222
+ checkpoint: maybeValidCheckpointToPromote
1223
+ }, evictProposedFrom);
1224
+ // Set before the span wrappers finish, so a throw while closing the span doesn't count as unpersisted.
1225
+ batchPersisted = true;
1226
+ return addResult;
1227
+ }));
1228
+ if (validCheckpoints.length > 0) {
1229
+ this.instrumentation.processNewCheckpointedBlocks(processDuration / validCheckpoints.length, validCheckpoints.flatMap((c)=>c.checkpoint.blocks));
1230
+ }
1231
+ // Record blocks newly fetched from L1 checkpoint payloads as added. The promoted checkpoint (if any) is
1232
+ // excluded: its blocks were already in local archiver storage (added via the proposed-block path) so the
1233
+ // block stream does not need them re-downloaded.
1234
+ blocksAdded.push(...checkpointsToAdd.flatMap((c)=>c.checkpoint.blocks));
1235
+ // If blocks were pruned due to conflict with L1 checkpoints, emit event
1236
+ if (result.prunedBlocks && result.prunedBlocks.length > 0) {
1237
+ const prunedCheckpointNumber = result.prunedBlocks[0].checkpointNumber;
1238
+ const prunedSlotNumber = result.prunedBlocks[0].header.globalVariables.slotNumber;
1239
+ this.log.info(`Pruned ${result.prunedBlocks.length} mismatching blocks for checkpoint ${prunedCheckpointNumber}`, {
1240
+ prunedBlocks: result.prunedBlocks.map((b)=>b.toBlockInfo()),
1241
+ prunedSlotNumber,
1242
+ prunedCheckpointNumber
1243
+ });
1244
+ this.instrumentation.recordPrune('l1_conflict');
1245
+ // Emit event for listening services to react to the prune.
1246
+ // Note: slotNumber comes from the first pruned block. If pruned blocks theoretically spanned multiple slots,
1247
+ // only one slot number would be reported (though in practice all blocks in a checkpoint span a single slot).
1248
+ this.events.emit(L2BlockSourceEvents.L2PruneUncheckpointed, {
1249
+ type: L2BlockSourceEvents.L2PruneUncheckpointed,
1250
+ slotNumber: prunedSlotNumber,
1251
+ blocks: result.prunedBlocks
1252
+ });
1253
+ }
1254
+ } catch (err) {
1255
+ if (err instanceof InitialCheckpointNumberNotSequentialError) {
1256
+ const { previousCheckpointNumber, newCheckpointNumber } = err;
1257
+ const previousCheckpoint = previousCheckpointNumber ? await this.stores.blocks.getCheckpointData(CheckpointNumber(previousCheckpointNumber)) : undefined;
1258
+ const lastFinalizedCheckpoint = await this.stores.blocks.getCheckpointData(await this.stores.blocks.getFinalizedCheckpointNumber());
1259
+ const updatedL1SyncPoint = previousCheckpoint?.l1.blockNumber ?? lastFinalizedCheckpoint?.l1.blockNumber ?? this.l1Constants.l1StartBlock;
1260
+ await this.stores.blocks.setSynchedL1BlockNumber(updatedL1SyncPoint);
1261
+ this.log.warn(`Attempting to insert checkpoint ${newCheckpointNumber} with previous block ${previousCheckpointNumber}. Rolling back L1 sync point to ${updatedL1SyncPoint} to try and fetch the missing blocks.`, {
1262
+ previousCheckpointNumber,
1263
+ previousCheckpoint: previousCheckpoint?.header.toInspect(),
1264
+ lastFinalizedCheckpoint: lastFinalizedCheckpoint?.header.toInspect(),
1265
+ l1StartBlock: this.l1Constants.l1StartBlock,
1266
+ newCheckpointNumber,
1267
+ updatedL1SyncPoint
1268
+ });
1269
+ }
1270
+ throw err;
1208
1271
  }
1209
- // Record blocks newly fetched from L1 checkpoint payloads as added. The promoted checkpoint (if any) is
1210
- // excluded: its blocks were already in local archiver storage (added via the proposed-block path) so the
1211
- // block stream does not need them re-downloaded.
1212
- blocksAdded.push(...checkpointsToAdd.flatMap((c)=>c.checkpoint.blocks));
1213
- // If blocks were pruned due to conflict with L1 checkpoints, emit event
1214
- if (result.prunedBlocks && result.prunedBlocks.length > 0) {
1215
- const prunedCheckpointNumber = result.prunedBlocks[0].checkpointNumber;
1216
- const prunedSlotNumber = result.prunedBlocks[0].header.globalVariables.slotNumber;
1217
- this.log.info(`Pruned ${result.prunedBlocks.length} mismatching blocks for checkpoint ${prunedCheckpointNumber}`, {
1218
- prunedBlocks: result.prunedBlocks.map((b)=>b.toBlockInfo()),
1219
- prunedSlotNumber,
1220
- prunedCheckpointNumber
1221
- });
1222
- this.instrumentation.recordPrune('l1_conflict');
1223
- // Emit event for listening services to react to the prune.
1224
- // Note: slotNumber comes from the first pruned block. If pruned blocks theoretically spanned multiple slots,
1225
- // only one slot number would be reported (though in practice all blocks in a checkpoint span a single slot).
1226
- this.events.emit(L2BlockSourceEvents.L2PruneUncheckpointed, {
1227
- type: L2BlockSourceEvents.L2PruneUncheckpointed,
1228
- slotNumber: prunedSlotNumber,
1229
- blocks: result.prunedBlocks
1272
+ for (const checkpoint of validCheckpoints){
1273
+ this.log.info(`Downloaded checkpoint ${checkpoint.checkpoint.number}`, {
1274
+ checkpointHash: checkpoint.checkpoint.hash(),
1275
+ checkpointNumber: checkpoint.checkpoint.number,
1276
+ blockCount: checkpoint.checkpoint.blocks.length,
1277
+ txCount: checkpoint.checkpoint.blocks.reduce((acc, b)=>acc + b.body.txEffects.length, 0),
1278
+ header: checkpoint.checkpoint.header.toInspect(),
1279
+ archiveRoot: checkpoint.checkpoint.archive.root.toString(),
1280
+ archiveNextLeafIndex: checkpoint.checkpoint.archive.nextAvailableLeafIndex
1230
1281
  });
1231
1282
  }
1283
+ lastRetrievedCheckpoint = validCheckpoints.at(-1) ?? lastRetrievedCheckpoint;
1284
+ // The last checkpoint seen on L1 this batch (valid or rejected), tracked from calldata since
1285
+ // rejected checkpoints are no longer built into PublishedCheckpoints.
1286
+ lastSeenCheckpoint = lastCalldataCheckpoint;
1232
1287
  } catch (err) {
1233
- if (err instanceof InitialCheckpointNumberNotSequentialError) {
1234
- const { previousCheckpointNumber, newCheckpointNumber } = err;
1235
- const previousCheckpoint = previousCheckpointNumber ? await this.stores.blocks.getCheckpointData(CheckpointNumber(previousCheckpointNumber)) : undefined;
1236
- const lastFinalizedCheckpoint = await this.stores.blocks.getCheckpointData(await this.stores.blocks.getFinalizedCheckpointNumber());
1237
- const updatedL1SyncPoint = previousCheckpoint?.l1.blockNumber ?? lastFinalizedCheckpoint?.l1.blockNumber ?? this.l1Constants.l1StartBlock;
1238
- await this.stores.blocks.setSynchedL1BlockNumber(updatedL1SyncPoint);
1239
- this.log.warn(`Attempting to insert checkpoint ${newCheckpointNumber} with previous block ${previousCheckpointNumber}. Rolling back L1 sync point to ${updatedL1SyncPoint} to try and fetch the missing blocks.`, {
1240
- previousCheckpointNumber,
1241
- previousCheckpoint: previousCheckpoint?.header.toInspect(),
1242
- lastFinalizedCheckpoint: lastFinalizedCheckpoint?.header.toInspect(),
1243
- l1StartBlock: this.l1Constants.l1StartBlock,
1244
- newCheckpointNumber,
1245
- updatedL1SyncPoint
1288
+ const restoreTo = syncPointBeforeBatch ?? this.l1Constants.l1StartBlock;
1289
+ const currentSyncPoint = await this.stores.blocks.getSynchedL1BlockNumber();
1290
+ // Only ever lower it, so a deeper rewind made while handling the error (e.g. on a checkpoint gap) is kept.
1291
+ if (!batchPersisted && currentSyncPoint !== undefined && currentSyncPoint > restoreTo) {
1292
+ this.log.warn(`Restoring L1 sync point to ${restoreTo} after failing to process checkpoints`, {
1293
+ currentSyncPoint,
1294
+ restoreTo,
1295
+ searchStartBlock,
1296
+ searchEndBlock
1246
1297
  });
1298
+ await this.stores.blocks.setSynchedL1BlockNumber(restoreTo);
1247
1299
  }
1248
1300
  throw err;
1249
1301
  }
1250
- for (const checkpoint of validCheckpoints){
1251
- this.log.info(`Downloaded checkpoint ${checkpoint.checkpoint.number}`, {
1252
- checkpointHash: checkpoint.checkpoint.hash(),
1253
- checkpointNumber: checkpoint.checkpoint.number,
1254
- blockCount: checkpoint.checkpoint.blocks.length,
1255
- txCount: checkpoint.checkpoint.blocks.reduce((acc, b)=>acc + b.body.txEffects.length, 0),
1256
- header: checkpoint.checkpoint.header.toInspect(),
1257
- archiveRoot: checkpoint.checkpoint.archive.root.toString(),
1258
- archiveNextLeafIndex: checkpoint.checkpoint.archive.nextAvailableLeafIndex
1259
- });
1260
- }
1261
- lastRetrievedCheckpoint = validCheckpoints.at(-1) ?? lastRetrievedCheckpoint;
1262
- // The last checkpoint seen on L1 this batch (valid or rejected), tracked from calldata since
1263
- // rejected checkpoints are no longer built into PublishedCheckpoints.
1264
- lastSeenCheckpoint = lastCalldataCheckpoint;
1265
1302
  }while (searchEndBlock < currentL1BlockNumber)
1266
1303
  // Important that we update AFTER inserting the blocks.
1267
1304
  await this.updateProvenCheckpoint(rollupStatus.provenCheckpointNumber, provenArchive);
@@ -3,7 +3,7 @@ import type { ViemCommitteeAttestations } from '@aztec-labs/ethereum/contracts';
3
3
  import { type CheckpointNumber } from '@aztec-labs/foundation/branded-types';
4
4
  import type { Fr } from '@aztec-labs/foundation/curves/bn254';
5
5
  import type { Logger } from '@aztec-labs/foundation/log';
6
- import { type AttestationInfo, type CommitteeAttestation, type ValidateCheckpointResult } from '@aztec-labs/stdlib/block';
6
+ import { type AttestationInfo, CommitteeAttestation, type ValidateCheckpointResult } from '@aztec-labs/stdlib/block';
7
7
  import type { CheckpointInfo, PublishedCheckpoint } from '@aztec-labs/stdlib/checkpoint';
8
8
  import { type L1RollupConstants } from '@aztec-labs/stdlib/epoch-helpers';
9
9
  import { ConsensusPayload, type CoordinationSignatureContext } from '@aztec-labs/stdlib/p2p';
@@ -20,21 +20,27 @@ export type CalldataCheckpointForAttestations = {
20
20
  archiveRoot: Fr;
21
21
  feeAssetPriceModifier: bigint;
22
22
  header: CheckpointHeader;
23
- attestations: CommitteeAttestation[];
24
23
  /** The exact packed attestations tuple from L1 calldata, carried verbatim for byte-faithful invalidation. */
25
24
  verbatimAttestations: ViemCommitteeAttestations;
26
25
  };
26
+ /** A checkpoint's attestations decoded for its epoch committee, and their validation result. */
27
+ export type ResolvedCheckpointAttestations = {
28
+ /** Empty if the epoch has an open escape hatch or no committee. */
29
+ attestations: CommitteeAttestation[];
30
+ validationResult: ValidateCheckpointResult;
31
+ };
27
32
  /**
28
- * Validates the attestations of a checkpoint from L1 calldata only, without fetching or decoding its blobs.
29
- * The signed consensus payload (header, archive root, fee asset price modifier) is fully available from
30
- * calldata, so an invalid-attestation checkpoint can be rejected before any (possibly malformed) blob is
31
- * fetched and decoded.
33
+ * Decodes and validates the attestations of a checkpoint from L1 calldata only, so an invalid checkpoint can be
34
+ * rejected before its blobs are fetched. The tuple is decoded against the epoch's committee size, and not at all
35
+ * during an escape hatch, where the proposer may post an arbitrary tuple.
36
+ *
37
+ * @throws CheckpointAttestationsDecodeError if the tuple does not decode for the epoch committee.
32
38
  */
33
- export declare function validateCheckpointAttestationsFromCalldata(checkpoint: CalldataCheckpointForAttestations, epochCache: EpochCache, constants: Pick<L1RollupConstants, 'epochDuration'>, signatureContext: CoordinationSignatureContext, logger?: Logger): Promise<ValidateCheckpointResult>;
39
+ export declare function resolveCheckpointAttestationsFromCalldata(checkpoint: CalldataCheckpointForAttestations, epochCache: EpochCache, constants: Pick<L1RollupConstants, 'epochDuration'>, signatureContext: CoordinationSignatureContext, logger?: Logger): Promise<ResolvedCheckpointAttestations>;
34
40
  /**
35
41
  * Core attestation validation over a consensus payload, its attestations, and checkpoint metadata --
36
42
  * independent of whether the checkpoint's blocks have been decoded from blobs. Returns true if the
37
43
  * attestations are valid and sufficient, false otherwise.
38
44
  */
39
45
  export declare function validateAttestations(payload: ConsensusPayload, attestations: CommitteeAttestation[], verbatimAttestations: ViemCommitteeAttestations, checkpointInfo: CheckpointInfo, epochCache: EpochCache, constants: Pick<L1RollupConstants, 'epochDuration'>, logger?: Logger): Promise<ValidateCheckpointResult>;
40
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidmFsaWRhdGlvbi5kLnRzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vc3JjL21vZHVsZXMvdmFsaWRhdGlvbi50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEtBQUssRUFBRSxVQUFVLEVBQUUsTUFBTSx5QkFBeUIsQ0FBQztBQUMxRCxPQUFPLEtBQUssRUFBRSx5QkFBeUIsRUFBRSxNQUFNLGdDQUFnQyxDQUFDO0FBQ2hGLE9BQU8sRUFBRSxLQUFLLGdCQUFnQixFQUFlLE1BQU0sc0NBQXNDLENBQUM7QUFFMUYsT0FBTyxLQUFLLEVBQUUsRUFBRSxFQUFFLE1BQU0scUNBQXFDLENBQUM7QUFDOUQsT0FBTyxLQUFLLEVBQUUsTUFBTSxFQUFFLE1BQU0sNEJBQTRCLENBQUM7QUFDekQsT0FBTyxFQUNMLEtBQUssZUFBZSxFQUNwQixLQUFLLG9CQUFvQixFQUV6QixLQUFLLHdCQUF3QixFQUU5QixNQUFNLDBCQUEwQixDQUFDO0FBQ2xDLE9BQU8sS0FBSyxFQUFFLGNBQWMsRUFBRSxtQkFBbUIsRUFBRSxNQUFNLCtCQUErQixDQUFDO0FBQ3pGLE9BQU8sRUFBRSxLQUFLLGlCQUFpQixFQUFpQyxNQUFNLGtDQUFrQyxDQUFDO0FBQ3pHLE9BQU8sRUFBRSxnQkFBZ0IsRUFBRSxLQUFLLDRCQUE0QixFQUFFLE1BQU0sd0JBQXdCLENBQUM7QUFDN0YsT0FBTyxLQUFLLEVBQUUsZ0JBQWdCLEVBQUUsTUFBTSwyQkFBMkIsQ0FBQztBQUVsRSxZQUFZLEVBQUUsd0JBQXdCLEVBQUUsQ0FBQztBQUV6Qzs7O0dBR0c7QUFDSCx3QkFBZ0IseUNBQXlDLENBQ3ZELEVBQUUsVUFBVSxFQUFFLFlBQVksRUFBRSxFQUFFLG1CQUFtQixFQUNqRCxnQkFBZ0IsRUFBRSw0QkFBNEIsR0FDN0MsZUFBZSxFQUFFLENBR25CO0FBRUQsOEZBQThGO0FBQzlGLE1BQU0sTUFBTSxpQ0FBaUMsR0FBRztJQUM5QyxnQkFBZ0IsRUFBRSxnQkFBZ0IsQ0FBQztJQUNuQyxXQUFXLEVBQUUsRUFBRSxDQUFDO0lBQ2hCLHFCQUFxQixFQUFFLE1BQU0sQ0FBQztJQUM5QixNQUFNLEVBQUUsZ0JBQWdCLENBQUM7SUFDekIsWUFBWSxFQUFFLG9CQUFvQixFQUFFLENBQUM7SUFDckMsNkdBQTZHO0lBQzdHLG9CQUFvQixFQUFFLHlCQUF5QixDQUFDO0NBQ2pELENBQUM7QUFFRjs7Ozs7R0FLRztBQUNILHdCQUFnQiwwQ0FBMEMsQ0FDeEQsVUFBVSxFQUFFLGlDQUFpQyxFQUM3QyxVQUFVLEVBQUUsVUFBVSxFQUN0QixTQUFTLEVBQUUsSUFBSSxDQUFDLGlCQUFpQixFQUFFLGVBQWUsQ0FBQyxFQUNuRCxnQkFBZ0IsRUFBRSw0QkFBNEIsRUFDOUMsTUFBTSxDQUFDLEVBQUUsTUFBTSxHQUNkLE9BQU8sQ0FBQyx3QkFBd0IsQ0FBQyxDQXVCbkM7QUFFRDs7OztHQUlHO0FBQ0gsd0JBQXNCLG9CQUFvQixDQUN4QyxPQUFPLEVBQUUsZ0JBQWdCLEVBQ3pCLFlBQVksRUFBRSxvQkFBb0IsRUFBRSxFQUNwQyxvQkFBb0IsRUFBRSx5QkFBeUIsRUFDL0MsY0FBYyxFQUFFLGNBQWMsRUFDOUIsVUFBVSxFQUFFLFVBQVUsRUFDdEIsU0FBUyxFQUFFLElBQUksQ0FBQyxpQkFBaUIsRUFBRSxlQUFlLENBQUMsRUFDbkQsTUFBTSxDQUFDLEVBQUUsTUFBTSxHQUNkLE9BQU8sQ0FBQyx3QkFBd0IsQ0FBQyxDQTRGbkMifQ==
46
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidmFsaWRhdGlvbi5kLnRzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vc3JjL21vZHVsZXMvdmFsaWRhdGlvbi50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEtBQUssRUFBRSxVQUFVLEVBQUUsTUFBTSx5QkFBeUIsQ0FBQztBQUMxRCxPQUFPLEtBQUssRUFBRSx5QkFBeUIsRUFBRSxNQUFNLGdDQUFnQyxDQUFDO0FBQ2hGLE9BQU8sRUFBRSxLQUFLLGdCQUFnQixFQUFlLE1BQU0sc0NBQXNDLENBQUM7QUFFMUYsT0FBTyxLQUFLLEVBQUUsRUFBRSxFQUFFLE1BQU0scUNBQXFDLENBQUM7QUFFOUQsT0FBTyxLQUFLLEVBQUUsTUFBTSxFQUFFLE1BQU0sNEJBQTRCLENBQUM7QUFDekQsT0FBTyxFQUNMLEtBQUssZUFBZSxFQUNwQixvQkFBb0IsRUFFcEIsS0FBSyx3QkFBd0IsRUFFOUIsTUFBTSwwQkFBMEIsQ0FBQztBQUNsQyxPQUFPLEtBQUssRUFBRSxjQUFjLEVBQUUsbUJBQW1CLEVBQUUsTUFBTSwrQkFBK0IsQ0FBQztBQUN6RixPQUFPLEVBQUUsS0FBSyxpQkFBaUIsRUFBaUMsTUFBTSxrQ0FBa0MsQ0FBQztBQUN6RyxPQUFPLEVBQUUsZ0JBQWdCLEVBQUUsS0FBSyw0QkFBNEIsRUFBRSxNQUFNLHdCQUF3QixDQUFDO0FBQzdGLE9BQU8sS0FBSyxFQUFFLGdCQUFnQixFQUFFLE1BQU0sMkJBQTJCLENBQUM7QUFJbEUsWUFBWSxFQUFFLHdCQUF3QixFQUFFLENBQUM7QUFFekM7OztHQUdHO0FBQ0gsd0JBQWdCLHlDQUF5QyxDQUN2RCxFQUFFLFVBQVUsRUFBRSxZQUFZLEVBQUUsRUFBRSxtQkFBbUIsRUFDakQsZ0JBQWdCLEVBQUUsNEJBQTRCLEdBQzdDLGVBQWUsRUFBRSxDQUduQjtBQUVELDhGQUE4RjtBQUM5RixNQUFNLE1BQU0saUNBQWlDLEdBQUc7SUFDOUMsZ0JBQWdCLEVBQUUsZ0JBQWdCLENBQUM7SUFDbkMsV0FBVyxFQUFFLEVBQUUsQ0FBQztJQUNoQixxQkFBcUIsRUFBRSxNQUFNLENBQUM7SUFDOUIsTUFBTSxFQUFFLGdCQUFnQixDQUFDO0lBQ3pCLDZHQUE2RztJQUM3RyxvQkFBb0IsRUFBRSx5QkFBeUIsQ0FBQztDQUNqRCxDQUFDO0FBRUYsZ0dBQWdHO0FBQ2hHLE1BQU0sTUFBTSw4QkFBOEIsR0FBRztJQUMzQyxtRUFBbUU7SUFDbkUsWUFBWSxFQUFFLG9CQUFvQixFQUFFLENBQUM7SUFDckMsZ0JBQWdCLEVBQUUsd0JBQXdCLENBQUM7Q0FDNUMsQ0FBQztBQUVGOzs7Ozs7R0FNRztBQUNILHdCQUFzQix5Q0FBeUMsQ0FDN0QsVUFBVSxFQUFFLGlDQUFpQyxFQUM3QyxVQUFVLEVBQUUsVUFBVSxFQUN0QixTQUFTLEVBQUUsSUFBSSxDQUFDLGlCQUFpQixFQUFFLGVBQWUsQ0FBQyxFQUNuRCxnQkFBZ0IsRUFBRSw0QkFBNEIsRUFDOUMsTUFBTSxDQUFDLEVBQUUsTUFBTSxHQUNkLE9BQU8sQ0FBQyw4QkFBOEIsQ0FBQyxDQWtEekM7QUFFRDs7OztHQUlHO0FBQ0gsd0JBQXNCLG9CQUFvQixDQUN4QyxPQUFPLEVBQUUsZ0JBQWdCLEVBQ3pCLFlBQVksRUFBRSxvQkFBb0IsRUFBRSxFQUNwQyxvQkFBb0IsRUFBRSx5QkFBeUIsRUFDL0MsY0FBYyxFQUFFLGNBQWMsRUFDOUIsVUFBVSxFQUFFLFVBQVUsRUFDdEIsU0FBUyxFQUFFLElBQUksQ0FBQyxpQkFBaUIsRUFBRSxlQUFlLENBQUMsRUFDbkQsTUFBTSxDQUFDLEVBQUUsTUFBTSxHQUNkLE9BQU8sQ0FBQyx3QkFBd0IsQ0FBQyxDQTJCbkMifQ==
@@ -1 +1 @@
1
- {"version":3,"file":"validation.d.ts","sourceRoot":"","sources":["../../src/modules/validation.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,yBAAyB,CAAC;AAC1D,OAAO,KAAK,EAAE,yBAAyB,EAAE,MAAM,gCAAgC,CAAC;AAChF,OAAO,EAAE,KAAK,gBAAgB,EAAe,MAAM,sCAAsC,CAAC;AAE1F,OAAO,KAAK,EAAE,EAAE,EAAE,MAAM,qCAAqC,CAAC;AAC9D,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,4BAA4B,CAAC;AACzD,OAAO,EACL,KAAK,eAAe,EACpB,KAAK,oBAAoB,EAEzB,KAAK,wBAAwB,EAE9B,MAAM,0BAA0B,CAAC;AAClC,OAAO,KAAK,EAAE,cAAc,EAAE,mBAAmB,EAAE,MAAM,+BAA+B,CAAC;AACzF,OAAO,EAAE,KAAK,iBAAiB,EAAiC,MAAM,kCAAkC,CAAC;AACzG,OAAO,EAAE,gBAAgB,EAAE,KAAK,4BAA4B,EAAE,MAAM,wBAAwB,CAAC;AAC7F,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,2BAA2B,CAAC;AAElE,YAAY,EAAE,wBAAwB,EAAE,CAAC;AAEzC;;;GAGG;AACH,wBAAgB,yCAAyC,CACvD,EAAE,UAAU,EAAE,YAAY,EAAE,EAAE,mBAAmB,EACjD,gBAAgB,EAAE,4BAA4B,GAC7C,eAAe,EAAE,CAGnB;AAED,8FAA8F;AAC9F,MAAM,MAAM,iCAAiC,GAAG;IAC9C,gBAAgB,EAAE,gBAAgB,CAAC;IACnC,WAAW,EAAE,EAAE,CAAC;IAChB,qBAAqB,EAAE,MAAM,CAAC;IAC9B,MAAM,EAAE,gBAAgB,CAAC;IACzB,YAAY,EAAE,oBAAoB,EAAE,CAAC;IACrC,6GAA6G;IAC7G,oBAAoB,EAAE,yBAAyB,CAAC;CACjD,CAAC;AAEF;;;;;GAKG;AACH,wBAAgB,0CAA0C,CACxD,UAAU,EAAE,iCAAiC,EAC7C,UAAU,EAAE,UAAU,EACtB,SAAS,EAAE,IAAI,CAAC,iBAAiB,EAAE,eAAe,CAAC,EACnD,gBAAgB,EAAE,4BAA4B,EAC9C,MAAM,CAAC,EAAE,MAAM,GACd,OAAO,CAAC,wBAAwB,CAAC,CAuBnC;AAED;;;;GAIG;AACH,wBAAsB,oBAAoB,CACxC,OAAO,EAAE,gBAAgB,EACzB,YAAY,EAAE,oBAAoB,EAAE,EACpC,oBAAoB,EAAE,yBAAyB,EAC/C,cAAc,EAAE,cAAc,EAC9B,UAAU,EAAE,UAAU,EACtB,SAAS,EAAE,IAAI,CAAC,iBAAiB,EAAE,eAAe,CAAC,EACnD,MAAM,CAAC,EAAE,MAAM,GACd,OAAO,CAAC,wBAAwB,CAAC,CA4FnC"}
1
+ {"version":3,"file":"validation.d.ts","sourceRoot":"","sources":["../../src/modules/validation.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,yBAAyB,CAAC;AAC1D,OAAO,KAAK,EAAE,yBAAyB,EAAE,MAAM,gCAAgC,CAAC;AAChF,OAAO,EAAE,KAAK,gBAAgB,EAAe,MAAM,sCAAsC,CAAC;AAE1F,OAAO,KAAK,EAAE,EAAE,EAAE,MAAM,qCAAqC,CAAC;AAE9D,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,4BAA4B,CAAC;AACzD,OAAO,EACL,KAAK,eAAe,EACpB,oBAAoB,EAEpB,KAAK,wBAAwB,EAE9B,MAAM,0BAA0B,CAAC;AAClC,OAAO,KAAK,EAAE,cAAc,EAAE,mBAAmB,EAAE,MAAM,+BAA+B,CAAC;AACzF,OAAO,EAAE,KAAK,iBAAiB,EAAiC,MAAM,kCAAkC,CAAC;AACzG,OAAO,EAAE,gBAAgB,EAAE,KAAK,4BAA4B,EAAE,MAAM,wBAAwB,CAAC;AAC7F,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,2BAA2B,CAAC;AAIlE,YAAY,EAAE,wBAAwB,EAAE,CAAC;AAEzC;;;GAGG;AACH,wBAAgB,yCAAyC,CACvD,EAAE,UAAU,EAAE,YAAY,EAAE,EAAE,mBAAmB,EACjD,gBAAgB,EAAE,4BAA4B,GAC7C,eAAe,EAAE,CAGnB;AAED,8FAA8F;AAC9F,MAAM,MAAM,iCAAiC,GAAG;IAC9C,gBAAgB,EAAE,gBAAgB,CAAC;IACnC,WAAW,EAAE,EAAE,CAAC;IAChB,qBAAqB,EAAE,MAAM,CAAC;IAC9B,MAAM,EAAE,gBAAgB,CAAC;IACzB,6GAA6G;IAC7G,oBAAoB,EAAE,yBAAyB,CAAC;CACjD,CAAC;AAEF,gGAAgG;AAChG,MAAM,MAAM,8BAA8B,GAAG;IAC3C,mEAAmE;IACnE,YAAY,EAAE,oBAAoB,EAAE,CAAC;IACrC,gBAAgB,EAAE,wBAAwB,CAAC;CAC5C,CAAC;AAEF;;;;;;GAMG;AACH,wBAAsB,yCAAyC,CAC7D,UAAU,EAAE,iCAAiC,EAC7C,UAAU,EAAE,UAAU,EACtB,SAAS,EAAE,IAAI,CAAC,iBAAiB,EAAE,eAAe,CAAC,EACnD,gBAAgB,EAAE,4BAA4B,EAC9C,MAAM,CAAC,EAAE,MAAM,GACd,OAAO,CAAC,8BAA8B,CAAC,CAkDzC;AAED;;;;GAIG;AACH,wBAAsB,oBAAoB,CACxC,OAAO,EAAE,gBAAgB,EACzB,YAAY,EAAE,oBAAoB,EAAE,EACpC,oBAAoB,EAAE,yBAAyB,EAC/C,cAAc,EAAE,cAAc,EAC9B,UAAU,EAAE,UAAU,EACtB,SAAS,EAAE,IAAI,CAAC,iBAAiB,EAAE,eAAe,CAAC,EACnD,MAAM,CAAC,EAAE,MAAM,GACd,OAAO,CAAC,wBAAwB,CAAC,CA2BnC"}