@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.
- package/dist/btp/btp-claim-types.d.ts +24 -2
- package/dist/btp/btp-claim-types.d.ts.map +1 -1
- package/dist/btp/btp-claim-types.js +115 -9
- package/dist/btp/btp-claim-types.js.map +1 -1
- package/dist/btp/inbound-claim-validator.d.ts +5 -1
- package/dist/btp/inbound-claim-validator.d.ts.map +1 -1
- package/dist/btp/inbound-claim-validator.js +52 -10
- package/dist/btp/inbound-claim-validator.js.map +1 -1
- package/dist/config/config-loader.d.ts.map +1 -1
- package/dist/config/config-loader.js +3 -0
- package/dist/config/config-loader.js.map +1 -1
- package/dist/config/types.d.ts +15 -0
- package/dist/config/types.d.ts.map +1 -1
- package/dist/config/types.js +45 -0
- package/dist/config/types.js.map +1 -1
- package/dist/core/connector-node.d.ts.map +1 -1
- package/dist/core/connector-node.js +57 -12
- package/dist/core/connector-node.js.map +1 -1
- package/dist/core/packet-handler.d.ts +10 -1
- package/dist/core/packet-handler.d.ts.map +1 -1
- package/dist/core/packet-handler.js +67 -2
- package/dist/core/packet-handler.js.map +1 -1
- package/dist/settlement/channel-manager.d.ts +3 -2
- package/dist/settlement/channel-manager.d.ts.map +1 -1
- package/dist/settlement/channel-manager.js +18 -6
- package/dist/settlement/channel-manager.js.map +1 -1
- package/dist/settlement/claim-receiver.d.ts +16 -3
- package/dist/settlement/claim-receiver.d.ts.map +1 -1
- package/dist/settlement/claim-receiver.js +434 -117
- package/dist/settlement/claim-receiver.js.map +1 -1
- package/dist/settlement/claim-sender.d.ts +4 -2
- package/dist/settlement/claim-sender.d.ts.map +1 -1
- package/dist/settlement/claim-sender.js +38 -0
- package/dist/settlement/claim-sender.js.map +1 -1
- package/dist/settlement/mina-payment-channel-sdk.d.ts +73 -0
- package/dist/settlement/mina-payment-channel-sdk.d.ts.map +1 -0
- package/dist/settlement/mina-payment-channel-sdk.js +538 -0
- package/dist/settlement/mina-payment-channel-sdk.js.map +1 -0
- package/dist/settlement/per-packet-claim-service.d.ts +12 -6
- package/dist/settlement/per-packet-claim-service.d.ts.map +1 -1
- package/dist/settlement/per-packet-claim-service.js +205 -40
- package/dist/settlement/per-packet-claim-service.js.map +1 -1
- package/dist/settlement/privacy/index.d.ts +3 -0
- package/dist/settlement/privacy/index.d.ts.map +1 -0
- package/dist/settlement/privacy/index.js +11 -0
- package/dist/settlement/privacy/index.js.map +1 -0
- package/dist/settlement/privacy/nip59-claim-wrapper.d.ts +60 -0
- package/dist/settlement/privacy/nip59-claim-wrapper.d.ts.map +1 -0
- package/dist/settlement/privacy/nip59-claim-wrapper.js +361 -0
- package/dist/settlement/privacy/nip59-claim-wrapper.js.map +1 -0
- package/dist/settlement/provider/chain-provider-registry.d.ts +20 -0
- package/dist/settlement/provider/chain-provider-registry.d.ts.map +1 -0
- package/dist/settlement/provider/chain-provider-registry.js +53 -0
- package/dist/settlement/provider/chain-provider-registry.js.map +1 -0
- package/dist/settlement/provider/evm-payment-channel-provider.d.ts +31 -0
- package/dist/settlement/provider/evm-payment-channel-provider.d.ts.map +1 -0
- package/dist/settlement/provider/evm-payment-channel-provider.js +207 -0
- package/dist/settlement/provider/evm-payment-channel-provider.js.map +1 -0
- package/dist/settlement/provider/index.d.ts +6 -0
- package/dist/settlement/provider/index.d.ts.map +1 -0
- package/dist/settlement/provider/index.js +16 -0
- package/dist/settlement/provider/index.js.map +1 -0
- package/dist/settlement/provider/mina-payment-channel-provider.d.ts +43 -0
- package/dist/settlement/provider/mina-payment-channel-provider.d.ts.map +1 -0
- package/dist/settlement/provider/mina-payment-channel-provider.js +330 -0
- package/dist/settlement/provider/mina-payment-channel-provider.js.map +1 -0
- package/dist/settlement/provider/payment-channel-provider.d.ts +79 -0
- package/dist/settlement/provider/payment-channel-provider.d.ts.map +1 -0
- package/dist/settlement/provider/payment-channel-provider.js +3 -0
- package/dist/settlement/provider/payment-channel-provider.js.map +1 -0
- package/dist/settlement/provider/solana-payment-channel-provider.d.ts +38 -0
- package/dist/settlement/provider/solana-payment-channel-provider.d.ts.map +1 -0
- package/dist/settlement/provider/solana-payment-channel-provider.js +262 -0
- package/dist/settlement/provider/solana-payment-channel-provider.js.map +1 -0
- package/dist/settlement/settlement-executor.d.ts +11 -7
- package/dist/settlement/settlement-executor.d.ts.map +1 -1
- package/dist/settlement/settlement-executor.js +74 -65
- package/dist/settlement/settlement-executor.js.map +1 -1
- package/dist/settlement/settlement-monitor.d.ts.map +1 -1
- package/dist/settlement/settlement-monitor.js.map +1 -1
- package/dist/settlement/solana-payment-channel-sdk.d.ts +75 -0
- package/dist/settlement/solana-payment-channel-sdk.d.ts.map +1 -0
- package/dist/settlement/solana-payment-channel-sdk.js +600 -0
- package/dist/settlement/solana-payment-channel-sdk.js.map +1 -0
- package/dist/settlement/types.d.ts +1 -1
- package/dist/settlement/types.d.ts.map +1 -1
- package/dist/settlement/types.js.map +1 -1
- 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
|
-
|
|
19
|
+
chainProviderRegistry;
|
|
17
20
|
logger;
|
|
18
21
|
channelManager;
|
|
19
22
|
peerIdToAddressMap;
|
|
20
|
-
|
|
23
|
+
_nip59Wrapper;
|
|
24
|
+
_nodePrivateKey;
|
|
25
|
+
constructor(db, chainProviderRegistry, logger, channelManager, peerIdToAddressMap, _nip59Wrapper, _nodePrivateKey) {
|
|
21
26
|
super();
|
|
22
27
|
this.db = db;
|
|
23
|
-
this.
|
|
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
|
-
|
|
50
|
-
|
|
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.
|
|
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
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
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
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
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
|
-
|
|
163
|
-
const
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
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
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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,
|