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
JavaScript
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);
}
}