Add findSequenceForSignedTx

This commit is contained in:
Simon Warta 2020-02-14 14:06:03 +01:00
parent 0818542fec
commit 83479f3bf7
8 changed files with 268 additions and 0 deletions

View File

@ -24,3 +24,4 @@ export {
encodeBech32Pubkey,
encodeSecp256k1Pubkey,
} from "./pubkey";
export { findSequenceForSignedTx } from "./sequence";

View File

@ -0,0 +1,30 @@
import { findSequenceForSignedTx } from "./sequence";
import response1 from "./testdata/txresponse1.json";
import response2 from "./testdata/txresponse2.json";
import response3 from "./testdata/txresponse3.json";
// Those values must match ./testdata/txresponse*.json
const chainId = "testing";
const accountNumber = 4;
describe("sequence", () => {
describe("findSequenceForSignedTx", () => {
it("works", async () => {
const current = 100; // what we get from GET /auth/accounts/{address}
expect(await findSequenceForSignedTx(response1.tx, chainId, accountNumber, current)).toEqual(10);
// We know response3.height > response1.height, so the sequence must be at least 10+1
expect(await findSequenceForSignedTx(response3.tx, chainId, accountNumber, current, 11)).toEqual(19);
// We know response3.height > response2.height > response1.height, so the sequence must be at least 10+1 and smaller than 19
expect(await findSequenceForSignedTx(response2.tx, chainId, accountNumber, 19, 11)).toEqual(13);
});
it("returns undefined when sequence is not in range", async () => {
expect(await findSequenceForSignedTx(response1.tx, chainId, accountNumber, 5)).toBeUndefined();
expect(await findSequenceForSignedTx(response1.tx, chainId, accountNumber, 20, 11)).toBeUndefined();
expect(await findSequenceForSignedTx(response1.tx, chainId, accountNumber, 20, 50)).toBeUndefined();
// upper bound is not included in the possible results
expect(await findSequenceForSignedTx(response1.tx, chainId, accountNumber, 10)).toBeUndefined();
});
});
});

View File

@ -0,0 +1,46 @@
import { Secp256k1, Secp256k1Signature, Sha256 } from "@iov/crypto";
import { makeSignBytes } from "./encoding";
import { decodeSignature } from "./signature";
import { CosmosSdkTx } from "./types";
/**
* Serach for sequence s with `min` <= `s` < `upperBound` to find the sequence that was used to sign the transaction
*
* @param tx The signed transaction
* @param chainId The chain ID for which this transaction was signed
* @param accountNumber The account number for which this transaction was signed
* @param upperBound The upper bound for the testing, i.e. sequence must be lower than this value
* @param min The lowest sequence that is tested
*
* @returns the sequence if a match was found and undefined otherwise
*/
export async function findSequenceForSignedTx(
tx: CosmosSdkTx,
chainId: string,
accountNumber: number,
upperBound: number,
min = 1,
): Promise<number | undefined> {
const firstSignature = tx.value.signatures.find(() => true);
if (!firstSignature) throw new Error("Signature missing in tx");
const { pubkey, signature } = decodeSignature(firstSignature);
const secp256keSignature = new Secp256k1Signature(signature.slice(0, 32), signature.slice(32, 64));
for (let s = min; s < upperBound; s++) {
// console.log(`Trying sequence ${s}`);
const signBytes = makeSignBytes(
tx.value.msg,
tx.value.fee,
chainId,
tx.value.memo || "",
accountNumber,
s,
);
const prehashed = new Sha256(signBytes).digest();
const valid = await Secp256k1.verifySignature(secp256keSignature, prehashed, pubkey);
if (valid) return s;
}
return undefined;
}

View File

