Skip to content

Commit

Permalink
DLC close messagetype (#122)
Browse files Browse the repository at this point in the history
* Dlc close messagetype

* update test info

* stub more fields in tests

* add validations and update tests

* update comment

* remove random comment
  • Loading branch information
dillionverma authored Aug 11, 2021
1 parent 8e3f2e0 commit 4b9d1d0
Show file tree
Hide file tree
Showing 3 changed files with 264 additions and 0 deletions.
130 changes: 130 additions & 0 deletions packages/messaging/__tests__/messages/DlcCloseV0.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
import { expect } from 'chai';

import { DlcClose, DlcCloseV0 } from '../../lib/messages/DlcClose';
import { FundingInputV0 } from '../../lib/messages/FundingInput';
import { MessageType } from '../../lib/MessageType';

describe('DlcClose', () => {
let instance: DlcCloseV0;

const type = Buffer.from('cbca', 'hex');

const contractId = Buffer.from(
'c1c79e1e9e2fa2840b2514902ea244f39eb3001a4037a52ea43c797d4f841269',
'hex',
);

const closeSignature = Buffer.from(
'7c8ad6de287b62a1ed1d74ed9116a5158abc7f97376d201caa88e0f9daad68fcda4c271cc003512e768f403a57e5242bd1f6aa1750d7f3597598094a43b1c7bb',
'hex',
);

const offerPayoutSatoshis = Buffer.from('0000000005f5e100', 'hex');
const acceptPayoutSatoshis = Buffer.from('0000000005f5e100', 'hex');

const fundingInputsLen = Buffer.from('0001', 'hex');
const fundingInputV0 = Buffer.from(
'fda714' + // type funding_input_v0
'3f' + // length
'000000000000dae8' + // input_serial_id
'0029' + // prevtx_len
'02000000000100c2eb0b000000001600149ea3bf2d6eb9c2ffa35e36f41e117403ed7fafe900000000' + // prevtx
'00000000' + // prevtx_vout
'ffffffff' + // sequence
'006b' + // max_witness_len
'0000', // redeem_script_len
'hex',
);

const dlcCloseHex = Buffer.concat([
type,
contractId,
closeSignature,
offerPayoutSatoshis,
acceptPayoutSatoshis,
fundingInputsLen,
fundingInputV0,
]);

beforeEach(() => {
instance = new DlcCloseV0();
instance.contractId = contractId;
instance.closeSignature = closeSignature;
instance.offerPayoutSatoshis = BigInt(100000000);
instance.acceptPayoutSatoshis = BigInt(100000000);
instance.fundingInputs = [FundingInputV0.deserialize(fundingInputV0)];
});

describe('deserialize', () => {
it('should throw if incorrect type', () => {
instance.type = 0x123;
expect(function () {
DlcClose.deserialize(instance.serialize());
}).to.throw(Error);
});

it('has correct type', () => {
expect(DlcClose.deserialize(instance.serialize()).type).to.equal(
instance.type,
);
});
});

describe('DlcCloseV0', () => {
describe('serialize', () => {
it('serializes', () => {
expect(instance.serialize().toString('hex')).to.equal(
dlcCloseHex.toString('hex'),
);
});
});

describe('deserialize', () => {
it('deserializes', () => {
const instance = DlcCloseV0.deserialize(dlcCloseHex);
expect(instance.contractId).to.deep.equal(contractId);
expect(instance.closeSignature).to.deep.equal(closeSignature);
expect(Number(instance.offerPayoutSatoshis)).to.equal(100000000);
expect(Number(instance.acceptPayoutSatoshis)).to.equal(100000000);
expect(instance.fundingInputs[0].serialize().toString('hex')).to.equal(
fundingInputV0.toString('hex'),
);
});

it('has correct type', () => {
expect(DlcCloseV0.deserialize(dlcCloseHex).type).to.equal(
MessageType.DlcCloseV0,
);
});
});

describe('toJSON', () => {
it('convert to JSON', async () => {
const json = instance.toJSON();
expect(json.contractId).to.equal(contractId.toString('hex'));
expect(json.closeSignature).to.equal(closeSignature.toString('hex'));
expect(json.fundingInputs[0].prevTx).to.equal(
instance.fundingInputs[0].prevTx.serialize().toString('hex'),
);
});
});

describe('validate', () => {
it('should throw if inputSerialIds arent unique', () => {
instance.fundingInputs = [
FundingInputV0.deserialize(fundingInputV0),
FundingInputV0.deserialize(fundingInputV0),
];
expect(function () {
instance.validate();
}).to.throw(Error);
});
it('should ensure funding inputs are segwit', () => {
instance.fundingInputs = [FundingInputV0.deserialize(fundingInputV0)];
expect(function () {
instance.validate();
}).to.throw(Error);
});
});
});
});
1 change: 1 addition & 0 deletions packages/messaging/lib/MessageType.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ export enum MessageType {
DlcAcceptV0 = 42780,
DlcSignV0 = 42782,

DlcCloseV0 = 52170, // TODO: Temporary type
DlcCancelV0 = 52172,

/**
Expand Down
133 changes: 133 additions & 0 deletions packages/messaging/lib/messages/DlcClose.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
import { BufferReader, BufferWriter } from '@node-lightning/bufio';

import { MessageType } from '../MessageType';
import { getTlv } from '../serialize/getTlv';
import { IDlcMessage } from './DlcMessage';
import { FundingInputV0, IFundingInputV0JSON } from './FundingInput';

export abstract class DlcClose {
public static deserialize(buf: Buffer): DlcCloseV0 {
const reader = new BufferReader(buf);

const type = Number(reader.readUInt16BE());

switch (type) {
case MessageType.DlcCloseV0:
return DlcCloseV0.deserialize(buf);
default:
throw new Error(`DLC Close message type must be DlcCloseV0`); // This is a temporary measure while protocol is being developed
}
}

public abstract type: number;

public abstract toJSON(): IDlcCloseV0JSON;

public abstract serialize(): Buffer;
}

/**
* DlcClose message contains information about a node and indicates its
* desire to close an existing contract.
*/
export class DlcCloseV0 extends DlcClose implements IDlcMessage {
public static type = MessageType.DlcCloseV0;

/**
* Deserializes an close_dlc_v0 message
* @param buf
*/
public static deserialize(buf: Buffer): DlcCloseV0 {
const instance = new DlcCloseV0();
const reader = new BufferReader(buf);

reader.readUInt16BE(); // read type
instance.contractId = reader.readBytes(32);
instance.closeSignature = reader.readBytes(64);
instance.offerPayoutSatoshis = reader.readUInt64BE();
instance.acceptPayoutSatoshis = reader.readUInt64BE();
const fundingInputsLen = reader.readUInt16BE();
for (let i = 0; i < fundingInputsLen; i++) {
instance.fundingInputs.push(FundingInputV0.deserialize(getTlv(reader)));
}

return instance;
}

/**
* The type for close_dlc_v0 message. close_dlc_v0 = 52170 // TODO
*/
public type = DlcCloseV0.type;

public contractId: Buffer;

public closeSignature: Buffer;

public offerPayoutSatoshis: bigint;

public acceptPayoutSatoshis: bigint;

public fundingInputs: FundingInputV0[] = [];

/**
* Serializes the close_dlc_v0 message into a Buffer
*/
public serialize(): Buffer {
const writer = new BufferWriter();
writer.writeUInt16BE(this.type);
writer.writeBytes(this.contractId);
writer.writeBytes(this.closeSignature);
writer.writeUInt64BE(this.offerPayoutSatoshis);
writer.writeUInt64BE(this.acceptPayoutSatoshis);
writer.writeUInt16BE(this.fundingInputs.length);

for (const fundingInput of this.fundingInputs) {
writer.writeBytes(fundingInput.serialize());
}

return writer.toBuffer();
}

/**
* Validates correctness of all fields
* @throws Will throw an error if validation fails
*/
public validate(): void {
// Type is set automatically in class

// Ensure input serial ids are unique
const inputSerialIds = this.fundingInputs.map(
(input: FundingInputV0) => input.inputSerialId,
);

if (new Set(inputSerialIds).size !== inputSerialIds.length) {
throw new Error('inputSerialIds must be unique');
}

// Ensure funding inputs are segwit
this.fundingInputs.forEach((input: FundingInputV0) => input.validate());
}

/**
* Converts dlc_close_v0 to JSON
*/
public toJSON(): IDlcCloseV0JSON {
return {
type: this.type,
contractId: this.contractId.toString('hex'),
closeSignature: this.closeSignature.toString('hex'),
offerPayoutSatoshis: Number(this.offerPayoutSatoshis),
acceptPayoutSatoshis: Number(this.acceptPayoutSatoshis),
fundingInputs: this.fundingInputs.map((input) => input.toJSON()),
};
}
}

export interface IDlcCloseV0JSON {
type: number;
contractId: string;
closeSignature: string;
offerPayoutSatoshis: number;
acceptPayoutSatoshis: number;
fundingInputs: IFundingInputV0JSON[];
}

0 comments on commit 4b9d1d0

Please sign in to comment.