amino: Transfer signdoc functions from launchpad

This commit is contained in:
willclarktech 2021-03-24 15:31:50 +01:00
parent 4d5e0866ad
commit 651af7d653
No known key found for this signature in database
GPG Key ID: 551A86E2E398ADF7
4 changed files with 214 additions and 39 deletions

View File

@ -20,13 +20,5 @@ export {
} from "./pubkeys";
export { createMultisigThresholdPubkey } from "./multisig";
export { decodeSignature, encodeSecp256k1Signature, StdSignature } from "./signature";
export {
AccountData,
Algo,
AminoMsg,
AminoSignResponse,
Coin,
OfflineAminoSigner,
StdFee,
StdSignDoc,
} from "./signer";
export { AminoMsg, Coin, makeSignDoc, serializeSignDoc, StdFee, StdSignDoc } from "./signdoc";
export { AccountData, Algo, AminoSignResponse, OfflineAminoSigner } from "./signer";

View File

@ -0,0 +1,135 @@
/* eslint-disable @typescript-eslint/naming-convention */
import { Random } from "@cosmjs/crypto";
import { Bech32 } from "@cosmjs/encoding";
import { AminoMsg, makeSignDoc, sortedJsonStringify } from "./signdoc";
function makeRandomAddress(): string {
return Bech32.encode("cosmos", Random.getBytes(20));
}
const testAddress = "cosmos1pkptre7fdkl6gfrzlesjjvhxhlc3r4gmmk8rs6";
const testValidatorAddress = "cosmosvaloper1yfkkk04ve8a0sugj4fe6q6zxuvmvza8r3arurr";
describe("encoding", () => {
describe("sortedJsonStringify", () => {
it("leaves non-objects unchanged", () => {
expect(sortedJsonStringify(true)).toEqual(`true`);
expect(sortedJsonStringify(false)).toEqual(`false`);
expect(sortedJsonStringify("aabbccdd")).toEqual(`"aabbccdd"`);
expect(sortedJsonStringify(75)).toEqual(`75`);
expect(sortedJsonStringify(null)).toEqual(`null`);
expect(sortedJsonStringify([5, 6, 7, 1])).toEqual(`[5,6,7,1]`);
expect(sortedJsonStringify([5, ["a", "b"], true, null, 1])).toEqual(`[5,["a","b"],true,null,1]`);
});
it("sorts objects by key", () => {
// already sorted
expect(sortedJsonStringify({})).toEqual(`{}`);
expect(sortedJsonStringify({ a: 3 })).toEqual(`{"a":3}`);
expect(sortedJsonStringify({ a: 3, b: 2, c: 1 })).toEqual(`{"a":3,"b":2,"c":1}`);
// not yet sorted
expect(sortedJsonStringify({ b: 2, a: 3, c: 1 })).toEqual(`{"a":3,"b":2,"c":1}`);
expect(sortedJsonStringify({ aaa: true, aa: true, a: true })).toEqual(
`{"a":true,"aa":true,"aaa":true}`,
);
});
it("sorts nested objects", () => {
// already sorted
expect(sortedJsonStringify({ x: { y: { z: null } } })).toEqual(`{"x":{"y":{"z":null}}}`);
// not yet sorted
expect(sortedJsonStringify({ b: { z: true, x: true, y: true }, a: true, c: true })).toEqual(
`{"a":true,"b":{"x":true,"y":true,"z":true},"c":true}`,
);
});
it("sorts objects in arrays", () => {
// already sorted
expect(sortedJsonStringify([1, 2, { x: { y: { z: null } } }, 4])).toEqual(
`[1,2,{"x":{"y":{"z":null}}},4]`,
);
// not yet sorted
expect(sortedJsonStringify([1, 2, { b: { z: true, x: true, y: true }, a: true, c: true }, 4])).toEqual(
`[1,2,{"a":true,"b":{"x":true,"y":true,"z":true},"c":true},4]`,
);
});
});
describe("makeSignDoc", () => {
it("works", () => {
const chainId = "testspace-12";
const msg1: AminoMsg = {
type: "cosmos-sdk/MsgDelegate",
value: {
delegator_address: testAddress,
validator_address: testValidatorAddress,
amount: { amount: "1234", denom: "ustake" },
},
};
const msg2: AminoMsg = {
type: "cosmos-sdk/MsgSend",
value: {
from_address: testAddress,
to_address: makeRandomAddress(),
amount: [{ amount: "1234567", denom: "ucosm" }],
},
};
const fee = {
amount: [{ amount: "2000", denom: "ucosm" }],
gas: "180000", // 180k
};
const memo = "Use your power wisely";
const accountNumber = 15;
const sequence = 16;
const signDoc = makeSignDoc([msg1, msg2], fee, chainId, memo, accountNumber, sequence);
expect(signDoc).toEqual({
msgs: [msg1, msg2],
fee: fee,
chain_id: chainId,
account_number: accountNumber.toString(),
sequence: sequence.toString(),
memo: memo,
});
});
it("works with undefined memo", () => {
const chainId = "testspace-12";
const msg1: AminoMsg = {
type: "cosmos-sdk/MsgDelegate",
value: {
delegator_address: testAddress,
validator_address: testValidatorAddress,
amount: { amount: "1234", denom: "ustake" },
},
};
const msg2: AminoMsg = {
type: "cosmos-sdk/MsgSend",
value: {
from_address: testAddress,
to_address: makeRandomAddress(),
amount: [{ amount: "1234567", denom: "ucosm" }],
},
};
const fee = {
amount: [{ amount: "2000", denom: "ucosm" }],
gas: "180000", // 180k
};
const accountNumber = 15;
const sequence = 16;
const signDoc = makeSignDoc([msg1, msg2], fee, chainId, undefined, accountNumber, sequence);
expect(signDoc).toEqual({
msgs: [msg1, msg2],
fee: fee,
chain_id: chainId,
account_number: accountNumber.toString(),
sequence: sequence.toString(),
memo: "",
});
});
});
});

