luniehq / cosmos-keys

Library for creating keys and signing messages on Cosmos 🔑
https://lunie.io
28 stars 19 forks source link

Unable to sign transactions #6

Open AlexBHarley opened 5 years ago

AlexBHarley commented 5 years ago

Have run into multiple issues with this library, namely signature verification failed; verify correct account sequence and chain-id but I thought it'd be best to set up a test repo to see what I'm doing wrong.

I'm running the cosmos sdk application tutorial https://github.com/cosmos/sdk-application-tutorial and I have edited the handleMsgBuyName function in sdk-application-tutorial/x/nameservice/handler.go

to look like this

func handleMsgBuyName(ctx sdk.Context, keeper Keeper, msg MsgBuyName) sdk.Result {
    keeper.SetOwner(ctx, msg.Name, msg.Buyer)
    keeper.SetPrice(ctx, msg.Name, msg.Bid)
    return sdk.Result{}
}

My script then looks like this

import fs from "fs";
import fetch from "isomorphic-fetch";
import { getWalletFromSeed, signWithPrivateKey } from "./keys";

const mnemonic =
  "gather clown spoon share report crystal educate lift fatal gaze flip live accident detect gas humble reunion what range trouble fatal clump desert actor";
const { cosmosAddress, privateKey, publicKey } = getWalletFromSeed(mnemonic);

(async function main() {
  const request = {
    base_req: {
      chain_id: "namechain",
      from: cosmosAddress
    },
    name: "test.com",
    amount: "10dxt",
    buyer: cosmosAddress
  };

  const unsigned = await fetch("http://localhost:1317/nameservice/names", {
    method: "post",
    headers: {
      "content-type": "application/x-www-form-urlencoded"
    },
    body: JSON.stringify(request)
  }).then(x => x.json());

  const result = await fetch(
    `http://localhost:1317/auth/accounts/${cosmosAddress}`
  ).then(x => x.json());
  let { account_number, sequence } = result.value;

  const signMsg = JSON.stringify(
    removeEmptyProperties({
      chain_id: "namechain",
      account_number,
      sequence,
      fee: unsigned.value.fee,
      msgs: unsigned.value.msg,
      memo: unsigned.value.memo
    })
  );

  const signature = signWithPrivateKey(signMsg, Buffer.from(privateKey, "hex"));

  const tx = JSON.stringify({
    type: unsigned.type,
    value: {
      chain_id: "namechain",
      account_number,
      sequence,
      memo: unsigned.value.memo,
      fee: unsigned.value.fee,
      msg: unsigned.value.msg,
      signatures: [
        {
          signature: signature.toString("base64"),
          account_number: account_number,
          sequence: sequence,
          pub_key: {
            type: "tendermint/PubKeySecp256k1",
            value: publicKey.toString("base64")
          }
        }
      ]
    }
  });

  console.log(JSON.stringify(JSON.parse(tx), null, 2));
  fs.writeFileSync("/Users/alex/signed.json", Buffer.from(tx));
})();

function removeEmptyProperties(jsonTx) {
  if (jsonTx === null) return null;

  if (Array.isArray(jsonTx)) {
    return jsonTx.map(removeEmptyProperties);
  }

  // string or number
  if (typeof jsonTx !== "object") {
    return jsonTx;
  }

  const sorted = {};

  Object.keys(jsonTx)
    .sort()
    .forEach(key => {
      if (jsonTx[key] === undefined || jsonTx[key] === null) {
        return;
      }

      sorted[key] = removeEmptyProperties(jsonTx[key]);
    });
  return sorted;
}

Is there something obvious I am doing wrong?

AlexBHarley commented 5 years ago

./keys is just a nodejs compatible file of cosmos-keys that I have made. Didn't want to bother setting up a tsc flow

faboweb commented 5 years ago

I can look more into this, but you replicate a lot of the logic of https://github.com/luniehq/cosmos-api which you may want to fork instead of creating all the structures and signing logic again.