proto-signing: Switch to ts-proto codec

This commit is contained in:
willclarktech 2021-01-14 11:02:45 +00:00
parent c0dd55bc94
commit bbb768a3df
No known key found for this signature in database
GPG Key ID: 551A86E2E398ADF7
19 changed files with 189 additions and 197 deletions

View File

@ -3,7 +3,10 @@ import { assert } from "@cosmjs/utils";
import Long from "long";
import protobuf from "protobufjs";
import { cosmos, google } from "./codec";
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";
@ -12,11 +15,6 @@ type MsgDemo = {
readonly example: string;
};
const { Coin } = cosmos.base.v1beta1;
const { TxBody } = cosmos.tx.v1beta1;
const { MsgSend } = cosmos.bank.v1beta1;
const { Any } = google.protobuf;
function getTypeName(typeUrl: string): string {
const parts = typeUrl.split(".");
return parts[parts.length - 1];
@ -24,21 +22,21 @@ function getTypeName(typeUrl: string): string {
describe("protobuf demo", () => {
it("works with generated static code", () => {
const coin = Coin.create({
const coin = Coin.fromJSON({
denom: "ucosm",
amount: "1234567890",
});
const msgSend = MsgSend.create({
const msgSend = MsgSend.fromJSON({
fromAddress: "cosmos1pkptre7fdkl6gfrzlesjjvhxhlc3r4gmmk8rs6",
toAddress: "cosmos1qypqxpq9qcrsszg2pvxq6rs0zqg3yyc5lzv7xu",
amount: [coin],
});
const msgSendBytes = MsgSend.encode(msgSend).finish();
const msgSendWrapped = Any.create({
type_url: "/cosmos.bank.v1beta1.MsgSend",
const msgSendWrapped = Any.fromJSON({
typeUrl: "/cosmos.bank.v1beta1.MsgSend",
value: msgSendBytes,
});
const txBody = TxBody.create({
const txBody = TxBody.fromPartial({
messages: [msgSendWrapped],
memo: "Some memo",
timeoutHeight: Long.fromNumber(9999),
@ -66,11 +64,11 @@ describe("protobuf demo", () => {
example: "Some example text",
}) as unknown) as MsgDemo;
const msgDemoBytes = encoder.encode(msgDemo).finish();
const msgDemoWrapped = Any.create({
type_url: typeUrl,
const msgDemoWrapped = Any.fromJSON({
typeUrl: typeUrl,
value: msgDemoBytes,
});
const txBody = TxBody.create({
const txBody = TxBody.fromPartial({
messages: [msgDemoWrapped],
memo: "Some memo",
timeoutHeight: Long.fromNumber(9999),
@ -81,10 +79,10 @@ describe("protobuf demo", () => {
// Deserialization
const txBodyDecoded = TxBody.decode(txBodyBytes);
const msg = txBodyDecoded.messages[0];
assert(msg.type_url);
assert(msg.typeUrl);
assert(msg.value);
const decoder = root.lookupType(getTypeName(msg.type_url));
const decoder = root.lookupType(getTypeName(msg.typeUrl));
const msgDemoDecoded = (decoder.decode(msg.value) as unknown) as MsgDemo;
expect(msgDemoDecoded.example).toEqual(msgDemo.example);
});
@ -97,11 +95,11 @@ describe("protobuf demo", () => {
example: "Some example text",
}) as unknown) as MsgDemo;
const msgDemoBytes = encoder.encode(msgDemo).finish();
const msgDemoWrapped = Any.create({
type_url: typeUrl,
const msgDemoWrapped = Any.fromJSON({
typeUrl: typeUrl,
value: msgDemoBytes,
});
const txBody = TxBody.create({
const txBody = TxBody.fromPartial({
messages: [msgDemoWrapped],
memo: "Some memo",
timeoutHeight: Long.fromNumber(9999),
@ -112,10 +110,10 @@ describe("protobuf demo", () => {
// Deserialization
const txBodyDecoded = TxBody.decode(txBodyBytes);
const msg = txBodyDecoded.messages[0];
assert(msg.type_url);
assert(msg.typeUrl);
assert(msg.value);
const decoder = root.lookupType(getTypeName(msg.type_url));
const decoder = root.lookupType(getTypeName(msg.typeUrl));
const msgDemoDecoded = (decoder.decode(msg.value) as unknown) as MsgDemo;
expect(msgDemoDecoded.example).toEqual(msgDemo.example);
});
@ -127,11 +125,11 @@ describe("protobuf demo", () => {
example: "Some example text",
}) as unknown) as MsgDemo;
const msgDemoBytes = encoder.encode(msgDemo).finish();
const msgDemoWrapped = Any.create({
type_url: typeUrl,
const msgDemoWrapped = Any.fromJSON({
typeUrl: typeUrl,
value: msgDemoBytes,
});
const txBody = TxBody.create({
const txBody = TxBody.fromPartial({
messages: [msgDemoWrapped],
memo: "Some memo",
timeoutHeight: Long.fromNumber(9999),
@ -142,10 +140,10 @@ describe("protobuf demo", () => {
// Deserialization
const txBodyDecoded = TxBody.decode(txBodyBytes);
const msg = txBodyDecoded.messages[0];
assert(msg.type_url);
assert(msg.typeUrl);
assert(msg.value);
const decoder = reflectionRoot.lookupType(getTypeName(msg.type_url));
const decoder = reflectionRoot.lookupType(getTypeName(msg.typeUrl));
const msgDemoDecoded = (decoder.decode(msg.value) as unknown) as MsgDemo;
expect(msgDemoDecoded.example).toEqual(msgDemo.example);
});

View File

@ -15,7 +15,7 @@ import {
rawSecp256k1PubkeyToAddress,
} from "@cosmjs/launchpad";
import { cosmos } from "./codec";
import { SignDoc } from "./codec/cosmos/tx/v1beta1/tx";
import { DirectSignResponse, OfflineDirectSigner } from "./signer";
import { makeSignBytes } from "./signing";
@ -116,7 +116,7 @@ export class DirectSecp256k1HdWallet implements OfflineDirectSigner {
];
}
public async signDirect(address: string, signDoc: cosmos.tx.v1beta1.ISignDoc): Promise<DirectSignResponse> {
public async signDirect(address: string, signDoc: SignDoc): Promise<DirectSignResponse> {
const signBytes = makeSignBytes(signDoc);
if (address !== this.address) {
throw new Error(`Address ${address} not found in wallet`);

View File

@ -1,7 +1,7 @@
import { Secp256k1, sha256 } from "@cosmjs/crypto";
import { AccountData, encodeSecp256k1Signature, rawSecp256k1PubkeyToAddress } from "@cosmjs/launchpad";
import { cosmos } from "./codec";
import { SignDoc } from "./codec/cosmos/tx/v1beta1/tx";
import { DirectSignResponse, OfflineDirectSigner } from "./signer";
import { makeSignBytes } from "./signing";
@ -46,7 +46,7 @@ export class DirectSecp256k1Wallet implements OfflineDirectSigner {
];
}
public async signDirect(address: string, signDoc: cosmos.tx.v1beta1.ISignDoc): Promise<DirectSignResponse> {
public async signDirect(address: string, signDoc: SignDoc): Promise<DirectSignResponse> {
const signBytes = makeSignBytes(signDoc);
if (address !== this.address) {
throw new Error(`Address ${address} not found in wallet`);

View File

@ -1,5 +1,3 @@
export { Coin } from "./msgs";
export { cosmosField, registered } from "./decorator";
export { EncodeObject, GeneratedType, Registry } from "./registry";
export { DirectSecp256k1HdWallet } from "./directsecp256k1hdwallet";
export { DirectSecp256k1Wallet } from "./directsecp256k1wallet";

View File

@ -1,11 +1,9 @@
/* eslint-disable @typescript-eslint/naming-convention */
import { fromBase64 } from "@cosmjs/encoding";
import { google } from "./codec";
import { Any } from "./codec/google/protobuf/any";
import { decodePubkey, encodePubkey } from "./pubkey";
const { Any } = google.protobuf;
describe("pubkey", () => {
const defaultPubkeyBase64 = "AtQaCqFnshaZQp6rIkvAPyzThvCvXSDO+9AzbxVErqJP";
const defaultPubkeyBytes = fromBase64(defaultPubkeyBase64);
@ -15,8 +13,8 @@ describe("pubkey", () => {
it("works for secp256k1", () => {
const pubkey = { type: "tendermint/PubKeySecp256k1", value: defaultPubkeyBase64 };
expect(encodePubkey(pubkey)).toEqual(
Any.create({
type_url: "/cosmos.crypto.secp256k1.PubKey",
Any.fromJSON({
typeUrl: "/cosmos.crypto.secp256k1.PubKey",
value: defaultPubkeyProtoBytes,
}),
);
@ -34,7 +32,7 @@ describe("pubkey", () => {
describe("decodePubkey", () => {
it("works for secp256k1", () => {
const pubkey = {
type_url: "/cosmos.crypto.secp256k1.PubKey",
typeUrl: "/cosmos.crypto.secp256k1.PubKey",
value: defaultPubkeyProtoBytes,
};
expect(decodePubkey(pubkey)).toEqual({
@ -45,7 +43,7 @@ describe("pubkey", () => {
it("throws for unsupported pubkey types", () => {
const pubkey = {
type_url: "/cosmos.crypto.unknown.PubKey",
typeUrl: "/cosmos.crypto.unknown.PubKey",
value: defaultPubkeyProtoBytes,
};
expect(() => decodePubkey(pubkey)).toThrowError(/not recognized/i);

View File

@ -1,20 +1,19 @@
/* eslint-disable @typescript-eslint/naming-convention */
import { fromBase64 } from "@cosmjs/encoding";
import { encodeSecp256k1Pubkey, PubKey } from "@cosmjs/launchpad";
import { encodeSecp256k1Pubkey, PubKey as LaunchpadPubKey } from "@cosmjs/launchpad";
import { cosmos, google } from "./codec";
import { PubKey } from "./codec/cosmos/crypto/secp256k1/keys";
import { Any } from "./codec/google/protobuf/any";
const { Any } = google.protobuf;
export function encodePubkey(pubkey: PubKey): google.protobuf.IAny {
export function encodePubkey(pubkey: LaunchpadPubKey): Any {
switch (pubkey.type) {
case "tendermint/PubKeySecp256k1": {
const pubkeyProto = cosmos.crypto.secp256k1.PubKey.create({
const pubkeyProto = PubKey.fromJSON({
key: fromBase64(pubkey.value),
});
return Any.create({
type_url: "/cosmos.crypto.secp256k1.PubKey",
value: Uint8Array.from(cosmos.crypto.secp256k1.PubKey.encode(pubkeyProto).finish()),
return Any.fromJSON({
typeUrl: "/cosmos.crypto.secp256k1.PubKey",
value: Uint8Array.from(PubKey.encode(pubkeyProto).finish()),
});
}
default:
@ -22,17 +21,17 @@ export function encodePubkey(pubkey: PubKey): google.protobuf.IAny {
}
}
export function decodePubkey(pubkey?: google.protobuf.IAny | null): PubKey | null {
export function decodePubkey(pubkey?: Any | null): LaunchpadPubKey | null {
if (!pubkey || !pubkey.value) {
return null;
}
switch (pubkey.type_url) {
switch (pubkey.typeUrl) {
case "/cosmos.crypto.secp256k1.PubKey": {
const { key } = cosmos.crypto.secp256k1.PubKey.decode(pubkey.value);
const { key } = PubKey.decode(pubkey.value);
return encodeSecp256k1Pubkey(key);
}
default:
throw new Error(`Pubkey type_url ${pubkey.type_url} not recognized`);
throw new Error(`Pubkey type_url ${pubkey.typeUrl} not recognized`);
}
}

View File

@ -2,34 +2,32 @@
import { assert } from "@cosmjs/utils";
import Long from "long";
import { cosmos, google } from "./codec";
import { MsgDemo as MsgDemoType } from "./demo";
import { MsgSend as IMsgSend } from "./codec/cosmos/bank/v1beta1/tx";
import { TxBody } from "./codec/cosmos/tx/v1beta1/tx";
import { Any } from "./codec/google/protobuf/any";
import { Registry } from "./registry";
const { TxBody } = cosmos.tx.v1beta1;
const { Any } = google.protobuf;
describe("registry demo", () => {
it("works with a default msg", () => {
const registry = new Registry();
const Coin = registry.lookupType("/cosmos.base.v1beta1.Coin")!;
const MsgSend = registry.lookupType("/cosmos.bank.v1beta1.MsgSend")!;
const coin = Coin.create({
const coin = Coin.fromJSON({
denom: "ucosm",
amount: "1234567890",
});
const msgSend = (MsgSend.create({
const msgSend = (MsgSend.fromJSON({
fromAddress: "cosmos1pkptre7fdkl6gfrzlesjjvhxhlc3r4gmmk8rs6",
toAddress: "cosmos1qypqxpq9qcrsszg2pvxq6rs0zqg3yyc5lzv7xu",
amount: [coin],
}) as unknown) as cosmos.bank.v1beta1.MsgSend;
}) as unknown) as IMsgSend;
const msgSendBytes = MsgSend.encode(msgSend).finish();
const msgSendWrapped = Any.create({
type_url: "/cosmos.bank.v1beta1.MsgSend",
const msgSendWrapped = Any.fromJSON({
typeUrl: "/cosmos.bank.v1beta1.MsgSend",
value: msgSendBytes,
});
const txBody = TxBody.create({
const txBody = TxBody.fromPartial({
messages: [msgSendWrapped],
memo: "Some memo",
timeoutHeight: Long.fromNumber(9999),
@ -39,10 +37,10 @@ describe("registry demo", () => {
const txBodyDecoded = TxBody.decode(txBodyBytes);
const msg = txBodyDecoded.messages[0];
assert(msg.type_url);
assert(msg.typeUrl);
assert(msg.value);
const decoder = registry.lookupType(msg.type_url)!;
const decoder = registry.lookupType(msg.typeUrl)!;
const msgSendDecoded = decoder.decode(msg.value);
// fromAddress and toAddress are now Buffers
@ -51,34 +49,35 @@ describe("registry demo", () => {
expect(msgSendDecoded.amount).toEqual(msgSend.amount);
});
it("works with a custom msg", () => {
const typeUrl = "/demo.MsgDemo";
const registry = new Registry([[typeUrl, MsgDemoType]]);
const MsgDemo = registry.lookupType(typeUrl)!;
// TODO: Can't autogenerate types from TS code using ts-proto
// it("works with a custom msg", () => {
// const typeUrl = "/demo.MsgDemo";
// const registry = new Registry([[typeUrl, MsgDemoType]]);
// const MsgDemo = registry.lookupType(typeUrl)!;
const msgDemo = MsgDemo.create({
example: "Some example text",
});
const msgDemoBytes = MsgDemo.encode(msgDemo).finish();
const msgDemoWrapped = Any.create({
type_url: typeUrl,
value: msgDemoBytes,
});
const txBody = TxBody.create({
messages: [msgDemoWrapped],
memo: "Some memo",
timeoutHeight: Long.fromNumber(9999),
extensionOptions: [],
});
const txBodyBytes = TxBody.encode(txBody).finish();
// const msgDemo = MsgDemo.fromJSON({
// example: "Some example text",
// });
// const msgDemoBytes = MsgDemo.encode(msgDemo).finish();
// const msgDemoWrapped = Any.create({
// type_url: typeUrl,
// value: msgDemoBytes,
// });
// const txBody = TxBody.create({
// messages: [msgDemoWrapped],
// memo: "Some memo",
// timeoutHeight: Long.fromNumber(9999),
// extensionOptions: [],
// });
// const txBodyBytes = TxBody.encode(txBody).finish();
const txBodyDecoded = TxBody.decode(txBodyBytes);
const msg = txBodyDecoded.messages[0];
assert(msg.type_url);
assert(msg.value);
// const txBodyDecoded = TxBody.decode(txBodyBytes);
// const msg = txBodyDecoded.messages[0];
// assert(msg.type_url);
// assert(msg.value);
const decoder = registry.lookupType(msg.type_url)!;
const msgDemoDecoded = decoder.decode(msg.value);
expect(msgDemoDecoded.example).toEqual(msgDemo.example);
});
// const decoder = registry.lookupType(msg.type_url)!;
// const msgDemoDecoded = decoder.decode(msg.value);
// expect(msgDemoDecoded.example).toEqual(msgDemo.example);
// });
});

View File

@ -2,12 +2,17 @@
import Long from "long";
import protobuf from "protobufjs";
import { cosmos, google } from "./codec";
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";
export interface GeneratedType {
readonly create: (properties?: { [k: string]: any }) => any;
readonly encode: (message: any | { [k: string]: any }, writer?: protobuf.Writer) => protobuf.Writer;
readonly decode: (reader: protobuf.Reader | Uint8Array, length?: number) => any;
readonly decode: (input: Uint8Array | protobuf.Reader, length?: number) => any;
readonly fromJSON: (object: { [k: string]: any }) => any;
readonly fromPartial: (object: { [k: string]: any }) => any;
readonly toJSON: (message: any | { [k: string]: any }) => unknown;
}
export interface EncodeObject {
@ -24,8 +29,8 @@ export interface TxBodyValue {
readonly messages: readonly EncodeObject[];
readonly memo?: string;
readonly timeoutHeight?: Long;
readonly extensionOptions?: google.protobuf.IAny[];
readonly nonCriticalExtensionOptions?: google.protobuf.IAny[];
readonly extensionOptions?: Any[];
readonly nonCriticalExtensionOptions?: Any[];
}
const defaultTypeUrls = {
@ -41,8 +46,8 @@ export class Registry {
public constructor(customTypes: Iterable<[string, GeneratedType]> = []) {
const { cosmosCoin, cosmosMsgSend } = defaultTypeUrls;
this.types = new Map<string, GeneratedType>([
[cosmosCoin, cosmos.base.v1beta1.Coin],
[cosmosMsgSend, cosmos.bank.v1beta1.MsgSend],
[cosmosCoin, Coin],
[cosmosMsgSend, MsgSend],
...customTypes,
]);
}
@ -68,22 +73,19 @@ export class Registry {
return this.encodeTxBody(value);
}
const type = this.lookupTypeWithError(typeUrl);
const created = type.create(value);
const created = type.fromPartial(value);
return Uint8Array.from(type.encode(created).finish());
}
public encodeTxBody(txBodyFields: TxBodyValue): Uint8Array {
const { TxBody } = cosmos.tx.v1beta1;
const { Any } = google.protobuf;
const wrappedMessages = txBodyFields.messages.map((message) => {
const messageBytes = this.encode(message);
return Any.create({
type_url: message.typeUrl,
return Any.fromJSON({
typeUrl: message.typeUrl,
value: messageBytes,
});
});
const txBody = TxBody.create({
const txBody = TxBody.fromPartial({
...txBodyFields,
messages: wrappedMessages,
});
@ -104,13 +106,12 @@ export class Registry {
return decoded;
}
public decodeTxBody(txBody: Uint8Array): cosmos.tx.v1beta1.TxBody {
const { TxBody } = cosmos.tx.v1beta1;
public decodeTxBody(txBody: Uint8Array): TxBody {
const decodedTxBody = TxBody.decode(txBody);
return {
...decodedTxBody,
messages: decodedTxBody.messages.map(({ type_url: typeUrl, value }: google.protobuf.IAny) => {
messages: decodedTxBody.messages.map(({ typeUrl: typeUrl, value }: Any) => {
if (!typeUrl) {
throw new Error("Missing type_url in Any");
}

View File

@ -1,22 +1,19 @@
import { AccountData, OfflineSigner as OfflineAminoSigner, StdSignature } from "@cosmjs/launchpad";
import { cosmos } from "./codec";
import { SignDoc } from "./codec/cosmos/tx/v1beta1/tx";
export interface DirectSignResponse {
/**
* The sign doc that was signed.
* This may be different from the input signDoc when the signer modifies it as part of the signing process.
*/
readonly signed: cosmos.tx.v1beta1.ISignDoc;
readonly signed: SignDoc;
readonly signature: StdSignature;
}
export interface OfflineDirectSigner {
readonly getAccounts: () => Promise<readonly AccountData[]>;
readonly signDirect: (
signerAddress: string,
signDoc: cosmos.tx.v1beta1.ISignDoc,
) => Promise<DirectSignResponse>;
readonly signDirect: (signerAddress: string, signDoc: SignDoc) => Promise<DirectSignResponse>;
}
export type OfflineSigner = OfflineAminoSigner | OfflineDirectSigner;

View File

@ -1,17 +1,15 @@
/* eslint-disable @typescript-eslint/naming-convention */
import { fromBase64, fromHex, toHex } from "@cosmjs/encoding";
import { cosmos, google } from "./codec";
import { PubKey } from "./codec/cosmos/crypto/secp256k1/keys";
import { SignMode } from "./codec/cosmos/tx/signing/v1beta1/signing";
import { Tx, TxRaw } from "./codec/cosmos/tx/v1beta1/tx";
import { Any } from "./codec/google/protobuf/any";
import { DirectSecp256k1HdWallet } from "./directsecp256k1hdwallet";
import { defaultRegistry } from "./msgs";
import { Registry, TxBodyValue } from "./registry";
import { makeAuthInfoBytes, makeSignBytes, makeSignDoc } from "./signing";
import { faucet, testVectors } from "./testutils.spec";
const { Tx, TxRaw } = cosmos.tx.v1beta1;
const { PubKey } = cosmos.crypto.secp256k1;
const { Any } = google.protobuf;
describe("signing", () => {
const chainId = "simd-testing";
const toAddress = "cosmos1qypqxpq9qcrsszg2pvxq6rs0zqg3yyc5lzv7xu";
@ -34,25 +32,26 @@ describe("signing", () => {
testVectors.forEach(({ signedTxBytes }) => {
const parsedTestTx = Tx.decode(fromHex(signedTxBytes));
expect(parsedTestTx.signatures.length).toEqual(1);
expect(parsedTestTx.authInfo!.signerInfos!.length).toEqual(1);
expect(Uint8Array.from(parsedTestTx.authInfo!.signerInfos![0].publicKey!.value ?? [])).toEqual(
expect(parsedTestTx.authInfo!.signerInfos.length).toEqual(1);
expect(Uint8Array.from(parsedTestTx.authInfo!.signerInfos[0].publicKey!.value ?? [])).toEqual(
prefixedPubkeyBytes,
);
expect(parsedTestTx.authInfo?.signerInfos![0].modeInfo!.single!.mode).toEqual(
cosmos.tx.signing.v1beta1.SignMode.SIGN_MODE_DIRECT,
SignMode.SIGN_MODE_DIRECT,
);
expect({ ...parsedTestTx.authInfo!.fee!.amount![0] }).toEqual({ denom: "ucosm", amount: "2000" });
expect(parsedTestTx.authInfo!.fee!.gasLimit!.toString()).toEqual(gasLimit.toString());
expect({ ...parsedTestTx.authInfo!.fee!.amount[0] }).toEqual({ denom: "ucosm", amount: "2000" });
expect(parsedTestTx.authInfo!.fee!.gasLimit.toString()).toEqual(gasLimit.toString());
expect(parsedTestTx.body!.extensionOptions).toEqual([]);
expect(parsedTestTx.body!.nonCriticalExtensionOptions).toEqual([]);
expect(parsedTestTx.body!.messages!.length).toEqual(1);
expect(parsedTestTx.body!.messages.length).toEqual(1);
const parsedTestTxMsg = defaultRegistry.decode({
typeUrl: parsedTestTx.body!.messages![0].type_url!,
value: parsedTestTx.body!.messages![0].value!,
const registry = new Registry();
const parsedTestTxMsg = registry.decode({
typeUrl: parsedTestTx.body!.messages[0].typeUrl,
value: parsedTestTx.body!.messages[0].value,
});
expect(parsedTestTxMsg.from_address).toEqual(address);
expect(parsedTestTxMsg.to_address).toEqual(toAddress);
expect(parsedTestTxMsg.fromAddress).toEqual(address);
expect(parsedTestTxMsg.toAddress).toEqual(toAddress);
expect(parsedTestTxMsg.amount.length).toEqual(1);
expect(parsedTestTxMsg.amount[0].denom).toEqual(sendDenom);
expect(parsedTestTxMsg.amount[0].amount).toEqual(sendAmount);
@ -63,7 +62,7 @@ describe("signing", () => {
const myRegistry = new Registry();
const wallet = await DirectSecp256k1HdWallet.fromMnemonic(faucet.mnemonic);
const [{ address, pubkey: pubkeyBytes }] = await wallet.getAccounts();
const publicKey = PubKey.create({
const publicKey = PubKey.fromJSON({
key: pubkeyBytes,
});
const publicKeyBytes = PubKey.encode(publicKey).finish();
@ -90,7 +89,7 @@ describe("signing", () => {
value: txBodyFields,
});
const publicKeyAny = Any.create({ type_url: "/cosmos.crypto.secp256k1.PubKey", value: publicKeyBytes });
const publicKeyAny = Any.fromJSON({ typeUrl: "/cosmos.crypto.secp256k1.PubKey", value: publicKeyBytes });
const accountNumber = 1;
await Promise.all(
@ -101,7 +100,7 @@ describe("signing", () => {
expect(toHex(signDocBytes)).toEqual(signBytes);
const { signature } = await wallet.signDirect(address, signDoc);
const txRaw = TxRaw.create({
const txRaw = TxRaw.fromPartial({
bodyBytes: txBodyBytes,
authInfoBytes: authInfoBytes,
signatures: [fromBase64(signature.signature)],

View File

@ -1,34 +1,37 @@
/* eslint-disable @typescript-eslint/naming-convention */
import Long from "long";
import { omitDefaults } from "./adr27";
import { cosmos, google } from "./codec";
const { SignDoc, AuthInfo } = cosmos.tx.v1beta1;
import { Coin } from "./codec/cosmos/base/v1beta1/coin";
import { SignMode } from "./codec/cosmos/tx/signing/v1beta1/signing";
import { AuthInfo, SignDoc, SignerInfo } from "./codec/cosmos/tx/v1beta1/tx";
import { Any } from "./codec/google/protobuf/any";
/**
* Creates and serializes an AuthInfo document using SIGN_MODE_DIRECT.
*/
export function makeAuthInfoBytes(
pubkeys: readonly google.protobuf.IAny[],
feeAmount: readonly cosmos.base.v1beta1.Coin[],
pubkeys: readonly Any[],
feeAmount: readonly Coin[],
gasLimit: number,
sequence: number,
signMode = cosmos.tx.signing.v1beta1.SignMode.SIGN_MODE_DIRECT,
signMode = SignMode.SIGN_MODE_DIRECT,
): Uint8Array {
const authInfo = {
signerInfos: pubkeys.map(
(pubkey): cosmos.tx.v1beta1.ISignerInfo => ({
(pubkey): SignerInfo => ({
publicKey: pubkey,
modeInfo: {
single: { mode: signMode },
},
sequence: sequence ? Long.fromNumber(sequence) : undefined,
sequence: Long.fromNumber(sequence),
}),
),
fee: { amount: [...feeAmount], gasLimit: Long.fromNumber(gasLimit) },
fee: {
amount: [...feeAmount],
gasLimit: Long.fromNumber(gasLimit),
},
};
return Uint8Array.from(AuthInfo.encode(authInfo).finish());
return AuthInfo.encode(AuthInfo.fromPartial(authInfo)).finish();
}
export function makeSignDoc(
@ -36,7 +39,7 @@ export function makeSignDoc(
authInfoBytes: Uint8Array,
chainId: string,
accountNumber: number,
): cosmos.tx.v1beta1.ISignDoc {
): SignDoc {
return {
bodyBytes: bodyBytes,
authInfoBytes: authInfoBytes,
@ -45,19 +48,12 @@ export function makeSignDoc(
};
}
export function makeSignBytes({
accountNumber,
authInfoBytes,
bodyBytes,
chainId,
}: cosmos.tx.v1beta1.ISignDoc): Uint8Array {
const signDoc = SignDoc.create(
omitDefaults({
accountNumber: accountNumber,
authInfoBytes: authInfoBytes,
bodyBytes: bodyBytes,
chainId: chainId,
}),
);
return Uint8Array.from(SignDoc.encode(signDoc).finish());
export function makeSignBytes({ accountNumber, authInfoBytes, bodyBytes, chainId }: SignDoc): Uint8Array {
const signDoc = SignDoc.fromPartial({
accountNumber: accountNumber,
authInfoBytes: authInfoBytes,
bodyBytes: bodyBytes,
chainId: chainId,
});
return SignDoc.encode(signDoc).finish();
}

View File

@ -15,33 +15,33 @@ export const testVectors = [
{
sequence: 0,
signedTxBytes:
"0a93010a90010a1c2f636f736d6f732e62616e6b2e763162657461312e4d736753656e6412700a2d636f736d6f7331706b707472653766646b6c366766727a6c65736a6a766878686c63337234676d6d6b38727336122d636f736d6f7331717970717870713971637273737a673270767871367273307a716733797963356c7a763778751a100a0575636f736d12073132333435363712650a4e0a460a1f2f636f736d6f732e63727970746f2e736563703235366b312e5075624b657912230a21034f04181eeba35391b858633a765c4a0c189697b40d216354d50890d350c7029012040a02080112130a0d0a0575636f736d12043230303010c09a0c1a40c9dd20e07464d3a688ff4b710b1fbc027e495e797cfa0b4804da2ed117959227772de059808f765aa29b8f92edf30f4c2c5a438e30d3fe6897daa7141e3ce6f9",
"0a97010a90010a1c2f636f736d6f732e62616e6b2e763162657461312e4d736753656e6412700a2d636f736d6f7331706b707472653766646b6c366766727a6c65736a6a766878686c63337234676d6d6b38727336122d636f736d6f7331717970717870713971637273737a673270767871367273307a716733797963356c7a763778751a100a0575636f736d12073132333435363712001800126b0a500a460a1f2f636f736d6f732e63727970746f2e736563703235366b312e5075624b657912230a21034f04181eeba35391b858633a765c4a0c189697b40d216354d50890d350c7029012040a020801180012170a0d0a0575636f736d12043230303010c09a0c1a0022001a40d1bb55b0f3e5f9c260a20c8ccf2cb44f19bafccba84c7fb6e2b77fac401b15c9228c33236a701dd7a7c4a481aa5dedb902c982fe565504c4f4e7f39ccfcd73d6",
bodyBytes:
"0a90010a1c2f636f736d6f732e62616e6b2e763162657461312e4d736753656e6412700a2d636f736d6f7331706b707472653766646b6c366766727a6c65736a6a766878686c63337234676d6d6b38727336122d636f736d6f7331717970717870713971637273737a673270767871367273307a716733797963356c7a763778751a100a0575636f736d120731323334353637",
signBytes:
"0a93010a90010a1c2f636f736d6f732e62616e6b2e763162657461312e4d736753656e6412700a2d636f736d6f7331706b707472653766646b6c366766727a6c65736a6a766878686c63337234676d6d6b38727336122d636f736d6f7331717970717870713971637273737a673270767871367273307a716733797963356c7a763778751a100a0575636f736d12073132333435363712650a4e0a460a1f2f636f736d6f732e63727970746f2e736563703235366b312e5075624b657912230a21034f04181eeba35391b858633a765c4a0c189697b40d216354d50890d350c7029012040a02080112130a0d0a0575636f736d12043230303010c09a0c1a0c73696d642d74657374696e672001",
"0a97010a90010a1c2f636f736d6f732e62616e6b2e763162657461312e4d736753656e6412700a2d636f736d6f7331706b707472653766646b6c366766727a6c65736a6a766878686c63337234676d6d6b38727336122d636f736d6f7331717970717870713971637273737a673270767871367273307a716733797963356c7a763778751a100a0575636f736d12073132333435363712001800126b0a500a460a1f2f636f736d6f732e63727970746f2e736563703235366b312e5075624b657912230a21034f04181eeba35391b858633a765c4a0c189697b40d216354d50890d350c7029012040a020801180012170a0d0a0575636f736d12043230303010c09a0c1a0022001a0c73696d642d74657374696e672001",
signature:
"c9dd20e07464d3a688ff4b710b1fbc027e495e797cfa0b4804da2ed117959227772de059808f765aa29b8f92edf30f4c2c5a438e30d3fe6897daa7141e3ce6f9",
},
{
sequence: 1,
signedTxBytes:
"0a93010a90010a1c2f636f736d6f732e62616e6b2e763162657461312e4d736753656e6412700a2d636f736d6f7331706b707472653766646b6c366766727a6c65736a6a766878686c63337234676d6d6b38727336122d636f736d6f7331717970717870713971637273737a673270767871367273307a716733797963356c7a763778751a100a0575636f736d12073132333435363712670a500a460a1f2f636f736d6f732e63727970746f2e736563703235366b312e5075624b657912230a21034f04181eeba35391b858633a765c4a0c189697b40d216354d50890d350c7029012040a020801180112130a0d0a0575636f736d12043230303010c09a0c1a40525adc7e61565a509c60497b798c549fbf217bb5cd31b24cc9b419d098cc95330c99ecc4bc72448f85c365a4e3f91299a3d40412fb3751bab82f1940a83a0a4c",
signBytes:
"0a93010a90010a1c2f636f736d6f732e62616e6b2e763162657461312e4d736753656e6412700a2d636f736d6f7331706b707472653766646b6c366766727a6c65736a6a766878686c63337234676d6d6b38727336122d636f736d6f7331717970717870713971637273737a673270767871367273307a716733797963356c7a763778751a100a0575636f736d12073132333435363712670a500a460a1f2f636f736d6f732e63727970746f2e736563703235366b312e5075624b657912230a21034f04181eeba35391b858633a765c4a0c189697b40d216354d50890d350c7029012040a020801180112130a0d0a0575636f736d12043230303010c09a0c1a0c73696d642d74657374696e672001",
"0a97010a90010a1c2f636f736d6f732e62616e6b2e763162657461312e4d736753656e6412700a2d636f736d6f7331706b707472653766646b6c366766727a6c65736a6a766878686c63337234676d6d6b38727336122d636f736d6f7331717970717870713971637273737a673270767871367273307a716733797963356c7a763778751a100a0575636f736d12073132333435363712001800126b0a500a460a1f2f636f736d6f732e63727970746f2e736563703235366b312e5075624b657912230a21034f04181eeba35391b858633a765c4a0c189697b40d216354d50890d350c7029012040a020801180112170a0d0a0575636f736d12043230303010c09a0c1a0022001a40d6d876ec9c01d03b025443822b1385e29ed559341ec71853ac310219b4bcb4112bf24265a19d805f0cbb1cb39f4ac4fe75235445070aa14845733117a746c248",
bodyBytes:
"0a90010a1c2f636f736d6f732e62616e6b2e763162657461312e4d736753656e6412700a2d636f736d6f7331706b707472653766646b6c366766727a6c65736a6a766878686c63337234676d6d6b38727336122d636f736d6f7331717970717870713971637273737a673270767871367273307a716733797963356c7a763778751a100a0575636f736d120731323334353637",
signBytes:
"0a97010a90010a1c2f636f736d6f732e62616e6b2e763162657461312e4d736753656e6412700a2d636f736d6f7331706b707472653766646b6c366766727a6c65736a6a766878686c63337234676d6d6b38727336122d636f736d6f7331717970717870713971637273737a673270767871367273307a716733797963356c7a763778751a100a0575636f736d12073132333435363712001800126b0a500a460a1f2f636f736d6f732e63727970746f2e736563703235366b312e5075624b657912230a21034f04181eeba35391b858633a765c4a0c189697b40d216354d50890d350c7029012040a020801180112170a0d0a0575636f736d12043230303010c09a0c1a0022001a0c73696d642d74657374696e672001",
signature:
"525adc7e61565a509c60497b798c549fbf217bb5cd31b24cc9b419d098cc95330c99ecc4bc72448f85c365a4e3f91299a3d40412fb3751bab82f1940a83a0a4c",
},
{
sequence: 2,
signedTxBytes:
"0a93010a90010a1c2f636f736d6f732e62616e6b2e763162657461312e4d736753656e6412700a2d636f736d6f7331706b707472653766646b6c366766727a6c65736a6a766878686c63337234676d6d6b38727336122d636f736d6f7331717970717870713971637273737a673270767871367273307a716733797963356c7a763778751a100a0575636f736d12073132333435363712670a500a460a1f2f636f736d6f732e63727970746f2e736563703235366b312e5075624b657912230a21034f04181eeba35391b858633a765c4a0c189697b40d216354d50890d350c7029012040a020801180212130a0d0a0575636f736d12043230303010c09a0c1a40f3f2ca73806f2abbf6e0fe85f9b8af66f0e9f7f79051fdb8abe5bb8633b17da132e82d577b9d5f7a6dae57a144efc9ccc6eef15167b44b3b22a57240109762af",
"0a97010a90010a1c2f636f736d6f732e62616e6b2e763162657461312e4d736753656e6412700a2d636f736d6f7331706b707472653766646b6c366766727a6c65736a6a766878686c63337234676d6d6b38727336122d636f736d6f7331717970717870713971637273737a673270767871367273307a716733797963356c7a763778751a100a0575636f736d12073132333435363712001800126b0a500a460a1f2f636f736d6f732e63727970746f2e736563703235366b312e5075624b657912230a21034f04181eeba35391b858633a765c4a0c189697b40d216354d50890d350c7029012040a020801180212170a0d0a0575636f736d12043230303010c09a0c1a0022001a40c62931c5f283dc72774cb34165ad98169c40de14c13e9c11c696cb439c7c58ab0c2017c95cb9ed874ace06460b61f1296dddb20c9e0609e364d1f38e163b7c7b",
bodyBytes:
"0a90010a1c2f636f736d6f732e62616e6b2e763162657461312e4d736753656e6412700a2d636f736d6f7331706b707472653766646b6c366766727a6c65736a6a766878686c63337234676d6d6b38727336122d636f736d6f7331717970717870713971637273737a673270767871367273307a716733797963356c7a763778751a100a0575636f736d120731323334353637",
signBytes:
"0a93010a90010a1c2f636f736d6f732e62616e6b2e763162657461312e4d736753656e6412700a2d636f736d6f7331706b707472653766646b6c366766727a6c65736a6a766878686c63337234676d6d6b38727336122d636f736d6f7331717970717870713971637273737a673270767871367273307a716733797963356c7a763778751a100a0575636f736d12073132333435363712670a500a460a1f2f636f736d6f732e63727970746f2e736563703235366b312e5075624b657912230a21034f04181eeba35391b858633a765c4a0c189697b40d216354d50890d350c7029012040a020801180212130a0d0a0575636f736d12043230303010c09a0c1a0c73696d642d74657374696e672001",
"0a97010a90010a1c2f636f736d6f732e62616e6b2e763162657461312e4d736753656e6412700a2d636f736d6f7331706b707472653766646b6c366766727a6c65736a6a766878686c63337234676d6d6b38727336122d636f736d6f7331717970717870713971637273737a673270767871367273307a716733797963356c7a763778751a100a0575636f736d12073132333435363712001800126b0a500a460a1f2f636f736d6f732e63727970746f2e736563703235366b312e5075624b657912230a21034f04181eeba35391b858633a765c4a0c189697b40d216354d50890d350c7029012040a020801180212170a0d0a0575636f736d12043230303010c09a0c1a0022001a0c73696d642d74657374696e672001",
signature:
"f3f2ca73806f2abbf6e0fe85f9b8af66f0e9f7f79051fdb8abe5bb8633b17da132e82d577b9d5f7a6dae57a144efc9ccc6eef15167b44b3b22a57240109762af",
},

View File

@ -1,6 +1,6 @@
import { EnglishMnemonic, HdPath } from "@cosmjs/crypto";
import { AccountData } from "@cosmjs/launchpad";
import { cosmos } from "./codec";
import { SignDoc } from "./codec/cosmos/tx/v1beta1/tx";
import { DirectSignResponse, OfflineDirectSigner } from "./signer";
/** A wallet for protobuf based signing using SIGN_MODE_DIRECT */
export declare class DirectSecp256k1HdWallet implements OfflineDirectSigner {
@ -41,5 +41,5 @@ export declare class DirectSecp256k1HdWallet implements OfflineDirectSigner {
get mnemonic(): string;
private get address();
getAccounts(): Promise<readonly AccountData[]>;
signDirect(address: string, signDoc: cosmos.tx.v1beta1.ISignDoc): Promise<DirectSignResponse>;
signDirect(address: string, signDoc: SignDoc): Promise<DirectSignResponse>;
}

View File

@ -1,5 +1,5 @@
import { AccountData } from "@cosmjs/launchpad";
import { cosmos } from "./codec";
import { SignDoc } from "./codec/cosmos/tx/v1beta1/tx";
import { DirectSignResponse, OfflineDirectSigner } from "./signer";
/**
* A wallet that holds a single secp256k1 keypair.
@ -20,5 +20,5 @@ export declare class DirectSecp256k1Wallet implements OfflineDirectSigner {
private constructor();
private get address();
getAccounts(): Promise<readonly AccountData[]>;
signDirect(address: string, signDoc: cosmos.tx.v1beta1.ISignDoc): Promise<DirectSignResponse>;
signDirect(address: string, signDoc: SignDoc): Promise<DirectSignResponse>;
}

View File

@ -1,5 +1,3 @@
export { Coin } from "./msgs";
export { cosmosField, registered } from "./decorator";
export { EncodeObject, GeneratedType, Registry } from "./registry";
export { DirectSecp256k1HdWallet } from "./directsecp256k1hdwallet";
export { DirectSecp256k1Wallet } from "./directsecp256k1wallet";

View File

@ -1,4 +1,4 @@
import { PubKey } from "@cosmjs/launchpad";
import { google } from "./codec";
export declare function encodePubkey(pubkey: PubKey): google.protobuf.IAny;
export declare function decodePubkey(pubkey?: google.protobuf.IAny | null): PubKey | null;
import { PubKey as LaunchpadPubKey } from "@cosmjs/launchpad";
import { Any } from "./codec/google/protobuf/any";
export declare function encodePubkey(pubkey: LaunchpadPubKey): Any;
export declare function decodePubkey(pubkey?: Any | null): LaunchpadPubKey | null;

View File

@ -1,8 +1,8 @@
import Long from "long";
import protobuf from "protobufjs";
import { cosmos, google } from "./codec";
import { TxBody } from "./codec/cosmos/tx/v1beta1/tx";
import { Any } from "./codec/google/protobuf/any";
export interface GeneratedType {
readonly create: (properties?: { [k: string]: any }) => any;
readonly encode: (
message:
| any
@ -11,7 +11,16 @@ export interface GeneratedType {
},
writer?: protobuf.Writer,
) => protobuf.Writer;
readonly decode: (reader: protobuf.Reader | Uint8Array, length?: number) => any;
readonly decode: (input: Uint8Array | protobuf.Reader, length?: number) => any;
readonly fromJSON: (object: { [k: string]: any }) => any;
readonly fromPartial: (object: { [k: string]: any }) => any;
readonly toJSON: (
message:
| any
| {
[k: string]: any;
},
) => unknown;
}
export interface EncodeObject {
readonly typeUrl: string;
@ -25,8 +34,8 @@ export interface TxBodyValue {
readonly messages: readonly EncodeObject[];
readonly memo?: string;
readonly timeoutHeight?: Long;
readonly extensionOptions?: google.protobuf.IAny[];
readonly nonCriticalExtensionOptions?: google.protobuf.IAny[];
readonly extensionOptions?: Any[];
readonly nonCriticalExtensionOptions?: Any[];
}
export declare class Registry {
private readonly types;
@ -37,5 +46,5 @@ export declare class Registry {
encode({ typeUrl, value }: EncodeObject): Uint8Array;
encodeTxBody(txBodyFields: TxBodyValue): Uint8Array;
decode({ typeUrl, value }: DecodeObject): any;
decodeTxBody(txBody: Uint8Array): cosmos.tx.v1beta1.TxBody;
decodeTxBody(txBody: Uint8Array): TxBody;
}

View File

@ -1,19 +1,16 @@
import { AccountData, OfflineSigner as OfflineAminoSigner, StdSignature } from "@cosmjs/launchpad";
import { cosmos } from "./codec";
import { SignDoc } from "./codec/cosmos/tx/v1beta1/tx";
export interface DirectSignResponse {
/**
* The sign doc that was signed.
* This may be different from the input signDoc when the signer modifies it as part of the signing process.
*/
readonly signed: cosmos.tx.v1beta1.ISignDoc;
readonly signed: SignDoc;
readonly signature: StdSignature;
}
export interface OfflineDirectSigner {
readonly getAccounts: () => Promise<readonly AccountData[]>;
readonly signDirect: (
signerAddress: string,
signDoc: cosmos.tx.v1beta1.ISignDoc,
) => Promise<DirectSignResponse>;
readonly signDirect: (signerAddress: string, signDoc: SignDoc) => Promise<DirectSignResponse>;
}
export declare type OfflineSigner = OfflineAminoSigner | OfflineDirectSigner;
export declare function isOfflineDirectSigner(signer: OfflineSigner): signer is OfflineDirectSigner;

View File

@ -1,23 +1,26 @@
import { cosmos, google } from "./codec";
import { Coin } from "./codec/cosmos/base/v1beta1/coin";
import { SignMode } from "./codec/cosmos/tx/signing/v1beta1/signing";
import { SignDoc } from "./codec/cosmos/tx/v1beta1/tx";
import { Any } from "./codec/google/protobuf/any";
/**
* Creates and serializes an AuthInfo document using SIGN_MODE_DIRECT.
*/
export declare function makeAuthInfoBytes(
pubkeys: readonly google.protobuf.IAny[],
feeAmount: readonly cosmos.base.v1beta1.Coin[],
pubkeys: readonly Any[],
feeAmount: readonly Coin[],
gasLimit: number,
sequence: number,
signMode?: cosmos.tx.signing.v1beta1.SignMode,
signMode?: SignMode,
): Uint8Array;
export declare function makeSignDoc(
bodyBytes: Uint8Array,
authInfoBytes: Uint8Array,
chainId: string,
accountNumber: number,
): cosmos.tx.v1beta1.ISignDoc;
): SignDoc;
export declare function makeSignBytes({
accountNumber,
authInfoBytes,
bodyBytes,
chainId,
}: cosmos.tx.v1beta1.ISignDoc): Uint8Array;
}: SignDoc): Uint8Array;