@verana-labs/vs-agent-sdk 2.0.0-dev.7 → 2.0.0-dev.72

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 (167) hide show
  1. package/README.md +0 -2
  2. package/build/adminApi/AdminApiError.d.ts +28 -0
  3. package/build/adminApi/AdminApiError.js +60 -0
  4. package/build/adminApi/AdminApiError.js.map +1 -0
  5. package/build/adminApi/didcommModules.d.ts +2 -0
  6. package/build/adminApi/didcommModules.js +8 -0
  7. package/build/adminApi/didcommModules.js.map +1 -0
  8. package/build/adminApi/index.d.ts +4 -0
  9. package/build/adminApi/index.js +21 -0
  10. package/build/adminApi/index.js.map +1 -0
  11. package/build/adminApi/moduleEndpoint.d.ts +8 -0
  12. package/build/adminApi/moduleEndpoint.js +28 -0
  13. package/build/adminApi/moduleEndpoint.js.map +1 -0
  14. package/build/adminApi/pagination/PageDto.d.ts +7 -0
  15. package/build/adminApi/pagination/PageDto.js +50 -0
  16. package/build/adminApi/pagination/PageDto.js.map +1 -0
  17. package/build/adminApi/pagination/PaginationQueryDto.d.ts +11 -0
  18. package/build/adminApi/pagination/PaginationQueryDto.js +46 -0
  19. package/build/adminApi/pagination/PaginationQueryDto.js.map +1 -0
  20. package/build/adminApi/pagination/cursor.d.ts +28 -0
  21. package/build/adminApi/pagination/cursor.js +64 -0
  22. package/build/adminApi/pagination/cursor.js.map +1 -0
  23. package/build/adminApi/pagination/index.d.ts +4 -0
  24. package/build/adminApi/pagination/index.js +21 -0
  25. package/build/adminApi/pagination/index.js.map +1 -0
  26. package/build/adminApi/pagination/paginate.d.ts +27 -0
  27. package/build/adminApi/pagination/paginate.js +77 -0
  28. package/build/adminApi/pagination/paginate.js.map +1 -0
  29. package/build/agent/VsAgent.d.ts +17 -34
  30. package/build/agent/VsAgent.js +75 -205
  31. package/build/agent/VsAgent.js.map +1 -1
  32. package/build/agent/createVsAgent.d.ts +5 -6
  33. package/build/agent/createVsAgent.js +3 -5
  34. package/build/agent/createVsAgent.js.map +1 -1
  35. package/build/blockchain/AnonCredsTrustService.d.ts +62 -0
  36. package/build/blockchain/AnonCredsTrustService.js +256 -0
  37. package/build/blockchain/AnonCredsTrustService.js.map +1 -0
  38. package/build/blockchain/AuthorizationService.d.ts +5 -3
  39. package/build/blockchain/AuthorizationService.js +17 -29
  40. package/build/blockchain/AuthorizationService.js.map +1 -1
  41. package/build/blockchain/IndexerWebSocketService.d.ts +7 -1
  42. package/build/blockchain/IndexerWebSocketService.js +68 -36
  43. package/build/blockchain/IndexerWebSocketService.js.map +1 -1
  44. package/build/blockchain/VeranaChainService.d.ts +26 -14
  45. package/build/blockchain/VeranaChainService.js +139 -127
  46. package/build/blockchain/VeranaChainService.js.map +1 -1
  47. package/build/blockchain/VeranaIndexerService.d.ts +14 -1
  48. package/build/blockchain/VeranaIndexerService.js +122 -8
  49. package/build/blockchain/VeranaIndexerService.js.map +1 -1
  50. package/build/blockchain/handlers/authorizationHandlers.js +2 -2
  51. package/build/blockchain/handlers/authorizationHandlers.js.map +1 -1
  52. package/build/blockchain/handlers/defaultHandlers.js +6 -1
  53. package/build/blockchain/handlers/defaultHandlers.js.map +1 -1
  54. package/build/blockchain/handlers/index.d.ts +1 -0
  55. package/build/blockchain/handlers/index.js +1 -0
  56. package/build/blockchain/handlers/index.js.map +1 -1
  57. package/build/blockchain/handlers/selfIssuanceHandlers.d.ts +13 -0
  58. package/build/blockchain/handlers/selfIssuanceHandlers.js +76 -0
  59. package/build/blockchain/handlers/selfIssuanceHandlers.js.map +1 -0
  60. package/build/blockchain/handlers/stateMutations.d.ts +20 -3
  61. package/build/blockchain/handlers/stateMutations.js +286 -41
  62. package/build/blockchain/handlers/stateMutations.js.map +1 -1
  63. package/build/blockchain/index.d.ts +2 -0
  64. package/build/blockchain/index.js +2 -0
  65. package/build/blockchain/index.js.map +1 -1
  66. package/build/blockchain/indexerMappers.d.ts +3 -0
  67. package/build/blockchain/indexerMappers.js +53 -0
  68. package/build/blockchain/indexerMappers.js.map +1 -0
  69. package/build/blockchain/types.d.ts +13 -58
  70. package/build/blockchain/types.js.map +1 -1
  71. package/build/bootstrap/EcsBootstrapService.d.ts +14 -3
  72. package/build/bootstrap/EcsBootstrapService.js +178 -79
  73. package/build/bootstrap/EcsBootstrapService.js.map +1 -1
  74. package/build/connections/ParentConnectionModule.d.ts +20 -0
  75. package/build/connections/ParentConnectionModule.js +56 -0
  76. package/build/connections/ParentConnectionModule.js.map +1 -0
  77. package/build/credentials/FullTailsFileService.d.ts +0 -1
  78. package/build/credentials/FullTailsFileService.js +0 -49
  79. package/build/credentials/FullTailsFileService.js.map +1 -1
  80. package/build/did/CachedDocumentLoader.d.ts +3 -0
  81. package/build/did/CachedDocumentLoader.js +7 -1
  82. package/build/did/CachedDocumentLoader.js.map +1 -1
  83. package/build/did/CachedWebDidResolver.d.ts +0 -4
  84. package/build/did/CachedWebDidResolver.js +1 -5
  85. package/build/did/CachedWebDidResolver.js.map +1 -1
  86. package/build/did/artifactServices.d.ts +12 -0
  87. package/build/did/artifactServices.js +48 -0
  88. package/build/did/artifactServices.js.map +1 -0
  89. package/build/did/legacyDidWeb.d.ts +6 -4
  90. package/build/did/legacyDidWeb.js +17 -14
  91. package/build/did/legacyDidWeb.js.map +1 -1
  92. package/build/did/publicDid.d.ts +4 -0
  93. package/build/did/publicDid.js +19 -0
  94. package/build/did/publicDid.js.map +1 -0
  95. package/build/events/BaseMessageEvents.js +139 -5
  96. package/build/events/BaseMessageEvents.js.map +1 -1
  97. package/build/events/ConnectionEvents.js +129 -82
  98. package/build/events/ConnectionEvents.js.map +1 -1
  99. package/build/events/VsAgentEvents.d.ts +11 -9
  100. package/build/events/VsAgentEvents.js +8 -1
  101. package/build/events/VsAgentEvents.js.map +1 -1
  102. package/build/events/VtFlowEvents.d.ts +1 -1
  103. package/build/events/VtFlowEvents.js +29 -17
  104. package/build/events/VtFlowEvents.js.map +1 -1
  105. package/build/index.d.ts +7 -1
  106. package/build/index.js +15 -2
  107. package/build/index.js.map +1 -1
  108. package/build/plugins/setupBaseDidComm.js +9 -12
  109. package/build/plugins/setupBaseDidComm.js.map +1 -1
  110. package/build/transports/VsAgentWsInboundTransport.d.ts +1 -1
  111. package/build/transports/VsAgentWsInboundTransport.js +20 -14
  112. package/build/transports/VsAgentWsInboundTransport.js.map +1 -1
  113. package/build/types.d.ts +7 -13
  114. package/build/types.js +1 -2
  115. package/build/types.js.map +1 -1
  116. package/build/utils/agent.d.ts +29 -4
  117. package/build/utils/agent.js +91 -22
  118. package/build/utils/agent.js.map +1 -1
  119. package/build/utils/didReadiness.d.ts +9 -0
  120. package/build/utils/didReadiness.js +42 -0
  121. package/build/utils/didReadiness.js.map +1 -0
  122. package/build/utils/ecsClaims.d.ts +47 -0
  123. package/build/utils/ecsClaims.js +120 -0
  124. package/build/utils/ecsClaims.js.map +1 -0
  125. package/build/utils/ecsService.d.ts +8 -0
  126. package/build/utils/ecsService.js +23 -0
  127. package/build/utils/ecsService.js.map +1 -0
  128. package/build/utils/index.d.ts +4 -0
  129. package/build/utils/index.js +4 -0
  130. package/build/utils/index.js.map +1 -1
  131. package/build/utils/selfIssuedEcsCredential.d.ts +8 -0
  132. package/build/utils/selfIssuedEcsCredential.js +158 -0
  133. package/build/utils/selfIssuedEcsCredential.js.map +1 -0
  134. package/build/utils/setupSelfTr.d.ts +34 -66
  135. package/build/utils/setupSelfTr.js +90 -294
  136. package/build/utils/setupSelfTr.js.map +1 -1
  137. package/build/utils/trustCredentialStore.d.ts +67 -9
  138. package/build/utils/trustCredentialStore.js +296 -111
  139. package/build/utils/trustCredentialStore.js.map +1 -1
  140. package/build/utils/util.d.ts +10 -0
  141. package/build/utils/util.js +16 -0
  142. package/build/utils/util.js.map +1 -1
  143. package/build/utils/vcdm2.d.ts +34 -0
  144. package/build/utils/vcdm2.js +60 -0
  145. package/build/utils/vcdm2.js.map +1 -0
  146. package/build/utils/vtjscResolver.d.ts +18 -0
  147. package/build/utils/vtjscResolver.js +54 -0
  148. package/build/utils/vtjscResolver.js.map +1 -0
  149. package/build/vtFlow/VtFlowOrchestrator.d.ts +107 -14
  150. package/build/vtFlow/VtFlowOrchestrator.js +734 -109
  151. package/build/vtFlow/VtFlowOrchestrator.js.map +1 -1
  152. package/build/vtFlow/assertVerifiableService.d.ts +1 -0
  153. package/build/vtFlow/assertVerifiableService.js +45 -5
  154. package/build/vtFlow/assertVerifiableService.js.map +1 -1
  155. package/build/vtFlow/ecsIssuanceExemption.d.ts +12 -0
  156. package/build/vtFlow/ecsIssuanceExemption.js +84 -0
  157. package/build/vtFlow/ecsIssuanceExemption.js.map +1 -0
  158. package/build/vtFlow/index.d.ts +1 -0
  159. package/build/vtFlow/index.js +1 -0
  160. package/build/vtFlow/index.js.map +1 -1
  161. package/package.json +17 -12
  162. package/build/did/migrateWebVhLog.d.ts +0 -34
  163. package/build/did/migrateWebVhLog.js +0 -230
  164. package/build/did/migrateWebVhLog.js.map +0 -1
  165. package/build/did/migrateWebVhVersionTime.d.ts +0 -9
  166. package/build/did/migrateWebVhVersionTime.js +0 -191
  167. package/build/did/migrateWebVhVersionTime.js.map +0 -1
