UNPKG

@node-dlc/messaging

Version:
315 lines (263 loc) 10.2 kB
import { BufferReader, BufferWriter } from '@node-dlc/bufio'; import { MessageType } from '../MessageType'; import { getTlv } from '../serialize/getTlv'; import { ContractDescriptor, ContractDescriptorV0JSON, ContractDescriptorV1, ContractDescriptorV1JSON, } from './ContractDescriptor'; import { DlcMessage, IDlcMessage } from './DlcMessage'; import { DigitDecompositionEventDescriptorV0 } from './EventDescriptor'; import { OracleEventV0 } from './OracleEventV0'; import { OracleInfoV0, OracleInfoV0JSON } from './OracleInfoV0'; export abstract class ContractInfo extends DlcMessage { public static deserialize(buf: Buffer): ContractInfoV0 | ContractInfoV1 { const reader = new BufferReader(buf); const type = Number(reader.readBigSize()); switch (type) { case MessageType.ContractInfoV0: return ContractInfoV0.deserialize(buf); case MessageType.ContractInfoV1: return ContractInfoV1.deserialize(buf); default: throw new Error( `Contract info TLV type must be ContractInfoV0 or ContractInfoV1`, ); } } public abstract type: number; public abstract length: bigint; public abstract totalCollateral: bigint; public abstract validate(): void; public abstract toJSON(): IContractInfoV0JSON | IContractInfoV1JSON; public abstract serialize(): Buffer; } /** * ContractInfo V0 contains information about a contract's outcomes, * their corresponding payouts, and the oracles to be used. */ export class ContractInfoV0 implements IDlcMessage { public static type = MessageType.ContractInfoV0; /** * Deserializes an contract_info_v0 message * @param buf */ public static deserialize(buf: Buffer): ContractInfoV0 { const instance = new ContractInfoV0(); const reader = new BufferReader(buf); reader.readBigSize(); // read type instance.length = reader.readBigSize(); instance.totalCollateral = reader.readUInt64BE(); instance.contractDescriptor = ContractDescriptor.deserialize( getTlv(reader), ); instance.oracleInfo = OracleInfoV0.deserialize(getTlv(reader)); return instance; } /** * The type for contract_info_v0 message. contract_info_v0 = 55342 */ public type = ContractInfoV0.type; public length: bigint; public totalCollateral: bigint; public contractDescriptor: ContractDescriptor; public oracleInfo: OracleInfoV0; /** * Validates correctness of all fields in the message * https://github.com/discreetlogcontracts/dlcspecs/blob/master/Messaging.md#the-contract_info-type * @throws Will throw an error if validation fails */ public validate(): void { this.oracleInfo.validate(); switch (this.contractDescriptor.type) { case MessageType.ContractDescriptorV1: // eslint-disable-next-line no-case-declarations const contractDescriptor = this .contractDescriptor as ContractDescriptorV1; contractDescriptor.validate(); // roundingmod should not be greater than totalCollateral for (const interval of contractDescriptor.roundingIntervals.intervals) { if (interval.roundingMod > this.totalCollateral) { throw new Error( `Rounding modulus ${interval.roundingMod} is greater than total collateral ${this.totalCollateral}`, ); } } switch (this.oracleInfo.announcement.oracleEvent.eventDescriptor.type) { case MessageType.DigitDecompositionEventDescriptorV0: // eslint-disable-next-line no-case-declarations const eventDescriptor = this.oracleInfo.announcement.oracleEvent .eventDescriptor as DigitDecompositionEventDescriptorV0; if (eventDescriptor.nbDigits !== contractDescriptor.numDigits) throw new Error( 'DigitDecompositionEventDescriptorV0 and ContractDescriptorV1 must have the same numDigits', ); // Sanity check. Shouldn't be hit since there are other validations which should catch this in OracleEventV0. // eslint-disable-next-line no-case-declarations const oracleEvent = this.oracleInfo.announcement .oracleEvent as OracleEventV0; if ( oracleEvent.oracleNonces.length !== contractDescriptor.numDigits ) { throw new Error( 'oracleEvent.oracleNonces.length and contractDescriptor.numDigits must be the same', ); } break; default: throw new Error( 'Only ContractDescriptorV1 can be used with DigitDecompositionEventDescriptor', ); } } } /** * Converts contract_info_v0 to JSON */ public toJSON(): IContractInfoV0JSON { return { type: this.type, totalCollateral: Number(this.totalCollateral), contractDescriptor: this.contractDescriptor.toJSON(), oracleInfo: this.oracleInfo.toJSON(), }; } /** * Serializes the contract_info_v0 message into a Buffer */ public serialize(): Buffer { const writer = new BufferWriter(); writer.writeBigSize(this.type); const dataWriter = new BufferWriter(); dataWriter.writeUInt64BE(this.totalCollateral); dataWriter.writeBytes(this.contractDescriptor.serialize()); dataWriter.writeBytes(this.oracleInfo.serialize()); writer.writeBigSize(dataWriter.size); writer.writeBytes(dataWriter.toBuffer()); return writer.toBuffer(); } } /** * ContractInfo V1 contains information about a contract's outcomes, * their corresponding payouts, and the oracles to be used. */ export class ContractInfoV1 implements IDlcMessage { public static type = MessageType.ContractInfoV1; /** * Deserializes an contract_info_v0 message * @param buf */ public static deserialize(buf: Buffer): ContractInfoV1 { const instance = new ContractInfoV1(); const reader = new BufferReader(buf); reader.readBigSize(); // read type instance.length = reader.readBigSize(); instance.totalCollateral = reader.readUInt64BE(); reader.readBigSize(); // read num_disjoint_events while (!reader.eof) { const contractDescriptor = ContractDescriptor.deserialize(getTlv(reader)); const oracleInfo = OracleInfoV0.deserialize(getTlv(reader)); instance.contractOraclePairs.push({ contractDescriptor, oracleInfo }); } return instance; } /** * The type for contract_info_v1 message. contract_info_v0 = 55342 */ public type = ContractInfoV1.type; public length: bigint; public totalCollateral: bigint; public contractOraclePairs: IContractOraclePair[] = []; /** * Validates correctness of all fields in the message * https://github.com/discreetlogcontracts/dlcspecs/blob/master/Messaging.md#the-contract_info-type * @throws Will throw an error if validation fails */ public validate(): void { this.contractOraclePairs.forEach((oraclePair) => { oraclePair.oracleInfo.validate(); switch (oraclePair.contractDescriptor.type) { case MessageType.ContractDescriptorV1: // eslint-disable-next-line no-case-declarations const contractDescriptor = oraclePair.contractDescriptor as ContractDescriptorV1; contractDescriptor.validate(); switch ( oraclePair.oracleInfo.announcement.oracleEvent.eventDescriptor.type ) { case MessageType.DigitDecompositionEventDescriptorV0: // eslint-disable-next-line no-case-declarations const eventDescriptor = oraclePair.oracleInfo.announcement .oracleEvent .eventDescriptor as DigitDecompositionEventDescriptorV0; if (eventDescriptor.nbDigits !== contractDescriptor.numDigits) throw new Error( 'DigitDecompositionEventDescriptorV0 and ContractDescriptorV1 must have the same numDigits', ); // eslint-disable-next-line no-case-declarations const oracleEvent = oraclePair.oracleInfo.announcement .oracleEvent as OracleEventV0; if ( oracleEvent.oracleNonces.length !== contractDescriptor.numDigits ) { throw new Error( 'oracleEvent.oracleNonces.length and contractDescriptor.numDigits must be the same', ); } } } }); } /** * Converts contract_info_v1 to JSON */ public toJSON(): IContractInfoV1JSON { return { type: this.type, totalCollateral: Number(this.totalCollateral), contractOraclePairs: this.contractOraclePairs.map((oraclePairs) => { return { contractDescriptor: oraclePairs.contractDescriptor.toJSON(), oracleInfo: oraclePairs.oracleInfo.toJSON(), }; }), }; } /** * Serializes the contract_info_v1 message into a Buffer */ public serialize(): Buffer { const writer = new BufferWriter(); writer.writeBigSize(this.type); const dataWriter = new BufferWriter(); dataWriter.writeUInt64BE(this.totalCollateral); dataWriter.writeBigSize(this.contractOraclePairs.length); for (const contractOraclePair of this.contractOraclePairs) { const { contractDescriptor, oracleInfo } = contractOraclePair; dataWriter.writeBytes(contractDescriptor.serialize()); dataWriter.writeBytes(oracleInfo.serialize()); } writer.writeBigSize(dataWriter.size); writer.writeBytes(dataWriter.toBuffer()); return writer.toBuffer(); } } interface IContractOraclePair { contractDescriptor: ContractDescriptor; oracleInfo: OracleInfoV0; } interface IContractOraclePairJSON { contractDescriptor: ContractDescriptorV0JSON | ContractDescriptorV1JSON; oracleInfo: OracleInfoV0JSON; } export interface IContractInfoV0JSON { type: number; totalCollateral: number; contractDescriptor: ContractDescriptorV0JSON | ContractDescriptorV1JSON; oracleInfo: OracleInfoV0JSON; } export interface IContractInfoV1JSON { type: number; totalCollateral: number; contractOraclePairs: IContractOraclePairJSON[]; }