From 90d16c61d83112e5993150d96896f34920c63f6b Mon Sep 17 00:00:00 2001 From: willclarktech Date: Mon, 15 Jun 2020 13:38:02 +0100 Subject: [PATCH] demo-protobuf: Add alternatives to generated static code --- packages/demo-protobuf/src/demo.json | 12 ++ packages/demo-protobuf/src/demo.proto.ts | 8 ++ packages/demo-protobuf/src/demo.spec.ts | 118 +++++++++++++++++-- packages/demo-protobuf/src/demo.ts | 7 ++ packages/demo-protobuf/types/demo.d.ts | 2 + packages/demo-protobuf/types/demo.proto.d.ts | 2 + 6 files changed, 142 insertions(+), 7 deletions(-) create mode 100644 packages/demo-protobuf/src/demo.json create mode 100644 packages/demo-protobuf/src/demo.proto.ts create mode 100644 packages/demo-protobuf/types/demo.proto.d.ts diff --git a/packages/demo-protobuf/src/demo.json b/packages/demo-protobuf/src/demo.json new file mode 100644 index 00000000..b9b01c24 --- /dev/null +++ b/packages/demo-protobuf/src/demo.json @@ -0,0 +1,12 @@ +{ + "nested": { + "MsgDemo": { + "fields": { + "example": { + "type": "string", + "id": 1 + } + } + } + } +} diff --git a/packages/demo-protobuf/src/demo.proto.ts b/packages/demo-protobuf/src/demo.proto.ts new file mode 100644 index 00000000..c78a4538 --- /dev/null +++ b/packages/demo-protobuf/src/demo.proto.ts @@ -0,0 +1,8 @@ +export default ` +syntax = "proto3"; +package demo; + +message MsgDemo { + string example = 1; +} +`; diff --git a/packages/demo-protobuf/src/demo.spec.ts b/packages/demo-protobuf/src/demo.spec.ts index 7a03412b..a4ab1151 100644 --- a/packages/demo-protobuf/src/demo.spec.ts +++ b/packages/demo-protobuf/src/demo.spec.ts @@ -1,14 +1,28 @@ +/* eslint-disable @typescript-eslint/camelcase */ import { assert } from "@cosmjs/utils"; +import protobuf from "protobufjs"; +import reflectionRoot from "./demo"; +import demoJson from "./demo.json"; +import demoProto from "./demo.proto"; import { cosmos_sdk as cosmosSdk, google } from "./generated/codecimpl"; +type MsgDemo = { + readonly example: string; +}; + const { Coin } = cosmosSdk.v1; const { TxBody } = cosmosSdk.tx.v1; const { MsgSend } = cosmosSdk.x.bank.v1; const { Any } = google.protobuf; +function getTypeName(typeUrl: string): string { + const parts = typeUrl.split("."); + return parts[parts.length - 1]; +} + describe("protobuf demo", () => { - it("works", () => { + it("works with generated static code", () => { const coin = Coin.create({ denom: "ucosm", amount: "1234567890", @@ -18,12 +32,9 @@ describe("protobuf demo", () => { toAddress: Uint8Array.from(Array.from({ length: 20 }, () => 2)), amount: [coin], }); - - // Serialization const msgSendBytes = MsgSend.encode(msgSend).finish(); const msgSendWrapped = Any.create({ - // eslint-disable-next-line @typescript-eslint/camelcase - type_url: "xxx", + type_url: "cosmos_sdk/cosmos.bank.MsgSend", value: msgSendBytes, }); const txBody = TxBody.create({ @@ -36,12 +47,105 @@ describe("protobuf demo", () => { // Deserialization const txBodyDecoded = TxBody.decode(txBodyBytes); - assert(txBodyDecoded.messages[0].value); - const msgSendDecoded = MsgSend.decode(txBodyDecoded.messages[0].value); + const msg = txBodyDecoded.messages[0]; + assert(msg.value); + const msgSendDecoded = MsgSend.decode(msg.value); // fromAddress and toAddress are now Buffers expect(Uint8Array.from(msgSendDecoded.fromAddress)).toEqual(msgSend.fromAddress); expect(Uint8Array.from(msgSendDecoded.toAddress)).toEqual(msgSend.toAddress); expect(msgSendDecoded.amount).toEqual(msgSend.amount); }); + + it("works with dynamically loaded proto files", () => { + const { root } = protobuf.parse(demoProto); + const typeUrl = "demo/demo.MsgDemo"; + const encoder = root.lookupType(getTypeName(typeUrl)); + const msgDemo = (encoder.create({ + example: "Some example text", + }) as unknown) as MsgDemo; + const msgDemoBytes = encoder.encode(msgDemo).finish(); + const msgDemoWrapped = Any.create({ + type_url: typeUrl, + value: msgDemoBytes, + }); + const txBody = TxBody.create({ + messages: [msgDemoWrapped], + memo: "Some memo", + timeoutHeight: 9999, + extensionOptions: [], + }); + const txBodyBytes = TxBody.encode(txBody).finish(); + + // Deserialization + const txBodyDecoded = TxBody.decode(txBodyBytes); + const msg = txBodyDecoded.messages[0]; + assert(msg.type_url); + assert(msg.value); + + const decoder = root.lookupType(getTypeName(msg.type_url)); + const msgDemoDecoded = (decoder.decode(msg.value) as unknown) as MsgDemo; + expect(msgDemoDecoded.example).toEqual(msgDemo.example); + }); + + it("works with dynamically loaded json files", () => { + const root = protobuf.Root.fromJSON(demoJson); + const typeUrl = "demo/demo.MsgDemo"; + const encoder = root.lookupType(getTypeName(typeUrl)); + const msgDemo = (encoder.create({ + example: "Some example text", + }) as unknown) as MsgDemo; + const msgDemoBytes = encoder.encode(msgDemo).finish(); + const msgDemoWrapped = Any.create({ + type_url: typeUrl, + value: msgDemoBytes, + }); + const txBody = TxBody.create({ + messages: [msgDemoWrapped], + memo: "Some memo", + timeoutHeight: 9999, + extensionOptions: [], + }); + const txBodyBytes = TxBody.encode(txBody).finish(); + + // Deserialization + const txBodyDecoded = TxBody.decode(txBodyBytes); + const msg = txBodyDecoded.messages[0]; + assert(msg.type_url); + assert(msg.value); + + const decoder = root.lookupType(getTypeName(msg.type_url)); + const msgDemoDecoded = (decoder.decode(msg.value) as unknown) as MsgDemo; + expect(msgDemoDecoded.example).toEqual(msgDemo.example); + }); + + it("works with reflection", () => { + const typeUrl = "demo/demo.MsgDemo"; + const encoder = reflectionRoot.lookupType(getTypeName(typeUrl)); + const msgDemo = (encoder.create({ + example: "Some example text", + }) as unknown) as MsgDemo; + const msgDemoBytes = encoder.encode(msgDemo).finish(); + const msgDemoWrapped = Any.create({ + type_url: typeUrl, + value: msgDemoBytes, + }); + const txBody = TxBody.create({ + messages: [msgDemoWrapped], + memo: "Some memo", + timeoutHeight: 9999, + extensionOptions: [], + }); + const txBodyBytes = TxBody.encode(txBody).finish(); + + // Deserialization + const txBodyDecoded = TxBody.decode(txBodyBytes); + const msg = txBodyDecoded.messages[0]; + assert(msg.type_url); + assert(msg.value); + + const decoder = reflectionRoot.lookupType(getTypeName(msg.type_url)); + const msgDemoDecoded = (decoder.decode(msg.value) as unknown) as MsgDemo; + expect(msgDemoDecoded.example).toEqual(msgDemo.example); + }); }); diff --git a/packages/demo-protobuf/src/demo.ts b/packages/demo-protobuf/src/demo.ts index e69de29b..6f7e31d7 100644 --- a/packages/demo-protobuf/src/demo.ts +++ b/packages/demo-protobuf/src/demo.ts @@ -0,0 +1,7 @@ +import { Field, Root, Type } from "protobufjs"; + +const MsgDemo = new Type("MsgDemo").add(new Field("example", 1, "string")); + +const root = new Root().define("demo").add(MsgDemo); + +export default root; diff --git a/packages/demo-protobuf/types/demo.d.ts b/packages/demo-protobuf/types/demo.d.ts index e69de29b..0b1d1914 100644 --- a/packages/demo-protobuf/types/demo.d.ts +++ b/packages/demo-protobuf/types/demo.d.ts @@ -0,0 +1,2 @@ +declare const root: import("protobufjs").Namespace; +export default root; diff --git a/packages/demo-protobuf/types/demo.proto.d.ts b/packages/demo-protobuf/types/demo.proto.d.ts new file mode 100644 index 00000000..a08652d5 --- /dev/null +++ b/packages/demo-protobuf/types/demo.proto.d.ts @@ -0,0 +1,2 @@ +declare const _default: '\nsyntax = "proto3";\npackage demo;\n\nmessage MsgDemo {\n string example = 1;\n}\n'; +export default _default;