@@ -3,10 +3,46 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.VtFlowOrchestrator = void 0;
4
4
  const core_1 = require("@credo-ts/core");
5
5
  const credo_ts_didcomm_vt_flow_1 = require("@verana-labs/credo-ts-didcomm-vt-flow");
6
+ const verana_types_1 = require("@verana-labs/verana-types");
6
7
  const verre_1 = require("@verana-labs/verre");
8
+ const vs_agent_model_1 = require("@verana-labs/vs-agent-model");
9
+ const adminApi_1 = require("../adminApi");
10
+ const ecsIssuanceExemption_1 = require("./ecsIssuanceExemption");
7
11
  const types_1 = require("../blockchain/types");
8
12
  const types_2 = require("../types");
9
13
  const utils_1 = require("../utils");
14
+ // The chain carries a fee discount as an integer from 0 to 10000. The API and the indexer carry it
15
+ // as a decimal from 0 to 1, so only the message is scaled.
16
+ const DISCOUNT_SCALE = 10000;
17
+ const TX_LOOKUP_TIMEOUT_MS = 60000;
18
+ const TX_LOOKUP_INTERVAL_MS = 3000;
19
+ const FEE_DENOM = 'uvna';
20
+ // the notification handler and the tx lookup can both reach markValidated for one flow at the same time
21
+ const markingValidated = new Set();
22
+ const FEE_KEYS = ['validationFees', 'issuanceFees', 'verificationFees'];
23
+ const DISCOUNT_KEYS = ['issuanceFeeDiscount', 'verificationFeeDiscount'];
24
+ function invalidInput(message) {
25
+ return new adminApi_1.AdminApiError(adminApi_1.AdminApiErrorCode.InvalidInput, 400, message);
26
+ }
27
+ function invalidState(message) {
28
+ return new adminApi_1.AdminApiError(adminApi_1.AdminApiErrorCode.InvalidState, 409, message);
29
+ }
30
+ // AUTHZ-CHECK-3 step 1. A record's expiration is only its budget clock, not a validity window.
31
+ function isActiveParticipant(participant) {
32
+ const now = Date.now();
33
+ if (!participant.effective_from || Date.parse(participant.effective_from) > now)
34
+ return false;
35
+ if (participant.effective_until && Date.parse(participant.effective_until) <= now)
36
+ return false;
37
+ return !participant.revoked && !participant.slashed;
38
+ }
39
+ function sameTerm(key, given, onEntry) {
40
+ const scale = DISCOUNT_KEYS.includes(key) ? DISCOUNT_SCALE : 1;
41
+ return Math.round(given * scale) === Math.round(onEntry * scale);
42
+ }
43
+ function errorMessage(error) {
44
+ return error instanceof Error ? error.message : String(error);
45
+ }
10
46
  class VtFlowOrchestrator {
11
47
  constructor(agent, options = {}) {
12
48
  this.agent = agent;
@@ -14,10 +50,10 @@ class VtFlowOrchestrator {
14
50
  }
15
51
  async startOnboardingProcess(input) {
16
52
  var _a, _b, _c, _d;
17
- const chain = this.requireChain();
53
+ this.requireChain();
18
54
  if (!this.agent.did)
19
55
  throw new Error('Agent has no public DID');
20
- const holderParticipant = await chain.getParticipant(input.applicantParticipantId);
56
+ const holderParticipant = await this.agent.indexer.findParticipant(input.applicantParticipantId);
21
57
  if (!holderParticipant) {
22
58
  throw new Error(`Applicant participant ${input.applicantParticipantId} not found on chain`);
23
59
  }
@@ -37,7 +73,7 @@ class VtFlowOrchestrator {
37
73
  if (!holderParticipant.validatorParticipantId) {
38
74
  throw new Error(`Applicant participant ${input.applicantParticipantId} has no validator_participant_id`);
39
75
  }
40
- const validatorParticipant = await chain.getParticipant(Number(holderParticipant.validatorParticipantId));
76
+ const validatorParticipant = await this.agent.indexer.findParticipant(Number(holderParticipant.validatorParticipantId));
41
77
  if (!(validatorParticipant === null || validatorParticipant === void 0 ? void 0 : validatorParticipant.did)) {
42
78
  throw new Error(`Validator participant ${holderParticipant.validatorParticipantId} not resolvable`);
43
79
  }
@@ -47,46 +83,58 @@ class VtFlowOrchestrator {
47
83
  // Renewals resend the OR with the same session id so the validator re-attaches the finished flow.
48
84
  const vtFlowApi = this.resolveVtFlowApi();
49
85
  const [latest] = (await vtFlowApi.findAllByQuery({
50
- participantId: String(holderParticipant.id),
86
+ applicantParticipantId: String(holderParticipant.id),
51
87
  role: credo_ts_didcomm_vt_flow_1.VtFlowRole.Applicant,
52
88
  flowVariant: credo_ts_didcomm_vt_flow_1.VtFlowVariant.OnboardingProcess,
53
89
  }))
54
90
  .filter(record => !(0, credo_ts_didcomm_vt_flow_1.isVtFlowTerminalState)(record.state))
55
91
  .sort((a, b) => b.createdAt.getTime() - a.createdAt.getTime());
56
- if (latest && latest.state !== credo_ts_didcomm_vt_flow_1.VtFlowState.Completed && latest.state !== credo_ts_didcomm_vt_flow_1.VtFlowState.CredRevoked) {
57
- this.agent.config.logger.info(`[vt-flow] onboarding flow ${latest.id} for participant ${holderParticipant.id} is already in progress (state ${latest.state}); not resending`);
58
- return latest;
59
- }
60
- const existing = latest;
92
+ const running = latest !== undefined && !(0, credo_ts_didcomm_vt_flow_1.isVtFlowRenewable)(latest);
61
93
  let connectionId;
62
- if (existing) {
63
- const connection = await this.agent.didcomm.connections.findById(existing.connectionId);
64
- if (connection === null || connection === void 0 ? void 0 : connection.isReady)
94
+ if (latest) {
95
+ const connection = await this.agent.didcomm.connections.findById(latest.connectionId);
96
+ if (connection && (0, utils_1.isUsableConnectionTo)(connection, validatorParticipant.did))
65
97
  connectionId = connection.id;
66
98
  }
99
+ const undelivered = running && latest.state === credo_ts_didcomm_vt_flow_1.VtFlowState.OrSent && holderParticipant.opState === types_1.ValidationState.PENDING;
100
+ if (running && connectionId && !undelivered) {
101
+ this.agent.config.logger.info(`[vt-flow] onboarding flow ${latest.id} for participant ${holderParticipant.id} is in progress (state ${latest.state}) on an open connection, not resending`);
102
+ return latest;
103
+ }
67
104
  if (!connectionId) {
68
- const { connectionRecord } = await this.agent.didcomm.oob.receiveImplicitInvitation({
69
- did: validatorParticipant.did,
70
- ourDid: this.agent.did,
71
- label: this.agent.label,
72
- didCommVersion: 'v2',
73
- });
74
- if (!connectionRecord)
75
- throw new Error('Failed to establish DIDComm connection to validator');
76
- const ready = await this.agent.didcomm.connections.returnWhenIsConnected(connectionRecord.id);
77
- connectionId = ready.id;
105
+ connectionId = await (0, utils_1.connectToPublicDid)(this.agent, validatorParticipant.did);
106
+ }
107
+ if (running) {
108
+ this.agent.config.logger.info(`[vt-flow] resending the onboarding request of flow ${latest.id} (state ${latest.state}) on ${connectionId === latest.connectionId ? 'its' : 'a new'} connection`);
109
+ return vtFlowApi.resendOnboardingRequest({ vtFlowRecordId: latest.id, connectionId });
78
110
  }
79
111
  return vtFlowApi.sendOnboardingRequest({
80
112
  connectionId,
81
- participantSessionId: (_b = (_a = input.participantSessionId) !== null && _a !== void 0 ? _a : existing === null || existing === void 0 ? void 0 : existing.participantSessionId) !== null && _b !== void 0 ? _b : core_1.utils.uuid(),
82
- participantId: String(holderParticipant.id),
113
+ participantSessionId: (_b = (_a = input.participantSessionId) !== null && _a !== void 0 ? _a : latest === null || latest === void 0 ? void 0 : latest.participantSessionId) !== null && _b !== void 0 ? _b : core_1.utils.uuid(),
114
+ applicantParticipantId: String(holderParticipant.id),
115
+ applicantParticipantRole: Number(holderParticipant.role),
116
+ validatorParticipantId: String(holderParticipant.validatorParticipantId),
117
+ schemaId: String(holderParticipant.schemaId),
83
118
  agentParticipantId: String((_c = this.options.agentParticipantId) !== null && _c !== void 0 ? _c : 0),
84
119
  walletAgentParticipantId: String((_d = this.options.walletAgentParticipantId) !== null && _d !== void 0 ? _d : 0),
85
120
  claims: input.claims,
86
121
  });
87
122
  }
88
- async validateAndOfferCredential(input) {
89
- var _a;
123
+ /**
124
+ * Validate an onboarding process: record the outcome on-chain, accept the onboarding request and
125
+ * move the record to VALIDATED. It sends no credential. Only a HOLDER takes part in a credential
126
+ * exchange; an ISSUER, a VERIFIER or a grantor joins the Ecosystem, and SetParticipantOPToValidated
127
+ * is the whole of it. The caller decides what follows, from the role of the returned participant.
128
+ *
129
+ * For a HOLDER it builds the credential before the chain write, and returns it for
130
+ * offerOnboardingCredential. Claims that do not satisfy the schema, or a schema fetch that fails,
131
+ * must stop the process while the chain holds no outcome yet and the caller can repeat the call.
132
+ *
133
+ * A record that is already VALIDATED and has no credential exchange keeps the chain outcome and
134
+ * only builds the credential again. A repeat call thus re-drives an offer that did not complete.
135
+ */
136
+ async validateOnboardingProcess(input) {
137
+ var _a, _b;
90
138
  const chain = this.requireChain();
91
139
  if (!this.agent.did)
92
140
  throw new Error('Agent has no public DID');
@@ -99,43 +147,550 @@ class VtFlowOrchestrator {
99
147
  if (record.variant !== credo_ts_didcomm_vt_flow_1.VtFlowVariant.OnboardingProcess) {
100
148
  throw new Error(`Record variant is '${record.variant}', expected OnboardingProcess`);
101
149
  }
102
- if (record.state !== credo_ts_didcomm_vt_flow_1.VtFlowState.AwaitingOr) {
103
- throw new Error(`Record state is '${record.state}', expected '${credo_ts_didcomm_vt_flow_1.VtFlowState.AwaitingOr}'`);
104
- }
105
- if (!record.participantId)
106
- throw new Error('Record has no participantId');
107
- const holderParticipantId = Number(record.participantId);
108
- const holderParticipant = await chain.getParticipant(holderParticipantId);
109
- if (!holderParticipant)
110
- throw new Error(`Holder participant ${holderParticipantId} not found on chain`);
111
- if (!holderParticipant.did)
112
- throw new Error('Holder participant has no DID');
113
- const unsignedCredentialJson = await this.buildCredential({
114
- credentialSchemaId: input.credentialSchemaId,
115
- subjectDid: holderParticipant.did,
116
- claims: ((_a = record.claims) !== null && _a !== void 0 ? _a : {}),
117
- credentialType: input.credentialType,
118
- credentialContext: input.credentialContext,
119
- });
150
+ const resumeOffer = record.state === credo_ts_didcomm_vt_flow_1.VtFlowState.Validated && !record.credentialExchangeRecordId;
151
+ if (record.state !== credo_ts_didcomm_vt_flow_1.VtFlowState.AwaitingOr && !resumeOffer) {
152
+ throw new Error(`Record state is '${record.state}', expected '${credo_ts_didcomm_vt_flow_1.VtFlowState.AwaitingOr}', or ` +
153
+ `'${credo_ts_didcomm_vt_flow_1.VtFlowState.Validated}' with no credential exchange`);
154
+ }
155
+ if (!record.applicantParticipantId)
156
+ throw new Error('Record has no applicantParticipantId');
157
+ const participantId = Number(record.applicantParticipantId);
158
+ const participant = await this.agent.indexer.findParticipant(participantId);
159
+ if (!participant)
160
+ throw new Error(`Applicant participant ${participantId} not found on chain`);
161
+ if (!participant.did)
162
+ throw new Error('Applicant participant has no DID');
163
+ const credential = Number(participant.role) === types_2.HOLDER_PARTICIPANT_TYPE
164
+ ? await this.buildCredential({
165
+ credentialSchemaId: (_a = input.credentialSchemaId) !== null && _a !== void 0 ? _a : String(participant.schemaId),
166
+ subjectDid: participant.did,
167
+ claims: ((_b = record.claims) !== null && _b !== void 0 ? _b : {}),
168
+ credentialType: input.credentialType,
169
+ credentialContext: input.credentialContext,
170
+ })
171
+ : undefined;
172
+ if (resumeOffer)
173
+ return { record, participant, credential };
120
174
  // op_summary_digest (MOD-PP-MSG-3) digests the applicant's submission, not the credential
121
- await chain.setParticipantOPToValidated({
122
- id: holderParticipantId,
123
- corporation: holderParticipant.corporation,
124
- });
175
+ await chain.setParticipantOPToValidated({ id: participantId, corporation: participant.corporation });
125
176
  await vtFlowApi.acceptOnboardingRequest(record.id);
126
- await vtFlowApi.markValidated(record.id);
177
+ const validated = await vtFlowApi.markValidated(record.id);
178
+ return { record: validated, participant, credential };
179
+ }
180
+ /**
181
+ * Offer the credential of an onboarding process to its applicant. Call it only for a HOLDER, and
182
+ * only after validateOnboardingProcess has moved the record to VALIDATED. Give it the credential
183
+ * that validateOnboardingProcess built, so that the process does not build the credential twice.
184
+ */
185
+ async offerOnboardingCredential(input) {
186
+ var _a, _b, _c, _d, _e;
187
+ this.requireChain();
188
+ const vtFlowApi = this.resolveVtFlowApi();
189
+ const record = await vtFlowApi.findById(input.vtFlowRecordId);
190
+ if (!record)
191
+ throw new Error(`vt-flow record ${input.vtFlowRecordId} not found`);
192
+ if (!record.applicantParticipantId)
193
+ throw new Error('Record has no applicantParticipantId');
194
+ const participant = (_a = input.participant) !== null && _a !== void 0 ? _a : (await this.agent.indexer.findParticipant(Number(record.applicantParticipantId)));
195
+ if (!(participant === null || participant === void 0 ? void 0 : participant.did))
196
+ throw new Error('Applicant participant has no DID');
197
+ const unsignedCredentialJson = (_b = input.credential) !== null && _b !== void 0 ? _b : (await this.buildCredential({
198
+ credentialSchemaId: (_c = input.credentialSchemaId) !== null && _c !== void 0 ? _c : String(participant.schemaId),
199
+ subjectDid: participant.did,
200
+ claims: ((_d = record.claims) !== null && _d !== void 0 ? _d : {}),
201
+ credentialType: input.credentialType,
202
+ credentialContext: input.credentialContext,
203
+ // [VSA-VTI-FLOW-FMT-1]: markValidated sets it from the entry on every pass, a renewal included
204
+ validUntil: (_e = record.validation) === null || _e === void 0 ? void 0 : _e.effectiveUntil,
205
+ }));
206
+ // W3C Data Integrity attachment format (RFC 0809). The applicant DID is the credential subject
207
+ // and the exchange runs over its authenticated connection, so no extra binding is required. The
208
+ // cryptosuite is the issuer's choice and is applied when the credential is issued.
127
209
  const { record: offered } = await vtFlowApi.offerCredentialForSession({
128
210
  vtFlowRecordId: record.id,
129
- issuerParticipantId: Number(holderParticipant.validatorParticipantId),
211
+ issuerParticipantId: Number(participant.validatorParticipantId),
130
212
  credentialFormats: {
131
- jsonld: {
132
- credential: unsignedCredentialJson,
133
- options: { proofType: 'Ed25519Signature2020', proofPurpose: 'assertionMethod' },
134
- },
213
+ dataIntegrity: { credential: unsignedCredentialJson, bindingRequired: false },
135
214
  },
136
215
  });
137
216
  return offered;
138
217
  }
218
+ /**
219
+ * Close an onboarding process that carries no credential exchange, on the validator side. The
220
+ * applicant reaches the same state from the SetParticipantOPToValidated chain event.
221
+ */
222
+ async completeOnboardingProcess(vtFlowRecordId) {
223
+ return this.resolveVtFlowApi().markCompleted(vtFlowRecordId);
224
+ }
225
+ /** [VSA-ADM-VT-FL-VALIDATE] */
226
+ async validateFlow(input) {
227
+ var _a, _b;
228
+ this.requireChain();
229
+ const vtFlowApi = this.resolveVtFlowApi();
230
+ const record = await vtFlowApi.findById(input.vtFlowRecordId);
231
+ if (!record) {
232
+ throw new adminApi_1.AdminApiError(adminApi_1.AdminApiErrorCode.UnknownId, 404, `no flow with id "${input.vtFlowRecordId}"`);
233
+ }
234
+ if (record.role !== credo_ts_didcomm_vt_flow_1.VtFlowRole.Validator)
235
+ throw invalidState('validate is a validator action');
236
+ const reissue = record.state === credo_ts_didcomm_vt_flow_1.VtFlowState.CredOffered && ((_b = (_a = record.issuance) === null || _a === void 0 ? void 0 : _a.tx) === null || _b === void 0 ? void 0 : _b.status) === credo_ts_didcomm_vt_flow_1.VtFlowTxStatus.Failed;
237
+ if (reissue && record.credentialExchangeRecordId) {
238
+ const { record: issued } = await vtFlowApi.issueCredentialForSession({
239
+ vtFlowRecordId: record.id,
240
+ credentialExchangeRecordId: record.credentialExchangeRecordId,
241
+ });
242
+ return issued;
243
+ }
244
+ if (record.variant === credo_ts_didcomm_vt_flow_1.VtFlowVariant.DirectIssuance)
245
+ return this.validateDirectIssuance(record);
246
+ if (!record.applicantParticipantId)
247
+ throw invalidState('the flow names no applicant participant');
248
+ const applicant = await this.agent.indexer.getParticipant(record.applicantParticipantId);
249
+ const entryValidated = applicant.op_state === 'VALIDATED';
250
+ const rejectedInFlight = entryValidated && (await this.isRejectedInFlight(record));
251
+ if (!rejectedInFlight) {
252
+ this.assertValidateState(record, entryValidated);
253
+ if (record.connectionTerminated)
254
+ throw invalidState('the flow connection is TERMINATED until the applicant reconnects');
255
+ }
256
+ const terms = this.validationTerms(input, applicant, record);
257
+ await this.assertClaimsAndTerm(record, applicant, terms);
258
+ // [VSA-VTI-FLOW-OP-ISSUE-6]: the connection stays TERMINATED, so issuance waits for the applicant to reconnect
259
+ if (rejectedInFlight)
260
+ return this.markValidated(record.id, applicant);
261
+ if (record.state === credo_ts_didcomm_vt_flow_1.VtFlowState.OobPending)
262
+ await this.sendValidating(record);
263
+ if (record.state === credo_ts_didcomm_vt_flow_1.VtFlowState.ValidatedPendingClaims)
264
+ return this.continueAfterValidated(record.id);
265
+ if (entryValidated) {
266
+ if (record.state !== credo_ts_didcomm_vt_flow_1.VtFlowState.Validated)
267
+ await this.markValidated(record.id, applicant);
268
+ return this.continueAfterValidated(record.id);
269
+ }
270
+ const validation = Object.assign({ decidedAt: new Date().toISOString(), submission: await this.submissionPath(applicant) }, terms);
271
+ if (validation.submission === credo_ts_didcomm_vt_flow_1.VtFlowSubmission.Operator) {
272
+ return vtFlowApi.recordValidation(record.id, validation, credo_ts_didcomm_vt_flow_1.VtFlowState.AwaitingValidationTx);
273
+ }
274
+ return this.submitValidation(record.id, applicant, validation);
275
+ }
276
+ // A Direct Issuance flow has no entry to validate, so no transaction: check the claims, then offer.
277
+ async validateDirectIssuance(record) {
278
+ if (record.state !== credo_ts_didcomm_vt_flow_1.VtFlowState.Validating && record.state !== credo_ts_didcomm_vt_flow_1.VtFlowState.OobPending) {
279
+ throw invalidState(`the flow is '${record.state}', which validate does not accept`);
280
+ }
281
+ if (!record.schemaId)
282
+ throw invalidState('the flow names no credential schema');
283
+ const connection = await this.agent.didcomm.connections.findById(record.connectionId);
284
+ if (!(connection === null || connection === void 0 ? void 0 : connection.theirDid))
285
+ throw invalidState('the flow connection has no peer DID');
286
+ const schema = await this.agent.indexer.getCredentialSchema(record.schemaId);
287
+ this.assertClaims(schema.json_schema, connection.theirDid, record.claims);
288
+ const vtFlowApi = this.resolveVtFlowApi();
289
+ if (record.state === credo_ts_didcomm_vt_flow_1.VtFlowState.OobPending)
290
+ await this.sendValidating(record);
291
+ const offer = await this.buildDirectIssuanceOffer(record.id);
292
+ if (!offer)
293
+ throw invalidState('the agent holds no active ISSUER participant for the schema of the flow');
294
+ const { record: offered } = await vtFlowApi.offerCredentialForSession(Object.assign({ vtFlowRecordId: record.id }, offer));
295
+ return offered;
296
+ }
297
+ // [VSA-ADM-VT-FL-START]: checked before the flow moves, as a failed send would leave it in VALIDATING
298
+ async sendValidating(record) {
299
+ const connection = await this.agent.didcomm.connections.findById(record.connectionId);
300
+ if (!(connection === null || connection === void 0 ? void 0 : connection.isReady))
301
+ throw invalidState('the flow connection is not ESTABLISHED');
302
+ await this.resolveVtFlowApi().sendValidating(record.id);
303
+ }
304
+ assertClaims(jsonSchema, subjectDid, claims) {
305
+ const violations = (0, utils_1.schemaViolations)(JSON.parse(jsonSchema), Object.assign({ id: subjectDid }, (claims !== null && claims !== void 0 ? claims : {})));
306
+ if (violations.length > 0) {
307
+ throw new adminApi_1.AdminApiError(adminApi_1.AdminApiErrorCode.InvalidClaims, 422, 'the claim set does not satisfy the json_schema', { violations });
308
+ }
309
+ }
310
+ assertValidateState(record, entryValidated) {
311
+ const accepted = [
312
+ credo_ts_didcomm_vt_flow_1.VtFlowState.Validating,
313
+ credo_ts_didcomm_vt_flow_1.VtFlowState.OobPending,
314
+ credo_ts_didcomm_vt_flow_1.VtFlowState.ValidationTxFailed,
315
+ credo_ts_didcomm_vt_flow_1.VtFlowState.ValidatedPendingClaims,
316
+ ];
317
+ // Once the entry is VALIDATED on chain any validator-side state resumes into issuance, which is
318
+ // how a backend that disabled the default notification handler drives the flow ([VSA-ADM-VT-FL-VALIDATE-11]).
319
+ const acceptedOnceValidated = [
320
+ credo_ts_didcomm_vt_flow_1.VtFlowState.AwaitingOr,
321
+ credo_ts_didcomm_vt_flow_1.VtFlowState.AwaitingValidationTx,
322
+ credo_ts_didcomm_vt_flow_1.VtFlowState.ValidationTxSubmitted,
323
+ credo_ts_didcomm_vt_flow_1.VtFlowState.Validated,
324
+ ];
325
+ if (accepted.includes(record.state))
326
+ return;
327
+ if (entryValidated && acceptedOnceValidated.includes(record.state))
328
+ return;
329
+ throw invalidState(`the flow is '${record.state}', which validate does not accept`);
330
+ }
331
+ validationTerms(input, applicant, record) {
332
+ var _a, _b, _c, _d, _e, _f, _g, _h;
333
+ for (const key of FEE_KEYS) {
334
+ const fee = input[key];
335
+ if (fee !== undefined && !(Number.isInteger(fee) && fee >= 0)) {
336
+ throw invalidInput(`${key} must be a non-negative integer`);
337
+ }
338
+ }
339
+ for (const key of DISCOUNT_KEYS) {
340
+ const discount = input[key];
341
+ if (discount !== undefined && !(discount >= 0 && discount <= 1)) {
342
+ throw invalidInput(`${key} must be between 0 and 1`);
343
+ }
344
+ }
345
+ if (input.effectiveUntil !== undefined && Number.isNaN(Date.parse(input.effectiveUntil))) {
346
+ throw invalidInput('effectiveUntil must be an ISO 8601 datetime');
347
+ }
348
+ const onEntry = {
349
+ validationFees: (_a = applicant.validation_fees) !== null && _a !== void 0 ? _a : 0,
350
+ issuanceFees: (_b = applicant.issuance_fees) !== null && _b !== void 0 ? _b : 0,
351
+ verificationFees: (_c = applicant.verification_fees) !== null && _c !== void 0 ? _c : 0,
352
+ issuanceFeeDiscount: (_d = applicant.issuance_fee_discount) !== null && _d !== void 0 ? _d : 0,
353
+ verificationFeeDiscount: (_e = applicant.verification_fee_discount) !== null && _e !== void 0 ? _e : 0,
354
+ };
355
+ const renewal = !!applicant.effective_from;
356
+ const recorded = record.state === credo_ts_didcomm_vt_flow_1.VtFlowState.ValidationTxFailed ? record.validation : undefined;
357
+ const terms = {
358
+ effectiveUntil: (_f = input.effectiveUntil) !== null && _f !== void 0 ? _f : recorded === null || recorded === void 0 ? void 0 : recorded.effectiveUntil,
359
+ opSummaryDigest: (_g = input.opSummaryDigest) !== null && _g !== void 0 ? _g : recorded === null || recorded === void 0 ? void 0 : recorded.opSummaryDigest,
360
+ };
361
+ for (const key of [...FEE_KEYS, ...DISCOUNT_KEYS]) {
362
+ const given = input[key];
363
+ if (renewal) {
364
+ if (given !== undefined && !sameTerm(key, given, onEntry[key])) {
365
+ throw invalidInput(`${key} differs from the entry, and a renewal keeps the agreed value`);
366
+ }
367
+ terms[key] = onEntry[key];
368
+ }
369
+ else {
370
+ terms[key] = (_h = given !== null && given !== void 0 ? given : recorded === null || recorded === void 0 ? void 0 : recorded[key]) !== null && _h !== void 0 ? _h : 0;
371
+ }
372
+ }
373
+ return terms;
374
+ }
375
+ async assertClaimsAndTerm(record, applicant, terms) {
376
+ var _a;
377
+ if (applicant.role !== types_1.ParticipantRole.Holder)
378
+ return;
379
+ if (!applicant.did)
380
+ throw invalidState('the applicant entry has no DID to issue to');
381
+ const schema = await this.agent.indexer.getCredentialSchema(applicant.schema_id);
382
+ this.assertClaims(schema.json_schema, applicant.did, record.claims);
383
+ // An ECS Organization or Persona credential needs a validUntil. With a validity period of 0 the
384
+ // VPR sets no effective_until, so the call has to carry one.
385
+ const needsValidUntil = (0, vs_agent_model_1.ecsRequiresValidUntil)(await (0, vs_agent_model_1.classifyEcsSchema)(schema.json_schema));
386
+ if (needsValidUntil && ((_a = schema.holder_validation_validity_period) !== null && _a !== void 0 ? _a : 0) === 0 && !terms.effectiveUntil) {
387
+ throw invalidInput('this schema has no validity period, so effectiveUntil is required');
388
+ }
389
+ }
390
+ async submissionPath(applicant) {
391
+ const authorization = this.agent.authorizationService;
392
+ if (!authorization || applicant.validator_participant_id == null)
393
+ return credo_ts_didcomm_vt_flow_1.VtFlowSubmission.Operator;
394
+ const validator = await this.agent.indexer
395
+ .getParticipant(applicant.validator_participant_id)
396
+ .catch(() => undefined);
397
+ if (!validator || !isActiveParticipant(validator))
398
+ return credo_ts_didcomm_vt_flow_1.VtFlowSubmission.Operator;
399
+ await authorization.refreshForOperator().catch(() => undefined);
400
+ const grant = authorization.getVsOperatorAuthorizationRecord(validator.id);
401
+ return (grant === null || grant === void 0 ? void 0 : grant.msgTypes.includes(verana_types_1.veranaTypeUrls.MsgSetParticipantOPToValidated))
402
+ ? credo_ts_didcomm_vt_flow_1.VtFlowSubmission.Agent
403
+ : credo_ts_didcomm_vt_flow_1.VtFlowSubmission.Operator;
404
+ }
405
+ async submitValidation(recordId, applicant, validation) {
406
+ var _a, _b;
407
+ const chain = this.requireChain();
408
+ const vtFlowApi = this.resolveVtFlowApi();
409
+ const fail = (reason, error) => vtFlowApi.recordValidation(recordId, Object.assign(Object.assign({}, validation), { tx: { status: credo_ts_didcomm_vt_flow_1.VtFlowTxStatus.Failed, reason, error } }), credo_ts_didcomm_vt_flow_1.VtFlowState.ValidationTxFailed);
410
+ const message = chain.setParticipantOPToValidatedMsg({
411
+ id: applicant.id,
412
+ effectiveUntil: validation.effectiveUntil ? new Date(validation.effectiveUntil) : undefined,
413
+ validationFees: validation.validationFees,
414
+ issuanceFees: validation.issuanceFees,
415
+ verificationFees: validation.verificationFees,
416
+ issuanceFeeDiscount: Math.round(((_a = validation.issuanceFeeDiscount) !== null && _a !== void 0 ? _a : 0) * DISCOUNT_SCALE),
417
+ verificationFeeDiscount: Math.round(((_b = validation.verificationFeeDiscount) !== null && _b !== void 0 ? _b : 0) * DISCOUNT_SCALE),
418
+ opSummaryDigest: validation.opSummaryDigest,
419
+ });
420
+ const checked = await this.preflight(message, applicant.validator_participant_id);
421
+ if ('reason' in checked)
422
+ return fail(checked.reason, checked.error);
423
+ let hash;
424
+ try {
425
+ hash = await chain.broadcastWithoutWaiting([message], checked.fee);
426
+ }
427
+ catch (error) {
428
+ return fail(credo_ts_didcomm_vt_flow_1.VtFlowTxReason.BroadcastError, errorMessage(error));
429
+ }
430
+ const submitted = await vtFlowApi.recordValidation(recordId, Object.assign(Object.assign({}, validation), { tx: { hash, submittedAt: new Date().toISOString(), status: credo_ts_didcomm_vt_flow_1.VtFlowTxStatus.Submitted } }), credo_ts_didcomm_vt_flow_1.VtFlowState.ValidationTxSubmitted);
431
+ void this.resolveValidationTx(recordId).catch(error => this.agent.config.logger.error(`[vt-flow] resolving the validation of ${recordId} failed`, {
432
+ error: errorMessage(error),
433
+ }));
434
+ return submitted;
435
+ }
436
+ // [VSA-ADM-VT-FL-VALIDATE-6]: the fee payer is the Corporation when the grant of the entry has with_feegrant
437
+ async preflight(message, participantId) {
438
+ var _a, _b, _c;
439
+ const chain = this.requireChain();
440
+ const grant = participantId == null
441
+ ? undefined
442
+ : (_a = this.agent.authorizationService) === null || _a === void 0 ? void 0 : _a.getVsOperatorAuthorizationRecord(participantId);
443
+ const granter = (grant === null || grant === void 0 ? void 0 : grant.withFeegrant) ? chain.corporation : undefined;
444
+ const failed = (reason, error) => ({
445
+ reason,
446
+ error,
447
+ });
448
+ let allowance;
449
+ try {
450
+ allowance = granter ? await chain.feeAllowance(granter, FEE_DENOM) : undefined;
451
+ }
452
+ catch (error) {
453
+ return failed(credo_ts_didcomm_vt_flow_1.VtFlowTxReason.PreflightError, errorMessage(error));
454
+ }
455
+ if (granter && !allowance) {
456
+ return failed(credo_ts_didcomm_vt_flow_1.VtFlowTxReason.FeegrantExpired, 'the Corporation grants the agent no active fee allowance');
457
+ }
458
+ let fee;
459
+ try {
460
+ fee = await chain.estimateFee([message], granter);
461
+ }
462
+ catch (error) {
463
+ return failed(credo_ts_didcomm_vt_flow_1.VtFlowTxReason.PreflightError, errorMessage(error));
464
+ }
465
+ const amount = BigInt((_c = (_b = fee.amount.find(coin => coin.denom === FEE_DENOM)) === null || _b === void 0 ? void 0 : _b.amount) !== null && _c !== void 0 ? _c : '0');
466
+ if (granter && allowance) {
467
+ if (!allowance.unlimited && allowance.remaining < amount) {
468
+ return failed(credo_ts_didcomm_vt_flow_1.VtFlowTxReason.FeegrantExhausted, `the fee allowance has ${allowance.remaining}${FEE_DENOM} left`);
469
+ }
470
+ let corporation;
471
+ try {
472
+ corporation = BigInt((await chain.getAccountBalance(granter, FEE_DENOM)).amount);
473
+ }
474
+ catch (error) {
475
+ return failed(credo_ts_didcomm_vt_flow_1.VtFlowTxReason.PreflightError, errorMessage(error));
476
+ }
477
+ if (corporation < amount) {
478
+ return failed(credo_ts_didcomm_vt_flow_1.VtFlowTxReason.InsufficientFundsCorporation, `the Corporation holds ${corporation}${FEE_DENOM}`);
479
+ }
480
+ }
481
+ else {
482
+ let own;
483
+ try {
484
+ own = BigInt((await chain.getBalance(FEE_DENOM)).amount);
485
+ }
486
+ catch (error) {
487
+ return failed(credo_ts_didcomm_vt_flow_1.VtFlowTxReason.PreflightError, errorMessage(error));
488
+ }
489
+ if (own < amount) {
490
+ return failed(credo_ts_didcomm_vt_flow_1.VtFlowTxReason.InsufficientFundsAgent, `the agent account holds ${own}${FEE_DENOM}`);
491
+ }
492
+ }
493
+ return { fee };
494
+ }
495
+ // [VSA-VTI-FLOW-ISSUE-1]: [VSA-ADM-VT-FL-VALIDATE-6] to -9 applied to the anchoring, which the delivery waits for.
496
+ // A transaction left SUBMITTED by a restart is looked up again, not broadcast twice.
497
+ async anchor(record, message, issuerParticipantId) {
498
+ var _a, _b, _c;
499
+ const chain = this.requireChain();
500
+ const pending = ((_b = (_a = record.issuance) === null || _a === void 0 ? void 0 : _a.tx) === null || _b === void 0 ? void 0 : _b.status) === credo_ts_didcomm_vt_flow_1.VtFlowTxStatus.Submitted ? record.issuance.tx : undefined;
501
+ let hash = pending === null || pending === void 0 ? void 0 : pending.hash;
502
+ let submittedAt = (_c = pending === null || pending === void 0 ? void 0 : pending.submittedAt) !== null && _c !== void 0 ? _c : new Date().toISOString();
503
+ if (!hash) {
504
+ const checked = await this.preflight(message, issuerParticipantId);
505
+ if ('reason' in checked)
506
+ return Object.assign({ status: credo_ts_didcomm_vt_flow_1.VtFlowTxStatus.Failed }, checked);
507
+ try {
508
+ hash = await chain.broadcastWithoutWaiting([message], checked.fee);
509
+ }
510
+ catch (error) {
511
+ return {
512
+ status: credo_ts_didcomm_vt_flow_1.VtFlowTxStatus.Failed,
513
+ reason: credo_ts_didcomm_vt_flow_1.VtFlowTxReason.BroadcastError,
514
+ error: errorMessage(error),
515
+ };
516
+ }
517
+ submittedAt = new Date().toISOString();
518
+ await this.resolveVtFlowApi().recordIssuance(record.id, {
519
+ tx: { hash, submittedAt, status: credo_ts_didcomm_vt_flow_1.VtFlowTxStatus.Submitted },
520
+ });
521
+ }
522
+ for (;;) {
523
+ const tx = await chain.findTx(hash).catch(() => undefined);
524
+ if ((tx === null || tx === void 0 ? void 0 : tx.code) === 0)
525
+ return { hash, submittedAt, height: tx.height, status: credo_ts_didcomm_vt_flow_1.VtFlowTxStatus.Succeeded };
526
+ if (tx) {
527
+ const failure = { status: credo_ts_didcomm_vt_flow_1.VtFlowTxStatus.Failed, reason: credo_ts_didcomm_vt_flow_1.VtFlowTxReason.TxFailed, error: tx.rawLog };
528
+ return Object.assign({ hash, submittedAt, height: tx.height }, failure);
529
+ }
530
+ if (Date.now() - Date.parse(submittedAt) >= TX_LOOKUP_TIMEOUT_MS) {
531
+ const error = 'not found 60 seconds after the broadcast';
532
+ return { hash, submittedAt, status: credo_ts_didcomm_vt_flow_1.VtFlowTxStatus.Failed, reason: credo_ts_didcomm_vt_flow_1.VtFlowTxReason.TxNotFound, error };
533
+ }
534
+ await new Promise(resolve => setTimeout(resolve, TX_LOOKUP_INTERVAL_MS));
535
+ }
536
+ }
537
+ // [VSA-ADM-VT-FL-VALIDATE-8]. Leaving VALIDATION_TX_SUBMITTED ends the loop, so the notification
538
+ // handler moving the flow first wins the race.
539
+ async resolveValidationTx(recordId) {
540
+ var _a, _b, _c;
541
+ const chain = this.requireChain();
542
+ const vtFlowApi = this.resolveVtFlowApi();
543
+ let landed;
544
+ for (;;) {
545
+ const record = await vtFlowApi.findById(recordId);
546
+ const validation = record === null || record === void 0 ? void 0 : record.validation;
547
+ const hash = (_a = validation === null || validation === void 0 ? void 0 : validation.tx) === null || _a === void 0 ? void 0 : _a.hash;
548
+ if (!(record === null || record === void 0 ? void 0 : record.applicantParticipantId) || !validation || !hash)
549
+ return;
550
+ if (record.state !== credo_ts_didcomm_vt_flow_1.VtFlowState.ValidationTxSubmitted)
551
+ return;
552
+ const tx = landed !== null && landed !== void 0 ? landed : (await chain.findTx(hash).catch(() => undefined));
553
+ if (tx && tx.code === 0) {
554
+ landed = tx;
555
+ // the indexer can lag the block of the transaction, and the fill needs the validated entry
556
+ const entry = await this.agent.indexer.getParticipant(record.applicantParticipantId);
557
+ if (entry.op_state === 'VALIDATED') {
558
+ await this.markValidated(recordId, entry, { hash, height: tx.height });
559
+ await this.continueAfterValidated(recordId);
560
+ return;
561
+ }
562
+ }
563
+ else if (tx) {
564
+ return this.failUnlessValidated(record, validation, credo_ts_didcomm_vt_flow_1.VtFlowTxReason.TxFailed, tx.rawLog, tx.height);
565
+ }
566
+ const submittedAt = (_c = (_b = validation.tx) === null || _b === void 0 ? void 0 : _b.submittedAt) !== null && _c !== void 0 ? _c : validation.decidedAt;
567
+ if (Date.now() - Date.parse(submittedAt) >= TX_LOOKUP_TIMEOUT_MS) {
568
+ if (tx) {
569
+ this.agent.config.logger.warn(`[vt-flow] the transaction of flow ${recordId} landed, but the indexer does not show the entry VALIDATED yet: its notification completes the flow`);
570
+ return;
571
+ }
572
+ return this.failUnlessValidated(record, validation, credo_ts_didcomm_vt_flow_1.VtFlowTxReason.TxNotFound, 'not found 60 seconds after the broadcast');
573
+ }
574
+ await new Promise(resolve => setTimeout(resolve, TX_LOOKUP_INTERVAL_MS));
575
+ }
576
+ }
577
+ // The VPR has no distinct error for a second SetParticipantOPtoValidated, so the entry tells a
578
+ // rejected duplicate apart from a real failure.
579
+ async failUnlessValidated(record, validation, reason, error, height) {
580
+ var _a;
581
+ const vtFlowApi = this.resolveVtFlowApi();
582
+ const applicant = record.applicantParticipantId
583
+ ? await this.agent.indexer.getParticipant(record.applicantParticipantId)
584
+ : undefined;
585
+ const failed = Object.assign(Object.assign({}, validation), { tx: Object.assign(Object.assign({}, validation.tx), { height, status: credo_ts_didcomm_vt_flow_1.VtFlowTxStatus.Failed, reason, error }) });
586
+ if ((applicant === null || applicant === void 0 ? void 0 : applicant.op_state) === 'VALIDATED') {
587
+ // the notification handler may have moved the flow while the tx and the entry were read
588
+ if (((_a = (await vtFlowApi.findById(record.id))) === null || _a === void 0 ? void 0 : _a.state) !== credo_ts_didcomm_vt_flow_1.VtFlowState.ValidationTxSubmitted)
589
+ return;
590
+ if (reason === credo_ts_didcomm_vt_flow_1.VtFlowTxReason.TxFailed)
591
+ await vtFlowApi.recordValidation(record.id, failed);
592
+ await this.markValidated(record.id, applicant);
593
+ await this.continueAfterValidated(record.id);
594
+ return;
595
+ }
596
+ await vtFlowApi.recordValidation(record.id, failed, credo_ts_didcomm_vt_flow_1.VtFlowState.ValidationTxFailed);
597
+ }
598
+ /**
599
+ * [VSA-VTI-FLOW-OP-ISSUE]: moves the flow to VALIDATED and fills `validation` from the validated
600
+ * entry. Without the landed transaction, a recorded broadcast keeps its tx, and its submission
601
+ * unless that tx failed.
602
+ */
603
+ async markValidated(recordId, entry, landed) {
604
+ var _a, _b, _c, _d, _e;
605
+ const vtFlowApi = this.resolveVtFlowApi();
606
+ if (markingValidated.has(recordId))
607
+ throw invalidState('the flow is already moving to VALIDATED');
608
+ markingValidated.add(recordId);
609
+ try {
610
+ const record = await vtFlowApi.findById(recordId);
611
+ if (!record)
612
+ throw new Error(`vt-flow record ${recordId} not found`);
613
+ if (!credo_ts_didcomm_vt_flow_1.VtFlowValidatedFromStates.has(record.state) &&
614
+ record.state !== credo_ts_didcomm_vt_flow_1.VtFlowState.TerminatedByValidator) {
615
+ throw invalidState(`the flow is '${record.state}', which does not precede VALIDATED`);
616
+ }
617
+ const recorded = record.validation;
618
+ const agentTx = landed && ((_a = recorded === null || recorded === void 0 ? void 0 : recorded.tx) === null || _a === void 0 ? void 0 : _a.hash) === landed.hash ? recorded.tx : undefined;
619
+ const validation = Object.assign(Object.assign(Object.assign({}, recorded), { decidedAt: (_c = (_b = recorded === null || recorded === void 0 ? void 0 : recorded.decidedAt) !== null && _b !== void 0 ? _b : landed === null || landed === void 0 ? void 0 : landed.timestamp) !== null && _c !== void 0 ? _c : entry.modified, submission: !landed && ((_d = recorded === null || recorded === void 0 ? void 0 : recorded.tx) === null || _d === void 0 ? void 0 : _d.hash) && recorded.tx.reason !== credo_ts_didcomm_vt_flow_1.VtFlowTxReason.TxFailed
620
+ ? recorded.submission
621
+ : agentTx
622
+ ? credo_ts_didcomm_vt_flow_1.VtFlowSubmission.Agent
623
+ : credo_ts_didcomm_vt_flow_1.VtFlowSubmission.Operator, validationFees: entry.validation_fees, issuanceFees: entry.issuance_fees, verificationFees: entry.verification_fees, issuanceFeeDiscount: entry.issuance_fee_discount, verificationFeeDiscount: entry.verification_fee_discount, effectiveUntil: (_e = entry.effective_until) !== null && _e !== void 0 ? _e : undefined }), (agentTx && {
624
+ tx: {
625
+ hash: agentTx.hash,
626
+ submittedAt: agentTx.submittedAt,
627
+ height: landed === null || landed === void 0 ? void 0 : landed.height,
628
+ status: credo_ts_didcomm_vt_flow_1.VtFlowTxStatus.Succeeded,
629
+ },
630
+ }));
631
+ return await vtFlowApi.recordValidation(recordId, validation, credo_ts_didcomm_vt_flow_1.VtFlowState.Validated);
632
+ }
633
+ finally {
634
+ markingValidated.delete(recordId);
635
+ }
636
+ }
637
+ /** rejectFlow ended the flow with its transaction in flight, and no newer flow of the applicant replaced it ([VSA-ADM-VT-FL-REJECT-2]). */
638
+ async isRejectedInFlight(record) {
639
+ if (record.state !== credo_ts_didcomm_vt_flow_1.VtFlowState.TerminatedByValidator || !record.validation)
640
+ return false;
641
+ if (!record.applicantParticipantId)
642
+ return false;
643
+ const [latest] = (await this.resolveVtFlowApi().findAllByQuery({
644
+ role: credo_ts_didcomm_vt_flow_1.VtFlowRole.Validator,
645
+ applicantParticipantId: record.applicantParticipantId,
646
+ })).sort((a, b) => b.createdAt.getTime() - a.createdAt.getTime());
647
+ return (latest === null || latest === void 0 ? void 0 : latest.id) === record.id;
648
+ }
649
+ async continueAfterValidated(recordId) {
650
+ var _a;
651
+ const vtFlowApi = this.resolveVtFlowApi();
652
+ const record = await vtFlowApi.findById(recordId);
653
+ if (!record)
654
+ throw new Error(`vt-flow record ${recordId} not found`);
655
+ if (record.state !== credo_ts_didcomm_vt_flow_1.VtFlowState.Validated && record.state !== credo_ts_didcomm_vt_flow_1.VtFlowState.ValidatedPendingClaims) {
656
+ return record;
657
+ }
658
+ const participant = await this.agent.indexer.findParticipant(Number(record.applicantParticipantId));
659
+ if (!participant)
660
+ throw new Error(`Applicant participant ${record.applicantParticipantId} not found`);
661
+ if (Number(participant.role) !== types_2.HOLDER_PARTICIPANT_TYPE)
662
+ return record;
663
+ const schema = await this.agent.indexer.getCredentialSchema(participant.schemaId);
664
+ const violations = (0, utils_1.schemaViolations)(JSON.parse(schema.json_schema), Object.assign({ id: participant.did }, ((_a = record.claims) !== null && _a !== void 0 ? _a : {})));
665
+ if (violations.length > 0) {
666
+ this.agent.config.logger.error(`[vt-flow] not offering the credential of flow ${recordId}: its claims do not satisfy the ` +
667
+ `json_schema of schema ${participant.schemaId}: ${violations.map(v => `${v.path} ${v.message}`.trim()).join(', ')}`);
668
+ return record.state === credo_ts_didcomm_vt_flow_1.VtFlowState.Validated ? vtFlowApi.markPendingClaims(recordId) : record;
669
+ }
670
+ return this.offerOnboardingCredential({ vtFlowRecordId: recordId, participant });
671
+ }
672
+ /** [VSA-ADM-VT-FL-VALIDATE-8]: at startup, resume every flow left in VALIDATION_TX_SUBMITTED. */
673
+ async resumeValidationSubmissions() {
674
+ const pending = await this.resolveVtFlowApi().findAllByQuery({
675
+ role: credo_ts_didcomm_vt_flow_1.VtFlowRole.Validator,
676
+ flowState: credo_ts_didcomm_vt_flow_1.VtFlowState.ValidationTxSubmitted,
677
+ });
678
+ await Promise.all(pending.map(record => this.resolveValidationTx(record.id)));
679
+ }
680
+ /** [VSA-VTI-FLOW-ISSUE-1]: at startup, resume every anchoring left SUBMITTED through the stored issued credential. */
681
+ async resumeIssuanceSubmissions() {
682
+ const vtFlowApi = this.resolveVtFlowApi();
683
+ const offered = await vtFlowApi.findAllByQuery({
684
+ role: credo_ts_didcomm_vt_flow_1.VtFlowRole.Validator,
685
+ flowState: credo_ts_didcomm_vt_flow_1.VtFlowState.CredOffered,
686
+ });
687
+ await Promise.all(offered.map(({ id, issuance, credentialExchangeRecordId }) => {
688
+ var _a;
689
+ return ((_a = issuance === null || issuance === void 0 ? void 0 : issuance.tx) === null || _a === void 0 ? void 0 : _a.status) === credo_ts_didcomm_vt_flow_1.VtFlowTxStatus.Submitted && credentialExchangeRecordId
690
+ ? vtFlowApi.issueCredentialForSession({ vtFlowRecordId: id, credentialExchangeRecordId })
691
+ : undefined;
692
+ }));
693
+ }
139
694
  async acceptCredential(input) {
140
695
  const vtFlowApi = this.resolveVtFlowApi();
141
696
  const record = await vtFlowApi.findById(input.vtFlowRecordId);
@@ -154,7 +709,7 @@ class VtFlowOrchestrator {
154
709
  const chain = this.requireChain();
155
710
  if (!this.agent.did)
156
711
  throw new Error('Agent has no public DID');
157
- const indexer = this.requireIndexer();
712
+ const indexer = this.agent.indexer;
158
713
  const vtFlowApi = this.resolveVtFlowApi();
159
714
  const record = await vtFlowApi.findById(vtFlowRecordId);
160
715
  if (!record)
@@ -185,49 +740,51 @@ class VtFlowOrchestrator {
185
740
  });
186
741
  return {
187
742
  credentialFormats: {
188
- jsonld: {
189
- credential: unsignedCredentialJson,
190
- options: { proofType: 'Ed25519Signature2020', proofPurpose: 'assertionMethod' },
191
- },
743
+ dataIntegrity: { credential: unsignedCredentialJson, bindingRequired: false },
192
744
  },
193
745
  issuerParticipantId: issuer.id,
194
746
  };
195
747
  }
748
+ /**
749
+ * Builds the unsigned VC Data Model 2.0 credential that goes into the offer. RFC 0809 derives the
750
+ * advertised data model version from its context, and the issuer secures it with a
751
+ * DataIntegrityProof once the applicant requests it.
752
+ */
196
753
  async buildCredential(input) {
197
- var _a, _b, _c;
198
- const chain = this.requireChain();
199
- const didRecords = await this.agent.dids.getCreatedDids({ did: this.agent.did });
200
- const didRecord = didRecords[0];
201
- if (!didRecord)
202
- throw new Error('Agent DID record not found');
203
- const schemaRef = `vpr:verana:${chain.getChainId}:cs:${input.credentialSchemaId}`;
204
- const entry = await (0, utils_1.findMetadataEntry)(didRecord, '_vt/jsc', '', schemaRef);
205
- if (!entry)
206
- throw new Error(`No stored VTJSC found for ${schemaRef}`);
207
- const { data } = entry;
208
- const unsignedCredential = (0, utils_1.createCredential)({
754
+ var _a;
755
+ const jsonSchemaCredentialId = await this.resolveJsonSchemaCredentialId(input.credentialSchemaId);
756
+ // A credential whose claims don't satisfy the schema's required fields is not a valid
757
+ // instance of that credential type — reject it here rather than issue an empty shell.
758
+ const schema = await this.agent.indexer.getCredentialSchema(input.credentialSchemaId);
759
+ (0, utils_1.validateSchema)(JSON.parse(schema.json_schema), Object.assign({ id: input.subjectDid }, input.claims));
760
+ // Data model 2.0 holds the claims on the credential subject itself; the applicant DID wins over
761
+ // any `id` claim so the credential can never name a different subject
762
+ const unsignedCredential = (0, utils_1.createW3cV2Credential)({
209
763
  id: `${this.agent.did}#${core_1.utils.uuid()}`,
210
764
  type: (_a = input.credentialType) !== null && _a !== void 0 ? _a : ['VerifiableCredential', 'VerifiableTrustCredential'],
211
765
  issuer: this.agent.did,
212
- credentialSubject: { id: input.subjectDid, claims: input.claims },
766
+ context: input.credentialContext,
767
+ credentialSubject: Object.assign(Object.assign({}, input.claims), { id: input.subjectDid }),
768
+ credentialSchema: { id: jsonSchemaCredentialId, type: 'JsonSchemaCredential' },
769
+ validUntil: input.validUntil,
213
770
  });
214
- if (input.credentialContext)
215
- unsignedCredential.context = input.credentialContext;
216
- unsignedCredential.credentialSchema = {
217
- id: (_c = (_b = data.verifiableCredential) === null || _b === void 0 ? void 0 : _b[0]) === null || _c === void 0 ? void 0 : _c.id,
218
- type: 'JsonSchemaCredential',
219
- };
220
- return core_1.JsonTransformer.toJSON(unsignedCredential);
771
+ return (0, utils_1.toOfferedCredentialJson)(unsignedCredential);
772
+ }
773
+ async resolveJsonSchemaCredentialId(credentialSchemaId) {
774
+ return (0, utils_1.resolveJsonSchemaCredentialId)(this.agent, this.agent.indexer, credentialSchemaId, this.requireChain().getChainId);
221
775
  }
222
776
  /** Per the spec the algorithm comes from the schema, never from the digest value. */
223
777
  async digestAlgorithmForSchema(schemaId) {
224
- const schema = await this.requireIndexer().getCredentialSchema(schemaId);
778
+ return (await this.credentialSchema(schemaId)).digestAlgorithm;
779
+ }
780
+ async credentialSchema(schemaId) {
781
+ const schema = await this.agent.indexer.getCredentialSchema(schemaId);
225
782
  if (!schema.digest_algorithm) {
226
783
  throw new Error(`Credential schema ${schemaId} has no digest_algorithm`);
227
784
  }
228
- return schema.digest_algorithm;
785
+ return { digestAlgorithm: schema.digest_algorithm, ecsKey: await (0, vs_agent_model_1.classifyEcsSchema)(schema.json_schema) };
229
786
  }
230
- /** Fired after signing and before delivery, so a failure here must abort the issuance. */
787
+ /** Fired after signing and before delivery. A failed anchoring comes back in `issuance`, and the credential stays undelivered. */
231
788
  async onCredentialIssued(vtFlowRecordId, signedCredential) {
232
789
  var _a, _b;
233
790
  const chain = this.requireChain();
@@ -238,17 +795,20 @@ class VtFlowOrchestrator {
238
795
  if (record.issuerParticipantId == null) {
239
796
  throw new Error(`vt-flow record ${vtFlowRecordId} has no issuerParticipantId to anchor against`);
240
797
  }
241
- const issuer = await this.requireIndexer().getParticipant(record.issuerParticipantId);
798
+ const issuer = await this.agent.indexer.getParticipant(record.issuerParticipantId);
242
799
  const algorithm = await this.digestAlgorithmForSchema(issuer.schema_id);
243
800
  const digest = (0, verre_1.computeCredentialDigestJCS)(signedCredential, algorithm);
244
- await chain.createOrUpdateParticipantSession({
801
+ const message = chain.createOrUpdateParticipantSessionMsg({
245
802
  id: record.participantSessionId,
246
803
  issuerParticipantId: record.issuerParticipantId,
247
804
  agentParticipantId: Number((_a = record.agentParticipantId) !== null && _a !== void 0 ? _a : 0) || 0,
248
805
  walletAgentParticipantId: Number((_b = record.walletAgentParticipantId) !== null && _b !== void 0 ? _b : 0) || 0,
249
806
  digest,
250
807
  });
251
- return digest;
808
+ return {
809
+ credentialDigest: digest,
810
+ issuance: { tx: await this.anchor(record, message, record.issuerParticipantId) },
811
+ };
252
812
  }
253
813
  async verifyOfferedCredential(vtFlowRecordId) {
254
814
  var _a, _b;
@@ -261,7 +821,7 @@ class VtFlowOrchestrator {
261
821
  if (!record.credentialExchangeRecordId) {
262
822
  throw new Error('Record has no credentialExchangeRecordId; nothing to verify');
263
823
  }
264
- const indexer = this.requireIndexer();
824
+ const indexer = this.agent.indexer;
265
825
  const session = await indexer.getParticipantSession(record.participantSessionId);
266
826
  if (!session) {
267
827
  throw new Error(`ParticipantSession ${record.participantSessionId} not found on indexer`);
@@ -282,13 +842,36 @@ class VtFlowOrchestrator {
282
842
  if (record.schemaId != null && Number(issuer.schema_id) !== Number(record.schemaId)) {
283
843
  throw new Error(`Issuer participant schema ${issuer.schema_id} does not match credential schema ${record.schemaId}`);
284
844
  }
285
- const credentialJson = await this.getOfferedCredentialJson(record.credentialExchangeRecordId);
286
- const algorithm = await this.digestAlgorithmForSchema(issuer.schema_id);
287
- const digest = (0, verre_1.computeCredentialDigestJCS)(credentialJson, algorithm);
845
+ const credentialJson = await this.getReceivedCredentialJson(record.credentialExchangeRecordId);
846
+ await this.assertFlowCredentialFormat(credentialJson, issuer);
847
+ const { digestAlgorithm, ecsKey } = await this.credentialSchema(issuer.schema_id);
848
+ const digest = (0, verre_1.computeCredentialDigestJCS)(credentialJson, digestAlgorithm);
288
849
  const anchored = await indexer.getDigest(digest);
289
850
  if (!anchored) {
290
851
  throw new Error(`Credential digest ${digest} is not anchored on-chain`);
291
852
  }
853
+ if (ecsKey)
854
+ await vtFlowApi.setEcsSchemaKey(record.id, ecsKey);
855
+ }
856
+ /** [VSA-VTI-FLOW-FMT-1]. Credo already verified the proof and its assertionMethod key, for data model 2.0 only. */
857
+ async assertFlowCredentialFormat(credential, issuer) {
858
+ var _a, _b, _c;
859
+ if (!(0, utils_1.isVcdm2Credential)(credential)) {
860
+ throw new Error('Received credential is not a VC Data Model 2.0 credential');
861
+ }
862
+ const verificationMethod = (_a = credential.proof) === null || _a === void 0 ? void 0 : _a.verificationMethod;
863
+ if (!issuer.did || (verificationMethod === null || verificationMethod === void 0 ? void 0 : verificationMethod.split('#')[0]) !== issuer.did) {
864
+ throw new Error(`Proof verification method ${verificationMethod} is not one of the validator ${issuer.did}`);
865
+ }
866
+ const subjectId = (_b = credential.credentialSubject) === null || _b === void 0 ? void 0 : _b.id;
867
+ if (subjectId !== this.agent.did) {
868
+ throw new Error(`Credential subject ${subjectId} is not the applicant ${this.agent.did}`);
869
+ }
870
+ const schemaRef = (_c = credential.credentialSchema) === null || _c === void 0 ? void 0 : _c.id;
871
+ const jsonSchemaCredentialId = await this.resolveJsonSchemaCredentialId(String(issuer.schema_id));
872
+ if (schemaRef !== jsonSchemaCredentialId) {
873
+ throw new Error(`Credential schema ${schemaRef} is not the VTJSC ${jsonSchemaCredentialId} of the flow`);
874
+ }
292
875
  }
293
876
  async onCredentialRevoked(vtFlowRecordId) {
294
877
  const vtFlowApi = this.resolveVtFlowApi();
@@ -297,7 +880,7 @@ class VtFlowOrchestrator {
297
880
  return;
298
881
  if (!this.options.publicApiBaseUrl || !record.credentialExchangeRecordId)
299
882
  return;
300
- const credentialId = await (0, utils_1.removeStoredTrustCredential)(this.agent, this.options.publicApiBaseUrl, record.credentialExchangeRecordId);
883
+ const credentialId = await (0, utils_1.removeStoredTrustCredential)(this.agent, record.credentialExchangeRecordId);
301
884
  if (credentialId) {
302
885
  this.agent.config.logger.info(`[vt-flow] removed revoked credential and its linked VP (${credentialId})`);
303
886
  }
@@ -318,26 +901,68 @@ class VtFlowOrchestrator {
318
901
  const chain = this.agent.veranaChain;
319
902
  if (!chain || !chain.autoTriggerResolverEnabled)
320
903
  return;
321
- if (record.participantId == null)
904
+ if (record.applicantParticipantId == null)
322
905
  return;
323
- await chain.triggerResolver(Number(record.participantId));
906
+ await chain.triggerResolver(Number(record.applicantParticipantId));
324
907
  }
325
- async getOfferedCredentialJson(credentialExchangeRecordId) {
326
- var _a;
908
+ /** The signed credential as the issuer attached it, which is the exact JSON its digest covers. */
909
+ async getReceivedCredentialJson(credentialExchangeRecordId) {
910
+ var _a, _b;
327
911
  const formatData = await this.agent.didcomm.credentials.getFormatData(credentialExchangeRecordId);
328
- const credentialJson = (_a = formatData.credential) === null || _a === void 0 ? void 0 : _a.jsonld;
912
+ const credentialJson = (_b = (_a = formatData.credential) === null || _a === void 0 ? void 0 : _a.dataIntegrity) === null || _b === void 0 ? void 0 : _b.credential;
329
913
  if (!credentialJson)
330
- throw new Error('Offered credential has no JSON-LD body to verify');
914
+ throw new Error('Received credential has no data integrity credential body');
331
915
  return credentialJson;
332
916
  }
333
- requireIndexer() {
334
- if (!this.options.indexer) {
335
- throw new Error('Agent has no indexer configured (set VERANA_INDEXER_BASE_URL)');
917
+ // VS-CONN-VS gate: consulted only after trust resolution rejected the peer.
918
+ async checkEcsIssuanceExemption(context) {
919
+ return (0, ecsIssuanceExemption_1.isEcsIssuanceExempt)({
920
+ indexer: this.agent.indexer,
921
+ agent: this.agent,
922
+ trustedEcosystemDids: this.agent.trustedEcosystemDids,
923
+ }, context);
924
+ }
925
+ // the indexer can still lag the applicant's own transaction, so wait for it as a tx lookup does
926
+ async checkParticipantId({ record }) {
927
+ if (!record.applicantParticipantId || !this.agent.did)
928
+ return false;
929
+ const deadline = Date.now() + TX_LOOKUP_TIMEOUT_MS;
930
+ let validatedAtFirstRead;
931
+ for (;;) {
932
+ const entry = await this.agent.indexer
933
+ .getParticipant(record.applicantParticipantId)
934
+ .catch(() => undefined);
935
+ validatedAtFirstRead !== null && validatedAtFirstRead !== void 0 ? validatedAtFirstRead : (validatedAtFirstRead = (entry === null || entry === void 0 ? void 0 : entry.op_state) === 'VALIDATED');
936
+ if (entry) {
937
+ if (entry.revoked || entry.slashed || entry.validator_participant_id == null)
938
+ return false;
939
+ const validator = await this.agent.indexer
940
+ .getParticipant(entry.validator_participant_id)
941
+ .catch(() => undefined);
942
+ if (validator && validator.did !== this.agent.did)
943
+ return false;
944
+ if (validator && entry.op_state === 'PENDING')
945
+ return true;
946
+ if (validator && entry.op_state === 'VALIDATED') {
947
+ const flow = await this.resolveVtFlowApi().findById(record.id);
948
+ // the flow has moved on, or another path is moving it to VALIDATED and continues it itself
949
+ if ((flow === null || flow === void 0 ? void 0 : flow.state) !== credo_ts_didcomm_vt_flow_1.VtFlowState.AwaitingOr || markingValidated.has(record.id))
950
+ return 'validated';
951
+ // VALIDATED at the first read can be the round before this request, which it does not settle
952
+ if (!validatedAtFirstRead) {
953
+ await this.markValidated(record.id, entry);
954
+ await this.continueAfterValidated(record.id);
955
+ return 'validated';
956
+ }
957
+ }
958
+ }
959
+ if (Date.now() >= deadline)
960
+ return false;
961
+ await new Promise(resolve => setTimeout(resolve, TX_LOOKUP_INTERVAL_MS));
336
962
  }
337
- return this.options.indexer;
338
963
  }
339
964
  async publishCredentialAsLinkedVp(vtFlowRecordId) {
340
- var _a, _b;
965
+ var _a;
341
966
  if (!this.options.publicApiBaseUrl) {
342
967
  throw new Error('publicApiBaseUrl is required to publish a LinkedVerifiablePresentation');
343
968
  }
@@ -350,18 +975,18 @@ class VtFlowOrchestrator {
350
975
  if (!record.credentialExchangeRecordId) {
351
976
  throw new Error(`vt-flow record ${vtFlowRecordId} has no credentialExchangeRecordId`);
352
977
  }
353
- const formatData = await this.agent.didcomm.credentials.getFormatData(record.credentialExchangeRecordId);
354
- const jsonld = (_a = formatData.credential) === null || _a === void 0 ? void 0 : _a.jsonld;
355
- if (!jsonld) {
356
- throw new Error(`No jsonld credential in exchange ${record.credentialExchangeRecordId}`);
357
- }
358
- const schemaRef = (_b = jsonld.credentialSchema) === null || _b === void 0 ? void 0 : _b.id;
978
+ const credentialJson = await this.getReceivedCredentialJson(record.credentialExchangeRecordId);
979
+ const schemaRef = (_a = credentialJson.credentialSchema) === null || _a === void 0 ? void 0 : _a.id;
359
980
  const schemaBaseId = schemaRef ? this.extractSchemaBaseId(schemaRef) : undefined;
360
981
  if (!schemaBaseId) {
361
982
  throw new Error(`Cannot publish Linked VP: credential has no extractable schema base id (credentialSchema.id=${schemaRef !== null && schemaRef !== void 0 ? schemaRef : 'undefined'})`);
362
983
  }
363
- const w3cCredential = core_1.JsonTransformer.fromJSON(jsonld, core_1.W3cJsonLdVerifiableCredential);
364
- await (0, utils_1.createVtc)(this.agent, this.options.publicApiBaseUrl, schemaBaseId, w3cCredential);
984
+ // A data model 2.0 credential arrives secured with a DataIntegrityProof; a validator still on
985
+ // data model 1.1 sends a linked data proof, and both are published the same way
986
+ const credential = (0, utils_1.isVcdm2Credential)(credentialJson)
987
+ ? core_1.W3cV2DataIntegrityVerifiableCredential.fromObject(credentialJson)
988
+ : core_1.JsonTransformer.fromJSON(credentialJson, core_1.W3cJsonLdVerifiableCredential);
989
+ await (0, utils_1.createVtc)(this.agent, this.options.publicApiBaseUrl, record.ecsSchemaKey ? (0, utils_1.linkedVpSchemaId)(record.ecsSchemaKey) : schemaBaseId, credential);
365
990
  }
366
991
  extractSchemaBaseId(jscUrl) {
367
992
  var _a;