lotus-sdk
Version:
Central repository for several classes of tools for integrating with, and building for, the Lotusia ecosystem
161 lines (160 loc) • 7.02 kB
JavaScript
import { MuSig2MessageType, } from './types.js';
import { EventEmitter } from 'events';
import { validateMessageStructure, validateSessionJoinPayload, validateSessionJoinAckPayload, validateNonceSharePayload, validatePartialSigSharePayload, validateSessionAbortPayload, validateSessionCompletePayload, } from './validation.js';
import { ValidationError, DeserializationError, SerializationError, ErrorCode, } from './errors.js';
export class MuSig2ProtocolHandler extends EventEmitter {
protocolName = 'musig2';
protocolId = '/lotus/musig2/1.0.0';
securityValidator;
setSecurityValidator(validator) {
this.securityValidator = validator;
}
async handleMessage(message, from) {
try {
if (message.protocol !== this.protocolName) {
console.warn(`[MuSig2Protocol] Ignoring message with wrong protocol: ${message.protocol}`);
return;
}
if (this.securityValidator) {
const isSecure = await this.securityValidator.validateMessage(message, from);
if (!isSecure) {
console.warn(`[MuSig2Protocol] Security validation failed for ${message.type} from ${from.peerId}`);
this.emit('security:rejected', {
message,
from,
reason: 'security_validation_failed',
});
return;
}
}
validateMessageStructure(message);
const validatedPayload = this._validateAndRouteMessage(message);
this._emitValidatedMessage(message.type, validatedPayload, from);
}
catch (error) {
this._handleMessageError(error, message, from);
}
}
_validateAndRouteMessage(message) {
switch (message.type) {
case MuSig2MessageType.SESSION_JOIN:
validateSessionJoinPayload(message.payload);
return message.payload;
case MuSig2MessageType.SESSION_JOIN_ACK:
validateSessionJoinAckPayload(message.payload);
return message.payload;
case MuSig2MessageType.NONCE_SHARE:
validateNonceSharePayload(message.payload);
return message.payload;
case MuSig2MessageType.PARTIAL_SIG_SHARE:
validatePartialSigSharePayload(message.payload);
return message.payload;
case MuSig2MessageType.SESSION_ABORT:
validateSessionAbortPayload(message.payload);
return message.payload;
case MuSig2MessageType.SESSION_COMPLETE:
validateSessionCompletePayload(message.payload);
return message.payload;
default:
throw new ValidationError(ErrorCode.INVALID_PAYLOAD, `Unknown message type: ${message.type}`);
}
}
_emitValidatedMessage(type, payload, from) {
switch (type) {
case MuSig2MessageType.SESSION_JOIN:
this.emit('session:join', payload, from);
break;
case MuSig2MessageType.SESSION_JOIN_ACK:
this.emit('session:join-ack', payload, from);
break;
case MuSig2MessageType.NONCE_SHARE:
this.emit('nonce:share', payload, from);
break;
case MuSig2MessageType.PARTIAL_SIG_SHARE:
this.emit('partial-sig:share', payload, from);
break;
case MuSig2MessageType.SESSION_ABORT:
this.emit('session:abort', payload, from);
break;
case MuSig2MessageType.SESSION_COMPLETE:
this.emit('session:complete', payload, from);
break;
}
}
_handleMessageError(error, message, from) {
if (error instanceof ValidationError) {
console.warn(`[MuSig2Protocol] Validation failed for ${message.type} from ${from.peerId}: ${error.message}`);
this.emit('validation:error', { error, message, from });
return;
}
if (error instanceof DeserializationError) {
console.warn(`[MuSig2Protocol] Deserialization failed for ${message.type} from ${from.peerId}: ${error.message}`);
this.emit('deserialization:error', { error, message, from });
return;
}
if (error instanceof SerializationError) {
console.warn(`[MuSig2Protocol] Serialization error for ${message.type} from ${from.peerId}: ${error.message}`);
this.emit('serialization:error', { error, message, from });
return;
}
console.error(`[MuSig2Protocol] Unexpected error processing ${message.type} from ${from.peerId}:`, error);
this.emit('unexpected:error', { error, message, from });
}
async onPeerConnected(peerId) {
console.log(`[MuSig2Protocol] Peer connected: ${peerId}`);
this.emit('peer:connected', peerId);
}
async onPeerDisconnected(peerId) {
console.log(`[MuSig2Protocol] Peer disconnected: ${peerId}`);
this.emit('peer:disconnected', peerId);
}
async onPeerDiscovered(peerInfo) {
console.log(`[MuSig2Protocol] Peer discovered: ${peerInfo.peerId}`);
this.emit('peer:discovered', peerInfo);
}
validateMessagePayload(type, payload) {
try {
switch (type) {
case MuSig2MessageType.SESSION_JOIN:
validateSessionJoinPayload(payload);
break;
case MuSig2MessageType.SESSION_JOIN_ACK:
validateSessionJoinAckPayload(payload);
break;
case MuSig2MessageType.NONCE_SHARE:
validateNonceSharePayload(payload);
break;
case MuSig2MessageType.PARTIAL_SIG_SHARE:
validatePartialSigSharePayload(payload);
break;
case MuSig2MessageType.SESSION_ABORT:
validateSessionAbortPayload(payload);
break;
case MuSig2MessageType.SESSION_COMPLETE:
validateSessionCompletePayload(payload);
break;
default:
return false;
}
return true;
}
catch (error) {
return false;
}
}
getValidationInfo() {
return {
supportedMessageTypes: [
MuSig2MessageType.SESSION_JOIN,
MuSig2MessageType.SESSION_JOIN_ACK,
MuSig2MessageType.NONCE_SHARE,
MuSig2MessageType.PARTIAL_SIG_SHARE,
MuSig2MessageType.SESSION_ABORT,
MuSig2MessageType.SESSION_COMPLETE,
],
validationEnabled: true,
errorHandlingEnabled: true,
securityChecksEnabled: true,
};
}
}