Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Query txs by signature and by address+seq #9750

Merged
merged 25 commits into from
Jul 27, 2021
Merged
Show file tree
Hide file tree
Changes from 11 commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
dafbe1f
WIP for auth query
amaury1093 Jul 21, 2021
d801fbd
Make query by addr and seq work
amaury1093 Jul 22, 2021
3628100
Add tests for sigs
amaury1093 Jul 22, 2021
51f4cf2
Merge branch 'master' into am/9741-tx-query
amaury1093 Jul 22, 2021
ea3dc2e
Make query by sig work
amaury1093 Jul 22, 2021
96d357c
Fix lint
amaury1093 Jul 22, 2021
069e823
Improve err message
amaury1093 Jul 22, 2021
f319d4a
Move HasExtensionOptionsTx to authtx?
amaury1093 Jul 22, 2021
eb06533
Switch to bez's CLI UX
amaury1093 Jul 22, 2021
b7218b1
Fix cycle dep
amaury1093 Jul 22, 2021
56c331a
Cleanups
amaury1093 Jul 22, 2021
620b3ea
Update CHANGELOG.md
amaury1093 Jul 22, 2021
76f6fdd
Emit all nested sigs
amaury1093 Jul 23, 2021
acefab9
Merge branch 'am/9741-tx-query' of ssh://github.com/cosmos/cosmos-sdk…
amaury1093 Jul 23, 2021
209ffb3
Merge branch 'master' into am/9741-tx-query
amaury1093 Jul 23, 2021
f6e61e1
Index by addr++seq
amaury1093 Jul 23, 2021
ef80915
Merge branch 'master' into am/9741-tx-query
amaury1093 Jul 26, 2021
1d0c8c5
Use '/' delimiter
amaury1093 Jul 26, 2021
1eb8abd
Merge branch 'master' into am/9741-tx-query
alexanderbez Jul 26, 2021
fade9f7
Update x/auth/client/cli/query.go
amaury1093 Jul 27, 2021
10f846e
Update x/auth/client/cli/query.go
amaury1093 Jul 27, 2021
2e9a636
Update x/auth/client/cli/query.go
amaury1093 Jul 27, 2021
4e19f3d
Update x/auth/client/cli/query.go
amaury1093 Jul 27, 2021
91314fe
Address review comments
amaury1093 Jul 27, 2021
78b7526
Merge branch 'master' into am/9741-tx-query
amaury1093 Jul 27, 2021
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ Ref: https://keepachangelog.com/en/1.0.0/

* [\#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 by signature (`tx.signature='<base64_sig>'`) or by address and sequence combo (`tx.sequence='<seq>' AND message.sender='<addr>'`).
amaury1093 marked this conversation as resolved.
Show resolved Hide resolved

### API Breaking Changes

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"

AttributeKeySequence = "sequence"
amaury1093 marked this conversation as resolved.
Show resolved Hide resolved
AttributeKeySignature = "signature"

EventTypeMessage = "message"

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

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

"github.com/cosmos/cosmos-sdk/crypto/keys/ed25519"
kmultisig "github.com/cosmos/cosmos-sdk/crypto/keys/multisig"
Expand Down Expand Up @@ -94,6 +96,29 @@ 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>'`),
// - address and sequence (via `message.sender=<addr> AND tx.sequence='<seq>' `).
sigs, err := sigTx.GetSignaturesV2()
if err != nil {
return ctx, err
}

var events sdk.Events
for _, sig := range sigs {
sigBz, err := signatureDataToBz(sig.Data)
amaury1093 marked this conversation as resolved.
Show resolved Hide resolved
if err != nil {
return ctx, err
}

events = append(events, sdk.NewEvent(sdk.EventTypeTx,
sdk.NewAttribute(sdk.AttributeKeySequence, strconv.FormatUint(sig.Sequence, 10)),
amaury1093 marked this conversation as resolved.
Show resolved Hide resolved
sdk.NewAttribute(sdk.AttributeKeySignature, base64.StdEncoding.EncodeToString(sigBz)),
))
}
ctx.EventManager().EmitEvents(events)

return next(ctx, tx, simulate)
}

Expand Down Expand Up @@ -447,3 +472,41 @@ func CountSubKeys(pub cryptotypes.PubKey) int {

return numKeys
}

// signatureDataToBz converts a SignatureData into raw bytes signature. It is
// the same function as in auth/tx/sigs.go, but copied here because of import
alexanderbez marked this conversation as resolved.
Show resolved Hide resolved
// cycles.
// TODO: https://github.com/cosmos/cosmos-sdk/issues/9753
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 data.Signature, nil
case *signing.MultiSignatureData:
n := len(data.Signatures)
sigs := make([][]byte, n)
var err error

for i, d := range data.Signatures {
sigs[i], err = signatureDataToBz(d)
if err != nil {
return nil, err
}
}

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

return sig, nil
default:
return nil, fmt.Errorf("unexpected signature data type %T", data)
amaury1093 marked this conversation as resolved.
Show resolved Hide resolved
}
}
107 changes: 95 additions & 12 deletions x/auth/client/cli/query.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,13 @@ import (
)

