mirror of
https://github.com/cosmos/cosmjs.git
synced 2025-03-11 14:09:15 +00:00
proto-signing: Add encode/decode methods to registry
This commit is contained in:
parent
c1770dbe0c
commit
bb2bbcca2d
89
packages/proto-signing/src/magic.spec.ts
Normal file
89
packages/proto-signing/src/magic.spec.ts
Normal file
@ -0,0 +1,89 @@
|
||||
/* eslint-disable @typescript-eslint/camelcase */
|
||||
import { Message } from "protobufjs";
|
||||
|
||||
import { cosmosField, cosmosMessage } from "./decorator";
|
||||
import { Registry } from "./registry";
|
||||
|
||||
describe("registry magic demo", () => {
|
||||
it("works with a custom msg", () => {
|
||||
const nestedTypeUrl = "/demo.MsgNestedDemo";
|
||||
const typeUrl = "/demo.MsgDemo";
|
||||
const myRegistry = new Registry();
|
||||
|
||||
@cosmosMessage(myRegistry, nestedTypeUrl)
|
||||
class MsgNestedDemo extends Message<{}> {
|
||||
@cosmosField.string(1)
|
||||
public readonly foo?: string;
|
||||
}
|
||||
|
||||
@cosmosMessage(myRegistry, typeUrl)
|
||||
class MsgDemo extends Message<{}> {
|
||||
@cosmosField.boolean(1)
|
||||
public readonly booleanDemo?: boolean;
|
||||
|
||||
@cosmosField.string(2)
|
||||
public readonly stringDemo?: string;
|
||||
|
||||
@cosmosField.bytes(3)
|
||||
public readonly bytesDemo?: Uint8Array;
|
||||
|
||||
@cosmosField.int64(4)
|
||||
public readonly int64Demo?: number;
|
||||
|
||||
@cosmosField.uint64(5)
|
||||
public readonly uint64Demo?: number;
|
||||
|
||||
@cosmosField.repeatedString(6)
|
||||
public readonly listDemo?: readonly string[];
|
||||
|
||||
@cosmosField.message(7, MsgNestedDemo)
|
||||
public readonly nestedDemo?: MsgNestedDemo;
|
||||
}
|
||||
|
||||
const msgNestedDemoFields = {
|
||||
foo: "bar",
|
||||
};
|
||||
const msgDemoFields = {
|
||||
booleanDemo: true,
|
||||
stringDemo: "example text",
|
||||
bytesDemo: Uint8Array.from([1, 2, 3, 4, 5, 6, 7, 8]),
|
||||
int64Demo: -123,
|
||||
uint64Demo: 123,
|
||||
listDemo: ["this", "is", "a", "list"],
|
||||
nestedDemo: msgNestedDemoFields,
|
||||
};
|
||||
const txBodyFields = {
|
||||
messages: [{ typeUrl: typeUrl, value: msgDemoFields }],
|
||||
memo: "Some memo",
|
||||
timeoutHeight: 9999,
|
||||
extensionOptions: [],
|
||||
};
|
||||
const txBodyBytes = myRegistry.encode({
|
||||
typeUrl: "/cosmos.tx.TxBody",
|
||||
value: txBodyFields,
|
||||
});
|
||||
|
||||
const txBodyDecoded = myRegistry.decode({
|
||||
typeUrl: "/cosmos.tx.TxBody",
|
||||
value: txBodyBytes,
|
||||
});
|
||||
expect(txBodyDecoded.memo).toEqual(txBodyFields.memo);
|
||||
// int64Demo and uint64Demo decode to Long in Node
|
||||
expect(Number(txBodyDecoded.timeoutHeight)).toEqual(txBodyFields.timeoutHeight);
|
||||
expect(txBodyDecoded.extensionOptions).toEqual(txBodyFields.extensionOptions);
|
||||
|
||||
const msgDemoDecoded = txBodyDecoded.messages[0] as MsgDemo;
|
||||
expect(msgDemoDecoded).toBeInstanceOf(MsgDemo);
|
||||
expect(msgDemoDecoded.booleanDemo).toEqual(msgDemoFields.booleanDemo);
|
||||
expect(msgDemoDecoded.stringDemo).toEqual(msgDemoFields.stringDemo);
|
||||
// bytesDemo decodes to a Buffer in Node
|
||||
expect(Uint8Array.from(msgDemoDecoded.bytesDemo!)).toEqual(msgDemoFields.bytesDemo);
|
||||
// int64Demo and uint64Demo decode to Long in Node
|
||||
expect(Number(msgDemoDecoded.int64Demo)).toEqual(msgDemoFields.int64Demo);
|
||||
expect(Number(msgDemoDecoded.uint64Demo)).toEqual(msgDemoFields.uint64Demo);
|
||||
expect(msgDemoDecoded.listDemo).toEqual(msgDemoFields.listDemo);
|
||||
|
||||
expect(msgDemoDecoded.nestedDemo).toBeInstanceOf(MsgNestedDemo);
|
||||
expect(msgDemoDecoded.nestedDemo!.foo).toEqual(msgDemoFields.nestedDemo.foo);
|
||||
});
|
||||
});
|
@ -1,3 +1,4 @@
|
||||
/* eslint-disable @typescript-eslint/camelcase */
|
||||
import protobuf from "protobufjs";
|
||||
|
||||
import { cosmos_sdk as cosmosSdk, google } from "./generated/codecimpl";
|
||||
@ -8,24 +9,111 @@ export interface GeneratedType {
|
||||
readonly decode: (reader: protobuf.Reader | Uint8Array, length?: number) => any;
|
||||
}
|
||||
|
||||
export type EncodeObject = {
|
||||
readonly typeUrl: string;
|
||||
readonly value: any;
|
||||
};
|
||||
|
||||
export type DecodeObject = {
|
||||
readonly typeUrl: string;
|
||||
readonly value: Uint8Array;
|
||||
};
|
||||
|
||||
export type TxBodyValue = {
|
||||
readonly messages: readonly EncodeObject[];
|
||||
readonly memo?: string;
|
||||
readonly timeoutHeight?: number;
|
||||
readonly extensionOptions?: readonly any[];
|
||||
readonly nonCriticalExtensionOptions?: readonly any[];
|
||||
};
|
||||
|
||||
const defaultTypeUrls = {
|
||||
cosmosCoin: "/cosmos.Coin",
|
||||
cosmosMsgSend: "/cosmos.bank.MsgSend",
|
||||
cosmosTxBody: "/cosmos.tx.TxBody",
|
||||
googleAny: "/google.protobuf.Any",
|
||||
};
|
||||
|
||||
export class Registry {
|
||||
private readonly types: Map<string, GeneratedType>;
|
||||
|
||||
constructor(customTypes: Iterable<[string, GeneratedType]> = []) {
|
||||
const { cosmosCoin, cosmosMsgSend, cosmosTxBody, googleAny } = defaultTypeUrls;
|
||||
this.types = new Map<string, GeneratedType>([
|
||||
["/cosmos.Coin", cosmosSdk.v1.Coin],
|
||||
["/cosmos.bank.MsgSend", cosmosSdk.x.bank.v1.MsgSend],
|
||||
["/cosmos.tx.TxBody", cosmosSdk.tx.v1.TxBody],
|
||||
["/google.protobuf.Any", google.protobuf.Any],
|
||||
[cosmosCoin, cosmosSdk.v1.Coin],
|
||||
[cosmosMsgSend, cosmosSdk.x.bank.v1.MsgSend],
|
||||
[cosmosTxBody, cosmosSdk.tx.v1.TxBody],
|
||||
[googleAny, google.protobuf.Any],
|
||||
...customTypes,
|
||||
]);
|
||||
}
|
||||
|
||||
public register(name: string, type: GeneratedType): void {
|
||||
this.types.set(name, type);
|
||||
public register(typeUrl: string, type: GeneratedType): void {
|
||||
this.types.set(typeUrl, type);
|
||||
}
|
||||
|
||||
public lookupType(name: string): GeneratedType | undefined {
|
||||
return this.types.get(name);
|
||||
public lookupType(typeUrl: string): GeneratedType | undefined {
|
||||
return this.types.get(typeUrl);
|
||||
}
|
||||
|
||||
private lookupTypeWithError(typeUrl: string): GeneratedType {
|
||||
const type = this.lookupType(typeUrl);
|
||||
if (!type) {
|
||||
throw new Error(`Unregistered type url: ${typeUrl}`);
|
||||
}
|
||||
return type;
|
||||
}
|
||||
|
||||
public encode({ typeUrl, value }: EncodeObject): Uint8Array {
|
||||
if (typeUrl === defaultTypeUrls.cosmosTxBody) {
|
||||
return this.encodeTxBody(value);
|
||||
}
|
||||
const type = this.lookupTypeWithError(typeUrl);
|
||||
const created = type.create(value);
|
||||
return type.encode(created).finish();
|
||||
}
|
||||
|
||||
public encodeTxBody(txBodyFields: TxBodyValue): Uint8Array {
|
||||
const TxBody = this.lookupTypeWithError(defaultTypeUrls.cosmosTxBody);
|
||||
const Any = this.lookupTypeWithError(defaultTypeUrls.googleAny);
|
||||
|
||||
const wrappedMessages = txBodyFields.messages.map((message) => {
|
||||
const messageBytes = this.encode(message);
|
||||
return Any.create({
|
||||
type_url: message.typeUrl,
|
||||
value: messageBytes,
|
||||
});
|
||||
});
|
||||
const txBody = TxBody.create({
|
||||
...txBodyFields,
|
||||
messages: wrappedMessages,
|
||||
});
|
||||
return TxBody.encode(txBody).finish();
|
||||
}
|
||||
|
||||
public decode({ typeUrl, value }: DecodeObject): any {
|
||||
if (typeUrl === defaultTypeUrls.cosmosTxBody) {
|
||||
return this.decodeTxBody(value);
|
||||
}
|
||||
const type = this.lookupTypeWithError(typeUrl);
|
||||
return type.decode(value);
|
||||
}
|
||||
|
||||
public decodeTxBody(txBody: Uint8Array): cosmosSdk.tx.v1.TxBody {
|
||||
const TxBody = this.lookupTypeWithError(defaultTypeUrls.cosmosTxBody);
|
||||
const decodedTxBody = TxBody.decode(txBody);
|
||||
|
||||
return {
|
||||
...decodedTxBody,
|
||||
messages: decodedTxBody.messages.map(({ type_url: typeUrl, value }: google.protobuf.IAny) => {
|
||||
if (!typeUrl) {
|
||||
throw new Error("Missing type_url in Any");
|
||||
}
|
||||
if (!value) {
|
||||
throw new Error("Missing value in Any");
|
||||
}
|
||||
return this.decode({ typeUrl, value });
|
||||
}),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
25
packages/proto-signing/types/registry.d.ts
vendored
25
packages/proto-signing/types/registry.d.ts
vendored
@ -1,4 +1,5 @@
|
||||
import protobuf from "protobufjs";
|
||||
import { cosmos_sdk as cosmosSdk } from "./generated/codecimpl";
|
||||
export interface GeneratedType {
|
||||
readonly create: (properties?: { [k: string]: any }) => any;
|
||||
readonly encode: (
|
||||
@ -11,9 +12,29 @@ export interface GeneratedType {
|
||||
) => protobuf.Writer;
|
||||
readonly decode: (reader: protobuf.Reader | Uint8Array, length?: number) => any;
|
||||
}
|
||||
export declare type EncodeObject = {
|
||||
readonly typeUrl: string;
|
||||
readonly value: any;
|
||||
};
|
||||
export declare type DecodeObject = {
|
||||
readonly typeUrl: string;
|
||||
readonly value: Uint8Array;
|
||||
};
|
||||
export declare type TxBodyValue = {
|
||||
readonly messages: readonly EncodeObject[];
|
||||
readonly memo?: string;
|
||||
readonly timeoutHeight?: number;
|
||||
readonly extensionOptions?: readonly any[];
|
||||
readonly nonCriticalExtensionOptions?: readonly any[];
|
||||
};
|
||||
export declare class Registry {
|
||||
private readonly types;
|
||||
constructor(customTypes?: Iterable<[string, GeneratedType]>);
|
||||
register(name: string, type: GeneratedType): void;
|
||||
lookupType(name: string): GeneratedType | undefined;
|
||||
register(typeUrl: string, type: GeneratedType): void;
|
||||
lookupType(typeUrl: string): GeneratedType | undefined;
|
||||
private lookupTypeWithError;
|
||||
encode({ typeUrl, value }: EncodeObject): Uint8Array;
|
||||
encodeTxBody(txBodyFields: TxBodyValue): Uint8Array;
|
||||
decode({ typeUrl, value }: DecodeObject): any;
|
||||
decodeTxBody(txBody: Uint8Array): cosmosSdk.tx.v1.TxBody;
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user