Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion packages/core/src/Serialization/CBOR/CborReader.ts
Original file line number Diff line number Diff line change
Expand Up @@ -886,7 +886,7 @@ export class CborReader {
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)]);
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🥲

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-len */
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,
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,
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,
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,
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,
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,
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,
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
]
};

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
Loading