@ -0,0 +1,57 @@
{
"height": "15888",
"txhash": "672DEDE8EF4DE8B5818959F417CCA357079D4D7A19C4B65443C7FBF8176AABF9",
"raw_log": "[{\"msg_index\":0,\"log\":\"\",\"events\":[{\"type\":\"message\",\"attributes\":[{\"key\":\"action\",\"value\":\"send\"},{\"key\":\"sender\",\"value\":\"cosmos1pkptre7fdkl6gfrzlesjjvhxhlc3r4gmmk8rs6\"},{\"key\":\"module\",\"value\":\"bank\"}]},{\"type\":\"transfer\",\"attributes\":[{\"key\":\"recipient\",\"value\":\"cosmos1t70qnpr0az8tf7py83m4ue5y89w58lkjmx0yq2\"},{\"key\":\"amount\",\"value\":\"75000ucosm\"}]}]}]",
"logs": [
{
"msg_index": 0,
"log": "",
"events": [
{
"type": "message",
"attributes": [
{ "key": "action", "value": "send" },
{ "key": "sender", "value": "cosmos1pkptre7fdkl6gfrzlesjjvhxhlc3r4gmmk8rs6" },
{ "key": "module", "value": "bank" }
]
},
{
"type": "transfer",
"attributes": [
{ "key": "recipient", "value": "cosmos1t70qnpr0az8tf7py83m4ue5y89w58lkjmx0yq2" },
{ "key": "amount", "value": "75000ucosm" }
]
}
]
}
],
"gas_wanted": "200000",
"gas_used": "65407",
"tx": {
"type": "cosmos-sdk/StdTx",
"value": {
"msg": [
{
"type": "cosmos-sdk/MsgSend",
"value": {
"from_address": "cosmos1pkptre7fdkl6gfrzlesjjvhxhlc3r4gmmk8rs6",
"to_address": "cosmos1t70qnpr0az8tf7py83m4ue5y89w58lkjmx0yq2",
"amount": [{ "denom": "ucosm", "amount": "75000" }]
}
}
],
"fee": { "amount": [{ "denom": "ucosm", "amount": "5000" }], "gas": "200000" },
"signatures": [
{
"pub_key": {
"type": "tendermint/PubKeySecp256k1",
"value": "A08EGB7ro1ORuFhjOnZcSgwYlpe0DSFjVNUIkNNQxwKQ"
},
"signature": "US7oH8S/8TxVrtBQkOhHxAM+oDB2spNAEawgh6H8CCFLRMOJK+uvQZZ6ceUgUsvDbxwCz7re1RU272fymMYRZQ=="
}
],
"memo": "My first payment"
}
},
"timestamp": "2020-02-14T11:25:55Z"
}

View File

@ -0,0 +1,57 @@
{
"height": "16456",
"txhash": "7BFE4B93AF190F60132C62D08FDF50BE462FBCE374EB13D3FD0C32461E771EC0",
"raw_log": "[{\"msg_index\":0,\"log\":\"\",\"events\":[{\"type\":\"message\",\"attributes\":[{\"key\":\"action\",\"value\":\"send\"},{\"key\":\"sender\",\"value\":\"cosmos1pkptre7fdkl6gfrzlesjjvhxhlc3r4gmmk8rs6\"},{\"key\":\"module\",\"value\":\"bank\"}]},{\"type\":\"transfer\",\"attributes\":[{\"key\":\"recipient\",\"value\":\"cosmos1t70qnpr0az8tf7py83m4ue5y89w58lkjmx0yq2\"},{\"key\":\"amount\",\"value\":\"75000ucosm\"}]}]}]",
"logs": [
{
"msg_index": 0,
"log": "",
"events": [
{
"type": "message",
"attributes": [
{ "key": "action", "value": "send" },
{ "key": "sender", "value": "cosmos1pkptre7fdkl6gfrzlesjjvhxhlc3r4gmmk8rs6" },
{ "key": "module", "value": "bank" }
]
},
{
"type": "transfer",
"attributes": [
{ "key": "recipient", "value": "cosmos1t70qnpr0az8tf7py83m4ue5y89w58lkjmx0yq2" },
{ "key": "amount", "value": "75000ucosm" }
]
}
]
}
],
"gas_wanted": "200000",
"gas_used": "65407",
"tx": {
"type": "cosmos-sdk/StdTx",
"value": {
"msg": [
{
"type": "cosmos-sdk/MsgSend",
"value": {
"from_address": "cosmos1pkptre7fdkl6gfrzlesjjvhxhlc3r4gmmk8rs6",
"to_address": "cosmos1t70qnpr0az8tf7py83m4ue5y89w58lkjmx0yq2",
"amount": [{ "denom": "ucosm", "amount": "75000" }]
}
}
],
"fee": { "amount": [{ "denom": "ucosm", "amount": "5000" }], "gas": "200000" },
"signatures": [
{
"pub_key": {
"type": "tendermint/PubKeySecp256k1",
"value": "A08EGB7ro1ORuFhjOnZcSgwYlpe0DSFjVNUIkNNQxwKQ"
},
"signature": "ltvd9Rb3RF4zjbUVrpDpkok34g+py7XR8ZcM0tZUYRxxVdcMEin010x+ZFd/mOuutPj9fDmSENnienc/yi4msw=="
}
],
"memo": "My first payment"
}
},
"timestamp": "2020-02-14T11:35:41Z"
}

