2018-02-23 04:53:49 -08:00
package tx
import (
"errors"
"fmt"
2018-02-28 23:16:54 -08:00
"net/http"
2018-06-15 13:24:04 -07:00
"net/url"
2018-02-23 04:53:49 -08:00
"strings"
2018-03-02 01:24:07 -08:00
"github.com/cosmos/cosmos-sdk/client"
2018-04-02 05:05:23 -07:00
"github.com/cosmos/cosmos-sdk/client/context"
2018-09-13 11:17:32 -07:00
"github.com/cosmos/cosmos-sdk/codec"
2018-06-11 13:09:29 -07:00
sdk "github.com/cosmos/cosmos-sdk/types"
2018-08-06 11:11:30 -07:00
"github.com/spf13/cobra"
"github.com/spf13/viper"
ctypes "github.com/tendermint/tendermint/rpc/core/types"
2018-02-23 04:53:49 -08:00
)
const (
flagTags = "tag"
flagAny = "any"
)
2018-02-28 17:57:38 -08:00
// default client command to search through tagged transactions
2018-09-13 11:17:32 -07:00
func SearchTxCmd ( cdc * codec . Codec ) * cobra . Command {
2018-02-23 04:53:49 -08:00
cmd := & cobra . Command {
Use : "txs" ,
2018-08-10 23:42:57 -07:00
Short : "Search for all transactions that match the given tags." ,
Long : strings . TrimSpace ( `
Search for transactions that match the given tags . By default , transactions must match ALL tags
passed to the -- tags option . To match any transaction , use the -- any option .
For example :
$ gaiacli tendermint txs -- tag test1 , test2
will match any transaction tagged with both test1 , test2 . To match a transaction tagged with either
test1 or test2 , use :
$ gaiacli tendermint txs -- tag test1 , test2 -- any
` ) ,
2018-04-18 21:49:24 -07:00
RunE : func ( cmd * cobra . Command , args [ ] string ) error {
tags := viper . GetStringSlice ( flagTags )
2018-08-06 11:11:30 -07:00
cliCtx := context . NewCLIContext ( ) . WithCodec ( cdc )
txs , err := searchTxs ( cliCtx , cdc , tags )
2018-06-11 13:09:29 -07:00
if err != nil {
return err
}
2018-08-06 11:11:30 -07:00
2018-06-11 13:09:29 -07:00
output , err := cdc . MarshalJSON ( txs )
2018-04-18 21:49:24 -07:00
if err != nil {
return err
}
2018-08-06 11:11:30 -07:00
2018-04-18 21:49:24 -07:00
fmt . Println ( string ( output ) )
return nil
} ,
2018-02-23 04:53:49 -08:00
}
2018-04-18 21:49:24 -07:00
2018-06-13 15:13:51 -07:00
cmd . Flags ( ) . StringP ( client . FlagNode , "n" , "tcp://localhost:26657" , "Node to connect to" )
2018-09-14 11:41:21 -07:00
cmd . Flags ( ) . Bool ( client . FlagTrustNode , false , "Trust connected full node (don't verify proofs for responses)" )
cmd . Flags ( ) . String ( client . FlagChainID , "" , "Chain ID of Tendermint node" )
2018-08-10 23:42:57 -07:00
cmd . Flags ( ) . StringSlice ( flagTags , nil , "Comma-separated list of tags that must match" )
2018-02-23 04:53:49 -08:00
cmd . Flags ( ) . Bool ( flagAny , false , "Return transactions that match ANY tag, rather than ALL" )
return cmd
}
2018-09-13 11:17:32 -07:00
func searchTxs ( cliCtx context . CLIContext , cdc * codec . Codec , tags [ ] string ) ( [ ] Info , error ) {
2018-02-23 04:53:49 -08:00
if len ( tags ) == 0 {
2018-06-13 12:13:22 -07:00
return nil , errors . New ( "must declare at least one tag to search" )
2018-02-23 04:53:49 -08:00
}
2018-08-06 11:11:30 -07:00
2018-02-23 04:53:49 -08:00
// XXX: implement ANY
query := strings . Join ( tags , " AND " )
2018-08-06 11:11:30 -07:00
2018-02-23 04:53:49 -08:00
// get the node
2018-08-06 11:11:30 -07:00
node , err := cliCtx . GetNode ( )
2018-02-28 15:26:39 -08:00
if err != nil {
2018-02-28 23:16:54 -08:00
return nil , err
2018-02-23 04:53:49 -08:00
}
2018-09-14 11:41:21 -07:00
prove := ! cliCtx . TrustNode
2018-08-06 11:11:30 -07:00
2018-05-20 07:35:19 -07:00
// TODO: take these as args
page := 0
perPage := 100
res , err := node . TxSearch ( query , prove , page , perPage )
2018-02-23 04:53:49 -08:00
if err != nil {
2018-02-28 23:16:54 -08:00
return nil , err
2018-02-23 04:53:49 -08:00
}
2018-09-14 11:41:21 -07:00
if prove {
for _ , tx := range res . Txs {
err := ValidateTxResult ( cliCtx , tx )
if err != nil {
return nil , err
}
}
}
2018-09-17 07:13:11 -07:00
info , err := FormatTxResults ( cdc , res . Txs )
2018-02-23 04:53:49 -08:00
if err != nil {
2018-02-28 23:16:54 -08:00
return nil , err
2018-02-23 04:53:49 -08:00
}
2018-06-11 13:09:29 -07:00
return info , nil
2018-02-23 04:53:49 -08:00
}
2018-08-08 03:38:39 -07:00
// parse the indexed txs into an array of Info
2018-09-17 07:13:11 -07:00
func FormatTxResults ( cdc * codec . Codec , res [ ] * ctypes . ResultTx ) ( [ ] Info , error ) {
2018-02-23 04:53:49 -08:00
var err error
2018-08-08 03:38:39 -07:00
out := make ( [ ] Info , len ( res ) )
2018-02-23 04:53:49 -08:00
for i := range res {
2018-02-28 17:57:38 -08:00
out [ i ] , err = formatTxResult ( cdc , res [ i ] )
2018-02-23 04:53:49 -08:00
if err != nil {
return nil , err
}
}
return out , nil
}
2018-02-28 23:16:54 -08:00
2018-04-18 21:49:24 -07:00
/////////////////////////////////////////
2018-02-28 23:16:54 -08:00
// REST
2018-04-18 21:49:24 -07:00
// Search Tx REST Handler
2018-09-13 11:17:32 -07:00
func SearchTxRequestHandlerFn ( cliCtx context . CLIContext , cdc * codec . Codec ) http . HandlerFunc {
2018-02-28 23:16:54 -08:00
return func ( w http . ResponseWriter , r * http . Request ) {
tag := r . FormValue ( "tag" )
2018-03-14 05:01:55 -07:00
if tag == "" {
w . WriteHeader ( 400 )
2018-06-11 13:09:29 -07:00
w . Write ( [ ] byte ( "You need to provide at least a tag as a key=value pair to search for. Postfix the key with _bech32 to search bech32-encoded addresses or public keys" ) )
2018-03-14 05:01:55 -07:00
return
}
2018-08-06 11:11:30 -07:00
2018-06-11 13:09:29 -07:00
keyValue := strings . Split ( tag , "=" )
key := keyValue [ 0 ]
2018-08-06 11:11:30 -07:00
2018-06-15 13:24:04 -07:00
value , err := url . QueryUnescape ( keyValue [ 1 ] )
if err != nil {
w . WriteHeader ( 400 )
w . Write ( [ ] byte ( "Could not decode address: " + err . Error ( ) ) )
return
}
2018-08-06 11:11:30 -07:00
2018-06-11 13:09:29 -07:00
if strings . HasSuffix ( key , "_bech32" ) {
bech32address := strings . Trim ( value , "'" )
prefix := strings . Split ( bech32address , "1" ) [ 0 ]
bz , err := sdk . GetFromBech32 ( bech32address , prefix )
if err != nil {
w . WriteHeader ( 400 )
w . Write ( [ ] byte ( err . Error ( ) ) )
return
}
2018-07-06 00:06:53 -07:00
tag = strings . TrimRight ( key , "_bech32" ) + "='" + sdk . AccAddress ( bz ) . String ( ) + "'"
2018-06-11 13:09:29 -07:00
}
2018-03-14 05:01:55 -07:00
2018-08-06 11:11:30 -07:00
txs , err := searchTxs ( cliCtx , cdc , [ ] string { tag } )
2018-02-28 23:16:54 -08:00
if err != nil {
w . WriteHeader ( 500 )
w . Write ( [ ] byte ( err . Error ( ) ) )
return
}
2018-06-11 13:09:29 -07:00
if len ( txs ) == 0 {
w . Write ( [ ] byte ( "[]" ) )
return
}
output , err := cdc . MarshalJSON ( txs )
if err != nil {
w . WriteHeader ( 500 )
w . Write ( [ ] byte ( err . Error ( ) ) )
return
}
2018-02-28 23:16:54 -08:00
w . Write ( output )
}
}