Merge pull request #694 from cosmos/remove-unused
Remove unused modules from proto-signing
This commit is contained in:
commit
e46db075f4
@ -1,239 +0,0 @@
|
||||
import { fromHex } from "@cosmjs/encoding";
|
||||
import Long from "long";
|
||||
import { parse } from "protobufjs";
|
||||
|
||||
import { omitDefault, omitDefaults } from "./adr27";
|
||||
|
||||
describe("adr27", () => {
|
||||
describe("omitDefault", () => {
|
||||
it("works for strings", () => {
|
||||
expect(omitDefault("abc")).toEqual("abc");
|
||||
expect(omitDefault("")).toEqual(null);
|
||||
});
|
||||
|
||||
it("works for bytes", () => {
|
||||
expect(omitDefault(fromHex("ab"))).toEqual(fromHex("ab"));
|
||||
expect(omitDefault(fromHex(""))).toEqual(null);
|
||||
});
|
||||
|
||||
it("works for integers", () => {
|
||||
expect(omitDefault(123)).toEqual(123);
|
||||
expect(omitDefault(0)).toEqual(null);
|
||||
});
|
||||
|
||||
it("works for floats", () => {
|
||||
expect(omitDefault(1.234)).toEqual(1.234);
|
||||
expect(omitDefault(0.0)).toEqual(null);
|
||||
});
|
||||
|
||||
it("works for Long", () => {
|
||||
// unsigned
|
||||
expect(omitDefault(Long.fromNumber(123, true))).toEqual(Long.fromNumber(123, true));
|
||||
expect(omitDefault(Long.fromNumber(0, true))).toEqual(null);
|
||||
|
||||
// signed
|
||||
expect(omitDefault(Long.fromNumber(123, false))).toEqual(Long.fromNumber(123, false));
|
||||
expect(omitDefault(Long.fromNumber(0, false))).toEqual(null);
|
||||
});
|
||||
|
||||
it("works for booleans", () => {
|
||||
expect(omitDefault(true)).toEqual(true);
|
||||
expect(omitDefault(false)).toEqual(null);
|
||||
});
|
||||
|
||||
it("works for repeated", () => {
|
||||
expect(omitDefault(["a", "b", "c"])).toEqual(["a", "b", "c"]);
|
||||
expect(omitDefault([])).toEqual(null);
|
||||
});
|
||||
|
||||
it("works for enums", () => {
|
||||
const proto = `
|
||||
package blog;
|
||||
syntax = "proto3";
|
||||
|
||||
enum Review {
|
||||
UNSPECIFIED = 0;
|
||||
ACCEPTED = 1;
|
||||
REJECTED = 2;
|
||||
};
|
||||
`;
|
||||
// eslint-disable-next-line @typescript-eslint/naming-convention
|
||||
const Review = parse(proto).root.lookupEnum("blog.Review");
|
||||
expect(omitDefault(Review.values["ACCEPTED"])).toEqual(Review.values["ACCEPTED"]);
|
||||
expect(omitDefault(Review.values["UNSPECIFIED"])).toEqual(null);
|
||||
});
|
||||
|
||||
it("works for unset", () => {
|
||||
// null and undefined both represent unset in protobuf.js serialization
|
||||
expect(omitDefault(undefined)).toEqual(null);
|
||||
expect(omitDefault(null)).toEqual(null);
|
||||
});
|
||||
});
|
||||
|
||||
describe("omitDefaults", () => {
|
||||
it("works for scalars", () => {
|
||||
expect(omitDefaults("abc")).toEqual("abc");
|
||||
expect(omitDefaults("")).toEqual(null);
|
||||
|
||||
expect(omitDefaults(fromHex("ab"))).toEqual(fromHex("ab"));
|
||||
expect(omitDefaults(fromHex(""))).toEqual(null);
|
||||
|
||||
expect(omitDefaults(123)).toEqual(123);
|
||||
expect(omitDefaults(0)).toEqual(null);
|
||||
|
||||
expect(omitDefaults(1.234)).toEqual(1.234);
|
||||
expect(omitDefaults(0.0)).toEqual(null);
|
||||
|
||||
expect(omitDefaults(Long.fromNumber(123, true))).toEqual(Long.fromNumber(123, true));
|
||||
expect(omitDefaults(Long.fromNumber(0, true))).toEqual(null);
|
||||
|
||||
expect(omitDefaults(Long.fromNumber(123, false))).toEqual(Long.fromNumber(123, false));
|
||||
expect(omitDefaults(Long.fromNumber(0, false))).toEqual(null);
|
||||
|
||||
expect(omitDefaults(true)).toEqual(true);
|
||||
expect(omitDefaults(false)).toEqual(null);
|
||||
});
|
||||
|
||||
it("works for repeated", () => {
|
||||
expect(omitDefaults(["a", "b", "c"])).toEqual(["a", "b", "c"]);
|
||||
expect(omitDefaults([])).toEqual(null);
|
||||
});
|
||||
|
||||
it("works for enums", () => {
|
||||
const proto = `
|
||||
package blog;
|
||||
syntax = "proto3";
|
||||
|
||||
enum Review {
|
||||
UNSPECIFIED = 0;
|
||||
ACCEPTED = 1;
|
||||
REJECTED = 2;
|
||||
};
|
||||
`;
|
||||
// eslint-disable-next-line @typescript-eslint/naming-convention
|
||||
const Review = parse(proto).root.lookupEnum("blog.Review");
|
||||
expect(omitDefaults(Review.values["ACCEPTED"])).toEqual(Review.values["ACCEPTED"]);
|
||||
expect(omitDefaults(Review.values["UNSPECIFIED"])).toEqual(null);
|
||||
});
|
||||
|
||||
it("works for unset", () => {
|
||||
// null and undefined both represent unset in protobuf.js serialization
|
||||
expect(omitDefaults(undefined)).toEqual(null);
|
||||
expect(omitDefaults(null)).toEqual(null);
|
||||
});
|
||||
|
||||
it("works for objects", () => {
|
||||
// empty
|
||||
expect(omitDefaults({})).toEqual({});
|
||||
|
||||
// simple
|
||||
expect(
|
||||
omitDefaults({
|
||||
a: "foo",
|
||||
b: "",
|
||||
c: 100,
|
||||
d: 0,
|
||||
}),
|
||||
).toEqual({
|
||||
a: "foo",
|
||||
b: null,
|
||||
c: 100,
|
||||
d: null,
|
||||
});
|
||||
|
||||
// nested
|
||||
|
||||
expect(
|
||||
omitDefaults({
|
||||
a: {
|
||||
x: "foo",
|
||||
y: "",
|
||||
},
|
||||
b: {
|
||||
x: {
|
||||
o: 1.2,
|
||||
p: false,
|
||||
q: 0,
|
||||
},
|
||||
},
|
||||
}),
|
||||
).toEqual({
|
||||
a: {
|
||||
x: "foo",
|
||||
y: null,
|
||||
},
|
||||
b: {
|
||||
x: {
|
||||
o: 1.2,
|
||||
p: null,
|
||||
q: null,
|
||||
},
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
it("can be used to reproduce ADR 027 test vector", () => {
|
||||
const proto = `
|
||||
// Article.proto
|
||||
|
||||
package blog;
|
||||
syntax = "proto3";
|
||||
|
||||
enum Type {
|
||||
UNSPECIFIED = 0;
|
||||
IMAGES = 1;
|
||||
NEWS = 2;
|
||||
};
|
||||
|
||||
enum Review {
|
||||
UNSPECIFIED = 0;
|
||||
ACCEPTED = 1;
|
||||
REJECTED = 2;
|
||||
};
|
||||
|
||||
message Article {
|
||||
string title = 1;
|
||||
string description = 2;
|
||||
uint64 created = 3;
|
||||
uint64 updated = 4;
|
||||
bool public = 5;
|
||||
bool promoted = 6;
|
||||
Type type = 7;
|
||||
Review review = 8;
|
||||
repeated string comments = 9;
|
||||
repeated string backlinks = 10;
|
||||
};
|
||||
`;
|
||||
const root = parse(proto).root;
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/naming-convention
|
||||
const Article = root.lookupType("blog.Article");
|
||||
// eslint-disable-next-line @typescript-eslint/naming-convention
|
||||
const Type = root.lookupEnum("blog.Type");
|
||||
// eslint-disable-next-line @typescript-eslint/naming-convention
|
||||
const Review = root.lookupEnum("blog.Review");
|
||||
|
||||
const expected = fromHex(
|
||||
"0a1654686520776f726c64206e65656473206368616e676518e8bebec8bc2e280138024a084e696365206f6e654a095468616e6b20796f75",
|
||||
);
|
||||
|
||||
const serialization = Uint8Array.from(
|
||||
Article.encode(
|
||||
omitDefaults({
|
||||
title: "The world needs change",
|
||||
description: "",
|
||||
created: 1596806111080,
|
||||
updated: 0,
|
||||
public: true,
|
||||
promoted: false,
|
||||
type: Type.values["NEWS"],
|
||||
review: Review.values["UNSPECIFIED"],
|
||||
comments: ["Nice one", "Thank you"],
|
||||
backlinks: [],
|
||||
}),
|
||||
).finish(),
|
||||
);
|
||||
expect(serialization).toEqual(expected);
|
||||
});
|
||||
});
|
||||
});
|
||||
@ -1,60 +0,0 @@
|
||||
import { isNonNullObject, isUint8Array } from "@cosmjs/utils";
|
||||
import Long from "long";
|
||||
|
||||
/**
|
||||
* Converts default values to null in order to tell protobuf.js
|
||||
* to not serialize them.
|
||||
*
|
||||
* @see https://github.com/cosmos/cosmos-sdk/pull/6979
|
||||
*/
|
||||
export function omitDefault<T>(input: T): T | null {
|
||||
if (input === undefined || input === null) return null;
|
||||
|
||||
if (typeof input === "number" || typeof input === "boolean" || typeof input === "string") {
|
||||
return input || null;
|
||||
}
|
||||
|
||||
if (Long.isLong(input)) {
|
||||
return !input.isZero() ? input : null;
|
||||
}
|
||||
|
||||
if (Array.isArray(input) || isUint8Array(input)) {
|
||||
return input.length ? input : null;
|
||||
}
|
||||
|
||||
throw new Error("Input type not supported");
|
||||
}
|
||||
|
||||
/**
|
||||
* Walks through a potentially nested object and calls omitDefault on each element.
|
||||
*/
|
||||
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
|
||||
export function omitDefaults(input: any): any {
|
||||
// Unset
|
||||
if (input === undefined || input === null) return null;
|
||||
|
||||
// Protobuf element
|
||||
if (
|
||||
typeof input === "number" ||
|
||||
typeof input === "boolean" ||
|
||||
typeof input === "string" ||
|
||||
Long.isLong(input) ||
|
||||
Array.isArray(input) ||
|
||||
isUint8Array(input)
|
||||
) {
|
||||
return omitDefault(input);
|
||||
}
|
||||
|
||||
// Object
|
||||
if (isNonNullObject(input)) {
|
||||
return Object.entries(input).reduce(
|
||||
(accumulator, [key, value]) => ({
|
||||
...accumulator,
|
||||
[key]: omitDefaults(value),
|
||||
}),
|
||||
{},
|
||||
);
|
||||
}
|
||||
|
||||
throw new Error(`Input type not supported: ${typeof input}`);
|
||||
}
|
||||
@ -1,12 +0,0 @@
|
||||
{
|
||||
"nested": {
|
||||
"MsgDemo": {
|
||||
"fields": {
|
||||
"example": {
|
||||
"type": "string",
|
||||
"id": 1
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,12 +0,0 @@
|
||||
// Without this TS infers this is a string literal.
|
||||
// eslint-disable-next-line @typescript-eslint/no-inferrable-types
|
||||
const proto: string = `
|
||||
syntax = "proto3";
|
||||
package demo;
|
||||
|
||||
message MsgDemo {
|
||||
string example = 1;
|
||||
}
|
||||
`;
|
||||
|
||||
export default proto;
|
||||
@ -1,150 +0,0 @@
|
||||
/* eslint-disable @typescript-eslint/naming-convention */
|
||||
import { assert } from "@cosmjs/utils";
|
||||
import Long from "long";
|
||||
import protobuf from "protobufjs";
|
||||
|
||||
import { MsgSend } from "./codec/cosmos/bank/v1beta1/tx";
|
||||
import { Coin } from "./codec/cosmos/base/v1beta1/coin";
|
||||
import { TxBody } from "./codec/cosmos/tx/v1beta1/tx";
|
||||
import { Any } from "./codec/google/protobuf/any";
|
||||
import reflectionRoot from "./demo";
|
||||
import demoJson from "./demo.json";
|
||||
import demoProto from "./demo.proto";
|
||||
|
||||
type MsgDemo = {
|
||||
readonly example: string;
|
||||
};
|
||||
|
||||
function getTypeName(typeUrl: string): string {
|
||||
const parts = typeUrl.split(".");
|
||||
return parts[parts.length - 1];
|
||||
}
|
||||
|
||||
describe("protobuf demo", () => {
|
||||
it("works with generated static code", () => {
|
||||
const coin = Coin.fromPartial({
|
||||
denom: "ucosm",
|
||||
amount: "1234567890",
|
||||
});
|
||||
const msgSend = MsgSend.fromPartial({
|
||||
fromAddress: "cosmos1pkptre7fdkl6gfrzlesjjvhxhlc3r4gmmk8rs6",
|
||||
toAddress: "cosmos1qypqxpq9qcrsszg2pvxq6rs0zqg3yyc5lzv7xu",
|
||||
amount: [coin],
|
||||
});
|
||||
const msgSendBytes = MsgSend.encode(msgSend).finish();
|
||||
const msgSendWrapped = Any.fromPartial({
|
||||
typeUrl: "/cosmos.bank.v1beta1.MsgSend",
|
||||
value: msgSendBytes,
|
||||
});
|
||||
const txBody = TxBody.fromPartial({
|
||||
messages: [msgSendWrapped],
|
||||
memo: "Some memo",
|
||||
timeoutHeight: Long.fromNumber(9999),
|
||||
extensionOptions: [],
|
||||
});
|
||||
const txBodyBytes = TxBody.encode(txBody).finish();
|
||||
|
||||
// Deserialization
|
||||
const txBodyDecoded = TxBody.decode(txBodyBytes);
|
||||
const msg = txBodyDecoded.messages[0];
|
||||
assert(msg.value);
|
||||
const msgSendDecoded = MsgSend.decode(msg.value);
|
||||
|
||||
// fromAddress and toAddress are now Buffers
|
||||
expect(msgSendDecoded.fromAddress).toEqual(msgSend.fromAddress);
|
||||
expect(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.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.fromPartial({
|
||||
typeUrl: typeUrl,
|
||||
value: msgDemoBytes,
|
||||
});
|
||||
const txBody = TxBody.fromPartial({
|
||||
messages: [msgDemoWrapped],
|
||||
memo: "Some memo",
|
||||
timeoutHeight: Long.fromNumber(9999),
|
||||
extensionOptions: [],
|
||||
});
|
||||
const txBodyBytes = TxBody.encode(txBody).finish();
|
||||
|
||||
// Deserialization
|
||||
const txBodyDecoded = TxBody.decode(txBodyBytes);
|
||||
const msg = txBodyDecoded.messages[0];
|
||||
assert(msg.typeUrl);
|
||||
assert(msg.value);
|
||||
|
||||
const decoder = root.lookupType(getTypeName(msg.typeUrl));
|
||||
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.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.fromPartial({
|
||||
typeUrl: typeUrl,
|
||||
value: msgDemoBytes,
|
||||
});
|
||||
const txBody = TxBody.fromPartial({
|
||||
messages: [msgDemoWrapped],
|
||||
memo: "Some memo",
|
||||
timeoutHeight: Long.fromNumber(9999),
|
||||
extensionOptions: [],
|
||||
});
|
||||
const txBodyBytes = TxBody.encode(txBody).finish();
|
||||
|
||||
// Deserialization
|
||||
const txBodyDecoded = TxBody.decode(txBodyBytes);
|
||||
const msg = txBodyDecoded.messages[0];
|
||||
assert(msg.typeUrl);
|
||||
assert(msg.value);
|
||||
|
||||
const decoder = root.lookupType(getTypeName(msg.typeUrl));
|
||||
const msgDemoDecoded = (decoder.decode(msg.value) as unknown) as MsgDemo;
|
||||
expect(msgDemoDecoded.example).toEqual(msgDemo.example);
|
||||
});
|
||||
|
||||
it("works with reflection", () => {
|
||||
const typeUrl = "/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.fromPartial({
|
||||
typeUrl: typeUrl,
|
||||
value: msgDemoBytes,
|
||||
});
|
||||
const txBody = TxBody.fromPartial({
|
||||
messages: [msgDemoWrapped],
|
||||
memo: "Some memo",
|
||||
timeoutHeight: Long.fromNumber(9999),
|
||||
extensionOptions: [],
|
||||
});
|
||||
const txBodyBytes = TxBody.encode(txBody).finish();
|
||||
|
||||
// Deserialization
|
||||
const txBodyDecoded = TxBody.decode(txBodyBytes);
|
||||
const msg = txBodyDecoded.messages[0];
|
||||
assert(msg.typeUrl);
|
||||
assert(msg.value);
|
||||
|
||||
const decoder = reflectionRoot.lookupType(getTypeName(msg.typeUrl));
|
||||
const msgDemoDecoded = (decoder.decode(msg.value) as unknown) as MsgDemo;
|
||||
expect(msgDemoDecoded.example).toEqual(msgDemo.example);
|
||||
});
|
||||
});
|
||||
@ -1,8 +0,0 @@
|
||||
/* eslint-disable @typescript-eslint/naming-convention */
|
||||
import { Field, Root, Type } from "protobufjs";
|
||||
|
||||
export const MsgDemo = new Type("MsgDemo").add(new Field("example", 1, "string"));
|
||||
|
||||
const root = new Root().define("demo").add(MsgDemo);
|
||||
|
||||
export default root;
|
||||
Loading…
Reference in New Issue
Block a user