mirror of
https://github.com/cosmos/cosmjs.git
synced 2025-03-11 14:09:15 +00:00
Make Pen construction async and pubkey synchonous
This avoids a lot of computation, since the HD derivation only happens once.
This commit is contained in:
parent
6ed7305f8a
commit
999f0f3830
@ -6,22 +6,22 @@ import { Secp256k1Pen } from "./pen";
|
||||
const { fromHex } = Encoding;
|
||||
|
||||
describe("Sec256k1Pen", () => {
|
||||
it("can be constructed", () => {
|
||||
const pen = new Secp256k1Pen(
|
||||
it("can be constructed", async () => {
|
||||
const pen = await Secp256k1Pen.fromMnemonic(
|
||||
"zebra slush diet army arrest purpose hawk source west glimpse custom record",
|
||||
);
|
||||
expect(pen).toBeTruthy();
|
||||
});
|
||||
|
||||
describe("getPubkey", () => {
|
||||
describe("pubkey", () => {
|
||||
it("returns compressed pubkey", async () => {
|
||||
// special sign fit simple patrol salute grocery chicken wheat radar tonight ceiling
|
||||
// m/44'/118'/0'/0/0
|
||||
// pubkey: 02baa4ef93f2ce84592a49b1d729c074eab640112522a7a89f7d03ebab21ded7b6
|
||||
const pen = new Secp256k1Pen(
|
||||
const pen = await Secp256k1Pen.fromMnemonic(
|
||||
"special sign fit simple patrol salute grocery chicken wheat radar tonight ceiling",
|
||||
);
|
||||
expect(await pen.getPubkey()).toEqual(
|
||||
expect(pen.pubkey).toEqual(
|
||||
fromHex("02baa4ef93f2ce84592a49b1d729c074eab640112522a7a89f7d03ebab21ded7b6"),
|
||||
);
|
||||
});
|
||||
@ -29,10 +29,7 @@ describe("Sec256k1Pen", () => {
|
||||
|
||||
describe("createSignature", () => {
|
||||
it("creates correct signatures", async () => {
|
||||
// special sign fit simple patrol salute grocery chicken wheat radar tonight ceiling
|
||||
// m/44'/118'/0'/0/0
|
||||
// pubkey: 02baa4ef93f2ce84592a49b1d729c074eab640112522a7a89f7d03ebab21ded7b6
|
||||
const pen = new Secp256k1Pen(
|
||||
const pen = await Secp256k1Pen.fromMnemonic(
|
||||
"special sign fit simple patrol salute grocery chicken wheat radar tonight ceiling",
|
||||
);
|
||||
const data = Encoding.toAscii("foo bar");
|
||||
@ -41,7 +38,7 @@ describe("Sec256k1Pen", () => {
|
||||
const valid = await Secp256k1.verifySignature(
|
||||
new Secp256k1Signature(signature.slice(0, 32), signature.slice(32, 64)),
|
||||
new Sha256(data).digest(),
|
||||
fromHex("02baa4ef93f2ce84592a49b1d729c074eab640112522a7a89f7d03ebab21ded7b6"),
|
||||
pen.pubkey,
|
||||
);
|
||||
expect(valid).toEqual(true);
|
||||
});
|
||||
|
@ -22,7 +22,7 @@ export type PrehashType = "sha256" | "sha512" | null;
|
||||
* obfuscation of sensitive data.
|
||||
*/
|
||||
export interface Pen {
|
||||
readonly getPubkey: () => Promise<Uint8Array>;
|
||||
readonly pubkey: Uint8Array;
|
||||
readonly createSignature: (signBytes: Uint8Array, prehashType?: PrehashType) => Promise<Uint8Array>;
|
||||
}
|
||||
|
||||
@ -54,18 +54,22 @@ export function makeCosmoshubPath(a: number): readonly Slip10RawIndex[] {
|
||||
}
|
||||
|
||||
export class Secp256k1Pen implements Pen {
|
||||
private readonly mnemonic: EnglishMnemonic;
|
||||
private readonly hdPath: readonly Slip10RawIndex[];
|
||||
|
||||
public constructor(mnemonic: string, hdPath: readonly Slip10RawIndex[] = makeCosmoshubPath(0)) {
|
||||
this.mnemonic = new EnglishMnemonic(mnemonic);
|
||||
this.hdPath = hdPath;
|
||||
public static async fromMnemonic(
|
||||
mnemonic: string,
|
||||
hdPath: readonly Slip10RawIndex[] = makeCosmoshubPath(0),
|
||||
): Promise<Secp256k1Pen> {
|
||||
const seed = await Bip39.mnemonicToSeed(new EnglishMnemonic(mnemonic));
|
||||
const { privkey } = Slip10.derivePath(Slip10Curve.Secp256k1, seed, hdPath);
|
||||
const uncompressed = (await Secp256k1.makeKeypair(privkey)).pubkey;
|
||||
return new Secp256k1Pen(privkey, Secp256k1.compressPubkey(uncompressed));
|
||||
}
|
||||
|
||||
public async getPubkey(): Promise<Uint8Array> {
|
||||
const privkey = await this.getPrivkey();
|
||||
const uncompressed = (await Secp256k1.makeKeypair(privkey)).pubkey;
|
||||
return Secp256k1.compressPubkey(uncompressed);
|
||||
public readonly pubkey: Uint8Array;
|
||||
private readonly privkey: Uint8Array;
|
||||
|
||||
private constructor(privkey: Uint8Array, pubkey: Uint8Array) {
|
||||
this.privkey = privkey;
|
||||
this.pubkey = pubkey;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -76,12 +80,7 @@ export class Secp256k1Pen implements Pen {
|
||||
prehashType: PrehashType = "sha256",
|
||||
): Promise<Uint8Array> {
|
||||
const message = prehash(signBytes, prehashType);
|
||||
const signature = await Secp256k1.createSignature(message, await this.getPrivkey());
|
||||
const signature = await Secp256k1.createSignature(message, this.privkey);
|
||||
return new Uint8Array([...signature.r(32), ...signature.s(32)]);
|
||||
}
|
||||
|
||||
private async getPrivkey(): Promise<Uint8Array> {
|
||||
const seed = await Bip39.mnemonicToSeed(this.mnemonic);
|
||||
return Slip10.derivePath(Slip10Curve.Secp256k1, seed, this.hdPath).privkey;
|
||||
}
|
||||
}
|
||||
|
@ -25,9 +25,8 @@ const { fromBase64, fromHex, toAscii, toBase64, toHex } = Encoding;
|
||||
|
||||
const httpUrl = "http://localhost:1317";
|
||||
const defaultNetworkId = "testing";
|
||||
const faucetPen = new Secp256k1Pen(
|
||||
"economy stock theory fatal elder harbor betray wasp final emotion task crumble siren bottom lizard educate guess current outdoor pair theory focus wife stone",
|
||||
);
|
||||
const faucetMnemonic =
|
||||
"economy stock theory fatal elder harbor betray wasp final emotion task crumble siren bottom lizard educate guess current outdoor pair theory focus wife stone";
|
||||
const faucetAddress = "cosmos1pkptre7fdkl6gfrzlesjjvhxhlc3r4gmmk8rs6";
|
||||
const emptyAddress = "cosmos1ltkhnmdcqemmd2tkhnx7qx66tq7e0wykw2j85k";
|
||||
|
||||
@ -118,7 +117,7 @@ async function uploadContract(client: RestClient, pen: Pen): Promise<PostTxsResp
|
||||
|
||||
const account = (await client.authAccounts(faucetAddress)).result.value;
|
||||
const signBytes = makeSignBytes([theMsg], fee, defaultNetworkId, memo, account);
|
||||
const signature = encodeSecp256k1Signature(await pen.getPubkey(), await pen.createSignature(signBytes));
|
||||
const signature = encodeSecp256k1Signature(pen.pubkey, await pen.createSignature(signBytes));
|
||||
const signedTx = makeSignedTx(theMsg, fee, memo, signature);
|
||||
return client.postTx(marshalTx(signedTx));
|
||||
}
|
||||
@ -155,7 +154,7 @@ async function instantiateContract(
|
||||
|
||||
const account = (await client.authAccounts(faucetAddress)).result.value;
|
||||
const signBytes = makeSignBytes([theMsg], fee, defaultNetworkId, memo, account);
|
||||
const signature = encodeSecp256k1Signature(await pen.getPubkey(), await pen.createSignature(signBytes));
|
||||
const signature = encodeSecp256k1Signature(pen.pubkey, await pen.createSignature(signBytes));
|
||||
const signedTx = makeSignedTx(theMsg, fee, memo, signature);
|
||||
return client.postTx(marshalTx(signedTx));
|
||||
}
|
||||
@ -187,7 +186,7 @@ async function executeContract(
|
||||
|
||||
const account = (await client.authAccounts(faucetAddress)).result.value;
|
||||
const signBytes = makeSignBytes([theMsg], fee, defaultNetworkId, memo, account);
|
||||
const signature = encodeSecp256k1Signature(await pen.getPubkey(), await pen.createSignature(signBytes));
|
||||
const signature = encodeSecp256k1Signature(pen.pubkey, await pen.createSignature(signBytes));
|
||||
const signedTx = makeSignedTx(theMsg, fee, memo, signature);
|
||||
return client.postTx(marshalTx(signedTx));
|
||||
}
|
||||
@ -230,6 +229,7 @@ describe("RestClient", () => {
|
||||
describe("post", () => {
|
||||
it("can send tokens", async () => {
|
||||
pendingWithoutCosmos();
|
||||
const pen = await Secp256k1Pen.fromMnemonic(faucetMnemonic);
|
||||
|
||||
const memo = "My first contract on chain";
|
||||
const theMsg: MsgSend = {
|
||||
@ -260,10 +260,7 @@ describe("RestClient", () => {
|
||||
const account = (await client.authAccounts(faucetAddress)).result.value;
|
||||
|
||||
const signBytes = makeSignBytes([theMsg], fee, defaultNetworkId, memo, account);
|
||||
const signature = encodeSecp256k1Signature(
|
||||
await faucetPen.getPubkey(),
|
||||
await faucetPen.createSignature(signBytes),
|
||||
);
|
||||
const signature = encodeSecp256k1Signature(pen.pubkey, await pen.createSignature(signBytes));
|
||||
const signedTx = makeSignedTx(theMsg, fee, memo, signature);
|
||||
const result = await client.postTx(marshalTx(signedTx));
|
||||
// console.log("Raw log:", result.raw_log);
|
||||
@ -272,6 +269,7 @@ describe("RestClient", () => {
|
||||
|
||||
it("can upload, instantiate and execute wasm", async () => {
|
||||
pendingWithoutCosmos();
|
||||
const pen = await Secp256k1Pen.fromMnemonic(faucetMnemonic);
|
||||
const client = new RestClient(httpUrl);
|
||||
|
||||
const transferAmount: readonly Coin[] = [
|
||||
@ -291,7 +289,7 @@ describe("RestClient", () => {
|
||||
// upload
|
||||
{
|
||||
// console.log("Raw log:", result.raw_log);
|
||||
const result = await uploadContract(client, faucetPen);
|
||||
const result = await uploadContract(client, pen);
|
||||
expect(result.code).toBeFalsy();
|
||||
const logs = parseSuccess(result.raw_log);
|
||||
const codeIdAttr = findAttribute(logs, "message", "code_id");
|
||||
@ -304,13 +302,7 @@ describe("RestClient", () => {
|
||||
|
||||
// instantiate
|
||||
{
|
||||
const result = await instantiateContract(
|
||||
client,
|
||||
faucetPen,
|
||||
codeId,
|
||||
beneficiaryAddress,
|
||||
transferAmount,
|
||||
);
|
||||
const result = await instantiateContract(client, pen, codeId, beneficiaryAddress, transferAmount);
|
||||
expect(result.code).toBeFalsy();
|
||||
// console.log("Raw log:", result.raw_log);
|
||||
const logs = parseSuccess(result.raw_log);
|
||||
@ -325,7 +317,7 @@ describe("RestClient", () => {
|
||||
|
||||
// execute
|
||||
{
|
||||
const result = await executeContract(client, faucetPen, contractAddress);
|
||||
const result = await executeContract(client, pen, contractAddress);
|
||||
expect(result.code).toBeFalsy();
|
||||
// console.log("Raw log:", result.raw_log);
|
||||
const [firstLog] = parseSuccess(result.raw_log);
|
||||
@ -343,6 +335,7 @@ describe("RestClient", () => {
|
||||
describe("query", () => {
|
||||
it("can list upload code", async () => {
|
||||
pendingWithoutCosmos();
|
||||
const pen = await Secp256k1Pen.fromMnemonic(faucetMnemonic);
|
||||
const client = new RestClient(httpUrl);
|
||||
|
||||
// check with contracts were here first to compare
|
||||
@ -351,7 +344,7 @@ describe("RestClient", () => {
|
||||
const numExisting = existingInfos.length;
|
||||
|
||||
// upload data
|
||||
const result = await uploadContract(client, faucetPen);
|
||||
const result = await uploadContract(client, pen);
|
||||
expect(result.code).toBeFalsy();
|
||||
const logs = parseSuccess(result.raw_log);
|
||||
const codeIdAttr = findAttribute(logs, "message", "code_id");
|
||||
@ -372,6 +365,7 @@ describe("RestClient", () => {
|
||||
|
||||
it("can list contracts and get info", async () => {
|
||||
pendingWithoutCosmos();
|
||||
const pen = await Secp256k1Pen.fromMnemonic(faucetMnemonic);
|
||||
const client = new RestClient(httpUrl);
|
||||
const beneficiaryAddress = makeRandomAddress();
|
||||
const transferAmount: readonly Coin[] = [
|
||||
@ -387,7 +381,7 @@ describe("RestClient", () => {
|
||||
if (existingInfos.length > 0) {
|
||||
codeId = existingInfos[existingInfos.length - 1].id;
|
||||
} else {
|
||||
const uploaded = await uploadContract(client, faucetPen);
|
||||
const uploaded = await uploadContract(client, pen);
|
||||
expect(uploaded.code).toBeFalsy();
|
||||
const uploadLogs = parseSuccess(uploaded.raw_log);
|
||||
const codeIdAttr = findAttribute(uploadLogs, "message", "code_id");
|
||||
@ -397,7 +391,7 @@ describe("RestClient", () => {
|
||||
// create new instance and compare before and after
|
||||
const existingContracts = await client.listContractAddresses();
|
||||
|
||||
const result = await instantiateContract(client, faucetPen, codeId, beneficiaryAddress, transferAmount);
|
||||
const result = await instantiateContract(client, pen, codeId, beneficiaryAddress, transferAmount);
|
||||
expect(result.code).toBeFalsy();
|
||||
const logs = parseSuccess(result.raw_log);
|
||||
const contractAddressAttr = findAttribute(logs, "message", "contract_address");
|
||||
|
11
packages/sdk/types/pen.d.ts
vendored
11
packages/sdk/types/pen.d.ts
vendored
@ -11,7 +11,7 @@ export declare type PrehashType = "sha256" | "sha512" | null;
|
||||
* obfuscation of sensitive data.
|
||||
*/
|
||||
export interface Pen {
|
||||
readonly getPubkey: () => Promise<Uint8Array>;
|
||||
readonly pubkey: Uint8Array;
|
||||
readonly createSignature: (signBytes: Uint8Array, prehashType?: PrehashType) => Promise<Uint8Array>;
|
||||
}
|
||||
/**
|
||||
@ -20,13 +20,12 @@ export interface Pen {
|
||||
*/
|
||||
export declare function makeCosmoshubPath(a: number): readonly Slip10RawIndex[];
|
||||
export declare class Secp256k1Pen implements Pen {
|
||||
private readonly mnemonic;
|
||||
private readonly hdPath;
|
||||
constructor(mnemonic: string, hdPath?: readonly Slip10RawIndex[]);
|
||||
getPubkey(): Promise<Uint8Array>;
|
||||
static fromMnemonic(mnemonic: string, hdPath?: readonly Slip10RawIndex[]): Promise<Secp256k1Pen>;
|
||||
readonly pubkey: Uint8Array;
|
||||
private readonly privkey;
|
||||
private constructor();
|
||||
/**
|
||||
* Creates a fixed length encoding of the signature parameters r (32 bytes) and s (32 bytes).
|
||||
*/
|
||||
createSignature(signBytes: Uint8Array, prehashType?: PrehashType): Promise<Uint8Array>;
|
||||
private getPrivkey;
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user