package main import ( "encoding/base64" "encoding/hex" "fmt" "os" "path" "github.com/cosmos/cosmos-sdk/store" "github.com/cosmos/cosmos-sdk/baseapp" "github.com/spf13/cobra" "github.com/spf13/viper" abci "github.com/tendermint/tendermint/abci/types" "github.com/tendermint/tendermint/crypto/ed25519" cmn "github.com/tendermint/tendermint/libs/common" dbm "github.com/tendermint/tendermint/libs/db" "github.com/tendermint/tendermint/libs/log" bam "github.com/cosmos/cosmos-sdk/baseapp" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/codec" "github.com/cosmos/cosmos-sdk/x/auth" "github.com/cosmos/cosmos-sdk/x/bank" "github.com/cosmos/cosmos-sdk/x/params" "github.com/cosmos/cosmos-sdk/x/slashing" "github.com/cosmos/cosmos-sdk/x/staking" gaia "github.com/cosmos/cosmos-sdk/cmd/gaia/app" ) func runHackCmd(cmd *cobra.Command, args []string) error { if len(args) != 1 { return fmt.Errorf("Expected 1 arg") } // ".gaiad" dataDir := args[0] dataDir = path.Join(dataDir, "data") // load the app logger := log.NewTMLogger(log.NewSyncWriter(os.Stdout)) db, err := sdk.NewLevelDB("gaia", dataDir) if err != nil { fmt.Println(err) os.Exit(1) } app := NewGaiaApp(logger, db, baseapp.SetPruning(store.NewPruningOptionsFromString(viper.GetString("pruning")))) // print some info id := app.LastCommitID() lastBlockHeight := app.LastBlockHeight() fmt.Println("ID", id) fmt.Println("LastBlockHeight", lastBlockHeight) //---------------------------------------------------- // XXX: start hacking! //---------------------------------------------------- // eg. gaia-6001 testnet bug // We paniced when iterating through the "bypower" keys. // The following powerKey was there, but the corresponding "trouble" validator did not exist. // So here we do a binary search on the past states to find when the powerKey first showed up ... // operator of the validator the bonds, gets jailed, later unbonds, and then later is still found in the bypower store trouble := hexToBytes("D3DC0FF59F7C3B548B7AFA365561B87FD0208AF8") // this is his "bypower" key powerKey := hexToBytes("05303030303030303030303033FFFFFFFFFFFF4C0C0000FFFED3DC0FF59F7C3B548B7AFA365561B87FD0208AF8") topHeight := lastBlockHeight bottomHeight := int64(0) checkHeight := topHeight for { // load the given version of the state err = app.LoadVersion(checkHeight, app.keyMain) if err != nil { fmt.Println(err) os.Exit(1) } ctx := app.NewContext(true, abci.Header{}) // check for the powerkey and the validator from the store store := ctx.KVStore(app.keyStaking) res := store.Get(powerKey) val, _ := app.stakingKeeper.GetValidator(ctx, trouble) fmt.Println("checking height", checkHeight, res, val) if res == nil { bottomHeight = checkHeight } else { topHeight = checkHeight } checkHeight = (topHeight + bottomHeight) / 2 } } func base64ToPub(b64 string) ed25519.PubKeyEd25519 { data, _ := base64.StdEncoding.DecodeString(b64) var pubKey ed25519.PubKeyEd25519 copy(pubKey[:], data) return pubKey } func hexToBytes(h string) []byte { trouble, _ := hex.DecodeString(h) return trouble } //-------------------------------------------------------------------------------- // NOTE: This is all copied from gaia/app/app.go // so we can access internal fields! const ( appName = "GaiaApp" ) // default home directories for expected binaries var ( DefaultCLIHome = os.ExpandEnv("$HOME/.gaiacli") DefaultNodeHome = os.ExpandEnv("$HOME/.gaiad") ) // Extended ABCI application type GaiaApp struct { *bam.BaseApp cdc *codec.Codec // keys to access the substores keyMain *sdk.KVStoreKey keyAccount *sdk.KVStoreKey keyStaking *sdk.KVStoreKey tkeyStaking *sdk.TransientStoreKey keySlashing *sdk.KVStoreKey keyParams *sdk.KVStoreKey tkeyParams *sdk.TransientStoreKey // Manage getting and setting accounts accountKeeper auth.AccountKeeper feeCollectionKeeper auth.FeeCollectionKeeper bankKeeper bank.Keeper stakingKeeper staking.Keeper slashingKeeper slashing.Keeper paramsKeeper params.Keeper } func NewGaiaApp(logger log.Logger, db dbm.DB, baseAppOptions ...func(*bam.BaseApp)) *GaiaApp { cdc := MakeCodec() bApp := bam.NewBaseApp(appName, logger, db, auth.DefaultTxDecoder(cdc), baseAppOptions...) bApp.SetCommitMultiStoreTracer(os.Stdout) // create your application object var app = &GaiaApp{ BaseApp: bApp, cdc: cdc, keyMain: sdk.NewKVStoreKey(bam.MainStoreKey), keyAccount: sdk.NewKVStoreKey(auth.StoreKey), keyStaking: sdk.NewKVStoreKey(staking.StoreKey), tkeyStaking: sdk.NewTransientStoreKey(staking.TStoreKey), keySlashing: sdk.NewKVStoreKey(slashing.StoreKey), keyParams: sdk.NewKVStoreKey(params.StoreKey), tkeyParams: sdk.NewTransientStoreKey(params.TStoreKey), } app.paramsKeeper = params.NewKeeper(app.cdc, app.keyParams, app.tkeyParams) // define the accountKeeper app.accountKeeper = auth.NewAccountKeeper( app.cdc, app.keyAccount, // target store app.paramsKeeper.Subspace(auth.DefaultParamspace), auth.ProtoBaseAccount, // prototype ) // add handlers app.bankKeeper = bank.NewBaseKeeper(app.accountKeeper, app.paramsKeeper.Subspace(bank.DefaultParamspace), bank.DefaultCodespace) app.stakingKeeper = staking.NewKeeper(app.cdc, app.keyStaking, app.tkeyStaking, app.bankKeeper, app.paramsKeeper.Subspace(staking.DefaultParamspace), staking.DefaultCodespace) app.slashingKeeper = slashing.NewKeeper(app.cdc, app.keySlashing, app.stakingKeeper, app.paramsKeeper.Subspace(slashing.DefaultParamspace), slashing.DefaultCodespace) // register message routes app.Router(). AddRoute("bank", bank.NewHandler(app.bankKeeper)). AddRoute(staking.RouterKey, staking.NewHandler(app.stakingKeeper)) // initialize BaseApp app.SetInitChainer(app.initChainer) app.SetBeginBlocker(app.BeginBlocker) app.SetEndBlocker(app.EndBlocker) app.SetAnteHandler(auth.NewAnteHandler(app.accountKeeper, app.feeCollectionKeeper)) app.MountStores(app.keyMain, app.keyAccount, app.keyStaking, app.keySlashing, app.keyParams) app.MountStore(app.tkeyParams, sdk.StoreTypeTransient) err := app.LoadLatestVersion(app.keyMain) if err != nil { cmn.Exit(err.Error()) } app.Seal() return app } // custom tx codec func MakeCodec() *codec.Codec { var cdc = codec.New() bank.RegisterCodec(cdc) staking.RegisterCodec(cdc) slashing.RegisterCodec(cdc) auth.RegisterCodec(cdc) sdk.RegisterCodec(cdc) codec.RegisterCrypto(cdc) cdc.Seal() return cdc } // application updates every end block func (app *GaiaApp) BeginBlocker(ctx sdk.Context, req abci.RequestBeginBlock) abci.ResponseBeginBlock { tags := slashing.BeginBlocker(ctx, req, app.slashingKeeper) return abci.ResponseBeginBlock{ Tags: tags.ToKVPairs(), } } // application updates every end block // nolint: unparam func (app *GaiaApp) EndBlocker(ctx sdk.Context, req abci.RequestEndBlock) abci.ResponseEndBlock { validatorUpdates, tags := staking.EndBlocker(ctx, app.stakingKeeper) return abci.ResponseEndBlock{ ValidatorUpdates: validatorUpdates, Tags: tags, } } // custom logic for gaia initialization func (app *GaiaApp) initChainer(ctx sdk.Context, req abci.RequestInitChain) abci.ResponseInitChain { stateJSON := req.AppStateBytes // TODO is this now the whole genesis file? var genesisState gaia.GenesisState err := app.cdc.UnmarshalJSON(stateJSON, &genesisState) if err != nil { panic(err) // TODO https://github.com/cosmos/cosmos-sdk/issues/468 // return sdk.ErrGenesisParse("").TraceCause(err, "") } // load the accounts for _, gacc := range genesisState.Accounts { acc := gacc.ToAccount() app.accountKeeper.SetAccount(ctx, acc) } // load the initial staking information validators, err := staking.InitGenesis(ctx, app.stakingKeeper, genesisState.StakingData) if err != nil { panic(err) // TODO https://github.com/cosmos/cosmos-sdk/issues/468 // return sdk.ErrGenesisParse("").TraceCause(err, "") } slashing.InitGenesis(ctx, app.slashingKeeper, genesisState.SlashingData, genesisState.StakingData.Validators.ToSDKValidators()) return abci.ResponseInitChain{ Validators: validators, } }