Skip to content

Commit

Permalink
Harden serialization code for JS minimizers
Browse files Browse the repository at this point in the history
  • Loading branch information
vgrichina committed Aug 22, 2019
1 parent e8174f5 commit a10ff41
Show file tree
Hide file tree
Showing 6 changed files with 100 additions and 98 deletions.
82 changes: 40 additions & 42 deletions lib/transaction.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 3 additions & 2 deletions lib/utils/serialize.d.ts

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

23 changes: 12 additions & 11 deletions lib/utils/serialize.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

49 changes: 24 additions & 25 deletions src.ts/transaction.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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<Function, any>([
[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],
Expand All @@ -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));
Expand Down
37 changes: 20 additions & 17 deletions src.ts/utils/serialize.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ export function base_decode(value: string): Uint8Array {

const INITIAL_LENGTH = 1024;

export type Schema = Map<Function, any>;

/// Binary encoder.
export class BinaryWriter {
buf: Buffer;
Expand Down Expand Up @@ -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) {
Expand All @@ -168,39 +170,40 @@ 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);
break;
}
}
} 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) {
Expand All @@ -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);
}
2 changes: 1 addition & 1 deletion test/serialize.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down

0 comments on commit a10ff41

Please sign in to comment.