Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix parameter serialization and deserialization #263

Merged
merged 8 commits into from
Sep 14, 2023
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
14 changes: 14 additions & 0 deletions packages/common/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,19 @@
# Changelog

## Unreleased

### Breaking changes

- The following functions now parse using `json-bigint` meaning that they return bigints instead of numbers _for all numbers no matter size_
- `deserializeReceiveReturnValue`
- `deserializeReceiveError`
- `deserializeInitError`
- `deserializeTypeValue`

### Added

- All JSON serialization in `serialization.ts` is now handled by `json-bigint` meaning that all functions now correctly handles bigint inputs

## 9.4.0

### Added
Expand Down
21 changes: 17 additions & 4 deletions packages/common/src/deserialization.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import {
import { AccountAddress } from './types/accountAddress';
import { TransactionExpiry } from './types/transactionExpiry';
import { PassThrough, Readable } from 'stream';
import JSONbig from 'json-bigint/';

/**
* Reads an unsigned 8-bit integer from the given {@link Readable}.
Expand Down Expand Up @@ -242,7 +243,10 @@ export function deserializeReceiveReturnValue(
verboseErrorMessage
);
try {
return JSON.parse(deserializedReturnValue);
return JSONbig({
alwaysParseAsBig: true,
useNativeBigInt: true,
}).parse(deserializedReturnValue);
} catch (e) {
throw new Error(
'unable to deserialize the return value, due to: ' +
Expand Down Expand Up @@ -275,7 +279,10 @@ export function deserializeReceiveError(
verboseErrorMessage
);
try {
return JSON.parse(deserializedError);
return JSONbig({
alwaysParseAsBig: true,
useNativeBigInt: true,
}).parse(deserializedError);
} catch (e) {
throw new Error(
'unable to deserialize the error value, due to: ' +
Expand Down Expand Up @@ -305,7 +312,10 @@ export function deserializeInitError(
verboseErrorMessage
);
try {
return JSON.parse(deserializedError);
return JSONbig({
alwaysParseAsBig: true,
useNativeBigInt: true,
}).parse(deserializedError);
} catch (e) {
throw new Error(
'unable to deserialize the error value, due to: ' +
Expand All @@ -331,5 +341,8 @@ export function deserializeTypeValue(
rawSchema.toString('hex'),
verboseErrorMessage
);
return JSON.parse(deserializedValue);
return JSONbig({
alwaysParseAsBig: true,
useNativeBigInt: true,
}).parse(deserializedValue);
}
13 changes: 7 additions & 6 deletions packages/common/src/serialization.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ import { countSignatures } from './util';
import { AccountAddress } from './types/accountAddress';
import { sha256 } from './hash';
import * as wasm from '@concordium/rust-bindings';
import JSONbig from 'json-bigint/';

function serializeAccountTransactionType(type: AccountTransactionType): Buffer {
return Buffer.from(Uint8Array.of(type));
Expand Down Expand Up @@ -426,7 +427,7 @@ export function getCredentialDeploymentTransactionHash(
const credentialDeploymentInfo: DeploymentDetailsResult = JSON.parse(
wasm.getDeploymentDetails(
signatures,
JSON.stringify(credentialDeployment.unsignedCdi),
JSONbig.stringify(credentialDeployment.unsignedCdi),
credentialDeployment.expiry.expiryEpochSeconds
)
);
Expand All @@ -447,7 +448,7 @@ export function serializeCredentialDeploymentTransactionForSubmission(
const credentialDeploymentInfo: DeploymentDetailsResult = JSON.parse(
wasm.getDeploymentDetails(
signatures,
JSON.stringify(credentialDeployment.unsignedCdi),
JSONbig.stringify(credentialDeployment.unsignedCdi),
credentialDeployment.expiry.expiryEpochSeconds
)
);
Expand All @@ -471,7 +472,7 @@ export function serializeInitContractParameters(
verboseErrorMessage = false
): Buffer {
const serializedParameters = wasm.serializeInitContractParameters(
JSON.stringify(parameters),
JSONbig.stringify(parameters),
rawSchema.toString('hex'),
contractName,
schemaVersion,
Expand Down Expand Up @@ -499,7 +500,7 @@ export function serializeUpdateContractParameters(
verboseErrorMessage = false
): Buffer {
const serializedParameters = wasm.serializeReceiveContractParameters(
JSON.stringify(parameters),
JSONbig.stringify(parameters),
rawSchema.toString('hex'),
contractName,
receiveFunctionName,
Expand All @@ -523,7 +524,7 @@ export function serializeTypeValue(
verboseErrorMessage = false
): Buffer {
const serializedValue = wasm.serializeTypeValue(
JSON.stringify(value),
JSONbig.stringify(value),
rawSchema.toString('hex'),
verboseErrorMessage
);
Expand Down Expand Up @@ -583,7 +584,7 @@ export function serializeCredentialDeploymentPayload(
): Buffer {
const payloadByteArray = wasm.serializeCredentialDeploymentPayload(
signatures,
JSON.stringify(credentialDeploymentTransaction.unsignedCdi)
JSONbig.stringify(credentialDeploymentTransaction.unsignedCdi)
);
return Buffer.from(payloadByteArray);
}
22 changes: 18 additions & 4 deletions packages/common/test/deserialization.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,11 @@ import {
TEST_CONTRACT_SCHEMA,
TEST_CONTRACT_RECEIVE_ERROR_SCHEMA,
AUCTION_WITH_ERRORS_VIEW_RETURN_VALUE_SCHEMA,
TEST_CONTRACT_U64,
} from './resources/schema';

const U64_MAX = 18446744073709551615n;

test('test that deserializeContractState works', () => {
const state = deserializeContractState(
'PiggyBank',
Expand Down Expand Up @@ -196,6 +199,17 @@ test('Return value can be deserialized - auction using deserializeTypeValue', (
expectAuctionReturnValue(returnValue);
});

test('U64_MAX can be deserialized', () => {
const returnVal = deserializeReceiveReturnValue(
Buffer.from('ffffffffffffffff', 'hex'),
Buffer.from(TEST_CONTRACT_U64, 'base64'),
'test',
'receive'
);

expect(returnVal).toEqual(U64_MAX);
});

test('Receive error can be deserialized', () => {
const error = deserializeReceiveError(
Buffer.from('ffff', 'hex'),
Expand All @@ -204,7 +218,7 @@ test('Receive error can be deserialized', () => {
'receive_function'
);

expect(error).toEqual(-1);
expect(error).toEqual(-1n);
});

/**
Expand All @@ -215,7 +229,7 @@ test('Receive error can be deserialized using deserializeTypeValue', () => {
Buffer.from('ffff', 'hex'),
Buffer.from(TEST_CONTRACT_RECEIVE_ERROR_SCHEMA, 'base64')
);
expect(error).toEqual(-1);
expect(error).toEqual(-1n);
});

test('Init error can be deserialized', () => {
Expand All @@ -225,7 +239,7 @@ test('Init error can be deserialized', () => {
'TestContract'
);

expect(error).toEqual(1);
expect(error).toEqual(1n);
});

/**
Expand All @@ -236,7 +250,7 @@ test('Init error can be deserialized using deserializeTypeValue', () => {
Buffer.from('0100', 'hex'),
Buffer.from(TEST_CONTRACT_INIT_ERROR_SCHEMA, 'base64')
);
expect(error).toEqual(1);
expect(error).toEqual(1n);
});

test('Test parsing of Token Addresses', () => {
Expand Down
4 changes: 4 additions & 0 deletions packages/common/test/resources/schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,7 @@ export const TEST_CONTRACT_RECEIVE_ERROR_SCHEMA = 'Bw';
export const TEST_CONTRACT_INIT_ERROR_SCHEMA = 'Aw';
export const AUCTION_WITH_ERRORS_VIEW_RETURN_VALUE_SCHEMA =
'FAAEAAAADQAAAGF1Y3Rpb25fc3RhdGUVAgAAAAoAAABOb3RTb2xkWWV0AgQAAABTb2xkAQEAAAALDgAAAGhpZ2hlc3RfYmlkZGVyFQIAAAAEAAAATm9uZQIEAAAAU29tZQEBAAAACwQAAABpdGVtFgIDAAAAZW5kDQ';

// contract: "test", init = (param = u64), receive = (name: "receive", param = u64, return = u64)
export const TEST_CONTRACT_U64 =
'//8DAQAAAAQAAAB0ZXN0AQAFAQAAAAcAAAByZWNlaXZlAgUFAA==';
44 changes: 43 additions & 1 deletion packages/common/test/serialization.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,13 @@ import {
SimpleTransferPayload,
} from '../src/types';
import { TransactionExpiry } from '../src/types/transactionExpiry';
import { getUpdateContractParameterSchema } from '../src';
import {
getUpdateContractParameterSchema,
serializeInitContractParameters,
} from '../src';
import { TEST_CONTRACT_U64 } from './resources/schema';

const U64_MAX = 18446744073709551615n;

test('fail account transaction serialization if no signatures', () => {
const simpleTransferPayload: SimpleTransferPayload = {
Expand Down Expand Up @@ -94,6 +100,42 @@ test('serialize UpdateContractParameters using CIS2 contract', () => {
);
});

test('Parameter serialization works for U64_MAX', () => {
const updateParam = serializeUpdateContractParameters(
'test',
'receive',
U64_MAX,
Buffer.from(TEST_CONTRACT_U64, 'base64')
);
const initParam = serializeInitContractParameters(
'test',
U64_MAX,
Buffer.from(TEST_CONTRACT_U64, 'base64')
);
expect(updateParam.toString('hex')).toEqual('ffffffffffffffff');
expect(initParam.toString('hex')).toEqual('ffffffffffffffff');
});

test('Parameter serialization errors on (U64_MAX + 1)', () => {
const errMsg =
'Unable to serialize parameters, due to: Unsigned integer required';
const updateParam = () =>
serializeUpdateContractParameters(
'test',
'receive',
U64_MAX + 1n,
Buffer.from(TEST_CONTRACT_U64, 'base64')
);
const initParam = () =>
serializeInitContractParameters(
'test',
U64_MAX + 1n,
Buffer.from(TEST_CONTRACT_U64, 'base64')
);
expect(updateParam).toThrow(errMsg);
expect(initParam).toThrow(errMsg);
});

test('serialize UpdateContractParameters using CIS2 contract and incorrect name', () => {
const parameter = function () {
serializeUpdateContractParameters(
Expand Down