Skip to content

Commit

Permalink
Merge pull request #113 from Concordium/serialize-type-value
Browse files Browse the repository at this point in the history
Add function to serialize parameters with the direct type schemas
  • Loading branch information
shjortConcordium authored Jan 5, 2023
2 parents 900ae77 + f462041 commit 20dea03
Show file tree
Hide file tree
Showing 15 changed files with 259 additions and 17 deletions.
8 changes: 8 additions & 0 deletions packages/common/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,13 @@
# Changelog

## 6.2.0 2023-01-04

### Added

- `serializeTypeValue` that allows smart contract types to be serialized using the specific schema, instead of only by providing the entire module's schema.
- `getInitContractParameterSchema` Given a buffer containing the schema for a module, extract the schema for a given contract's init function's parameters.
- `getReceiveContractParameterSchema` Given a buffer containing the schema for a module, extract the schema for a given contract's receive methods' parameters.

## 6.1.0 2022-11-30

### Added
Expand Down
20 changes: 20 additions & 0 deletions packages/common/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -447,6 +447,26 @@ const updateContractTransaction: AccountTransaction = {
```
Finally, to actually update the contract on the chain, send the constructed `updateContractTransaction` to the chain using `sendAccountTransaction`. (See [Send Account Transaction](#Send-Account-Transaction) for how to do this)

## Serialize parameters with only the specific type's schema
In the previous section the schema used was assumed to be the schema for an entire module. In some cases one might want to use a schema containing only the specific type of the parameter.

For this, the function `serializeTypeValue` can used.
```
const inputParams = serializeTypeValue(userInput, rawTypeSchema);
```

For reference, the type schema for parameters can be extracted using the functions `getInitContractParameterSchema` and `getUpdateContractParameterSchema`.

```
const rawTypeSchema = getUpdateContractParameterSchema(
rawModuleSchema,
contractName,
receiveFunctionName,
userInput,
schemaVersion
)
```

# Utility functions

## Generate account alias
Expand Down
4 changes: 2 additions & 2 deletions packages/common/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@concordium/common-sdk",
"version": "6.1.0",
"version": "6.2.0",
"license": "Apache-2.0",
"engines": {
"node": ">=14.16.0"
Expand Down Expand Up @@ -39,7 +39,7 @@
"build": "tsc"
},
"dependencies": {
"@concordium/rust-bindings": "0.8.0",
"@concordium/rust-bindings": "0.9.0",
"@noble/ed25519": "^1.7.1",
"@scure/bip39": "^1.1.0",
"bs58check": "^2.1.2",
Expand Down
2 changes: 2 additions & 0 deletions packages/common/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ export {
serializeAccountTransactionForSubmission,
serializeCredentialDeploymentTransactionForSubmission,
getSignedCredentialDeploymentTransactionHash,
serializeTypeValue,
} from './serialization';
export { sha256 };
export { CredentialRegistrationId } from './types/CredentialRegistrationId';
Expand Down Expand Up @@ -42,6 +43,7 @@ export * from './accountHelpers';
export * from './blockSummaryHelpers';
export * from './rewardStatusHelpers';
export * from './HdWallet';
export * from './schemaHelpers';

export { isHex } from './util';

Expand Down
56 changes: 56 additions & 0 deletions packages/common/src/schemaHelpers.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
import { Buffer } from 'buffer/';
import { SchemaVersion } from './types';
import * as wasm from '@concordium/rust-bindings';

/**
* @param moduleSchema buffer for the schema of a module that contains the contract
* @param contractName name of the contract that the init contract transaction will initialize
* @param schemaVersion the version of the schema provided
* @returns buffer containing the schema for of init contract parameters
*/
export function getInitContractParameterSchema(
moduleSchema: Buffer,
contractName: string,
schemaVersion?: SchemaVersion
): Buffer {
const parameterSchema = wasm.getInitContractParameterSchema(
moduleSchema.toString('hex'),
contractName,
schemaVersion
);
try {
return Buffer.from(parameterSchema, 'hex');
} catch (e) {
throw new Error(
'unable to get parameter schema, due to: ' + parameterSchema
); // In this case parameterSchema is the error message from the rust module
}
}

/**
* @param moduleSchema buffer for the schema of a module that contains the contract
* @param contractName name of the contract that the update contract transaction will update
* @param receiveFunctionName name of function that the update contract transaction will invoke
* @param schemaVersion the version of the schema provided
* @returns buffer containing the schema for of update contract parameters
*/
export function getUpdateContractParameterSchema(
moduleSchema: Buffer,
contractName: string,
receiveFunctionName: string,
schemaVersion?: SchemaVersion
): Buffer {
const parameterSchema = wasm.getReceiveContractParameterSchema(
moduleSchema.toString('hex'),
contractName,
receiveFunctionName,
schemaVersion
);
try {
return Buffer.from(parameterSchema, 'hex');
} catch (e) {
throw new Error(
'unable to get parameter schema, due to: ' + parameterSchema
); // In this case parameterSchema is the error message from the rust module
}
}
31 changes: 27 additions & 4 deletions packages/common/src/serialization.ts
Original file line number Diff line number Diff line change
Expand Up @@ -362,7 +362,7 @@ export function getCredentialForExistingAccountSignDigest(

/**
* Returns the digest of the credential deployment transaction that has to be signed.
* @param credentialDeploymentTransaction the credential deployment transaction
* @param credentialDeployment the credential deployment transaction
* @returns the sha256 of the serialized unsigned credential deployment information
*/
export function getCredentialDeploymentSignDigest(
Expand Down Expand Up @@ -392,7 +392,7 @@ interface DeploymentDetailsResult {
/**
* Gets the transaction hash that is used to look up the status of a credential
* deployment transaction.
* @param credentialDeploymentTransaction the transaction to hash
* @param credentialDeployment the transaction to hash
* @param signatures the signatures that will also be part of the hash
* @returns the sha256 hash of the serialized block item kind, signatures, and credential deployment transaction
*/
Expand All @@ -413,7 +413,7 @@ export function getCredentialDeploymentTransactionHash(
/**
* Serializes a credential deployment transaction of a new account, so that it is ready for being
* submitted to the node.
* @param credentialDeploymentTransaction the credenetial deployment transaction
* @param credentialDeployment the credenetial deployment transaction
* @param signatures the signatures on the hash of unsigned credential deployment information
* @returns the serialization of the credential deployment transaction ready for being submitted to a node
*/
Expand Down Expand Up @@ -468,7 +468,6 @@ export function serializeInitContractParameters(
* @param schemaVersion the version of the schema provided
* @returns serialized buffer of update contract parameters
*/

export function serializeUpdateContractParameters(
contractName: string,
receiveFunctionName: string,
Expand All @@ -493,6 +492,30 @@ export function serializeUpdateContractParameters(
}
}

/**
* Given a value for a smart contract type, and the raw schema for that type, serialize the value into binary format.
* @param value the value that should be serialized. Should correspond to the JSON representation
* @param rawSchema the schema for the type that the given value should be serialized as
* @returns serialized buffer of the value
*/
export function serializeTypeValue(
// eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/explicit-module-boundary-types
value: any,
rawSchema: Buffer
): Buffer {
const serializedValue = wasm.serializeTypeValue(
JSON.stringify(value),
rawSchema.toString('hex')
);
try {
return Buffer.from(serializedValue, 'hex');
} catch (e) {
throw new Error(
'unable to deserialize value, due to: ' + serializedValue
); // In this case serializedValue is the error message from the rust module
}
}

function serializeSignedCredentialDeploymentDetails(
credentialDetails: SignedCredentialDeploymentDetails
): Buffer {
Expand Down
46 changes: 46 additions & 0 deletions packages/common/test/serialization.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import {
serializeAccountTransactionForSubmission,
serializeAccountTransactionSignature,
serializeUpdateContractParameters,
serializeTypeValue,
} from '../src/serialization';
import {
AccountTransaction,
Expand All @@ -23,6 +24,7 @@ import {
serializeULeb128,
} from '../src/serializationHelpers';
import { SizeLength } from '../src/deserializeSchema';
import { getUpdateContractParameterSchema } from '../src';

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

test('serialize type value and serializeUpdateContractParameters give same result', () => {
const parameters = [
{
token_id: [],
amount: [200, 0],
from: {
Account: ['4RgTGQhg1Y8DAUkC2TpZsKmXdicArDqY9gcgJmBDECg4kkYNg4'],
},
to: {
Account: ['3UiNwnmZ64YR423uamgZyY8RnRkD88tfn6SYtKzvWZCkyFdN94'],
},
data: [],
},
];
const fullSchema = Buffer.from(
fs.readFileSync('./test/resources/cis2-nft-schema.bin')
);
const schemaVersion = 1;
const contractName = 'CIS2-NFT';
const functionName = 'transfer';

const serializedParameter = serializeUpdateContractParameters(
contractName,
functionName,
parameters,
fullSchema,
schemaVersion
);

const serializedType = serializeTypeValue(
parameters,
getUpdateContractParameterSchema(
fullSchema,
contractName,
functionName,
schemaVersion
)
);

expect(serializedParameter.toString('hex')).toEqual(
serializedType.toString('hex')
);
});

test('serialize ULeb128', () => {
let parameter = serializeULeb128(
{ typeTag: ParameterType.ULeb128, constraint: 5 },
Expand Down
2 changes: 1 addition & 1 deletion packages/nodejs/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@
"build": "rm -rf grpc; mkdir -p grpc; yarn generate && tsc"
},
"dependencies": {
"@concordium/common-sdk": "6.1.0",
"@concordium/common-sdk": "6.2.0",
"@grpc/grpc-js": "^1.3.4",
"@protobuf-ts/grpc-transport": "2.8.1",
"buffer": "^6.0.3",
Expand Down
8 changes: 8 additions & 0 deletions packages/rust-bindings/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,13 @@
# Changelog

## 0.9.0 2023-1-4

### Added

- `serializeTypeValue`
- `getInitContractParameterSchema`
- `getReceiveContractParameterSchema`

## 0.8.0 2022-11-30

### Added
Expand Down
2 changes: 1 addition & 1 deletion packages/rust-bindings/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@concordium/rust-bindings",
"version": "0.8.0",
"version": "0.9.0",
"license": "Apache-2.0",
"engines": {
"node": ">=14.16.0"
Expand Down
39 changes: 39 additions & 0 deletions packages/rust-bindings/src/aux_functions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -700,6 +700,45 @@ pub fn serialize_init_contract_parameters_aux(
Ok(hex::encode(buf))
}

pub fn get_receive_contract_parameter_schema_aux(
schema: HexString,
contract_name: &str,
function_name: &str,
schema_version: Option<u8>,
) -> Result<HexString> {
let module_schema = VersionedModuleSchema::new(&hex::decode(schema)?, &schema_version)?;
let parameter_type = module_schema.get_receive_param_schema(contract_name, function_name)?;
Ok(hex::encode(concordium_contracts_common::to_bytes(
&parameter_type,
)))
}

pub fn get_init_contract_parameter_schema_aux(
schema: HexString,
contract_name: &str,
schema_version: Option<u8>,
) -> Result<HexString> {
let module_schema = VersionedModuleSchema::new(&hex::decode(schema)?, &schema_version)?;
let parameter_type = module_schema.get_init_param_schema(contract_name)?;
Ok(hex::encode(concordium_contracts_common::to_bytes(
&parameter_type,
)))
}

pub fn serialize_type_value_aux(parameters: JsonString, schema: HexString) -> Result<HexString> {
let parameter_type: Type = from_bytes(&hex::decode(schema)?)?;
serialize_type_value(parameters, parameter_type)
}

fn serialize_type_value(raw_value: JsonString, value_type: Type) -> Result<HexString> {
let value: SerdeValue = serde_json::from_str(&raw_value)?;

let mut buf: Vec<u8> = vec![];
Type::write_bytes_from_json_schema_type(&value_type, &value, &mut buf)?;

Ok(hex::encode(buf))
}

#[derive(SerdeSerialize, SerdeDeserialize)]
#[serde(rename_all = "camelCase")]
pub struct IdProofInput {
Expand Down
34 changes: 34 additions & 0 deletions packages/rust-bindings/src/external_functions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,7 @@ pub fn serialize_receive_contract_parameters(
Err(e) => format!("{}", e),
}
}

#[wasm_bindgen(js_name = serializeInitContractParameters)]
pub fn serialize_init_contract_parameters(
parameters: JsonString,
Expand All @@ -125,6 +126,39 @@ pub fn serialize_init_contract_parameters(
}
}

#[wasm_bindgen(js_name = getReceiveContractParameterSchema)]
pub fn get_receive_contract_parameter_schema_ext(
schema: HexString,
contract_name: &str,
function_name: &str,
schema_version: Option<u8>,
) -> String {
error_to_string(get_receive_contract_parameter_schema_aux(
schema,
contract_name,
function_name,
schema_version,
))
}

#[wasm_bindgen(js_name = getInitContractParameterSchema)]
pub fn get_init_contract_parameter_schema_ext(
schema: HexString,
contract_name: &str,
schema_version: Option<u8>,
) -> String {
error_to_string(get_init_contract_parameter_schema_aux(
schema,
contract_name,
schema_version,
))
}

#[wasm_bindgen(js_name = serializeTypeValue)]
pub fn serialize_type_value_ext(value: JsonString, value_type: HexString) -> String {
error_to_string(serialize_type_value_aux(value, value_type))
}

#[wasm_bindgen(js_name = createIdRequestV1)]
pub fn create_id_request_v1_ext(input: &str) -> String {
match create_id_request_v1_aux(serde_json::from_str(input).unwrap()) {
Expand Down
Loading

0 comments on commit 20dea03

Please sign in to comment.