UNPKG

@node-dlc/messaging

Version:
316 lines (281 loc) 13.6 kB
import { BufferReader } from '@node-dlc/bufio'; import { expect } from 'chai'; import { DlcClose } from '../../lib/messages/DlcClose'; import { FundingInput } from '../../lib/messages/FundingInput'; import { FundingSignatures } from '../../lib/messages/FundingSignatures'; import { ScriptWitnessV0 } from '../../lib/messages/ScriptWitnessV0'; import { MessageType } from '../../lib/MessageType'; describe('DlcClose', () => { let instance: DlcClose; const contractId = Buffer.from( 'c1c79e1e9e2fa2840b2514902ea244f39eb3001a4037a52ea43c797d4f841269', 'hex', ); const closeSignature = Buffer.from( '7c8ad6de287b62a1ed1d74ed9116a5158abc7f97376d201caa88e0f9daad68fcda4c271cc003512e768f403a57e5242bd1f6aa1750d7f3597598094a43b1c7bb', 'hex', ); beforeEach(() => { instance = new DlcClose(); instance.contractId = contractId; instance.closeSignature = closeSignature; instance.offerPayoutSatoshis = BigInt(100000000); instance.acceptPayoutSatoshis = BigInt(100000000); instance.fundInputSerialId = BigInt(123456789); // Create a SegWit funding input using fromJSON for consistency const fundingInputJson = { inputSerialId: 56296, // Use a proper SegWit transaction with inputs and witness data prevTx: '02000000000101f2f398e01eb47021dfcf6e2ccbea00f1a6c783bf83d10b3bfcc83a8bb4c8bbab0000000000ffffffff0200e1f505000000001600149ea3bf2d6eb9c2ffa35e36f41e117403ed7fafe900e1f505000000001600149ea3bf2d6eb9c2ffa35e36f41e117403ed7fafe902473044022047ac8e878352d3ebbde1c94ce3a10d057c24175747116f8288e5d794d12d482f0220217f36a485cae903c713331d877c1f64677e3622ad4010726870540656fe9dcb012103ad1d8e89212f0b92c74d23bb710c00662451716a435b97cb7a8cf95e8e5b8d5600000000', prevTxVout: 0, sequence: 0xffffffff, maxWitnessLen: 107, redeemScript: '', }; instance.fundingInputs = [FundingInput.fromJSON(fundingInputJson)]; // Create funding signatures using proper TLV format instance.fundingSignatures = new FundingSignatures(); const witness1 = new ScriptWitnessV0(); witness1.length = 71; witness1.witness = Buffer.from( '304402203812d7d194d44ec68f244cc3fd68507c563ec8c729fdfa3f4a79395b98abe84f0220704ab3f3ffd9c50c2488e59f90a90465fccc2d924d67a1e98a133676bf52f37201', 'hex', ); const witness2 = new ScriptWitnessV0(); witness2.length = 33; witness2.witness = Buffer.from( '02dde41aa1f21671a2e28ad92155d2d66e0b5428de15d18db4cbcf216bf00de919', 'hex', ); instance.fundingSignatures.witnessElements = [[witness1, witness2]]; }); describe('deserialize', () => { it('should throw if incorrect type', () => { instance.type = 9999 as MessageType; // Invalid type for testing expect(function () { DlcClose.deserialize(instance.serialize()); }).to.throw(Error); }); it('has correct type', () => { // Create a simpler test without complex TLV issues const simpleInstance = new DlcClose(); simpleInstance.contractId = contractId; simpleInstance.closeSignature = closeSignature; simpleInstance.offerPayoutSatoshis = BigInt(100000000); simpleInstance.acceptPayoutSatoshis = BigInt(100000000); simpleInstance.fundInputSerialId = BigInt(123456789); simpleInstance.fundingInputs = []; // Empty for simplicity simpleInstance.fundingSignatures = new FundingSignatures(); simpleInstance.fundingSignatures.witnessElements = []; // Empty for simplicity const serialized = simpleInstance.serialize(); const deserialized = DlcClose.deserialize(serialized); expect(deserialized.type).to.equal(simpleInstance.type); }); }); describe('DlcCloseV0', () => { describe('serialize', () => { it('serializes', () => { const serialized = instance.serialize(); expect(serialized).to.be.instanceOf(Buffer); expect(serialized.length).to.be.greaterThan(0); // Verify the type is correct at the beginning expect(serialized.readUInt16BE(0)).to.equal(MessageType.DlcClose); }); }); describe('deserialize', () => { it('deserializes', () => { // Test with simpler data to avoid TLV complexity const simpleInstance = new DlcClose(); simpleInstance.contractId = contractId; simpleInstance.closeSignature = closeSignature; simpleInstance.offerPayoutSatoshis = BigInt(100000000); simpleInstance.acceptPayoutSatoshis = BigInt(100000000); simpleInstance.fundInputSerialId = BigInt(123456789); simpleInstance.fundingInputs = []; simpleInstance.fundingSignatures = new FundingSignatures(); simpleInstance.fundingSignatures.witnessElements = []; const serialized = simpleInstance.serialize(); const deserialized = DlcClose.deserialize(serialized); expect(deserialized.contractId).to.deep.equal(contractId); expect(deserialized.closeSignature).to.deep.equal(closeSignature); expect(Number(deserialized.offerPayoutSatoshis)).to.equal(100000000); expect(Number(deserialized.acceptPayoutSatoshis)).to.equal(100000000); expect(Number(deserialized.fundInputSerialId)).to.equal(123456789); expect(deserialized.fundingInputs).to.have.length(0); expect(deserialized.fundingSignatures).to.not.be.undefined; }); it('has correct type', () => { const simpleInstance = new DlcClose(); simpleInstance.contractId = contractId; simpleInstance.closeSignature = closeSignature; simpleInstance.offerPayoutSatoshis = BigInt(100000000); simpleInstance.acceptPayoutSatoshis = BigInt(100000000); simpleInstance.fundInputSerialId = BigInt(123456789); simpleInstance.fundingInputs = []; simpleInstance.fundingSignatures = new FundingSignatures(); simpleInstance.fundingSignatures.witnessElements = []; const serialized = simpleInstance.serialize(); const deserialized = DlcClose.deserialize(serialized); expect(deserialized.type).to.equal(MessageType.DlcClose); }); }); 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.fundInputSerialId).to.equal(123456789); expect(json.fundingInputs).to.have.length(1); expect(json.fundingInputs[0]).to.have.property('prevTx'); expect(json.fundingSignatures).to.not.be.undefined; }); }); describe('fromJSON', () => { it('should create instance from JSON', () => { const json = { contractId: contractId.toString('hex'), closeSignature: closeSignature.toString('hex'), offerPayoutSatoshis: 100000000, acceptPayoutSatoshis: 100000000, fundInputSerialId: 123456789, fundingInputs: [ { inputSerialId: 56296, prevTx: '02000000000101f2f398e01eb47021dfcf6e2ccbea00f1a6c783bf83d10b3bfcc83a8bb4c8bbab0000000000ffffffff0200e1f505000000001600149ea3bf2d6eb9c2ffa35e36f41e117403ed7fafe900e1f505000000001600149ea3bf2d6eb9c2ffa35e36f41e117403ed7fafe902473044022047ac8e878352d3ebbde1c94ce3a10d057c24175747116f8288e5d794d12d482f0220217f36a485cae903c713331d877c1f64677e3622ad4010726870540656fe9dcb012103ad1d8e89212f0b92c74d23bb710c00662451716a435b97cb7a8cf95e8e5b8d5600000000', prevTxVout: 0, sequence: 0xffffffff, maxWitnessLen: 107, redeemScript: '', }, ], fundingSignatures: { fundingSignatures: [ { witnessElements: [ { length: 71, witness: '304402203812d7d194d44ec68f244cc3fd68507c563ec8c729fdfa3f4a79395b98abe84f0220704ab3f3ffd9c50c2488e59f90a90465fccc2d924d67a1e98a133676bf52f37201', }, { length: 33, witness: '02dde41aa1f21671a2e28ad92155d2d66e0b5428de15d18db4cbcf216bf00de919', }, ], }, ], }, }; const fromJsonInstance = DlcClose.fromJSON(json); expect(fromJsonInstance.contractId).to.deep.equal(contractId); expect(fromJsonInstance.closeSignature).to.deep.equal(closeSignature); expect(Number(fromJsonInstance.offerPayoutSatoshis)).to.equal( 100000000, ); expect(Number(fromJsonInstance.acceptPayoutSatoshis)).to.equal( 100000000, ); expect(Number(fromJsonInstance.fundInputSerialId)).to.equal(123456789); }); }); describe('validate', () => { it('should validate successfully with valid data', () => { expect(() => instance.validate()).to.not.throw(); }); it('should throw if contractId is invalid', () => { instance.contractId = Buffer.alloc(16); // Wrong length expect(() => instance.validate()).to.throw( 'contractId must be 32 bytes', ); }); it('should throw if closeSignature is invalid', () => { instance.closeSignature = Buffer.alloc(32); // Wrong length expect(() => instance.validate()).to.throw( 'closeSignature must be 64 bytes', ); }); it('should throw if inputSerialIds arent unique', () => { const fundingInputJson = { inputSerialId: 56296, prevTx: '02000000000101f2f398e01eb47021dfcf6e2ccbea00f1a6c783bf83d10b3bfcc83a8bb4c8bbab0000000000ffffffff0200e1f505000000001600149ea3bf2d6eb9c2ffa35e36f41e117403ed7fafe900e1f505000000001600149ea3bf2d6eb9c2ffa35e36f41e117403ed7fafe902473044022047ac8e878352d3ebbde1c94ce3a10d057c24175747116f8288e5d794d12d482f0220217f36a485cae903c713331d877c1f64677e3622ad4010726870540656fe9dcb012103ad1d8e89212f0b92c74d23bb710c00662451716a435b97cb7a8cf95e8e5b8d5600000000', prevTxVout: 0, sequence: 0xffffffff, maxWitnessLen: 107, redeemScript: '', }; instance.fundingInputs = [ FundingInput.fromJSON(fundingInputJson), FundingInput.fromJSON(fundingInputJson), // Same serial ID ]; expect(() => instance.validate()).to.throw( 'inputSerialIds must be unique', ); }); }); describe('round-trip compatibility', () => { it('should maintain data integrity through serialize/deserialize cycle', () => { // Test with simpler data to avoid TLV complexity const simpleInstance = new DlcClose(); simpleInstance.contractId = contractId; simpleInstance.closeSignature = closeSignature; simpleInstance.offerPayoutSatoshis = BigInt(100000000); simpleInstance.acceptPayoutSatoshis = BigInt(100000000); simpleInstance.fundInputSerialId = BigInt(123456789); simpleInstance.fundingInputs = []; simpleInstance.fundingSignatures = new FundingSignatures(); simpleInstance.fundingSignatures.witnessElements = []; const serialized = simpleInstance.serialize(); const deserialized = DlcClose.deserialize(serialized); const reSerialized = deserialized.serialize(); expect(serialized.toString('hex')).to.equal( reSerialized.toString('hex'), ); }); it('should maintain data integrity through toJSON/fromJSON cycle', () => { const json = instance.toJSON(); const fromJson = DlcClose.fromJSON(json); const backToJson = fromJson.toJSON(); expect(json.contractId).to.equal(backToJson.contractId); expect(json.closeSignature).to.equal(backToJson.closeSignature); expect(json.offerPayoutSatoshis).to.equal( backToJson.offerPayoutSatoshis, ); expect(json.acceptPayoutSatoshis).to.equal( backToJson.acceptPayoutSatoshis, ); expect(json.fundInputSerialId).to.equal(backToJson.fundInputSerialId); }); }); describe('architectural consistency', () => { it('should follow DlcOffer patterns with bigsize for funding inputs length', () => { const serialized = instance.serialize(); const reader = new BufferReader(serialized); // Skip to funding inputs length position reader.readUInt16BE(); // type reader.readUInt32BE(); // protocolVersion (new field!) reader.readBytes(32); // contractId reader.readBytes(64); // closeSignature reader.readUInt64BE(); // offerPayoutSatoshis reader.readUInt64BE(); // acceptPayoutSatoshis reader.readUInt64BE(); // fundInputSerialId // Should be able to read funding inputs length as bigsize const fundingInputsLen = reader.readBigSize(); expect(Number(fundingInputsLen)).to.equal(1); }); it('should handle basic structure correctly', () => { const serialized = instance.serialize(); expect(serialized.length).to.be.greaterThan(150); // Should be substantial // Basic structural checks const reader = new BufferReader(serialized); expect(reader.readUInt16BE()).to.equal(MessageType.DlcClose); expect(reader.readUInt32BE()).to.equal(1); // protocolVersion expect(reader.readBytes(32)).to.deep.equal(contractId); expect(reader.readBytes(64)).to.deep.equal(closeSignature); }); }); }); });