View File

@ -0,0 +1,57 @@
{
"height": "20730",
"txhash": "625BC75E697F73DA037387C34002BB2F682E7ACDCC4E015D3E90420516C6D0C8",
"raw_log": "[{\"msg_index\":0,\"log\":\"\",\"events\":[{\"type\":\"message\",\"attributes\":[{\"key\":\"action\",\"value\":\"send\"},{\"key\":\"sender\",\"value\":\"cosmos1pkptre7fdkl6gfrzlesjjvhxhlc3r4gmmk8rs6\"},{\"key\":\"module\",\"value\":\"bank\"}]},{\"type\":\"transfer\",\"attributes\":[{\"key\":\"recipient\",\"value\":\"cosmos1t70qnpr0az8tf7py83m4ue5y89w58lkjmx0yq2\"},{\"key\":\"amount\",\"value\":\"75000ucosm\"}]}]}]",
"logs": [
{
"msg_index": 0,
"log": "",
"events": [
{
"type": "message",
"attributes": [
{ "key": "action", "value": "send" },
{ "key": "sender", "value": "cosmos1pkptre7fdkl6gfrzlesjjvhxhlc3r4gmmk8rs6" },
{ "key": "module", "value": "bank" }
]
},
{
"type": "transfer",
"attributes": [
{ "key": "recipient", "value": "cosmos1t70qnpr0az8tf7py83m4ue5y89w58lkjmx0yq2" },
{ "key": "amount", "value": "75000ucosm" }
]
}
]
}
],
"gas_wanted": "200000",
"gas_used": "65407",
"tx": {
"type": "cosmos-sdk/StdTx",
"value": {
"msg": [
{
"type": "cosmos-sdk/MsgSend",
"value": {
"from_address": "cosmos1pkptre7fdkl6gfrzlesjjvhxhlc3r4gmmk8rs6",
"to_address": "cosmos1t70qnpr0az8tf7py83m4ue5y89w58lkjmx0yq2",
"amount": [{ "denom": "ucosm", "amount": "75000" }]
}
}
],
"fee": { "amount": [{ "denom": "ucosm", "amount": "5000" }], "gas": "200000" },
"signatures": [
{
"pub_key": {
"type": "tendermint/PubKeySecp256k1",
"value": "A08EGB7ro1ORuFhjOnZcSgwYlpe0DSFjVNUIkNNQxwKQ"
},
"signature": "eOFGl1tIHDMv3JdCK9fRSikVbYUD8+B0ksb3dJFya8MPYgpEpdSA7zZc+5n/cW6LR/BJdib4nqmJQv1yD9lm3g=="
}
],
"memo": "My first payment"
}
},
"timestamp": "2020-02-14T12:48:56Z"
}

View File

@ -23,3 +23,4 @@ export {
encodeBech32Pubkey,
encodeSecp256k1Pubkey,
} from "./pubkey";
export { findSequenceForSignedTx } from "./sequence";

19
packages/sdk/types/sequence.d.ts vendored Normal file
View File

@ -0,0 +1,19 @@
import { CosmosSdkTx } from "./types";
/**
* Serach for sequence s with `min` <= `s` < `upperBound` to find the sequence that was used to sign the transaction
*
* @param tx The signed transaction
* @param chainId The chain ID for which this transaction was signed
* @param accountNumber The account number for which this transaction was signed
* @param upperBound The upper bound for the testing, i.e. sequence must be lower than this value
* @param min The lowest sequence that is tested
*
* @returns the sequence if a match was found and undefined otherwise
*/
export declare function findSequenceForSignedTx(
tx: CosmosSdkTx,
chainId: string,
accountNumber: number,
upperBound: number,
min?: number,
): Promise<number | undefined>;