UNPKG

@node-dlc/messaging

Version:
257 lines (214 loc) 8.12 kB
import { BufferReader, BufferWriter } from '@node-dlc/bufio'; import { RoundingIntervals } from './RoundingIntervals'; import { IRoundingIntervalsJSON } from './RoundingIntervals'; /** * Negotiation fields for DLC contract negotiation. * Follows the Rust enum pattern with Single and Disjoint variants. */ export abstract class NegotiationFields { public static deserialize(buf: Buffer): NegotiationFields { const reader = new BufferReader(buf); const discriminator = Number(reader.readBigSize()); switch (discriminator) { case 0: return SingleNegotiationFields.deserialize(buf); case 1: return DisjointNegotiationFields.deserialize(buf); default: throw new Error( `Invalid NegotiationFields discriminator: ${discriminator}. Must be 0 (Single) or 1 (Disjoint)`, ); } } /** * Creates a NegotiationFields from JSON data * @param json JSON object representing negotiation fields */ // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any public static fromJSON(json: any): NegotiationFields { if (!json || typeof json !== 'object') { throw new Error('Invalid JSON input for NegotiationFields'); } const variant = json.variant; switch (variant) { case 'Single': return SingleNegotiationFields.fromJSON(json); case 'Disjoint': return DisjointNegotiationFields.fromJSON(json); default: throw new Error( `Unknown negotiation fields variant: ${variant}. Must be 'Single' or 'Disjoint'`, ); } } public abstract variant: 'Single' | 'Disjoint'; public abstract discriminator: number; public abstract serialize(): Buffer; public abstract toJSON(): INegotiationFieldsJSON; } /** * Negotiation fields for contract based on a single event. */ export class SingleNegotiationFields extends NegotiationFields { /** * Creates a SingleNegotiationFields from JSON data * @param json JSON object representing single negotiation fields */ // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any public static fromJSON(json: any): SingleNegotiationFields { const instance = new SingleNegotiationFields(); if (json.variant !== 'Single') { throw new Error( `Invalid variant for SingleNegotiationFields: expected 'Single', got ${json.variant}`, ); } if (!json.roundingIntervals) { throw new Error( 'SingleNegotiationFields requires roundingIntervals field', ); } instance.roundingIntervals = RoundingIntervals.fromJSON( json.roundingIntervals, ); return instance; } /** * Deserializes single negotiation fields * @param buf */ public static deserialize(buf: Buffer): SingleNegotiationFields { const instance = new SingleNegotiationFields(); const reader = new BufferReader(buf); reader.readBigSize(); // read discriminator (0) // Read remaining bytes as raw RoundingIntervals data const remainingBytes = reader.readBytes(); instance.roundingIntervals = RoundingIntervals.deserialize(remainingBytes); return instance; } public variant = 'Single' as const; public discriminator = 0; public roundingIntervals: RoundingIntervals; /** * Converts single negotiation fields to JSON */ public toJSON(): ISingleNegotiationFieldsJSON { return { variant: this.variant, roundingIntervals: this.roundingIntervals.toJSON(), }; } /** * Serializes the single negotiation fields into a Buffer */ public serialize(): Buffer { const writer = new BufferWriter(); writer.writeBigSize(this.discriminator); writer.writeBytes(this.roundingIntervals.serialize()); return writer.toBuffer(); } } /** * Negotiation fields for contract based on multiple events. */ export class DisjointNegotiationFields extends NegotiationFields { /** * Creates a DisjointNegotiationFields from JSON data * @param json JSON object representing disjoint negotiation fields */ // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any public static fromJSON(json: any): DisjointNegotiationFields { const instance = new DisjointNegotiationFields(); if (json.variant !== 'Disjoint') { throw new Error( `Invalid variant for DisjointNegotiationFields: expected 'Disjoint', got ${json.variant}`, ); } if (!json.negotiationFields || !Array.isArray(json.negotiationFields)) { throw new Error( 'DisjointNegotiationFields requires negotiationFields array', ); } // eslint-disable-next-line @typescript-eslint/no-explicit-any instance.negotiationFields = json.negotiationFields.map((fieldJson: any) => NegotiationFields.fromJSON(fieldJson), ); return instance; } /** * Deserializes disjoint negotiation fields * @param buf */ public static deserialize(buf: Buffer): DisjointNegotiationFields { const instance = new DisjointNegotiationFields(); const reader = new BufferReader(buf); reader.readBigSize(); // read discriminator (1) const numFields = Number(reader.readBigSize()); for (let i = 0; i < numFields; i++) { // For simplicity, let's read the nested field by looking ahead // to determine its length based on its discriminator const startPos = reader.position; const discriminator = Number(reader.readBigSize()); if (discriminator === 0) { // Single field: discriminator + RoundingIntervals data // RoundingIntervals has its own length, so we need to parse it const roundingIntervals = RoundingIntervals.deserialize( reader.readBytes(), ); // Reset and read the complete field reader.position = startPos; const fieldLength = 1 + roundingIntervals.serialize().length; // discriminator + data length const fieldData = reader.readBytes(fieldLength); instance.negotiationFields.push( NegotiationFields.deserialize(fieldData), ); } else if (discriminator === 1) { throw new Error('Nested disjoint fields not yet supported'); } else { throw new Error(`Unknown discriminator: ${discriminator}`); } } return instance; } public variant = 'Disjoint' as const; public discriminator = 1; public negotiationFields: NegotiationFields[] = []; /** * Converts disjoint negotiation fields to JSON */ public toJSON(): IDisjointNegotiationFieldsJSON { return { variant: this.variant, negotiationFields: this.negotiationFields.map((field) => field.toJSON()), }; } /** * Serializes the disjoint negotiation fields into a Buffer */ public serialize(): Buffer { const writer = new BufferWriter(); writer.writeBigSize(this.discriminator); writer.writeBigSize(this.negotiationFields.length); for (const negotiationField of this.negotiationFields) { writer.writeBytes(negotiationField.serialize()); } return writer.toBuffer(); } } export type INegotiationFieldsJSON = | ISingleNegotiationFieldsJSON | IDisjointNegotiationFieldsJSON; export interface ISingleNegotiationFieldsJSON { variant: 'Single'; roundingIntervals: IRoundingIntervalsJSON; } export interface IDisjointNegotiationFieldsJSON { variant: 'Disjoint'; negotiationFields: INegotiationFieldsJSON[]; } // Legacy exports for backward compatibility - map to new structure export const NegotiationFieldsV0 = SingleNegotiationFields; // V0 was empty, now maps to Single export const NegotiationFieldsV1 = SingleNegotiationFields; // V1 had rounding intervals export const NegotiationFieldsV2 = DisjointNegotiationFields; // V2 had list of fields export type INegotiationFieldsV0JSON = ISingleNegotiationFieldsJSON; export type INegotiationFieldsV1JSON = ISingleNegotiationFieldsJSON; export type INegotiationFieldsV2JSON = IDisjointNegotiationFieldsJSON;