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"
"github.com/spf13/cobra"
"github.com/spf13/viper"
ctypes "github.com/tendermint/tendermint/rpc/core/types"
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-06-11 13:09:29 -07:00
sdk "github.com/cosmos/cosmos-sdk/types"
2018-03-02 01:24:07 -08:00
"github.com/cosmos/cosmos-sdk/wire"
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-04-18 21:49:24 -07:00
func SearchTxCmd ( cdc * wire . Codec ) * cobra . Command {
2018-02-23 04:53:49 -08:00
cmd := & cobra . Command {
Use : "txs" ,
Short : "Search for all transactions that match the given tags" ,
2018-04-18 21:49:24 -07:00
RunE : func ( cmd * cobra . Command , args [ ] string ) error {
tags := viper . GetStringSlice ( flagTags )
2018-06-11 13:09:29 -07:00
txs , err := searchTxs ( context . NewCoreContextFromViper ( ) , cdc , tags )
if err != nil {
return err
}
output , err := cdc . MarshalJSON ( txs )
2018-04-18 21:49:24 -07:00
if err != nil {
return err
}
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-04-18 21:49:24 -07:00
2018-02-28 17:57:38 -08:00
// TODO: change this to false once proofs built in
2018-02-23 04:53:49 -08:00
cmd . Flags ( ) . Bool ( client . FlagTrustNode , true , "Don't verify proofs for responses" )
cmd . Flags ( ) . StringSlice ( flagTags , nil , "Tags that must match (may provide multiple)" )
cmd . Flags ( ) . Bool ( flagAny , false , "Return transactions that match ANY tag, rather than ALL" )
return cmd
}
2018-06-11 13:09:29 -07:00
func searchTxs ( ctx context . CoreContext , cdc * wire . Codec , tags [ ] string ) ( [ ] txInfo , 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
}
// XXX: implement ANY
query := strings . Join ( tags , " AND " )
// get the node
2018-04-25 07:49:31 -07:00
node , err := ctx . 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
}
prove := ! viper . GetBool ( client . FlagTrustNode )
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-05-20 07:35:19 -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-02-28 17:57:38 -08:00
func formatTxResults ( cdc * wire . Codec , res [ ] * ctypes . ResultTx ) ( [ ] txInfo , error ) {
2018-02-23 04:53:49 -08:00
var err error
out := make ( [ ] txInfo , len ( res ) )
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-04-25 13:32:22 -07:00
func SearchTxRequestHandlerFn ( ctx context . CoreContext , cdc * wire . 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-06-11 13:09:29 -07:00
keyValue := strings . Split ( tag , "=" )
key := keyValue [ 0 ]
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-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
}
tag = strings . TrimRight ( key , "_bech32" ) + "='" + sdk . Address ( bz ) . String ( ) + "'"
}
2018-03-14 05:01:55 -07:00
2018-06-11 13:09:29 -07:00
txs , err := searchTxs ( ctx , 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 )
}
}