mirror of
https://github.com/cosmos/cosmjs.git
synced 2025-03-11 14:09:15 +00:00
Add findSequenceForSignedTx
This commit is contained in:
parent
0818542fec
commit
83479f3bf7
@ -24,3 +24,4 @@ export {
|
||||
encodeBech32Pubkey,
|
||||
encodeSecp256k1Pubkey,
|
||||
} from "./pubkey";
|
||||
export { findSequenceForSignedTx } from "./sequence";
|
||||
|
30
packages/sdk/src/sequence.spec.ts
Normal file
30
packages/sdk/src/sequence.spec.ts
Normal 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();
|
||||
});
|
||||
});
|
||||
});
|
46
packages/sdk/src/sequence.ts
Normal file
46
packages/sdk/src/sequence.ts
Normal 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;
|
||||
}
|
57
packages/sdk/src/testdata/txresponse1.json
vendored
Normal file
57
packages/sdk/src/testdata/txresponse1.json
vendored
Normal 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"
|
||||
}
|
57
packages/sdk/src/testdata/txresponse2.json
vendored
Normal file
57
packages/sdk/src/testdata/txresponse2.json
vendored
Normal 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"
|
||||
}
|
57
packages/sdk/src/testdata/txresponse3.json
vendored
Normal file
57
packages/sdk/src/testdata/txresponse3.json
vendored
Normal 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"
|
||||
}
|
1
packages/sdk/types/index.d.ts
vendored
1
packages/sdk/types/index.d.ts
vendored
@ -23,3 +23,4 @@ export {
|
||||
encodeBech32Pubkey,
|
||||
encodeSecp256k1Pubkey,
|
||||
} from "./pubkey";
|
||||
export { findSequenceForSignedTx } from "./sequence";
|
||||
|
19
packages/sdk/types/sequence.d.ts
vendored
Normal file
19
packages/sdk/types/sequence.d.ts
vendored
Normal 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>;
|
Loading…
x
Reference in New Issue
Block a user