Skip to content

Commit

Permalink
fix(core): plutus integers are now decoded correctly from indefinite …
Browse files Browse the repository at this point in the history
…length array form
  • Loading branch information
AngelCastilloB committed Sep 27, 2024
1 parent dd8b52f commit cc51d17
Show file tree
Hide file tree
Showing 4 changed files with 96 additions and 6 deletions.
4 changes: 2 additions & 2 deletions packages/core/src/Serialization/CBOR/CborReader.ts
Original file line number Diff line number Diff line change
Expand Up @@ -878,15 +878,15 @@ export class CborReader {
let concat = Buffer.from([]);
let encodingLength = 0;

let i = 1; // skip the indefinite-length initial byte
let i = 1; // skip offset + the indefinite-length initial byte

let nextInitialByte = CborReader.#peekNextInitialByte(data.slice(i), type);

while (nextInitialByte.getInitialByte() !== CborInitialByte.IndefiniteLengthBreakByte) {
const { length: chunkLength, bytesRead } = CborReader.#peekDefiniteLength(nextInitialByte, data.slice(i));
const payloadSize = bytesRead + Number(chunkLength);

concat = Buffer.concat([concat, this.#data.slice(i + (payloadSize - chunkLength), i + payloadSize)]);
concat = Buffer.concat([concat, data.slice(i + (payloadSize - chunkLength), i + payloadSize)]);

i += payloadSize;

Expand Down
42 changes: 38 additions & 4 deletions packages/core/src/Serialization/PlutusData/PlutusData.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ const MAX_WORD64 = 18_446_744_073_709_551_615n;
const INDEFINITE_BYTE_STRING = new Uint8Array([95]);
const MAX_BYTE_STRING_CHUNK_SIZE = 64;
const HASH_LENGTH_IN_BYTES = 32;
const MINUS_ONE = BigInt(-1);

/**
* A type corresponding to the Plutus Core Data datatype.
Expand All @@ -37,7 +38,7 @@ export class PlutusData {
*
* @returns The CBOR representation of this instance as a Uint8Array.
*/
// eslint-disable-next-line complexity
// eslint-disable-next-line complexity, sonarjs/cognitive-complexity, max-statements
toCbor(): HexBlob {
if (this.#originalBytes) return this.#originalBytes;

Expand Down Expand Up @@ -92,9 +93,22 @@ export class PlutusData {
) {
writer.writeInt(this.#integer!);
} else {
// Otherwise, it would be encoded as a bignum anyway, so we manually do the bignum
// encoding with a bytestring inside.
writer.writeBigInteger(this.#integer!);
const serializedBigint = PlutusData.bigintToBuffer(this.#integer!);

writer.writeTag(this.#integer! < 0 ? CborTag.NegativeBigNum : CborTag.UnsignedBigNum);

if (serializedBigint.length <= MAX_BYTE_STRING_CHUNK_SIZE) {
writer.writeByteString(serializedBigint);
} else {
writer.writeEncodedValue(INDEFINITE_BYTE_STRING);

for (let i = 0; i < serializedBigint.length; i += MAX_BYTE_STRING_CHUNK_SIZE) {
const chunk = serializedBigint.slice(i, i + MAX_BYTE_STRING_CHUNK_SIZE);
writer.writeByteString(chunk);
}

writer.writeEndArray();
}
}

cbor = bytesToHex(writer.encode());
Expand Down Expand Up @@ -468,4 +482,24 @@ export class PlutusData {
}
return ret;
}

/**
* Converts a bigint to an Uint8Array.
*
* @param value The bigint to serialize.
* @returns The Uint8Array with the bigint serialized data.
*/
private static bigintToBuffer(value: bigint): Uint8Array {
if (value < 0) {
value = -value + MINUS_ONE;
}

let str = value.toString(16);

if (str.length % 2) {
str = `0${str}`;
}

return Buffer.from(str, 'hex');
}
}
14 changes: 14 additions & 0 deletions packages/core/test/Serialization/CBOR/CborReader.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -374,6 +374,20 @@ describe('CborReader', () => {
'64676273786767746f6768646a7074657476746b636f6376796669647171676775726a687268716169697370717275656c687679707178656577707279667677'
);
expect(reader.peekState()).toBe(CborReaderState.Finished);

reader = new CborReader(
HexBlob(
'5f584037d34fac60a7dd2edba0c76fa58862c91c45ff4298e9134ba8e76be9a7513d88865bfdb9315073dc2690b0f2b59a232fbfa0a8a504df6ee9bb78e3f33fbdfef95529c9e74ff30ffe1bd1cc5795c37535899dba800000ff'
)
);

expect(reader.peekState()).toBe(CborReaderState.StartIndefiniteLengthByteString);
array = reader.readByteString();
expect(array.length).toEqual(85);
expect(Buffer.from(array).toString('hex')).toEqual(
'37d34fac60a7dd2edba0c76fa58862c91c45ff4298e9134ba8e76be9a7513d88865bfdb9315073dc2690b0f2b59a232fbfa0a8a504df6ee9bb78e3f33fbdfef929c9e74ff30ffe1bd1cc5795c37535899dba800000'
);
expect(reader.peekState()).toBe(CborReaderState.Finished);
});
});

Expand Down
42 changes: 42 additions & 0 deletions packages/core/test/Serialization/PlutusData.test.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
/* eslint-disable max-lines */
import { Cardano, Serialization } from '../../src';
import { HexBlob } from '@cardano-sdk/util';

Expand Down Expand Up @@ -66,6 +67,18 @@ describe('PlutusData', () => {
const data = Serialization.PlutusData.fromCbor(HexBlob('3bffffffffffffffff'));
expect(data.asInteger()).toEqual(-18_446_744_073_709_551_616n);
});

it('can decode a positive tagged indefinite length unbounded int', () => {
const data = Serialization.PlutusData.fromCbor(
HexBlob(
'c25f584037d34fac60a7dd2edba0c76fa58862c91c45ff4298e9134ba8e76be9a7513d88865bfdb9315073dc2690b0f2b59a232fbfa0a8a504df6ee9bb78e3f33fbdfef95529c9e74ff30ffe1bd1cc5795c37535899dba800000ff'
)
);
expect(data.asInteger()).toEqual(
// eslint-disable-next-line max-len
1_093_929_156_918_367_016_766_069_563_027_239_416_446_778_893_307_251_997_971_794_948_729_105_062_347_369_330_146_869_223_033_199_554_831_433_128_491_376_164_494_134_119_896_793_625_745_623_928_731_109_781_036_903_510_617_119_765_359_815_723_399_113_165_600_284_443_934_720n
);
});
});

describe('Bytes', () => {
Expand Down Expand Up @@ -130,6 +143,35 @@ describe('PlutusData', () => {
);
expect([...data.asBoundedBytes()!]).toEqual(payload);
});

it('can decode/encode a list of big integers', () => {
// Arrange

const expectedPlutusData: Cardano.PlutusList = {
items: [
1_093_929_156_918_367_016_766_069_563_027_239_416_446_778_893_307_251_997_971_794_948_729_105_062_347_369_330_146_869_223_033_199_554_831_433_128_491_376_164_494_134_119_896_793_625_745_623_928_731_109_781_036_903_510_617_119_765_359_815_723_399_113_165_600_284_443_934_720n,

Check warning on line 152 in packages/core/test/Serialization/PlutusData.test.ts

View workflow job for this annotation

GitHub Actions / build_and_test (ubuntu-20.04)

This line has a length of 285. Maximum allowed is 120
2_768_491_094_397_106_413_284_351_268_798_781_278_061_973_163_918_667_373_508_176_781_108_678_876_832_888_565_950_388_553_255_499_815_619_207_549_146_245_084_281_150_783_450_096_035_638_439_655_721_496_227_482_399_093_555_200_000_000_000_000_000_000_000_000_000_000_000_000n,

Check warning on line 153 in packages/core/test/Serialization/PlutusData.test.ts

View workflow job for this annotation

GitHub Actions / build_and_test (ubuntu-20.04)

This line has a length of 285. Maximum allowed is 120
2_768_491_094_397_106_413_284_351_268_798_781_278_061_973_163_918_667_373_508_176_781_108_678_876_832_888_565_950_388_553_255_499_815_619_207_549_146_245_084_281_150_783_450_096_035_638_439_655_721_496_227_482_399_093_555_200_000_000_000_000_000_000_000_000_000_000_000_000n,

Check warning on line 154 in packages/core/test/Serialization/PlutusData.test.ts

View workflow job for this annotation

GitHub Actions / build_and_test (ubuntu-20.04)

This line has a length of 285. Maximum allowed is 120
1_127_320_948_699_467_529_606_464_548_687_160_198_167_487_105_208_190_997_153_720_362_564_942_186_550_892_230_582_242_980_573_812_448_057_150_419_530_802_096_156_402_677_128_058_112_319_272_573_039_196_273_296_535_693_983_366_369_964_092_325_725_072_645_646_768_416_006_720n,

Check warning on line 155 in packages/core/test/Serialization/PlutusData.test.ts

View workflow job for this annotation

GitHub Actions / build_and_test (ubuntu-20.04)

This line has a length of 285. Maximum allowed is 120
678_966_618_629_088_994_577_385_052_394_593_905_048_788_216_453_653_741_455_475_012_343_328_029_630_393_478_083_358_655_655_534_689_789_017_294_468_365_725_065_895_808_744_013_442_165_812_351_180_871_208_842_081_615_673_249_725_577_503_335_455_257_844_242_272_891_195_840n,

Check warning on line 156 in packages/core/test/Serialization/PlutusData.test.ts

View workflow job for this annotation

GitHub Actions / build_and_test (ubuntu-20.04)

This line has a length of 283. Maximum allowed is 120
1_337_829_155_615_373_710_780_861_189_358_723_839_738_261_900_670_472_008_493_768_766_460_943_065_914_931_970_040_774_692_071_540_815_257_661_221_428_415_268_570_880_739_215_388_841_910_028_989_315_213_224_986_535_176_632_464_067_341_466_233_795_236_134_699_058_357_952_960n,

Check warning on line 157 in packages/core/test/Serialization/PlutusData.test.ts

View workflow job for this annotation

GitHub Actions / build_and_test (ubuntu-20.04)

This line has a length of 285. Maximum allowed is 120
45_981_213_582_240_091_300_385_870_382_262_347_274_104_141_060_516_509_284_758_089_043_905_194_449_918_733_499_912_740_694_341_485_053_723_341_097_850_038_365_519_925_374_324_306_213_051_881_991_025_304_309_829_953_615_052_414_155_047_559_800_693_983_587_151_987_253_760n,

Check warning on line 158 in packages/core/test/Serialization/PlutusData.test.ts

View workflow job for this annotation

GitHub Actions / build_and_test (ubuntu-20.04)

This line has a length of 282. Maximum allowed is 120
2_413_605_787_847_473_064_058_493_109_882_761_763_812_632_923_885_676_112_901_376_523_745_345_875_592_342_323_079_462_001_682_936_368_998_782_686_824_629_943_810_471_167_748_859_099_323_567_551_094_056_876_663_897_197_968_204_837_564_889_906_128_763_937_156_053n

Check warning on line 159 in packages/core/test/Serialization/PlutusData.test.ts

View workflow job for this annotation

GitHub Actions / build_and_test (ubuntu-20.04)

This line has a length of 272. Maximum allowed is 120
]
};

const expectedCbor = HexBlob(
'9fc25f584037d34fac60a7dd2edba0c76fa58862c91c45ff4298e9134ba8e76be9a7513d88865bfdb9315073dc2690b0f2b59a232fbfa0a8a504df6ee9bb78e3f33fbdfef95529c9e74ff30ffe1bd1cc5795c37535899dba800000ffc25f58408d4820519e9bba2d6556c87b100709082f4c8958769899eb5d288b6f9ea9e0723df7211959860edea5829c9732422d25962e3945c68a6089f50a18b0114248b7555feea4851e9f099180600000000000000000000000ffc25f58408d4820519e9bba2d6556c87b100709082f4c8958769899eb5d288b6f9ea9e0723df7211959860edea5829c9732422d25962e3945c68a6089f50a18b0114248b7555feea4851e9f099180600000000000000000000000ffc25f584039878c5f4d4063e9a2ee75a3fbdd1492c3cad46f4ecbae977ac94b709a730e367edf9dae05acd59638d1dec25e2351c2eecb871694afae979de7085b522efe1355634138bbd920200d574cdf400324cdd1aafe10a240ffc25f584022a6282a7d960570c4c729decd677ec617061f0e501249c41f8724c89dc97dc0d24917bdb7a7ebd7c079c1c56fa21af0f119168966356ea384fb711cb766015e55bfc5bc86583f6a82ae605a93e7bf974ae74cd051c0ffc25f58404445ab8649611ee8f74a3c31e504a2f25f2f7631ef6ef828a405542904d84c997304b1b332d528ee54873b03cfb73cd3c5b35b91184f6846afccec7271bda8a05563ba46aed8c82611da47fd608d027447f8391161c0ffc25f58400258b535c4d4a22a483b22b2f5c5c65bed9e7de59266f6bbaa8997edf5bec6bb5d203641bb58d8ade1a3a5b4e5f923df502cf1e47691865fe1984eacef3be96a551ed585e070265db203a8866726bed053cb6c8aa200ffc25f5840021104310667ec434e9e2cd9fa71853593c42e1b55865ac49f80b2ea22beeec9b4a55e9545055a2bcde3a78d36836df11df0f91c1dae9a8aee58419b8650bc6c529361f9601a4005051b045d05f39a5f00ebd5ffff'
);

// Act
const actualPlutusData = Serialization.PlutusData.fromCbor(expectedCbor);
const actualCbor = Serialization.PlutusData.fromCore(expectedPlutusData).toCbor();

// Assert
expect((actualPlutusData.toCore() as Cardano.PlutusList).items).toEqual(expectedPlutusData.items);
expect(actualCbor).toEqual(expectedCbor);
});
});

describe('List', () => {
Expand Down

0 comments on commit cc51d17

Please sign in to comment.