@toon-protocol/connector 2.0.0 → 2.2.0

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 (88) hide show
  1. package/dist/btp/btp-claim-types.d.ts +24 -2
  2. package/dist/btp/btp-claim-types.d.ts.map +1 -1
  3. package/dist/btp/btp-claim-types.js +115 -9
  4. package/dist/btp/btp-claim-types.js.map +1 -1
  5. package/dist/btp/inbound-claim-validator.d.ts +5 -1
  6. package/dist/btp/inbound-claim-validator.d.ts.map +1 -1
  7. package/dist/btp/inbound-claim-validator.js +52 -10
  8. package/dist/btp/inbound-claim-validator.js.map +1 -1
  9. package/dist/config/config-loader.d.ts.map +1 -1
  10. package/dist/config/config-loader.js +3 -0
  11. package/dist/config/config-loader.js.map +1 -1
  12. package/dist/config/types.d.ts +15 -0
  13. package/dist/config/types.d.ts.map +1 -1
  14. package/dist/config/types.js +45 -0
  15. package/dist/config/types.js.map +1 -1
  16. package/dist/core/connector-node.d.ts.map +1 -1
  17. package/dist/core/connector-node.js +57 -12
  18. package/dist/core/connector-node.js.map +1 -1
  19. package/dist/core/packet-handler.d.ts +10 -1
  20. package/dist/core/packet-handler.d.ts.map +1 -1
  21. package/dist/core/packet-handler.js +67 -2
  22. package/dist/core/packet-handler.js.map +1 -1
  23. package/dist/settlement/channel-manager.d.ts +3 -2
  24. package/dist/settlement/channel-manager.d.ts.map +1 -1
  25. package/dist/settlement/channel-manager.js +18 -6
  26. package/dist/settlement/channel-manager.js.map +1 -1
  27. package/dist/settlement/claim-receiver.d.ts +16 -3
  28. package/dist/settlement/claim-receiver.d.ts.map +1 -1
  29. package/dist/settlement/claim-receiver.js +434 -117
  30. package/dist/settlement/claim-receiver.js.map +1 -1
  31. package/dist/settlement/claim-sender.d.ts +4 -2
  32. package/dist/settlement/claim-sender.d.ts.map +1 -1
  33. package/dist/settlement/claim-sender.js +38 -0
  34. package/dist/settlement/claim-sender.js.map +1 -1
  35. package/dist/settlement/mina-payment-channel-sdk.d.ts +73 -0
  36. package/dist/settlement/mina-payment-channel-sdk.d.ts.map +1 -0
  37. package/dist/settlement/mina-payment-channel-sdk.js +538 -0
  38. package/dist/settlement/mina-payment-channel-sdk.js.map +1 -0
  39. package/dist/settlement/per-packet-claim-service.d.ts +12 -6
  40. package/dist/settlement/per-packet-claim-service.d.ts.map +1 -1
  41. package/dist/settlement/per-packet-claim-service.js +205 -40
  42. package/dist/settlement/per-packet-claim-service.js.map +1 -1
  43. package/dist/settlement/privacy/index.d.ts +3 -0
  44. package/dist/settlement/privacy/index.d.ts.map +1 -0
  45. package/dist/settlement/privacy/index.js +11 -0
  46. package/dist/settlement/privacy/index.js.map +1 -0
  47. package/dist/settlement/privacy/nip59-claim-wrapper.d.ts +60 -0
  48. package/dist/settlement/privacy/nip59-claim-wrapper.d.ts.map +1 -0
  49. package/dist/settlement/privacy/nip59-claim-wrapper.js +361 -0
  50. package/dist/settlement/privacy/nip59-claim-wrapper.js.map +1 -0
  51. package/dist/settlement/provider/chain-provider-registry.d.ts +20 -0
  52. package/dist/settlement/provider/chain-provider-registry.d.ts.map +1 -0
  53. package/dist/settlement/provider/chain-provider-registry.js +53 -0
  54. package/dist/settlement/provider/chain-provider-registry.js.map +1 -0
  55. package/dist/settlement/provider/evm-payment-channel-provider.d.ts +31 -0
  56. package/dist/settlement/provider/evm-payment-channel-provider.d.ts.map +1 -0
  57. package/dist/settlement/provider/evm-payment-channel-provider.js +207 -0
  58. package/dist/settlement/provider/evm-payment-channel-provider.js.map +1 -0
  59. package/dist/settlement/provider/index.d.ts +6 -0
  60. package/dist/settlement/provider/index.d.ts.map +1 -0
  61. package/dist/settlement/provider/index.js +16 -0
  62. package/dist/settlement/provider/index.js.map +1 -0
  63. package/dist/settlement/provider/mina-payment-channel-provider.d.ts +43 -0
  64. package/dist/settlement/provider/mina-payment-channel-provider.d.ts.map +1 -0
  65. package/dist/settlement/provider/mina-payment-channel-provider.js +330 -0
  66. package/dist/settlement/provider/mina-payment-channel-provider.js.map +1 -0
  67. package/dist/settlement/provider/payment-channel-provider.d.ts +79 -0
  68. package/dist/settlement/provider/payment-channel-provider.d.ts.map +1 -0
  69. package/dist/settlement/provider/payment-channel-provider.js +3 -0
  70. package/dist/settlement/provider/payment-channel-provider.js.map +1 -0
  71. package/dist/settlement/provider/solana-payment-channel-provider.d.ts +38 -0
  72. package/dist/settlement/provider/solana-payment-channel-provider.d.ts.map +1 -0
  73. package/dist/settlement/provider/solana-payment-channel-provider.js +262 -0
  74. package/dist/settlement/provider/solana-payment-channel-provider.js.map +1 -0
  75. package/dist/settlement/settlement-executor.d.ts +11 -7
  76. package/dist/settlement/settlement-executor.d.ts.map +1 -1
  77. package/dist/settlement/settlement-executor.js +74 -65
  78. package/dist/settlement/settlement-executor.js.map +1 -1
  79. package/dist/settlement/settlement-monitor.d.ts.map +1 -1
  80. package/dist/settlement/settlement-monitor.js.map +1 -1
  81. package/dist/settlement/solana-payment-channel-sdk.d.ts +75 -0
  82. package/dist/settlement/solana-payment-channel-sdk.d.ts.map +1 -0
  83. package/dist/settlement/solana-payment-channel-sdk.js +600 -0
  84. package/dist/settlement/solana-payment-channel-sdk.js.map +1 -0
  85. package/dist/settlement/types.d.ts +1 -1
  86. package/dist/settlement/types.d.ts.map +1 -1
  87. package/dist/settlement/types.js.map +1 -1
  88. package/package.json +12 -1