View File

@ -0,0 +1,76 @@
/* eslint-disable @typescript-eslint/naming-convention */
import { toUtf8 } from "@cosmjs/encoding";
import { Uint53 } from "@cosmjs/math";
export interface AminoMsg {
readonly type: string;
readonly value: any;
}
export interface Coin {
readonly denom: string;
readonly amount: string;
}
export interface StdFee {
readonly amount: readonly Coin[];
readonly gas: string;
}
/**
* The document to be signed
*
* @see https://docs.cosmos.network/master/modules/auth/03_types.html#stdsigndoc
*/
export interface StdSignDoc {
readonly chain_id: string;
readonly account_number: string;
readonly sequence: string;
readonly fee: StdFee;
readonly msgs: readonly AminoMsg[];
readonly memo: string;
}
function sortedObject(obj: any): any {
if (typeof obj !== "object" || obj === null) {
return obj;
}
if (Array.isArray(obj)) {
return obj.map(sortedObject);
}
const sortedKeys = Object.keys(obj).sort();
const result: Record<string, any> = {};
// NOTE: Use forEach instead of reduce for performance with large objects eg Wasm code
sortedKeys.forEach((key) => {
result[key] = sortedObject(obj[key]);
});
return result;
}
/** Returns a JSON string with objects sorted by key */
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
export function sortedJsonStringify(obj: any): string {
return JSON.stringify(sortedObject(obj));
}
export function makeSignDoc(
msgs: readonly AminoMsg[],
fee: StdFee,
chainId: string,
memo: string | undefined,
accountNumber: number | string,
sequence: number | string,
): StdSignDoc {
return {
chain_id: chainId,
account_number: Uint53.fromString(accountNumber.toString()).toString(),
sequence: Uint53.fromString(sequence.toString()).toString(),
fee: fee,
msgs: msgs,
memo: memo || "",
};
}
export function serializeSignDoc(signDoc: StdSignDoc): Uint8Array {
return toUtf8(sortedJsonStringify(signDoc));
}

View File

@ -1,34 +1,6 @@
/* eslint-disable @typescript-eslint/naming-convention */
import { StdSignature } from "./signature";
export interface AminoMsg {
readonly type: string;
readonly value: any;
}
export interface Coin {
readonly denom: string;
readonly amount: string;
}
export interface StdFee {
readonly amount: readonly Coin[];
readonly gas: string;
}
/**
* The document to be signed
*
* @see https://docs.cosmos.network/master/modules/auth/03_types.html#stdsigndoc
*/
export interface StdSignDoc {
readonly chain_id: string;
readonly account_number: string;
readonly sequence: string;
readonly fee: StdFee;
readonly msgs: readonly AminoMsg[];
readonly memo: string;
}
import { StdSignDoc } from "./signdoc";
export type Algo = "secp256k1" | "ed25519" | "sr25519";