Skip to content

Commit

Permalink
feat: add batch funding group tlv (#207)
Browse files Browse the repository at this point in the history
  • Loading branch information
matthewjablack authored Mar 14, 2024
1 parent bb6b4ab commit 73d5226
Show file tree
Hide file tree
Showing 8 changed files with 271 additions and 2 deletions.
52 changes: 52 additions & 0 deletions packages/messaging/__tests__/messages/BatchFundingGroup.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import { Value } from '@node-dlc/bitcoin';
import { expect } from 'chai';

import { BatchFundingGroup } from '../../lib';

describe('BatchFundingGroup TLV', () => {
it('should serialize and deserialize without contract ids', () => {
const batchFundingGroup = new BatchFundingGroup();

const eventIds = ['event1', 'event2', 'event3'];

batchFundingGroup.eventIds = eventIds;
batchFundingGroup.allocatedCollateral = Value.fromBitcoin(0.5);

const deserialized = BatchFundingGroup.deserialize(
batchFundingGroup.serialize(),
);

expect(deserialized.eventIds).to.deep.equal(eventIds);
expect(batchFundingGroup.serialize()).to.deep.equal(
deserialized.serialize(),
);
});

it('should serialize and deserialize with contract ids', () => {
const batchFundingGroup = new BatchFundingGroup();

const eventIds = ['event1', 'event2', 'event3'];

batchFundingGroup.eventIds = eventIds;
batchFundingGroup.allocatedCollateral = Value.fromBitcoin(0.5);
batchFundingGroup.tempContractIds = [
Buffer.from('tempContractId1'),
Buffer.from('tempContractId2'),
Buffer.from('tempContractId3'),
];
batchFundingGroup.contractIds = [
Buffer.from('contractId1'),
Buffer.from('contractId2'),
Buffer.from('contractId3'),
];

const deserialized = BatchFundingGroup.deserialize(
batchFundingGroup.serialize(),
);

expect(deserialized.eventIds).to.deep.equal(eventIds);
expect(batchFundingGroup.serialize()).to.deep.equal(
deserialized.serialize(),
);
});
});
2 changes: 2 additions & 0 deletions packages/messaging/lib/MessageType.ts
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,8 @@ export enum MessageType {

AddressCache = 65132,

BatchFundingGroup = 65430,

IrcMessageV0 = 59314,

NodeAnnouncement = 51394,
Expand Down
1 change: 1 addition & 0 deletions packages/messaging/lib/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ export * from './chain/IChainFilterChainClient';
export * from './domain/Address';
export * from './irc/IrcMessage';
export * from './messages/AddressCache';
export * from './messages/BatchFundingGroup';
export * from './messages/CetAdaptorSignaturesV0';
export * from './messages/ContractDescriptor';
export * from './messages/ContractInfo';
Expand Down
131 changes: 131 additions & 0 deletions packages/messaging/lib/messages/BatchFundingGroup.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
import { Value } from '@node-dlc/bitcoin';
import { BufferReader, BufferWriter } from '@node-lightning/bufio';

import { MessageType } from '../MessageType';
import { IDlcMessage } from './DlcMessage';

/**
* The BatchFundingGroup TLV contains information about the intent to
* enter multiple DLCs simulatenously within one batch dlc funding
* transaction in the contract negotiation stage of the peer protocol
*
* This is the first step toward creating a batch dlc funding transaction
*
* A DlcOffer or DlcAccept can contain one or multiple BatchFundingInfo
* TLVs to specify one or more groupings. This allows specification of
* collateral put towards different types of contracts, such as options
* contracts, futures contracts, or other investment types.
*
* Attributes:
* - tempContractIds: Temporary identifiers for contracts proposed in DlcOffers.
* - contractIds: Identifiers for contracts that have been accepted and are
* part of the funding transaction. These are derived from DlcOffers and DlcAccepts.
* - allocatedCollateral: The amount of collateral allocated to the contracts
* within this group. This is specified early in the negotiation process.
* - eventIds: Oracle event identifiers for the contracts in this group. These
* are also specified early in the negotiation process.
*
* Note: During the early stages of the negotiation protocol, only allocatedCollateral
* and eventIds are specified. tempContractIds and contractIds are added to the
* DlcAccept upon creation.
*/
export class BatchFundingGroup implements IDlcMessage {
public static type = MessageType.BatchFundingGroup;

/**
* Deserializes a batch_contract_info message
* @param buf
*/
public static deserialize(buf: Buffer): BatchFundingGroup {
const instance = new BatchFundingGroup();
const reader = new BufferReader(buf);

reader.readBigSize(); // read type
const tempContractIdsCount = reader.readBigSize();
for (let i = 0; i < Number(tempContractIdsCount); i++) {
const length = reader.readBigSize();
instance.tempContractIds.push(reader.readBytes(Number(length)));
}

const contractIdsCount = reader.readBigSize();
for (let i = 0; i < Number(contractIdsCount); i++) {
const length = reader.readBigSize();
instance.contractIds.push(reader.readBytes(Number(length)));
}

instance.allocatedCollateral = Value.fromSats(reader.readUInt64BE());

const eventIdsCount = reader.readBigSize();
for (let i = 0; i < Number(eventIdsCount); i++) {
const length = reader.readBigSize();
instance.eventIds.push(reader.readBytes(Number(length)).toString());
}

return instance;
}

/**
* The type for batch_contract_info message.
*/
public type = BatchFundingGroup.type;

public tempContractIds: Buffer[] = [];

public contractIds: Buffer[] = [];

public allocatedCollateral: Value;

public eventIds: string[] = [];

/**
* Converts batch_funding_info to JSON
*/
public toJSON(): IBatchFundingGroupJSON {
return {
type: this.type,
tempContractIds: this.tempContractIds.map((id) => id.toString('hex')),
contractIds: this.contractIds.map((id) => id.toString('hex')),
totalCollateral: Number(this.allocatedCollateral.sats),
eventIds: this.eventIds,
};
}

/**
* Serializes the batch_funding_info message into a Buffer
*/
public serialize(): Buffer {
const writer = new BufferWriter();
writer.writeBigSize(this.type);

writer.writeBigSize(this.tempContractIds.length);
this.tempContractIds.forEach((id) => {
writer.writeBigSize(id.length);
writer.writeBytes(id);
});

writer.writeBigSize(this.contractIds.length);
this.contractIds.forEach((id) => {
writer.writeBigSize(id.length);
writer.writeBytes(id);
});

writer.writeUInt64BE(this.allocatedCollateral.sats);

writer.writeBigSize(this.eventIds.length);
this.eventIds.forEach((id) => {
const idBuffer = Buffer.from(id);
writer.writeBigSize(idBuffer.length);
writer.writeBytes(idBuffer);
});

return writer.toBuffer();
}
}

export interface IBatchFundingGroupJSON {
type: number;
tempContractIds: string[];
contractIds: string[];
totalCollateral: number;
eventIds: string[];
}
38 changes: 38 additions & 0 deletions packages/messaging/lib/messages/DlcAccept.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,9 @@ import { address } from 'bitcoinjs-lib';
import secp256k1 from 'secp256k1';

import { MessageType } from '../MessageType';
import { deserializeTlv } from '../serialize/deserializeTlv';
import { getTlv, skipTlv } from '../serialize/getTlv';
import { BatchFundingGroup, IBatchFundingGroupJSON } from './BatchFundingGroup';
import {
CetAdaptorSignaturesV0,
ICetAdaptorSignaturesV0JSON,
Expand Down Expand Up @@ -85,6 +87,23 @@ export class DlcAcceptV0 extends DlcAccept implements IDlcMessage {
instance.refundSignature = reader.readBytes(64);
instance.negotiationFields = NegotiationFields.deserialize(getTlv(reader));

while (!reader.eof) {
const buf = getTlv(reader);
const tlvReader = new BufferReader(buf);
const { type } = deserializeTlv(tlvReader);

switch (Number(type)) {
case MessageType.BatchFundingGroup:
if (!instance.batchFundingGroups) {
instance.batchFundingGroups = [];
}
instance.batchFundingGroups.push(BatchFundingGroup.deserialize(buf));
break;
default:
break;
}
}

return instance;
}

Expand Down Expand Up @@ -115,6 +134,8 @@ export class DlcAcceptV0 extends DlcAccept implements IDlcMessage {

public negotiationFields: NegotiationFields;

public batchFundingGroups?: BatchFundingGroup[];

/**
* Get funding, change and payout address from DlcOffer
* @param network Bitcoin Network
Expand Down Expand Up @@ -196,6 +217,14 @@ export class DlcAcceptV0 extends DlcAccept implements IDlcMessage {
* Converts dlc_accept_v0 to JSON
*/
public toJSON(): IDlcAcceptV0JSON {
const tlvs = [];

if (this.batchFundingGroups) {
this.batchFundingGroups.forEach((group) => {
tlvs.push(group.serialize());
});
}

return {
type: this.type,
tempContractId: this.tempContractId.toString('hex'),
Expand All @@ -209,6 +238,7 @@ export class DlcAcceptV0 extends DlcAccept implements IDlcMessage {
cetSignatures: this.cetSignatures.toJSON(),
refundSignature: this.refundSignature.toString('hex'),
negotiationFields: this.negotiationFields.toJSON(),
tlvs,
};
}

Expand Down Expand Up @@ -237,6 +267,11 @@ export class DlcAcceptV0 extends DlcAccept implements IDlcMessage {
writer.writeBytes(this.refundSignature);
writer.writeBytes(this.negotiationFields.serialize());

if (this.batchFundingGroups)
this.batchFundingGroups.forEach((fundingInfo) =>
writer.writeBytes(fundingInfo.serialize()),
);

return writer.toBuffer();
}

Expand All @@ -251,6 +286,7 @@ export class DlcAcceptV0 extends DlcAccept implements IDlcMessage {
this.changeSPK,
this.changeSerialId,
this.negotiationFields,
this.batchFundingGroups,
);
}
}
Expand All @@ -266,6 +302,7 @@ export class DlcAcceptWithoutSigs {
readonly changeSPK: Buffer,
readonly changeSerialId: bigint,
readonly negotiationFields: NegotiationFields,
readonly batchFundingGroups?: BatchFundingGroup[],
) {}
}

Expand All @@ -285,6 +322,7 @@ export interface IDlcAcceptV0JSON {
| INegotiationFieldsV0JSON
| INegotiationFieldsV1JSON
| INegotiationFieldsV2JSON;
tlvs: IBatchFundingGroupJSON[];
}

export interface IDlcAcceptV0Addresses {
Expand Down
24 changes: 23 additions & 1 deletion packages/messaging/lib/messages/DlcOffer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import secp256k1 from 'secp256k1';
import { MessageType } from '../MessageType';
import { deserializeTlv } from '../serialize/deserializeTlv';
import { getTlv } from '../serialize/getTlv';
import { BatchFundingGroup, IBatchFundingGroupJSON } from './BatchFundingGroup';
import {
ContractInfo,
IContractInfoV0JSON,
Expand Down Expand Up @@ -111,6 +112,12 @@ export class DlcOfferV0 extends DlcOffer implements IDlcMessage {
case MessageType.OrderPositionInfoV0:
instance.positionInfo = OrderPositionInfo.deserialize(buf);
break;
case MessageType.BatchFundingGroup:
if (!instance.batchFundingGroups) {
instance.batchFundingGroups = [];
}
instance.batchFundingGroups.push(BatchFundingGroup.deserialize(buf));
break;
default:
break;
}
Expand Down Expand Up @@ -158,6 +165,8 @@ export class DlcOfferV0 extends DlcOffer implements IDlcMessage {

public positionInfo?: OrderPositionInfo;

public batchFundingGroups?: BatchFundingGroup[];

/**
* Get funding, change and payout address from DlcOffer
* @param network Bitcoin Network
Expand Down Expand Up @@ -294,6 +303,10 @@ export class DlcOfferV0 extends DlcOffer implements IDlcMessage {
if (this.metadata) tlvs.push(this.metadata.toJSON());
if (this.ircInfo) tlvs.push(this.ircInfo.toJSON());
if (this.positionInfo) tlvs.push(this.positionInfo.toJSON());
if (this.batchFundingGroups)
this.batchFundingGroups.forEach((fundingInfo) =>
tlvs.push(fundingInfo.toJSON()),
);

return {
type: this.type,
Expand Down Expand Up @@ -346,6 +359,10 @@ export class DlcOfferV0 extends DlcOffer implements IDlcMessage {
if (this.metadata) writer.writeBytes(this.metadata.serialize());
if (this.ircInfo) writer.writeBytes(this.ircInfo.serialize());
if (this.positionInfo) writer.writeBytes(this.positionInfo.serialize());
if (this.batchFundingGroups)
this.batchFundingGroups.forEach((fundingInfo) =>
writer.writeBytes(fundingInfo.serialize()),
);

return writer.toBuffer();
}
Expand All @@ -367,7 +384,12 @@ export interface IDlcOfferV0JSON {
feeRatePerVb: number;
cetLocktime: number;
refundLocktime: number;
tlvs: (IOrderMetadataJSON | IOrderIrcInfoJSON | IOrderPositionInfoJSON)[];
tlvs: (
| IOrderMetadataJSON
| IOrderIrcInfoJSON
| IOrderPositionInfoJSON
| IBatchFundingGroupJSON
)[];
}

export interface IDlcOfferV0Addresses {
Expand Down
Loading

0 comments on commit 73d5226

Please sign in to comment.