From f25946dc8bd716f5f699a787e9591385c0dec173 Mon Sep 17 00:00:00 2001 From: Abner Zheng Date: Mon, 4 Jan 2021 23:21:36 +0800 Subject: [PATCH 01/22] ECO-790: keep raw clValue. Signed-off-by: Abner Zheng --- packages/sdk/src/lib/CLValue.ts | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/packages/sdk/src/lib/CLValue.ts b/packages/sdk/src/lib/CLValue.ts index 1a2f2394..6ebcfe39 100644 --- a/packages/sdk/src/lib/CLValue.ts +++ b/packages/sdk/src/lib/CLValue.ts @@ -4,7 +4,6 @@ import { toBytesBytesArray, toBytesNumber, toBytesString, - toBytesStringList, toBytesU32, toBytesVecT } from './byterepr'; @@ -1295,10 +1294,14 @@ export class CLValue implements ToBytes { /** * Please use static methods to constructs a new `CLValue` */ - private constructor(private bytes: ByteArray, private clType: CLType) {} + private constructor(public value: CLTypedAndToBytes, public clType: CLType) {} + + public get clValueBytes() { + return this.value.toBytes(); + } public static fromT(v: T) { - return new CLValue(v.toBytes(), v.clType()); + return new CLValue(v, v.clType()); } /** @@ -1306,7 +1309,7 @@ export class CLValue implements ToBytes { */ public toBytes() { return concat([ - toBytesArrayU8(this.bytes), + toBytesArrayU8(this.clValueBytes), CLTypeHelper.toBytesHelper(this.clType) ]); } @@ -1320,7 +1323,8 @@ export class CLValue implements ToBytes { if (clTypeRes.hasError()) { return Result.Err(clTypeRes.error); } - const clValue = new CLValue(bytesRes.value.rawBytes, clTypeRes.value); + const v = fromBytesByCLType(clTypeRes.value, bytesRes.value.rawBytes); + const clValue = new CLValue(v.value, clTypeRes.value); return Result.Ok(clValue, clTypeRes.remainder); } @@ -1377,10 +1381,10 @@ export class CLValue implements ToBytes { }; public static stringList = (strings: string[]) => { - return new CLValue( - toBytesStringList(strings), - CLTypeHelper.list(SimpleType.String) + const v = CLTypedAndToBytesHelper.list( + strings.map(s => CLTypedAndToBytesHelper.string(s)) ); + return new CLValue(v, CLTypeHelper.list(SimpleType.String)); }; public static list(vec: T[]) { From 946f1e60cdb8e44c6881e0d60e4655ef3aa57590 Mon Sep 17 00:00:00 2001 From: Abner Zheng Date: Mon, 4 Jan 2021 23:23:16 +0800 Subject: [PATCH 02/22] ECO-790: add CLValue to StoredValue. Signed-off-by: Abner Zheng --- packages/sdk/src/lib/StoredValue.ts | 48 ++++++++++++++++++++--------- 1 file changed, 34 insertions(+), 14 deletions(-) diff --git a/packages/sdk/src/lib/StoredValue.ts b/packages/sdk/src/lib/StoredValue.ts index 994e03d9..24a22f02 100644 --- a/packages/sdk/src/lib/StoredValue.ts +++ b/packages/sdk/src/lib/StoredValue.ts @@ -1,4 +1,5 @@ import { jsonArrayMember, jsonMember, jsonObject } from 'typedjson'; +import { CLValue } from './CLValue'; @jsonObject class NamedKey { @@ -29,7 +30,7 @@ class ActionThresholds { * Structure representing a user's account, stored in global state. */ @jsonObject -class SAccount { +class AccountJson { get accountHash(): string { return this._accountHash; } @@ -47,7 +48,25 @@ class SAccount { } @jsonObject -export class STransfer { +class CLValueJson { + @jsonMember({ name: 'cl_type', constructor: String }) + public clTypeStr: string; + + @jsonMember({ name: 'serialized_bytes', constructor: String }) + public serializedBytes: string; + + public clValue?: CLValue; + + @jsonMember({ + name: 'parsed_to_json', + constructor: String, + preserveNull: true + }) + public parsedToJson: string | null; +} + +@jsonObject +export class TransferJson { // Deploy that created the transfer @jsonMember({ name: 'deploy_hash', constructor: String }) public deployHash: string; @@ -78,7 +97,7 @@ export class STransfer { } @jsonObject -export class SDeployInfo { +export class DeployInfoJson { // The relevant Deploy. @jsonMember({ name: 'deploy_hash', constructor: String }) public deployHash: string; @@ -147,18 +166,19 @@ export class SeigniorageAllocation { * Auction metdata. Intended to be recorded at each era. */ @jsonObject -export class EraInfo { +export class EraInfoJson { @jsonArrayMember(SeigniorageAllocation, { name: 'seigniorage_allocations' }) public seigniorageAllocations: SeigniorageAllocation[]; } @jsonObject export class StoredValue { - // todo SCLValue; - + // StoredValue + @jsonMember({ constructor: CLValueJson }) + public CLValue?: CLValueJson; // An account - @jsonMember({ constructor: SAccount }) - public Account?: SAccount; + @jsonMember({ constructor: AccountJson }) + public Account?: AccountJson; // A contract's Wasm @jsonMember({ constructor: String }) @@ -173,13 +193,13 @@ export class StoredValue { public ContractPackage?: string; // A record of a transfer - @jsonMember({ constructor: STransfer }) - public Transfer?: STransfer; + @jsonMember({ constructor: TransferJson }) + public Transfer?: TransferJson; // A record of a deploy - @jsonMember({ constructor: SDeployInfo }) - public DeployInfo?: SDeployInfo; + @jsonMember({ constructor: DeployInfoJson }) + public DeployInfo?: DeployInfoJson; - @jsonMember({ constructor: EraInfo }) - public EraInfo?: EraInfo; + @jsonMember({ constructor: EraInfoJson }) + public EraInfo?: EraInfoJson; } From b18b316dd4d3db2da21ac35eec1cc5998f48f182 Mon Sep 17 00:00:00 2001 From: Abner Zheng Date: Mon, 4 Jan 2021 23:24:11 +0800 Subject: [PATCH 03/22] ECO-790: update getBlockState to support parsing StoredValue. Signed-off-by: Abner Zheng --- .../src/services/CasperServiceByJsonRPC.ts | 23 +++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/packages/sdk/src/services/CasperServiceByJsonRPC.ts b/packages/sdk/src/services/CasperServiceByJsonRPC.ts index af38178c..966438b8 100644 --- a/packages/sdk/src/services/CasperServiceByJsonRPC.ts +++ b/packages/sdk/src/services/CasperServiceByJsonRPC.ts @@ -1,6 +1,8 @@ import Client, { HTTPTransport, RequestManager } from 'rpc-client-js'; -import { DeployUtil, encodeBase16, PublicKey } from '..'; +import { CLValue, decodeBase16, DeployUtil, encodeBase16, PublicKey } from '..'; import { deployToJson } from '../lib/DeployUtil'; +import { TypedJSON } from 'typedjson'; +import { StoredValue } from '../lib/StoredValue'; interface RpcResult { api_version: string; @@ -270,7 +272,7 @@ export class CasperServiceByJsonRPC { key: string, path: string[] ) { - return await this.client.request({ + const res = await this.client.request({ method: 'state_get_item', params: { state_root_hash: stateRootHash, @@ -278,6 +280,23 @@ export class CasperServiceByJsonRPC { path } }); + if (res.error) { + return res; + } else { + const storedValueJson = res.result.stored_value; + const serializer = new TypedJSON(StoredValue); + const storedValue = serializer.parse(storedValueJson); + + if (storedValue!.CLValue) { + const clValue = CLValue.fromBytes( + decodeBase16(storedValue!.CLValue.serializedBytes) + ); + storedValue!.CLValue.clValue = clValue.value; + return storedValue; + } else { + return storedValue; + } + } } public async deploy(signedDeploy: DeployUtil.Deploy) { From d1a19c12eda88ced8b0f83e41501d03c0cb9a6c3 Mon Sep 17 00:00:00 2001 From: Abner Zheng Date: Tue, 5 Jan 2021 20:58:19 +0800 Subject: [PATCH 04/22] ECO-790: fix typo. Signed-off-by: Abner Zheng --- packages/sdk/src/lib/CLValue.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/sdk/src/lib/CLValue.ts b/packages/sdk/src/lib/CLValue.ts index 6ebcfe39..c88f9310 100644 --- a/packages/sdk/src/lib/CLValue.ts +++ b/packages/sdk/src/lib/CLValue.ts @@ -376,7 +376,7 @@ class U256 extends NumberCoder { } @staticImplements>() -class U512 extends NumberCoder { +export class U512 extends NumberCoder { constructor(n: BigNumberish) { super(512, false, n); } @@ -1195,8 +1195,8 @@ class ByteArrayValue extends CLTypedAndToBytes { if (u32Res.remainder.length < size) { return Result.Err(FromBytesError.EarlyEndOfStream); } - const b = new ByteArrayValue(u32Res.remainder.subarray(0, length)); - const rem = u32Res.remainder.subarray(length); + const b = new ByteArrayValue(u32Res.remainder.subarray(0, size)); + const rem = u32Res.remainder.subarray(size); return Result.Ok(b, rem); } } From fddfa70b677a1f41ca92cabebf75247839d431de Mon Sep 17 00:00:00 2001 From: Abner Zheng Date: Tue, 5 Jan 2021 20:59:10 +0800 Subject: [PATCH 05/22] ECO-790: use custom deserializer method to simplify code. Signed-off-by: Abner Zheng --- packages/sdk/src/lib/StoredValue.ts | 22 ++++++++++++------ .../src/services/CasperServiceByJsonRPC.ts | 23 ++++++------------- 2 files changed, 22 insertions(+), 23 deletions(-) diff --git a/packages/sdk/src/lib/StoredValue.ts b/packages/sdk/src/lib/StoredValue.ts index 24a22f02..9399e846 100644 --- a/packages/sdk/src/lib/StoredValue.ts +++ b/packages/sdk/src/lib/StoredValue.ts @@ -1,5 +1,6 @@ import { jsonArrayMember, jsonMember, jsonObject } from 'typedjson'; import { CLValue } from './CLValue'; +import { decodeBase16 } from './Conversions'; @jsonObject class NamedKey { @@ -50,19 +51,26 @@ class AccountJson { @jsonObject class CLValueJson { @jsonMember({ name: 'cl_type', constructor: String }) - public clTypeStr: string; + public typeStr: string; - @jsonMember({ name: 'serialized_bytes', constructor: String }) - public serializedBytes: string; - - public clValue?: CLValue; + @jsonMember({ + name: 'serialized_bytes', + deserializer: (b: string) => { + const res = CLValue.fromBytes(decodeBase16(b)); + if (res.hasError()) { + throw res.error; + } + return res.value; + } + }) + public value: CLValue; @jsonMember({ name: 'parsed_to_json', - constructor: String, + deserializer: v => v, preserveNull: true }) - public parsedToJson: string | null; + public parsedToJson: string | number | null; } @jsonObject diff --git a/packages/sdk/src/services/CasperServiceByJsonRPC.ts b/packages/sdk/src/services/CasperServiceByJsonRPC.ts index 966438b8..a91bc4b0 100644 --- a/packages/sdk/src/services/CasperServiceByJsonRPC.ts +++ b/packages/sdk/src/services/CasperServiceByJsonRPC.ts @@ -1,5 +1,5 @@ import Client, { HTTPTransport, RequestManager } from 'rpc-client-js'; -import { CLValue, decodeBase16, DeployUtil, encodeBase16, PublicKey } from '..'; +import { DeployUtil, encodeBase16, PublicKey } from '..'; import { deployToJson } from '../lib/DeployUtil'; import { TypedJSON } from 'typedjson'; import { StoredValue } from '../lib/StoredValue'; @@ -216,8 +216,8 @@ export class CasperServiceByJsonRPC { stateRootHash, 'account-hash-' + accountHash, [] - ).then(res => res.stored_value.Account); - return account.main_purse; + ).then(res => res.Account!); + return account.mainPurse; } /** @@ -271,7 +271,7 @@ export class CasperServiceByJsonRPC { stateRootHash: string, key: string, path: string[] - ) { + ): Promise { const res = await this.client.request({ method: 'state_get_item', params: { @@ -283,19 +283,10 @@ export class CasperServiceByJsonRPC { if (res.error) { return res; } else { - const storedValueJson = res.result.stored_value; + const storedValueJson = res.stored_value; const serializer = new TypedJSON(StoredValue); - const storedValue = serializer.parse(storedValueJson); - - if (storedValue!.CLValue) { - const clValue = CLValue.fromBytes( - decodeBase16(storedValue!.CLValue.serializedBytes) - ); - storedValue!.CLValue.clValue = clValue.value; - return storedValue; - } else { - return storedValue; - } + const storedValue = serializer.parse(storedValueJson)!; + return storedValue; } } From 6eb50675577e5ddafcf3f0f8f50a7d00a4b1a3b0 Mon Sep 17 00:00:00 2001 From: Abner Zheng Date: Mon, 11 Jan 2021 18:57:29 +0800 Subject: [PATCH 06/22] NO_TICKET: expose args, and add isTransfer helper function --- packages/sdk/src/lib/Contracts.ts | 14 +--- packages/sdk/src/lib/DeployUtil.ts | 81 ++++++++++++------- packages/sdk/src/lib/RuntimeArgs.ts | 23 ++++-- .../containers/DeployContractsContainer.ts | 11 +-- 4 files changed, 74 insertions(+), 55 deletions(-) diff --git a/packages/sdk/src/lib/Contracts.ts b/packages/sdk/src/lib/Contracts.ts index 03774289..8d4c8f17 100644 --- a/packages/sdk/src/lib/Contracts.ts +++ b/packages/sdk/src/lib/Contracts.ts @@ -2,10 +2,10 @@ import blake from 'blakejs'; import * as fs from 'fs'; import { PublicKey } from '../index'; import * as DeployUtil from './DeployUtil'; +import { DeployParams } from './DeployUtil'; import { RuntimeArgs } from './RuntimeArgs'; -import { CLValue, AccountHash, KeyValue } from './CLValue'; +import { AccountHash, CLValue, KeyValue } from './CLValue'; import { AsymmetricKey } from './Keys'; -import { DeployParams } from './DeployUtil'; // https://www.npmjs.com/package/tweetnacl-ts // https://github.com/dcposch/blakejs @@ -53,18 +53,12 @@ export class Contract { signingKeyPair: AsymmetricKey, chainName: string ): DeployUtil.Deploy { - const session = new DeployUtil.ModuleBytes( - this.sessionWasm, - args.toBytes() - ); + const session = new DeployUtil.ModuleBytes(this.sessionWasm, args); const paymentArgs = RuntimeArgs.fromMap({ amount: CLValue.u512(paymentAmount.toString()) }); - const payment = new DeployUtil.ModuleBytes( - this.paymentWasm, - paymentArgs.toBytes() - ); + const payment = new DeployUtil.ModuleBytes(this.paymentWasm, paymentArgs); const deploy = DeployUtil.makeDeploy( new DeployParams(accountPublicKey, chainName), diff --git a/packages/sdk/src/lib/DeployUtil.ts b/packages/sdk/src/lib/DeployUtil.ts index 5ff676c8..d9b09ed6 100644 --- a/packages/sdk/src/lib/DeployUtil.ts +++ b/packages/sdk/src/lib/DeployUtil.ts @@ -159,15 +159,25 @@ interface ToJson { export abstract class ExecutableDeployItem implements ToBytes, ToJson { public abstract tag: number; + public abstract args: RuntimeArgs; + public abstract toBytes(): ByteArray; public abstract toJson(): Record; + + public isTransfer(): boolean { + return this.tag === 5; + } + + public getArgByName(argName: string): CLValue | undefined { + return this.args.args[argName]; + } } export class ModuleBytes extends ExecutableDeployItem { public tag = 0; - constructor(private moduleBytes: Uint8Array, private args: Uint8Array) { + constructor(public moduleBytes: Uint8Array, public args: RuntimeArgs) { super(); } @@ -175,7 +185,7 @@ export class ModuleBytes extends ExecutableDeployItem { return concat([ Uint8Array.from([this.tag]), toBytesArrayU8(this.moduleBytes), - toBytesArrayU8(this.args) + toBytesArrayU8(this.args.toBytes()) ]); } @@ -183,7 +193,7 @@ export class ModuleBytes extends ExecutableDeployItem { return { ModuleBytes: { module_bytes: encodeBase16(this.moduleBytes), - args: encodeBase16(this.args) + args: encodeBase16(this.args.toBytes()) } }; } @@ -193,9 +203,9 @@ export class StoredContractByHash extends ExecutableDeployItem { public tag = 1; constructor( - private hash: Uint8Array, - private entryPoint: string, - private args: Uint8Array + public hash: Uint8Array, + public entryPoint: string, + public args: RuntimeArgs ) { super(); } @@ -205,7 +215,7 @@ export class StoredContractByHash extends ExecutableDeployItem { Uint8Array.from([this.tag]), toBytesBytesArray(this.hash), toBytesString(this.entryPoint), - toBytesArrayU8(this.args) + toBytesArrayU8(this.args.toBytes()) ]); } @@ -214,7 +224,7 @@ export class StoredContractByHash extends ExecutableDeployItem { StoredContractByHash: { hash: encodeBase16(this.hash), entry_point: this.entryPoint, - args: encodeBase16(this.args) + args: encodeBase16(this.args.toBytes()) } }; } @@ -224,9 +234,9 @@ export class StoredContractByName extends ExecutableDeployItem { public tag = 2; constructor( - private name: string, - private entryPoint: string, - private args: Uint8Array + public name: string, + public entryPoint: string, + public args: RuntimeArgs ) { super(); } @@ -236,7 +246,7 @@ export class StoredContractByName extends ExecutableDeployItem { Uint8Array.from([this.tag]), toBytesString(this.name), toBytesString(this.entryPoint), - toBytesArrayU8(this.args) + toBytesArrayU8(this.args.toBytes()) ]); } @@ -245,7 +255,7 @@ export class StoredContractByName extends ExecutableDeployItem { StoredContractByName: { name: this.name, entry_point: this.entryPoint, - args: encodeBase16(this.args) + args: encodeBase16(this.args.toBytes()) } }; } @@ -255,10 +265,10 @@ export class StoredVersionedContractByName extends ExecutableDeployItem { public tag = 4; constructor( - private name: string, - private version: number | null, - private entryPoint: string, - private args: Uint8Array + public name: string, + public version: number | null, + public entryPoint: string, + public args: RuntimeArgs ) { super(); } @@ -275,7 +285,7 @@ export class StoredVersionedContractByName extends ExecutableDeployItem { toBytesString(this.name), serializedVersion.toBytes(), toBytesString(this.entryPoint), - toBytesArrayU8(this.args) + toBytesArrayU8(this.args.toBytes()) ]); } @@ -284,21 +294,27 @@ export class StoredVersionedContractByName extends ExecutableDeployItem { StoredVersionedContractByName: { name: this.name, entry_point: this.entryPoint, - args: encodeBase16(this.args) + args: encodeBase16(this.args.toBytes()) } }; } } export class StoredVersionedContractByHash extends ExecutableDeployItem { - public hash: Uint8Array; - public version: number | null; - public entryPoint: string; - public args: ByteArray; public tag = 3; + constructor( + public hash: Uint8Array, + public version: number | null, + public entryPoint: string, + public args: RuntimeArgs + ) { + super(); + } + public toBytes(): ByteArray { let serializedVersion; + if (this.version === null) { serializedVersion = new Option(null, CLTypeHelper.u32()); } else { @@ -309,7 +325,7 @@ export class StoredVersionedContractByHash extends ExecutableDeployItem { toBytesBytesArray(this.hash), serializedVersion.toBytes(), toBytesString(this.entryPoint), - toBytesArrayU8(this.args) + toBytesArrayU8(this.args.toBytes()) ]); } @@ -319,14 +335,14 @@ export class StoredVersionedContractByHash extends ExecutableDeployItem { hash: encodeBase16(this.hash), version: this.version, entry_point: this.entryPoint, - args: encodeBase16(this.args) + args: encodeBase16(this.args.toBytes()) } }; } } export class Transfer extends ExecutableDeployItem { - public args: ByteArray; + public args: RuntimeArgs; public tag = 5; /** @@ -344,7 +360,7 @@ export class Transfer extends ExecutableDeployItem { id: number | null = null ) { super(); - const runtimeArgs = new RuntimeArgs([]); + const runtimeArgs = RuntimeArgs.fromMap({}); runtimeArgs.insert('amount', CLValue.u512(amount)); if (sourcePurse) { runtimeArgs.insert('source', CLValue.uref(sourcePurse)); @@ -364,16 +380,19 @@ export class Transfer extends ExecutableDeployItem { CLValue.option(CLTypedAndToBytesHelper.u64(id), CLTypeHelper.u64()) ); } - this.args = runtimeArgs.toBytes(); + this.args = runtimeArgs; } public toBytes(): ByteArray { - return concat([Uint8Array.from([this.tag]), toBytesArrayU8(this.args)]); + return concat([ + Uint8Array.from([this.tag]), + toBytesArrayU8(this.args.toBytes()) + ]); } public toJson(): Record { return { - Transfer: { args: encodeBase16(this.args) } + Transfer: { args: encodeBase16(this.args.toBytes()) } }; } } @@ -528,7 +547,7 @@ export const standardPayment = (paymentAmount: bigint | JSBI) => { amount: CLValue.u512(paymentAmount.toString()) }); - return new ModuleBytes(Uint8Array.from([]), paymentArgs.toBytes()); + return new ModuleBytes(Uint8Array.from([]), paymentArgs); }; /** diff --git a/packages/sdk/src/lib/RuntimeArgs.ts b/packages/sdk/src/lib/RuntimeArgs.ts index acf6b470..84a34817 100644 --- a/packages/sdk/src/lib/RuntimeArgs.ts +++ b/packages/sdk/src/lib/RuntimeArgs.ts @@ -14,20 +14,29 @@ export class NamedArg implements ToBytes { } export class RuntimeArgs implements ToBytes { - constructor(private args: NamedArg[]) {} + constructor(public args: Record) {} public static fromMap(args: Record) { - const vec = Object.keys(args).map(a => { - return new NamedArg(a, args[a]); - }); - return new RuntimeArgs(vec); + return new RuntimeArgs(args); + } + + public static fromNamedArgs(namedArgs: NamedArg[]) { + const args = namedArgs.reduce>((pre, cur) => { + pre[cur.name] = cur.value; + return pre; + }, {}); + return RuntimeArgs.fromMap(args); } public insert(key: string, value: CLValue) { - this.args.push(new NamedArg(key, value)); + this.args[key] = value; } public toBytes() { - return toBytesVecT(this.args); + const vec = Object.keys(this.args).map(a => { + return new NamedArg(a, this.args[a]); + }); + + return toBytesVecT(vec); } } diff --git a/packages/ui/src/containers/DeployContractsContainer.ts b/packages/ui/src/containers/DeployContractsContainer.ts index af1b5800..cfe7a6e2 100644 --- a/packages/ui/src/containers/DeployContractsContainer.ts +++ b/packages/ui/src/containers/DeployContractsContainer.ts @@ -362,7 +362,7 @@ export class DeployContractsContainer { const args = deployArguments.value; let session: ByteArray | string; - let runtimeArgs = new RuntimeArgs( + let runtimeArgs = RuntimeArgs.fromNamedArgs( args.map((arg: FormState) => { return DeployArgumentParser.buildArgument(arg); }) @@ -372,17 +372,14 @@ export class DeployContractsContainer { if (config.contractType.value === DeployUtil.ContractType.WASM) { session = this.selectedFileContent!; - sessionExecutionItem = new DeployUtil.ModuleBytes( - session, - runtimeArgs.toBytes() - ); + sessionExecutionItem = new DeployUtil.ModuleBytes(session, runtimeArgs); } else if (config.contractType.value === DeployUtil.ContractType.Hash) { session = decodeBase16(config.contractHash.value); const entryPoint = config.entryPoint.value; sessionExecutionItem = new DeployUtil.StoredContractByHash( session, entryPoint, - runtimeArgs.toBytes() + runtimeArgs ); } else if (config.contractType.value === DeployUtil.ContractType.Name) { session = config.contractName.value; @@ -390,7 +387,7 @@ export class DeployContractsContainer { sessionExecutionItem = new DeployUtil.StoredContractByName( session, entryPoint, - runtimeArgs.toBytes() + runtimeArgs ); } From 1a6039c42e968fa2dfd07f05447aea618fcd115c Mon Sep 17 00:00:00 2001 From: Abner Zheng Date: Wed, 13 Jan 2021 22:32:43 +0800 Subject: [PATCH 07/22] ECO-822: expose isTransfer and isStandardPayment via Deploy --- packages/sdk/src/lib/DeployUtil.ts | 69 ++++++++++++++---------------- 1 file changed, 33 insertions(+), 36 deletions(-) diff --git a/packages/sdk/src/lib/DeployUtil.ts b/packages/sdk/src/lib/DeployUtil.ts index d9b09ed6..29a748f9 100644 --- a/packages/sdk/src/lib/DeployUtil.ts +++ b/packages/sdk/src/lib/DeployUtil.ts @@ -8,14 +8,7 @@ import blake from 'blakejs'; import { Option } from './option'; import { encodeBase16 } from './Conversions'; import humanizeDuration from 'humanize-duration'; -import { - CLTypedAndToBytesHelper, - CLTypeHelper, - CLValue, - PublicKey, - ToBytes, - U32 -} from './CLValue'; +import { CLTypedAndToBytesHelper, CLTypeHelper, CLValue, PublicKey, ToBytes, U32 } from './CLValue'; import { toBytesArrayU8, toBytesBytesArray, @@ -95,7 +88,8 @@ export interface DeployHeader { * The cryptographic hash of a Deploy. */ class DeployHash implements ToBytes { - constructor(private hash: ByteArray) {} + constructor(private hash: ByteArray) { + } public toBytes(): ByteArray { return toBytesDeployHash(this.hash); @@ -121,27 +115,34 @@ const toBytesDeployHeader = (deployHeader: DeployHeader) => { /** * A deploy containing a smart contract along with the requester's signature(s). */ -export interface Deploy { - /** - * The DeployHash identifying this Deploy - */ - hash: ByteArray; - /** - * The deployHeader - */ - header: DeployHeader; +export class Deploy { /** - * The ExecutableDeployItem for payment code. + * + * @param hash The DeployHash identifying this Deploy + * @param header The deployHeader + * @param payment The ExecutableDeployItem for payment code. + * @param session the ExecutableDeployItem for session code. + * @param approvals An array of signature and public key of the signers, who approve this deploy */ - payment: ExecutableDeployItem; - /** - * the ExecutableDeployItem for session code. - */ - session: ExecutableDeployItem; - /** - * An array of signature and public key of the signers, who approve this deploy - */ - approvals: Approval[]; + constructor( + public hash: ByteArray, + public header: DeployHeader, + public payment: ExecutableDeployItem, + public session: ExecutableDeployItem, + public approvals: Approval[]) { + } + + public isTransfer(): boolean { + return this.session.tag === 5; + } + + public isStandardPayment(): boolean { + if (this.payment.tag === 0) { + const paymentModuleBytes = this.payment as ModuleBytes; + return paymentModuleBytes.moduleBytes.length === 0; + } + return false; + } } /** @@ -165,10 +166,6 @@ export abstract class ExecutableDeployItem implements ToBytes, ToJson { public abstract toJson(): Record; - public isTransfer(): boolean { - return this.tag === 5; - } - public getArgByName(argName: string): CLValue | undefined { return this.args.args[argName]; } @@ -476,13 +473,13 @@ export function makeDeploy( }; const serializedHeader = serializeHeader(header); const deployHash = blake.blake2b(serializedHeader, null, 32); - return { - hash: deployHash, + return new Deploy( + deployHash, header, payment, session, - approvals: [] - }; + [] + ); } /** From b289d9eefd3064a74073d490f9cfcaf88750b8f9 Mon Sep 17 00:00:00 2001 From: Abner Zheng Date: Thu, 14 Jan 2021 00:08:38 +0800 Subject: [PATCH 08/22] fix bug --- packages/server/src/StoredFaucetService.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/server/src/StoredFaucetService.ts b/packages/server/src/StoredFaucetService.ts index 4b244892..00fa6389 100644 --- a/packages/server/src/StoredFaucetService.ts +++ b/packages/server/src/StoredFaucetService.ts @@ -26,7 +26,7 @@ export class StoredFaucetService { const sessionArgs = CallFaucet.args( accountPublicKeyHash, this.transferAmount - ).toBytes(); + ); const session = new DeployUtil.StoredContractByName( CONTRACT_NAME, ENTRY_POINT_NAME, From 14e0c4b5f3533d43a5248523038b21692023dba5 Mon Sep 17 00:00:00 2001 From: Abner Zheng Date: Thu, 14 Jan 2021 00:11:19 +0800 Subject: [PATCH 09/22] upgrade husky --- packages/signer/package.json | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/packages/signer/package.json b/packages/signer/package.json index 5aebe405..7906113d 100644 --- a/packages/signer/package.json +++ b/packages/signer/package.json @@ -83,10 +83,7 @@ "trailingComma": "none" }, "lint-staged": { - "src/**/*.{js,jsx,ts,tsx,json,css,scss,md}": [ - "prettier --write", - "git add" - ] + "src/**/*.{js,jsx,ts,tsx,json,css,scss,md}": "prettier --write" }, "devDependencies": { "@types/file-saver": "^2.0.1", From 186facbb618ebfeab1f3eb46f08cae31cecfb24e Mon Sep 17 00:00:00 2001 From: Abner Zheng Date: Thu, 14 Jan 2021 23:38:53 +0800 Subject: [PATCH 10/22] ECO-822: implement jsonToDeploy --- packages/sdk/src/lib/CasperClient.ts | 26 +- packages/sdk/src/lib/Contracts.ts | 9 +- packages/sdk/src/lib/DeployUtil.ts | 694 +++++++++++++++++++-------- packages/sdk/src/lib/RuntimeArgs.ts | 39 +- 4 files changed, 557 insertions(+), 211 deletions(-) diff --git a/packages/sdk/src/lib/CasperClient.ts b/packages/sdk/src/lib/CasperClient.ts index 8269d6ab..67a18e1b 100644 --- a/packages/sdk/src/lib/CasperClient.ts +++ b/packages/sdk/src/lib/CasperClient.ts @@ -1,18 +1,7 @@ -import { - AccountDeploy, - CasperServiceByJsonRPC, - DeployResult, - EventService, - TransferResult -} from '../services'; +import { AccountDeploy, CasperServiceByJsonRPC, DeployResult, EventService, TransferResult } from '../services'; import { DeployUtil, Keys, PublicKey } from './index'; import { encodeBase16 } from './Conversions'; -import { - Deploy, - DeployParams, - ExecutableDeployItem, - Transfer -} from './DeployUtil'; +import { Deploy, DeployParams, ExecutableDeployItem, ExecutableDeployItemJsonWrapper, Transfer } from './DeployUtil'; import { AsymmetricKey, SignatureAlgorithm } from './Keys'; import { CasperHDKey } from './CasperHDKey'; @@ -137,7 +126,7 @@ export class CasperClient { session: ExecutableDeployItem, payment: ExecutableDeployItem ): Deploy { - return DeployUtil.makeDeploy(deployParams, session, payment); + return DeployUtil.makeDeploy(deployParams, ExecutableDeployItemJsonWrapper.fromExecutionDeployItem(session), ExecutableDeployItemJsonWrapper.fromExecutionDeployItem(payment)); } /** @@ -165,6 +154,15 @@ export class CasperClient { return DeployUtil.deployToJson(deploy); } + /** + * Convert the json to deploy object + * + * @param json + */ + public jsonToDeploy(json: any) { + return DeployUtil.jsonToDeploy(json); + }; + /** * Construct the deploy for transfer purpose * diff --git a/packages/sdk/src/lib/Contracts.ts b/packages/sdk/src/lib/Contracts.ts index 8d4c8f17..eda23ea7 100644 --- a/packages/sdk/src/lib/Contracts.ts +++ b/packages/sdk/src/lib/Contracts.ts @@ -2,7 +2,7 @@ import blake from 'blakejs'; import * as fs from 'fs'; import { PublicKey } from '../index'; import * as DeployUtil from './DeployUtil'; -import { DeployParams } from './DeployUtil'; +import { DeployParams, ExecutableDeployItemJsonWrapper } from './DeployUtil'; import { RuntimeArgs } from './RuntimeArgs'; import { AccountHash, CLValue, KeyValue } from './CLValue'; import { AsymmetricKey } from './Keys'; @@ -53,12 +53,12 @@ export class Contract { signingKeyPair: AsymmetricKey, chainName: string ): DeployUtil.Deploy { - const session = new DeployUtil.ModuleBytes(this.sessionWasm, args); + const session = ExecutableDeployItemJsonWrapper.newModuleBytes(this.sessionWasm, args); const paymentArgs = RuntimeArgs.fromMap({ amount: CLValue.u512(paymentAmount.toString()) }); - const payment = new DeployUtil.ModuleBytes(this.paymentWasm, paymentArgs); + const payment = ExecutableDeployItemJsonWrapper.newModuleBytes(this.paymentWasm, paymentArgs); const deploy = DeployUtil.makeDeploy( new DeployParams(accountPublicKey, chainName), @@ -76,7 +76,8 @@ export class BoundContract { constructor( private contract: Contract, private contractKeyPair: AsymmetricKey - ) {} + ) { + } public deploy( args: RuntimeArgs, diff --git a/packages/sdk/src/lib/DeployUtil.ts b/packages/sdk/src/lib/DeployUtil.ts index 29a748f9..f96983da 100644 --- a/packages/sdk/src/lib/DeployUtil.ts +++ b/packages/sdk/src/lib/DeployUtil.ts @@ -6,7 +6,7 @@ import { concat } from '@ethersproject/bytes'; import blake from 'blakejs'; import { Option } from './option'; -import { encodeBase16 } from './Conversions'; +import { decodeBase16, encodeBase16 } from './Conversions'; import humanizeDuration from 'humanize-duration'; import { CLTypedAndToBytesHelper, CLTypeHelper, CLValue, PublicKey, ToBytes, U32 } from './CLValue'; import { @@ -22,6 +22,7 @@ import JSBI from 'jsbi'; import { Keys, URef } from './index'; import { AsymmetricKey, SignatureAlgorithm } from './Keys'; import { BigNumberish } from '@ethersproject/bignumber'; +import { jsonArrayMember, jsonMember, jsonObject, TypedJSON } from 'typedjson'; const shortEnglishHumanizer = humanizeDuration.humanizer({ spacer: '', @@ -42,6 +43,14 @@ const shortEnglishHumanizer = humanizeDuration.humanizer({ } }); +const byteArrayJsonSerializer: (bytes: ByteArray) => string = (bytes: ByteArray) => { + return encodeBase16(bytes); +}; + +const byteArrayJsonDeserializer: (str: string) => ByteArray = (str: string) => { + return decodeBase16(str); +}; + /** * Return a humanizer duration * @param ttl in milliseconds @@ -50,38 +59,86 @@ export const humanizerTTL = (ttl: number) => { return shortEnglishHumanizer(ttl); }; -/** - * The header portion of a Deploy - */ -export interface DeployHeader { - /** - * The account within which the deploy will be run. - */ - account: PublicKey; - /** - * When the deploy was created. - */ - timestamp: number; - /** - * How long the deploy will stay valid. - */ - ttl: number; - /** - * Price per gas unit for this deploy. - */ - gasPrice: number; - /** - * Hash of the Wasm code. - */ - bodyHash: ByteArray; - /** - * Other deploys that have to be run before this one. - */ - dependencies: ByteArray[]; +@jsonObject +export class DeployHeader implements ToBytes { + @jsonMember({ + serializer: (account: PublicKey) => { + return account.toAccountHex(); + }, + deserializer: (hexStr: string) => { + return PublicKey.fromHex(hexStr); + } + }) + public account: PublicKey; + + @jsonMember({ constructor: Number }) + public timestamp: number; + + @jsonMember({ constructor: Number }) + public ttl: number; + + @jsonMember({ constructor: Number, name: 'gas_price' }) + public gasPrice: number; + + @jsonMember({ + name: 'body_hash', + serializer: byteArrayJsonSerializer, + deserializer: byteArrayJsonDeserializer + }) + public bodyHash: ByteArray; + + @jsonArrayMember( + () => { + // @ts-ignore + }, { + serializer: byteArrayJsonSerializer, + deserializer: byteArrayJsonDeserializer + }) + public dependencies: ByteArray[]; + + @jsonMember({ name: 'chain_name', constructor: String }) + public chainName: string; + /** - * Which chain the deploy is supposed to be run on. + * The header portion of a Deploy + * + * @param account The account within which the deploy will be run. + * @param timestamp When the deploy was created. + * @param ttl How long the deploy will stay valid. + * @param gasPrice Price per gas unit for this deploy. + * @param bodyHash Hash of the Wasm code. + * @param dependencies Other deploys that have to be run before this one. + * @param chainName Which chain the deploy is supposed to be run on. */ - chainName: string; + constructor( + account: PublicKey, + timestamp: number, + ttl: number, + gasPrice: number, + bodyHash: ByteArray, + dependencies: ByteArray[], + chainName: string + ) { + this.account = account; + this.timestamp = timestamp; + this.ttl = ttl; + this.gasPrice = gasPrice; + this.bodyHash = bodyHash; + this.dependencies = dependencies; + this.chainName = chainName; + } + + public toBytes(): ByteArray { + return concat([ + this.account.toBytes(), + toBytesU64(this.timestamp), + toBytesU64(this.ttl), + toBytesU64(this.gasPrice), + toBytesDeployHash(this.bodyHash), + toBytesVecT(this.dependencies.map(d => new DeployHash(d))), + toBytesString(this.chainName) + ]); + } } /** @@ -96,86 +153,71 @@ class DeployHash implements ToBytes { } } -/** - * Serialized DeployHeader to an array of bytes - * @param deployHeader - */ -const toBytesDeployHeader = (deployHeader: DeployHeader) => { - return concat([ - deployHeader.account.toBytes(), - toBytesU64(deployHeader.timestamp), - toBytesU64(deployHeader.ttl), - toBytesU64(deployHeader.gasPrice), - toBytesDeployHash(deployHeader.bodyHash), - toBytesVecT(deployHeader.dependencies.map(d => new DeployHash(d))), - toBytesString(deployHeader.chainName) - ]); -}; - -/** - * A deploy containing a smart contract along with the requester's signature(s). - */ -export class Deploy { - /** - * - * @param hash The DeployHash identifying this Deploy - * @param header The deployHeader - * @param payment The ExecutableDeployItem for payment code. - * @param session the ExecutableDeployItem for session code. - * @param approvals An array of signature and public key of the signers, who approve this deploy - */ - constructor( - public hash: ByteArray, - public header: DeployHeader, - public payment: ExecutableDeployItem, - public session: ExecutableDeployItem, - public approvals: Approval[]) { - } - - public isTransfer(): boolean { - return this.session.tag === 5; - } - - public isStandardPayment(): boolean { - if (this.payment.tag === 0) { - const paymentModuleBytes = this.payment as ModuleBytes; - return paymentModuleBytes.moduleBytes.length === 0; - } - return false; - } +export interface DeployJson { + session: Record; + approvals: { signature: string; signer: string }[]; + header: DeployHeader; + payment: Record; + hash: string } + /** * A struct containing a signature and the public key of the signer. */ +@jsonObject export class Approval { + @jsonMember({ constructor: String }) public signer: string; + @jsonMember({ constructor: String }) public signature: string; } -interface ToJson { - toJson(): Record; -} - -export abstract class ExecutableDeployItem implements ToBytes, ToJson { +export abstract class ExecutableDeployItem implements ToBytes { public abstract tag: number; public abstract args: RuntimeArgs; public abstract toBytes(): ByteArray; - public abstract toJson(): Record; - public getArgByName(argName: string): CLValue | undefined { return this.args.args[argName]; } } +const argsSerializer = (args: RuntimeArgs) => encodeBase16(args.toBytes()); + +const argsDeserializer = (byteStr: string) => { + const argsRes = RuntimeArgs.fromBytes(decodeBase16(byteStr)); + if (argsRes.hasError()) { + throw new Error('Failed to deserialized RuntimeArgs'); + } + return argsRes.value; +}; + +@jsonObject export class ModuleBytes extends ExecutableDeployItem { public tag = 0; - constructor(public moduleBytes: Uint8Array, public args: RuntimeArgs) { + @jsonMember({ + name: 'module_bytes', + serializer: byteArrayJsonSerializer, + deserializer: byteArrayJsonDeserializer + }) + public moduleBytes: Uint8Array; + + @jsonMember({ + serializer: argsSerializer, + deserializer: argsDeserializer + } + ) + public args: RuntimeArgs; + + constructor(moduleBytes: Uint8Array, args: RuntimeArgs) { super(); + + this.moduleBytes = moduleBytes; + this.args = args; } public toBytes(): ByteArray { @@ -185,26 +227,40 @@ export class ModuleBytes extends ExecutableDeployItem { toBytesArrayU8(this.args.toBytes()) ]); } - - public toJson(): Record { - return { - ModuleBytes: { - module_bytes: encodeBase16(this.moduleBytes), - args: encodeBase16(this.args.toBytes()) - } - }; - } } +@jsonObject export class StoredContractByHash extends ExecutableDeployItem { public tag = 1; + @jsonMember({ + serializer: byteArrayJsonSerializer, + deserializer: byteArrayJsonDeserializer + }) + public hash: ByteArray; + + @jsonMember({ + name: 'entry_point', + constructor: String + }) + public entryPoint: string; + + @jsonMember({ + serializer: argsSerializer, + deserializer: argsDeserializer + }) + public args: RuntimeArgs; + constructor( - public hash: Uint8Array, - public entryPoint: string, - public args: RuntimeArgs + hash: ByteArray, + entryPoint: string, + args: RuntimeArgs ) { super(); + + this.entryPoint = entryPoint; + this.args = args; + this.hash = hash; } public toBytes(): ByteArray { @@ -215,27 +271,37 @@ export class StoredContractByHash extends ExecutableDeployItem { toBytesArrayU8(this.args.toBytes()) ]); } - - public toJson(): Record { - return { - StoredContractByHash: { - hash: encodeBase16(this.hash), - entry_point: this.entryPoint, - args: encodeBase16(this.args.toBytes()) - } - }; - } } +@jsonObject export class StoredContractByName extends ExecutableDeployItem { public tag = 2; + @jsonMember({ constructor: String }) + public name: string; + + @jsonMember({ + name: 'entry_point', + constructor: String + }) + public entryPoint: string; + + @jsonMember({ + serializer: argsSerializer, + deserializer: argsDeserializer + }) + public args: RuntimeArgs; + constructor( - public name: string, - public entryPoint: string, - public args: RuntimeArgs + name: string, + entryPoint: string, + args: RuntimeArgs ) { super(); + + this.name = name; + this.entryPoint = entryPoint; + this.args = args; } public toBytes(): ByteArray { @@ -246,28 +312,38 @@ export class StoredContractByName extends ExecutableDeployItem { toBytesArrayU8(this.args.toBytes()) ]); } - - public toJson(): Record { - return { - StoredContractByName: { - name: this.name, - entry_point: this.entryPoint, - args: encodeBase16(this.args.toBytes()) - } - }; - } } +@jsonObject export class StoredVersionedContractByName extends ExecutableDeployItem { public tag = 4; + @jsonMember({ constructor: String }) + public name: string; + + @jsonMember({ constructor: Number, preserveNull: true }) + public version: number | null; + + @jsonMember({ name: 'entry_point', constructor: String }) + public entryPoint: string; + + @jsonMember({ + serializer: argsSerializer, + deserializer: argsDeserializer + }) + public args: RuntimeArgs; + constructor( - public name: string, - public version: number | null, - public entryPoint: string, - public args: RuntimeArgs + name: string, + version: number | null, + entryPoint: string, + args: RuntimeArgs ) { super(); + this.name = name; + this.version = version; + this.entryPoint = entryPoint; + this.args = args; } public toBytes(): ByteArray { @@ -285,28 +361,48 @@ export class StoredVersionedContractByName extends ExecutableDeployItem { toBytesArrayU8(this.args.toBytes()) ]); } - - public toJson(): Record { - return { - StoredVersionedContractByName: { - name: this.name, - entry_point: this.entryPoint, - args: encodeBase16(this.args.toBytes()) - } - }; - } } +@jsonObject export class StoredVersionedContractByHash extends ExecutableDeployItem { public tag = 3; + @jsonMember({ + serializer: byteArrayJsonSerializer, + deserializer: byteArrayJsonDeserializer + }) + public hash: Uint8Array; + + + @jsonMember({ + constructor: Number, + preserveNull: true + }) + public version: number | null; + + @jsonMember({ + name: 'entry_point', + constructor: String + }) + public entryPoint: string; + + @jsonMember({ + serializer: argsSerializer, + deserializer: argsDeserializer + }) + public args: RuntimeArgs; + constructor( - public hash: Uint8Array, - public version: number | null, - public entryPoint: string, - public args: RuntimeArgs + hash: Uint8Array, + version: number | null, + entryPoint: string, + args: RuntimeArgs ) { super(); + this.hash = hash; + this.version = version; + this.entryPoint = entryPoint; + this.args = args; } public toBytes(): ByteArray { @@ -325,23 +421,18 @@ export class StoredVersionedContractByHash extends ExecutableDeployItem { toBytesArrayU8(this.args.toBytes()) ]); } - - public toJson(): Record { - return { - StoredVersionedContractByHash: { - hash: encodeBase16(this.hash), - version: this.version, - entry_point: this.entryPoint, - args: encodeBase16(this.args.toBytes()) - } - }; - } } +@jsonObject export class Transfer extends ExecutableDeployItem { - public args: RuntimeArgs; public tag = 5; + @jsonMember({ + serializer: argsSerializer, + deserializer: argsDeserializer + }) + public args: RuntimeArgs; + /** * Constructor for Transfer deploy item. * @param amount The number of motes to transfer @@ -386,11 +477,243 @@ export class Transfer extends ExecutableDeployItem { toBytesArrayU8(this.args.toBytes()) ]); } +} + +@jsonObject +export class ExecutableDeployItemJsonWrapper implements ToBytes { + + @jsonMember({ + name: 'ModuleBytes', + constructor: ModuleBytes + }) + public moduleBytes?: ModuleBytes; + + @jsonMember({ + name: 'StoredVersionedContractByHash', + constructor: StoredContractByHash + }) + public storedContractByHash?: StoredContractByHash; + + @jsonMember({ + name: 'StoredContractByName', + constructor: StoredContractByName + }) + public storedContractByName?: StoredContractByName; + + @jsonMember({ + name: 'StoredVersionedContractByHash', + constructor: StoredVersionedContractByHash + }) + public storedVersionedContractByHash?: StoredVersionedContractByHash; + + @jsonMember({ + name: 'StoredVersionedContractByName', + constructor: StoredVersionedContractByName + }) + public storedVersionedContractByName?: StoredVersionedContractByName; + @jsonMember({ + name: 'Transfer', + constructor: Transfer + }) + public transfer?: Transfer; + + public toBytes(): ByteArray { + if (this.isModuleBytes()) { + return this.moduleBytes!.toBytes(); + } else if (this.isStoredContractByHash()) { + return this.storedContractByHash!.toBytes(); + } else if (this.isStoredContractByName()) { + return this.storedContractByName!.toBytes(); + } else if (this.isStoredVersionContractByHash()) { + return this.storedVersionedContractByHash!.toBytes(); + } else if (this.isStoredVersionContractByName()) { + return this.storedVersionedContractByName!.toBytes(); + } else if (this.isTransfer()) { + return this.transfer!.toBytes(); + } + throw new Error('failed to serialize ExecutableDeployItemJsonWrapper'); + } + + public static fromExecutionDeployItem(item: ExecutableDeployItem) { + const res = new ExecutableDeployItemJsonWrapper(); + switch (item.tag) { + case 0: + res.moduleBytes = item as ModuleBytes; + break; + case 1: + res.storedContractByHash = item as StoredContractByHash; + break; + case 2: + res.storedContractByName = item as StoredContractByName; + break; + case 3: + res.storedVersionedContractByHash = item as StoredVersionedContractByHash; + break; + case 4: + res.storedVersionedContractByName = item as StoredVersionedContractByName; + break; + case 5: + res.transfer = item as Transfer; + break; + } + return res; + } + + public static newModuleBytes(moduleBytes: ByteArray, args: RuntimeArgs): ExecutableDeployItemJsonWrapper { + return ExecutableDeployItemJsonWrapper.fromExecutionDeployItem(new ModuleBytes(moduleBytes, args)); + } + + public static newStoredContractByHash( + hash: Uint8Array, + version: number | null, + entryPoint: string, + args: RuntimeArgs + ) { + return ExecutableDeployItemJsonWrapper.fromExecutionDeployItem(new StoredVersionedContractByHash(hash, version, entryPoint, args)); + } + + public static newStoredContractByName( + name: string, + entryPoint: string, + args: RuntimeArgs + ) { + return ExecutableDeployItemJsonWrapper.fromExecutionDeployItem(new StoredContractByName(name, entryPoint, args)); + } + + public static newStoredVersionContractByHash( + hash: Uint8Array, + version: number | null, + entryPoint: string, + args: RuntimeArgs + ) { + return ExecutableDeployItemJsonWrapper.fromExecutionDeployItem(new StoredVersionedContractByHash(hash, version, entryPoint, args)); + } + + public static newStoredVersionContractByName( + name: string, + version: number | null, + entryPoint: string, + args: RuntimeArgs + ) { + return ExecutableDeployItemJsonWrapper.fromExecutionDeployItem(new StoredVersionedContractByName(name, version, entryPoint, args)); + } + + public static newTransfer( + amount: BigNumberish, + target: URef | PublicKey, + sourcePurse?: URef, + id: number | null = null + ) { + return ExecutableDeployItemJsonWrapper.fromExecutionDeployItem(new Transfer(amount, target, sourcePurse, id)); + } + + public isModuleBytes(): boolean { + return !!this.moduleBytes; + } + + public asModuleBytes(): ModuleBytes | undefined { + return this.moduleBytes; + } + + public isStoredContractByHash(): boolean { + return !!this.storedContractByHash; + } + + public asStoredContractByHash(): StoredContractByHash | undefined { + return this.storedContractByHash; + } + + public isStoredContractByName(): boolean { + return !!this.storedContractByName; + } + + public asStoredContractByName(): StoredContractByName | undefined { + return this.storedContractByName; + } + + public isStoredVersionContractByName(): boolean { + return !!this.storedVersionedContractByName; + } - public toJson(): Record { - return { - Transfer: { args: encodeBase16(this.args.toBytes()) } - }; + public asStoredVersionContractByName(): StoredVersionedContractByName | undefined { + return this.storedVersionedContractByName; + } + + public isStoredVersionContractByHash(): boolean { + return !!this.storedVersionedContractByHash; + } + + public asStoredVersionContractByHash(): StoredVersionedContractByHash | undefined { + return this.storedVersionedContractByHash; + } + + public isTransfer() { + return !!this.transfer; + } + + public asTransfer(): Transfer | undefined { + return this.transfer; + } +} + +/** + * A deploy containing a smart contract along with the requester's signature(s). + */ +@jsonObject +export class Deploy { + @jsonMember({ + serializer: byteArrayJsonSerializer, + deserializer: byteArrayJsonDeserializer + }) + public hash: ByteArray; + + @jsonMember({ constructor: DeployHeader }) + public header: DeployHeader; + + @jsonMember({ + constructor: ExecutableDeployItemJsonWrapper + }) + public payment: ExecutableDeployItemJsonWrapper; + + @jsonMember({ + constructor: ExecutableDeployItemJsonWrapper + }) + public session: ExecutableDeployItemJsonWrapper; + + @jsonArrayMember(Approval) + public approvals: Approval[]; + + /** + * + * @param hash The DeployHash identifying this Deploy + * @param header The deployHeader + * @param payment The ExecutableDeployItem for payment code. + * @param session the ExecutableDeployItem for session code. + * @param approvals An array of signature and public key of the signers, who approve this deploy + */ + constructor( + hash: ByteArray, + header: DeployHeader, + payment: ExecutableDeployItemJsonWrapper, + session: ExecutableDeployItemJsonWrapper, + approvals: Approval[] + ) { + this.approvals = approvals; + this.session = session; + this.payment = payment; + this.header = header; + this.hash = hash; + } + + public isTransfer(): boolean { + return this.session.isTransfer(); + } + + public isStandardPayment(): boolean { + if (this.payment.isModuleBytes()) { + return this.payment.asModuleBytes()?.moduleBytes.length === 0; + } + return false; } } @@ -399,7 +722,7 @@ export class Transfer extends ExecutableDeployItem { * @param deployHeader */ export const serializeHeader = (deployHeader: DeployHeader) => { - return toBytesDeployHeader(deployHeader); + return deployHeader.toBytes(); }; /** @@ -408,8 +731,8 @@ export const serializeHeader = (deployHeader: DeployHeader) => { * @param session */ export const serializeBody = ( - payment: ExecutableDeployItem, - session: ExecutableDeployItem + payment: ExecutableDeployItemJsonWrapper, + session: ExecutableDeployItemJsonWrapper ) => { return concat([payment.toBytes(), session.toBytes()]); }; @@ -456,21 +779,21 @@ export class DeployParams { */ export function makeDeploy( deployParam: DeployParams, - session: ExecutableDeployItem, - payment: ExecutableDeployItem + session: ExecutableDeployItemJsonWrapper, + payment: ExecutableDeployItemJsonWrapper ): Deploy { const serializedBody = serializeBody(payment, session); const bodyHash = blake.blake2b(serializedBody, null, 32); - const header: DeployHeader = { - account: deployParam.accountPublicKey, + const header: DeployHeader = new DeployHeader( + deployParam.accountPublicKey, + deployParam.timestamp!, + deployParam.ttl, + deployParam.gasPrice, bodyHash, - chainName: deployParam.chainName, - dependencies: deployParam.dependencies, - gasPrice: deployParam.gasPrice, - timestamp: deployParam.timestamp!, - ttl: deployParam.ttl - }; + deployParam.dependencies, + deployParam.chainName + ); const serializedHeader = serializeHeader(header); const deployHash = blake.blake2b(serializedHeader, null, 32); return new Deploy( @@ -553,27 +876,18 @@ export const standardPayment = (paymentAmount: bigint | JSBI) => { * @param deploy */ export const deployToJson = (deploy: Deploy) => { - const header = deploy.header; - const headerJson = { - account: header.account.toAccountHex(), - timestamp: new Date(header.timestamp).toISOString(), - ttl: humanizerTTL(deploy.header.ttl), - gas_price: header.gasPrice, - body_hash: encodeBase16(header.bodyHash), - dependencies: header.dependencies.map(it => encodeBase16(it)), - chain_name: header.chainName - }; - const json = { - hash: encodeBase16(deploy.hash), - header: headerJson, - payment: deploy.payment.toJson(), - session: deploy.session.toJson(), - approvals: deploy.approvals.map(it => { - return { - signer: it.signer, - signature: it.signature - }; - }) + const serializer = new TypedJSON(Deploy); + return { + deploy: serializer.stringify(deploy) }; - return { deploy: json }; +}; + +/** + * Convert the json to deploy object + * + * @param json + */ +export const jsonToDeploy = (json: any) => { + const serializer = new TypedJSON(Deploy); + return serializer.parse(json); }; diff --git a/packages/sdk/src/lib/RuntimeArgs.ts b/packages/sdk/src/lib/RuntimeArgs.ts index 84a34817..3f2c1c87 100644 --- a/packages/sdk/src/lib/RuntimeArgs.ts +++ b/packages/sdk/src/lib/RuntimeArgs.ts @@ -2,19 +2,33 @@ * Implements a collection of runtime arguments. */ import { toBytesString, toBytesVecT } from './byterepr'; -import { CLValue, ToBytes } from './CLValue'; +import { CLValue, Result, StringValue, ToBytes, U32 } from './CLValue'; import { concat } from '@ethersproject/bytes'; export class NamedArg implements ToBytes { - constructor(public name: string, public value: CLValue) {} + constructor(public name: string, public value: CLValue) { + } public toBytes(): ByteArray { return concat([toBytesString(this.name), this.value.toBytes()]); } + + public static fromBytes(bytes: Uint8Array): Result { + const nameRes = StringValue.fromBytes(bytes); + if (nameRes.hasError()) { + return Result.Err(nameRes.error); + } + const clValueRes = CLValue.fromBytes(nameRes.remainder); + if (clValueRes.hasError()) { + return Result.Err(clValueRes.error); + } + return Result.Ok(new NamedArg(nameRes.value.str, clValueRes.value), clValueRes.remainder); + } } export class RuntimeArgs implements ToBytes { - constructor(public args: Record) {} + constructor(public args: Record) { + } public static fromMap(args: Record) { return new RuntimeArgs(args); @@ -39,4 +53,23 @@ export class RuntimeArgs implements ToBytes { return toBytesVecT(vec); } + + public static fromBytes(bytes: Uint8Array): Result { + const sizeRes = U32.fromBytes(bytes); + if (sizeRes.hasError()) { + return Result.Err(sizeRes.error); + } + const size = sizeRes.value.value as number; + let remainBytes = sizeRes.remainder; + const res: NamedArg[] = []; + for (let i = 0; i < size; i++) { + const namedArgRes = NamedArg.fromBytes(remainBytes); + if (namedArgRes.hasError()) { + return Result.Err(namedArgRes.error); + } + res.push(namedArgRes.value); + remainBytes = namedArgRes.remainder; + } + return Result.Ok(RuntimeArgs.fromNamedArgs(res), remainBytes); + } } From 7f4218340d063c1de826252a92547908e2abd787 Mon Sep 17 00:00:00 2001 From: Abner Zheng Date: Thu, 14 Jan 2021 23:48:16 +0800 Subject: [PATCH 11/22] ECO-822: rename to DeployFromJson --- packages/sdk/src/lib/CasperClient.ts | 4 ++-- packages/sdk/src/lib/DeployUtil.ts | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/sdk/src/lib/CasperClient.ts b/packages/sdk/src/lib/CasperClient.ts index 67a18e1b..e1375582 100644 --- a/packages/sdk/src/lib/CasperClient.ts +++ b/packages/sdk/src/lib/CasperClient.ts @@ -159,8 +159,8 @@ export class CasperClient { * * @param json */ - public jsonToDeploy(json: any) { - return DeployUtil.jsonToDeploy(json); + public deployFromJson(json: any) { + return DeployUtil.deployFromJson(json); }; /** diff --git a/packages/sdk/src/lib/DeployUtil.ts b/packages/sdk/src/lib/DeployUtil.ts index f96983da..e275992c 100644 --- a/packages/sdk/src/lib/DeployUtil.ts +++ b/packages/sdk/src/lib/DeployUtil.ts @@ -887,7 +887,7 @@ export const deployToJson = (deploy: Deploy) => { * * @param json */ -export const jsonToDeploy = (json: any) => { +export const deployFromJson = (json: any) => { const serializer = new TypedJSON(Deploy); return serializer.parse(json); }; From 8a174fc56c7f2a48a6b0f5f8a660dcc812fe125a Mon Sep 17 00:00:00 2001 From: Abner Zheng Date: Fri, 15 Jan 2021 16:49:03 +0800 Subject: [PATCH 12/22] ECO-822: update version --- packages/server/package.json | 2 +- packages/ui/package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/server/package.json b/packages/server/package.json index ca20d35d..48d7040f 100644 --- a/packages/server/package.json +++ b/packages/server/package.json @@ -40,7 +40,7 @@ "@types/http-proxy-middleware": "^0.19.3", "auth0": "^2.28.0", "blakejs": "^1.1.0", - "casper-client-sdk": "1.0.9", + "casper-client-sdk": "1.0.10", "command-line-args": "^5.1.1", "cron": "1.7.2", "dotenv": "^8.0.0", diff --git a/packages/ui/package.json b/packages/ui/package.json index 870598ab..1c570b28 100644 --- a/packages/ui/package.json +++ b/packages/ui/package.json @@ -21,7 +21,7 @@ "@types/react-router-dom": "^5.0.1", "bootstrap": "^4.3.1", "bootstrap-components": "^1.1.287", - "casper-client-sdk": "^1.0.9", + "casper-client-sdk": "^1.0.10", "change-case": "^4.1.1", "chart.js": "^2.9.3", "d3": "^5.9.7", From 58fc1e5a8e2bc15332c084b5939023616af3d8e5 Mon Sep 17 00:00:00 2001 From: Abner Zheng Date: Fri, 15 Jan 2021 20:37:33 +0800 Subject: [PATCH 13/22] ECO-822: add unit test for DeployHeader and fix type declaration. Signed-off-by: Abner Zheng --- packages/sdk/src/lib/DeployUtil.ts | 12 +++++------- packages/sdk/test/lib/DeployUtil.test.ts | 15 +++++++++++++++ 2 files changed, 20 insertions(+), 7 deletions(-) create mode 100644 packages/sdk/test/lib/DeployUtil.test.ts diff --git a/packages/sdk/src/lib/DeployUtil.ts b/packages/sdk/src/lib/DeployUtil.ts index e275992c..51fde0c0 100644 --- a/packages/sdk/src/lib/DeployUtil.ts +++ b/packages/sdk/src/lib/DeployUtil.ts @@ -23,6 +23,7 @@ import { Keys, URef } from './index'; import { AsymmetricKey, SignatureAlgorithm } from './Keys'; import { BigNumberish } from '@ethersproject/bignumber'; import { jsonArrayMember, jsonMember, jsonObject, TypedJSON } from 'typedjson'; +import { ByteArray } from 'tweetnacl-ts'; const shortEnglishHumanizer = humanizeDuration.humanizer({ spacer: '', @@ -87,13 +88,10 @@ export class DeployHeader implements ToBytes { }) public bodyHash: ByteArray; - @jsonArrayMember( - () => { - // @ts-ignore - }, { - serializer: byteArrayJsonSerializer, - deserializer: byteArrayJsonDeserializer - }) + @jsonArrayMember(ByteArray, { + serializer: (value: Array) => value.map(it => byteArrayJsonSerializer(it)), + deserializer: (json: any) => json.map((it: string) => byteArrayJsonDeserializer(it)) + }) public dependencies: ByteArray[]; @jsonMember({ name: 'chain_name', constructor: String }) diff --git a/packages/sdk/test/lib/DeployUtil.test.ts b/packages/sdk/test/lib/DeployUtil.test.ts new file mode 100644 index 00000000..4db662a9 --- /dev/null +++ b/packages/sdk/test/lib/DeployUtil.test.ts @@ -0,0 +1,15 @@ +import { DeployHeader } from '../../src/lib/DeployUtil'; +import { expect } from 'chai'; +import { Keys } from '../../src/lib'; +import { TypedJSON } from 'typedjson'; + +describe('DeployUtil', () => { + it('should stringify/parse DeployHeader correctly', function() { + const ed25519Key = Keys.Ed25519.new(); + const deployHeader = new DeployHeader(ed25519Key.publicKey, 123456, 654321, 10, Uint8Array.from(Array(32).fill(42)), [Uint8Array.from(Array(32).fill(2))], 'test-network'); + const serializer = new TypedJSON(DeployHeader); + const json = serializer.stringify(deployHeader); + const deployHeader1 = serializer.parse(json); + expect(deployHeader1).to.deep.equal(deployHeader); + }); +}); From 9a2268c5f0a9f319b59d6991b4d931fbb1209882 Mon Sep 17 00:00:00 2001 From: Abner Zheng Date: Fri, 15 Jan 2021 22:07:26 +0800 Subject: [PATCH 14/22] ECO-822: update other components --- packages/sdk/src/lib/CasperClient.ts | 9 ++- packages/sdk/src/lib/Contracts.ts | 6 +- packages/sdk/src/lib/DeployUtil.ts | 59 +++++++++---------- packages/server/src/StoredFaucetService.ts | 13 ++-- .../containers/DeployContractsContainer.ts | 6 +- 5 files changed, 46 insertions(+), 47 deletions(-) diff --git a/packages/sdk/src/lib/CasperClient.ts b/packages/sdk/src/lib/CasperClient.ts index 207586f2..2c8db8fc 100644 --- a/packages/sdk/src/lib/CasperClient.ts +++ b/packages/sdk/src/lib/CasperClient.ts @@ -1,7 +1,7 @@ import { AccountDeploy, CasperServiceByJsonRPC, DeployResult, EventService, TransferResult } from '../services'; import { DeployUtil, Keys, PublicKey } from './index'; import { encodeBase16 } from './Conversions'; -import { Deploy, DeployParams, ExecutableDeployItem, ExecutableDeployItemJsonWrapper, Transfer } from './DeployUtil'; +import { Deploy, DeployParams, ExecutableDeployItem } from './DeployUtil'; import { AsymmetricKey, SignatureAlgorithm } from './Keys'; import { CasperHDKey } from './CasperHDKey'; @@ -126,7 +126,7 @@ export class CasperClient { session: ExecutableDeployItem, payment: ExecutableDeployItem ): Deploy { - return DeployUtil.makeDeploy(deployParams, ExecutableDeployItemJsonWrapper.fromExecutionDeployItem(session), ExecutableDeployItemJsonWrapper.fromExecutionDeployItem(payment)); + return DeployUtil.makeDeploy(deployParams, session, payment); } /** @@ -172,9 +172,12 @@ export class CasperClient { */ public makeTransferDeploy( deployParams: DeployParams, - session: Transfer, + session: ExecutableDeployItem, payment: ExecutableDeployItem ): Deploy { + if (!session.isTransfer()) { + throw new Error('The session is not a Transfer ExecutableDeployItem'); + } return this.makeDeploy(deployParams, session, payment); } diff --git a/packages/sdk/src/lib/Contracts.ts b/packages/sdk/src/lib/Contracts.ts index eda23ea7..c142f7de 100644 --- a/packages/sdk/src/lib/Contracts.ts +++ b/packages/sdk/src/lib/Contracts.ts @@ -2,7 +2,7 @@ import blake from 'blakejs'; import * as fs from 'fs'; import { PublicKey } from '../index'; import * as DeployUtil from './DeployUtil'; -import { DeployParams, ExecutableDeployItemJsonWrapper } from './DeployUtil'; +import { DeployParams, ExecutableDeployItem } from './DeployUtil'; import { RuntimeArgs } from './RuntimeArgs'; import { AccountHash, CLValue, KeyValue } from './CLValue'; import { AsymmetricKey } from './Keys'; @@ -53,12 +53,12 @@ export class Contract { signingKeyPair: AsymmetricKey, chainName: string ): DeployUtil.Deploy { - const session = ExecutableDeployItemJsonWrapper.newModuleBytes(this.sessionWasm, args); + const session = ExecutableDeployItem.newModuleBytes(this.sessionWasm, args); const paymentArgs = RuntimeArgs.fromMap({ amount: CLValue.u512(paymentAmount.toString()) }); - const payment = ExecutableDeployItemJsonWrapper.newModuleBytes(this.paymentWasm, paymentArgs); + const payment = ExecutableDeployItem.newModuleBytes(this.paymentWasm, paymentArgs); const deploy = DeployUtil.makeDeploy( new DeployParams(accountPublicKey, chainName), diff --git a/packages/sdk/src/lib/DeployUtil.ts b/packages/sdk/src/lib/DeployUtil.ts index 51fde0c0..978afc29 100644 --- a/packages/sdk/src/lib/DeployUtil.ts +++ b/packages/sdk/src/lib/DeployUtil.ts @@ -89,7 +89,7 @@ export class DeployHeader implements ToBytes { public bodyHash: ByteArray; @jsonArrayMember(ByteArray, { - serializer: (value: Array) => value.map(it => byteArrayJsonSerializer(it)), + serializer: (value: ByteArray[]) => value.map(it => byteArrayJsonSerializer(it)), deserializer: (json: any) => json.map((it: string) => byteArrayJsonDeserializer(it)) }) public dependencies: ByteArray[]; @@ -171,7 +171,7 @@ export class Approval { public signature: string; } -export abstract class ExecutableDeployItem implements ToBytes { +abstract class ExecutableDeployItemInternal implements ToBytes { public abstract tag: number; public abstract args: RuntimeArgs; @@ -194,7 +194,7 @@ const argsDeserializer = (byteStr: string) => { }; @jsonObject -export class ModuleBytes extends ExecutableDeployItem { +export class ModuleBytes extends ExecutableDeployItemInternal { public tag = 0; @jsonMember({ @@ -228,7 +228,7 @@ export class ModuleBytes extends ExecutableDeployItem { } @jsonObject -export class StoredContractByHash extends ExecutableDeployItem { +export class StoredContractByHash extends ExecutableDeployItemInternal { public tag = 1; @jsonMember({ @@ -272,7 +272,7 @@ export class StoredContractByHash extends ExecutableDeployItem { } @jsonObject -export class StoredContractByName extends ExecutableDeployItem { +export class StoredContractByName extends ExecutableDeployItemInternal { public tag = 2; @jsonMember({ constructor: String }) @@ -313,7 +313,7 @@ export class StoredContractByName extends ExecutableDeployItem { } @jsonObject -export class StoredVersionedContractByName extends ExecutableDeployItem { +export class StoredVersionedContractByName extends ExecutableDeployItemInternal { public tag = 4; @jsonMember({ constructor: String }) @@ -362,7 +362,7 @@ export class StoredVersionedContractByName extends ExecutableDeployItem { } @jsonObject -export class StoredVersionedContractByHash extends ExecutableDeployItem { +export class StoredVersionedContractByHash extends ExecutableDeployItemInternal { public tag = 3; @jsonMember({ @@ -422,7 +422,7 @@ export class StoredVersionedContractByHash extends ExecutableDeployItem { } @jsonObject -export class Transfer extends ExecutableDeployItem { +export class Transfer extends ExecutableDeployItemInternal { public tag = 5; @jsonMember({ @@ -478,7 +478,7 @@ export class Transfer extends ExecutableDeployItem { } @jsonObject -export class ExecutableDeployItemJsonWrapper implements ToBytes { +export class ExecutableDeployItem implements ToBytes { @jsonMember({ name: 'ModuleBytes', @@ -532,8 +532,8 @@ export class ExecutableDeployItemJsonWrapper implements ToBytes { throw new Error('failed to serialize ExecutableDeployItemJsonWrapper'); } - public static fromExecutionDeployItem(item: ExecutableDeployItem) { - const res = new ExecutableDeployItemJsonWrapper(); + public static fromExecutableDeployItemInternal(item: ExecutableDeployItemInternal) { + const res = new ExecutableDeployItem(); switch (item.tag) { case 0: res.moduleBytes = item as ModuleBytes; @@ -557,17 +557,16 @@ export class ExecutableDeployItemJsonWrapper implements ToBytes { return res; } - public static newModuleBytes(moduleBytes: ByteArray, args: RuntimeArgs): ExecutableDeployItemJsonWrapper { - return ExecutableDeployItemJsonWrapper.fromExecutionDeployItem(new ModuleBytes(moduleBytes, args)); + public static newModuleBytes(moduleBytes: ByteArray, args: RuntimeArgs): ExecutableDeployItem { + return ExecutableDeployItem.fromExecutableDeployItemInternal(new ModuleBytes(moduleBytes, args)); } public static newStoredContractByHash( hash: Uint8Array, - version: number | null, entryPoint: string, args: RuntimeArgs ) { - return ExecutableDeployItemJsonWrapper.fromExecutionDeployItem(new StoredVersionedContractByHash(hash, version, entryPoint, args)); + return ExecutableDeployItem.fromExecutableDeployItemInternal(new StoredContractByHash(hash, entryPoint, args)); } public static newStoredContractByName( @@ -575,7 +574,7 @@ export class ExecutableDeployItemJsonWrapper implements ToBytes { entryPoint: string, args: RuntimeArgs ) { - return ExecutableDeployItemJsonWrapper.fromExecutionDeployItem(new StoredContractByName(name, entryPoint, args)); + return ExecutableDeployItem.fromExecutableDeployItemInternal(new StoredContractByName(name, entryPoint, args)); } public static newStoredVersionContractByHash( @@ -584,7 +583,7 @@ export class ExecutableDeployItemJsonWrapper implements ToBytes { entryPoint: string, args: RuntimeArgs ) { - return ExecutableDeployItemJsonWrapper.fromExecutionDeployItem(new StoredVersionedContractByHash(hash, version, entryPoint, args)); + return ExecutableDeployItem.fromExecutableDeployItemInternal(new StoredVersionedContractByHash(hash, version, entryPoint, args)); } public static newStoredVersionContractByName( @@ -593,7 +592,7 @@ export class ExecutableDeployItemJsonWrapper implements ToBytes { entryPoint: string, args: RuntimeArgs ) { - return ExecutableDeployItemJsonWrapper.fromExecutionDeployItem(new StoredVersionedContractByName(name, version, entryPoint, args)); + return ExecutableDeployItem.fromExecutableDeployItemInternal(new StoredVersionedContractByName(name, version, entryPoint, args)); } public static newTransfer( @@ -602,7 +601,7 @@ export class ExecutableDeployItemJsonWrapper implements ToBytes { sourcePurse?: URef, id: number | null = null ) { - return ExecutableDeployItemJsonWrapper.fromExecutionDeployItem(new Transfer(amount, target, sourcePurse, id)); + return ExecutableDeployItem.fromExecutableDeployItemInternal(new Transfer(amount, target, sourcePurse, id)); } public isModuleBytes(): boolean { @@ -669,14 +668,14 @@ export class Deploy { public header: DeployHeader; @jsonMember({ - constructor: ExecutableDeployItemJsonWrapper + constructor: ExecutableDeployItem }) - public payment: ExecutableDeployItemJsonWrapper; + public payment: ExecutableDeployItem; @jsonMember({ - constructor: ExecutableDeployItemJsonWrapper + constructor: ExecutableDeployItem }) - public session: ExecutableDeployItemJsonWrapper; + public session: ExecutableDeployItem; @jsonArrayMember(Approval) public approvals: Approval[]; @@ -692,8 +691,8 @@ export class Deploy { constructor( hash: ByteArray, header: DeployHeader, - payment: ExecutableDeployItemJsonWrapper, - session: ExecutableDeployItemJsonWrapper, + payment: ExecutableDeployItem, + session: ExecutableDeployItem, approvals: Approval[] ) { this.approvals = approvals; @@ -729,8 +728,8 @@ export const serializeHeader = (deployHeader: DeployHeader) => { * @param session */ export const serializeBody = ( - payment: ExecutableDeployItemJsonWrapper, - session: ExecutableDeployItemJsonWrapper + payment: ExecutableDeployItem, + session: ExecutableDeployItem ) => { return concat([payment.toBytes(), session.toBytes()]); }; @@ -777,8 +776,8 @@ export class DeployParams { */ export function makeDeploy( deployParam: DeployParams, - session: ExecutableDeployItemJsonWrapper, - payment: ExecutableDeployItemJsonWrapper + session: ExecutableDeployItem, + payment: ExecutableDeployItem ): Deploy { const serializedBody = serializeBody(payment, session); const bodyHash = blake.blake2b(serializedBody, null, 32); @@ -865,7 +864,7 @@ export const standardPayment = (paymentAmount: bigint | JSBI) => { amount: CLValue.u512(paymentAmount.toString()) }); - return new ModuleBytes(Uint8Array.from([]), paymentArgs); + return ExecutableDeployItem.newModuleBytes(Uint8Array.from([]), paymentArgs); }; /** diff --git a/packages/server/src/StoredFaucetService.ts b/packages/server/src/StoredFaucetService.ts index 00fa6389..8cdeb414 100644 --- a/packages/server/src/StoredFaucetService.ts +++ b/packages/server/src/StoredFaucetService.ts @@ -1,8 +1,4 @@ -import { - encodeBase16, - CasperServiceByJsonRPC, - DeployUtil -} from 'casper-client-sdk'; +import { CasperServiceByJsonRPC, DeployUtil, encodeBase16 } from 'casper-client-sdk'; import { ByteArray } from 'tweetnacl-ts'; import { CallFaucet } from './lib/Contracts'; import { AsymmetricKey } from 'casper-client-sdk/dist/lib/Keys'; @@ -18,7 +14,8 @@ export class StoredFaucetService { private transferAmount: bigint, private casperService: CasperServiceByJsonRPC, private chainName: string - ) {} + ) { + } async callStoredFaucet(accountPublicKeyHash: ByteArray): Promise { const state = await this.checkState(); @@ -27,7 +24,7 @@ export class StoredFaucetService { accountPublicKeyHash, this.transferAmount ); - const session = new DeployUtil.StoredContractByName( + const session = DeployUtil.ExecutableDeployItem.newStoredContractByName( CONTRACT_NAME, ENTRY_POINT_NAME, sessionArgs @@ -49,7 +46,7 @@ export class StoredFaucetService { await this.casperService.deploy(signedDeploy); return signedDeploy.hash; } else { - throw new Error("Can't do faucet now"); + throw new Error('Can\'t do faucet now'); } } diff --git a/packages/ui/src/containers/DeployContractsContainer.ts b/packages/ui/src/containers/DeployContractsContainer.ts index cfe7a6e2..4c571b17 100644 --- a/packages/ui/src/containers/DeployContractsContainer.ts +++ b/packages/ui/src/containers/DeployContractsContainer.ts @@ -372,11 +372,11 @@ export class DeployContractsContainer { if (config.contractType.value === DeployUtil.ContractType.WASM) { session = this.selectedFileContent!; - sessionExecutionItem = new DeployUtil.ModuleBytes(session, runtimeArgs); + sessionExecutionItem = DeployUtil.ExecutableDeployItem.newModuleBytes(session, runtimeArgs); } else if (config.contractType.value === DeployUtil.ContractType.Hash) { session = decodeBase16(config.contractHash.value); const entryPoint = config.entryPoint.value; - sessionExecutionItem = new DeployUtil.StoredContractByHash( + sessionExecutionItem = DeployUtil.ExecutableDeployItem.newStoredContractByHash( session, entryPoint, runtimeArgs @@ -384,7 +384,7 @@ export class DeployContractsContainer { } else if (config.contractType.value === DeployUtil.ContractType.Name) { session = config.contractName.value; const entryPoint = config.entryPoint.value; - sessionExecutionItem = new DeployUtil.StoredContractByName( + sessionExecutionItem = DeployUtil.ExecutableDeployItem.newStoredContractByName( session, entryPoint, runtimeArgs From 5e379149d45d00a1047ebcd934109dcb6429436f Mon Sep 17 00:00:00 2001 From: Abner Zheng Date: Fri, 15 Jan 2021 23:07:32 +0800 Subject: [PATCH 15/22] ECO-822: convert CLValue to JSON values --- packages/sdk/src/lib/CLValue.ts | 155 ++++++++++++++++++++++------ packages/sdk/src/lib/RuntimeArgs.ts | 4 +- packages/sdk/src/lib/option.ts | 2 +- 3 files changed, 125 insertions(+), 36 deletions(-) diff --git a/packages/sdk/src/lib/CLValue.ts b/packages/sdk/src/lib/CLValue.ts index f4fe861d..862caf90 100644 --- a/packages/sdk/src/lib/CLValue.ts +++ b/packages/sdk/src/lib/CLValue.ts @@ -1,12 +1,5 @@ import { concat } from '@ethersproject/bytes'; -import { - toBytesArrayU8, - toBytesBytesArray, - toBytesNumber, - toBytesString, - toBytesU32, - toBytesVecT -} from './byterepr'; +import { toBytesArrayU8, toBytesBytesArray, toBytesNumber, toBytesString, toBytesU32, toBytesVecT } from './byterepr'; import { BigNumber, BigNumberish } from '@ethersproject/bignumber'; import { decodeBase16, encodeBase16 } from './Conversions'; import { Option } from './option'; @@ -25,7 +18,8 @@ export interface BytesDeserializableStatic extends Type { fromBytes(bytes: ByteArray): Result; } -export interface BytesSerializable extends CLTyped, ToBytes {} +export interface BytesSerializable extends CLTyped, ToBytes { +} export interface CLTyped { /** @@ -121,7 +115,8 @@ export class Result { private val: T | null, private rem: ByteArray | null, public error: FromBytesError - ) {} + ) { + } public static Err(errorCode: FromBytesError) { return new Result(null, null, errorCode); @@ -133,7 +128,7 @@ export class Result { get remainder(): ByteArray { if (this.rem === null) { - throw new Error("Don't have remainder"); + throw new Error('Don\'t have remainder'); } return this.rem; } @@ -143,7 +138,7 @@ export class Result { */ get value(): T { if (!this.hasValue()) { - throw new Error("Don't have value"); + throw new Error('Don\'t have value'); } return this.val!; } @@ -167,12 +162,12 @@ export class Result { @staticImplements>() export class Bool extends CLTypedAndToBytes { - constructor(private b: boolean) { + constructor(public val: boolean) { super(); } public toBytes(): ByteArray { - return new Uint8Array([this.b ? 1 : 0]); + return new Uint8Array([this.val ? 1 : 0]); } public clType(): CLType { @@ -196,7 +191,7 @@ export class Bool extends CLTypedAndToBytes { abstract class NumberCoder extends CLTypedAndToBytes { public bitSize: number; public signed: boolean; - public value: BigNumberish; + public val: BigNumberish; public name: string; protected constructor(bitSize: number, signed: boolean, value: BigNumberish) { @@ -204,11 +199,11 @@ abstract class NumberCoder extends CLTypedAndToBytes { this.name = (signed ? 'i' : 'u') + bitSize; this.bitSize = bitSize; this.signed = signed; - this.value = value; + this.val = value; } public toBytes = (): ByteArray => { - return toBytesNumber(this.bitSize, this.signed, this.value); + return toBytesNumber(this.bitSize, this.signed, this.val); }; public abstract clType(): CLType; @@ -419,12 +414,12 @@ export class Unit extends CLTypedAndToBytes { @staticImplements>() export class StringValue extends CLTypedAndToBytes { - constructor(public str: string) { + constructor(public val: string) { super(); } public toBytes = () => { - return toBytesString(this.str); + return toBytesString(this.val); }; public clType(): CLType { @@ -436,7 +431,7 @@ export class StringValue extends CLTypedAndToBytes { if (res.hasError()) { return Result.Err(res.error); } - const len = res.value.value as number; + const len = res.value.val as number; const str = Buffer.from(res.remainder.subarray(0, len)).toString('utf8'); return Result.Ok( new StringValue(str), @@ -531,7 +526,7 @@ export class List extends CLTypedAndToBytes { constructor(private vec: T[]) { super(); if (vec.length === 0) { - throw new Error("Can't create instance for empty list"); + throw new Error('Can\'t create instance for empty list'); } } @@ -551,7 +546,7 @@ export class List extends CLTypedAndToBytes { if (u32Res.hasError()) { return Result.Err(u32Res.error); } - const size = u32Res.value.value as number; + const size = u32Res.value.val as number; const vec = []; let remainder = u32Res.remainder; for (let i = 0; i < size; i++) { @@ -813,7 +808,7 @@ export class MapValue extends CLTypedAndToBytes { if (u32Res.hasError()) { return Result.Err(u32Res.error); } - const size = u32Res.value.value as number; + const size = u32Res.value.val as number; const vec: MapEntry[] = []; let remainder = u32Res.remainder; for (let i = 0; i < size; i++) { @@ -836,7 +831,8 @@ export class MapValue extends CLTypedAndToBytes { export class OptionType { public tag = ComplexType.Option; - constructor(public innerType: CLType) {} + constructor(public innerType: CLType) { + } } class ListType { @@ -851,31 +847,36 @@ class ListType { class ByteArrayType { public tag = ComplexType.ByteArray; - constructor(public size: number) {} + constructor(public size: number) { + } } class MapType { public tag = ComplexType.Map; - constructor(public keyType: CLType, public valueType: CLType) {} + constructor(public keyType: CLType, public valueType: CLType) { + } } class Tuple1Type { public tag = ComplexType.Tuple1; - constructor(public t0: CLType) {} + constructor(public t0: CLType) { + } } class Tuple2Type { public tag = ComplexType.Tuple2; - constructor(public t0: CLType, public t1: CLType) {} + constructor(public t0: CLType, public t1: CLType) { + } } class Tuple3Type { public tag = ComplexType.Tuple3; - constructor(public t0: CLType, public t1: CLType, public t2: CLType) {} + constructor(public t0: CLType, public t1: CLType, public t2: CLType) { + } } export type CLType = @@ -1025,7 +1026,7 @@ export class CLTypeHelper { return Result.Err(sizeRes.error); } return Result.Ok( - CLTypeHelper.byteArray(sizeRes.value.value as number), + CLTypeHelper.byteArray(sizeRes.value.val as number), sizeRes.remainder ); } @@ -1192,7 +1193,7 @@ class ByteArrayValue extends CLTypedAndToBytes { if (u32Res.hasError()) { return Result.Err(u32Res.error); } - const size = u32Res.value.value as number; + const size = u32Res.value.val as number; if (u32Res.remainder.length < size) { return Result.Err(FromBytesError.EarlyEndOfStream); } @@ -1295,7 +1296,8 @@ export class CLValue implements ToBytes { /** * Please use static methods to constructs a new `CLValue` */ - private constructor(public value: CLTypedAndToBytes, public clType: CLType) {} + private constructor(public value: CLTypedAndToBytes, public clType: CLType) { + } public get clValueBytes() { return this.value.toBytes(); @@ -1419,6 +1421,77 @@ export class CLValue implements ToBytes { public static byteArray(bytes: ByteArray) { return CLValue.fromT(new ByteArrayValue(bytes)); } + + public isBigNumber() { + return this.clType === SimpleType.U8 || this.clType === SimpleType.I32 || + this.clType === SimpleType.I64 || this.clType === SimpleType.U32 || + this.clType === SimpleType.U64 || this.clType === SimpleType.U128 || + this.clType === SimpleType.U256 || this.clType === SimpleType.U512; + } + + public asBigNumber(): BigNumber { + if (this.isBigNumber()) { + const numberCoder = this.value as NumberCoder; + return BigNumber.from(numberCoder.val); + } else { + throw new Error('The CLValue can\'t convert to BigNumber'); + } + } + + public isBoolean() { + return this.clType === SimpleType.Bool; + } + + public asBoolean() { + if (!this.isBoolean()) { + throw new Error('The CLValue can\'t convert to Boolean'); + } + return (this.value as Bool).val; + } + + public isString() { + return this.clType === SimpleType.String; + } + + public asString() { + if (!this.isString()) { + throw new Error('The CLValue can\'t convert to String'); + } + return (this.value as StringValue).val; + } + + public isPublicKey() { + return this.clType === SimpleType.PublicKey; + } + + public asPublicKey(): PublicKey { + if (!this.isPublicKey()) { + throw new Error('The CLValue can\'t convert to PublicKey'); + } + return this.value as PublicKey; + } + + public isKey() { + return this.clType === SimpleType.Key; + } + + public asKey() { + if (!this.isKey()) { + throw new Error('The CLValue can\'t convert to Key'); + } + return this.value as KeyValue; + } + + public isURef() { + return this.clType === SimpleType.URef; + } + + public asURef() { + if (!this.isURef()) { + throw new Error('The CLValue can\'t convert to URef'); + } + return this.value as URef; + } } export enum KeyVariant { @@ -1485,6 +1558,18 @@ export class KeyValue extends CLTypedAndToBytes { public uRef: URef | null; public account: AccountHash | null; + public isHash() { + return this.variant === KeyVariant.HASH_ID; + } + + public isURef() { + return this.variant === KeyVariant.UREF_ID; + } + + public isAccount() { + return this.variant === KeyVariant.ACCOUNT_ID; + } + /** Creates a `Key` from a given [[URef]]. */ public static fromURef(uref: URef): KeyValue { const key = new KeyValue(); @@ -1612,7 +1697,7 @@ export class URef extends CLTypedAndToBytes { */ public static fromFormattedStr(input: string) { if (!input.startsWith(FORMATTED_STRING_PREFIX)) { - throw new Error("prefix is not 'uref-'"); + throw new Error('prefix is not \'uref-\''); } const parts = input.substring(FORMATTED_STRING_PREFIX.length).split('-', 2); if (parts.length !== 2) { @@ -1625,6 +1710,10 @@ export class URef extends CLTypedAndToBytes { return new URef(addr, accessRight); } + public toFormattedStr() { + return [FORMATTED_STRING_PREFIX, encodeBase16(this.uRefAddr), this.accessRights.toString(8)].join('-'); + } + /** * Serializes the URef into an array of bytes that represents it in the Casper serialization * format. diff --git a/packages/sdk/src/lib/RuntimeArgs.ts b/packages/sdk/src/lib/RuntimeArgs.ts index 3f2c1c87..479e3985 100644 --- a/packages/sdk/src/lib/RuntimeArgs.ts +++ b/packages/sdk/src/lib/RuntimeArgs.ts @@ -22,7 +22,7 @@ export class NamedArg implements ToBytes { if (clValueRes.hasError()) { return Result.Err(clValueRes.error); } - return Result.Ok(new NamedArg(nameRes.value.str, clValueRes.value), clValueRes.remainder); + return Result.Ok(new NamedArg(nameRes.value.val, clValueRes.value), clValueRes.remainder); } } @@ -59,7 +59,7 @@ export class RuntimeArgs implements ToBytes { if (sizeRes.hasError()) { return Result.Err(sizeRes.error); } - const size = sizeRes.value.value as number; + const size = sizeRes.value.val as number; let remainBytes = sizeRes.remainder; const res: NamedArg[] = []; for (let i = 0; i < size; i++) { diff --git a/packages/sdk/src/lib/option.ts b/packages/sdk/src/lib/option.ts index 5312cb5b..534bf435 100644 --- a/packages/sdk/src/lib/option.ts +++ b/packages/sdk/src/lib/option.ts @@ -75,7 +75,7 @@ export class Option extends CLTypedAndToBytes { if (u8Res.hasError()) { return Result.Err(u8Res.error); } - const optionTag = u8Res.value.value as number; + const optionTag = u8Res.value.val as number; if (optionTag === OPTION_TAG_NONE) { return Result.Ok(new Option(null, type.innerType), u8Res.remainder); } else if (optionTag === OPTION_TAG_SOME) { From 3a822853b02d61030c5a20ec8e62510a4bb2d439 Mon Sep 17 00:00:00 2001 From: Abner Zheng Date: Thu, 21 Jan 2021 23:48:08 +0800 Subject: [PATCH 16/22] ECO-822: serialize/deserialize clType to/from json --- packages/sdk/src/lib/CLValue.ts | 303 ++++++++++++++++++++++++---- packages/sdk/src/lib/DeployUtil.ts | 121 ++++++----- packages/sdk/src/lib/RuntimeArgs.ts | 27 ++- packages/sdk/src/lib/StoredValue.ts | 32 +-- 4 files changed, 349 insertions(+), 134 deletions(-) diff --git a/packages/sdk/src/lib/CLValue.ts b/packages/sdk/src/lib/CLValue.ts index 862caf90..7a9199fd 100644 --- a/packages/sdk/src/lib/CLValue.ts +++ b/packages/sdk/src/lib/CLValue.ts @@ -1,10 +1,18 @@ import { concat } from '@ethersproject/bytes'; -import { toBytesArrayU8, toBytesBytesArray, toBytesNumber, toBytesString, toBytesU32, toBytesVecT } from './byterepr'; +import { + toBytesArrayU8, + toBytesBytesArray, + toBytesNumber, + toBytesString, + toBytesU32, + toBytesVecT +} from './byterepr'; import { BigNumber, BigNumberish } from '@ethersproject/bignumber'; import { decodeBase16, encodeBase16 } from './Conversions'; import { Option } from './option'; import { byteHash } from './Contracts'; import { SignatureAlgorithm } from './Keys'; +import { jsonMember, jsonObject } from 'typedjson'; const ED25519_PUBLIC_KEY_LENGTH = 32; const SECP256K1_PUBLIC_KEY_LENGTH = 33; @@ -18,8 +26,7 @@ export interface BytesDeserializableStatic extends Type { fromBytes(bytes: ByteArray): Result; } -export interface BytesSerializable extends CLTyped, ToBytes { -} +export interface BytesSerializable extends CLTyped, ToBytes {} export interface CLTyped { /** @@ -115,8 +122,7 @@ export class Result { private val: T | null, private rem: ByteArray | null, public error: FromBytesError - ) { - } + ) {} public static Err(errorCode: FromBytesError) { return new Result(null, null, errorCode); @@ -128,7 +134,7 @@ export class Result { get remainder(): ByteArray { if (this.rem === null) { - throw new Error('Don\'t have remainder'); + throw new Error("Don't have remainder"); } return this.rem; } @@ -138,7 +144,7 @@ export class Result { */ get value(): T { if (!this.hasValue()) { - throw new Error('Don\'t have value'); + throw new Error("Don't have value"); } return this.val!; } @@ -526,7 +532,7 @@ export class List extends CLTypedAndToBytes { constructor(private vec: T[]) { super(); if (vec.length === 0) { - throw new Error('Can\'t create instance for empty list'); + throw new Error("Can't create instance for empty list"); } } @@ -828,54 +834,114 @@ export class MapValue extends CLTypedAndToBytes { } } -export class OptionType { +export interface ToJSON { + toJSON: () => any; +} + +export class OptionType implements ToJSON { + public static TypeId = 'Option'; public tag = ComplexType.Option; - constructor(public innerType: CLType) { + constructor(public innerType: CLType) {} + + public toJSON(): any { + const innerTypeInJSON = clTypeToJSON(this.innerType); + return { + [OptionType.TypeId]: innerTypeInJSON + }; } } -class ListType { +class ListType implements ToJSON { + public static TypeId = 'List'; public tag = ComplexType.List; public innerType: CLType; constructor(innerType: CLType) { this.innerType = innerType; } + + public toJSON(): any { + const innerTypeInJSON = clTypeToJSON(this.innerType); + return { + [ListType.TypeId]: innerTypeInJSON + }; + } } -class ByteArrayType { +class ByteArrayType implements ToJSON { + public static TypeId = 'ByteArray'; public tag = ComplexType.ByteArray; - constructor(public size: number) { + constructor(public size: number) {} + + public toJSON() { + return { + [ByteArrayType.TypeId]: this.size + }; } } -class MapType { +class MapType implements ToJSON { + public static TypeId = 'Map'; public tag = ComplexType.Map; - constructor(public keyType: CLType, public valueType: CLType) { + constructor(public keyType: CLType, public valueType: CLType) {} + + public toJSON(): any { + return { + [MapType.TypeId]: { + key: clTypeToJSON(this.keyType), + value: clTypeToJSON(this.valueType) + } + }; } } -class Tuple1Type { +class Tuple1Type implements ToJSON { + public static TypeId = 'Tuple1'; public tag = ComplexType.Tuple1; - constructor(public t0: CLType) { + constructor(public t0: CLType) {} + + public toJSON(): any { + const t0TypeInJSON = clTypeToJSON(this.t0); + return { + [Tuple1Type.TypeId]: t0TypeInJSON + }; } } -class Tuple2Type { +class Tuple2Type implements ToJSON { + public static TypeId = 'Tuple2'; public tag = ComplexType.Tuple2; - constructor(public t0: CLType, public t1: CLType) { + constructor(public t0: CLType, public t1: CLType) {} + + public toJSON(): any { + const t0TypeInJSON = clTypeToJSON(this.t0); + const t1TypeInJSON = clTypeToJSON(this.t1); + return { + [Tuple2Type.TypeId]: [t0TypeInJSON, t1TypeInJSON] + }; } } class Tuple3Type { + public static TypeId = 'Tuple3'; + public tag = ComplexType.Tuple3; - constructor(public t0: CLType, public t1: CLType, public t2: CLType) { + constructor(public t0: CLType, public t1: CLType, public t2: CLType) {} + + public toJSON(): any { + const t0TypeInJSON = clTypeToJSON(this.t0); + const t1TypeInJSON = clTypeToJSON(this.t1); + const t2TypeInJSON = clTypeToJSON(this.t2); + + return { + [Tuple3Type.TypeId]: [t0TypeInJSON, t1TypeInJSON, t2TypeInJSON] + }; } } @@ -1107,7 +1173,6 @@ export class CLTypeHelper { case ComplexType.Any: // todo(abner) support Any throw new Error('Any type is unsupported now'); - break; default: return Result.Err(FromBytesError.FormattingError); } @@ -1286,17 +1351,167 @@ export class CLTypedAndToBytesHelper { } } +function toJSONSimpleType(type: SimpleType) { + switch (type) { + case SimpleType.Bool: + return 'Bool'; + case SimpleType.I32: + return 'I32'; + case SimpleType.I64: + return 'I64'; + case SimpleType.U8: + return 'U8'; + case SimpleType.U32: + return 'U32'; + case SimpleType.U64: + return 'U64'; + case SimpleType.U128: + return 'U128'; + case SimpleType.U256: + return 'U256'; + case SimpleType.U512: + return 'U512'; + case SimpleType.Unit: + return 'Unit'; + case SimpleType.String: + return 'String'; + case SimpleType.Key: + return 'Key'; + case SimpleType.URef: + return 'URef'; + case SimpleType.PublicKey: + return 'PublicKey'; + } +} + +function jsonToSimpleType(str: string): CLType { + switch (str) { + case 'Bool': + return SimpleType.Bool; + case 'I32': + return SimpleType.I32; + case 'I64': + return SimpleType.I64; + case 'U8': + return SimpleType.U8; + case 'U32': + return SimpleType.U32; + case 'U64': + return SimpleType.U64; + case 'U128': + return SimpleType.U128; + case 'U256': + return SimpleType.U256; + case 'U512': + return SimpleType.U512; + case 'Unit': + return SimpleType.Unit; + case 'String': + return SimpleType.String; + case 'Key': + return SimpleType.Key; + case 'URef': + return SimpleType.URef; + case 'PublicKey': + return SimpleType.PublicKey; + default: + throw new Error(`The type ${str} is not supported`); + } +} + +const clTypeToJSON = (type: CLType) => { + if ( + type instanceof ListType || + type instanceof Tuple1Type || + type instanceof Tuple2Type || + type instanceof Tuple3Type || + type instanceof ByteArrayType || + type instanceof MapType || + type instanceof OptionType + ) { + return type.toJSON(); + } else { + return toJSONSimpleType(type); + } +}; + +const jsonToCLType = (json: any): CLType => { + if (typeof json === typeof 'str') { + return jsonToSimpleType(json); + } else if (typeof json === typeof {}) { + if (ListType.TypeId in json) { + const innerType = jsonToCLType(json[ListType.TypeId]); + return CLTypeHelper.list(innerType); + } else if (Tuple1Type.TypeId in json) { + const t0Type = jsonToCLType(json[Tuple1Type.TypeId][0]); + return CLTypeHelper.tuple1(t0Type); + } else if (Tuple2Type.TypeId in json) { + const innerTypes = json[Tuple2Type.TypeId]; + const t0Type = jsonToCLType(innerTypes[0]); + const t1Type = jsonToCLType(innerTypes[1]); + return CLTypeHelper.tuple2(t0Type, t1Type); + } else if (Tuple3Type.TypeId in json) { + const innerTypes = json[Tuple2Type.TypeId]; + const t0Type = jsonToCLType(innerTypes[0]); + const t1Type = jsonToCLType(innerTypes[1]); + const t2Type = jsonToCLType(innerTypes[2]); + return CLTypeHelper.tuple3(t0Type, t1Type, t2Type); + } else if (ByteArrayType.TypeId in json) { + const size = json[ByteArrayType.TypeId]; + return CLTypeHelper.byteArray(size); + } else if (OptionType.TypeId in json) { + const innerType = jsonToCLType(json[OptionType.TypeId]); + return CLTypeHelper.option(innerType); + } else if (MapType.TypeId in json) { + const keyType = jsonToCLType(json[MapType.TypeId].key); + const valueType = jsonToCLType(json[MapType.TypeId].value); + return CLTypeHelper.map(keyType, valueType); + } else { + throw new Error(`The type ${json} is not supported`); + } + } else { + throw new Error(`The type ${json} is not supported`); + } +}; + /** * A Casper value, i.e. a value which can be stored and manipulated by smart contracts. * * It holds the underlying data as a type-erased, serialized array of bytes and also holds the * [[CLType]] of the underlying data as a separate member. */ +@jsonObject({ + onDeserialized: 'reconstruct' +}) export class CLValue implements ToBytes { + @jsonMember({ + name: 'cl_type', + serializer: clTypeToJSON, + deserializer: jsonToCLType + }) + public clType: CLType; + + @jsonMember({ + constructor: String + }) + public bytes: string; + + @jsonMember({ + name: 'parsed_to_json', + deserializer: v => v, + preserveNull: true + }) + public parsedToJson: any; + + private value: CLTypedAndToBytes; + /** - * Please use static methods to constructs a new `CLValue` + * Please use static methodsto constructs a new `CLValue` */ - private constructor(public value: CLTypedAndToBytes, public clType: CLType) { + private constructor(value: CLTypedAndToBytes, clType: CLType) { + this.value = value; + this.clType = clType; + this.bytes = encodeBase16(this.value.toBytes()); } public get clValueBytes() { @@ -1331,6 +1546,14 @@ export class CLValue implements ToBytes { return Result.Ok(clValue, clTypeRes.remainder); } + protected reconstruct() { + const v = fromBytesByCLType(this.clType, decodeBase16(this.bytes)); + if (v.hasError()) { + throw new Error('Failed to deserialize CLValue'); + } + this.value = v.value; + } + public static bool = (b: boolean) => { return CLValue.fromT(new Bool(b)); }; @@ -1423,10 +1646,16 @@ export class CLValue implements ToBytes { } public isBigNumber() { - return this.clType === SimpleType.U8 || this.clType === SimpleType.I32 || - this.clType === SimpleType.I64 || this.clType === SimpleType.U32 || - this.clType === SimpleType.U64 || this.clType === SimpleType.U128 || - this.clType === SimpleType.U256 || this.clType === SimpleType.U512; + return ( + this.clType === SimpleType.U8 || + this.clType === SimpleType.I32 || + this.clType === SimpleType.I64 || + this.clType === SimpleType.U32 || + this.clType === SimpleType.U64 || + this.clType === SimpleType.U128 || + this.clType === SimpleType.U256 || + this.clType === SimpleType.U512 + ); } public asBigNumber(): BigNumber { @@ -1434,7 +1663,7 @@ export class CLValue implements ToBytes { const numberCoder = this.value as NumberCoder; return BigNumber.from(numberCoder.val); } else { - throw new Error('The CLValue can\'t convert to BigNumber'); + throw new Error("The CLValue can't convert to BigNumber"); } } @@ -1444,7 +1673,7 @@ export class CLValue implements ToBytes { public asBoolean() { if (!this.isBoolean()) { - throw new Error('The CLValue can\'t convert to Boolean'); + throw new Error("The CLValue can't convert to Boolean"); } return (this.value as Bool).val; } @@ -1455,7 +1684,7 @@ export class CLValue implements ToBytes { public asString() { if (!this.isString()) { - throw new Error('The CLValue can\'t convert to String'); + throw new Error("The CLValue can't convert to String"); } return (this.value as StringValue).val; } @@ -1466,7 +1695,7 @@ export class CLValue implements ToBytes { public asPublicKey(): PublicKey { if (!this.isPublicKey()) { - throw new Error('The CLValue can\'t convert to PublicKey'); + throw new Error("The CLValue can't convert to PublicKey"); } return this.value as PublicKey; } @@ -1477,7 +1706,7 @@ export class CLValue implements ToBytes { public asKey() { if (!this.isKey()) { - throw new Error('The CLValue can\'t convert to Key'); + throw new Error("The CLValue can't convert to Key"); } return this.value as KeyValue; } @@ -1488,7 +1717,7 @@ export class CLValue implements ToBytes { public asURef() { if (!this.isURef()) { - throw new Error('The CLValue can\'t convert to URef'); + throw new Error("The CLValue can't convert to URef"); } return this.value as URef; } @@ -1697,7 +1926,7 @@ export class URef extends CLTypedAndToBytes { */ public static fromFormattedStr(input: string) { if (!input.startsWith(FORMATTED_STRING_PREFIX)) { - throw new Error('prefix is not \'uref-\''); + throw new Error("prefix is not 'uref-'"); } const parts = input.substring(FORMATTED_STRING_PREFIX.length).split('-', 2); if (parts.length !== 2) { @@ -1711,7 +1940,11 @@ export class URef extends CLTypedAndToBytes { } public toFormattedStr() { - return [FORMATTED_STRING_PREFIX, encodeBase16(this.uRefAddr), this.accessRights.toString(8)].join('-'); + return [ + FORMATTED_STRING_PREFIX, + encodeBase16(this.uRefAddr), + this.accessRights.toString(8) + ].join('-'); } /** diff --git a/packages/sdk/src/lib/DeployUtil.ts b/packages/sdk/src/lib/DeployUtil.ts index 978afc29..c71e63a8 100644 --- a/packages/sdk/src/lib/DeployUtil.ts +++ b/packages/sdk/src/lib/DeployUtil.ts @@ -8,7 +8,14 @@ import blake from 'blakejs'; import { Option } from './option'; import { decodeBase16, encodeBase16 } from './Conversions'; import humanizeDuration from 'humanize-duration'; -import { CLTypedAndToBytesHelper, CLTypeHelper, CLValue, PublicKey, ToBytes, U32 } from './CLValue'; +import { + CLTypedAndToBytesHelper, + CLTypeHelper, + CLValue, + PublicKey, + ToBytes, + U32 +} from './CLValue'; import { toBytesArrayU8, toBytesBytesArray, @@ -44,7 +51,9 @@ const shortEnglishHumanizer = humanizeDuration.humanizer({ } }); -const byteArrayJsonSerializer: (bytes: ByteArray) => string = (bytes: ByteArray) => { +const byteArrayJsonSerializer: (bytes: ByteArray) => string = ( + bytes: ByteArray +) => { return encodeBase16(bytes); }; @@ -89,8 +98,10 @@ export class DeployHeader implements ToBytes { public bodyHash: ByteArray; @jsonArrayMember(ByteArray, { - serializer: (value: ByteArray[]) => value.map(it => byteArrayJsonSerializer(it)), - deserializer: (json: any) => json.map((it: string) => byteArrayJsonDeserializer(it)) + serializer: (value: ByteArray[]) => + value.map(it => byteArrayJsonSerializer(it)), + deserializer: (json: any) => + json.map((it: string) => byteArrayJsonDeserializer(it)) }) public dependencies: ByteArray[]; @@ -143,8 +154,7 @@ export class DeployHeader implements ToBytes { * The cryptographic hash of a Deploy. */ class DeployHash implements ToBytes { - constructor(private hash: ByteArray) { - } + constructor(private hash: ByteArray) {} public toBytes(): ByteArray { return toBytesDeployHash(this.hash); @@ -156,10 +166,9 @@ export interface DeployJson { approvals: { signature: string; signer: string }[]; header: DeployHeader; payment: Record; - hash: string + hash: string; } - /** * A struct containing a signature and the public key of the signer. */ @@ -179,20 +188,10 @@ abstract class ExecutableDeployItemInternal implements ToBytes { public abstract toBytes(): ByteArray; public getArgByName(argName: string): CLValue | undefined { - return this.args.args[argName]; + return this.args.args.get(argName); } } -const argsSerializer = (args: RuntimeArgs) => encodeBase16(args.toBytes()); - -const argsDeserializer = (byteStr: string) => { - const argsRes = RuntimeArgs.fromBytes(decodeBase16(byteStr)); - if (argsRes.hasError()) { - throw new Error('Failed to deserialized RuntimeArgs'); - } - return argsRes.value; -}; - @jsonObject export class ModuleBytes extends ExecutableDeployItemInternal { public tag = 0; @@ -205,10 +204,8 @@ export class ModuleBytes extends ExecutableDeployItemInternal { public moduleBytes: Uint8Array; @jsonMember({ - serializer: argsSerializer, - deserializer: argsDeserializer - } - ) + constructor: RuntimeArgs + }) public args: RuntimeArgs; constructor(moduleBytes: Uint8Array, args: RuntimeArgs) { @@ -244,16 +241,11 @@ export class StoredContractByHash extends ExecutableDeployItemInternal { public entryPoint: string; @jsonMember({ - serializer: argsSerializer, - deserializer: argsDeserializer + constructor: RuntimeArgs }) public args: RuntimeArgs; - constructor( - hash: ByteArray, - entryPoint: string, - args: RuntimeArgs - ) { + constructor(hash: ByteArray, entryPoint: string, args: RuntimeArgs) { super(); this.entryPoint = entryPoint; @@ -285,16 +277,11 @@ export class StoredContractByName extends ExecutableDeployItemInternal { public entryPoint: string; @jsonMember({ - serializer: argsSerializer, - deserializer: argsDeserializer + constructor: RuntimeArgs }) public args: RuntimeArgs; - constructor( - name: string, - entryPoint: string, - args: RuntimeArgs - ) { + constructor(name: string, entryPoint: string, args: RuntimeArgs) { super(); this.name = name; @@ -326,8 +313,7 @@ export class StoredVersionedContractByName extends ExecutableDeployItemInternal public entryPoint: string; @jsonMember({ - serializer: argsSerializer, - deserializer: argsDeserializer + constructor: RuntimeArgs }) public args: RuntimeArgs; @@ -371,7 +357,6 @@ export class StoredVersionedContractByHash extends ExecutableDeployItemInternal }) public hash: Uint8Array; - @jsonMember({ constructor: Number, preserveNull: true @@ -385,8 +370,7 @@ export class StoredVersionedContractByHash extends ExecutableDeployItemInternal public entryPoint: string; @jsonMember({ - serializer: argsSerializer, - deserializer: argsDeserializer + constructor: RuntimeArgs }) public args: RuntimeArgs; @@ -426,8 +410,7 @@ export class Transfer extends ExecutableDeployItemInternal { public tag = 5; @jsonMember({ - serializer: argsSerializer, - deserializer: argsDeserializer + constructor: RuntimeArgs }) public args: RuntimeArgs; @@ -479,7 +462,6 @@ export class Transfer extends ExecutableDeployItemInternal { @jsonObject export class ExecutableDeployItem implements ToBytes { - @jsonMember({ name: 'ModuleBytes', constructor: ModuleBytes @@ -532,7 +514,9 @@ export class ExecutableDeployItem implements ToBytes { throw new Error('failed to serialize ExecutableDeployItemJsonWrapper'); } - public static fromExecutableDeployItemInternal(item: ExecutableDeployItemInternal) { + public static fromExecutableDeployItemInternal( + item: ExecutableDeployItemInternal + ) { const res = new ExecutableDeployItem(); switch (item.tag) { case 0: @@ -557,8 +541,13 @@ export class ExecutableDeployItem implements ToBytes { return res; } - public static newModuleBytes(moduleBytes: ByteArray, args: RuntimeArgs): ExecutableDeployItem { - return ExecutableDeployItem.fromExecutableDeployItemInternal(new ModuleBytes(moduleBytes, args)); + public static newModuleBytes( + moduleBytes: ByteArray, + args: RuntimeArgs + ): ExecutableDeployItem { + return ExecutableDeployItem.fromExecutableDeployItemInternal( + new ModuleBytes(moduleBytes, args) + ); } public static newStoredContractByHash( @@ -566,7 +555,9 @@ export class ExecutableDeployItem implements ToBytes { entryPoint: string, args: RuntimeArgs ) { - return ExecutableDeployItem.fromExecutableDeployItemInternal(new StoredContractByHash(hash, entryPoint, args)); + return ExecutableDeployItem.fromExecutableDeployItemInternal( + new StoredContractByHash(hash, entryPoint, args) + ); } public static newStoredContractByName( @@ -574,7 +565,9 @@ export class ExecutableDeployItem implements ToBytes { entryPoint: string, args: RuntimeArgs ) { - return ExecutableDeployItem.fromExecutableDeployItemInternal(new StoredContractByName(name, entryPoint, args)); + return ExecutableDeployItem.fromExecutableDeployItemInternal( + new StoredContractByName(name, entryPoint, args) + ); } public static newStoredVersionContractByHash( @@ -583,7 +576,9 @@ export class ExecutableDeployItem implements ToBytes { entryPoint: string, args: RuntimeArgs ) { - return ExecutableDeployItem.fromExecutableDeployItemInternal(new StoredVersionedContractByHash(hash, version, entryPoint, args)); + return ExecutableDeployItem.fromExecutableDeployItemInternal( + new StoredVersionedContractByHash(hash, version, entryPoint, args) + ); } public static newStoredVersionContractByName( @@ -592,7 +587,9 @@ export class ExecutableDeployItem implements ToBytes { entryPoint: string, args: RuntimeArgs ) { - return ExecutableDeployItem.fromExecutableDeployItemInternal(new StoredVersionedContractByName(name, version, entryPoint, args)); + return ExecutableDeployItem.fromExecutableDeployItemInternal( + new StoredVersionedContractByName(name, version, entryPoint, args) + ); } public static newTransfer( @@ -601,7 +598,9 @@ export class ExecutableDeployItem implements ToBytes { sourcePurse?: URef, id: number | null = null ) { - return ExecutableDeployItem.fromExecutableDeployItemInternal(new Transfer(amount, target, sourcePurse, id)); + return ExecutableDeployItem.fromExecutableDeployItemInternal( + new Transfer(amount, target, sourcePurse, id) + ); } public isModuleBytes(): boolean { @@ -632,7 +631,9 @@ export class ExecutableDeployItem implements ToBytes { return !!this.storedVersionedContractByName; } - public asStoredVersionContractByName(): StoredVersionedContractByName | undefined { + public asStoredVersionContractByName(): + | StoredVersionedContractByName + | undefined { return this.storedVersionedContractByName; } @@ -640,7 +641,9 @@ export class ExecutableDeployItem implements ToBytes { return !!this.storedVersionedContractByHash; } - public asStoredVersionContractByHash(): StoredVersionedContractByHash | undefined { + public asStoredVersionContractByHash(): + | StoredVersionedContractByHash + | undefined { return this.storedVersionedContractByHash; } @@ -793,13 +796,7 @@ export function makeDeploy( ); const serializedHeader = serializeHeader(header); const deployHash = blake.blake2b(serializedHeader, null, 32); - return new Deploy( - deployHash, - header, - payment, - session, - [] - ); + return new Deploy(deployHash, header, payment, session, []); } /** diff --git a/packages/sdk/src/lib/RuntimeArgs.ts b/packages/sdk/src/lib/RuntimeArgs.ts index 479e3985..5592a0ef 100644 --- a/packages/sdk/src/lib/RuntimeArgs.ts +++ b/packages/sdk/src/lib/RuntimeArgs.ts @@ -4,10 +4,10 @@ import { toBytesString, toBytesVecT } from './byterepr'; import { CLValue, Result, StringValue, ToBytes, U32 } from './CLValue'; import { concat } from '@ethersproject/bytes'; +import { jsonMapMember, jsonObject } from 'typedjson'; export class NamedArg implements ToBytes { - constructor(public name: string, public value: CLValue) { - } + constructor(public name: string, public value: CLValue) {} public toBytes(): ByteArray { return concat([toBytesString(this.name), this.value.toBytes()]); @@ -22,16 +22,27 @@ export class NamedArg implements ToBytes { if (clValueRes.hasError()) { return Result.Err(clValueRes.error); } - return Result.Ok(new NamedArg(nameRes.value.val, clValueRes.value), clValueRes.remainder); + return Result.Ok( + new NamedArg(nameRes.value.val, clValueRes.value), + clValueRes.remainder + ); } } +@jsonObject export class RuntimeArgs implements ToBytes { - constructor(public args: Record) { + @jsonMapMember(String, CLValue) + public args: Map; + + constructor(args: Map) { + this.args = args; } public static fromMap(args: Record) { - return new RuntimeArgs(args); + const map: Map = new Map( + Object.keys(args).map(k => [k, args[k]]) + ); + return new RuntimeArgs(map); } public static fromNamedArgs(namedArgs: NamedArg[]) { @@ -43,12 +54,12 @@ export class RuntimeArgs implements ToBytes { } public insert(key: string, value: CLValue) { - this.args[key] = value; + this.args.set(key, value); } public toBytes() { - const vec = Object.keys(this.args).map(a => { - return new NamedArg(a, this.args[a]); + const vec = Array.from(this.args.entries()).map((a: [string, CLValue]) => { + return new NamedArg(a[0], a[1]); }); return toBytesVecT(vec); diff --git a/packages/sdk/src/lib/StoredValue.ts b/packages/sdk/src/lib/StoredValue.ts index 9399e846..239b5adf 100644 --- a/packages/sdk/src/lib/StoredValue.ts +++ b/packages/sdk/src/lib/StoredValue.ts @@ -1,6 +1,5 @@ import { jsonArrayMember, jsonMember, jsonObject } from 'typedjson'; import { CLValue } from './CLValue'; -import { decodeBase16 } from './Conversions'; @jsonObject class NamedKey { @@ -48,31 +47,6 @@ class AccountJson { public actionThresholds: ActionThresholds; } -@jsonObject -class CLValueJson { - @jsonMember({ name: 'cl_type', constructor: String }) - public typeStr: string; - - @jsonMember({ - name: 'serialized_bytes', - deserializer: (b: string) => { - const res = CLValue.fromBytes(decodeBase16(b)); - if (res.hasError()) { - throw res.error; - } - return res.value; - } - }) - public value: CLValue; - - @jsonMember({ - name: 'parsed_to_json', - deserializer: v => v, - preserveNull: true - }) - public parsedToJson: string | number | null; -} - @jsonObject export class TransferJson { // Deploy that created the transfer @@ -181,9 +155,9 @@ export class EraInfoJson { @jsonObject export class StoredValue { - // StoredValue - @jsonMember({ constructor: CLValueJson }) - public CLValue?: CLValueJson; + // StoredVale + @jsonMember({ constructor: CLValue }) + public CLValue?: CLValue; // An account @jsonMember({ constructor: AccountJson }) public Account?: AccountJson; From d9cbd162a6fb2506d8367937ebf8cb7a680a9e2d Mon Sep 17 00:00:00 2001 From: Maciej Zielinski Date: Fri, 22 Jan 2021 17:22:36 +0100 Subject: [PATCH 17/22] Extracting all data from Deploy --- packages/sdk/src/lib/CLValue.ts | 83 +++++++++++++++--------- packages/sdk/src/lib/DeployUtil.ts | 23 ++++++- packages/sdk/src/lib/Keys.ts | 3 +- packages/sdk/src/lib/option.ts | 13 ++++ packages/sdk/test/lib/DeployUtil.test.ts | 38 +++++++++-- 5 files changed, 121 insertions(+), 39 deletions(-) diff --git a/packages/sdk/src/lib/CLValue.ts b/packages/sdk/src/lib/CLValue.ts index 7a9199fd..81d33c1e 100644 --- a/packages/sdk/src/lib/CLValue.ts +++ b/packages/sdk/src/lib/CLValue.ts @@ -834,6 +834,35 @@ export class MapValue extends CLTypedAndToBytes { } } +@staticImplements>() +class ByteArrayValue extends CLTypedAndToBytes { + constructor(public rawBytes: ByteArray) { + super(); + } + + public clType(): CLType { + return CLTypeHelper.byteArray(this.rawBytes.length); + } + + public toBytes(): ByteArray { + return toBytesBytesArray(this.rawBytes); + } + + public static fromBytes(bytes: ByteArray): Result { + const u32Res = U32.fromBytes(bytes); + if (u32Res.hasError()) { + return Result.Err(u32Res.error); + } + const size = u32Res.value.val as number; + if (u32Res.remainder.length < size) { + return Result.Err(FromBytesError.EarlyEndOfStream); + } + const b = new ByteArrayValue(u32Res.remainder.subarray(0, size)); + const rem = u32Res.remainder.subarray(size); + return Result.Ok(b, rem); + } +} + export interface ToJSON { toJSON: () => any; } @@ -1239,35 +1268,6 @@ export class CLTypeHelper { } } -@staticImplements>() -class ByteArrayValue extends CLTypedAndToBytes { - constructor(public rawBytes: ByteArray) { - super(); - } - - public clType(): CLType { - return CLTypeHelper.byteArray(this.rawBytes.length); - } - - public toBytes(): ByteArray { - return toBytesBytesArray(this.rawBytes); - } - - public static fromBytes(bytes: ByteArray): Result { - const u32Res = U32.fromBytes(bytes); - if (u32Res.hasError()) { - return Result.Err(u32Res.error); - } - const size = u32Res.value.val as number; - if (u32Res.remainder.length < size) { - return Result.Err(FromBytesError.EarlyEndOfStream); - } - const b = new ByteArrayValue(u32Res.remainder.subarray(0, size)); - const rem = u32Res.remainder.subarray(size); - return Result.Ok(b, rem); - } -} - export class CLTypedAndToBytesHelper { public static bool = (b: boolean) => { return new Bool(b); @@ -1499,7 +1499,7 @@ export class CLValue implements ToBytes { @jsonMember({ name: 'parsed_to_json', deserializer: v => v, - preserveNull: true + serializer: v => v }) public parsedToJson: any; @@ -1721,6 +1721,29 @@ export class CLValue implements ToBytes { } return this.value as URef; } + + public isBytesArray() { + return this.clType instanceof ByteArrayType; + } + + public asBytesArray() { + if (!this.isBytesArray()) { + throw new Error("The CLValue can't convert to BytesArray"); + } + return (this.value as ByteArrayValue).toBytes(); + } + + public isOption() { + return this.clType instanceof OptionType; + } + + public asOption() { + if (!this.isOption()) { + throw new Error("The CLValue can't convert to Option"); + } + return this.value as Option; + } + } export enum KeyVariant { diff --git a/packages/sdk/src/lib/DeployUtil.ts b/packages/sdk/src/lib/DeployUtil.ts index c71e63a8..c6a3790f 100644 --- a/packages/sdk/src/lib/DeployUtil.ts +++ b/packages/sdk/src/lib/DeployUtil.ts @@ -25,7 +25,7 @@ import { toBytesVecT } from './byterepr'; import { RuntimeArgs } from './RuntimeArgs'; -import JSBI from 'jsbi'; +// import JSBI from 'jsbi'; import { Keys, URef } from './index'; import { AsymmetricKey, SignatureAlgorithm } from './Keys'; import { BigNumberish } from '@ethersproject/bignumber'; @@ -514,6 +514,23 @@ export class ExecutableDeployItem implements ToBytes { throw new Error('failed to serialize ExecutableDeployItemJsonWrapper'); } + public getArgByName(name: string): CLValue | undefined { + if (this.isModuleBytes()) { + return this.moduleBytes!.getArgByName(name); + } else if (this.isStoredContractByHash()) { + return this.storedContractByHash!.getArgByName(name); + } else if (this.isStoredContractByName()) { + return this.storedContractByName!.getArgByName(name); + } else if (this.isStoredVersionContractByHash()) { + return this.storedVersionedContractByHash!.getArgByName(name); + } else if (this.isStoredVersionContractByName()) { + return this.storedVersionedContractByName!.getArgByName(name); + } else if (this.isTransfer()) { + return this.transfer!.getArgByName(name); + } + throw new Error('failed to serialize ExecutableDeployItemJsonWrapper'); + } + public static fromExecutableDeployItemInternal( item: ExecutableDeployItemInternal ) { @@ -856,7 +873,7 @@ export const setSignature = ( * * @param paymentAmount the number of motes paying to execution engine */ -export const standardPayment = (paymentAmount: bigint | JSBI) => { +export const standardPayment = (paymentAmount: BigNumberish) => { const paymentArgs = RuntimeArgs.fromMap({ amount: CLValue.u512(paymentAmount.toString()) }); @@ -883,5 +900,5 @@ export const deployToJson = (deploy: Deploy) => { */ export const deployFromJson = (json: any) => { const serializer = new TypedJSON(Deploy); - return serializer.parse(json); + return serializer.parse(json.deploy); }; diff --git a/packages/sdk/src/lib/Keys.ts b/packages/sdk/src/lib/Keys.ts index 487d0bae..18345816 100644 --- a/packages/sdk/src/lib/Keys.ts +++ b/packages/sdk/src/lib/Keys.ts @@ -2,7 +2,8 @@ import * as fs from 'fs'; import * as nacl from 'tweetnacl-ts'; import { SignKeyPair, SignLength } from 'tweetnacl-ts'; import { decodeBase64 } from 'tweetnacl-util'; -import { encodeBase16, encodeBase64, PublicKey } from '../index'; +import { encodeBase16, encodeBase64} from '../index'; +import { PublicKey } from '../lib/index'; import { byteHash } from './Contracts'; import { ec as EC } from 'elliptic'; import * as secp256k1 from 'ethereum-cryptography/secp256k1'; diff --git a/packages/sdk/src/lib/option.ts b/packages/sdk/src/lib/option.ts index 534bf435..8f2b1e38 100644 --- a/packages/sdk/src/lib/option.ts +++ b/packages/sdk/src/lib/option.ts @@ -3,6 +3,7 @@ import { CLType, CLTypedAndToBytes, CLTypeHelper, + CLValue, fromBytesByCLType, FromBytesError, OptionType, @@ -56,6 +57,18 @@ export class Option extends CLTypedAndToBytes { return this.t !== null; } + /** + * Extract value. + * + * @returns CLValue if the `Option` has some value. + */ + public getSome(): CLValue { + if (!this.isSome()){ + throw new Error('Value is None'); + }; + return CLValue.fromT(this.t!); + } + /** * Serializes the `Option` into an array of bytes. */ diff --git a/packages/sdk/test/lib/DeployUtil.test.ts b/packages/sdk/test/lib/DeployUtil.test.ts index 4db662a9..5a8b79ea 100644 --- a/packages/sdk/test/lib/DeployUtil.test.ts +++ b/packages/sdk/test/lib/DeployUtil.test.ts @@ -1,15 +1,43 @@ -import { DeployHeader } from '../../src/lib/DeployUtil'; -import { expect } from 'chai'; -import { Keys } from '../../src/lib'; +import { expect, assert } from 'chai'; +import { Keys, DeployUtil, CasperClient } from '../../src/lib'; import { TypedJSON } from 'typedjson'; describe('DeployUtil', () => { it('should stringify/parse DeployHeader correctly', function() { const ed25519Key = Keys.Ed25519.new(); - const deployHeader = new DeployHeader(ed25519Key.publicKey, 123456, 654321, 10, Uint8Array.from(Array(32).fill(42)), [Uint8Array.from(Array(32).fill(2))], 'test-network'); - const serializer = new TypedJSON(DeployHeader); + const deployHeader = new DeployUtil.DeployHeader(ed25519Key.publicKey, 123456, 654321, 10, Uint8Array.from(Array(32).fill(42)), [Uint8Array.from(Array(32).fill(2))], 'test-network'); + const serializer = new TypedJSON(DeployUtil.DeployHeader); const json = serializer.stringify(deployHeader); const deployHeader1 = serializer.parse(json); expect(deployHeader1).to.deep.equal(deployHeader); }); + + it('should allow to extract data from Transfer', function() { + const senderKey = Keys.Ed25519.new(); + const recipientKey = Keys.Ed25519.new(); + const networkName = 'test-network'; + const paymentAmount = 10000000000000; + const transferAmount = 10; + const id = 34; + + let deployParams = new DeployUtil.DeployParams( + senderKey.publicKey, + networkName + ); + let session = DeployUtil.ExecutableDeployItem.newTransfer(transferAmount, recipientKey.publicKey, undefined, id); + let payment = DeployUtil.standardPayment(paymentAmount); + let deploy = DeployUtil.makeDeploy(deployParams, session, payment); + + let json = DeployUtil.deployToJson(deploy); + console.log(json); + deploy = DeployUtil.deployFromJson(json)!; + + console.log(deploy); + // assert.isTrue(deploy.isTransfer()); + // assert.isTrue(deploy.isStandardPayment()); + // assert.deepEqual(deploy.payment.getArgByName('amount')!.asBigNumber().toNumber(), paymentAmount); + // assert.deepEqual(deploy.session.getArgByName('amount')!.asBigNumber().toNumber(), transferAmount); + // assert.deepEqual(deploy.session.getArgByName('target')!.asBytesArray(), recipientKey.accountHash()); + // assert.deepEqual(deploy.session.getArgByName('id')!.asOption().getSome().asBigNumber().toNumber(), id); + }); }); From 5a5da8ceefeca2f230d23b4a02eda4da2c316762 Mon Sep 17 00:00:00 2001 From: Maciej Zielinski Date: Mon, 25 Jan 2021 15:50:27 +0100 Subject: [PATCH 18/22] Deploy toJSON. --- packages/sdk/src/lib/CLValue.ts | 120 ++++++++++----------- packages/sdk/src/lib/DeployUtil.ts | 57 +++++----- packages/sdk/src/lib/Keys.ts | 2 +- packages/sdk/src/lib/RuntimeArgs.ts | 2 +- packages/sdk/src/lib/option.ts | 6 +- packages/sdk/test/lib/CasperClient.test.ts | 27 +---- packages/sdk/test/lib/DeployUtil.test.ts | 40 ++++--- packages/sdk/test/lib/Keys.test.ts | 1 - packages/sdk/test/lib/RuntimeArgs.test.ts | 38 ++++++- 9 files changed, 154 insertions(+), 139 deletions(-) diff --git a/packages/sdk/src/lib/CLValue.ts b/packages/sdk/src/lib/CLValue.ts index 81d33c1e..d65e13fb 100644 --- a/packages/sdk/src/lib/CLValue.ts +++ b/packages/sdk/src/lib/CLValue.ts @@ -13,6 +13,7 @@ import { Option } from './option'; import { byteHash } from './Contracts'; import { SignatureAlgorithm } from './Keys'; import { jsonMember, jsonObject } from 'typedjson'; +import { ByteArray } from 'tweetnacl-ts'; const ED25519_PUBLIC_KEY_LENGTH = 32; const SECP256K1_PUBLIC_KEY_LENGTH = 33; @@ -197,7 +198,7 @@ export class Bool extends CLTypedAndToBytes { abstract class NumberCoder extends CLTypedAndToBytes { public bitSize: number; public signed: boolean; - public val: BigNumberish; + public val: BigNumber; public name: string; protected constructor(bitSize: number, signed: boolean, value: BigNumberish) { @@ -205,7 +206,7 @@ abstract class NumberCoder extends CLTypedAndToBytes { this.name = (signed ? 'i' : 'u') + bitSize; this.bitSize = bitSize; this.signed = signed; - this.val = value; + this.val = BigNumber.from(value); } public toBytes = (): ByteArray => { @@ -330,20 +331,7 @@ export class U128 extends NumberCoder { } public static fromBytes(bytes: ByteArray): Result { - if (bytes.length < 1) { - return Result.Err(FromBytesError.EarlyEndOfStream); - } - const tmp = Uint8Array.from(bytes); - const n = tmp[0]; - if (n === 0 || n > 16) { - return Result.Err(FromBytesError.FormattingError); - } - if (n + 1 > bytes.length) { - return Result.Err(FromBytesError.EarlyEndOfStream); - } - const u128Bytes = tmp.subarray(1, 1 + n); - const rem = tmp.subarray(1 + n); - return Result.Ok(new U128(BigNumber.from(u128Bytes.reverse())), rem); + return fromBytesBigInt(bytes, 128); } } @@ -358,20 +346,7 @@ class U256 extends NumberCoder { } public static fromBytes(bytes: ByteArray): Result { - if (bytes.length < 1) { - return Result.Err(FromBytesError.EarlyEndOfStream); - } - const tmp = Uint8Array.from(bytes); - const n = tmp[0]; - if (n === 0 || n > 32) { - return Result.Err(FromBytesError.FormattingError); - } - if (n + 1 > bytes.length) { - return Result.Err(FromBytesError.EarlyEndOfStream); - } - const u256Bytes = tmp.subarray(1, 1 + n); - const rem = tmp.subarray(1 + n); - return Result.Ok(new U256(BigNumber.from(u256Bytes.reverse())), rem); + return fromBytesBigInt(bytes, 256); } } @@ -386,20 +361,7 @@ export class U512 extends NumberCoder { } public static fromBytes(bytes: ByteArray): Result { - if (bytes.length < 1) { - return Result.Err(FromBytesError.EarlyEndOfStream); - } - const tmp = Uint8Array.from(bytes); - const n = tmp[0]; - if (n === 0 || n > 64) { - return Result.Err(FromBytesError.FormattingError); - } - if (n + 1 > bytes.length) { - return Result.Err(FromBytesError.EarlyEndOfStream); - } - const u512Bytes = tmp.subarray(1, 1 + n); - const rem = tmp.subarray(1 + n); - return Result.Ok(new U512(BigNumber.from(u512Bytes.reverse())), rem); + return fromBytesBigInt(bytes, 512); } } @@ -437,7 +399,7 @@ export class StringValue extends CLTypedAndToBytes { if (res.hasError()) { return Result.Err(res.error); } - const len = res.value.val as number; + const len = res.value.val.toNumber(); const str = Buffer.from(res.remainder.subarray(0, len)).toString('utf8'); return Result.Ok( new StringValue(str), @@ -552,7 +514,7 @@ export class List extends CLTypedAndToBytes { if (u32Res.hasError()) { return Result.Err(u32Res.error); } - const size = u32Res.value.val as number; + const size = u32Res.value.val.toNumber(); const vec = []; let remainder = u32Res.remainder; for (let i = 0; i < size; i++) { @@ -814,7 +776,7 @@ export class MapValue extends CLTypedAndToBytes { if (u32Res.hasError()) { return Result.Err(u32Res.error); } - const size = u32Res.value.val as number; + const size = u32Res.value.val.toNumber(); const vec: MapEntry[] = []; let remainder = u32Res.remainder; for (let i = 0; i < size; i++) { @@ -849,20 +811,45 @@ class ByteArrayValue extends CLTypedAndToBytes { } public static fromBytes(bytes: ByteArray): Result { - const u32Res = U32.fromBytes(bytes); - if (u32Res.hasError()) { - return Result.Err(u32Res.error); - } - const size = u32Res.value.val as number; - if (u32Res.remainder.length < size) { - return Result.Err(FromBytesError.EarlyEndOfStream); - } - const b = new ByteArrayValue(u32Res.remainder.subarray(0, size)); - const rem = u32Res.remainder.subarray(size); - return Result.Ok(b, rem); + const b = new ByteArrayValue(bytes); + return Result.Ok(b, bytes.subarray(32)); } } +const fromBytesBigInt: ( + bytes: ByteArray, + bitSize: number +) => Result = (bytes: ByteArray, bitSize: number) => { + const byteSize = bitSize / 8; + if (bytes.length < 1) { + return Result.Err(FromBytesError.EarlyEndOfStream); + } + const tmp = Uint8Array.from(bytes); + const n = tmp[0]; + if (n > byteSize) { + return Result.Err(FromBytesError.FormattingError); + } + if (n + 1 > bytes.length) { + return Result.Err(FromBytesError.EarlyEndOfStream); + } + let bigIntBytes; + if (n === 0) { + bigIntBytes = [0]; + } else { + bigIntBytes = tmp.subarray(1, 1 + n); + } + const rem = tmp.subarray(1 + n); + if (bitSize === 128) { + return Result.Ok(new U128(BigNumber.from(bigIntBytes.reverse())), rem); + } else if (bitSize === 256) { + return Result.Ok(new U256(BigNumber.from(bigIntBytes.reverse())), rem); + } else if (bitSize === 512) { + return Result.Ok(new U512(BigNumber.from(bigIntBytes.reverse())), rem); + } else { + return Result.Err(FromBytesError.FormattingError); + } +}; + export interface ToJSON { toJSON: () => any; } @@ -1121,7 +1108,7 @@ export class CLTypeHelper { return Result.Err(sizeRes.error); } return Result.Ok( - CLTypeHelper.byteArray(sizeRes.value.val as number), + CLTypeHelper.byteArray(sizeRes.value.val.toNumber()), sizeRes.remainder ); } @@ -1480,8 +1467,15 @@ const jsonToCLType = (json: any): CLType => { * It holds the underlying data as a type-erased, serialized array of bytes and also holds the * [[CLType]] of the underlying data as a separate member. */ + +function deserializeCLValue(_a: any, _b: any) { + let v = fromBytesByCLType(_a.clType, decodeBase16(_a.bytes)); + let ret = CLValue.fromT(v.value); + return ret; +} + @jsonObject({ - onDeserialized: 'reconstruct' + initializer: (a, b) => deserializeCLValue(a, b) }) export class CLValue implements ToBytes { @jsonMember({ @@ -1496,11 +1490,6 @@ export class CLValue implements ToBytes { }) public bytes: string; - @jsonMember({ - name: 'parsed_to_json', - deserializer: v => v, - serializer: v => v - }) public parsedToJson: any; private value: CLTypedAndToBytes; @@ -1743,7 +1732,6 @@ export class CLValue implements ToBytes { } return this.value as Option; } - } export enum KeyVariant { diff --git a/packages/sdk/src/lib/DeployUtil.ts b/packages/sdk/src/lib/DeployUtil.ts index c6a3790f..2862c7d7 100644 --- a/packages/sdk/src/lib/DeployUtil.ts +++ b/packages/sdk/src/lib/DeployUtil.ts @@ -423,33 +423,10 @@ export class Transfer extends ExecutableDeployItemInternal { * @param id user-defined transfer id */ constructor( - amount: BigNumberish, - target: URef | PublicKey, - sourcePurse?: URef, - id: number | null = null + args: RuntimeArgs ) { super(); - const runtimeArgs = RuntimeArgs.fromMap({}); - runtimeArgs.insert('amount', CLValue.u512(amount)); - if (sourcePurse) { - runtimeArgs.insert('source', CLValue.uref(sourcePurse)); - } - if (target instanceof URef) { - runtimeArgs.insert('target', CLValue.uref(target)); - } else if (target instanceof PublicKey) { - runtimeArgs.insert('target', CLValue.byteArray(target.toAccountHash())); - } else { - throw new Error('Please specify target'); - } - if (!id) { - runtimeArgs.insert('id', CLValue.option(null, CLTypeHelper.u64())); - } else { - runtimeArgs.insert( - 'id', - CLValue.option(CLTypedAndToBytesHelper.u64(id), CLTypeHelper.u64()) - ); - } - this.args = runtimeArgs; + this.args = args; } public toBytes(): ByteArray { @@ -609,14 +586,42 @@ export class ExecutableDeployItem implements ToBytes { ); } + /** + * Constructor for Transfer deploy item. + * @param amount The number of motes to transfer + * @param target URef of the target purse or the public key of target account. You could generate this public key from accountHex by PublicKey.fromHex + * @param sourcePurse URef of the source purse. If this is omitted, the main purse of the account creating this \ + * transfer will be used as the source purse + * @param id user-defined transfer id + */ public static newTransfer( amount: BigNumberish, target: URef | PublicKey, sourcePurse?: URef, id: number | null = null ) { + const runtimeArgs = RuntimeArgs.fromMap({}); + runtimeArgs.insert('amount', CLValue.u512(amount)); + if (sourcePurse) { + runtimeArgs.insert('source', CLValue.uref(sourcePurse)); + } + if (target instanceof URef) { + runtimeArgs.insert('target', CLValue.uref(target)); + } else if (target instanceof PublicKey) { + runtimeArgs.insert('target', CLValue.byteArray(target.toAccountHash())); + } else { + throw new Error('Please specify target'); + } + if (!id) { + runtimeArgs.insert('id', CLValue.option(null, CLTypeHelper.u64())); + } else { + runtimeArgs.insert( + 'id', + CLValue.option(CLTypedAndToBytesHelper.u64(id), CLTypeHelper.u64()) + ); + } return ExecutableDeployItem.fromExecutableDeployItemInternal( - new Transfer(amount, target, sourcePurse, id) + new Transfer(runtimeArgs) ); } diff --git a/packages/sdk/src/lib/Keys.ts b/packages/sdk/src/lib/Keys.ts index 18345816..0ab3c5a4 100644 --- a/packages/sdk/src/lib/Keys.ts +++ b/packages/sdk/src/lib/Keys.ts @@ -2,7 +2,7 @@ import * as fs from 'fs'; import * as nacl from 'tweetnacl-ts'; import { SignKeyPair, SignLength } from 'tweetnacl-ts'; import { decodeBase64 } from 'tweetnacl-util'; -import { encodeBase16, encodeBase64} from '../index'; +import { encodeBase16, encodeBase64 } from '../index'; import { PublicKey } from '../lib/index'; import { byteHash } from './Contracts'; import { ec as EC } from 'elliptic'; diff --git a/packages/sdk/src/lib/RuntimeArgs.ts b/packages/sdk/src/lib/RuntimeArgs.ts index 5592a0ef..54952ff5 100644 --- a/packages/sdk/src/lib/RuntimeArgs.ts +++ b/packages/sdk/src/lib/RuntimeArgs.ts @@ -70,7 +70,7 @@ export class RuntimeArgs implements ToBytes { if (sizeRes.hasError()) { return Result.Err(sizeRes.error); } - const size = sizeRes.value.val as number; + const size = sizeRes.value.val.toNumber(); let remainBytes = sizeRes.remainder; const res: NamedArg[] = []; for (let i = 0; i < size; i++) { diff --git a/packages/sdk/src/lib/option.ts b/packages/sdk/src/lib/option.ts index 8f2b1e38..1fd7750a 100644 --- a/packages/sdk/src/lib/option.ts +++ b/packages/sdk/src/lib/option.ts @@ -63,9 +63,9 @@ export class Option extends CLTypedAndToBytes { * @returns CLValue if the `Option` has some value. */ public getSome(): CLValue { - if (!this.isSome()){ + if (!this.isSome()) { throw new Error('Value is None'); - }; + } return CLValue.fromT(this.t!); } @@ -88,7 +88,7 @@ export class Option extends CLTypedAndToBytes { if (u8Res.hasError()) { return Result.Err(u8Res.error); } - const optionTag = u8Res.value.val as number; + const optionTag = u8Res.value.val.toNumber(); if (optionTag === OPTION_TAG_NONE) { return Result.Ok(new Option(null, type.innerType), u8Res.remainder); } else if (optionTag === OPTION_TAG_SOME) { diff --git a/packages/sdk/test/lib/CasperClient.test.ts b/packages/sdk/test/lib/CasperClient.test.ts index 59d9628a..6d4ea780 100644 --- a/packages/sdk/test/lib/CasperClient.test.ts +++ b/packages/sdk/test/lib/CasperClient.test.ts @@ -3,9 +3,8 @@ import { CasperClient } from '../../src/lib/CasperClient'; import * as fs from 'fs'; import * as path from 'path'; import * as os from 'os'; -import { DeployUtil, Keys, PublicKey } from '../../src/lib'; -import { Ed25519, Secp256K1, SignatureAlgorithm } from '../../src/lib/Keys'; -import JSBI from 'jsbi'; +import { Keys } from '../../src/lib'; +import { Secp256K1, SignatureAlgorithm } from '../../src/lib/Keys'; import { decodeBase16 } from '../../src'; let casperClient: CasperClient; @@ -118,28 +117,6 @@ describe('CasperClient', () => { expect(loadedKeyPair.privateKey).to.deep.equal(edKeyPair.privateKey); }); - // todo move it to example once we publish transfer feature - describe.skip('transfer', async () => { - const transfer = new DeployUtil.Transfer( - 100000000000000, - PublicKey.fromHex( - '01a72eb5ba13e243d40e56b0547536e3ad1584eee5a386c7be5d5a1f94c09a6592' - ) - ); - const keyPair = Ed25519.parseKeyFiles( - '../server/test.public.key', - '../server/test.private.key' - ); - const deploy = casperClient.makeTransferDeploy( - new DeployUtil.DeployParams(keyPair.publicKey, 'casper-net-1'), - transfer, - DeployUtil.standardPayment(JSBI.BigInt(100000000000000)) - ); - const signedDeploy = casperClient.signDeploy(deploy, keyPair); - const deployHash = await casperClient.putDeploy(signedDeploy); - console.log(deployHash); - }); - it('should create a HK wallet and derive child account correctly', function () { const seed = 'fffcf9f6f3f0edeae7e4e1dedbd8d5d2cfccc9c6c3c0bdbab7b4b1aeaba8a5a29f9c999693908d8a8784817e7b7875726f6c696663605d5a5754514e4b484542'; diff --git a/packages/sdk/test/lib/DeployUtil.test.ts b/packages/sdk/test/lib/DeployUtil.test.ts index 5a8b79ea..3a45c04a 100644 --- a/packages/sdk/test/lib/DeployUtil.test.ts +++ b/packages/sdk/test/lib/DeployUtil.test.ts @@ -1,11 +1,19 @@ import { expect, assert } from 'chai'; -import { Keys, DeployUtil, CasperClient } from '../../src/lib'; +import { Keys, DeployUtil } from '../../src/lib'; import { TypedJSON } from 'typedjson'; describe('DeployUtil', () => { it('should stringify/parse DeployHeader correctly', function() { const ed25519Key = Keys.Ed25519.new(); - const deployHeader = new DeployUtil.DeployHeader(ed25519Key.publicKey, 123456, 654321, 10, Uint8Array.from(Array(32).fill(42)), [Uint8Array.from(Array(32).fill(2))], 'test-network'); + const deployHeader = new DeployUtil.DeployHeader( + ed25519Key.publicKey, + 123456, + 654321, + 10, + Uint8Array.from(Array(32).fill(42)), + [Uint8Array.from(Array(32).fill(2))], + 'test-network' + ); const serializer = new TypedJSON(DeployUtil.DeployHeader); const json = serializer.stringify(deployHeader); const deployHeader1 = serializer.parse(json); @@ -24,20 +32,28 @@ describe('DeployUtil', () => { senderKey.publicKey, networkName ); - let session = DeployUtil.ExecutableDeployItem.newTransfer(transferAmount, recipientKey.publicKey, undefined, id); + let session = DeployUtil.ExecutableDeployItem.newTransfer( + transferAmount, + recipientKey.publicKey, + undefined, + id + ); let payment = DeployUtil.standardPayment(paymentAmount); let deploy = DeployUtil.makeDeploy(deployParams, session, payment); - + deploy = DeployUtil.signDeploy(deploy, senderKey); + deploy = DeployUtil.signDeploy(deploy, recipientKey); + let json = DeployUtil.deployToJson(deploy); - console.log(json); deploy = DeployUtil.deployFromJson(json)!; - console.log(deploy); - // assert.isTrue(deploy.isTransfer()); - // assert.isTrue(deploy.isStandardPayment()); - // assert.deepEqual(deploy.payment.getArgByName('amount')!.asBigNumber().toNumber(), paymentAmount); - // assert.deepEqual(deploy.session.getArgByName('amount')!.asBigNumber().toNumber(), transferAmount); - // assert.deepEqual(deploy.session.getArgByName('target')!.asBytesArray(), recipientKey.accountHash()); - // assert.deepEqual(deploy.session.getArgByName('id')!.asOption().getSome().asBigNumber().toNumber(), id); + assert.isTrue(deploy.isTransfer()); + assert.isTrue(deploy.isStandardPayment()); + assert.deepEqual(deploy.header.account, senderKey.publicKey); + assert.deepEqual(deploy.payment.getArgByName('amount')!.asBigNumber().toNumber(), paymentAmount); + assert.deepEqual(deploy.session.getArgByName('amount')!.asBigNumber().toNumber(), transferAmount); + assert.deepEqual(deploy.session.getArgByName('target')!.asBytesArray(), recipientKey.accountHash()); + assert.deepEqual(deploy.session.getArgByName('id')!.asOption().getSome().asBigNumber().toNumber(), id); + assert.deepEqual(deploy.approvals[0].signer, senderKey.accountHex()); + assert.deepEqual(deploy.approvals[1].signer, recipientKey.accountHex()); }); }); diff --git a/packages/sdk/test/lib/Keys.test.ts b/packages/sdk/test/lib/Keys.test.ts index 43ff91bd..59ba6266 100644 --- a/packages/sdk/test/lib/Keys.test.ts +++ b/packages/sdk/test/lib/Keys.test.ts @@ -166,7 +166,6 @@ describe('Secp256K1', () => { // expect we could sign the message and verify the signature later. const message = Buffer.from('hello world'); const signature = signKeyPair.sign(Buffer.from(message)); - console.log(encodeBase16(signature)); // expect we could verify the signature created by ourself expect(signKeyPair.verfiy(signature, message)).to.equal(true); }); diff --git a/packages/sdk/test/lib/RuntimeArgs.test.ts b/packages/sdk/test/lib/RuntimeArgs.test.ts index 8ffe79df..1cbdcf1b 100644 --- a/packages/sdk/test/lib/RuntimeArgs.test.ts +++ b/packages/sdk/test/lib/RuntimeArgs.test.ts @@ -1,7 +1,7 @@ -import { expect } from 'chai'; -import { CLValue, RuntimeArgs } from '../../src/lib'; +import { expect, assert } from 'chai'; +import { CLValue, RuntimeArgs, CLTypedAndToBytesHelper } from '../../src/lib'; import { decodeBase16 } from '../../src'; -import { toBytesU32 } from '../../src/lib/byterepr'; +import { TypedJSON } from 'typedjson'; describe(`RuntimeArgs`, () => { it('should serialize RuntimeArgs correctly', () => { @@ -38,8 +38,38 @@ describe(`RuntimeArgs`, () => { it('should serialize empty NamedArgs correctly', () => { const truth = decodeBase16('00000000'); const runtimeArgs = RuntimeArgs.fromMap({}); - console.log(toBytesU32(0)); const bytes = runtimeArgs.toBytes(); expect(bytes).to.deep.eq(truth); }); + + it('should deserialize U512', () => { + let value = CLValue.u512(43000000000); + let serializer = new TypedJSON(CLValue); + let str = serializer.stringify(value); + assert.deepEqual(value.asBigNumber(), serializer.parse(str)!.asBigNumber()); + }); + + it('should deserialize Option of U512', () => { + let a = CLTypedAndToBytesHelper.u512(123); + let value = CLValue.option(a, a.clType()); + let serializer = new TypedJSON(CLValue); + let str = serializer.stringify(value); + let parsed = serializer.parse(str)!; + assert.deepEqual( + value.asOption().getSome().asBigNumber(), + parsed.asOption().getSome().asBigNumber()); + }); + + it('should deserialize RuntimeArgs', () => { + let a = CLTypedAndToBytesHelper.u512(123); + const runtimeArgs = RuntimeArgs.fromMap({ + a: CLValue.option(null, a.clType()) + }); + let serializer = new TypedJSON(RuntimeArgs); + let str = serializer.stringify(runtimeArgs); + let value = serializer.parse(str)!; + assert.isTrue( + value.args.get('a')!.asOption().isNone() + ); + }); }); From 5d784ce4d0d020535342df6e47a95b762ace6fd6 Mon Sep 17 00:00:00 2001 From: Maciej Zielinski Date: Mon, 25 Jan 2021 15:51:14 +0100 Subject: [PATCH 19/22] Deploy toJSON + version bump to 1.0.11. --- packages/sdk/package.json | 2 +- packages/sdk/src/lib/DeployUtil.ts | 4 +-- packages/sdk/test/lib/CasperClient.test.ts | 2 +- packages/sdk/test/lib/DeployUtil.test.ts | 33 ++++++++++++++++++---- packages/sdk/test/lib/RuntimeArgs.test.ts | 16 +++++++++-- 5 files changed, 44 insertions(+), 13 deletions(-) diff --git a/packages/sdk/package.json b/packages/sdk/package.json index 46a5abf2..ef300be2 100644 --- a/packages/sdk/package.json +++ b/packages/sdk/package.json @@ -1,6 +1,6 @@ { "name": "casper-client-sdk", - "version": "1.0.10", + "version": "1.0.11", "license": "Apache 2.0", "description": "SDK to interact with the Casper blockchain", "main": "dist/index.js", diff --git a/packages/sdk/src/lib/DeployUtil.ts b/packages/sdk/src/lib/DeployUtil.ts index 2862c7d7..beb458eb 100644 --- a/packages/sdk/src/lib/DeployUtil.ts +++ b/packages/sdk/src/lib/DeployUtil.ts @@ -422,9 +422,7 @@ export class Transfer extends ExecutableDeployItemInternal { * transfer will be used as the source purse * @param id user-defined transfer id */ - constructor( - args: RuntimeArgs - ) { + constructor(args: RuntimeArgs) { super(); this.args = args; } diff --git a/packages/sdk/test/lib/CasperClient.test.ts b/packages/sdk/test/lib/CasperClient.test.ts index 6d4ea780..c11d8f2c 100644 --- a/packages/sdk/test/lib/CasperClient.test.ts +++ b/packages/sdk/test/lib/CasperClient.test.ts @@ -117,7 +117,7 @@ describe('CasperClient', () => { expect(loadedKeyPair.privateKey).to.deep.equal(edKeyPair.privateKey); }); - it('should create a HK wallet and derive child account correctly', function () { + it('should create a HK wallet and derive child account correctly', function() { const seed = 'fffcf9f6f3f0edeae7e4e1dedbd8d5d2cfccc9c6c3c0bdbab7b4b1aeaba8a5a29f9c999693908d8a8784817e7b7875726f6c696663605d5a5754514e4b484542'; const hdKey = casperClient.newHdWallet(decodeBase16(seed)); diff --git a/packages/sdk/test/lib/DeployUtil.test.ts b/packages/sdk/test/lib/DeployUtil.test.ts index 3a45c04a..f136ba45 100644 --- a/packages/sdk/test/lib/DeployUtil.test.ts +++ b/packages/sdk/test/lib/DeployUtil.test.ts @@ -42,17 +42,40 @@ describe('DeployUtil', () => { let deploy = DeployUtil.makeDeploy(deployParams, session, payment); deploy = DeployUtil.signDeploy(deploy, senderKey); deploy = DeployUtil.signDeploy(deploy, recipientKey); - + let json = DeployUtil.deployToJson(deploy); deploy = DeployUtil.deployFromJson(json)!; assert.isTrue(deploy.isTransfer()); assert.isTrue(deploy.isStandardPayment()); assert.deepEqual(deploy.header.account, senderKey.publicKey); - assert.deepEqual(deploy.payment.getArgByName('amount')!.asBigNumber().toNumber(), paymentAmount); - assert.deepEqual(deploy.session.getArgByName('amount')!.asBigNumber().toNumber(), transferAmount); - assert.deepEqual(deploy.session.getArgByName('target')!.asBytesArray(), recipientKey.accountHash()); - assert.deepEqual(deploy.session.getArgByName('id')!.asOption().getSome().asBigNumber().toNumber(), id); + assert.deepEqual( + deploy.payment + .getArgByName('amount')! + .asBigNumber() + .toNumber(), + paymentAmount + ); + assert.deepEqual( + deploy.session + .getArgByName('amount')! + .asBigNumber() + .toNumber(), + transferAmount + ); + assert.deepEqual( + deploy.session.getArgByName('target')!.asBytesArray(), + recipientKey.accountHash() + ); + assert.deepEqual( + deploy.session + .getArgByName('id')! + .asOption() + .getSome() + .asBigNumber() + .toNumber(), + id + ); assert.deepEqual(deploy.approvals[0].signer, senderKey.accountHex()); assert.deepEqual(deploy.approvals[1].signer, recipientKey.accountHex()); }); diff --git a/packages/sdk/test/lib/RuntimeArgs.test.ts b/packages/sdk/test/lib/RuntimeArgs.test.ts index 1cbdcf1b..3ef16770 100644 --- a/packages/sdk/test/lib/RuntimeArgs.test.ts +++ b/packages/sdk/test/lib/RuntimeArgs.test.ts @@ -56,8 +56,15 @@ describe(`RuntimeArgs`, () => { let str = serializer.stringify(value); let parsed = serializer.parse(str)!; assert.deepEqual( - value.asOption().getSome().asBigNumber(), - parsed.asOption().getSome().asBigNumber()); + value + .asOption() + .getSome() + .asBigNumber(), + parsed + .asOption() + .getSome() + .asBigNumber() + ); }); it('should deserialize RuntimeArgs', () => { @@ -69,7 +76,10 @@ describe(`RuntimeArgs`, () => { let str = serializer.stringify(runtimeArgs); let value = serializer.parse(str)!; assert.isTrue( - value.args.get('a')!.asOption().isNone() + value.args + .get('a')! + .asOption() + .isNone() ); }); }); From 9ee4f2b3eb5e43645edf79ae2c73b7dec9735df8 Mon Sep 17 00:00:00 2001 From: Maciej Zielinski Date: Mon, 25 Jan 2021 16:39:51 +0100 Subject: [PATCH 20/22] Server to use sdk from the repo --- packages/sdk/src/lib/CLValue.ts | 13 ++++++------- packages/server/package.json | 2 +- 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/packages/sdk/src/lib/CLValue.ts b/packages/sdk/src/lib/CLValue.ts index d65e13fb..5da9668d 100644 --- a/packages/sdk/src/lib/CLValue.ts +++ b/packages/sdk/src/lib/CLValue.ts @@ -1461,19 +1461,18 @@ const jsonToCLType = (json: any): CLType => { } }; +function deserializeCLValue(_a: any, _b: any) { + const v = fromBytesByCLType(_a.clType, decodeBase16(_a.bytes)); + const ret = CLValue.fromT(v.value); + return ret; +} + /** * A Casper value, i.e. a value which can be stored and manipulated by smart contracts. * * It holds the underlying data as a type-erased, serialized array of bytes and also holds the * [[CLType]] of the underlying data as a separate member. */ - -function deserializeCLValue(_a: any, _b: any) { - let v = fromBytesByCLType(_a.clType, decodeBase16(_a.bytes)); - let ret = CLValue.fromT(v.value); - return ret; -} - @jsonObject({ initializer: (a, b) => deserializeCLValue(a, b) }) diff --git a/packages/server/package.json b/packages/server/package.json index 48d7040f..4f369d0c 100644 --- a/packages/server/package.json +++ b/packages/server/package.json @@ -40,7 +40,7 @@ "@types/http-proxy-middleware": "^0.19.3", "auth0": "^2.28.0", "blakejs": "^1.1.0", - "casper-client-sdk": "1.0.10", + "casper-client-sdk": "file:./../sdk", "command-line-args": "^5.1.1", "cron": "1.7.2", "dotenv": "^8.0.0", From 814ef8e002bd47e41cec6ebd1c85637027a3e7c3 Mon Sep 17 00:00:00 2001 From: Maciej Zielinski Date: Mon, 25 Jan 2021 16:49:26 +0100 Subject: [PATCH 21/22] Build fixes --- packages/server/src/StoredFaucetService.ts | 2 +- packages/ui/src/containers/DeployContractsContainer.ts | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/packages/server/src/StoredFaucetService.ts b/packages/server/src/StoredFaucetService.ts index 8cdeb414..7197ab2e 100644 --- a/packages/server/src/StoredFaucetService.ts +++ b/packages/server/src/StoredFaucetService.ts @@ -30,7 +30,7 @@ export class StoredFaucetService { sessionArgs ); - const payment = DeployUtil.standardPayment(this.paymentAmount); + const payment = DeployUtil.standardPayment(this.paymentAmount.toString()); const deployByName = DeployUtil.makeDeploy( new DeployUtil.DeployParams( this.contractKeys.publicKey, diff --git a/packages/ui/src/containers/DeployContractsContainer.ts b/packages/ui/src/containers/DeployContractsContainer.ts index 4c571b17..52e9d406 100644 --- a/packages/ui/src/containers/DeployContractsContainer.ts +++ b/packages/ui/src/containers/DeployContractsContainer.ts @@ -367,7 +367,6 @@ export class DeployContractsContainer { return DeployArgumentParser.buildArgument(arg); }) ); - const paymentAmount = JSBI.BigInt(config.paymentAmount.value); let sessionExecutionItem: DeployUtil.ExecutableDeployItem | null = null; if (config.contractType.value === DeployUtil.ContractType.WASM) { @@ -398,7 +397,7 @@ export class DeployContractsContainer { window.config.network?.chainName || '' ), sessionExecutionItem, - DeployUtil.standardPayment(paymentAmount) + DeployUtil.standardPayment(config.paymentAmount.value) ); } return Promise.resolve(null); From c180dbb8d9bfd0193caf0484fa4ae14e6dfd787d Mon Sep 17 00:00:00 2001 From: Maciej Zielinski Date: Mon, 25 Jan 2021 17:30:40 +0100 Subject: [PATCH 22/22] PR fixes --- packages/sdk/src/lib/CLValue.ts | 2 +- packages/ui/package.json | 2 +- yarn.lock | 19 +++++++++++++++++++ 3 files changed, 21 insertions(+), 2 deletions(-) diff --git a/packages/sdk/src/lib/CLValue.ts b/packages/sdk/src/lib/CLValue.ts index 5da9668d..690b1a63 100644 --- a/packages/sdk/src/lib/CLValue.ts +++ b/packages/sdk/src/lib/CLValue.ts @@ -345,7 +345,7 @@ class U256 extends NumberCoder { return SimpleType.U256; } - public static fromBytes(bytes: ByteArray): Result { + public static fromBytes(bytes: ByteArray): Result { return fromBytesBigInt(bytes, 256); } } diff --git a/packages/ui/package.json b/packages/ui/package.json index 1c570b28..40992591 100644 --- a/packages/ui/package.json +++ b/packages/ui/package.json @@ -21,7 +21,7 @@ "@types/react-router-dom": "^5.0.1", "bootstrap": "^4.3.1", "bootstrap-components": "^1.1.287", - "casper-client-sdk": "^1.0.10", + "casper-client-sdk": "file:./../sdk", "change-case": "^4.1.1", "chart.js": "^2.9.3", "d3": "^5.9.7", diff --git a/yarn.lock b/yarn.lock index f95d2d8f..3d5a1230 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5560,6 +5560,25 @@ caseless@~0.12.0: resolved "https://registry.yarnpkg.com/caseless/-/caseless-0.12.0.tgz#1b681c21ff84033c826543090689420d187151dc" integrity sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw= +"casper-client-sdk@file:./packages/sdk": + version "1.0.11" + dependencies: + "@ethersproject/bignumber" "^5.0.8" + "@ethersproject/bytes" "^5.0.5" + "@ethersproject/constants" "^5.0.5" + axios "^0.21.1" + blakejs "^1.1.0" + ethereum-cryptography "^0.1.3" + humanize-duration "^3.24.0" + jsbi "^3.1.2" + key-encoder "^2.0.3" + reflect-metadata "^0.1.13" + rpc-client-js "^1.0.2" + rxjs "^6.5.3" + tweetnacl-ts "^1.0.3" + tweetnacl-util "^0.15.0" + typedjson "^1.6.0-rc2" + casper-client-sdk@latest: version "1.0.8" resolved "https://registry.yarnpkg.com/casper-client-sdk/-/casper-client-sdk-1.0.8.tgz#00efdee87e040677a39178311f0f6ad127f984a8"