UNPKG

lotus-sdk

Version:

Central repository for several classes of tools for integrating with, and building for, the Lotusia ecosystem

344 lines (343 loc) 15.8 kB
import { SwapSigMessageType, } from './types.js'; import { deserializePublicKey } from '../musig2/serialization.js'; import { DeserializationError, ValidationError } from '../musig2/errors.js'; import { validatePoolJoinPayload, validateParticipantRegisteredPayload, validateRegistrationAckPayload, validateSetupTxBroadcastPayload, validateSetupConfirmedPayload, validateSetupCompletePayload, validateDestinationRevealPayload, validateRevealCompletePayload, validateSettlementTxBroadcastPayload, validateSettlementConfirmedPayload, validateSettlementCompletePayload, validatePoolAbortPayload, validateParticipantDroppedPayload, } from './validation.js'; export class SwapSigP2PProtocolHandler { protocolName = 'swapsig'; protocolId = '/lotus/swapsig/1.0.0'; coordinator; setCoordinator(coordinator) { this.coordinator = coordinator; } async handleMessage(message, from) { if (!this.coordinator) { console.error('[SwapSigP2P] Coordinator not set'); return; } if (message.protocol !== this.protocolName) { return; } try { switch (message.type) { case SwapSigMessageType.POOL_ANNOUNCE: await this._handlePoolAnnounce(message.payload, from); break; case SwapSigMessageType.POOL_JOIN: await this._handlePoolJoin(message.payload, from); break; case SwapSigMessageType.PARTICIPANT_REGISTERED: await this._handleParticipantRegistered(message.payload, from); break; case SwapSigMessageType.REGISTRATION_ACK: await this._handleRegistrationAck(message.payload, from); break; case SwapSigMessageType.SETUP_TX_BROADCAST: await this._handleSetupTxBroadcast(message.payload, from); break; case SwapSigMessageType.SETUP_CONFIRMED: await this._handleSetupConfirmed(message.payload, from); break; case SwapSigMessageType.SETUP_COMPLETE: await this._handleSetupComplete(message.payload, from); break; case SwapSigMessageType.DESTINATION_REVEAL: await this._handleDestinationReveal(message.payload, from); break; case SwapSigMessageType.REVEAL_COMPLETE: await this._handleRevealComplete(message.payload, from); break; case SwapSigMessageType.SETTLEMENT_TX_BROADCAST: await this._handleSettlementTxBroadcast(message.payload, from); break; case SwapSigMessageType.SETTLEMENT_CONFIRMED: await this._handleSettlementConfirmed(message.payload, from); break; case SwapSigMessageType.SETTLEMENT_COMPLETE: await this._handleSettlementComplete(message.payload, from); break; case SwapSigMessageType.POOL_ABORT: await this._handlePoolAbort(message.payload, from); break; case SwapSigMessageType.PARTICIPANT_DROPPED: await this._handleParticipantDropped(message.payload, from); break; default: console.warn(`[SwapSigP2P] Unknown message type: ${message.type}`); } } catch (error) { console.error(`[SwapSigP2P] Error handling message ${message.type}:`, error); if (message.payload && typeof message.payload === 'object' && 'poolId' in message.payload) { try { await this._sendPoolError(message.payload.poolId, from.peerId, error instanceof Error ? error.message : String(error)); } catch (sendError) { console.error('[SwapSigP2P] Failed to send error notification:', sendError); } } } } async onPeerConnected(peerId) { if (this.coordinator) { this.coordinator._onSwapSigPeerConnected(peerId); } } async onPeerDisconnected(peerId) { if (this.coordinator) { this.coordinator._onSwapSigPeerDisconnected(peerId); } } async _handlePoolAnnounce(payload, from) { if (!this.coordinator) return; const announcement = payload.announcement; if (!this.coordinator._validatePoolAnnouncement(announcement)) { console.warn(`[SwapSigP2P] Invalid pool announcement from ${from.peerId}`); return; } console.log(`[SwapSigP2P] Discovered pool ${announcement.poolId.substring(0, 8)}... from ${from.peerId}`); } async _handlePoolJoin(payload, from) { if (!this.coordinator) return; try { validatePoolJoinPayload(payload); await this.coordinator._handlePoolJoin(payload.poolId, payload.participantIndex, from.peerId); } catch (error) { if (error instanceof DeserializationError || error instanceof ValidationError) { console.warn(`[SwapSigP2P] ⚠️ Malformed pool join from ${from.peerId}: ${error.message}`); return; } console.error(`[SwapSigP2P] ❌ Unexpected error handling pool join from ${from.peerId}:`, error); return; } } async _handleParticipantRegistered(payload, from) { if (!this.coordinator) return; try { validateParticipantRegisteredPayload(payload); const publicKey = deserializePublicKey(payload.publicKey); const ownershipProof = Buffer.from(payload.ownershipProof, 'hex'); const finalOutputCommitment = Buffer.from(payload.finalOutputCommitment, 'hex'); await this.coordinator._handleParticipantRegistered(payload.poolId, payload.participantIndex, payload.peerId, publicKey, payload.inputTxId, payload.inputIndex, ownershipProof, finalOutputCommitment, from.peerId); } catch (error) { if (error instanceof DeserializationError || error instanceof ValidationError) { console.warn(`[SwapSigP2P] ⚠️ Malformed participant registration from ${from.peerId}: ${error.message}`); return; } console.error(`[SwapSigP2P] ❌ Unexpected error handling participant registration from ${from.peerId}:`, error); return; } } async _handleRegistrationAck(payload, from) { if (!this.coordinator) return; try { validateRegistrationAckPayload(payload); await this.coordinator._handleRegistrationAck(payload.poolId, payload.participantIndex, payload.acknowledgedBy, from.peerId); } catch (error) { if (error instanceof DeserializationError || error instanceof ValidationError) { console.warn(`[SwapSigP2P] ⚠️ Malformed registration ack from ${from.peerId}: ${error.message}`); return; } console.error(`[SwapSigP2P] ❌ Unexpected error handling registration ack from ${from.peerId}:`, error); return; } } async _handleSetupTxBroadcast(payload, from) { if (!this.coordinator) return; try { validateSetupTxBroadcastPayload(payload); await this.coordinator._handleSetupTxBroadcast(payload.poolId, payload.participantIndex, payload.txId, from.peerId); } catch (error) { if (error instanceof DeserializationError || error instanceof ValidationError) { console.warn(`[SwapSigP2P] ⚠️ Malformed setup tx broadcast from ${from.peerId}: ${error.message}`); return; } console.error(`[SwapSigP2P] ❌ Unexpected error handling setup tx broadcast from ${from.peerId}:`, error); return; } } async _handleSetupConfirmed(payload, from) { if (!this.coordinator) return; try { validateSetupConfirmedPayload(payload); await this.coordinator._handleSetupConfirmed(payload.poolId, payload.participantIndex, payload.txId, payload.confirmations, from.peerId); } catch (error) { if (error instanceof DeserializationError || error instanceof ValidationError) { console.warn(`[SwapSigP2P] ⚠️ Malformed setup confirmed from ${from.peerId}: ${error.message}`); return; } console.error(`[SwapSigP2P] ❌ Unexpected error handling setup confirmed from ${from.peerId}:`, error); return; } } async _handleSetupComplete(payload, from) { if (!this.coordinator) return; try { validateSetupCompletePayload(payload); await this.coordinator._handleSetupComplete(payload.poolId, from.peerId); } catch (error) { if (error instanceof DeserializationError || error instanceof ValidationError) { console.warn(`[SwapSigP2P] ⚠️ Malformed setup complete from ${from.peerId}: ${error.message}`); return; } console.error(`[SwapSigP2P] ❌ Unexpected error handling setup complete from ${from.peerId}:`, error); return; } } async _handleDestinationReveal(payload, from) { if (!this.coordinator) return; try { validateDestinationRevealPayload(payload); const { Address: AddressClass } = await import('../../bitcore/address.js'); const finalAddress = AddressClass.fromString(payload.finalAddress); const revealProof = Buffer.from(payload.revealProof, 'hex'); await this.coordinator._handleDestinationReveal(payload.poolId, payload.participantIndex, finalAddress, revealProof, from.peerId); } catch (error) { if (error instanceof DeserializationError || error instanceof ValidationError) { console.warn(`[SwapSigP2P] ⚠️ Malformed destination reveal from ${from.peerId}: ${error.message}`); return; } if (error instanceof Error && error.message.includes('address')) { console.warn(`[SwapSigP2P] ⚠️ Invalid address in destination reveal from ${from.peerId}: ${error.message}`); return; } console.error(`[SwapSigP2P] ❌ Unexpected error handling destination reveal from ${from.peerId}:`, error); return; } } async _handleRevealComplete(payload, from) { if (!this.coordinator) return; try { validateRevealCompletePayload(payload); await this.coordinator._handleRevealComplete(payload.poolId, from.peerId); } catch (error) { if (error instanceof DeserializationError || error instanceof ValidationError) { console.warn(`[SwapSigP2P] ⚠️ Malformed reveal complete from ${from.peerId}: ${error.message}`); return; } console.error(`[SwapSigP2P] ❌ Unexpected error handling reveal complete from ${from.peerId}:`, error); return; } } async _handleSettlementTxBroadcast(payload, from) { if (!this.coordinator) return; try { validateSettlementTxBroadcastPayload(payload); await this.coordinator._handleSettlementTxBroadcast(payload.poolId, payload.outputIndex, payload.txId, from.peerId); } catch (error) { if (error instanceof DeserializationError || error instanceof ValidationError) { console.warn(`[SwapSigP2P] ⚠️ Malformed settlement tx broadcast from ${from.peerId}: ${error.message}`); return; } console.error(`[SwapSigP2P] ❌ Unexpected error handling settlement tx broadcast from ${from.peerId}:`, error); return; } } async _handleSettlementConfirmed(payload, from) { if (!this.coordinator) return; try { validateSettlementConfirmedPayload(payload); await this.coordinator._handleSettlementConfirmed(payload.poolId, payload.outputIndex, payload.txId, payload.confirmations, from.peerId); } catch (error) { if (error instanceof DeserializationError || error instanceof ValidationError) { console.warn(`[SwapSigP2P] ⚠️ Malformed settlement confirmed from ${from.peerId}: ${error.message}`); return; } console.error(`[SwapSigP2P] ❌ Unexpected error handling settlement confirmed from ${from.peerId}:`, error); return; } } async _handleSettlementComplete(payload, from) { if (!this.coordinator) return; try { validateSettlementCompletePayload(payload); await this.coordinator._handleSettlementComplete(payload.poolId, from.peerId); } catch (error) { if (error instanceof DeserializationError || error instanceof ValidationError) { console.warn(`[SwapSigP2P] ⚠️ Malformed settlement complete from ${from.peerId}: ${error.message}`); return; } console.error(`[SwapSigP2P] ❌ Unexpected error handling settlement complete from ${from.peerId}:`, error); return; } } async _handlePoolAbort(payload, from) { if (!this.coordinator) return; try { validatePoolAbortPayload(payload); await this.coordinator._handlePoolAbort(payload.poolId, payload.reason, from.peerId); } catch (error) { if (error instanceof DeserializationError || error instanceof ValidationError) { console.warn(`[SwapSigP2P] ⚠️ Malformed pool abort from ${from.peerId}: ${error.message}`); return; } console.error(`[SwapSigP2P] ❌ Unexpected error handling pool abort from ${from.peerId}:`, error); return; } } async _handleParticipantDropped(payload, from) { if (!this.coordinator) return; try { validateParticipantDroppedPayload(payload); await this.coordinator._handleParticipantDropped(payload.poolId, payload.peerId, payload.reason, from.peerId); } catch (error) { if (error instanceof DeserializationError || error instanceof ValidationError) { console.warn(`[SwapSigP2P] ⚠️ Malformed participant dropped from ${from.peerId}: ${error.message}`); return; } console.error(`[SwapSigP2P] ❌ Unexpected error handling participant dropped from ${from.peerId}:`, error); return; } } async _sendPoolError(poolId, peerId, error) { if (!this.coordinator) return; const payload = { poolId, reason: error, timestamp: Date.now(), }; await this.coordinator._sendMessageToPeer(peerId, SwapSigMessageType.POOL_ABORT, payload); } }