Skip to content

Instantly share code, notes, and snippets.

@jackzampolin
Created May 30, 2019 01:54
Show Gist options
  • Save jackzampolin/d8ddcbac9b566c6687f1b8ccf4f75af4 to your computer and use it in GitHub Desktop.
Save jackzampolin/d8ddcbac9b566c6687f1b8ccf4f75af4 to your computer and use it in GitHub Desktop.
transaction-parser
package main
import (
"fmt"
"os"
"path"
"sort"
"github.com/cosmos/cosmos-sdk/client/tx"
"github.com/cosmos/cosmos-sdk/client"
"github.com/cosmos/cosmos-sdk/client/context"
"github.com/cosmos/cosmos-sdk/cmd/gaia/app"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/x/mint"
"github.com/cosmos/cosmos-sdk/x/staking"
"github.com/cosmos/cosmos-sdk/x/staking/types"
"github.com/spf13/cobra"
"github.com/spf13/viper"
"github.com/tendermint/tendermint/libs/cli"
gv "github.com/cosmos/cosmos-sdk/x/gov"
sl "github.com/cosmos/cosmos-sdk/x/slashing"
st "github.com/cosmos/cosmos-sdk/x/staking"
crisisclient "github.com/cosmos/cosmos-sdk/x/crisis/client"
distcmd "github.com/cosmos/cosmos-sdk/x/distribution"
distClient "github.com/cosmos/cosmos-sdk/x/distribution/client"
govClient "github.com/cosmos/cosmos-sdk/x/gov/client"
mintclient "github.com/cosmos/cosmos-sdk/x/mint/client"
slashingclient "github.com/cosmos/cosmos-sdk/x/slashing/client"
stakingclient "github.com/cosmos/cosmos-sdk/x/staking/client"
)
func main() {
// Configure cobra to sort commands
cobra.EnableCommandSorting = false
// Instantiate the codec for the command line application
cdc := app.MakeCodec()
// Read in the configuration file for the sdk
config := sdk.GetConfig()
config.SetBech32PrefixForAccount(sdk.Bech32PrefixAccAddr, sdk.Bech32PrefixAccPub)
config.SetBech32PrefixForValidator(sdk.Bech32PrefixValAddr, sdk.Bech32PrefixValPub)
config.SetBech32PrefixForConsensusNode(sdk.Bech32PrefixConsAddr, sdk.Bech32PrefixConsPub)
config.Seal()
_ = []sdk.ModuleClients{
govClient.NewModuleClient(gv.StoreKey, cdc),
distClient.NewModuleClient(distcmd.StoreKey, cdc),
stakingclient.NewModuleClient(st.StoreKey, cdc),
mintclient.NewModuleClient(mint.StoreKey, cdc),
slashingclient.NewModuleClient(sl.StoreKey, cdc),
crisisclient.NewModuleClient(sl.StoreKey, cdc),
}
rootCmd := &cobra.Command{
Use: "exploitDetector",
Short: "See if the redelegation attack on the Cosmos Hub is being exploited",
RunE: func(cmd *cobra.Command, args []string) error {
cliCtx := context.NewCLIContext().WithCodec(cdc)
cliCtx.TrustNode = true
resKVs, err := cliCtx.QuerySubspace(staking.ValidatorsKey, st.StoreKey)
if err != nil {
return err
}
var validators staking.Validators
for _, kv := range resKVs {
val := types.MustUnmarshalValidator(cdc, kv.Value)
if val.Status.String() == sdk.BondStatusUnbonded {
validators = append(validators, types.MustUnmarshalValidator(cdc, kv.Value))
}
}
var responses []sdk.TxResponse
for _, validator := range validators {
var tags []string
tag1 := fmt.Sprintf("%s='%s'", "action", "begin_redelegate")
tag2 := fmt.Sprintf("%s='%s'", "destination-validator", validator.GetOperator().String())
tags = append(tags, tag1)
tags = append(tags, tag2)
resp, err := tx.SearchTxs(cliCtx, cdc, tags, 1, 100000)
if err != nil {
panic(err)
}
responses = append(responses, resp...)
}
if !viper.GetBool("txids") {
fmt.Printf("Supicious txs: %d\n", len(responses))
return nil
}
sort.SliceStable(responses, func(i, j int) bool {
return responses[j].Timestamp > responses[i].Timestamp
})
for i, tx := range responses {
prt := newTxOut(tx, i)
fmt.Println(prt.String())
}
return nil
},
}
// Add --chain-id to persistent flags and mark it required
rootCmd.PersistentFlags().String(client.FlagChainID, "", "Chain ID of tendermint node")
rootCmd.PersistentFlags().Bool("txids", false, "print txids")
rootCmd.PersistentPreRunE = func(_ *cobra.Command, _ []string) error {
return initConfig(rootCmd)
}
// Add flags and prefix all env exposed with GA
executor := cli.PrepareMainCmd(rootCmd, "GA", app.DefaultCLIHome)
err := executor.Execute()
if err != nil {
fmt.Printf("Failed executing CLI command: %s, exiting...\n", err)
os.Exit(1)
}
}
func newTxOut(tx sdk.TxResponse, index int) (out txOut) {
out.Index = index
out.Time = tx.Timestamp
out.Hash = tx.TxHash
for _, msg := range tx.Tx.GetMsgs() {
switch msg.Type() {
case "begin_unbonding":
m := msg.(staking.MsgUndelegate)
out.Msgs = append(out.Msgs, msgOut{"begin_unbonding", m.Amount.String(), m.DelegatorAddress.String()})
case "begin_redelegate":
m := msg.(staking.MsgBeginRedelegate)
out.Msgs = append(out.Msgs, msgOut{"begin_redelegate", m.Amount.String(), m.DelegatorAddress.String()})
}
}
return out
}
type txOut struct {
Index int `json:"index"`
Time string `json:"time"`
Hash string `json:"hash"`
Msgs []msgOut `json:"msgs"`
}
type msgOut struct {
MsgType string `json:"msg_type"`
Amount string `json:"amount"`
DelAddr string `json:"del_addr"`
}
func (to txOut) String() (out string) {
out += fmt.Sprintf("%d %s %s %d\n", to.Index, to.Time, to.Hash, len(to.Msgs))
for _, msg := range to.Msgs {
out += fmt.Sprintf(" %s %s %s\n", msg.MsgType, msg.DelAddr, msg.Amount)
}
return
}
func initConfig(cmd *cobra.Command) error {
home, err := cmd.PersistentFlags().GetString(cli.HomeFlag)
if err != nil {
return err
}
cfgFile := path.Join(home, "config", "config.toml")
if _, err := os.Stat(cfgFile); err == nil {
viper.SetConfigFile(cfgFile)
if err := viper.ReadInConfig(); err != nil {
return err
}
}
if err := viper.BindPFlag(client.FlagChainID, cmd.PersistentFlags().Lookup(client.FlagChainID)); err != nil {
return err
}
if err := viper.BindPFlag(cli.EncodingFlag, cmd.PersistentFlags().Lookup(cli.EncodingFlag)); err != nil {
return err
}
return viper.BindPFlag(cli.OutputFlag, cmd.PersistentFlags().Lookup(cli.OutputFlag))
}
module github.com/{username}/transaction
go 1.12
require (
github.com/cosmos/cosmos-sdk v0.34.5
github.com/spf13/cobra v0.0.4
github.com/spf13/viper v1.4.0
github.com/tendermint/tendermint v0.31.5
)
replace golang.org/x/crypto => github.com/tendermint/crypto v0.0.0-20180820045704-3764759f34a5
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment