diff --git a/lib/transaction.js b/lib/transaction.js index 1d91feb8b2..99ba167bf2 100644 --- a/lib/transaction.js +++ b/lib/transaction.js @@ -121,48 +121,46 @@ exports.SignedTransaction = SignedTransaction; class Action extends Enum { } exports.Action = Action; -const SCHEMA = { - Signature: { kind: 'struct', fields: [['keyType', 'u8'], ['data', [32]]] }, - SignedTransaction: { kind: 'struct', fields: [['transaction', Transaction], ['signature', Signature]] }, - Transaction: { - kind: 'struct', fields: [['signerId', 'string'], ['publicKey', PublicKey], ['nonce', 'u64'], ['receiverId', 'string'], ['actions', [Action]]] - }, - PublicKey: { - kind: 'struct', fields: [['keyType', 'u8'], ['data', [32]]] - }, - AccessKey: { kind: 'struct', fields: [ - ['nonce', 'u64'], - ['permission', AccessKeyPermission], - ] }, - AccessKeyPermission: { kind: 'enum', field: 'enum', values: [ - ['functionCall', FunctionCallPermission], - ['fullAccess', FullAccessPermission], - ] }, - FunctionCallPermission: { kind: 'struct', fields: [ - ['allowance', { kind: 'option', type: 'u128' }], - ['receiverId', 'string'], - ['methodNames', ['string']], - ] }, - FullAccessPermission: { kind: 'struct', fields: [] }, - Action: { kind: 'enum', field: 'enum', values: [ - ['createAccount', CreateAccount], - ['deployContract', DeployContract], - ['functionCall', functionCall], - ['transfer', transfer], - ['stake', stake], - ['addKey', addKey], - ['deleteKey', deleteKey], - ['deleteAccount', deleteAccount], - ] }, - CreateAccount: { kind: 'struct', fields: [] }, - DeployContract: { kind: 'struct', fields: [['code', ['u8']]] }, - FunctionCall: { kind: 'struct', fields: [['methodName', 'string'], ['args', ['u8']], ['gas', 'u64'], ['deposit', 'u128']] }, - Transfer: { kind: 'struct', fields: [['deposit', 'u128']] }, - Stake: { kind: 'struct', fields: [['stake', 'u128'], ['publicKey', PublicKey]] }, - AddKey: { kind: 'struct', fields: [['publicKey', PublicKey], ['accessKey', AccessKey]] }, - DeleteKey: { kind: 'struct', fields: [['publicKey', PublicKey]] }, - DeleteAccount: { kind: 'struct', fields: [['beneficiaryId', 'string']] }, -}; +const SCHEMA = new Map([ + [Signature, { kind: 'struct', fields: [['keyType', 'u8'], ['data', [32]]] }], + [SignedTransaction, { kind: 'struct', fields: [['transaction', Transaction], ['signature', Signature]] }], + [Transaction, { kind: 'struct', fields: [['signerId', 'string'], ['publicKey', PublicKey], ['nonce', 'u64'], ['receiverId', 'string'], ['actions', [Action]]] }], + [PublicKey, { + kind: 'struct', fields: [['keyType', 'u8'], ['data', [32]]] + }], + [AccessKey, { kind: 'struct', fields: [ + ['nonce', 'u64'], + ['permission', AccessKeyPermission], + ] }], + [AccessKeyPermission, { kind: 'enum', field: 'enum', values: [ + ['functionCall', FunctionCallPermission], + ['fullAccess', FullAccessPermission], + ] }], + [FunctionCallPermission, { kind: 'struct', fields: [ + ['allowance', { kind: 'option', type: 'u128' }], + ['receiverId', 'string'], + ['methodNames', ['string']], + ] }], + [FullAccessPermission, { kind: 'struct', fields: [] }], + [Action, { kind: 'enum', field: 'enum', values: [ + ['createAccount', CreateAccount], + ['deployContract', DeployContract], + ['functionCall', functionCall], + ['transfer', transfer], + ['stake', stake], + ['addKey', addKey], + ['deleteKey', deleteKey], + ['deleteAccount', deleteAccount], + ] }], + [CreateAccount, { kind: 'struct', fields: [] }], + [DeployContract, { kind: 'struct', fields: [['code', ['u8']]] }], + [FunctionCall, { kind: 'struct', fields: [['methodName', 'string'], ['args', ['u8']], ['gas', 'u64'], ['deposit', 'u128']] }], + [Transfer, { kind: 'struct', fields: [['deposit', 'u128']] }], + [Stake, { kind: 'struct', fields: [['stake', 'u128'], ['publicKey', PublicKey]] }], + [AddKey, { kind: 'struct', fields: [['publicKey', PublicKey], ['accessKey', AccessKey]] }], + [DeleteKey, { kind: 'struct', fields: [['publicKey', PublicKey]] }], + [DeleteAccount, { kind: 'struct', fields: [['beneficiaryId', 'string']] }], +]); async function signTransaction(receiverId, nonce, actions, signer, accountId, networkId) { const publicKey = new PublicKey(await signer.getPublicKey(accountId, networkId)); const transaction = new Transaction({ signerId: accountId, publicKey, nonce, receiverId, actions }); diff --git a/lib/utils/serialize.d.ts b/lib/utils/serialize.d.ts index b2100acecc..c0fa4fc110 100644 --- a/lib/utils/serialize.d.ts +++ b/lib/utils/serialize.d.ts @@ -2,6 +2,7 @@ import BN from 'bn.js'; export declare function base_encode(value: Uint8Array | string): string; export declare function base_decode(value: string): Uint8Array; +export declare type Schema = Map; export declare class BinaryWriter { buf: Buffer; length: number; @@ -30,5 +31,5 @@ export declare class BinaryReader { read_fixed_array(len: number): Uint8Array; read_array(fn: any): any[]; } -export declare function serialize(schema: any, obj: any): Uint8Array; -export declare function deserialize(schema: any, classType: any, buffer: Buffer): any; +export declare function serialize(schema: Schema, obj: any): Uint8Array; +export declare function deserialize(schema: Schema, classType: any, buffer: Buffer): any; diff --git a/lib/utils/serialize.js b/lib/utils/serialize.js index 5dd1c3f759..a4beea05f3 100644 --- a/lib/utils/serialize.js +++ b/lib/utils/serialize.js @@ -150,19 +150,20 @@ function serializeField(schema, value, fieldType, writer) { } } function serializeStruct(schema, obj, writer) { - const className = obj.constructor.name; - if (schema[className] === undefined) { - throw new Error(`Class ${className} is missing in schema`); + console.log('serializeStruct', schema); + const structSchema = schema.get(obj.constructor); + if (!structSchema) { + throw new Error(`Class ${obj.constructor.name} is missing in schema`); } - if (schema[className].kind === 'struct') { - schema[className].fields.map(([fieldName, fieldType]) => { + if (structSchema.kind === 'struct') { + structSchema.fields.map(([fieldName, fieldType]) => { serializeField(schema, obj[fieldName], fieldType, writer); }); } - else if (schema[className].kind === 'enum') { - const name = obj[schema[className].field]; - for (let idx = 0; idx < schema[className].values.length; ++idx) { - const [fieldName, fieldType] = schema[className].values[idx]; + else if (structSchema.kind === 'enum') { + const name = obj[structSchema.field]; + for (let idx = 0; idx < structSchema.values.length; ++idx) { + const [fieldName, fieldType] = structSchema.values[idx]; if (fieldName === name) { writer.write_u8(idx); serializeField(schema, obj[fieldName], fieldType, writer); @@ -171,7 +172,7 @@ function serializeStruct(schema, obj, writer) { } } else { - throw new Error(`Unexpected schema kind: ${schema[className].kind} for ${className}`); + throw new Error(`Unexpected schema kind: ${structSchema.kind} for ${obj.constructor.name}`); } } /// Serialize given object using schema of the form: @@ -199,7 +200,7 @@ function deserializeField(schema, fieldType, reader) { } } function deserializeStruct(schema, classType, reader) { - const fields = schema[classType.name].fields.map(([fieldName, fieldType]) => { + const fields = schema.get(classType).fields.map(([fieldName, fieldType]) => { return deserializeField(schema, fieldType, reader); }); return new classType(...fields); diff --git a/src.ts/transaction.ts b/src.ts/transaction.ts index e805353ade..0b79cdc845 100644 --- a/src.ts/transaction.ts +++ b/src.ts/transaction.ts @@ -149,28 +149,27 @@ export class Action extends Enum { deleteAccount: DeleteAccount; } -const SCHEMA = { - Signature: {kind: 'struct', fields: [['keyType', 'u8'], ['data', [32]]]}, - SignedTransaction: {kind: 'struct', fields: [['transaction', Transaction], ['signature', Signature]]}, - Transaction: { - kind: 'struct', fields: [['signerId', 'string'], ['publicKey', PublicKey], ['nonce', 'u64'], ['receiverId', 'string'], ['actions', [Action]]] }, - PublicKey: { - kind: 'struct', fields: [['keyType', 'u8'], ['data', [32]]] }, - AccessKey: { kind: 'struct', fields: [ +const SCHEMA = new Map([ + [Signature, {kind: 'struct', fields: [['keyType', 'u8'], ['data', [32]]]}], + [SignedTransaction, {kind: 'struct', fields: [['transaction', Transaction], ['signature', Signature]]}], + [Transaction, { kind: 'struct', fields: [['signerId', 'string'], ['publicKey', PublicKey], ['nonce', 'u64'], ['receiverId', 'string'], ['actions', [Action]]] }], + [PublicKey, { + kind: 'struct', fields: [['keyType', 'u8'], ['data', [32]]] }], + [AccessKey, { kind: 'struct', fields: [ ['nonce', 'u64'], ['permission', AccessKeyPermission], - ]}, - AccessKeyPermission: {kind: 'enum', field: 'enum', values: [ + ]}], + [AccessKeyPermission, {kind: 'enum', field: 'enum', values: [ ['functionCall', FunctionCallPermission], ['fullAccess', FullAccessPermission], - ]}, - FunctionCallPermission: {kind: 'struct', fields: [ + ]}], + [FunctionCallPermission, {kind: 'struct', fields: [ ['allowance', {kind: 'option', type: 'u128'}], ['receiverId', 'string'], ['methodNames', ['string']], - ]}, - FullAccessPermission: {kind: 'struct', fields: []}, - Action: {kind: 'enum', field: 'enum', values: [ + ]}], + [FullAccessPermission, {kind: 'struct', fields: []}], + [Action, {kind: 'enum', field: 'enum', values: [ ['createAccount', CreateAccount], ['deployContract', DeployContract], ['functionCall', functionCall], @@ -179,16 +178,16 @@ const SCHEMA = { ['addKey', addKey], ['deleteKey', deleteKey], ['deleteAccount', deleteAccount], - ]}, - CreateAccount: { kind: 'struct', fields: [] }, - DeployContract: { kind: 'struct', fields: [['code', ['u8']]] }, - FunctionCall: { kind: 'struct', fields: [['methodName', 'string'], ['args', ['u8']], ['gas', 'u64'], ['deposit', 'u128']] }, - Transfer: { kind: 'struct', fields: [['deposit', 'u128']] }, - Stake: { kind: 'struct', fields: [['stake', 'u128'], ['publicKey', PublicKey]] }, - AddKey: { kind: 'struct', fields: [['publicKey', PublicKey], ['accessKey', AccessKey]] }, - DeleteKey: { kind: 'struct', fields: [['publicKey', PublicKey]] }, - DeleteAccount: { kind: 'struct', fields: [['beneficiaryId', 'string']] }, -}; + ]}], + [CreateAccount, { kind: 'struct', fields: [] }], + [DeployContract, { kind: 'struct', fields: [['code', ['u8']]] }], + [FunctionCall, { kind: 'struct', fields: [['methodName', 'string'], ['args', ['u8']], ['gas', 'u64'], ['deposit', 'u128']] }], + [Transfer, { kind: 'struct', fields: [['deposit', 'u128']] }], + [Stake, { kind: 'struct', fields: [['stake', 'u128'], ['publicKey', PublicKey]] }], + [AddKey, { kind: 'struct', fields: [['publicKey', PublicKey], ['accessKey', AccessKey]] }], + [DeleteKey, { kind: 'struct', fields: [['publicKey', PublicKey]] }], + [DeleteAccount, { kind: 'struct', fields: [['beneficiaryId', 'string']] }], +]); export async function signTransaction(receiverId: string, nonce: number, actions: Action[], signer: Signer, accountId?: string, networkId?: string): Promise<[Uint8Array, SignedTransaction]> { const publicKey = new PublicKey(await signer.getPublicKey(accountId, networkId)); diff --git a/src.ts/utils/serialize.ts b/src.ts/utils/serialize.ts index b41570a1a8..0e0cb2612f 100644 --- a/src.ts/utils/serialize.ts +++ b/src.ts/utils/serialize.ts @@ -16,6 +16,8 @@ export function base_decode(value: string): Uint8Array { const INITIAL_LENGTH = 1024; +export type Schema = Map; + /// Binary encoder. export class BinaryWriter { buf: Buffer; @@ -141,7 +143,7 @@ export class BinaryReader { } } -function serializeField(schema: any, value: any, fieldType: any, writer: any) { +function serializeField(schema: Schema, value: any, fieldType: any, writer: any) { if (typeof fieldType === 'string') { writer[`write_${fieldType}`](value); } else if (fieldType instanceof Array) { @@ -168,19 +170,20 @@ function serializeField(schema: any, value: any, fieldType: any, writer: any) { } } -function serializeStruct(schema: any, obj: any, writer: any) { - const className = obj.constructor.name; - if (schema[className] === undefined) { - throw new Error(`Class ${className} is missing in schema`); +function serializeStruct(schema: Schema, obj: any, writer: any) { + console.log('serializeStruct', schema); + const structSchema = schema.get(obj.constructor); + if (!structSchema) { + throw new Error(`Class ${obj.constructor.name} is missing in schema`); } - if (schema[className].kind === 'struct') { - schema[className].fields.map(([fieldName, fieldType]: [any, any]) => { + if (structSchema.kind === 'struct') { + structSchema.fields.map(([fieldName, fieldType]: [any, any]) => { serializeField(schema, obj[fieldName], fieldType, writer); }); - } else if (schema[className].kind === 'enum') { - const name = obj[schema[className].field]; - for (let idx = 0; idx < schema[className].values.length; ++idx) { - const [fieldName, fieldType]: [any, any] = schema[className].values[idx]; + } else if (structSchema.kind === 'enum') { + const name = obj[structSchema.field]; + for (let idx = 0; idx < structSchema.values.length; ++idx) { + const [fieldName, fieldType]: [any, any] = structSchema.values[idx]; if (fieldName === name) { writer.write_u8(idx); serializeField(schema, obj[fieldName], fieldType, writer); @@ -188,19 +191,19 @@ function serializeStruct(schema: any, obj: any, writer: any) { } } } else { - throw new Error(`Unexpected schema kind: ${schema[className].kind} for ${className}`); + throw new Error(`Unexpected schema kind: ${structSchema.kind} for ${obj.constructor.name}`); } } /// Serialize given object using schema of the form: /// { class_name -> [ [field_name, field_type], .. ], .. } -export function serialize(schema: any, obj: any): Uint8Array { +export function serialize(schema: Schema, obj: any): Uint8Array { const writer = new BinaryWriter(); serializeStruct(schema, obj, writer); return writer.toArray(); } -function deserializeField(schema: any, fieldType: any, reader: any): any { +function deserializeField(schema: Schema, fieldType: any, reader: any): any { if (typeof fieldType === 'string') { return reader[`read_${fieldType}`](); } else if (fieldType instanceof Array) { @@ -214,15 +217,15 @@ function deserializeField(schema: any, fieldType: any, reader: any): any { } } -function deserializeStruct(schema: any, classType: any, reader: any) { - const fields = schema[classType.name].fields.map(([fieldName, fieldType]: [any, any]) => { +function deserializeStruct(schema: Schema, classType: any, reader: any) { + const fields = schema.get(classType).fields.map(([fieldName, fieldType]: [any, any]) => { return deserializeField(schema, fieldType, reader); }); return new classType(...fields); } /// Deserializes object from bytes using schema. -export function deserialize(schema: any, classType: any, buffer: Buffer): any { +export function deserialize(schema: Schema, classType: any, buffer: Buffer): any { const reader = new BinaryReader(buffer); return deserializeStruct(schema, classType, reader); } diff --git a/test/serialize.test.js b/test/serialize.test.js index 275b9d8679..eb3749f853 100644 --- a/test/serialize.test.js +++ b/test/serialize.test.js @@ -12,7 +12,7 @@ class Test { test('serialize object', async () => { const value = new Test(255, 20, '123', [1, 2, 3]); - const schema = { 'Test': {kind: 'struct', fields: [['x', 'u8'], ['y', 'u64'], ['z', 'string'], ['q', [3]]] } }; + const schema = new Map([[Test, {kind: 'struct', fields: [['x', 'u8'], ['y', 'u64'], ['z', 'string'], ['q', [3]]] }]]); let buf = nearlib.utils.serialize.serialize(schema, value); let new_value = nearlib.utils.serialize.deserialize(schema, Test, buf); expect(new_value.x).toEqual(255);