@node-dlc/messaging
Version:
DLC Messaging Protocol
440 lines (372 loc) • 14.5 kB
text/typescript
/* eslint-disable @typescript-eslint/no-explicit-any */
import { expect } from 'chai';
import * as fs from 'fs';
import * as path from 'path';
import { DlcAccept, DlcOffer, DlcSign, MessageType } from '../../lib';
interface DlcSpecsTestData {
offer_message?: {
message: any;
serialized: string;
};
accept_message?: {
message: any;
serialized: string;
};
sign_message?: {
message: any;
serialized: string;
};
}
interface TestResult {
filename: string;
messageType: string;
success: boolean;
error?: string;
details?: any;
}
describe('DLC Ecosystem Compatibility Tests', () => {
const dlcSpecsDir = path.join(__dirname, '../../test_vectors/dlcspecs');
const rustDlcDir = path.join(__dirname, '../../test_vectors/rust-dlc');
let dlcSpecsFiles: string[] = [];
let rustDlcFiles: string[] = [];
before(() => {
// Discover all test vector files
if (fs.existsSync(dlcSpecsDir)) {
dlcSpecsFiles = fs
.readdirSync(dlcSpecsDir)
.filter((f) => f.endsWith('.json'));
}
if (fs.existsSync(rustDlcDir)) {
rustDlcFiles = fs
.readdirSync(rustDlcDir)
.filter((f) => f.endsWith('.json'));
}
});
describe('DLCSpecs Test Vector Compatibility', () => {
it('should test all dlcspecs test vectors with version compatibility', () => {
const allResults: TestResult[] = [];
let oldFormatCount = 0;
let newFormatCount = 0;
dlcSpecsFiles.forEach((filename) => {
const filePath = path.join(dlcSpecsDir, filename);
let testData: DlcSpecsTestData;
try {
testData = JSON.parse(fs.readFileSync(filePath, 'utf8'));
} catch (error) {
testData = {};
}
// Test offer message if present
if (testData.offer_message) {
const result: TestResult = {
filename,
messageType: 'offer',
success: false,
};
try {
const inputBuffer = Buffer.from(
testData.offer_message.serialized,
'hex',
);
const expectedMessage = testData.offer_message.message;
// Check format version based on serialization
const messageType = inputBuffer.readUInt16BE(0);
expect(messageType).to.equal(MessageType.DlcOffer);
// Detect old vs new format
const nextBytes = inputBuffer.slice(2, 7);
const possibleProtocolVersion = nextBytes.readUInt32BE(0);
const possibleContractFlags = nextBytes.readUInt8(4);
const isNewFormat =
possibleProtocolVersion >= 1 &&
possibleProtocolVersion <= 10 &&
possibleContractFlags === 0;
if (isNewFormat) {
newFormatCount++;
} else {
oldFormatCount++;
}
const offer = DlcOffer.deserialize(inputBuffer);
expect(offer.type).to.equal(MessageType.DlcOffer);
if (expectedMessage.protocolVersion) {
expect(offer.protocolVersion).to.equal(
expectedMessage.protocolVersion,
);
}
if (expectedMessage.chainHash) {
expect(offer.chainHash.toString('hex')).to.equal(
expectedMessage.chainHash,
);
}
if (expectedMessage.temporaryContractId) {
expect(offer.temporaryContractId.toString('hex')).to.equal(
expectedMessage.temporaryContractId,
);
}
// Test round-trip serialization
const serialized = offer.serialize();
const deserialized = DlcOffer.deserialize(serialized);
expect(deserialized.protocolVersion).to.equal(
offer.protocolVersion,
);
result.success = true;
result.details = {
protocolVersion: offer.protocolVersion,
formatVersion: isNewFormat ? 'new' : 'old',
contractInfoType: offer.contractInfo.constructor.name,
fundingInputsCount: offer.fundingInputs.length,
offerCollateral: Number(offer.offerCollateral),
};
} catch (error) {
result.error = error.message;
}
allResults.push(result);
}
// Test accept message if present
if (testData.accept_message) {
const result: TestResult = {
filename,
messageType: 'accept',
success: false,
};
try {
const inputBuffer = Buffer.from(
testData.accept_message.serialized,
'hex',
);
const expectedMessage = testData.accept_message.message;
const accept = DlcAccept.deserialize(inputBuffer);
expect(accept.type).to.equal(MessageType.DlcAccept);
if (expectedMessage.protocolVersion) {
expect(accept.protocolVersion).to.equal(
expectedMessage.protocolVersion,
);
}
if (expectedMessage.temporaryContractId) {
expect(accept.temporaryContractId.toString('hex')).to.equal(
expectedMessage.temporaryContractId,
);
}
// Test round-trip
const serialized = accept.serialize();
const deserialized = DlcAccept.deserialize(serialized);
expect(deserialized.protocolVersion).to.equal(
accept.protocolVersion,
);
result.success = true;
result.details = {
protocolVersion: accept.protocolVersion,
fundingInputsCount: accept.fundingInputs.length,
acceptCollateral: Number(accept.acceptCollateral),
hasNegotiationFields: !!accept.negotiationFields,
};
} catch (error) {
result.error = error.message;
}
allResults.push(result);
}
// Test sign message if present
if (testData.sign_message) {
const result: TestResult = {
filename,
messageType: 'sign',
success: false,
};
try {
const inputBuffer = Buffer.from(
testData.sign_message.serialized,
'hex',
);
const expectedMessage = testData.sign_message.message;
const sign = DlcSign.deserialize(inputBuffer);
expect(sign.type).to.equal(MessageType.DlcSign);
if (expectedMessage.protocolVersion) {
expect(sign.protocolVersion).to.equal(
expectedMessage.protocolVersion,
);
}
if (expectedMessage.contractId) {
expect(sign.contractId.toString('hex')).to.equal(
expectedMessage.contractId,
);
}
// Test round-trip
const serialized = sign.serialize();
const deserialized = DlcSign.deserialize(serialized);
expect(deserialized.protocolVersion).to.equal(sign.protocolVersion);
result.success = true;
result.details = {
protocolVersion: sign.protocolVersion,
cetSignatureCount: sign.cetAdaptorSignatures.sigs.length,
hasBatchFunding: !!sign.batchFundingGroups,
};
} catch (error) {
result.error = error.message;
}
allResults.push(result);
}
});
// Generate comprehensive report with version analysis
const dlcSpecsSuccessRate =
allResults.length > 0
? allResults.filter((r) => r.success).length / allResults.length
: 0;
const byMessageType = {
offer: allResults.filter((r) => r.messageType === 'offer'),
accept: allResults.filter((r) => r.messageType === 'accept'),
sign: allResults.filter((r) => r.messageType === 'sign'),
};
const failures = allResults.filter((r) => !r.success);
const reportLines = [
`DLCSpecs Test Vector Results (With Version Compatibility):`,
`Files Processed: ${dlcSpecsFiles.length}`,
`Tests Run: ${allResults.length}`,
`Success Rate: ${Math.round(dlcSpecsSuccessRate * 100)}% (${
allResults.filter((r) => r.success).length
}/${allResults.length})`,
'',
'Version Analysis:',
` • Old format (pre-protocol_version): ${oldFormatCount}`,
` • New format (with protocol_version): ${newFormatCount}`,
'',
'By Message Type:',
...Object.entries(byMessageType).map(([type, results]) => {
const successCount = results.filter((r) => r.success).length;
const rate =
results.length > 0
? Math.round((successCount / results.length) * 100)
: 0;
return ` • ${type}: ${rate}% (${successCount}/${results.length})`;
}),
];
if (failures.length > 0) {
reportLines.push('', 'Failures:');
failures.forEach((f) => {
reportLines.push(` • ${f.filename} (${f.messageType}): ${f.error}`);
});
}
// Enhanced expectation with version context
const expectedMinRate = 0.5; // Expect reasonable compatibility with version detection
expect(dlcSpecsSuccessRate).to.be.at.least(
expectedMinRate,
`DLCSpecs compatibility: ${Math.round(
dlcSpecsSuccessRate * 100,
)}%. Version Analysis: ${oldFormatCount} old format, ${newFormatCount} new format test vectors. \n\n${reportLines.join(
'\n',
)}`,
);
});
});
describe('Rust-DLC Test Vector Compatibility', () => {
it('should test all rust-dlc test vectors comprehensively', () => {
const allResults: TestResult[] = [];
rustDlcFiles.forEach((filename) => {
const filePath = path.join(rustDlcDir, filename);
let testData: any;
try {
testData = JSON.parse(fs.readFileSync(filePath, 'utf8'));
} catch (error) {
testData = {};
}
const result: TestResult = {
filename,
messageType: 'rust-dlc-validation',
success: false,
};
try {
// Detect message type from filename or structure
let messageType = 'unknown';
if (filename.includes('offer')) messageType = 'offer';
else if (filename.includes('accept')) messageType = 'accept';
else if (filename.includes('sign')) messageType = 'sign';
result.messageType = `rust-dlc-${messageType}`;
// Universal rust-dlc validations
if (testData.protocolVersion !== undefined) {
expect(testData.protocolVersion).to.be.a('number');
expect(testData.protocolVersion).to.equal(1);
}
// Message-specific validations
if (messageType === 'offer' || testData.contractInfo) {
expect(testData.chainHash).to.be.a('string');
expect(testData.temporaryContractId).to.be.a('string');
expect(testData.contractInfo).to.be.an('object');
expect(testData.fundingPubkey).to.be.a('string');
expect(testData.payoutSpk).to.be.a('string');
if (testData.contractInfo.singleContractInfo) {
expect(
testData.contractInfo.singleContractInfo.totalCollateral,
).to.be.a('number');
}
}
if (
messageType === 'accept' ||
testData.acceptCollateral !== undefined
) {
expect(testData.temporaryContractId).to.be.a('string');
expect(testData.acceptCollateral).to.be.a('number');
expect(testData.fundingPubkey).to.be.a('string');
expect(testData.cetAdaptorSignatures).to.be.an('object');
expect(
testData.cetAdaptorSignatures.ecdsaAdaptorSignatures,
).to.be.an('array');
}
if (messageType === 'sign' || testData.contractId) {
expect(testData.contractId).to.be.a('string');
expect(testData.cetAdaptorSignatures).to.be.an('object');
expect(testData.refundSignature).to.be.a('string');
expect(testData.fundingSignatures).to.be.an('object');
}
result.success = true;
result.details = {
detectedType: messageType,
protocolVersion: testData.protocolVersion,
hasContractInfo: !!testData.contractInfo,
hasCetSignatures: !!testData.cetAdaptorSignatures,
};
} catch (error) {
result.error = error.message;
}
allResults.push(result);
});
// Generate comprehensive report
const rustDlcSuccessRate =
allResults.length > 0
? allResults.filter((r) => r.success).length / allResults.length
: 0;
const failures = allResults.filter((r) => !r.success);
const reportLines = [
`Rust-DLC Test Vector Results:`,
`Files Processed: ${rustDlcFiles.length}`,
`Tests Run: ${allResults.length}`,
`Success Rate: ${Math.round(rustDlcSuccessRate * 100)}% (${
allResults.filter((r) => r.success).length
}/${allResults.length})`,
];
if (failures.length > 0) {
reportLines.push('', 'Failures:');
failures.forEach((f) => {
reportLines.push(` • ${f.filename}: ${f.error}`);
});
}
// We expect high compatibility with rust-dlc
expect(rustDlcSuccessRate).to.be.greaterThan(
0.8,
`Rust-DLC compatibility too low: ${Math.round(
rustDlcSuccessRate * 100,
)}%. \n\n${reportLines.join('\n')}`,
);
});
});
describe('Overall Ecosystem Compatibility Summary', () => {
it('should provide comprehensive ecosystem compatibility metrics with version analysis', () => {
expect(dlcSpecsFiles.length).to.be.greaterThan(
0,
'Should have dlcspecs test vectors',
);
expect(rustDlcFiles.length).to.be.greaterThan(
0,
'Should have rust-dlc test vectors',
);
// Report will be shown in console during test run through assertion messages
expect(true).to.be.true; // Always pass, this is just for reporting
});
});
});