From 1da1115a246b89f7365fdef8d526410b79400e3b Mon Sep 17 00:00:00 2001 From: Federico Kunze <31522760+fedekunze@users.noreply.github.com> Date: Wed, 8 Aug 2018 12:38:39 +0200 Subject: [PATCH] Merge PR #1880: Staking Gaia-lite (ex LCD) refactor --- Gopkg.lock | 47 ++- PENDING.md | 2 + client/lcd/lcd_test.go | 121 ++++++-- client/lcd/test_helpers.go | 3 +- client/tx/query.go | 10 +- client/tx/search.go | 9 +- client/tx/sign.go | 2 +- cmd/cosmos-sdk-cli/cmd/init.go | 3 +- docs/clients/lcd-rest-api.yaml | 392 ++++++++++++++++--------- docs/light/api.md | 216 ++++++++++++++ docs/light/readme.md | 4 +- docs/light/specification.md | 40 ++- x/stake/client/rest/query.go | 504 +++++++++++++++++++++++++-------- x/stake/client/rest/tx.go | 15 +- x/stake/client/rest/utils.go | 228 +++++++++++++++ x/stake/keeper/key.go | 49 ++-- 16 files changed, 1298 insertions(+), 347 deletions(-) create mode 100644 x/stake/client/rest/utils.go diff --git a/Gopkg.lock b/Gopkg.lock index 018836215..c69af1c2e 100644 --- a/Gopkg.lock +++ b/Gopkg.lock @@ -34,11 +34,11 @@ [[projects]] branch = "master" - digest = "1:2c00f064ba355903866cbfbf3f7f4c0fe64af6638cc7d1b8bdcf3181bc67f1d8" + digest = "1:6aabc1566d6351115d561d038da82a4c19b46c3b6e17f4a0a2fa60260663dc79" name = "github.com/btcsuite/btcd" packages = ["btcec"] pruneopts = "UT" - revision = "9a2f9524024889e129a5422aca2cff73cb3eabf6" + revision = "cf05f92c3f815bbd5091ed6c73eff51f7b1945e8" [[projects]] digest = "1:386de157f7d19259a7f9c81f26ce011223ce0f090353c1152ffdf730d7d10ac2" @@ -71,7 +71,7 @@ version = "v1.4.7" [[projects]] - digest = "1:fdf5169073fb0ad6dc12a70c249145e30f4058647bea25f0abd48b6d9f228a11" + digest = "1:fa30c0652956e159cdb97dcb2ef8b8db63ed668c02a5c3a40961c8f0641252fe" name = "github.com/go-kit/kit" packages = [ "log", @@ -103,7 +103,7 @@ version = "v1.7.0" [[projects]] - digest = "1:35621fe20f140f05a0c4ef662c26c0ab4ee50bca78aa30fe87d33120bd28165e" + digest = "1:212285efb97b9ec2e20550d81f0446cb7897e57cbdfd7301b1363ab113d8be45" name = "github.com/gogo/protobuf" packages = [ "gogoproto", @@ -118,7 +118,7 @@ version = "v1.1.1" [[projects]] - digest = "1:17fe264ee908afc795734e8c4e63db2accabaf57326dbf21763a7d6b86096260" + digest = "1:cb22af0ed7c72d495d8be1106233ee553898950f15fd3f5404406d44c2e86888" name = "github.com/golang/protobuf" packages = [ "proto", @@ -165,7 +165,7 @@ [[projects]] branch = "master" - digest = "1:a361611b8c8c75a1091f00027767f7779b29cb37c456a71b8f2604c88057ab40" + digest = "1:ac64f01acc5eeea9dde40e326de6b6471e501392ec06524c3b51033aa50789bc" name = "github.com/hashicorp/hcl" packages = [ ".", @@ -263,7 +263,7 @@ version = "v1.0.0" [[projects]] - digest = "1:c1a04665f9613e082e1209cf288bf64f4068dcd6c87a64bf1c4ff006ad422ba0" + digest = "1:98225904b7abff96c052b669b25788f18225a36673fba022fb93514bb9a2a64e" name = "github.com/prometheus/client_golang" packages = [ "prometheus", @@ -274,7 +274,7 @@ [[projects]] branch = "master" - digest = "1:2d5cd61daa5565187e1d96bae64dbbc6080dacf741448e9629c64fd93203b0d4" + digest = "1:0f37e09b3e92aaeda5991581311f8dbf38944b36a3edec61cc2d1991f527554a" name = "github.com/prometheus/client_model" packages = ["go"] pruneopts = "UT" @@ -282,7 +282,7 @@ [[projects]] branch = "master" - digest = "1:63b68062b8968092eb86bedc4e68894bd096ea6b24920faca8b9dcf451f54bb5" + digest = "1:dad2e5a2153ee7a6c9ab8fc13673a16ee4fb64434a7da980965a3741b0c981a3" name = "github.com/prometheus/common" packages = [ "expfmt", @@ -294,7 +294,7 @@ [[projects]] branch = "master" - digest = "1:8c49953a1414305f2ff5465147ee576dd705487c35b15918fcd4efdc0cb7a290" + digest = "1:a37c98f4b7a66bb5c539c0539f0915a74ef1c8e0b3b6f45735289d94cae92bfd" name = "github.com/prometheus/procfs" packages = [ ".", @@ -313,7 +313,7 @@ revision = "e2704e165165ec55d062f5919b4b29494e9fa790" [[projects]] - digest = "1:bd1ae00087d17c5a748660b8e89e1043e1e5479d0fea743352cda2f8dd8c4f84" + digest = "1:37ace7f35375adec11634126944bdc45a673415e2fcc07382d03b75ec76ea94c" name = "github.com/spf13/afero" packages = [ ".", @@ -332,7 +332,7 @@ version = "v1.2.0" [[projects]] - digest = "1:7ffc0983035bc7e297da3688d9fe19d60a420e9c38bef23f845c53788ed6a05e" + digest = "1:627ab2f549a6a55c44f46fa24a4307f4d0da81bfc7934ed0473bf38b24051d26" name = "github.com/spf13/cobra" packages = ["."] pruneopts = "UT" @@ -364,7 +364,7 @@ version = "v1.0.0" [[projects]] - digest = "1:7e8d267900c7fa7f35129a2a37596e38ed0f11ca746d6d9ba727980ee138f9f6" + digest = "1:73697231b93fb74a73ebd8384b68b9a60c57ea6b13c56d2425414566a72c8e6d" name = "github.com/stretchr/testify" packages = [ "assert", @@ -376,7 +376,7 @@ [[projects]] branch = "master" - digest = "1:b3cfb8d82b1601a846417c3f31c03a7961862cb2c98dcf0959c473843e6d9a2b" + digest = "1:922191411ad8f61bcd8018ac127589bb489712c1d1a0ab2497aca4b16de417d2" name = "github.com/syndtr/goleveldb" packages = [ "leveldb", @@ -397,7 +397,7 @@ [[projects]] branch = "master" - digest = "1:087aaa7920e5d0bf79586feb57ce01c35c830396ab4392798112e8aae8c47722" + digest = "1:203b409c21115233a576f99e8f13d8e07ad82b25500491f7e1cca12588fb3232" name = "github.com/tendermint/ed25519" packages = [ ".", @@ -424,7 +424,7 @@ version = "v0.9.2" [[projects]] - digest = "1:e10e95fd9f0a3a31686c9696f8995f6c04b0bc1b4ed0562a4f53cddc0b89d059" + digest = "1:049c779b867a182cea567c65d7c81e3b9e4e4a7eece4c35a19639f75d2aa7da9" name = "github.com/tendermint/tendermint" packages = [ "abci/client", @@ -499,7 +499,7 @@ [[projects]] branch = "master" - digest = "1:65a21a9e051d54eb6a3f70c659a765f706a998d9287c302269f4ed8054b2a852" + digest = "1:e8206c1653e050116ec8c9a823a86413fc9f9ee3c2f3ae977c96d6a1747f7325" name = "golang.org/x/crypto" packages = [ "blowfish", @@ -515,10 +515,10 @@ "salsa20/salsa", ] pruneopts = "UT" - revision = "c126467f60eb25f8f27e5a981f32a87e3965053f" + revision = "f027049dab0ad238e394a753dba2d14753473a04" [[projects]] - digest = "1:d36f55a999540d29b6ea3c2ea29d71c76b1d9853fdcd3e5c5cb4836f2ba118f1" + digest = "1:04dda8391c3e2397daf254ac68003f30141c069b228d06baec8324a5f81dc1e9" name = "golang.org/x/net" packages = [ "context", @@ -535,14 +535,14 @@ [[projects]] branch = "master" - digest = "1:8466957fb2af510f68b13aec64ccad13e9e756dc1da3ea28c422f8ac410e56f0" + digest = "1:4d7a8265af700258feaff86722049eb5b787240d66dfaf45ff4962f09de6e0be" name = "golang.org/x/sys" packages = ["unix"] pruneopts = "UT" - revision = "bd9dbc187b6e1dacfdd2722a87e83093c2d7bd6e" + revision = "acbc56fc7007d2a01796d5bde54f39e3b3e95945" [[projects]] - digest = "1:a2ab62866c75542dd18d2b069fec854577a20211d7c0ea6ae746072a1dccdd18" + digest = "1:7509ba4347d1f8de6ae9be8818b0cd1abc3deeffe28aeaf4be6d4b6b5178d9ca" name = "golang.org/x/text" packages = [ "collate", @@ -573,7 +573,7 @@ revision = "daca94659cb50e9f37c1b834680f2e46358f10b0" [[projects]] - digest = "1:2dab32a43451e320e49608ff4542fdfc653c95dcc35d0065ec9c6c3dd540ed74" + digest = "1:4515e3030c440845b046354fd5d57671238428b820deebce2e9dabb5cd3c51ac" name = "google.golang.org/grpc" packages = [ ".", @@ -617,7 +617,6 @@ [solve-meta] analyzer-name = "dep" analyzer-version = 1 - inputs-digest = "71e86b1f1e9ec71901c20d8532dc8477df66eff37a407322379f6a8b03e5d91b" input-imports = [ "github.com/bartekn/go-bip39", "github.com/bgentry/speakeasy", diff --git a/PENDING.md b/PENDING.md index 648d02eb3..dcfa56654 100644 --- a/PENDING.md +++ b/PENDING.md @@ -1,6 +1,8 @@ ## PENDING BREAKING CHANGES +* API + - \#1880 [x/stake] changed the endpoints to be more REST-ful * Update to tendermint v0.22.5. This involves changing all of the cryptography imports. [Ref](https://github.com/tendermint/tendermint/pull/1966) * [baseapp] Msgs are no longer run on CheckTx, removed `ctx.IsCheckTx()` * [x/gov] CLI flag changed from `proposalID` to `proposal-id` diff --git a/client/lcd/lcd_test.go b/client/lcd/lcd_test.go index 8f803ced9..b8909e226 100644 --- a/client/lcd/lcd_test.go +++ b/client/lcd/lcd_test.go @@ -7,13 +7,13 @@ import ( "regexp" "testing" + "github.com/cosmos/cosmos-sdk/client/tx" + "github.com/spf13/viper" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" cryptoKeys "github.com/cosmos/cosmos-sdk/crypto/keys" - abci "github.com/tendermint/tendermint/abci/types" - "github.com/tendermint/tendermint/libs/common" p2p "github.com/tendermint/tendermint/p2p" ctypes "github.com/tendermint/tendermint/rpc/core/types" @@ -27,6 +27,7 @@ import ( "github.com/cosmos/cosmos-sdk/x/gov" "github.com/cosmos/cosmos-sdk/x/slashing" "github.com/cosmos/cosmos-sdk/x/stake" + "github.com/cosmos/cosmos-sdk/x/stake/client/rest" ) func init() { @@ -316,13 +317,7 @@ func TestTxs(t *testing.T) { res, body = Request(t, port, "GET", fmt.Sprintf("/txs/%s", resultTx.Hash), nil) require.Equal(t, http.StatusOK, res.StatusCode, body) - type txInfo struct { - Hash common.HexBytes `json:"hash"` - Height int64 `json:"height"` - Tx sdk.Tx `json:"tx"` - Result abci.ResponseDeliverTx `json:"result"` - } - var indexedTxs []txInfo + var indexedTxs []tx.Info // check if tx is queryable res, body = Request(t, port, "GET", fmt.Sprintf("/txs?tag=tx.hash='%s'", resultTx.Hash), nil) @@ -374,6 +369,19 @@ func TestValidatorsQuery(t *testing.T) { require.True(t, foundVal, "pkBech %v, owner %v", pkBech, validators[0].Owner) } +func TestValidatorQuery(t *testing.T) { + cleanup, pks, port := InitializeTestLCD(t, 1, []sdk.AccAddress{}) + defer cleanup() + require.Equal(t, 1, len(pks)) + + validator1Owner := sdk.AccAddress(pks[0].Address()) + + validator := getValidator(t, port, validator1Owner) + bech32ValAddress, err := sdk.Bech32ifyValPub(pks[0]) + require.NoError(t, err) + assert.Equal(t, validator.PubKey, bech32ValAddress, "The returned validator does not hold the correct data") +} + func TestBonding(t *testing.T) { name, password, denom := "test", "1234567890", "steak" addr, seed := CreateAddr(t, "test", password, GetKeyBase(t)) @@ -398,7 +406,7 @@ func TestBonding(t *testing.T) { // query validator bond := getDelegation(t, port, addr, validator1Owner) - require.Equal(t, "60/1", bond.Shares.String()) + require.Equal(t, "60.0000000000", bond.Shares) ////////////////////// // testing unbonding @@ -409,7 +417,7 @@ func TestBonding(t *testing.T) { // query validator bond = getDelegation(t, port, addr, validator1Owner) - require.Equal(t, "30/1", bond.Shares.String()) + require.Equal(t, "30.0000000000", bond.Shares) // check if tx was committed require.Equal(t, uint32(0), resultTx.CheckTx.Code) @@ -421,7 +429,33 @@ func TestBonding(t *testing.T) { coins = acc.GetCoins() require.Equal(t, int64(40), coins.AmountOf("steak").Int64()) + // query unbonding delegation + validatorAddr := sdk.AccAddress(pks[0].Address()) + unbondings := getUndelegations(t, port, addr, validatorAddr) + assert.Len(t, unbondings, 1, "Unbondings holds all unbonding-delegations") + assert.Equal(t, "30", unbondings[0].Balance.Amount.String()) + + // query summary + summary := getDelegationSummary(t, port, addr) + + assert.Len(t, summary.Delegations, 1, "Delegation summary holds all delegations") + assert.Equal(t, "30.0000000000", summary.Delegations[0].Shares) + assert.Len(t, summary.UnbondingDelegations, 1, "Delegation summary holds all unbonding-delegations") + assert.Equal(t, "30", summary.UnbondingDelegations[0].Balance.Amount.String()) + // TODO add redelegation, need more complex capabilities such to mock context and + // TODO check summary for redelegation + // assert.Len(t, summary.Redelegations, 1, "Delegation summary holds all redelegations") + + // query txs + txs := getBondingTxs(t, port, addr, "") + assert.Len(t, txs, 2, "All Txs found") + + txs = getBondingTxs(t, port, addr, "bond") + assert.Len(t, txs, 1, "All bonding txs found") + + txs = getBondingTxs(t, port, addr, "unbond") + assert.Len(t, txs, 1, "All unbonding txs found") } func TestSubmitProposal(t *testing.T) { @@ -719,17 +753,58 @@ func getSigningInfo(t *testing.T, port string, validatorPubKey string) slashing. return signingInfo } -func getDelegation(t *testing.T, port string, delegatorAddr, validatorAddr sdk.AccAddress) stake.Delegation { +// ============= Stake Module ================ + +func getDelegation(t *testing.T, port string, delegatorAddr, validatorAddr sdk.AccAddress) rest.DelegationWithoutRat { // get the account to get the sequence - res, body := Request(t, port, "GET", fmt.Sprintf("/stake/%s/delegation/%s", delegatorAddr, validatorAddr), nil) + res, body := Request(t, port, "GET", fmt.Sprintf("/stake/delegators/%s/delegations/%s", delegatorAddr, validatorAddr), nil) require.Equal(t, http.StatusOK, res.StatusCode, body) - var bond stake.Delegation + var bond rest.DelegationWithoutRat err := cdc.UnmarshalJSON([]byte(body), &bond) require.Nil(t, err) return bond } +func getUndelegations(t *testing.T, port string, delegatorAddr, validatorAddr sdk.AccAddress) []stake.UnbondingDelegation { + + // get the account to get the sequence + res, body := Request(t, port, "GET", fmt.Sprintf("/stake/delegators/%s/unbonding_delegations/%s", delegatorAddr, validatorAddr), nil) + require.Equal(t, http.StatusOK, res.StatusCode, body) + var unbondings []stake.UnbondingDelegation + err := cdc.UnmarshalJSON([]byte(body), &unbondings) + require.Nil(t, err) + return unbondings +} + +func getDelegationSummary(t *testing.T, port string, delegatorAddr sdk.AccAddress) rest.DelegationSummary { + + // get the account to get the sequence + res, body := Request(t, port, "GET", fmt.Sprintf("/stake/delegators/%s", delegatorAddr), nil) + require.Equal(t, http.StatusOK, res.StatusCode, body) + var summary rest.DelegationSummary + err := cdc.UnmarshalJSON([]byte(body), &summary) + require.Nil(t, err) + return summary +} + +func getBondingTxs(t *testing.T, port string, delegatorAddr sdk.AccAddress, query string) []tx.Info { + + // get the account to get the sequence + var res *http.Response + var body string + if len(query) > 0 { + res, body = Request(t, port, "GET", fmt.Sprintf("/stake/delegators/%s/txs?type=%s", delegatorAddr, query), nil) + } else { + res, body = Request(t, port, "GET", fmt.Sprintf("/stake/delegators/%s/txs", delegatorAddr), nil) + } + require.Equal(t, http.StatusOK, res.StatusCode, body) + var txs []tx.Info + err := cdc.UnmarshalJSON([]byte(body), &txs) + require.Nil(t, err) + return txs +} + func doDelegate(t *testing.T, port, seed, name, password string, delegatorAddr, validatorAddr sdk.AccAddress) (resultTx ctypes.ResultBroadcastTxCommit) { // get the account to get the sequence acc := getAccount(t, port, delegatorAddr) @@ -758,7 +833,7 @@ func doDelegate(t *testing.T, port, seed, name, password string, delegatorAddr, "begin_redelegates": [], "complete_redelegates": [] }`, name, password, accnum, sequence, chainID, delegatorAddr, validatorAddr, "steak")) - res, body := Request(t, port, "POST", "/stake/delegations", jsonStr) + res, body := Request(t, port, "POST", fmt.Sprintf("/stake/delegators/%s/delegations", delegatorAddr), jsonStr) require.Equal(t, http.StatusOK, res.StatusCode, body) var results []ctypes.ResultBroadcastTxCommit @@ -798,7 +873,7 @@ func doBeginUnbonding(t *testing.T, port, seed, name, password string, "begin_redelegates": [], "complete_redelegates": [] }`, name, password, accnum, sequence, chainID, delegatorAddr, validatorAddr)) - res, body := Request(t, port, "POST", "/stake/delegations", jsonStr) + res, body := Request(t, port, "POST", fmt.Sprintf("/stake/delegators/%s/delegations", delegatorAddr), jsonStr) require.Equal(t, http.StatusOK, res.StatusCode, body) var results []ctypes.ResultBroadcastTxCommit @@ -839,7 +914,7 @@ func doBeginRedelegation(t *testing.T, port, seed, name, password string, ], "complete_redelegates": [] }`, name, password, accnum, sequence, chainID, delegatorAddr, validatorSrcAddr, validatorDstAddr)) - res, body := Request(t, port, "POST", "/stake/delegations", jsonStr) + res, body := Request(t, port, "POST", fmt.Sprintf("/stake/delegators/%s/delegations", delegatorAddr), jsonStr) require.Equal(t, http.StatusOK, res.StatusCode, body) var results []ctypes.ResultBroadcastTxCommit @@ -859,6 +934,18 @@ func getValidators(t *testing.T, port string) []stake.BechValidator { return validators } +func getValidator(t *testing.T, port string, validatorAddr sdk.AccAddress) stake.BechValidator { + // get the account to get the sequence + res, body := Request(t, port, "GET", fmt.Sprintf("/stake/validators/%s", validatorAddr.String()), nil) + require.Equal(t, http.StatusOK, res.StatusCode, body) + var validator stake.BechValidator + err := cdc.UnmarshalJSON([]byte(body), &validator) + require.Nil(t, err) + return validator +} + +// ============= Governance Module ================ + func getProposal(t *testing.T, port string, proposalID int64) gov.Proposal { res, body := Request(t, port, "GET", fmt.Sprintf("/gov/proposals/%d", proposalID), nil) require.Equal(t, http.StatusOK, res.StatusCode, body) diff --git a/client/lcd/test_helpers.go b/client/lcd/test_helpers.go index 6db10ef67..48de9598c 100644 --- a/client/lcd/test_helpers.go +++ b/client/lcd/test_helpers.go @@ -121,7 +121,7 @@ func InitializeTestLCD(t *testing.T, nValidators int, initAddrs []sdk.AccAddress config.TxIndex.IndexAllTags = true logger := log.NewTMLogger(log.NewSyncWriter(os.Stdout)) - logger = log.NewFilter(logger, log.AllowDebug()) + logger = log.NewFilter(logger, log.AllowError()) privValidatorFile := config.PrivValidatorFile() privVal := pvm.LoadOrGenFilePV(privValidatorFile) @@ -256,6 +256,7 @@ func Request(t *testing.T, port, method, path string, payload []byte) (*http.Res res *http.Response ) url := fmt.Sprintf("http://localhost:%v%v", port, path) + fmt.Println("REQUEST " + method + " " + url) req, err := http.NewRequest(method, url, bytes.NewBuffer(payload)) require.Nil(t, err) diff --git a/client/tx/query.go b/client/tx/query.go index 239c656a3..f95776b10 100644 --- a/client/tx/query.go +++ b/client/tx/query.go @@ -75,14 +75,14 @@ func queryTx(cdc *wire.Codec, cliCtx context.CLIContext, hashHexStr string, trus return wire.MarshalJSONIndent(cdc, info) } -func formatTxResult(cdc *wire.Codec, res *ctypes.ResultTx) (txInfo, error) { +func formatTxResult(cdc *wire.Codec, res *ctypes.ResultTx) (Info, error) { // TODO: verify the proof if requested tx, err := parseTx(cdc, res.Tx) if err != nil { - return txInfo{}, err + return Info{}, err } - return txInfo{ + return Info{ Hash: res.Hash, Height: res.Height, Tx: tx, @@ -90,8 +90,8 @@ func formatTxResult(cdc *wire.Codec, res *ctypes.ResultTx) (txInfo, error) { }, nil } -// txInfo is used to prepare info to display -type txInfo struct { +// Info is used to prepare info to display +type Info struct { Hash common.HexBytes `json:"hash"` Height int64 `json:"height"` Tx sdk.Tx `json:"tx"` diff --git a/client/tx/search.go b/client/tx/search.go index bd35d2a37..adad29d7d 100644 --- a/client/tx/search.go +++ b/client/tx/search.go @@ -57,7 +57,7 @@ func SearchTxCmd(cdc *wire.Codec) *cobra.Command { return cmd } -func searchTxs(cliCtx context.CLIContext, cdc *wire.Codec, tags []string) ([]txInfo, error) { +func searchTxs(cliCtx context.CLIContext, cdc *wire.Codec, tags []string) ([]Info, error) { if len(tags) == 0 { return nil, errors.New("must declare at least one tag to search") } @@ -81,7 +81,7 @@ func searchTxs(cliCtx context.CLIContext, cdc *wire.Codec, tags []string) ([]txI return nil, err } - info, err := formatTxResults(cdc, res.Txs) + info, err := FormatTxResults(cdc, res.Txs) if err != nil { return nil, err } @@ -89,9 +89,10 @@ func searchTxs(cliCtx context.CLIContext, cdc *wire.Codec, tags []string) ([]txI return info, nil } -func formatTxResults(cdc *wire.Codec, res []*ctypes.ResultTx) ([]txInfo, error) { +// parse the indexed txs into an array of Info +func FormatTxResults(cdc *wire.Codec, res []*ctypes.ResultTx) ([]Info, error) { var err error - out := make([]txInfo, len(res)) + out := make([]Info, len(res)) for i := range res { out[i], err = formatTxResult(cdc, res[i]) if err != nil { diff --git a/client/tx/sign.go b/client/tx/sign.go index bedd202b4..75239ef7b 100644 --- a/client/tx/sign.go +++ b/client/tx/sign.go @@ -8,7 +8,7 @@ import ( keys "github.com/cosmos/cosmos-sdk/crypto/keys" ) -// REST request body +// REST request body for signed txs // TODO does this need to be exposed? type SignTxBody struct { Name string `json:"name"` diff --git a/cmd/cosmos-sdk-cli/cmd/init.go b/cmd/cosmos-sdk-cli/cmd/init.go index d5d6422ad..8cebc48f2 100644 --- a/cmd/cosmos-sdk-cli/cmd/init.go +++ b/cmd/cosmos-sdk-cli/cmd/init.go @@ -7,10 +7,11 @@ import ( "os" "strings" + "path/filepath" + "github.com/cosmos/cosmos-sdk/version" "github.com/spf13/cobra" tmversion "github.com/tendermint/tendermint/version" - "path/filepath" ) var remoteBasecoinPath = "github.com/cosmos/cosmos-sdk/examples/basecoin" diff --git a/docs/clients/lcd-rest-api.yaml b/docs/clients/lcd-rest-api.yaml index a39b7bf08..063e3a55b 100644 --- a/docs/clients/lcd-rest-api.yaml +++ b/docs/clients/lcd-rest-api.yaml @@ -1,8 +1,26 @@ swagger: '2.0' info: version: '1.1.0' - title: Light client daemon to interface with Cosmos baseserver via REST - description: Specification for the LCD provided by `gaiacli advanced rest-server` + title: Gaia-Lite (former LCD) to interface with Cosmos BaseServer via REST + description: Specification for Gaia-lite provided by `gaiacli advanced rest-server` + +tags: +- name: keys + description: Key management to add or view local private keys +- name: send + description: Create and sign a send tx +- name: stake + description: Stake module API for staking and validation +- name: account + description: Query account balance +- name: query + description: Information about blocks and txs +- name: validator set + description: Check the state of the validator set +- name: node + description: Information of the connected node +- name: version + description: Information about the app version securityDefinitions: @@ -12,22 +30,28 @@ securityDefinitions: paths: /version: get: - summary: Version of the light client daemon - description: Get the version of the LCD running locally to compare against expected + summary: Version of Gaia-lite + tags: + - version + description: Get the version of gaia-lite running locally to compare against expected responses: 200: description: Plaintext version i.e. "v0.5.0" /node_version: get: summary: Version of the connected node + tags: + - node description: Get the version of the SDK running on the connected node to compare against expected responses: 200: description: Plaintext version i.e. "v0.5.0" /node_info: get: - description: Only the node info. Block information can be queried via /block/latest - summary: The propertied of the connected node + description: Information about the connected node + summary: The properties of the connected node + tags: + - node produces: - application/json responses: @@ -61,6 +85,8 @@ paths: /syncing: get: summary: Syncing state of node + tags: + - node description: Get if the node is currently syning with other nodes responses: 200: @@ -69,6 +95,8 @@ paths: /keys: get: summary: List of accounts stored locally + tags: + - keys produces: - application/json responses: @@ -80,6 +108,8 @@ paths: $ref: '#/definitions/Account' post: summary: Create a new account locally + tags: + - keys consumes: - application/json parameters: @@ -105,6 +135,8 @@ paths: /keys/seed: get: summary: Create a new seed to create a new account with + tags: + - keys produces: - application/json responses: @@ -121,6 +153,8 @@ paths: type: string get: summary: Get a certain locally stored account + tags: + - keys produces: - application/json responses: @@ -132,6 +166,8 @@ paths: description: Account is not available put: summary: Update the password for this account in the KMS + tags: + - keys consumes: - application/json parameters: @@ -157,6 +193,8 @@ paths: description: Account is not available delete: summary: Remove an account + tags: + - keys consumes: - application/json parameters: @@ -216,6 +254,8 @@ paths: type: string get: summary: Get the account balances + tags: + - account produces: - application/json responses: @@ -234,6 +274,8 @@ paths: type: string post: summary: Send coins (build -> sign -> send) + tags: + - send security: - kms: [] consumes: @@ -265,6 +307,8 @@ paths: /blocks/latest: get: summary: Get the latest block + tags: + - query produces: - application/json responses: @@ -281,6 +325,8 @@ paths: type: number get: summary: Get a block at a certain height + tags: + - query produces: - application/json responses: @@ -293,6 +339,8 @@ paths: /validatorsets/latest: get: summary: Get the latest validator set + tags: + - validator set produces: - application/json responses: @@ -316,6 +364,8 @@ paths: type: number get: summary: Get a validator set a certain height + tags: + - validator set produces: - application/json responses: @@ -407,6 +457,8 @@ paths: type: string get: summary: Get a Tx by hash + tags: + - query produces: - application/json responses: @@ -416,141 +468,201 @@ paths: $ref: "#/definitions/Tx" 404: description: Tx not available for provided hash - # /delegates: - # parameters: - # - in: query - # name: delegator - # description: Query for all delegates a delegator has stake with - # schema: - # $ref: "#/definitions/Address" - # get: - # summary: Get a list of canidates/delegates/validators (optionally filtered by delegator) - # responses: - # 200: - # description: List of delegates, filtered by provided delegator address - # content: - # application/json: - # schema: - # type: array - # items: - # $ref: "#/definitions/Delegate" - # /delegates/bond: - # post: - # summary: Bond atoms (build -> sign -> send) - # security: - # - sign: [] - # requestBody: - # content: - # application/json: - # schema: - # type: array - # items: - # type: object - # properties: - # amount: - # $ref: "#/definitions/Coins" - # pub_key: - # $ref: "#/definitions/PubKey" - # responses: - # 202: - # description: Tx was send and will probably be added to the next block - # 400: - # description: The Tx was malformated - # /delegates/unbond: - # post: - # summary: Unbond atoms (build -> sign -> send) - # security: - # - sign: [] - # requestBody: - # content: - # application/json: - # schema: - # type: array - # items: - # type: object - # properties: - # amount: - # $ref: "#/definitions/Coins" - # pub_key: - # $ref: "#/definitions/PubKey" - # responses: - # 202: - # description: Tx was send and will probably be added to the next block - # 400: - # description: The Tx was malformated - # /delegates/{pubkey}: - # parameters: - # - in: path - # name: pubkey - # description: Pubkey of a delegate - # required: true - # schema: - # type: string - # example: 81B11E717789600CC192B26F452A983DF13B985EE75ABD9DD9E68D7BA007A958 - # get: - # summary: Get a certain canidate/delegate/validator - # responses: - # 200: - # description: Delegate for specified pub_key - # content: - # application/json: - # schema: - # $ref: "#/definitions/Delegate" - # 404: - # description: No delegate found for provided pub_key - # /delegates/{pubkey}/bond: - # parameters: - # - in: path - # name: pubkey - # description: Pubkey of a delegate - # required: true - # schema: - # type: string - # example: 81B11E717789600CC192B26F452A983DF13B985EE75ABD9DD9E68D7BA007A958 - # post: - # summary: Bond atoms (build -> sign -> send) - # security: - # - sign: [] - # requestBody: - # content: - # application/json: - # schema: - # type: object - # properties: - # amount: - # $ref: "#/definitions/Coins" - # responses: - # 202: - # description: Tx was send and will probably be added to the next block - # 400: - # description: The Tx was malformated - # /delegates/{pubkey}/unbond: - # parameters: - # - in: path - # name: pubkey - # description: Pubkey of a delegate - # required: true - # schema: - # type: string - # example: 81B11E717789600CC192B26F452A983DF13B985EE75ABD9DD9E68D7BA007A958 - # post: - # summary: Unbond atoms (build -> sign -> send) - # security: - # - sign: [] - # requestBody: - # content: - # application/json: - # schema: - # type: object - # properties: - # amount: - # $ref: "#/definitions/Coins" - # responses: - # 202: - # description: Tx was send and will probably be added to the next block - # 400: - # description: The Tx was malformated +# ================== Staking Module # ================== + +# TODO create D + /stake/delegators/{delegatorAddr}: + parameters: + - in: path + name: delegatorAddr + description: AccAddress of Delegator + required: true + type: string + get: + summary: Get all delegations (delegation, undelegation) from a delegator + tags: + - stake + produces: + - application/json + responses: + 200: + description: OK + 404: + description: Not Found + 500: + description: Internal Server Error + + /stake/delegators/{delegatorAddr}/txs: + parameters: + - in: path + name: delegatorAddr + description: AccAddress of Delegator + required: true + type: string + get: + summary: Get all staking txs (i.e msgs) from a delegator + tags: + - stake + produces: + - application/json + responses: + 200: + description: OK + schema: + $ref: "#/definitions/Tx" + 404: + description: Not Found + 500: + description: Internal Server Error + + /stake/delegators/{delegatorAddr}/delegations: + parameters: + - in: path + name: delegatorAddr + description: Bech32 AccAddress of Delegator + required: true + type: string + post: + summary: Submit delegation + parameters: + - in: body + name: delegation + description: The password of the account to remove from the KMS + schema: + type: object + properties: + name: + type: string + password: + type: string + account_number: + type: number + delegations: + type: array + items: + type: string + begin_unbondings: + type: array + items: + type: string + complete_unbondings: + type: array + items: + type: string + begin_redelegates: + type: array + items: + type: string + complete_redelegates: + type: array + items: + type: string + chain_id: + type: string + gas: + type: number + sequence: + type: number + tags: + - stake + produces: + - application/json + responses: + 200: + description: OK + schema: + $ref: "#/definitions/Tx" + 404: + description: Not Found + 500: + description: Internal Server Error + + /stake/delegators/{delegatorAddr}/delegations/{validatorAddr}: + parameters: + - in: path + name: delegatorAddr + description: Bech32 AccAddress of Delegator + required: true + type: string + - in: path + name: validatorAddr + description: Bech32 ValAddress of Delegator + required: true + type: string + get: + summary: Query the current delegation status between a delegator and a validator + tags: + - stake + produces: + - application/json + responses: + 200: + description: OK + 404: + description: Not Found + + /stake/delegators/{delegatorAddr}/unbonding_delegations/{validatorAddr}: + parameters: + - in: path + name: delegatorAddr + description: Bech32 AccAddress of Delegator + required: true + type: string + - in: path + name: validatorAddr + description: Bech32 ValAddress of Delegator + required: true + type: string + get: + summary: Query all unbonding delegations between a delegator and a validator + tags: + - stake + produces: + - application/json + responses: + 200: + description: OK + 404: + description: Not Found + 500: + description: Internal Server Error + + /stake/validators: + get: + summary: Get all validator candidates + tags: + - stake + produces: + - application/json + responses: + 200: + description: OK + 500: + description: Internal Server Error + + /stake/validators/{validatorAddr}: + parameters: + - in: path + name: validatorAddr + description: Bech32 ValAddress of Delegator + required: true + type: string + get: + summary: Query the information from a single validator + tags: + - stake + produces: + - application/json + responses: + 200: + description: OK + 404: + description: Not Found + 500: + description: Internal Server Error + +# TODO Add staking definitions definitions: Address: type: string diff --git a/docs/light/api.md b/docs/light/api.md index 26fbe77c2..41abed5da 100644 --- a/docs/light/api.md +++ b/docs/light/api.md @@ -442,3 +442,219 @@ Returns on failure: "result":{} } ``` + +## ICS21 - StakingAPI + +The StakingAPI exposes all functionality needed for validation and delegation in Proof-of-Stake. + +### /stake/delegators/{delegatorAddr} - GET + +url: /stake/delegators/{delegatorAddr} + +Functionality: Get all delegations (delegation, undelegation) from a delegator. + +Returns on success: + +```json +{ + "rest api":"2.0", + "code":200, + "error":"", + "result": { + "atom":1000, + "photon":500, + "ether":20 + } +} +``` + +Returns on error: + +```json +{ + "rest api":"2.0", + "code":500, + "error":"Could not find any balance for the specified account.", + "result":{} +} +``` + +### /stake/delegators/{delegatorAddr}/txs - GET + +url: /stake/delegators/{delegatorAddr}/txs + +Functionality: Get all staking txs (i.e msgs) from a delegator. + +Returns on success: + +```json +{ + "rest api":"2.0", + "code":200, + "error":"", + "result":{ + "transaction":"TODO" + } +} +``` + +Returns on failure: + +```json +{ + "rest api":"2.0", + "code":500, + "error":"Could not create the transaction.", + "result":{} +} +``` + +### /stake/delegators/{delegatorAddr}/delegations - POST + +url: /stake/delegators/{delegatorAddr}/delegations + +Functionality: Submit a delegation. + +Returns on success: + +```json +{ + "rest api":"2.0", + "code":200, + "error":"", + "result":{ + "transaction":"TODO" + } +} +``` + +Returns on failure: + +```json +{ + "rest api":"2.0", + "code":500, + "error":"Could not create the transaction.", + "result":{} +} +``` + +### /stake/delegators/{delegatorAddr}/delegations/{validatorAddr} - GET + +url: /stake/delegators/{delegatorAddr}/delegations/{validatorAddr} + +Functionality: Query the current delegation status between a delegator and a validator. + +Returns on success: + +```json +{ + "rest api":"2.0", + "code":200, + "error":"", + "result":{ + "transaction":"TODO" + } +} +``` + +Returns on failure: + +```json +{ + "rest api":"2.0", + "code":500, + "error":"Could not create the transaction.", + "result":{} +} +``` + +### /stake/delegators/{delegatorAddr}/unbonding_delegations/{validatorAddr} - GET + +url: /stake/delegators/{delegatorAddr}/unbonding_delegations/{validatorAddr} + +Functionality: Query all unbonding delegations between a delegator and a validator. + +Returns on success: + +```json +{ + "rest api":"2.0", + "code":200, + "error":"", + "result":{ + "transaction":"TODO" + } +} +``` + +Returns on failure: + +```json +{ + "rest api":"2.0", + "code":500, + "error":"Could not create the transaction.", + "result":{} +} +``` + +### /stake/validators - GET + +url: /stake/validators + +Functionality: Get all validator candidates. + +Returns on success: + +```json +{ + "rest api":"2.0", + "code":200, + "error":"", + "result":{ + "transaction":"TODO" + } +} +``` + +Returns on failure: + +```json +{ + "rest api":"2.0", + "code":500, + "error":"Could not create the transaction.", + "result":{} +} +``` + +### /stake/validators/{validatorAddr} - GET + +url: /stake/validators/{validatorAddr} + +Functionality: Query the information from a single validator. + +Returns on success: + +```json +{ + "rest api":"2.0", + "code":200, + "error":"", + "result":{ + "transaction":"TODO" + } +} +``` + +Returns on failure: + +```json +{ + "rest api":"2.0", + "code":500, + "error":"Could not create the transaction.", + "result":{} +} +``` diff --git a/docs/light/readme.md b/docs/light/readme.md index 53e3b59bd..a9cdb2fff 100644 --- a/docs/light/readme.md +++ b/docs/light/readme.md @@ -19,7 +19,7 @@ LCD will be used in the Cosmos Hub, the first Hub in the Cosmos network. 1. [**Overview**](##Overview) 2. [**Get Started**](getting_started.md) 3. [**API**](api.md) -4. [**Specifications**](hspecification.md) +4. [**Specifications**](specification.md) ## Overview @@ -67,7 +67,7 @@ A full node of ABCI is different from its light client in the following ways: | Provide APIs|All cosmos APIs|Modular APIs|Full node supports all cosmos APIs. LCD provides modular APIs according to users' configuration| | Secuity level| High|High|Full node will verify all transactions and blocks by itself. LCD can't do this, but it can query any data from other full nodes and verify the data independently. So both full node and LCD don't need to trust any third nodes, they all can achieve high security| -According to the above table, LCD can meet all users' functionality and security requirements, but +According to the above table, LCD can meet all users' functionality and security requirements, but only requires little resource on bandwidth, computing, storage and power. ## How does LCD achieve high security? diff --git a/docs/light/specification.md b/docs/light/specification.md index 48c87d047..d8897b244 100644 --- a/docs/light/specification.md +++ b/docs/light/specification.md @@ -14,7 +14,7 @@ tree is the AppHash which will be included in block header. ![Simple Merkle Tree](pics/simpleMerkleTree.png) -As we have discussed in [LCD trust-propagation](https://github.com/irisnet/cosmos-sdk/tree/bianjie/lcd_spec/docs/spec/lcd#trust-propagation), +As we have discussed in [LCD trust-propagation](https://github.com/irisnet/cosmos-sdk/tree/bianjie/lcd_spec/docs/spec/lcd#trust-propagation), the AppHash can be verified by checking voting power against a trusted validator set. Here we just need to build proof from ABCI state to AppHash. The proof contains two parts: @@ -212,7 +212,7 @@ For instance: To improve LCD reliability and TPS, we recommend to connect LCD to more than one fullnode. But the complexity will increase a lot. So load balancing module will be imported as the adapter. Please -refer to this link for detailed description: [load balancing](https://github.com/irisnet/cosmos-sdk/blob/bianjie/lcd_spec/docs/spec/lcd/loadbalance.md) +refer to this link for detailed description: [load balancer](load_balancer.md) ## ICS1 (KeyAPI) @@ -305,14 +305,44 @@ return KeyOutput{ ## ICS20 (TokenAPI) -### [/bank/balance/{account}](api.md#balanceaccount---get) +### [/bank/balance/{account}](api.md#bankbalanceaccount---get) 1. Decode the address from bech32 to hex. 2. Send a query request to a full node. Ask for proof if required by Gaia Light. 3. Verify the proof against the root of trust. -### [/bank/create_transfer](api.md#create_transfer---post) - +### [/bank/create_transfer](api.md#bankcreate_transfer---post) + 1. Check the parameters. 2. Build the transaction with the specified parameters. 3. Serialise the transaction and return the JSON encoded sign bytes. + +## ICS21 (StakingAPI) + +### [/stake/delegators/{delegatorAddr}](api.md#stakedelegatorsdelegatorAddr---get) + +TODO + +### [/stake/delegators/{delegatorAddr}/txs](api.md#stakedelegatorsdelegatorAddrtxs---get) + +TODO + +### [/stake/delegators/{delegatorAddr}/delegations](api.md#stakedelegatorsdelegatorAddrdelegations---post) + +TODO + +### [/stake/delegators/{delegatorAddr}/delegations/{validatorAddr}](api.md#stakedelegatorsdelegatorAddrdelegationsvalidatorAddr---get) + +TODO + +### [/stake/delegators/{delegatorAddr}/unbonding_delegations/{validatorAddr}](api.md#stakedelegatorsdelegatorAddrunbonding_delegationsvalidatorAddr---get) + +TODO + +### [/stake/validators](api.md#stakevalidators---get) + +TODO + +### [/stake/validators/{validatorAddr}](api.md#stakevalidatorsvalidatorAddr---get) + +TODO diff --git a/x/stake/client/rest/query.go b/x/stake/client/rest/query.go index 0d69c0a2e..0eba14aac 100644 --- a/x/stake/client/rest/query.go +++ b/x/stake/client/rest/query.go @@ -3,11 +3,14 @@ package rest import ( "fmt" "net/http" + "strings" "github.com/cosmos/cosmos-sdk/client/context" + "github.com/cosmos/cosmos-sdk/client/tx" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/wire" "github.com/cosmos/cosmos-sdk/x/stake" + "github.com/cosmos/cosmos-sdk/x/stake/tags" "github.com/cosmos/cosmos-sdk/x/stake/types" "github.com/gorilla/mux" @@ -16,33 +19,222 @@ import ( const storeName = "stake" func registerQueryRoutes(cliCtx context.CLIContext, r *mux.Router, cdc *wire.Codec) { + + // GET /stake/delegators/{delegatorAddr} // Get all delegations (delegation, undelegation and redelegation) from a delegator r.HandleFunc( - "/stake/{delegator}/delegation/{validator}", + "/stake/delegators/{delegatorAddr}", + delegatorHandlerFn(cliCtx, cdc), + ).Methods("GET") + + // GET /stake/delegators/{delegatorAddr}/txs?type= // Get all staking txs (i.e msgs) from a delegator + r.HandleFunc( + "/stake/delegators/{delegatorAddr}/txs", + delegatorTxsHandlerFn(cliCtx, cdc), + ).Methods("GET") + + // GET /stake/delegators/{delegatorAddr}/delegations/{validatorAddr} // Query a delegation between a delegator and a validator + r.HandleFunc( + "/stake/delegators/{delegatorAddr}/delegations/{validatorAddr}", delegationHandlerFn(cliCtx, cdc), ).Methods("GET") + // GET /stake/delegators/{delegatorAddr}/unbonding_delegations/{validatorAddr} // Query all unbonding_delegations between a delegator and a validator r.HandleFunc( - "/stake/{delegator}/ubd/{validator}", - ubdHandlerFn(cliCtx, cdc), - ).Methods("GET") - - r.HandleFunc( - "/stake/{delegator}/red/{validator_src}/{validator_dst}", - redHandlerFn(cliCtx, cdc), + "/stake/delegators/{delegatorAddr}/unbonding_delegations/{validatorAddr}", + unbondingDelegationsHandlerFn(cliCtx, cdc), ).Methods("GET") + // GET /stake/validators/ r.HandleFunc( "/stake/validators", validatorsHandlerFn(cliCtx, cdc), ).Methods("GET") + + // GET /stake/validators/{addr} + r.HandleFunc( + "/stake/validators/{addr}", + validatorHandlerFn(cliCtx, cdc), + ).Methods("GET") } -// http request handler to query a delegation -func delegationHandlerFn(cliCtx context.CLIContext, cdc *wire.Codec) http.HandlerFunc { +// already resolve the rational shares to not handle this in the client + +// defines a delegation without type Rat for shares +type DelegationWithoutRat struct { + DelegatorAddr sdk.AccAddress `json:"delegator_addr"` + ValidatorAddr sdk.AccAddress `json:"validator_addr"` + Shares string `json:"shares"` + Height int64 `json:"height"` +} + +// aggregation of all delegations, unbondings and redelegations +type DelegationSummary struct { + Delegations []DelegationWithoutRat `json:"delegations"` + UnbondingDelegations []stake.UnbondingDelegation `json:"unbonding_delegations"` + Redelegations []stake.Redelegation `json:"redelegations"` +} + +// HTTP request handler to query a delegator delegations +func delegatorHandlerFn(cliCtx context.CLIContext, cdc *wire.Codec) http.HandlerFunc { + return func(w http.ResponseWriter, r *http.Request) { + + var validatorAddr sdk.AccAddress + var delegationSummary = DelegationSummary{} + + // read parameters + vars := mux.Vars(r) + bech32delegator := vars["delegatorAddr"] + + delegatorAddr, err := sdk.AccAddressFromBech32(bech32delegator) + if err != nil { + w.WriteHeader(http.StatusBadRequest) + w.Write([]byte(err.Error())) + return + } + + // Get all validators using key + validators, statusCode, errMsg, err := getBech32Validators(storeName, cliCtx, cdc) + if err != nil { + w.WriteHeader(statusCode) + w.Write([]byte(fmt.Sprintf("%s%s", errMsg, err.Error()))) + return + } + + for _, validator := range validators { + validatorAddr = validator.Owner + + // Delegations + delegations, statusCode, errMsg, err := getDelegatorDelegations(cliCtx, cdc, delegatorAddr, validatorAddr) + if err != nil { + w.WriteHeader(statusCode) + w.Write([]byte(fmt.Sprintf("%s%s", errMsg, err.Error()))) + return + } + if statusCode != http.StatusNoContent { + delegationSummary.Delegations = append(delegationSummary.Delegations, delegations) + } + + // Undelegations + unbondingDelegation, statusCode, errMsg, err := getDelegatorUndelegations(cliCtx, cdc, delegatorAddr, validatorAddr) + if err != nil { + w.WriteHeader(statusCode) + w.Write([]byte(fmt.Sprintf("%s%s", errMsg, err.Error()))) + return + } + if statusCode != http.StatusNoContent { + delegationSummary.UnbondingDelegations = append(delegationSummary.UnbondingDelegations, unbondingDelegation) + } + + // Redelegations + // only querying redelegations to a validator as this should give us already all relegations + // if we also would put in redelegations from, we would have every redelegation double + redelegations, statusCode, errMsg, err := getDelegatorRedelegations(cliCtx, cdc, delegatorAddr, validatorAddr) + if err != nil { + w.WriteHeader(statusCode) + w.Write([]byte(fmt.Sprintf("%s%s", errMsg, err.Error()))) + return + } + if statusCode != http.StatusNoContent { + delegationSummary.Redelegations = append(delegationSummary.Redelegations, redelegations) + } + + output, err := cdc.MarshalJSON(delegationSummary) + if err != nil { + w.WriteHeader(http.StatusInternalServerError) + w.Write([]byte(err.Error())) + return + } + + // success + w.Write(output) // write + } + } +} + +// nolint gocyclo +// HTTP request handler to query all staking txs (msgs) from a delegator +func delegatorTxsHandlerFn(cliCtx context.CLIContext, cdc *wire.Codec) http.HandlerFunc { + return func(w http.ResponseWriter, r *http.Request) { + var output []byte + var typesQuerySlice []string + vars := mux.Vars(r) + delegatorAddr := vars["delegatorAddr"] + + _, err := sdk.AccAddressFromBech32(delegatorAddr) + if err != nil { + w.WriteHeader(http.StatusBadRequest) + w.Write([]byte(err.Error())) + return + } + + node, err := cliCtx.GetNode() + if err != nil { + w.WriteHeader(http.StatusInternalServerError) + w.Write([]byte(fmt.Sprintf("Couldn't get current Node information. Error: %s", err.Error()))) + return + } + + // Get values from query + + typesQuery := r.URL.Query().Get("type") + trimmedQuery := strings.TrimSpace(typesQuery) + if len(trimmedQuery) != 0 { + typesQuerySlice = strings.Split(trimmedQuery, " ") + } + + noQuery := len(typesQuerySlice) == 0 + isBondTx := contains(typesQuerySlice, "bond") + isUnbondTx := contains(typesQuerySlice, "unbond") + isRedTx := contains(typesQuerySlice, "redelegate") + var txs = []tx.Info{} + var actions []string + + switch { + case isBondTx: + actions = append(actions, string(tags.ActionDelegate)) + case isUnbondTx: + actions = append(actions, string(tags.ActionBeginUnbonding)) + actions = append(actions, string(tags.ActionCompleteUnbonding)) + case isRedTx: + actions = append(actions, string(tags.ActionBeginRedelegation)) + actions = append(actions, string(tags.ActionCompleteRedelegation)) + case noQuery: + actions = append(actions, string(tags.ActionDelegate)) + actions = append(actions, string(tags.ActionBeginUnbonding)) + actions = append(actions, string(tags.ActionCompleteUnbonding)) + actions = append(actions, string(tags.ActionBeginRedelegation)) + actions = append(actions, string(tags.ActionCompleteRedelegation)) + default: + w.WriteHeader(http.StatusNoContent) + return + } + + for _, action := range actions { + foundTxs, errQuery := queryTxs(node, cdc, action, delegatorAddr) + if errQuery != nil { + w.WriteHeader(http.StatusInternalServerError) + w.Write([]byte(fmt.Sprintf("error querying transactions. Error: %s", errQuery.Error()))) + } + txs = append(txs, foundTxs...) + } + + // success + output, err = cdc.MarshalJSON(txs) + if err != nil { + w.WriteHeader(http.StatusInternalServerError) + w.Write([]byte(err.Error())) + return + } + w.Write(output) // write + } +} + +// HTTP request handler to query an unbonding-delegation +func unbondingDelegationsHandlerFn(cliCtx context.CLIContext, cdc *wire.Codec) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { vars := mux.Vars(r) - bech32delegator := vars["delegator"] - bech32validator := vars["validator"] + bech32delegator := vars["delegatorAddr"] + bech32validator := vars["validatorAddr"] delegatorAddr, err := sdk.AccAddressFromBech32(bech32delegator) if err != nil { @@ -57,8 +249,68 @@ func delegationHandlerFn(cliCtx context.CLIContext, cdc *wire.Codec) http.Handle w.Write([]byte(err.Error())) return } + validatorAddrAcc := sdk.AccAddress(validatorAddr) - key := stake.GetDelegationKey(delegatorAddr, validatorAddr) + key := stake.GetUBDKey(delegatorAddr, validatorAddrAcc) + + res, err := cliCtx.QueryStore(key, storeName) + if err != nil { + w.WriteHeader(http.StatusInternalServerError) + w.Write([]byte(fmt.Sprintf("couldn't query unbonding-delegation. Error: %s", err.Error()))) + return + } + + // the query will return empty if there is no data for this record + if len(res) == 0 { + w.WriteHeader(http.StatusNoContent) + return + } + + ubd, err := types.UnmarshalUBD(cdc, key, res) + if err != nil { + w.WriteHeader(http.StatusInternalServerError) + w.Write([]byte(fmt.Sprintf("couldn't unmarshall unbonding-delegation. Error: %s", err.Error()))) + return + } + + // unbondings will be a list in the future but is not yet, but we want to keep the API consistent + ubdArray := []stake.UnbondingDelegation{ubd} + + output, err := cdc.MarshalJSON(ubdArray) + if err != nil { + w.WriteHeader(http.StatusInternalServerError) + w.Write([]byte(fmt.Sprintf("couldn't marshall unbonding-delegation. Error: %s", err.Error()))) + return + } + + w.Write(output) + } +} + +// HTTP request handler to query a bonded validator +func delegationHandlerFn(cliCtx context.CLIContext, cdc *wire.Codec) http.HandlerFunc { + return func(w http.ResponseWriter, r *http.Request) { + // read parameters + vars := mux.Vars(r) + bech32delegator := vars["delegatorAddr"] + bech32validator := vars["validatorAddr"] + + delegatorAddr, err := sdk.AccAddressFromBech32(bech32delegator) + if err != nil { + w.WriteHeader(http.StatusBadRequest) + w.Write([]byte(err.Error())) + return + } + + validatorAddr, err := sdk.AccAddressFromBech32(bech32validator) + if err != nil { + w.WriteHeader(http.StatusBadRequest) + w.Write([]byte(err.Error())) + return + } + validatorAddrAcc := sdk.AccAddress(validatorAddr) + + key := stake.GetDelegationKey(delegatorAddr, validatorAddrAcc) res, err := cliCtx.QueryStore(key, storeName) if err != nil { @@ -80,7 +332,14 @@ func delegationHandlerFn(cliCtx context.CLIContext, cdc *wire.Codec) http.Handle return } - output, err := cdc.MarshalJSON(delegation) + outputDelegation := DelegationWithoutRat{ + DelegatorAddr: delegation.DelegatorAddr, + ValidatorAddr: delegation.ValidatorAddr, + Height: delegation.Height, + Shares: delegation.Shares.FloatString(), + } + + output, err := cdc.MarshalJSON(outputDelegation) if err != nil { w.WriteHeader(http.StatusInternalServerError) w.Write([]byte(err.Error())) @@ -91,68 +350,16 @@ func delegationHandlerFn(cliCtx context.CLIContext, cdc *wire.Codec) http.Handle } } -// http request handler to query an unbonding-delegation -func ubdHandlerFn(cliCtx context.CLIContext, cdc *wire.Codec) http.HandlerFunc { +// HTTP request handler to query all delegator bonded validators +func delegatorValidatorsHandlerFn(cliCtx context.CLIContext, cdc *wire.Codec) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { - vars := mux.Vars(r) - bech32delegator := vars["delegator"] - bech32validator := vars["validator"] - delegatorAddr, err := sdk.AccAddressFromBech32(bech32delegator) - if err != nil { - w.WriteHeader(http.StatusBadRequest) - w.Write([]byte(err.Error())) - return - } + var validatorAccAddr sdk.AccAddress + var bondedValidators []types.BechValidator - validatorAddr, err := sdk.AccAddressFromBech32(bech32validator) - if err != nil { - w.WriteHeader(http.StatusBadRequest) - w.Write([]byte(err.Error())) - return - } - - key := stake.GetUBDKey(delegatorAddr, validatorAddr) - - res, err := cliCtx.QueryStore(key, storeName) - if err != nil { - w.WriteHeader(http.StatusInternalServerError) - w.Write([]byte(fmt.Sprintf("couldn't query unbonding-delegation. Error: %s", err.Error()))) - return - } - - // the query will return empty if there is no data for this record - if len(res) == 0 { - w.WriteHeader(http.StatusNoContent) - return - } - - ubd, err := types.UnmarshalUBD(cdc, key, res) - if err != nil { - w.WriteHeader(http.StatusInternalServerError) - w.Write([]byte(fmt.Sprintf("couldn't query unbonding-delegation. Error: %s", err.Error()))) - return - } - - output, err := cdc.MarshalJSON(ubd) - if err != nil { - w.WriteHeader(http.StatusInternalServerError) - w.Write([]byte(err.Error())) - return - } - - w.Write(output) - } -} - -// http request handler to query an redelegation -func redHandlerFn(cliCtx context.CLIContext, cdc *wire.Codec) http.HandlerFunc { - return func(w http.ResponseWriter, r *http.Request) { // read parameters vars := mux.Vars(r) - bech32delegator := vars["delegator"] - bech32validatorSrc := vars["validator_src"] - bech32validatorDst := vars["validator_dst"] + bech32delegator := vars["delegatorAddr"] delegatorAddr, err := sdk.AccAddressFromBech32(bech32delegator) if err != nil { @@ -161,49 +368,86 @@ func redHandlerFn(cliCtx context.CLIContext, cdc *wire.Codec) http.HandlerFunc { return } - validatorSrcAddr, err := sdk.AccAddressFromBech32(bech32validatorSrc) - if err != nil { - w.WriteHeader(http.StatusBadRequest) - w.Write([]byte(err.Error())) - return - } - - validatorDstAddr, err := sdk.AccAddressFromBech32(bech32validatorDst) - if err != nil { - w.WriteHeader(http.StatusBadRequest) - w.Write([]byte(err.Error())) - return - } - - key := stake.GetREDKey(delegatorAddr, validatorSrcAddr, validatorDstAddr) - - res, err := cliCtx.QueryStore(key, storeName) + // Get all validators using key + kvs, err := cliCtx.QuerySubspace(stake.ValidatorsKey, storeName) if err != nil { w.WriteHeader(http.StatusInternalServerError) - w.Write([]byte(fmt.Sprintf("couldn't query redelegation. Error: %s", err.Error()))) + w.Write([]byte(fmt.Sprintf("couldn't query validators. Error: %s", err.Error()))) return - } - - // the query will return empty if there is no data for this record - if len(res) == 0 { + } else if len(kvs) == 0 { + // the query will return empty if there are no validators w.WriteHeader(http.StatusNoContent) return } - red, err := types.UnmarshalRED(cdc, key, res) + validators, err := getValidators(kvs, cdc) if err != nil { w.WriteHeader(http.StatusInternalServerError) - w.Write([]byte(fmt.Sprintf("couldn't query unbonding-delegation. Error: %s", err.Error()))) + w.Write([]byte(fmt.Sprintf("Error: %s", err.Error()))) return } - output, err := cdc.MarshalJSON(red) + for _, validator := range validators { + // get all transactions from the delegator to val and append + validatorAccAddr = validator.Owner + + validator, statusCode, errMsg, errRes := getDelegatorValidator(cliCtx, cdc, delegatorAddr, validatorAccAddr) + if errRes != nil { + w.WriteHeader(statusCode) + w.Write([]byte(fmt.Sprintf("%s%s", errMsg, errRes.Error()))) + return + } else if statusCode == http.StatusNoContent { + continue + } + + bondedValidators = append(bondedValidators, validator) + } + // success + output, err := cdc.MarshalJSON(bondedValidators) if err != nil { w.WriteHeader(http.StatusInternalServerError) w.Write([]byte(err.Error())) return } + w.Write(output) // write + } +} +// HTTP request handler to get information from a currently bonded validator +func delegatorValidatorHandlerFn(cliCtx context.CLIContext, cdc *wire.Codec) http.HandlerFunc { + return func(w http.ResponseWriter, r *http.Request) { + // read parameters + var output []byte + vars := mux.Vars(r) + bech32delegator := vars["delegatorAddr"] + bech32validator := vars["validatorAddr"] + + delegatorAddr, err := sdk.AccAddressFromBech32(bech32delegator) + validatorAccAddr, err := sdk.AccAddressFromBech32(bech32validator) + if err != nil { + w.WriteHeader(http.StatusBadRequest) + w.Write([]byte(fmt.Sprintf("Error: %s", err.Error()))) + return + } + + // Check if there if the delegator is bonded or redelegated to the validator + + validator, statusCode, errMsg, err := getDelegatorValidator(cliCtx, cdc, delegatorAddr, validatorAccAddr) + if err != nil { + w.WriteHeader(statusCode) + w.Write([]byte(fmt.Sprintf("%s%s", errMsg, err.Error()))) + return + } else if statusCode == http.StatusNoContent { + w.WriteHeader(statusCode) + return + } + // success + output, err = cdc.MarshalJSON(validator) + if err != nil { + w.WriteHeader(http.StatusInternalServerError) + w.Write([]byte(err.Error())) + return + } w.Write(output) } } @@ -225,25 +469,11 @@ func validatorsHandlerFn(cliCtx context.CLIContext, cdc *wire.Codec) http.Handle return } - // parse out the validators - validators := make([]types.BechValidator, len(kvs)) - for i, kv := range kvs { - - addr := kv.Key[1:] - validator, err := types.UnmarshalValidator(cdc, addr, kv.Value) - if err != nil { - w.WriteHeader(http.StatusInternalServerError) - w.Write([]byte(fmt.Sprintf("couldn't query unbonding-delegation. Error: %s", err.Error()))) - return - } - - bech32Validator, err := validator.Bech32Validator() - if err != nil { - w.WriteHeader(http.StatusBadRequest) - w.Write([]byte(err.Error())) - return - } - validators[i] = bech32Validator + validators, err := getValidators(kvs, cdc) + if err != nil { + w.WriteHeader(http.StatusInternalServerError) + w.Write([]byte(fmt.Sprintf("Error: %s", err.Error()))) + return } output, err := cdc.MarshalJSON(validators) @@ -256,3 +486,47 @@ func validatorsHandlerFn(cliCtx context.CLIContext, cdc *wire.Codec) http.Handle w.Write(output) } } + +// HTTP request handler to query the validator information from a given validator address +func validatorHandlerFn(cliCtx context.CLIContext, cdc *wire.Codec) http.HandlerFunc { + return func(w http.ResponseWriter, r *http.Request) { + + var output []byte + // read parameters + vars := mux.Vars(r) + bech32validatorAddr := vars["addr"] + valAddress, err := sdk.AccAddressFromBech32(bech32validatorAddr) + if err != nil { + w.WriteHeader(http.StatusBadRequest) + w.Write([]byte(fmt.Sprintf("Error: %s", err.Error()))) + return + } + + kvs, err := cliCtx.QuerySubspace(stake.ValidatorsKey, storeName) + if err != nil { + w.WriteHeader(http.StatusInternalServerError) + w.Write([]byte(fmt.Sprintf("Error: %s", err.Error()))) + return + } + + validator, err := getValidator(valAddress, kvs, cdc) + if err != nil { + w.WriteHeader(http.StatusInternalServerError) + w.Write([]byte(fmt.Sprintf("couldn't query validator. Error: %s", err.Error()))) + return + } + + output, err = cdc.MarshalJSON(validator) + if err != nil { + w.WriteHeader(http.StatusInternalServerError) + w.Write([]byte(fmt.Sprintf("Error: %s", err.Error()))) + return + } + + if output == nil { + w.WriteHeader(http.StatusNoContent) + return + } + w.Write(output) + } +} diff --git a/x/stake/client/rest/tx.go b/x/stake/client/rest/tx.go index 0ab52aa63..fbefc7f21 100644 --- a/x/stake/client/rest/tx.go +++ b/x/stake/client/rest/tx.go @@ -10,7 +10,7 @@ import ( "github.com/cosmos/cosmos-sdk/crypto/keys" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/wire" - authctx "github.com/cosmos/cosmos-sdk/x/auth/client/context" + authcliCtx "github.com/cosmos/cosmos-sdk/x/auth/client/context" "github.com/cosmos/cosmos-sdk/x/stake" "github.com/cosmos/cosmos-sdk/x/stake/types" @@ -21,8 +21,8 @@ import ( func registerTxRoutes(cliCtx context.CLIContext, r *mux.Router, cdc *wire.Codec, kb keys.Keybase) { r.HandleFunc( - "/stake/delegations", - editDelegationsRequestHandlerFn(cdc, kb, cliCtx), + "/stake/delegators/{delegatorAddr}/delegations", + delegationsRequestHandlerFn(cdc, kb, cliCtx), ).Methods("POST") } @@ -52,7 +52,7 @@ type msgCompleteUnbondingInput struct { ValidatorAddr string `json:"validator_addr"` // in bech32 } -// request body for edit delegations +// the request body for edit delegations type EditDelegationsBody struct { LocalAccountName string `json:"name"` Password string `json:"password"` @@ -69,7 +69,8 @@ type EditDelegationsBody struct { // nolint: gocyclo // TODO: Split this up into several smaller functions, and remove the above nolint -func editDelegationsRequestHandlerFn(cdc *wire.Codec, kb keys.Keybase, cliCtx context.CLIContext) http.HandlerFunc { +// TODO: use sdk.ValAddress instead of sdk.AccAddress for validators in messages +func delegationsRequestHandlerFn(cdc *wire.Codec, kb keys.Keybase, cliCtx context.CLIContext) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { var m EditDelegationsBody @@ -152,7 +153,6 @@ func editDelegationsRequestHandlerFn(cdc *wire.Codec, kb keys.Keybase, cliCtx co w.Write([]byte(fmt.Sprintf("Couldn't decode validator. Error: %s", err.Error()))) return } - validatorDstAddr, err := sdk.AccAddressFromBech32(msg.ValidatorDstAddr) if err != nil { w.WriteHeader(http.StatusInternalServerError) @@ -191,7 +191,6 @@ func editDelegationsRequestHandlerFn(cdc *wire.Codec, kb keys.Keybase, cliCtx co w.Write([]byte(fmt.Sprintf("Couldn't decode validator. Error: %s", err.Error()))) return } - validatorDstAddr, err := sdk.AccAddressFromBech32(msg.ValidatorDstAddr) if err != nil { w.WriteHeader(http.StatusInternalServerError) @@ -280,7 +279,7 @@ func editDelegationsRequestHandlerFn(cdc *wire.Codec, kb keys.Keybase, cliCtx co i++ } - txCtx := authctx.TxContext{ + txCtx := authcliCtx.TxContext{ Codec: cdc, ChainID: m.ChainID, Gas: m.Gas, diff --git a/x/stake/client/rest/utils.go b/x/stake/client/rest/utils.go new file mode 100644 index 000000000..86e714662 --- /dev/null +++ b/x/stake/client/rest/utils.go @@ -0,0 +1,228 @@ +package rest + +import ( + "bytes" + "fmt" + "net/http" + + "github.com/cosmos/cosmos-sdk/client/context" + "github.com/cosmos/cosmos-sdk/client/tx" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/wire" + "github.com/cosmos/cosmos-sdk/x/stake" + "github.com/cosmos/cosmos-sdk/x/stake/tags" + "github.com/cosmos/cosmos-sdk/x/stake/types" + "github.com/pkg/errors" + rpcclient "github.com/tendermint/tendermint/rpc/client" +) + +// contains checks if the a given query contains one of the tx types +func contains(stringSlice []string, txType string) bool { + for _, word := range stringSlice { + if word == txType { + return true + } + } + return false +} + +func getDelegatorValidator(cliCtx context.CLIContext, cdc *wire.Codec, delegatorAddr sdk.AccAddress, validatorAccAddr sdk.AccAddress) ( + validator types.BechValidator, httpStatusCode int, errMsg string, err error) { + + // check if the delegator is bonded or redelegated to the validator + keyDel := stake.GetDelegationKey(delegatorAddr, validatorAccAddr) + + res, err := cliCtx.QueryStore(keyDel, storeName) + if err != nil { + return types.BechValidator{}, http.StatusInternalServerError, "couldn't query delegation. Error: ", err + } + + if len(res) == 0 { + return types.BechValidator{}, http.StatusNoContent, "", nil + } + + kvs, err := cliCtx.QuerySubspace(stake.ValidatorsKey, storeName) + if err != nil { + return types.BechValidator{}, http.StatusInternalServerError, "Error: ", err + } + if len(kvs) == 0 { + // the query will return empty if there are no delegations + return types.BechValidator{}, http.StatusNoContent, "", nil + } + + validator, errVal := getValidatorFromAccAdrr(validatorAccAddr, kvs, cdc) + if errVal != nil { + return types.BechValidator{}, http.StatusInternalServerError, "Couldn't get info from validator. Error: ", errVal + } + return validator, http.StatusOK, "", nil +} + +func getDelegatorDelegations(cliCtx context.CLIContext, cdc *wire.Codec, delegatorAddr sdk.AccAddress, validatorAddr sdk.AccAddress) ( + outputDelegation DelegationWithoutRat, httpStatusCode int, errMsg string, err error) { + delegationKey := stake.GetDelegationKey(delegatorAddr, validatorAddr) + marshalledDelegation, err := cliCtx.QueryStore(delegationKey, storeName) + if err != nil { + return DelegationWithoutRat{}, http.StatusInternalServerError, "couldn't query delegation. Error: ", err + } + + // the query will return empty if there is no data for this record + if len(marshalledDelegation) == 0 { + return DelegationWithoutRat{}, http.StatusNoContent, "", nil + } + + delegation, err := types.UnmarshalDelegation(cdc, delegationKey, marshalledDelegation) + if err != nil { + return DelegationWithoutRat{}, http.StatusInternalServerError, "couldn't unmarshall delegation. Error: ", err + } + + outputDelegation = DelegationWithoutRat{ + DelegatorAddr: delegation.DelegatorAddr, + ValidatorAddr: delegation.ValidatorAddr, + Height: delegation.Height, + Shares: delegation.Shares.FloatString(), + } + + return outputDelegation, http.StatusOK, "", nil +} + +func getDelegatorUndelegations(cliCtx context.CLIContext, cdc *wire.Codec, delegatorAddr sdk.AccAddress, validatorAddr sdk.AccAddress) ( + unbonds types.UnbondingDelegation, httpStatusCode int, errMsg string, err error) { + undelegationKey := stake.GetUBDKey(delegatorAddr, validatorAddr) + marshalledUnbondingDelegation, err := cliCtx.QueryStore(undelegationKey, storeName) + if err != nil { + return types.UnbondingDelegation{}, http.StatusInternalServerError, "couldn't query unbonding-delegation. Error: ", err + } + + // the query will return empty if there is no data for this record + if len(marshalledUnbondingDelegation) == 0 { + return types.UnbondingDelegation{}, http.StatusNoContent, "", nil + } + + unbondingDelegation, err := types.UnmarshalUBD(cdc, undelegationKey, marshalledUnbondingDelegation) + if err != nil { + return types.UnbondingDelegation{}, http.StatusInternalServerError, "couldn't unmarshall unbonding-delegation. Error: ", err + } + return unbondingDelegation, http.StatusOK, "", nil +} + +func getDelegatorRedelegations(cliCtx context.CLIContext, cdc *wire.Codec, delegatorAddr sdk.AccAddress, validatorAddr sdk.AccAddress) ( + regelegations types.Redelegation, httpStatusCode int, errMsg string, err error) { + + keyRedelegateTo := stake.GetREDsByDelToValDstIndexKey(delegatorAddr, validatorAddr) + marshalledRedelegations, err := cliCtx.QueryStore(keyRedelegateTo, storeName) + if err != nil { + return types.Redelegation{}, http.StatusInternalServerError, "couldn't query redelegation. Error: ", err + } + + if len(marshalledRedelegations) == 0 { + return types.Redelegation{}, http.StatusNoContent, "", nil + } + + redelegations, err := types.UnmarshalRED(cdc, keyRedelegateTo, marshalledRedelegations) + if err != nil { + return types.Redelegation{}, http.StatusInternalServerError, "couldn't unmarshall redelegations. Error: ", err + } + + return redelegations, http.StatusOK, "", nil +} + +// queries staking txs +func queryTxs(node rpcclient.Client, cdc *wire.Codec, tag string, delegatorAddr string) ([]tx.Info, error) { + page := 0 + perPage := 100 + prove := false + query := fmt.Sprintf("%s='%s' AND %s='%s'", tags.Action, tag, tags.Delegator, delegatorAddr) + res, err := node.TxSearch(query, prove, page, perPage) + if err != nil { + return nil, err + } + + return tx.FormatTxResults(cdc, res.Txs) +} + +// gets all validators +func getValidators(validatorKVs []sdk.KVPair, cdc *wire.Codec) ([]types.BechValidator, error) { + validators := make([]types.BechValidator, len(validatorKVs)) + for i, kv := range validatorKVs { + + addr := kv.Key[1:] + validator, err := types.UnmarshalValidator(cdc, addr, kv.Value) + if err != nil { + return nil, err + } + + bech32Validator, err := validator.Bech32Validator() + if err != nil { + return nil, err + } + validators[i] = bech32Validator + } + return validators, nil +} + +// gets a validator given a ValAddress +func getValidator(address sdk.AccAddress, validatorKVs []sdk.KVPair, cdc *wire.Codec) (stake.BechValidator, error) { + // parse out the validators + for _, kv := range validatorKVs { + addr := kv.Key[1:] + validator, err := types.UnmarshalValidator(cdc, addr, kv.Value) + if err != nil { + return stake.BechValidator{}, err + } + + ownerAddress := validator.PubKey.Address() + if bytes.Equal(ownerAddress.Bytes(), address.Bytes()) { + bech32Validator, err := validator.Bech32Validator() + if err != nil { + return stake.BechValidator{}, err + } + + return bech32Validator, nil + } + } + return stake.BechValidator{}, errors.Errorf("Couldn't find validator") +} + +// gets a validator given an AccAddress +func getValidatorFromAccAdrr(address sdk.AccAddress, validatorKVs []sdk.KVPair, cdc *wire.Codec) (stake.BechValidator, error) { + // parse out the validators + for _, kv := range validatorKVs { + addr := kv.Key[1:] + validator, err := types.UnmarshalValidator(cdc, addr, kv.Value) + if err != nil { + return stake.BechValidator{}, err + } + + ownerAddress := validator.PubKey.Address() + if bytes.Equal(ownerAddress.Bytes(), address.Bytes()) { + bech32Validator, err := validator.Bech32Validator() + if err != nil { + return stake.BechValidator{}, err + } + + return bech32Validator, nil + } + } + return stake.BechValidator{}, errors.Errorf("Couldn't find validator") +} + +// gets all Bech32 validators from a key +func getBech32Validators(storeName string, cliCtx context.CLIContext, cdc *wire.Codec) ( + validators []types.BechValidator, httpStatusCode int, errMsg string, err error) { + // Get all validators using key + kvs, err := cliCtx.QuerySubspace(stake.ValidatorsKey, storeName) + if err != nil { + return nil, http.StatusInternalServerError, "couldn't query validators. Error: ", err + } + + // the query will return empty if there are no validators + if len(kvs) == 0 { + return nil, http.StatusNoContent, "", nil + } + + validators, err = getValidators(kvs, cdc) + if err != nil { + return nil, http.StatusInternalServerError, "Error: ", err + } + return validators, http.StatusOK, "", nil +} diff --git a/x/stake/keeper/key.go b/x/stake/keeper/key.go index e373ede18..502ec2654 100644 --- a/x/stake/keeper/key.go +++ b/x/stake/keeper/key.go @@ -34,19 +34,19 @@ var ( const maxDigitsForAccount = 12 // ~220,000,000 atoms created at launch -// get the key for the validator with address. +// gets the key for the validator with address // VALUE: stake/types.Validator func GetValidatorKey(ownerAddr sdk.AccAddress) []byte { return append(ValidatorsKey, ownerAddr.Bytes()...) } -// get the key for the validator with pubkey. +// gets the key for the validator with pubkey // VALUE: validator owner address ([]byte) func GetValidatorByPubKeyIndexKey(pubkey crypto.PubKey) []byte { return append(ValidatorsByPubKeyIndexKey, pubkey.Bytes()...) } -// get the key for the current validator group +// gets the key for the current validator group // VALUE: none (key rearrangement with GetValKeyFromValBondedIndexKey) func GetValidatorsBondedIndexKey(ownerAddr sdk.AccAddress) []byte { return append(ValidatorsBondedIndexKey, ownerAddr.Bytes()...) @@ -57,8 +57,9 @@ func GetAddressFromValBondedIndexKey(IndexKey []byte) []byte { return IndexKey[1:] // remove prefix bytes } -// get the validator by power index. power index is the key used in the power-store, -// and represents the relative power ranking of the validator. +// get the validator by power index. +// Power index is the key used in the power-store, and represents the relative +// power ranking of the validator. // VALUE: validator owner address ([]byte) func GetValidatorsByPowerIndexKey(validator types.Validator, pool types.Pool) []byte { // NOTE the address doesn't need to be stored because counter bytes must always be different @@ -93,29 +94,29 @@ func getValidatorPowerRank(validator types.Validator, pool types.Pool) []byte { counterBytes...) } -// get the key for the accumulated update validators. +// get the key for the accumulated update validators // VALUE: abci.Validator // note records using these keys should never persist between blocks func GetTendermintUpdatesKey(ownerAddr sdk.AccAddress) []byte { return append(TendermintUpdatesKey, ownerAddr.Bytes()...) } -//________________________________________________________________________________ +//______________________________________________________________________________ -// get the key for delegator bond with validator. +// gets the key for delegator bond with validator // VALUE: stake/types.Delegation func GetDelegationKey(delegatorAddr, validatorAddr sdk.AccAddress) []byte { return append(GetDelegationsKey(delegatorAddr), validatorAddr.Bytes()...) } -// get the prefix for a delegator for all validators +// gets the prefix for a delegator for all validators func GetDelegationsKey(delegatorAddr sdk.AccAddress) []byte { return append(DelegationKey, delegatorAddr.Bytes()...) } -//________________________________________________________________________________ +//______________________________________________________________________________ -// get the key for an unbonding delegation by delegator and validator addr. +// gets the key for an unbonding delegation by delegator and validator addr // VALUE: stake/types.UnbondingDelegation func GetUBDKey(delegatorAddr, validatorAddr sdk.AccAddress) []byte { return append( @@ -123,13 +124,13 @@ func GetUBDKey(delegatorAddr, validatorAddr sdk.AccAddress) []byte { validatorAddr.Bytes()...) } -// get the index-key for an unbonding delegation, stored by validator-index +// gets the index-key for an unbonding delegation, stored by validator-index // VALUE: none (key rearrangement used) func GetUBDByValIndexKey(delegatorAddr, validatorAddr sdk.AccAddress) []byte { return append(GetUBDsByValIndexKey(validatorAddr), delegatorAddr.Bytes()...) } -// rearrange the ValIndexKey to get the UBDKey +// rearranges the ValIndexKey to get the UBDKey func GetUBDKeyFromValIndexKey(IndexKey []byte) []byte { addrs := IndexKey[1:] // remove prefix bytes if len(addrs) != 2*sdk.AddrLen { @@ -142,19 +143,19 @@ func GetUBDKeyFromValIndexKey(IndexKey []byte) []byte { //______________ -// get the prefix for all unbonding delegations from a delegator +// gets the prefix for all unbonding delegations from a delegator func GetUBDsKey(delegatorAddr sdk.AccAddress) []byte { return append(UnbondingDelegationKey, delegatorAddr.Bytes()...) } -// get the prefix keyspace for the indexes of unbonding delegations for a validator +// gets the prefix keyspace for the indexes of unbonding delegations for a validator func GetUBDsByValIndexKey(validatorAddr sdk.AccAddress) []byte { return append(UnbondingDelegationByValIndexKey, validatorAddr.Bytes()...) } //________________________________________________________________________________ -// get the key for a redelegation +// gets the key for a redelegation // VALUE: stake/types.RedelegationKey func GetREDKey(delegatorAddr, validatorSrcAddr, validatorDstAddr sdk.AccAddress) []byte { return append(append( @@ -163,7 +164,7 @@ func GetREDKey(delegatorAddr, validatorSrcAddr, validatorDstAddr sdk.AccAddress) validatorDstAddr.Bytes()...) } -// get the index-key for a redelegation, stored by source-validator-index +// gets the index-key for a redelegation, stored by source-validator-index // VALUE: none (key rearrangement used) func GetREDByValSrcIndexKey(delegatorAddr, validatorSrcAddr, validatorDstAddr sdk.AccAddress) []byte { return append(append( @@ -172,7 +173,7 @@ func GetREDByValSrcIndexKey(delegatorAddr, validatorSrcAddr, validatorDstAddr sd validatorDstAddr.Bytes()...) } -// get the index-key for a redelegation, stored by destination-validator-index +// gets the index-key for a redelegation, stored by destination-validator-index // VALUE: none (key rearrangement used) func GetREDByValDstIndexKey(delegatorAddr, validatorSrcAddr, validatorDstAddr sdk.AccAddress) []byte { return append(append( @@ -181,7 +182,7 @@ func GetREDByValDstIndexKey(delegatorAddr, validatorSrcAddr, validatorDstAddr sd validatorSrcAddr.Bytes()...) } -// rearrange the ValSrcIndexKey to get the REDKey +// rearranges the ValSrcIndexKey to get the REDKey func GetREDKeyFromValSrcIndexKey(IndexKey []byte) []byte { addrs := IndexKey[1:] // remove prefix bytes if len(addrs) != 3*sdk.AddrLen { @@ -194,7 +195,7 @@ func GetREDKeyFromValSrcIndexKey(IndexKey []byte) []byte { return GetREDKey(delAddr, valSrcAddr, valDstAddr) } -// rearrange the ValDstIndexKey to get the REDKey +// rearranges the ValDstIndexKey to get the REDKey func GetREDKeyFromValDstIndexKey(IndexKey []byte) []byte { addrs := IndexKey[1:] // remove prefix bytes if len(addrs) != 3*sdk.AddrLen { @@ -208,22 +209,22 @@ func GetREDKeyFromValDstIndexKey(IndexKey []byte) []byte { //______________ -// get the prefix keyspace for redelegations from a delegator +// gets the prefix keyspace for redelegations from a delegator func GetREDsKey(delegatorAddr sdk.AccAddress) []byte { return append(RedelegationKey, delegatorAddr.Bytes()...) } -// get the prefix keyspace for all redelegations redelegating away from a source validator +// gets the prefix keyspace for all redelegations redelegating away from a source validator func GetREDsFromValSrcIndexKey(validatorSrcAddr sdk.AccAddress) []byte { return append(RedelegationByValSrcIndexKey, validatorSrcAddr.Bytes()...) } -// get the prefix keyspace for all redelegations redelegating towards a destination validator +// gets the prefix keyspace for all redelegations redelegating towards a destination validator func GetREDsToValDstIndexKey(validatorDstAddr sdk.AccAddress) []byte { return append(RedelegationByValDstIndexKey, validatorDstAddr.Bytes()...) } -// get the prefix keyspace for all redelegations redelegating towards a destination validator +// gets the prefix keyspace for all redelegations redelegating towards a destination validator // from a particular delegator func GetREDsByDelToValDstIndexKey(delegatorAddr, validatorDstAddr sdk.AccAddress) []byte { return append(