@node-dlc/messaging
Version:
DLC Messaging Protocol
424 lines (378 loc) • 17.1 kB
text/typescript
import { expect } from 'chai';
import * as fs from 'fs';
import * as path from 'path';
import {
DigitDecompositionEventDescriptor,
EnumEventDescriptor,
} from '../../lib/messages/EventDescriptor';
import { OracleAnnouncement } from '../../lib/messages/OracleAnnouncement';
import { OracleEvent } from '../../lib/messages/OracleEvent';
// 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('OracleAnnouncement', () => {
const announcementSig = Buffer.from(
'fab22628f6e2602e1671c286a2f63a9246794008627a1749639217f4214cb4a9' +
'494c93d1a852221080f44f697adb4355df59eb339f6ba0f9b01ba661a8b108d4',
'hex',
);
const oraclePubkey = Buffer.from(
'da078bbb1d34e7729e38e2ae34236e776da121af442626fa31e31ae55a279a0b',
'hex',
);
const invalidOraclePubkey = Buffer.from(
'5d1bcfab252c6dd9edd7aea4c5eeeef138f7ff7346061ea40143a9f5ae80baa9',
'hex',
);
describe('serialize', () => {
it('serializes', () => {
const instance = new OracleAnnouncement();
instance.length = BigInt(164);
instance.announcementSig = announcementSig;
instance.oraclePublicKey = oraclePubkey;
instance.oracleEvent = OracleEvent.deserialize(
Buffer.from(
'fdd822' + // type oracle_event
'40' + // length
'0001' + // nb_nonces
'3cfba011378411b20a5ab773cb95daab93e9bcd1e4cce44986a7dda84e01841b' + // oracle_nonces
'00000000' + // event_maturity_epoch
'fdd806' + // type enum_event_descriptor
'10' + // length
'0002' + // num_outcomes
'06' + // outcome_1_len
'64756d6d7931' + // outcome_1
'06' + // outcome_2_len
'64756d6d7932' + // outcome_2
'05' + // event_id_length
'64756d6d79', // event_id
'hex',
),
);
expect(instance.serialize().toString("hex")).to.equal(
"fdd824" + // type oracle_announcement
"a4" + // length
"fab22628f6e2602e1671c286a2f63a9246794008627a1749639217f4214cb4a9" + // announcement_signature_r
"494c93d1a852221080f44f697adb4355df59eb339f6ba0f9b01ba661a8b108d4" + // announcement_signature_s
"da078bbb1d34e7729e38e2ae34236e776da121af442626fa31e31ae55a279a0b" + // oracle_public_key
"fdd822" + // type oracle_event
"40" + // length
"0001" + // nb_nonces
"3cfba011378411b20a5ab773cb95daab93e9bcd1e4cce44986a7dda84e01841b" + // oracle_nonces
"00000000" + // event_maturity_epoch
"fdd806" + // type enum_event_descriptor
"10" + // length
"0002" + // num_outcomes
"06" + // outcome_1_len
"64756d6d7931" + // outcome_1
"06" + // outcome_2_len
"64756d6d7932" + // outcome_2
"05" + // event_id_length
"64756d6d79" // event_id
); // prettier-ignore
});
});
describe('deserialize', () => {
it('deserializes', () => {
const buf = Buffer.from(
"fdd824" + // type oracle_announcement
"a4" + // length
"fab22628f6e2602e1671c286a2f63a9246794008627a1749639217f4214cb4a9" + // announcement_signature_r
"494c93d1a852221080f44f697adb4355df59eb339f6ba0f9b01ba661a8b108d4" + // announcement_signature_s
"da078bbb1d34e7729e38e2ae34236e776da121af442626fa31e31ae55a279a0b" + // oracle_public_key
"fdd822" + // type oracle_event
"40" + // length
"0001" + // nb_nonces
"3cfba011378411b20a5ab773cb95daab93e9bcd1e4cce44986a7dda84e01841b" + // oracle_nonces
"00000000" + // event_maturity_epoch
"fdd806" + // type enum_event_descriptor
"10" + // length
"0002" + // num_outcomes
"06" + // outcome_1_len
"64756d6d7931" + // outcome_1
"06" + // outcome_2_len
"64756d6d7932" + // outcome_2
"05" + // event_id_length
"64756d6d79" // event_id
, "hex"
); // prettier-ignore
const instance = OracleAnnouncement.deserialize(buf);
expect(instance.length).to.deep.equal(BigInt(164));
expect(instance.announcementSig).to.deep.equal(announcementSig);
expect(instance.oraclePublicKey).to.deep.equal(oraclePubkey);
expect(instance.oracleEvent.serialize().toString('hex')).to.equal(
'fdd822' + // type oracle_event
'40' + // length
'0001' + // nb_nonces
'3cfba011378411b20a5ab773cb95daab93e9bcd1e4cce44986a7dda84e01841b' + // oracle_nonces
'00000000' + // event_maturity_epoch
'fdd806' + // type enum_event_descriptor
'10' + // length
'0002' + // num_outcomes
'06' + // outcome_1_len
'64756d6d7931' + // outcome_1
'06' + // outcome_2_len
'64756d6d7932' + // outcome_2
'05' + // event_id_length
'64756d6d79', // event_id
);
});
});
describe('validation', () => {
it('should validate when correct signature', () => {
const instance = new OracleAnnouncement();
instance.length = BigInt(164);
instance.announcementSig = announcementSig;
instance.oraclePublicKey = oraclePubkey;
instance.oracleEvent = OracleEvent.deserialize(
Buffer.from(
'fdd822' + // type oracle_event
'40' + // length
'0001' + // nb_nonces
'3cfba011378411b20a5ab773cb95daab93e9bcd1e4cce44986a7dda84e01841b' + // oracle_nonces
'00000000' + // event_maturity_epoch
'fdd806' + // type enum_event_descriptor
'10' + // length
'0002' + // num_outcomes
'06' + // outcome_1_len
'64756d6d7931' + // outcome_1
'06' + // outcome_2_len
'64756d6d7932' + // outcome_2
'05' + // event_id_length
'64756d6d79', // event_id
'hex',
),
);
expect(function () {
instance.validate();
}).to.not.throw(Error);
});
it('should invalidate when incorrect signature', () => {
const instance = new OracleAnnouncement();
instance.length = BigInt(164);
instance.announcementSig = announcementSig;
instance.oraclePublicKey = invalidOraclePubkey;
instance.oracleEvent = OracleEvent.deserialize(
Buffer.from(
'fdd822' + // type oracle_event
'40' + // length
'0001' + // nb_nonces
'3cfba011378411b20a5ab773cb95daab93e9bcd1e4cce44986a7dda84e01841b' + // oracle_nonces
'00000000' + // event_maturity_epoch
'fdd806' + // type enum_event_descriptor
'10' + // length
'0002' + // num_outcomes
'06' + // outcome_1_len
'64756d6d7931' + // outcome_1
'06' + // outcome_2_len
'64756d6d7932' + // outcome_2
'05' + // event_id_length
'64756d6d79', // event_id
'hex',
),
);
expect(function () {
instance.validate();
}).to.throw(Error);
});
});
/**
* External Test Vectors
* i.e. Suredbits Oracle: https://oracle.suredbits.com/event/1985a5e99a8c28a402b58c0c58cbf86bd66a0db850aafc7674f7226d5ce82fde
* oracle_announcement_v0
*/
describe('external test vectors', () => {
const buf = Buffer.from(
'fdd824fd02ab1efe41fa42ea1dcd103a0251929dd2b192d2daece8a4ce4d81f68a183b750d92d6f02d796965dc79adf4e7786e08f861a1ecc897afbba2dab9cff6eb0a81937eb8b005b07acf849ad2cec22107331dedbf5a607654fad4eafe39c278e27dde68fdd822fd02450011f9313f1edd903fab297d5350006b669506eb0ffda0bb58319b4df89ac24e14fd15f9791dc78d1596b06f4969bdb37d9e394dc9fedaa18d694027fa32b5ea2a5e60080c58e13727367c3a4ce1ad65dfb3c7e3ca1ea912b0299f6e383bab2875058aa96a1c74633130af6fbd008788de6ac9db76da4ecc7303383cc1a49f525316413850f7e3ac385019d560e84c5b3a3e9ae6c83f59fe4286ddfd23ea46d7ae04610a175cd28a9bf5f574e245c3dfe230dc4b0adf4daaea96780e594f6464f676505f4b74cfe3ffc33415a23de795bf939ce64c0c02033bbfc6c9ff26fb478943a1ece775f38f5db067ca4b2a9168b40792398def9164bfe5c46838472dc3c162af16c811b7a116e9417d5bccb9e5b8a5d7d26095aba993696188c3f85a02f7ab8d12ada171c352785eb63417228c7e248909fc2d673e1bb453140bf8bf429375819afb5e9556663b76ff09c2a7ba9779855ffddc6d360cb459cf8c42a2b949d0de19fe96163d336fd66a4ce2f1791110e679572a20036ffae50204ef520c01058ff4bef28218d1c0e362ee3694ad8b2ae83a51c86c4bc1630ed6202a158810096726f809fc828fafdcf053496affdf887ae8c54b6ca4323ccecf6a51121c4f0c60e790536dab41b221db1c6b35065dc19a9d31cf75901aa35eefecbb6fefd07296cda13cb34ce3b58eba20a0eb8f9614994ec7fee3cc290e30e6b1e3211ae1f3a85b6de6abdbb77d6d9ed33a1cee3bd5cd93a71f12c9c45e385d744ad0e7286660305100fdd80a11000200076274632f75736400000000001109425443205072696365',
'hex',
);
const instance = OracleAnnouncement.deserialize(buf);
it('deserializes', async () => {
expect(Number(instance.length)).to.equal(683);
expect(instance.announcementSig).to.deep.equal(
Buffer.from(
'1efe41fa42ea1dcd103a0251929dd2b192d2daece8a4ce4d81f68a183b750d92d6f02d796965dc79adf4e7786e08f861a1ecc897afbba2dab9cff6eb0a81937e',
'hex',
),
);
expect(instance.oraclePublicKey).to.deep.equal(
Buffer.from(
'b8b005b07acf849ad2cec22107331dedbf5a607654fad4eafe39c278e27dde68',
'hex',
),
);
expect(instance.oracleEvent.oracleNonces[0]).to.deep.equal(
Buffer.from(
'f9313f1edd903fab297d5350006b669506eb0ffda0bb58319b4df89ac24e14fd',
'hex',
),
);
expect(instance.oracleEvent.eventMaturityEpoch).to.equal(1613779200);
expect(
(instance.oracleEvent
.eventDescriptor as DigitDecompositionEventDescriptor).unit,
).to.equal('btc/usd');
});
it('should succeed validation', () => {
expect(function () {
instance.validate();
}).to.not.throw(Error);
});
});
/**
* Comprehensive Test Vectors
* Tests oracle announcements from various implementations and oracle providers
*/
describe('comprehensive announcement test vectors', () => {
describe('Atomic Finance announcements', () => {
let instance: OracleAnnouncement;
before(() => {
const buf = Buffer.from(testVectors.atomic.announcement, 'hex');
instance = OracleAnnouncement.deserialize(buf);
});
it('deserializes Atomic oracle announcement', () => {
expect(Number(instance.length)).to.be.greaterThan(0);
expect(instance.announcementSig).to.be.instanceOf(Buffer);
expect(instance.announcementSig.length).to.equal(64);
expect(instance.oraclePublicKey).to.be.instanceOf(Buffer);
expect(instance.oraclePublicKey.length).to.equal(32);
expect(instance.oracleEvent).to.be.instanceOf(OracleEvent);
expect(instance.getEventId()).to.equal(
testVectors.atomic.metadata.eventId,
);
});
it('validates Atomic oracle announcement', () => {
expect(() => instance.validate()).to.not.throw();
});
it('has correct digit decomposition event descriptor', () => {
const eventDescriptor = instance.oracleEvent
.eventDescriptor as DigitDecompositionEventDescriptor;
expect(eventDescriptor).to.be.instanceOf(
DigitDecompositionEventDescriptor,
);
expect(eventDescriptor.base).to.equal(2);
expect(eventDescriptor.unit).to.equal('BTCUSD');
expect(eventDescriptor.nbDigits).to.equal(18);
});
it('round-trip serialization works', () => {
const serialized = instance.serialize();
const deserialized = OracleAnnouncement.deserialize(serialized);
expect(deserialized.getEventId()).to.equal(instance.getEventId());
expect(deserialized.oraclePublicKey).to.deep.equal(
instance.oraclePublicKey,
);
});
});
// Note: Lava oracle announcement does not contain event id so we don't validate it
describe('Lava oracle', () => {
let instance: OracleAnnouncement;
before(() => {
const buf = Buffer.from(testVectors.lava.announcement, 'hex');
instance = OracleAnnouncement.deserialize(buf);
});
it('deserializes Lava oracle announcement', () => {
expect(Number(instance.length)).to.be.greaterThan(0);
expect(instance.announcementSig).to.be.instanceOf(Buffer);
expect(instance.announcementSig.length).to.equal(64);
expect(instance.oraclePublicKey).to.be.instanceOf(Buffer);
expect(instance.oraclePublicKey.length).to.equal(32);
expect(instance.oracleEvent).to.be.instanceOf(OracleEvent);
});
it('has correct digit decomposition event descriptor', () => {
const eventDescriptor = instance.oracleEvent
.eventDescriptor as DigitDecompositionEventDescriptor;
expect(eventDescriptor).to.be.instanceOf(
DigitDecompositionEventDescriptor,
);
expect(eventDescriptor.base).to.equal(2);
expect(eventDescriptor.unit).to.equal('BTCUSD');
expect(eventDescriptor.nbDigits).to.equal(18);
});
it('round-trip serialization works', () => {
const serialized = instance.serialize();
const deserialized = OracleAnnouncement.deserialize(serialized);
expect(deserialized.getEventId()).to.equal(instance.getEventId());
expect(deserialized.oraclePublicKey).to.deep.equal(
instance.oraclePublicKey,
);
});
});
describe('rust-dlc enum oracle', () => {
let instance: OracleAnnouncement;
before(() => {
const buf = Buffer.from(testVectors.rust_dlc_enum.announcement, 'hex');
instance = OracleAnnouncement.deserialize(buf);
});
it('deserializes rust-dlc enum oracle announcement', () => {
expect(Number(instance.length)).to.be.greaterThan(0);
expect(instance.announcementSig).to.be.instanceOf(Buffer);
expect(instance.announcementSig.length).to.equal(64);
expect(instance.oraclePublicKey).to.be.instanceOf(Buffer);
expect(instance.oraclePublicKey.length).to.equal(32);
expect(instance.oracleEvent).to.be.instanceOf(OracleEvent);
expect(instance.getEventId()).to.equal('sports-match-001');
});
it('has correct enum event descriptor', () => {
const eventDescriptor = instance.oracleEvent
.eventDescriptor as EnumEventDescriptor;
expect(eventDescriptor).to.be.instanceOf(EnumEventDescriptor);
expect(eventDescriptor.outcomes).to.deep.equal(['win', 'lose', 'draw']);
});
it('has single nonce for enum event', () => {
expect(instance.getNonces()).to.have.length(1);
});
it('round-trip serialization works', () => {
const serialized = instance.serialize();
const deserialized = OracleAnnouncement.deserialize(serialized);
expect(deserialized.getEventId()).to.equal(instance.getEventId());
expect(deserialized.oraclePublicKey).to.deep.equal(
instance.oraclePublicKey,
);
});
});
describe('rust-dlc numeric oracle', () => {
let instance: OracleAnnouncement;
before(() => {
const buf = Buffer.from(
testVectors.rust_dlc_numeric.announcement,
'hex',
);
instance = OracleAnnouncement.deserialize(buf);
});
it('deserializes rust-dlc numeric oracle announcement', () => {
expect(Number(instance.length)).to.be.greaterThan(0);
expect(instance.announcementSig).to.be.instanceOf(Buffer);
expect(instance.announcementSig.length).to.equal(64);
expect(instance.oraclePublicKey).to.be.instanceOf(Buffer);
expect(instance.oraclePublicKey.length).to.equal(32);
expect(instance.oracleEvent).to.be.instanceOf(OracleEvent);
expect(instance.getEventId()).to.equal('btc-price-test');
});
it('has correct digit decomposition event descriptor', () => {
const eventDescriptor = instance.oracleEvent
.eventDescriptor as DigitDecompositionEventDescriptor;
expect(eventDescriptor).to.be.instanceOf(
DigitDecompositionEventDescriptor,
);
expect(eventDescriptor.base).to.equal(2);
expect(eventDescriptor.unit).to.equal('BTCUSD');
expect(eventDescriptor.nbDigits).to.equal(8);
expect(eventDescriptor.isSigned).to.equal(false);
});
it('has correct number of nonces for 8-digit decomposition', () => {
expect(instance.getNonces()).to.have.length(8);
});
it('round-trip serialization works', () => {
const serialized = instance.serialize();
const deserialized = OracleAnnouncement.deserialize(serialized);
expect(deserialized.getEventId()).to.equal(instance.getEventId());
expect(deserialized.oraclePublicKey).to.deep.equal(
instance.oraclePublicKey,
);
});
});
});
});