Skip to content

Commit

Permalink
feat: Query txs by signature and by address+seq (#9750)
Browse files Browse the repository at this point in the history
<!--
The default pull request template is for types feat, fix, or refactor.
For other templates, add one of the following parameters to the url:
- template=docs.md
- template=other.md
-->

## Description

Closes: #9741

<!-- Add a description of the changes that this PR introduces and the files that
are the most critical to review. -->

---

### Author Checklist

*All items are required. Please add a note to the item if the item is not applicable and
please add links to any relevant follow up issues.*

I have...

- [ ] included the correct [type prefix](https://github.com/commitizen/conventional-commit-types/blob/v3.0.0/index.json) in the PR title
- [ ] added `!` to the type prefix if API or client breaking change
- [ ] targeted the correct branch (see [PR Targeting](https://github.com/cosmos/cosmos-sdk/blob/master/CONTRIBUTING.md#pr-targeting))
- [ ] provided a link to the relevant issue or specification
- [ ] followed the guidelines for [building modules](https://github.com/cosmos/cosmos-sdk/blob/master/docs/building-modules)
- [ ] included the necessary unit and integration [tests](https://github.com/cosmos/cosmos-sdk/blob/master/CONTRIBUTING.md#testing)
- [ ] added a changelog entry to `CHANGELOG.md`
- [ ] included comments for [documenting Go code](https://blog.golang.org/godoc)
- [ ] updated the relevant documentation or specification
- [ ] reviewed "Files changed" and left comments if necessary
- [ ] confirmed all CI checks have passed

### Reviewers Checklist

*All items are required. Please add a note if the item is not applicable and please add
your handle next to the items reviewed if you only reviewed selected items.*

I have...

- [ ] confirmed the correct [type prefix](https://github.com/commitizen/conventional-commit-types/blob/v3.0.0/index.json) in the PR title
- [ ] confirmed `!` in the type prefix if API or client breaking change
- [ ] confirmed all author checklist items have been addressed
- [ ] reviewed state machine logic
- [ ] reviewed API design and naming
- [ ] reviewed documentation is accurate
- [ ] reviewed tests and test coverage
- [ ] manually tested (if applicable)

(cherry picked from commit 7c19434)

# Conflicts:
#	CHANGELOG.md
#	x/auth/client/cli/cli_test.go
#	x/auth/client/cli/query.go
  • Loading branch information
amaury1093 authored and mergify-bot committed Jul 27, 2021
1 parent c26244c commit 0030c0b
Show file tree
Hide file tree
Showing 6 changed files with 382 additions and 12 deletions.
39 changes: 39 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,46 @@ Ref: https://keepachangelog.com/en/1.0.0/

# Changelog

<<<<<<< HEAD
## Unreleased
=======
## [Unreleased]

### Features

* [\#9533](https://github.com/cosmos/cosmos-sdk/pull/9533) Added a new gRPC method, `DenomOwners`, in `x/bank` to query for all account holders of a specific denomination.
* (bank) [\#9618](https://github.com/cosmos/cosmos-sdk/pull/9618) Update bank.Metadata: add URI and URIHash attributes.
* [\#9750](https://github.com/cosmos/cosmos-sdk/pull/9750) Emit events for tx signature and sequence, so clients can now query txs by signature (`tx.signature='<base64_sig>'`) or by address and sequence combo (`tx.acc_seq='<addr>/<seq>'`).

### API Breaking Changes

* [\#9628](https://github.com/cosmos/cosmos-sdk/pull/9628) Rename `x/{mod}/legacy` to `x/{mod}/migrations`.
* [\#9571](https://github.com/cosmos/cosmos-sdk/pull/9571) Implemented error handling for staking hooks, which now return an error on failure.
* [\#9427](https://github.com/cosmos/cosmos-sdk/pull/9427) Move simapp `FundAccount` and `FundModuleAccount` to `x/bank/testutil`
* (client/tx) [\#9421](https://github.com/cosmos/cosmos-sdk/pull/9421/) `BuildUnsignedTx`, `BuildSimTx`, `PrintUnsignedStdTx` functions are moved to
the Tx Factory as methods.
* (client/keys) [\#9407](https://github.com/cosmos/cosmos-sdk/pull/9601) Added `keys rename` CLI command and `Keyring.Rename` interface method to rename a key in the keyring.
* (x/slashing) [\#9458](https://github.com/cosmos/cosmos-sdk/pull/9458) Coins burned from slashing is now returned from Slash function and included in Slash event.
* [\#9246](https://github.com/cosmos/cosmos-sdk/pull/9246) The `New` method for the network package now returns an error.
* (codec) [\#9521](https://github.com/cosmos/cosmos-sdk/pull/9521) Removed deprecated `clientCtx.JSONCodec` from `client.Context`.
* (codec) [\#9521](https://github.com/cosmos/cosmos-sdk/pull/9521) Rename `EncodingConfig.Marshaler` to `Codec`.
* [\#9418](https://github.com/cosmos/cosmos-sdk/pull/9418) `sdk.Msg`'s `GetSigners()` method updated to return `[]string`.
* [\#9594](https://github.com/cosmos/cosmos-sdk/pull/9594) `RESTHandlerFn` argument is removed from the `gov/NewProposalHandler`.
* [\#9594](https://github.com/cosmos/cosmos-sdk/pull/9594) `types/rest` package moved to `testutil/rest`.
* [\#9432](https://github.com/cosmos/cosmos-sdk/pull/9432) `ConsensusParamsKeyTable` moved from `params/keeper` to `params/types`
* [\#9576](https://github.com/cosmos/cosmos-sdk/pull/9576) Add debug error message to `sdkerrors.QueryResult` when enabled
* [\#9650](https://github.com/cosmos/cosmos-sdk/pull/9650) Removed deprecated message handler implementation from the SDK modules.

### Client Breaking Changes

* [\#9594](https://github.com/cosmos/cosmos-sdk/pull/9594) Remove legacy REST API. Please see the [REST Endpoints Migration guide](https://docs.cosmos.network/master/migrations/rest.html) to migrate to the new REST endpoints.


### CLI Breaking Changes

* [\#9246](https://github.com/cosmos/cosmos-sdk/pull/9246) Removed the CLI flag `--setup-config-only` from the `testnet` command and added the subcommand `init-files`.
* [\#9371](https://github.com/cosmos/cosmos-sdk/pull/9371) Non-zero default fees/Server will error if there's an empty value for min-gas-price in app.toml
>>>>>>> 7c1943400 (feat: Query txs by signature and by address+seq (#9750))
### Improvements

Expand Down
5 changes: 5 additions & 0 deletions types/events.go
Original file line number Diff line number Diff line change
Expand Up @@ -223,6 +223,11 @@ func toBytes(i interface{}) []byte {

// Common event types and attribute keys
var (
EventTypeTx = "tx"

AttributeKeyAccountSequence = "acc_seq"
AttributeKeySignature = "signature"

EventTypeMessage = "message"

AttributeKeyAction = "action"
Expand Down
68 changes: 68 additions & 0 deletions x/auth/ante/sigverify.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package ante

import (
"bytes"
"encoding/base64"
"encoding/hex"
"fmt"

Expand Down Expand Up @@ -90,6 +91,34 @@ func (spkd SetPubKeyDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, simulate b
spkd.ak.SetAccount(ctx, acc)
}

// Also emit the following events, so that txs can be indexed by these
// indices:
// - signature (via `tx.signature='<sig_as_base64>'`),
// - concat(address,"/",sequence) (via `tx.acc_seq='cosmos1abc...def/42'`).
sigs, err := sigTx.GetSignaturesV2()
if err != nil {
return ctx, err
}

var events sdk.Events
for i, sig := range sigs {
events = append(events, sdk.NewEvent(sdk.EventTypeTx,
sdk.NewAttribute(sdk.AttributeKeyAccountSequence, fmt.Sprintf("%s/%d", signers[i], sig.Sequence)),
))

sigBzs, err := signatureDataToBz(sig.Data)
if err != nil {
return ctx, err
}
for _, sigBz := range sigBzs {
events = append(events, sdk.NewEvent(sdk.EventTypeTx,
sdk.NewAttribute(sdk.AttributeKeySignature, base64.StdEncoding.EncodeToString(sigBz)),
))
}
}

ctx.EventManager().EmitEvents(events)

return next(ctx, tx, simulate)
}

Expand Down Expand Up @@ -436,3 +465,42 @@ func CountSubKeys(pub cryptotypes.PubKey) int {

return numKeys
}

// signatureDataToBz converts a SignatureData into raw bytes signature.
// For SingleSignatureData, it returns the signature raw bytes.
// For MultiSignatureData, it returns an array of all individual signatures,
// as well as the aggregated signature.
func signatureDataToBz(data signing.SignatureData) ([][]byte, error) {
if data == nil {
return nil, fmt.Errorf("got empty SignatureData")
}

switch data := data.(type) {
case *signing.SingleSignatureData:
return [][]byte{data.Signature}, nil
case *signing.MultiSignatureData:
sigs := [][]byte{}
var err error

for _, d := range data.Signatures {
nestedSigs, err := signatureDataToBz(d)
if err != nil {
return nil, err
}
sigs = append(sigs, nestedSigs...)
}

multisig := cryptotypes.MultiSignature{
Signatures: sigs,
}
aggregatedSig, err := multisig.Marshal()
if err != nil {
return nil, err
}
sigs = append(sigs, aggregatedSig)

return sigs, nil
default:
return nil, sdkerrors.ErrInvalidType.Wrapf("unexpected signature data type %T", data)
}
}
137 changes: 132 additions & 5 deletions x/auth/client/cli/cli_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ package cli_test

import (
"context"
"encoding/base64"
"encoding/json"
"fmt"
"io/ioutil"
Expand Down Expand Up @@ -224,7 +225,7 @@ func checkSignatures(require *require.Assertions, txCfg client.TxConfig, output
}
}

func (s *IntegrationTestSuite) TestCLIQueryTxCmd() {
func (s *IntegrationTestSuite) TestCLIQueryTxCmdByHash() {
val := s.network.Validators[0]

account2, err := val.ClientCtx.Keyring.Key("newAccount2")
Expand Down Expand Up @@ -271,17 +272,20 @@ func (s *IntegrationTestSuite) TestCLIQueryTxCmd() {
expectErr bool
rawLogContains string
}{
{
"not enough args",
[]string{},
true, "",
},
{
"with invalid hash",
[]string{"somethinginvalid", fmt.Sprintf("--%s=json", tmcli.OutputFlag)},
true,
"",
true, "",
},
{
"with valid and not existing hash",
[]string{"C7E7D3A86A17AB3A321172239F3B61357937AF0F25D9FA4D2F4DCCAD9B0D7747", fmt.Sprintf("--%s=json", tmcli.OutputFlag)},
true,
"",
true, "",
},
{
"happy case (legacy Msg)",
Expand All @@ -293,7 +297,11 @@ func (s *IntegrationTestSuite) TestCLIQueryTxCmd() {
"happy case (service Msg)",
[]string{txRes.TxHash, fmt.Sprintf("--%s=json", tmcli.OutputFlag)},
false,
<<<<<<< HEAD:x/auth/client/cli/cli_test.go
"/cosmos.bank.v1beta1.Msg/Send",
=======
sdk.MsgTypeURL(&banktypes.MsgSend{}),
>>>>>>> 7c1943400 (feat: Query txs by signature and by address+seq (#9750)):x/auth/client/testutil/suite.go
},
}

Expand All @@ -318,6 +326,120 @@ func (s *IntegrationTestSuite) TestCLIQueryTxCmd() {
}
}

func (s *IntegrationTestSuite) TestCLIQueryTxCmdByEvents() {
val := s.network.Validators[0]

account2, err := val.ClientCtx.Keyring.Key("newAccount2")
s.Require().NoError(err)

sendTokens := sdk.NewInt64Coin(s.cfg.BondDenom, 10)

// Send coins.
out, err := s.createBankMsg(
val, account2.GetAddress(),
sdk.NewCoins(sendTokens),
)
s.Require().NoError(err)
var txRes sdk.TxResponse
s.Require().NoError(val.ClientCtx.Codec.UnmarshalJSON(out.Bytes(), &txRes))
s.Require().NoError(s.network.WaitForNextBlock())

// Query the tx by hash to get the inner tx.
out, err = clitestutil.ExecTestCLICmd(val.ClientCtx, authcli.QueryTxCmd(), []string{txRes.TxHash, fmt.Sprintf("--%s=json", tmcli.OutputFlag)})
s.Require().NoError(err)
s.Require().NoError(val.ClientCtx.Codec.UnmarshalJSON(out.Bytes(), &txRes))
protoTx := txRes.GetTx().(*tx.Tx)

testCases := []struct {
name string
args []string
expectErr bool
expectErrStr string
}{
{
"invalid --type",
[]string{
fmt.Sprintf("--type=%s", "foo"),
"bar",
fmt.Sprintf("--%s=json", tmcli.OutputFlag),
},
true, "unknown --type value foo",
},
{
"--type=acc_seq with no addr+seq",
[]string{
"--type=acc_seq",
"",
fmt.Sprintf("--%s=json", tmcli.OutputFlag),
},
true, "`acc_seq` type takes an argument '<addr>/<seq>'",
},
{
"non-existing addr+seq combo",
[]string{
"--type=acc_seq",
"foobar",
fmt.Sprintf("--%s=json", tmcli.OutputFlag),
},
true, "found no txs matching given address and sequence combination",
},
{
"addr+seq happy case",
[]string{
"--type=acc_seq",
fmt.Sprintf("%s/%d", val.Address, protoTx.AuthInfo.SignerInfos[0].Sequence),
fmt.Sprintf("--%s=json", tmcli.OutputFlag),
},
false, "",
},
{
"--type=signature with no signature",
[]string{
"--type=signature",
"",
fmt.Sprintf("--%s=json", tmcli.OutputFlag),
},
true, "argument should be comma-separated signatures",
},
{
"non-existing signatures",
[]string{
"--type=signature",
"foo",
fmt.Sprintf("--%s=json", tmcli.OutputFlag),
},
true, "found no txs matching given signatures",
},
{
"with --signatures happy case",
[]string{
"--type=signature",
base64.StdEncoding.EncodeToString(protoTx.Signatures[0]),
fmt.Sprintf("--%s=json", tmcli.OutputFlag),
},
false, "",
},
}

for _, tc := range testCases {
tc := tc
s.Run(tc.name, func() {
cmd := authcli.QueryTxCmd()
clientCtx := val.ClientCtx

out, err := clitestutil.ExecTestCLICmd(clientCtx, cmd, tc.args)
if tc.expectErr {
s.Require().Error(err)
s.Require().Contains(err.Error(), tc.expectErrStr)
} else {
var result sdk.TxResponse
s.Require().NoError(val.ClientCtx.Codec.UnmarshalJSON(out.Bytes(), &result))
s.Require().NotNil(result.Height)
}
})
}
}

func (s *IntegrationTestSuite) TestCLISendGenerateSignAndBroadcast() {
val1 := s.network.Validators[0]

Expand Down Expand Up @@ -732,8 +854,13 @@ func (s *IntegrationTestSuite) TestCLIMultisign() {

sign1File := testutil.WriteToNewTempFile(s.T(), account1Signature.String())

<<<<<<< HEAD:x/auth/client/cli/cli_test.go
// Sign with account1
account2Signature, err := authtest.TxSignExec(val1.ClientCtx, account2.GetAddress(), multiGeneratedTxFile.Name(), "--multisig", multisigInfo.GetAddress().String())
=======
// Sign with account2
account2Signature, err := TxSignExec(val1.ClientCtx, account2.GetAddress(), multiGeneratedTxFile.Name(), "--multisig", multisigInfo.GetAddress().String())
>>>>>>> 7c1943400 (feat: Query txs by signature and by address+seq (#9750)):x/auth/client/testutil/suite.go
s.Require().NoError(err)

sign2File := testutil.WriteToNewTempFile(s.T(), account2Signature.String())
Expand Down
Loading

0 comments on commit 0030c0b

Please sign in to comment.