@node-dlc/messaging
Version:
DLC Messaging Protocol
333 lines (287 loc) • 12.4 kB
text/typescript
import { expect } from 'chai';
import * as fs from 'fs';
import * as path from 'path';
import { OracleAnnouncement } from '../../lib/messages/OracleAnnouncement';
import { OracleAttestation } from '../../lib/messages/OracleAttestation';
// Load test vectors
const testVectorsPath = path.join(
__dirname,
'../../test_vectors/oracle/oracle_message_test_vectors.json',
);
const testVectors = JSON.parse(fs.readFileSync(testVectorsPath, 'utf8'));
describe('OracleAttestation', () => {
const attestationSig = Buffer.from(
'424c11a44c2e522f90bbe4abab6ec1bc8ab44c9b29316ce6e1d0d7d08385a474' +
'de6b75f1da183a2a4f9ad144b48bf1026cee9687221df58f04128db79ca17e2a',
'hex',
);
const oraclePubkey = Buffer.from(
'1d5dcdba2e64cb116cc0c375a0856298f0058b778f46bfe625ac6576204889e4',
'hex',
);
const invalidOraclePubkey = Buffer.from(
'5d1bcfab252c6dd9edd7aea4c5eeeef138f7ff7346061ea40143a9f5ae80baa9',
'hex',
);
describe('serialize', () => {
it('serializes', () => {
const instance = new OracleAttestation();
instance.eventId = 'BTC-USD-OVER-50K-COINBASE';
instance.oraclePublicKey = oraclePubkey;
instance.signatures.push(attestationSig);
instance.outcomes.push('NO');
// Test that it serializes without errors (new dlcspecs PR #163 format)
const serialized = instance.serialize();
expect(serialized).to.be.instanceof(Buffer);
expect(serialized.length).to.be.greaterThan(0);
});
});
describe('deserialize', () => {
it('deserializes', () => {
// Create a test instance and serialize it first for round-trip testing
const originalInstance = new OracleAttestation();
originalInstance.eventId = 'BTC-USD-OVER-50K-COINBASE';
originalInstance.oraclePublicKey = oraclePubkey;
originalInstance.signatures.push(attestationSig);
originalInstance.outcomes.push('NO');
// Serialize and then deserialize to ensure round-trip consistency
const serialized = originalInstance.serialize();
const instance = OracleAttestation.deserialize(serialized);
expect(Number(instance.length)).to.be.greaterThan(0); // Length is calculated automatically
expect(instance.eventId).to.equal('BTC-USD-OVER-50K-COINBASE');
expect(instance.oraclePublicKey).to.deep.equal(oraclePubkey);
expect(instance.signatures[0]).to.deep.equal(attestationSig);
expect(instance.outcomes[0]).to.equal('NO');
});
});
describe('validate', () => {
it('should validate when correct outcome signatures', () => {
const instance = new OracleAttestation();
instance.eventId = 'BTC-USD-OVER-50K-COINBASE';
instance.oraclePublicKey = oraclePubkey;
instance.signatures.push(attestationSig);
instance.outcomes.push('NO');
expect(function () {
instance.validate();
}).to.not.throw(Error);
});
it('should invalidate when incorrect outcome signatures', () => {
const instance = new OracleAttestation();
instance.eventId = 'BTC-USD-OVER-50K-COINBASE';
instance.oraclePublicKey = invalidOraclePubkey;
instance.signatures.push(attestationSig);
instance.outcomes.push('NO');
expect(function () {
instance.validate();
}).to.throw(Error);
});
});
/**
* Comprehensive Test Vectors
* Tests oracle attestations from various implementations and oracle providers
*/
describe('comprehensive attestation test vectors', () => {
describe('Atomic Finance attestation', () => {
let announcement: OracleAnnouncement;
let instance: OracleAttestation;
before(() => {
const announcementBuf = Buffer.from(
testVectors.atomic.announcement,
'hex',
);
announcement = OracleAnnouncement.deserialize(announcementBuf);
const attestationBuf = Buffer.from(
testVectors.atomic.attestation,
'hex',
);
instance = OracleAttestation.deserialize(attestationBuf);
});
it('deserializes Atomic oracle attestation', () => {
expect(Number(instance.length)).to.be.greaterThan(0);
expect(instance.eventId).to.equal(testVectors.atomic.metadata.eventId);
expect(instance.oraclePublicKey).to.be.instanceOf(Buffer);
expect(instance.oraclePublicKey.length).to.equal(32);
expect(instance.signatures).to.be.an('array');
expect(instance.outcomes).to.be.an('array');
expect(instance.signatures.length).to.equal(instance.outcomes.length);
});
it('validates Atomic oracle attestation', () => {
expect(() => instance.validate()).to.not.throw();
});
it('validates against corresponding announcement', () => {
expect(() => instance.validate(announcement)).to.not.throw();
});
it('has correct number of signatures for digit decomposition', () => {
expect(instance.signatures).to.have.length(18); // 18-digit binary decomposition
expect(instance.outcomes).to.have.length(18);
});
it('has binary outcomes (0 or 1)', () => {
instance.outcomes.forEach((outcome) => {
expect(['0', '1']).to.include(outcome);
});
});
it('round-trip serialization works', () => {
const serialized = instance.serialize();
const deserialized = OracleAttestation.deserialize(serialized);
expect(deserialized.eventId).to.equal(instance.eventId);
expect(deserialized.oraclePublicKey).to.deep.equal(
instance.oraclePublicKey,
);
expect(deserialized.outcomes).to.deep.equal(instance.outcomes);
});
});
describe('Lava attestation', () => {
let instance: OracleAttestation;
before(() => {
const announcementBuf = Buffer.from(
testVectors.lava.announcement,
'hex',
);
OracleAnnouncement.deserialize(announcementBuf); // ensure announcement deserializes
const attestationBuf = Buffer.from(testVectors.lava.attestation, 'hex');
instance = OracleAttestation.deserialize(attestationBuf);
});
it('deserializes Lava oracle attestation', () => {
expect(Number(instance.length)).to.be.greaterThan(0);
expect(instance.eventId).to.be.a('string');
expect(instance.oraclePublicKey).to.be.instanceOf(Buffer);
expect(instance.oraclePublicKey.length).to.equal(32);
expect(instance.signatures).to.be.an('array');
expect(instance.outcomes).to.be.an('array');
expect(instance.signatures.length).to.equal(instance.outcomes.length);
});
it('has correct number of signatures for digit decomposition', () => {
expect(instance.signatures).to.have.length(18); // 18-digit binary decomposition
expect(instance.outcomes).to.have.length(18);
});
it('has binary outcomes (0 or 1)', () => {
instance.outcomes.forEach((outcome) => {
expect(['0', '1']).to.include(outcome);
});
});
it('round-trip serialization works', () => {
const serialized = instance.serialize();
const deserialized = OracleAttestation.deserialize(serialized);
expect(deserialized.eventId).to.equal(instance.eventId);
expect(deserialized.oraclePublicKey).to.deep.equal(
instance.oraclePublicKey,
);
expect(deserialized.outcomes).to.deep.equal(instance.outcomes);
});
});
describe('rust-dlc enum oracle', () => {
let instance: OracleAttestation;
before(() => {
const announcementBuf = Buffer.from(
testVectors.rust_dlc_enum.announcement,
'hex',
);
OracleAnnouncement.deserialize(announcementBuf); // ensure announcement deserializes
const attestationBuf = Buffer.from(
testVectors.rust_dlc_enum.attestation,
'hex',
);
instance = OracleAttestation.deserialize(attestationBuf);
});
it('deserializes rust-dlc enum oracle attestation', () => {
expect(Number(instance.length)).to.be.greaterThan(0);
expect(instance.eventId).to.equal('sports-match-001');
expect(instance.oraclePublicKey).to.be.instanceOf(Buffer);
expect(instance.oraclePublicKey.length).to.equal(32);
expect(instance.signatures).to.be.an('array');
expect(instance.outcomes).to.be.an('array');
expect(instance.signatures.length).to.equal(instance.outcomes.length);
});
it('has single signature for enum event', () => {
expect(instance.signatures).to.have.length(1);
expect(instance.outcomes).to.have.length(1);
});
it('has valid enum outcome', () => {
expect(instance.outcomes[0]).to.equal('win');
expect(['win', 'lose', 'draw']).to.include(instance.outcomes[0]);
});
it('round-trip serialization works', () => {
const serialized = instance.serialize();
const deserialized = OracleAttestation.deserialize(serialized);
expect(deserialized.eventId).to.equal(instance.eventId);
expect(deserialized.oraclePublicKey).to.deep.equal(
instance.oraclePublicKey,
);
expect(deserialized.outcomes).to.deep.equal(instance.outcomes);
});
});
describe('rust-dlc numeric oracle', () => {
let instance: OracleAttestation;
before(() => {
const attestationBuf = Buffer.from(
testVectors.rust_dlc_numeric.attestation,
'hex',
);
instance = OracleAttestation.deserialize(attestationBuf);
});
it('deserializes rust-dlc numeric oracle attestation', () => {
expect(Number(instance.length)).to.be.greaterThan(0);
expect(instance.eventId).to.equal('btc-price-test');
expect(instance.oraclePublicKey).to.be.instanceOf(Buffer);
expect(instance.oraclePublicKey.length).to.equal(32);
expect(instance.signatures).to.be.an('array');
expect(instance.outcomes).to.be.an('array');
expect(instance.signatures.length).to.equal(instance.outcomes.length);
});
it('has correct number of signatures for 8-digit decomposition', () => {
expect(instance.signatures).to.have.length(8); // 8-digit binary decomposition
expect(instance.outcomes).to.have.length(8);
});
it('has binary outcomes (0 or 1)', () => {
instance.outcomes.forEach((outcome) => {
expect(['0', '1']).to.include(outcome);
});
});
it('represents number 42 in binary (00101010)', () => {
// Outcomes are in little-endian order (LSB first)
const expectedBinary = ['0', '1', '0', '1', '0', '1', '0', '0'];
expect(instance.outcomes).to.deep.equal(expectedBinary);
});
it('round-trip serialization works', () => {
const serialized = instance.serialize();
const deserialized = OracleAttestation.deserialize(serialized);
expect(deserialized.eventId).to.equal(instance.eventId);
expect(deserialized.oraclePublicKey).to.deep.equal(
instance.oraclePublicKey,
);
expect(deserialized.outcomes).to.deep.equal(instance.outcomes);
});
});
describe('cross-validation tests', () => {
it('attestations have consistent event IDs with announcements', () => {
const testCases = [
{
announcement: testVectors.atomic.announcement,
attestation: testVectors.atomic.attestation,
},
{
announcement: testVectors.lava.announcement,
attestation: testVectors.lava.attestation,
},
{
announcement: testVectors.rust_dlc_enum.announcement,
attestation: testVectors.rust_dlc_enum.attestation,
},
{
announcement: testVectors.rust_dlc_numeric.announcement,
attestation: testVectors.rust_dlc_numeric.attestation,
},
];
testCases.forEach((testCase) => {
const announcement = OracleAnnouncement.deserialize(
Buffer.from(testCase.announcement, 'hex'),
);
const attestation = OracleAttestation.deserialize(
Buffer.from(testCase.attestation, 'hex'),
);
expect(attestation.eventId).to.equal(announcement.getEventId());
});
});
});
});
});