@node-dlc/messaging
Version:
DLC Messaging Protocol
221 lines (191 loc) • 8.08 kB
text/typescript
import { expect } from 'chai';
import {
HyperbolaPayoutCurvePiece,
PayoutCurvePiece,
PolynomialPayoutCurvePiece,
} from '../../lib/messages/PayoutCurvePiece';
import { F64 } from '../../lib/serialize/F64';
describe('PayoutCurvePiece', () => {
describe('PolynomialPayoutCurvePiece', () => {
it('serializes and deserializes correctly', () => {
const instance = new PolynomialPayoutCurvePiece();
instance.points = [
{
eventOutcome: BigInt(0),
outcomePayout: BigInt(0),
extraPrecision: 0,
},
{
eventOutcome: BigInt(1),
outcomePayout: BigInt(1),
extraPrecision: 0,
},
];
// 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);
// Test round-trip serialization
const deserialized = PayoutCurvePiece.deserialize(
serialized,
) as PolynomialPayoutCurvePiece;
expect(deserialized).to.be.instanceof(PolynomialPayoutCurvePiece);
expect(deserialized.points.length).to.equal(2);
expect(deserialized.points[0].eventOutcome).to.equal(BigInt(0));
expect(deserialized.points[0].outcomePayout).to.equal(BigInt(0));
expect(deserialized.points[1].eventOutcome).to.equal(BigInt(1));
expect(deserialized.points[1].outcomePayout).to.equal(BigInt(1));
});
it('handles large BigInt values correctly', () => {
const instance = new PolynomialPayoutCurvePiece();
const largeBigInt = BigInt('18446744073709551615'); // Max uint64
instance.points = [
{
eventOutcome: largeBigInt,
outcomePayout: largeBigInt,
extraPrecision: 0,
},
];
const serialized = instance.serialize();
const deserialized = PayoutCurvePiece.deserialize(
serialized,
) as PolynomialPayoutCurvePiece;
expect(deserialized.points[0].eventOutcome).to.equal(largeBigInt);
expect(deserialized.points[0].outcomePayout).to.equal(largeBigInt);
});
});
describe('HyperbolaPayoutCurvePiece', () => {
it('serializes and deserializes correctly with normal values', () => {
const instance = new HyperbolaPayoutCurvePiece();
instance.usePositivePiece = true;
instance.translateOutcome = F64.fromNumber(100.5);
instance.translatePayout = F64.fromNumber(50.25);
instance.a = F64.fromNumber(1.0);
instance.b = F64.fromNumber(0.0);
instance.c = F64.fromNumber(0.0);
instance.d = F64.fromNumber(200.0);
const serialized = instance.serialize();
expect(serialized).to.be.instanceof(Buffer);
expect(serialized.length).to.be.greaterThan(0);
// Test round-trip
const deserialized = PayoutCurvePiece.deserialize(
serialized,
) as HyperbolaPayoutCurvePiece;
expect(deserialized.usePositivePiece).to.equal(true);
expect(deserialized.translateOutcome.toNumber()).to.equal(100.5);
});
it('handles very large f64 values without precision loss', () => {
// Test values that exceed JavaScript MAX_SAFE_INTEGER
const testCases = [
{
name: 'Large positive number',
value: 1.7976931348623157e100, // Very large but valid f64
},
{
name: 'Very small number',
value: 5e-324, // Smallest positive denormalized f64
},
{
name: 'Beyond safe integer',
value: 9007199254740992, // MAX_SAFE_INTEGER + 1
},
{
name: 'Scientific notation',
value: 1.23456789e50,
},
];
testCases.forEach(({ value }) => {
const instance = new HyperbolaPayoutCurvePiece();
instance.usePositivePiece = false;
instance.translateOutcome = F64.fromNumber(value);
instance.translatePayout = F64.fromNumber(123.456);
instance.a = F64.fromNumber(1.0);
instance.b = F64.fromNumber(0.0);
instance.c = F64.fromNumber(0.0);
instance.d = F64.fromNumber(value * 2);
// Test round-trip serialization preserves exact binary representation
const serialized = instance.serialize();
const deserialized = PayoutCurvePiece.deserialize(
serialized,
) as HyperbolaPayoutCurvePiece;
// The F64 should maintain exact binary representation
expect(deserialized.translateOutcome.equals(instance.translateOutcome))
.to.be.true;
expect(deserialized.d.equals(instance.d)).to.be.true;
});
});
it('handles JSON with string values (for very large numbers)', () => {
// Test JSON with string values for large numbers
const jsonWithStrings = {
usePositivePiece: true,
translateOutcome: '1.7976931348623157e+100', // String representation
translatePayout: 50000.0, // Regular number
a: '9007199254740992', // String for large integer
b: 0.0,
c: 0.0,
d: 1.5,
};
const instance = HyperbolaPayoutCurvePiece.fromJSON(jsonWithStrings);
// Should handle both string and number inputs correctly
expect(instance.usePositivePiece).to.be.true;
expect(instance.translateOutcome.toNumber()).to.equal(
1.7976931348623157e100,
);
expect(instance.translatePayout.toNumber()).to.equal(50000.0);
expect(instance.a.toNumber()).to.equal(9007199254740992);
});
it('JSON round-trip maintains values within JavaScript precision limits', () => {
const instance = new HyperbolaPayoutCurvePiece();
instance.usePositivePiece = true;
instance.translateOutcome = F64.fromNumber(50000.5);
instance.translatePayout = F64.fromNumber(25000.25);
instance.a = F64.fromNumber(1.5);
instance.b = F64.fromNumber(0.0);
instance.c = F64.fromNumber(0.0);
instance.d = F64.fromNumber(10000.0);
// Test JSON round-trip
const json = instance.toJSON();
const restored = HyperbolaPayoutCurvePiece.fromJSON(
json.hyperbolaPayoutCurvePiece,
);
expect(restored.usePositivePiece).to.equal(instance.usePositivePiece);
expect(restored.translateOutcome.toNumber()).to.equal(
instance.translateOutcome.toNumber(),
);
expect(restored.translatePayout.toNumber()).to.equal(
instance.translatePayout.toNumber(),
);
expect(restored.a.toNumber()).to.equal(instance.a.toNumber());
expect(restored.d.toNumber()).to.equal(instance.d.toNumber());
});
it('demonstrates precision limits with very large numbers in JSON', () => {
// This test demonstrates the precision tradeoff we're making
const veryLargeNumber = 1.2345678901234567e100;
const instance = new HyperbolaPayoutCurvePiece();
instance.usePositivePiece = false;
instance.translateOutcome = F64.fromNumber(veryLargeNumber);
instance.translatePayout = F64.fromNumber(0);
instance.a = F64.fromNumber(0);
instance.b = F64.fromNumber(0);
instance.c = F64.fromNumber(0);
instance.d = F64.fromNumber(0);
// Binary serialization preserves exact representation
const binarySerialized = instance.serialize();
const binaryDeserialized = PayoutCurvePiece.deserialize(
binarySerialized,
) as HyperbolaPayoutCurvePiece;
expect(
binaryDeserialized.translateOutcome.equals(instance.translateOutcome),
).to.be.true;
// JSON serialization may lose precision for very large numbers
const json = instance.toJSON();
const jsonRestored = HyperbolaPayoutCurvePiece.fromJSON(
json.hyperbolaPayoutCurvePiece,
);
// This may not be exactly equal due to JSON number precision limits
const jsonValue = jsonRestored.translateOutcome.toNumber();
// The important thing is that both are very large numbers in the same ballpark
expect(jsonValue).to.be.greaterThan(1e99);
});
});
});