const (
flagEvents = "events"
flagEvents = "events"
flagType = "type"
flagAddress = "address"

typeHash = "hash"
typeSeq = "sequence"
typeSig = "signature"

eventFormat = "{eventType}.{eventAttribute}={value}"
)
Expand Down Expand Up @@ -210,28 +216,105 @@ $ %s query txs --%s 'message.sender=cosmos1...&message.action=withdraw_delegator
// QueryTxCmd implements the default command for a tx query.
func QueryTxCmd() *cobra.Command {
cmd := &cobra.Command{
Use: "tx [hash]",
Short: "Query for a transaction by hash in a committed block",
Args: cobra.ExactArgs(1),
Use: "tx [hash|sequence|signature] --type=[hash|sequence|signature]",
Short: "Query for a transaction by hash, sequence or signature in a committed block",
Long: strings.TrimSpace(fmt.Sprintf(`
Example:
$ %s query tx <hash>
$ %s query tx --%s=%s <sequence> --%s=<addr>
$ %s query tx --%s=%s <sig1_base64,sig2_base64...>
`, version.AppName, version.AppName, flagType, typeSeq, flagAddress, version.AppName, flagType, typeSig)),
Args: cobra.ExactArgs(1),
RunE: func(cmd *cobra.Command, args []string) error {
clientCtx, err := client.GetClientQueryContext(cmd)
if err != nil {
return err
}
output, err := authtx.QueryTx(clientCtx, args[0])
if err != nil {
return err
}

if output.Empty() {
return fmt.Errorf("no transaction found with hash %s", args[0])
}
typ, _ := cmd.Flags().GetString(flagType)

return clientCtx.PrintProto(output)
switch typ {
case typeHash:
{
if args[0] == "" {
return fmt.Errorf("argument should be a tx hash")
}

// If hash is given, then query the tx by hash.
output, err := authtx.QueryTx(clientCtx, args[0])
if err != nil {
return err
}

if output.Empty() {
return fmt.Errorf("no transaction found with hash %s", args[0])
}

return clientCtx.PrintProto(output)
}
case typeSig:
{
if args[0] == "" {
amaury1093 marked this conversation as resolved.
Show resolved Hide resolved
return fmt.Errorf("argument should be comma-separated signatures")
}

sigParts := strings.Split(args[0], ",")
tmEvents := make([]string, len(sigParts))
for i, sig := range sigParts {
tmEvents[i] = fmt.Sprintf("%s.%s='%s'", sdk.EventTypeTx, sdk.AttributeKeySignature, sig)
}
amaury1093 marked this conversation as resolved.
Show resolved Hide resolved

txs, err := authtx.QueryTxsByEvents(clientCtx, tmEvents, query.DefaultPage, query.DefaultLimit, "")
if err != nil {
return err
}
if len(txs.Txs) == 0 {
return fmt.Errorf("found no txs matching given signatures")
}
if len(txs.Txs) > 1 {
// This case means there's a bug somewhere else in the code. Should not happen.
return fmt.Errorf("found %d txs matching given signatures", len(txs.Txs))
amaury1093 marked this conversation as resolved.
Show resolved Hide resolved
}

return clientCtx.PrintProto(txs.Txs[0])
}
case typeSeq:
{
if args[0] == "" {
return fmt.Errorf("argument should be a sequence")
}
addr, _ := cmd.Flags().GetString(flagAddress)
if addr == "" {
return fmt.Errorf("--%s is required when using --%s=%s", flagAddress, flagType, typeSeq)
}

tmEvents := []string{
fmt.Sprintf("%s.%s='%s'", sdk.EventTypeMessage, sdk.AttributeKeySender, addr),
fmt.Sprintf("%s.%s='%s'", sdk.EventTypeTx, sdk.AttributeKeySequence, args[0]),
}
txs, err := authtx.QueryTxsByEvents(clientCtx, tmEvents, query.DefaultPage, query.DefaultLimit, "")
if err != nil {
return err
}
if len(txs.Txs) == 0 {
return fmt.Errorf("found no txs matching given address and sequence combination")
}
if len(txs.Txs) > 1 {
// This case means there's a bug somewhere else in the code. Should not happen.
return fmt.Errorf("found %d txs matching given address and sequence combination", len(txs.Txs))
}

return clientCtx.PrintProto(txs.Txs[0])
}
default:
return fmt.Errorf("unknown --%s value %s", flagType, typ)
}
},
}

flags.AddQueryFlagsToCmd(cmd)
cmd.Flags().String(flagAddress, "", fmt.Sprintf("Query the tx by signer and sequence, required if --%s=%s is set", flagType, typeSeq))
cmd.Flags().String(flagType, typeHash, fmt.Sprintf("The type to be used when querying tx, can be one of \"%s\", \"%s\", \"%s\"", typeHash, typeSeq, typeSig))

return cmd
}
Loading