@@ -4,26 +4,33 @@ exports.ClaimReceiver = exports.ERRORS = void 0;
4
4
  const events_1 = require("events");
5
5
  const btp_types_1 = require("../btp/btp-types");
6
6
  const btp_claim_types_1 = require("../btp/btp-claim-types");
7
+ const nip59_claim_wrapper_1 = require("./privacy/nip59-claim-wrapper");
7
8
  exports.ERRORS = {
8
9
  MISSING_SELF_DESCRIBING_FIELDS: 'Missing self-describing fields for unknown channel (chainId, tokenNetworkAddress, tokenAddress required)',
9
10
  CHANNEL_NOT_FOUND: 'Channel does not exist on-chain',
10
11
  CHANNEL_NOT_OPENED: 'Channel not in opened state',
11
12
  SIGNER_NOT_PARTICIPANT: 'Signer is not a channel participant',
12
13
  ON_CHAIN_VERIFICATION_FAILED: 'On-chain channel verification failed',
14
+ INVALID_SIGNATURE: 'Invalid balance proof signature',
15
+ NO_PROVIDER_REGISTERED: 'No provider registered for blockchain:',
13
16
  };
14
17
  class ClaimReceiver extends events_1.EventEmitter {
15
18
  db;
16
- evmChannelSDK;
19
+ chainProviderRegistry;
17
20
  logger;
18
21
  channelManager;
19
22
  peerIdToAddressMap;
20
- constructor(db, evmChannelSDK, logger, channelManager, peerIdToAddressMap) {
23
+ _nip59Wrapper;
24
+ _nodePrivateKey;
25
+ constructor(db, chainProviderRegistry, logger, channelManager, peerIdToAddressMap, _nip59Wrapper, _nodePrivateKey) {
21
26
  super();
22
27
  this.db = db;
23
- this.evmChannelSDK = evmChannelSDK;
28
+ this.chainProviderRegistry = chainProviderRegistry;
24
29
  this.logger = logger;
25
30
  this.channelManager = channelManager;
26
31
  this.peerIdToAddressMap = peerIdToAddressMap;
32
+ this._nip59Wrapper = _nip59Wrapper;
33
+ this._nodePrivateKey = _nodePrivateKey;
27
34
  }
28
35
  registerWithBTPServer(btpServer) {
29
36
  btpServer.onMessage(async (peerId, message) => {
@@ -34,10 +41,32 @@ class ClaimReceiver extends events_1.EventEmitter {
34
41
  if (protocolData.protocolName === 'payment-channel-claim') {
35
42
  await this.handleClaimMessage(peerId, protocolData);
36
43
  }
44
+ else if (protocolData.protocolName === nip59_claim_wrapper_1.BTP_WRAPPED_CLAIM_PROTOCOL.NAME &&
45
+ this._nip59Wrapper &&
46
+ this._nodePrivateKey) {
47
+ await this.handleWrappedClaimMessage(peerId, protocolData);
48
+ }
37
49
  }
38
50
  });
39
51
  this.logger.info('ClaimReceiver registered with BTP server');
40
52
  }
53
+ async handleWrappedClaimMessage(peerId, protocolData) {
54
+ const childLogger = this.logger.child({ peerId, protocol: 'claim-receiver-nip59' });
55
+ try {
56
+ const wrappedClaim = (0, nip59_claim_wrapper_1.deserializeWrappedClaim)(protocolData.data);
57
+ const claimMessage = this._nip59Wrapper.unwrapClaim(wrappedClaim, this._nodePrivateKey);
58
+ childLogger.debug({ messageId: claimMessage.messageId }, 'NIP-59 claim unwrapped successfully');
59
+ const plaintextData = {
60
+ protocolName: 'payment-channel-claim',
61
+ contentType: 1,
62
+ data: Buffer.from(JSON.stringify(claimMessage), 'utf8'),
63
+ };
64
+ await this.handleClaimMessage(peerId, plaintextData);
65
+ }
66
+ catch (error) {
67
+ childLogger.warn({ error: error instanceof Error ? error.message : String(error) }, 'Failed to unwrap NIP-59 claim');
68
+ }
69
+ }
41
70
  async handleClaimMessage(peerId, protocolData) {
42
71
  const childLogger = this.logger.child({ peerId, protocol: 'claim-receiver' });
43
72
  try {
@@ -46,21 +75,54 @@ class ClaimReceiver extends events_1.EventEmitter {
46
75
  const messageId = claimMessage.messageId;
47
76
  const blockchain = claimMessage.blockchain;
48
77
  childLogger.info({ messageId, blockchain }, 'Received claim message');
49
- if (!(0, btp_claim_types_1.isEVMClaim)(claimMessage)) {
50
- throw new Error(`Unsupported blockchain type: ${blockchain}. Only EVM is supported.`);
78
+ const provider = this.resolveProvider(claimMessage);
79
+ if (!provider) {
80
+ const errorMsg = `${exports.ERRORS.NO_PROVIDER_REGISTERED} ${blockchain}`;
81
+ childLogger.warn({ messageId, blockchain }, errorMsg);
82
+ this._persistReceivedClaim(peerId, claimMessage, false);
83
+ return;
51
84
  }
52
- const verificationResult = await this.verifyEVMClaim(claimMessage, peerId);
85
+ const verificationResult = await this.verifyClaim(claimMessage, peerId, provider);
53
86
  if (verificationResult.valid) {
54
87
  this._persistReceivedClaim(peerId, claimMessage, true);
55
88
  childLogger.info({ messageId }, 'Claim verified and stored');
56
- if ((0, btp_claim_types_1.isEVMClaim)(claimMessage)) {
57
- const event = {
58
- peerId,
59
- channelId: claimMessage.channelId,
60
- cumulativeAmount: BigInt(claimMessage.transferredAmount),
61
- };
62
- this.emit('CLAIM_RECEIVED', event);
63
- childLogger.debug({ channelId: event.channelId, cumulativeAmount: event.cumulativeAmount.toString() }, 'CLAIM_RECEIVED event emitted');
89
+ try {
90
+ if ((0, btp_claim_types_1.isEVMClaim)(claimMessage)) {
91
+ const event = {
92
+ peerId,
93
+ channelId: claimMessage.channelId,
94
+ cumulativeAmount: BigInt(claimMessage.transferredAmount),
95
+ };
96
+ this.emit('CLAIM_RECEIVED', event);
97
+ childLogger.debug({ channelId: event.channelId, cumulativeAmount: event.cumulativeAmount.toString() }, 'CLAIM_RECEIVED event emitted');
98
+ }
99
+ else if ((0, btp_claim_types_1.isSolanaClaim)(claimMessage)) {
100
+ const event = {
101
+ peerId,
102
+ channelId: claimMessage.channelAccount,
103
+ cumulativeAmount: BigInt(claimMessage.transferredAmount),
104
+ };
105
+ this.emit('CLAIM_RECEIVED', event);
106
+ childLogger.debug({
107
+ channelAccount: event.channelId,
108
+ cumulativeAmount: event.cumulativeAmount.toString(),
109
+ }, 'CLAIM_RECEIVED event emitted (Solana)');
110
+ }
111
+ else if ((0, btp_claim_types_1.isMinaClaim)(claimMessage)) {
112
+ const event = {
113
+ peerId,
114
+ channelId: claimMessage.zkAppAddress,
115
+ cumulativeAmount: BigInt(0),
116
+ };
117
+ this.emit('CLAIM_RECEIVED', event);
118
+ childLogger.debug({ zkAppAddress: event.channelId }, 'CLAIM_RECEIVED event emitted (Mina)');
119
+ }
120
+ }
121
+ catch (eventError) {
122
+ childLogger.warn({
123
+ messageId: claimMessage.messageId,
124
+ error: eventError instanceof Error ? eventError.message : String(eventError),
125
+ }, 'Failed to emit CLAIM_RECEIVED event (invalid transferredAmount for BigInt conversion)');
64
126
  }
65
127
  }
66
128
  else {
@@ -72,114 +134,63 @@ class ClaimReceiver extends events_1.EventEmitter {
72
134
  childLogger.error({ error }, 'Failed to parse claim message');
73
135
  }
74
136
  }
75
- async verifyEVMClaim(claim, peerId) {
76
- try {
77
- const balanceProof = {
78
- channelId: claim.channelId,
79
- nonce: claim.nonce,
80
- transferredAmount: BigInt(claim.transferredAmount),
81
- lockedAmount: BigInt(claim.lockedAmount),
82
- locksRoot: claim.locksRoot,
83
- };
84
- this.logger.debug({ channelId: claim.channelId }, 'Checking channel existence in metadata');
85
- const knownChannel = this.channelManager?.getChannelById(claim.channelId);
86
- if (!knownChannel && this.channelManager) {
87
- this.logger.info({ channelId: claim.channelId }, 'Unknown channel detected, starting on-chain verification');
88
- if (claim.chainId === undefined || !claim.tokenNetworkAddress || !claim.tokenAddress) {
89
- this.logger.warn({ channelId: claim.channelId, signerAddress: claim.signerAddress }, exports.ERRORS.MISSING_SELF_DESCRIBING_FIELDS);
90
- return {
91
- valid: false,
92
- messageId: claim.messageId,
93
- error: exports.ERRORS.MISSING_SELF_DESCRIBING_FIELDS,
94
- };
95
- }
96
- let channelState;
97
- try {
98
- channelState = await this.evmChannelSDK.getChannelStateByNetwork(claim.channelId, claim.tokenNetworkAddress);
99
- }
100
- catch (error) {
101
- this.logger.warn({ channelId: claim.channelId, signerAddress: claim.signerAddress, error }, exports.ERRORS.ON_CHAIN_VERIFICATION_FAILED);
102
- return {
103
- valid: false,
104
- messageId: claim.messageId,
105
- error: exports.ERRORS.ON_CHAIN_VERIFICATION_FAILED,
106
- };
107
- }
108
- if (!channelState.exists) {
109
- this.logger.warn({ channelId: claim.channelId, signerAddress: claim.signerAddress }, exports.ERRORS.CHANNEL_NOT_FOUND);
110
- return {
111
- valid: false,
112
- messageId: claim.messageId,
113
- error: exports.ERRORS.CHANNEL_NOT_FOUND,
114
- };
115
- }
116
- if (channelState.state !== 1) {
117
- this.logger.warn({ channelId: claim.channelId, signerAddress: claim.signerAddress }, exports.ERRORS.CHANNEL_NOT_OPENED);
118
- return {
119
- valid: false,
120
- messageId: claim.messageId,
121
- error: exports.ERRORS.CHANNEL_NOT_OPENED,
122
- };
123
- }
124
- const signerLower = claim.signerAddress.toLowerCase();
125
- if (signerLower !== channelState.participant1.toLowerCase() &&
126
- signerLower !== channelState.participant2.toLowerCase()) {
127
- this.logger.warn({ channelId: claim.channelId, signerAddress: claim.signerAddress }, exports.ERRORS.SIGNER_NOT_PARTICIPANT);
128
- return {
129
- valid: false,
130
- messageId: claim.messageId,
131
- error: exports.ERRORS.SIGNER_NOT_PARTICIPANT,
132
- };
133
- }
134
- this.logger.info({
135
- channelId: claim.channelId,
136
- participant1: channelState.participant1,
137
- participant2: channelState.participant2,
138
- state: channelState.state,
139
- }, 'On-chain channel verified successfully');
140
- const sigValid = await this.evmChannelSDK.verifyBalanceProofWithDomain(balanceProof, claim.signature, claim.signerAddress, claim.chainId, claim.tokenNetworkAddress);
141
- if (!sigValid) {
142
- return {
143
- valid: false,
144
- messageId: claim.messageId,
145
- error: 'Invalid EIP-712 signature',
146
- };
147
- }
148
- this.channelManager.registerExternalChannel({
149
- channelId: claim.channelId,
150
- peerId,
151
- tokenAddress: claim.tokenAddress,
152
- tokenNetworkAddress: claim.tokenNetworkAddress,
153
- chainId: claim.chainId,
154
- status: 'open',
155
- });
156
- this.logger.info({ channelId: claim.channelId, peerId }, 'External channel registered');
157
- if (this.peerIdToAddressMap && !this.peerIdToAddressMap.has(peerId)) {
158
- this.peerIdToAddressMap.set(peerId, claim.signerAddress);
159
- this.logger.info({ peerId, signerAddress: claim.signerAddress }, 'Peer EVM address registered from self-describing claim');
137
+ resolveProvider(claim) {
138
+ if ((0, btp_claim_types_1.isEVMClaim)(claim)) {
139
+ if (this.channelManager) {
140
+ const knownChannel = this.channelManager.getChannelById(claim.channelId);
141
+ if (knownChannel && knownChannel.chain) {
142
+ return this.chainProviderRegistry.getProvider(claim.blockchain, knownChannel.chain);
160
143
  }
161
144
  }
162
- else {
163
- const isValid = await this.evmChannelSDK.verifyBalanceProof(balanceProof, claim.signature, claim.signerAddress);
164
- if (!isValid) {
165
- return {
166
- valid: false,
167
- messageId: claim.messageId,
168
- error: 'Invalid EIP-712 signature',
169
- };
145
+ if (claim.chainId !== undefined) {
146
+ const chainKey = `${claim.blockchain}:${claim.chainId}`;
147
+ return this.chainProviderRegistry.getProvider(claim.blockchain, chainKey);
148
+ }
149
+ }
150
+ if ((0, btp_claim_types_1.isSolanaClaim)(claim)) {
151
+ if (this.channelManager) {
152
+ const knownChannel = this.channelManager.getChannelById(claim.channelAccount);
153
+ if (knownChannel && knownChannel.chain) {
154
+ return this.chainProviderRegistry.getProvider(claim.blockchain, knownChannel.chain);
170
155
  }
171
156
  }
172
- const latestClaim = await this.getLatestVerifiedClaim(peerId, 'evm', claim.channelId);
173
- if (latestClaim && (0, btp_claim_types_1.isEVMClaim)(latestClaim)) {
174
- if (claim.nonce <= latestClaim.nonce) {
175
- return {
176
- valid: false,
177
- messageId: claim.messageId,
178
- error: 'Nonce not monotonically increasing',
179
- };
157
+ if (claim.cluster !== undefined) {
158
+ const chainKey = `${claim.blockchain}:${claim.cluster}`;
159
+ return this.chainProviderRegistry.getProvider(claim.blockchain, chainKey);
160
+ }
161
+ }
162
+ if ((0, btp_claim_types_1.isMinaClaim)(claim)) {
163
+ if (this.channelManager) {
164
+ const knownChannel = this.channelManager.getChannelById(claim.zkAppAddress);
165
+ if (knownChannel && knownChannel.chain) {
166
+ return this.chainProviderRegistry.getProvider(claim.blockchain, knownChannel.chain);
180
167
  }
181
168
  }
182
- return { valid: true, messageId: claim.messageId };
169
+ if (claim.network !== undefined) {
170
+ const chainKey = `${claim.blockchain}:${claim.network}`;
171
+ return this.chainProviderRegistry.getProvider(claim.blockchain, chainKey);
172
+ }
173
+ }
174
+ const allProviders = this.chainProviderRegistry.getAllProviders();
175
+ return allProviders.find((p) => p.chainType === claim.blockchain);
176
+ }
177
+ async verifyClaim(claim, peerId, provider) {
178
+ try {
179
+ if ((0, btp_claim_types_1.isEVMClaim)(claim)) {
180
+ return await this.verifyEVMClaim(claim, peerId, provider);
181
+ }
182
+ if ((0, btp_claim_types_1.isSolanaClaim)(claim)) {
183
+ return await this.verifySolanaClaim(claim, peerId, provider);
184
+ }
185
+ if ((0, btp_claim_types_1.isMinaClaim)(claim)) {
186
+ return await this.verifyMinaClaim(claim, peerId, provider);
187
+ }
188
+ const _exhaustiveCheck = claim;
189
+ return {
190
+ valid: false,
191
+ messageId: _exhaustiveCheck.messageId,
192
+ error: `Verification not supported for blockchain: ${_exhaustiveCheck.blockchain}`,
193
+ };
183
194
  }
184
195
  catch (error) {
185
196
  return {
@@ -189,9 +200,315 @@ class ClaimReceiver extends events_1.EventEmitter {
189
200
  };
190
201
  }
191
202
  }
203
+ async verifyEVMClaim(claim, peerId, provider) {
204
+ this.logger.debug({ channelId: claim.channelId }, 'Checking channel existence in metadata');
205
+ const knownChannel = this.channelManager?.getChannelById(claim.channelId);
206
+ if (!knownChannel && this.channelManager) {
207
+ this.logger.info({ channelId: claim.channelId }, 'Unknown channel detected, starting on-chain verification');
208
+ if (claim.chainId === undefined || !claim.tokenNetworkAddress || !claim.tokenAddress) {
209
+ this.logger.warn({ channelId: claim.channelId, signerAddress: claim.signerAddress }, exports.ERRORS.MISSING_SELF_DESCRIBING_FIELDS);
210
+ return {
211
+ valid: false,
212
+ messageId: claim.messageId,
213
+ error: exports.ERRORS.MISSING_SELF_DESCRIBING_FIELDS,
214
+ };
215
+ }
216
+ let channelState;
217
+ try {
218
+ channelState = await provider.getChannelState(claim.channelId);
219
+ }
220
+ catch (error) {
221
+ this.logger.warn({ channelId: claim.channelId, signerAddress: claim.signerAddress, error }, exports.ERRORS.ON_CHAIN_VERIFICATION_FAILED);
222
+ return {
223
+ valid: false,
224
+ messageId: claim.messageId,
225
+ error: exports.ERRORS.ON_CHAIN_VERIFICATION_FAILED,
226
+ };
227
+ }
228
+ if (channelState.status !== 'opened') {
229
+ const errorMsg = channelState.status === 'settled' || channelState.status === 'closed'
230
+ ? exports.ERRORS.CHANNEL_NOT_OPENED
231
+ : exports.ERRORS.CHANNEL_NOT_FOUND;
232
+ this.logger.warn({ channelId: claim.channelId, signerAddress: claim.signerAddress }, errorMsg);
233
+ return {
234
+ valid: false,
235
+ messageId: claim.messageId,
236
+ error: errorMsg,
237
+ };
238
+ }
239
+ const signerLower = claim.signerAddress.toLowerCase();
240
+ if (!channelState.participants.some((p) => p.toLowerCase() === signerLower)) {
241
+ this.logger.warn({ channelId: claim.channelId, signerAddress: claim.signerAddress }, exports.ERRORS.SIGNER_NOT_PARTICIPANT);
242
+ return {
243
+ valid: false,
244
+ messageId: claim.messageId,
245
+ error: exports.ERRORS.SIGNER_NOT_PARTICIPANT,
246
+ };
247
+ }
248
+ this.logger.info({
249
+ channelId: claim.channelId,
250
+ participants: channelState.participants,
251
+ status: channelState.status,
252
+ }, 'On-chain channel verified successfully');
253
+ const verifyParams = this.buildVerifyParams(claim);
254
+ const sigValid = await provider.verifyBalanceProof(verifyParams);
255
+ if (!sigValid) {
256
+ return {
257
+ valid: false,
258
+ messageId: claim.messageId,
259
+ error: exports.ERRORS.INVALID_SIGNATURE,
260
+ };
261
+ }
262
+ this.channelManager.registerExternalChannel({
263
+ channelId: claim.channelId,
264
+ peerId,
265
+ tokenAddress: claim.tokenAddress,
266
+ tokenNetworkAddress: claim.tokenNetworkAddress,
267
+ chainId: claim.chainId,
268
+ status: 'open',
269
+ });
270
+ this.logger.info({ channelId: claim.channelId, peerId }, 'External channel registered');
271
+ if (this.peerIdToAddressMap && !this.peerIdToAddressMap.has(peerId)) {
272
+ this.peerIdToAddressMap.set(peerId, claim.signerAddress);
273
+ this.logger.info({ peerId, signerAddress: claim.signerAddress }, 'Peer EVM address registered from self-describing claim');
274
+ }
275
+ }
276
+ else {
277
+ const verifyParams = this.buildVerifyParams(claim);
278
+ const isValid = await provider.verifyBalanceProof(verifyParams);
279
+ if (!isValid) {
280
+ return {
281
+ valid: false,
282
+ messageId: claim.messageId,
283
+ error: exports.ERRORS.INVALID_SIGNATURE,
284
+ };
285
+ }
286
+ }
287
+ const latestClaim = await this.getLatestVerifiedClaim(peerId, claim.blockchain, claim.channelId);
288
+ if (latestClaim && (0, btp_claim_types_1.isEVMClaim)(latestClaim)) {
289
+ if (claim.nonce <= latestClaim.nonce) {
290
+ return {
291
+ valid: false,
292
+ messageId: claim.messageId,
293
+ error: 'Nonce not monotonically increasing',
294
+ };
295
+ }
296
+ }
297
+ return { valid: true, messageId: claim.messageId };
298
+ }
299
+ buildVerifyParams(claim) {
300
+ return {
301
+ channelId: claim.channelId,
302
+ nonce: claim.nonce,
303
+ transferredAmount: claim.transferredAmount,
304
+ lockedAmount: claim.lockedAmount,
305
+ locksRoot: claim.locksRoot,
306
+ signature: claim.signature,
307
+ signerAddress: claim.signerAddress,
308
+ };
309
+ }
310
+ async verifySolanaClaim(claim, peerId, provider) {
311
+ this.logger.debug({ channelAccount: claim.channelAccount }, 'Verifying Solana claim');
312
+ const knownChannel = this.channelManager?.getChannelById(claim.channelAccount);
313
+ if (!knownChannel && this.channelManager) {
314
+ this.logger.info({ channelAccount: claim.channelAccount }, 'Unknown Solana channel detected, starting on-chain verification');
315
+ let channelState;
316
+ try {
317
+ channelState = await provider.getChannelState(claim.channelAccount);
318
+ }
319
+ catch (error) {
320
+ this.logger.warn({
321
+ channelAccount: claim.channelAccount,
322
+ signerPublicKey: claim.signerPublicKey,
323
+ error,
324
+ }, exports.ERRORS.ON_CHAIN_VERIFICATION_FAILED);
325
+ return {
326
+ valid: false,
327
+ messageId: claim.messageId,
328
+ error: exports.ERRORS.ON_CHAIN_VERIFICATION_FAILED,
329
+ };
330
+ }
331
+ if (channelState.status !== 'opened' && channelState.status !== 'closed') {
332
+ const errorMsg = channelState.status === 'settled' ? exports.ERRORS.CHANNEL_NOT_OPENED : exports.ERRORS.CHANNEL_NOT_FOUND;
333
+ this.logger.warn({ channelAccount: claim.channelAccount, status: channelState.status }, errorMsg);
334
+ return {
335
+ valid: false,
336
+ messageId: claim.messageId,
337
+ error: errorMsg,
338
+ };
339
+ }
340
+ if (!channelState.participants.includes(claim.signerPublicKey)) {
341
+ this.logger.warn({
342
+ channelAccount: claim.channelAccount,
343
+ signerPublicKey: claim.signerPublicKey,
344
+ }, exports.ERRORS.SIGNER_NOT_PARTICIPANT);
345
+ return {
346
+ valid: false,
347
+ messageId: claim.messageId,
348
+ error: exports.ERRORS.SIGNER_NOT_PARTICIPANT,
349
+ };
350
+ }
351
+ this.logger.info({
352
+ channelAccount: claim.channelAccount,
353
+ participants: channelState.participants,
354
+ status: channelState.status,
355
+ }, 'On-chain Solana channel verified successfully');
356
+ const verifyParams = this.buildSolanaVerifyParams(claim);
357
+ const sigValid = await provider.verifyBalanceProof(verifyParams);
358
+ if (!sigValid) {
359
+ return {
360
+ valid: false,
361
+ messageId: claim.messageId,
362
+ error: exports.ERRORS.INVALID_SIGNATURE,
363
+ };
364
+ }
365
+ this.channelManager.registerExternalChannel({
366
+ channelId: claim.channelAccount,
367
+ peerId,
368
+ tokenAddress: claim.programId,
369
+ status: 'open',
370
+ chain: `solana:${claim.cluster ?? 'devnet'}`,
371
+ });
372
+ this.logger.info({ channelAccount: claim.channelAccount, peerId }, 'External Solana channel registered');
373
+ if (this.peerIdToAddressMap && !this.peerIdToAddressMap.has(peerId)) {
374
+ this.peerIdToAddressMap.set(peerId, claim.signerPublicKey);
375
+ this.logger.info({ peerId, signerPublicKey: claim.signerPublicKey }, 'Peer Solana address registered from self-describing claim');
376
+ }
377
+ }
378
+ else {
379
+ const verifyParams = this.buildSolanaVerifyParams(claim);
380
+ const isValid = await provider.verifyBalanceProof(verifyParams);
381
+ if (!isValid) {
382
+ return {
383
+ valid: false,
384
+ messageId: claim.messageId,
385
+ error: exports.ERRORS.INVALID_SIGNATURE,
386
+ };
387
+ }
388
+ }
389
+ const latestClaim = await this.getLatestVerifiedClaim(peerId, claim.blockchain, claim.channelAccount);
390
+ if (latestClaim && (0, btp_claim_types_1.isSolanaClaim)(latestClaim)) {
391
+ if (claim.nonce <= latestClaim.nonce) {
392
+ return {
393
+ valid: false,
394
+ messageId: claim.messageId,
395
+ error: 'Nonce not monotonically increasing',
396
+ };
397
+ }
398
+ }
399
+ return { valid: true, messageId: claim.messageId };
400
+ }
401
+ buildSolanaVerifyParams(claim) {
402
+ return {
403
+ channelId: claim.channelAccount,
404
+ nonce: claim.nonce,
405
+ transferredAmount: claim.transferredAmount,
406
+ lockedAmount: '0',
407
+ locksRoot: '0x0000000000000000000000000000000000000000000000000000000000000000',
408
+ signature: claim.signature,
409
+ signerAddress: claim.signerPublicKey,
410
+ };
411
+ }
412
+ async verifyMinaClaim(claim, peerId, provider) {
413
+ this.logger.debug({
414
+ event: 'mina_claim_received',
415
+ messageId: claim.messageId,
416
+ zkAppAddress: claim.zkAppAddress,
417
+ }, 'Verifying Mina claim');
418
+ const knownChannel = this.channelManager?.getChannelById(claim.zkAppAddress);
419
+ if (!knownChannel && this.channelManager) {
420
+ this.logger.info({ zkAppAddress: claim.zkAppAddress }, 'Unknown Mina channel detected, starting on-chain verification');
421
+ let channelState;
422
+ try {
423
+ channelState = await provider.getChannelState(claim.zkAppAddress);
424
+ }
425
+ catch (error) {
426
+ this.logger.warn({ event: 'mina_claim_verification_failed', messageId: claim.messageId, error }, exports.ERRORS.ON_CHAIN_VERIFICATION_FAILED);
427
+ return {
428
+ valid: false,
429
+ messageId: claim.messageId,
430
+ error: exports.ERRORS.ON_CHAIN_VERIFICATION_FAILED,
431
+ };
432
+ }
433
+ if (channelState.status !== 'opened' && channelState.status !== 'closed') {
434
+ const errorMsg = channelState.status === 'settled' ? exports.ERRORS.CHANNEL_NOT_OPENED : exports.ERRORS.CHANNEL_NOT_FOUND;
435
+ this.logger.warn({ zkAppAddress: claim.zkAppAddress, status: channelState.status }, errorMsg);
436
+ return {
437
+ valid: false,
438
+ messageId: claim.messageId,
439
+ error: errorMsg,
440
+ };
441
+ }
442
+ this.logger.info({
443
+ zkAppAddress: claim.zkAppAddress,
444
+ participants: channelState.participants,
445
+ status: channelState.status,
446
+ }, 'On-chain Mina channel verified successfully');
447
+ const verifyParams = this.buildMinaVerifyParams(claim);
448
+ const proofValid = await provider.verifyBalanceProof(verifyParams);
449
+ if (!proofValid) {
450
+ this.logger.warn({ event: 'mina_claim_verification_failed', messageId: claim.messageId }, exports.ERRORS.INVALID_SIGNATURE);
451
+ return {
452
+ valid: false,
453
+ messageId: claim.messageId,
454
+ error: exports.ERRORS.INVALID_SIGNATURE,
455
+ };
456
+ }
457
+ this.channelManager.registerExternalChannel({
458
+ channelId: claim.zkAppAddress,
459
+ peerId,
460
+ tokenAddress: claim.tokenId,
461
+ status: 'open',
462
+ chain: `mina:${claim.network ?? 'devnet'}`,
463
+ });
464
+ this.logger.info({ zkAppAddress: claim.zkAppAddress, peerId }, 'External Mina channel registered');
465
+ }
466
+ else {
467
+ const verifyParams = this.buildMinaVerifyParams(claim);
468
+ const isValid = await provider.verifyBalanceProof(verifyParams);
469
+ if (!isValid) {
470
+ return {
471
+ valid: false,
472
+ messageId: claim.messageId,
473
+ error: exports.ERRORS.INVALID_SIGNATURE,
474
+ };
475
+ }
476
+ }
477
+ const latestClaim = await this.getLatestVerifiedClaim(peerId, claim.blockchain, claim.zkAppAddress);
478
+ if (latestClaim && (0, btp_claim_types_1.isMinaClaim)(latestClaim)) {
479
+ if (claim.nonce <= latestClaim.nonce) {
480
+ return {
481
+ valid: false,
482
+ messageId: claim.messageId,
483
+ error: 'Nonce not monotonically increasing',
484
+ };
485
+ }
486
+ }
487
+ return { valid: true, messageId: claim.messageId };
488
+ }
489
+ buildMinaVerifyParams(claim) {
490
+ return {
491
+ channelId: claim.zkAppAddress,
492
+ nonce: claim.nonce,
493
+ transferredAmount: claim.balanceCommitment,
494
+ lockedAmount: '0',
495
+ locksRoot: '0x' + '0'.repeat(64),
496
+ signature: claim.proof,
497
+ signerAddress: claim.zkAppAddress,
498
+ };
499
+ }
192
500
  _persistReceivedClaim(peerId, claim, verified) {
193
501
  try {
194
- const channelId = claim.channelId;
502
+ let channelId = '';
503
+ if ((0, btp_claim_types_1.isEVMClaim)(claim)) {
504
+ channelId = claim.channelId;
505
+ }
506
+ else if ((0, btp_claim_types_1.isSolanaClaim)(claim)) {
507
+ channelId = claim.channelAccount;
508
+ }
509
+ else if ((0, btp_claim_types_1.isMinaClaim)(claim)) {
510
+ channelId = claim.zkAppAddress;
511
+ }
195
512
  const stmt = this.db.prepare(`
196
513
  INSERT INTO received_claims (
197
514
  message_id,