From 5f409ce83247cce37ef34adeff2503f11a1903ee Mon Sep 17 00:00:00 2001 From: Fabian Date: Wed, 6 Jun 2018 06:53:04 +0200 Subject: [PATCH] Merge PR #1086: Bech32Cosmos output/input for the LCD * refactored bech32ization * updated keys endpoints for bech32 * bech32 for sending and querying * trying to change output of validator addresses * fixed validator output * linted * fixed merge conflict * added bech32 to staking endpoints * removed some logging statements * added GetAccPubKeyBech32Cosmos * fixed cli tests * updated swagger * merged standard bech32 change * renamed bech32cosmos to bech32 * bech32ify json output for key add * readded changelog * fixed changelog merge issue * Update CHANGELOG.md --- CHANGELOG.md | 4 ++ client/keys/add.go | 11 ++--- client/keys/list.go | 9 ++-- client/keys/show.go | 8 +++- client/keys/utils.go | 64 +++++++++++++++++------------ client/lcd/lcd_test.go | 69 +++++++++++++++++-------------- client/rpc/validators.go | 50 ++++++++++++++++++++++- cmd/gaia/cli_test/cli_test.go | 8 +++- docs/sdk/lcd-rest-api.yaml | 77 ++++++++++++++++++----------------- types/account.go | 17 +++++++- x/auth/client/rest/query.go | 8 ++-- x/bank/client/rest/sendtx.go | 11 ++++- x/ibc/client/rest/transfer.go | 11 ++++- x/stake/client/rest/query.go | 75 ++++++++++++++++++++++++++++++---- x/stake/client/rest/tx.go | 64 ++++++++++++++++++++++++----- 15 files changed, 348 insertions(+), 138 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index cf2ac21b03..731f3c14a3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,9 @@ # Changelog +BREAKING CHANGES + +* [lcd] Switch to bech32 for addresses on all human readable inputs and outputs + ## 0.18.0 _2018-06-05_ diff --git a/client/keys/add.go b/client/keys/add.go index da368a3a63..7ad9474cef 100644 --- a/client/keys/add.go +++ b/client/keys/add.go @@ -102,12 +102,6 @@ func runAddCmd(cmd *cobra.Command, args []string) error { return nil } -// addOutput lets us json format the data -type addOutput struct { - Key keys.Info `json:"key"` - Seed string `json:"seed"` -} - func printCreate(info keys.Info, seed string) { output := viper.Get(cli.OutputFlag) switch output { @@ -121,7 +115,10 @@ func printCreate(info keys.Info, seed string) { fmt.Println(seed) } case "json": - out := addOutput{Key: info} + out, err := Bech32KeyOutput(info) + if err != nil { + panic(err) + } if !viper.GetBool(flagNoBackup) { out.Seed = seed } diff --git a/client/keys/list.go b/client/keys/list.go index 9af511e5cd..22f163f1d8 100644 --- a/client/keys/list.go +++ b/client/keys/list.go @@ -4,7 +4,6 @@ import ( "encoding/json" "net/http" - sdk "github.com/cosmos/cosmos-sdk/types" "github.com/spf13/cobra" ) @@ -54,9 +53,11 @@ func QueryKeysRequestHandler(w http.ResponseWriter, r *http.Request) { w.Write([]byte("[]")) return } - keysOutput := make([]KeyOutput, len(infos)) - for i, info := range infos { - keysOutput[i] = KeyOutput{Name: info.Name, Address: sdk.Address(info.PubKey.Address().Bytes())} + keysOutput, err := Bech32KeysOutput(infos) + if err != nil { + w.WriteHeader(500) + w.Write([]byte(err.Error())) + return } output, err := json.MarshalIndent(keysOutput, "", " ") if err != nil { diff --git a/client/keys/show.go b/client/keys/show.go index c7be9cc9db..9051aba160 100644 --- a/client/keys/show.go +++ b/client/keys/show.go @@ -4,7 +4,6 @@ import ( "encoding/json" "net/http" - sdk "github.com/cosmos/cosmos-sdk/types" "github.com/gorilla/mux" keys "github.com/tendermint/go-crypto/keys" @@ -51,7 +50,12 @@ func GetKeyRequestHandler(w http.ResponseWriter, r *http.Request) { return } - keyOutput := KeyOutput{Name: info.Name, Address: sdk.Address(info.PubKey.Address())} + keyOutput, err := Bech32KeyOutput(info) + if err != nil { + w.WriteHeader(500) + w.Write([]byte(err.Error())) + return + } output, err := json.MarshalIndent(keyOutput, "", " ") if err != nil { w.WriteHeader(500) diff --git a/client/keys/utils.go b/client/keys/utils.go index 1c1fd0979a..d810dfa1f4 100644 --- a/client/keys/utils.go +++ b/client/keys/utils.go @@ -6,7 +6,6 @@ import ( "github.com/spf13/viper" - crypto "github.com/tendermint/go-crypto" keys "github.com/tendermint/go-crypto/keys" "github.com/tendermint/tmlibs/cli" dbm "github.com/tendermint/tmlibs/db" @@ -47,29 +46,47 @@ func SetKeyBase(kb keys.Keybase) { // used for outputting keys.Info over REST type KeyOutput struct { - Name string `json:"name"` - Address sdk.Address `json:"address"` - PubKey crypto.PubKey `json:"pub_key"` + Name string `json:"name"` + Address string `json:"address"` + PubKey string `json:"pub_key"` + Seed string `json:"seed,omitempty"` } -func NewKeyOutput(info keys.Info) KeyOutput { - return KeyOutput{ - Name: info.Name, - Address: sdk.Address(info.PubKey.Address().Bytes()), - PubKey: info.PubKey, - } -} - -func NewKeyOutputs(infos []keys.Info) []KeyOutput { +// create a list of KeyOutput in bech32 format +func Bech32KeysOutput(infos []keys.Info) ([]KeyOutput, error) { kos := make([]KeyOutput, len(infos)) for i, info := range infos { - kos[i] = NewKeyOutput(info) + ko, err := Bech32KeyOutput(info) + if err != nil { + return nil, err + } + kos[i] = ko } - return kos + return kos, nil +} + +// create a KeyOutput in bech32 format +func Bech32KeyOutput(info keys.Info) (KeyOutput, error) { + bechAccount, err := sdk.Bech32ifyAcc(sdk.Address(info.PubKey.Address().Bytes())) + if err != nil { + return KeyOutput{}, err + } + bechPubKey, err := sdk.Bech32ifyAccPub(info.PubKey) + if err != nil { + return KeyOutput{}, err + } + return KeyOutput{ + Name: info.Name, + Address: bechAccount, + PubKey: bechPubKey, + }, nil } func printInfo(info keys.Info) { - ko := NewKeyOutput(info) + ko, err := Bech32KeyOutput(info) + if err != nil { + panic(err) + } switch viper.Get(cli.OutputFlag) { case "text": fmt.Printf("NAME:\tADDRESS:\t\t\t\t\t\tPUBKEY:\n") @@ -84,7 +101,10 @@ func printInfo(info keys.Info) { } func printInfos(infos []keys.Info) { - kos := NewKeyOutputs(infos) + kos, err := Bech32KeysOutput(infos) + if err != nil { + panic(err) + } switch viper.Get(cli.OutputFlag) { case "text": fmt.Printf("NAME:\tADDRESS:\t\t\t\t\t\tPUBKEY:\n") @@ -101,13 +121,5 @@ func printInfos(infos []keys.Info) { } func printKeyOutput(ko KeyOutput) { - bechAccount, err := sdk.Bech32ifyAcc(ko.Address) - if err != nil { - panic(err) - } - bechPubKey, err := sdk.Bech32ifyAccPub(ko.PubKey) - if err != nil { - panic(err) - } - fmt.Printf("%s\t%s\t%s\n", ko.Name, bechAccount, bechPubKey) + fmt.Printf("%s\t%s\t%s\n", ko.Name, ko.Address, ko.PubKey) } diff --git a/client/lcd/lcd_test.go b/client/lcd/lcd_test.go index 7a9cdbc254..47df51e7e3 100644 --- a/client/lcd/lcd_test.go +++ b/client/lcd/lcd_test.go @@ -33,6 +33,7 @@ import ( client "github.com/cosmos/cosmos-sdk/client" keys "github.com/cosmos/cosmos-sdk/client/keys" + rpc "github.com/cosmos/cosmos-sdk/client/rpc" gapp "github.com/cosmos/cosmos-sdk/cmd/gaia/app" "github.com/cosmos/cosmos-sdk/server" tests "github.com/cosmos/cosmos-sdk/tests" @@ -40,14 +41,17 @@ import ( "github.com/cosmos/cosmos-sdk/wire" "github.com/cosmos/cosmos-sdk/x/auth" "github.com/cosmos/cosmos-sdk/x/stake" + stakerest "github.com/cosmos/cosmos-sdk/x/stake/client/rest" ) var ( coinDenom = "steak" coinAmount = int64(10000000) - validatorAddr1 = "" - validatorAddr2 = "" + validatorAddr1Hx = "" + validatorAddr2Hx = "" + validatorAddr1 = "" + validatorAddr2 = "" // XXX bad globals name = "test" @@ -99,13 +103,13 @@ func TestKeys(t *testing.T) { err = cdc.UnmarshalJSON([]byte(body), &m) require.Nil(t, err) - sendAddrAcc, _ := sdk.GetAccAddressHex(sendAddr) addrAcc, _ := sdk.GetAccAddressHex(addr) + addrBech32, _ := sdk.Bech32ifyAcc(addrAcc) - assert.Equal(t, m[0].Name, name, "Did not serve keys name correctly") - assert.Equal(t, m[0].Address, sendAddrAcc, "Did not serve keys Address correctly") - assert.Equal(t, m[1].Name, newName, "Did not serve keys name correctly") - assert.Equal(t, m[1].Address, addrAcc, "Did not serve keys Address correctly") + assert.Equal(t, name, m[0].Name, "Did not serve keys name correctly") + assert.Equal(t, sendAddr, m[0].Address, "Did not serve keys Address correctly") + assert.Equal(t, newName, m[1].Name, "Did not serve keys name correctly") + assert.Equal(t, addrBech32, m[1].Address, "Did not serve keys Address correctly") // select key keyEndpoint := fmt.Sprintf("/keys/%s", newName) @@ -116,7 +120,7 @@ func TestKeys(t *testing.T) { require.Nil(t, err) assert.Equal(t, newName, m2.Name, "Did not serve keys name correctly") - assert.Equal(t, addrAcc, m2.Address, "Did not serve keys Address correctly") + assert.Equal(t, addrBech32, m2.Address, "Did not serve keys Address correctly") // update key jsonStr = []byte(fmt.Sprintf(`{"old_password":"%s", "new_password":"12345678901"}`, newPassword)) @@ -198,7 +202,7 @@ func TestBlock(t *testing.T) { func TestValidators(t *testing.T) { - var resultVals ctypes.ResultValidators + var resultVals rpc.ResultValidatorsOutput res, body := request(t, port, "GET", "/validatorsets/latest", nil) require.Equal(t, http.StatusOK, res.StatusCode, body) @@ -206,7 +210,10 @@ func TestValidators(t *testing.T) { err := cdc.UnmarshalJSON([]byte(body), &resultVals) require.Nil(t, err, "Couldn't parse validatorset") - assert.NotEqual(t, ctypes.ResultValidators{}, resultVals) + assert.NotEqual(t, rpc.ResultValidatorsOutput{}, resultVals) + + assert.Contains(t, resultVals.Validators[0].Address, "cosmosvaladdr") + assert.Contains(t, resultVals.Validators[0].PubKey, "cosmosvalpub") // -- @@ -216,7 +223,7 @@ func TestValidators(t *testing.T) { err = cdc.UnmarshalJSON([]byte(body), &resultVals) require.Nil(t, err, "Couldn't parse validatorset") - assert.NotEqual(t, ctypes.ResultValidators{}, resultVals) + assert.NotEqual(t, rpc.ResultValidatorsOutput{}, resultVals) // -- @@ -225,10 +232,11 @@ func TestValidators(t *testing.T) { } func TestCoinSend(t *testing.T) { + bz, _ := hex.DecodeString("8FA6AB57AD6870F6B5B2E57735F38F2F30E73CB6") + someFakeAddr, _ := sdk.Bech32ifyAcc(bz) // query empty - //res, body := request(t, port, "GET", "/accounts/8FA6AB57AD6870F6B5B2E57735F38F2F30E73CB6", nil) - res, body := request(t, port, "GET", "/accounts/8FA6AB57AD6870F6B5B2E57735F38F2F30E73CB6", nil) + res, body := request(t, port, "GET", "/accounts/"+someFakeAddr, nil) require.Equal(t, http.StatusNoContent, res.StatusCode, body) acc := getAccount(t, sendAddr) @@ -323,15 +331,14 @@ func TestValidatorsQuery(t *testing.T) { // make sure all the validators were found (order unknown because sorted by owner addr) foundVal1, foundVal2 := false, false - res1, res2 := hex.EncodeToString(validators[0].Owner), hex.EncodeToString(validators[1].Owner) - if res1 == validatorAddr1 || res2 == validatorAddr1 { + if validators[0].Owner == validatorAddr1 || validators[1].Owner == validatorAddr1 { foundVal1 = true } - if res1 == validatorAddr2 || res2 == validatorAddr2 { + if validators[0].Owner == validatorAddr2 || validators[1].Owner == validatorAddr2 { foundVal2 = true } - assert.True(t, foundVal1, "validatorAddr1 %v, res1 %v, res2 %v", validatorAddr1, res1, res2) - assert.True(t, foundVal2, "validatorAddr2 %v, res1 %v, res2 %v", validatorAddr2, res1, res2) + assert.True(t, foundVal1, "validatorAddr1 %v, owner1 %v, owner2 %v", validatorAddr1, validators[0].Owner, validators[1].Owner) + assert.True(t, foundVal2, "validatorAddr2 %v, owner1 %v, owner2 %v", validatorAddr2, validators[0].Owner, validators[1].Owner) } func TestBond(t *testing.T) { @@ -418,8 +425,10 @@ func startTMAndLCD() (*nm.Node, net.Listener, error) { pk1 := genDoc.Validators[0].PubKey pk2 := genDoc.Validators[1].PubKey - validatorAddr1 = hex.EncodeToString(pk1.Address()) - validatorAddr2 = hex.EncodeToString(pk2.Address()) + validatorAddr1Hx = hex.EncodeToString(pk1.Address()) + validatorAddr2Hx = hex.EncodeToString(pk2.Address()) + validatorAddr1, _ = sdk.Bech32ifyVal(pk1.Address()) + validatorAddr2, _ = sdk.Bech32ifyVal(pk2.Address()) // NOTE it's bad practice to reuse pk address for the owner address but doing in the // test for simplicity @@ -444,7 +453,8 @@ func startTMAndLCD() (*nm.Node, net.Listener, error) { if err != nil { return nil, nil, err } - sendAddr = info.PubKey.Address().String() // XXX global + sendAddrHex, _ := sdk.GetAccAddressHex(info.PubKey.Address().String()) + sendAddr, _ = sdk.Bech32ifyAcc(sendAddrHex) // XXX global accAuth := auth.NewBaseAccountWithAddress(info.PubKey.Address()) accAuth.Coins = sdk.Coins{{"steak", 100}} acc := gapp.NewGenesisAccount(&accAuth) @@ -548,7 +558,7 @@ func doSend(t *testing.T, port, seed string) (receiveAddr string, resultTx ctype kb := client.MockKeyBase() receiveInfo, _, err := kb.Create("receive_address", "1234567890", cryptoKeys.CryptoAlgo("ed25519")) require.Nil(t, err) - receiveAddr = receiveInfo.PubKey.Address().String() + receiveAddr, _ = sdk.Bech32ifyAcc(receiveInfo.PubKey.Address()) acc := getAccount(t, sendAddr) sequence := acc.GetSequence() @@ -565,12 +575,11 @@ func doSend(t *testing.T, port, seed string) (receiveAddr string, resultTx ctype } func doIBCTransfer(t *testing.T, port, seed string) (resultTx ctypes.ResultBroadcastTxCommit) { - // create receive address kb := client.MockKeyBase() receiveInfo, _, err := kb.Create("receive_address", "1234567890", cryptoKeys.CryptoAlgo("ed25519")) require.Nil(t, err) - receiveAddr := receiveInfo.PubKey.Address().String() + receiveAddr, _ := sdk.Bech32ifyAcc(receiveInfo.PubKey.Address()) // get the account to get the sequence acc := getAccount(t, sendAddr) @@ -609,13 +618,13 @@ func doBond(t *testing.T, port, seed string) (resultTx ctypes.ResultBroadcastTxC "sequence": %d, "delegate": [ { - "delegator_addr": "%x", + "delegator_addr": "%s", "validator_addr": "%s", "bond": { "denom": "%s", "amount": 10 } } ], "unbond": [] - }`, name, password, sequence, acc.GetAddress(), validatorAddr1, coinDenom)) + }`, name, password, sequence, sendAddr, validatorAddr1, coinDenom)) res, body := request(t, port, "POST", "/stake/delegations", jsonStr) require.Equal(t, http.StatusOK, res.StatusCode, body) @@ -639,12 +648,12 @@ func doUnbond(t *testing.T, port, seed string) (resultTx ctypes.ResultBroadcastT "bond": [], "unbond": [ { - "delegator_addr": "%x", + "delegator_addr": "%s", "validator_addr": "%s", "shares": "1" } ] - }`, name, password, sequence, acc.GetAddress(), validatorAddr1)) + }`, name, password, sequence, sendAddr, validatorAddr1)) res, body := request(t, port, "POST", "/stake/delegations", jsonStr) require.Equal(t, http.StatusOK, res.StatusCode, body) @@ -655,11 +664,11 @@ func doUnbond(t *testing.T, port, seed string) (resultTx ctypes.ResultBroadcastT return results[0] } -func getValidators(t *testing.T) []stake.Validator { +func getValidators(t *testing.T) []stakerest.StakeValidatorOutput { // get the account to get the sequence res, body := request(t, port, "GET", "/stake/validators", nil) require.Equal(t, http.StatusOK, res.StatusCode, body) - var validators stake.Validators + var validators []stakerest.StakeValidatorOutput err := cdc.UnmarshalJSON([]byte(body), &validators) require.Nil(t, err) return validators diff --git a/client/rpc/validators.go b/client/rpc/validators.go index d1aa6c9c18..f8835d7377 100644 --- a/client/rpc/validators.go +++ b/client/rpc/validators.go @@ -10,6 +10,8 @@ import ( "github.com/cosmos/cosmos-sdk/client" "github.com/cosmos/cosmos-sdk/client/context" + sdk "github.com/cosmos/cosmos-sdk/types" + tmtypes "github.com/tendermint/tendermint/types" ) // TODO these next two functions feel kinda hacky based on their placement @@ -28,6 +30,38 @@ func ValidatorCommand() *cobra.Command { return cmd } +// Validator output in bech32 format +type ValidatorOutput struct { + Address string `json:"address"` // in bech32 + PubKey string `json:"pub_key"` // in bech32 + Accum int64 `json:"accum"` + VotingPower int64 `json:"voting_power"` +} + +// Validators at a certain height output in bech32 format +type ResultValidatorsOutput struct { + BlockHeight int64 `json:"block_height"` + Validators []ValidatorOutput `json:"validators"` +} + +func bech32ValidatorOutput(validator *tmtypes.Validator) (ValidatorOutput, error) { + bechAddress, err := sdk.Bech32ifyVal(validator.Address) + if err != nil { + return ValidatorOutput{}, err + } + bechValPubkey, err := sdk.Bech32ifyValPub(validator.PubKey) + if err != nil { + return ValidatorOutput{}, err + } + + return ValidatorOutput{ + Address: bechAddress, + PubKey: bechValPubkey, + Accum: validator.Accum, + VotingPower: validator.VotingPower, + }, nil +} + func getValidators(ctx context.CoreContext, height *int64) ([]byte, error) { // get the node node, err := ctx.GetNode() @@ -35,12 +69,23 @@ func getValidators(ctx context.CoreContext, height *int64) ([]byte, error) { return nil, err } - res, err := node.Validators(height) + validatorsRes, err := node.Validators(height) if err != nil { return nil, err } - output, err := cdc.MarshalJSON(res) + outputValidatorsRes := ResultValidatorsOutput{ + BlockHeight: validatorsRes.BlockHeight, + Validators: make([]ValidatorOutput, len(validatorsRes.Validators)), + } + for i := 0; i < len(validatorsRes.Validators); i++ { + outputValidatorsRes.Validators[i], err = bech32ValidatorOutput(validatorsRes.Validators[i]) + if err != nil { + return nil, err + } + } + + output, err := cdc.MarshalJSON(outputValidatorsRes) if err != nil { return nil, err } @@ -96,6 +141,7 @@ func ValidatorSetRequestHandlerFn(ctx context.CoreContext) http.HandlerFunc { w.Write([]byte(err.Error())) return } + w.Write(output) } } diff --git a/cmd/gaia/cli_test/cli_test.go b/cmd/gaia/cli_test/cli_test.go index ae46a623cd..1868452a64 100644 --- a/cmd/gaia/cli_test/cli_test.go +++ b/cmd/gaia/cli_test/cli_test.go @@ -170,7 +170,13 @@ func executeGetAddrPK(t *testing.T, cmdStr string) (sdk.Address, crypto.PubKey) var ko keys.KeyOutput keys.UnmarshalJSON([]byte(out), &ko) - return ko.Address, ko.PubKey + address, err := sdk.GetAccAddressBech32(ko.Address) + require.NoError(t, err) + + pk, err := sdk.GetAccPubKeyBech32(ko.PubKey) + require.NoError(t, err) + + return address, pk } func executeGetAccount(t *testing.T, cmdStr string) auth.BaseAccount { diff --git a/docs/sdk/lcd-rest-api.yaml b/docs/sdk/lcd-rest-api.yaml index 408b2a792b..3008d7f73c 100644 --- a/docs/sdk/lcd-rest-api.yaml +++ b/docs/sdk/lcd-rest-api.yaml @@ -102,7 +102,7 @@ paths: - application/json responses: 200: - description: 12 word Seed + description: 16 word Seed schema: type: string /keys/{name}: @@ -204,7 +204,7 @@ paths: parameters: - in: path name: address - description: Account address + description: Account address in bech32 format required: true type: string get: @@ -222,7 +222,7 @@ paths: parameters: - in: path name: address - description: Account address + description: Account address in bech32 format required: true type: string post: @@ -255,18 +255,6 @@ paths: description: Tx was send and will probably be added to the next block 400: description: The Tx was malformated - /accounts/{address}/nonce: - parameters: - - in: path - name: address - description: Account address - required: true - type: string - get: - summary: Get the nonce for a certain account - responses: - 200: - description: Plaintext nonce i.e. "4" defaults to "0" /blocks/latest: get: summary: Get the latest block @@ -304,9 +292,14 @@ paths: 200: description: The validator set at the latest block height schema: - type: array - items: - $ref: "#/definitions/Delegate" + type: object + properties: + block_height: + type: number + validators: + type: array + items: + $ref: "#/definitions/Validator" /validatorsets/{height}: parameters: - in: path @@ -322,9 +315,14 @@ paths: 200: description: The validator set at a specific block height schema: - type: array - items: - $ref: "#/definitions/Delegate" + type: object + properties: + block_height: + type: number + validators: + type: array + items: + $ref: "#/definitions/Validator" 404: description: Block at height not available # /txs: @@ -549,7 +547,20 @@ paths: definitions: Address: type: string - example: DF096FDE8D380FA5B2AD20DB2962C82DDEA1ED9B + description: bech32 encoded addres + example: cosmosaccaddr:zgnkwr7eyyv643dllwfpdwensmgdtz89yu73zq + ValidatorAddress: + type: string + description: bech32 encoded addres + example: cosmosvaladdr:zgnkwr7eyyv643dllwfpdwensmgdtz89yu73zq + PubKey: + type: string + description: bech32 encoded public key + example: cosmosaccpub:zgnkwr7eyyv643dllwfpdwensmgdtz89yu73zq + ValidatorPubKey: + type: string + description: bech32 encoded public key + example: cosmosvalpub:zgnkwr7eyyv643dllwfpdwensmgdtz89yu73zq Coins: type: object properties: @@ -652,16 +663,6 @@ definitions: example: 81B11E717789600CC192B26F452A983DF13B985EE75ABD9DD9E68D7BA007A958 Pubkey: $ref: "#/definitions/PubKey" - PubKey: - type: object - properties: - type: - type: string - enum: - - ed25519 - data: - type: string - example: 81B11E717789600CC192B26F452A983DF13B985EE75ABD9DD9E68D7BA007A958 Account: type: object properties: @@ -753,17 +754,19 @@ definitions: type: array items: type: object - Delegate: + Validator: type: object properties: + address: + $ref: '#/definitions/ValidatorAddress' pub_key: - $ref: "#/definitions/PubKey" + $ref: "#/definitions/ValidatorPubKey" power: type: number example: 1000 - name: - type: string - example: "159.89.3.34" + accum: + type: number + example: 1000 # Added by API Auto Mocking Plugin host: virtserver.swaggerhub.com basePath: /faboweb1/Cosmos-LCD-2/1.0.0 diff --git a/types/account.go b/types/account.go index ad0c58231f..00d180a1c7 100644 --- a/types/account.go +++ b/types/account.go @@ -32,7 +32,7 @@ func Bech32ifyAccPub(pub crypto.PubKey) (string, error) { } // Bech32ifyVal returns the bech32 encoded string for a validator address -func bech32ifyVal(addr Address) (string, error) { +func Bech32ifyVal(addr Address) (string, error) { return bech32.ConvertAndEncode(Bech32PrefixValAddr, addr.Bytes()) } @@ -62,6 +62,21 @@ func GetAccAddressBech32(address string) (addr Address, err error) { return Address(bz), nil } +// create a Pubkey from a string +func GetAccPubKeyBech32(address string) (pk crypto.PubKey, err error) { + bz, err := getFromBech32(address, Bech32PrefixAccPub) + if err != nil { + return nil, err + } + + pk, err = crypto.PubKeyFromBytes(bz) + if err != nil { + return nil, err + } + + return pk, nil +} + // create an Address from a hex string func GetValAddressHex(address string) (addr Address, err error) { if len(address) == 0 { diff --git a/x/auth/client/rest/query.go b/x/auth/client/rest/query.go index a60ce5cdb2..bcae59c203 100644 --- a/x/auth/client/rest/query.go +++ b/x/auth/client/rest/query.go @@ -1,7 +1,6 @@ package rest import ( - "encoding/hex" "fmt" "net/http" @@ -26,17 +25,16 @@ func RegisterRoutes(ctx context.CoreContext, r *mux.Router, cdc *wire.Codec, sto func QueryAccountRequestHandlerFn(storeName string, cdc *wire.Codec, decoder auth.AccountDecoder, ctx context.CoreContext) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { vars := mux.Vars(r) - addr := vars["address"] + bech32addr := vars["address"] - bz, err := hex.DecodeString(addr) + addr, err := sdk.GetAccAddressBech32(bech32addr) if err != nil { w.WriteHeader(http.StatusBadRequest) w.Write([]byte(err.Error())) return } - key := sdk.Address(bz) - res, err := ctx.Query(key, storeName) + res, err := ctx.Query(addr, storeName) if err != nil { w.WriteHeader(http.StatusInternalServerError) w.Write([]byte(fmt.Sprintf("Could't query account. Error: %s", err.Error()))) diff --git a/x/bank/client/rest/sendtx.go b/x/bank/client/rest/sendtx.go index defed3f144..83ab3b843f 100644 --- a/x/bank/client/rest/sendtx.go +++ b/x/bank/client/rest/sendtx.go @@ -41,7 +41,14 @@ func SendRequestHandlerFn(cdc *wire.Codec, kb keys.Keybase, ctx context.CoreCont return func(w http.ResponseWriter, r *http.Request) { // collect data vars := mux.Vars(r) - address := vars["address"] + bech32addr := vars["address"] + + address, err := sdk.GetAccAddressBech32(bech32addr) + if err != nil { + w.WriteHeader(http.StatusBadRequest) + w.Write([]byte(err.Error())) + return + } var m sendBody body, err := ioutil.ReadAll(r.Body) @@ -64,7 +71,7 @@ func SendRequestHandlerFn(cdc *wire.Codec, kb keys.Keybase, ctx context.CoreCont return } - to, err := sdk.GetAccAddressHex(address) + to, err := sdk.GetAccAddressHex(address.String()) if err != nil { w.WriteHeader(http.StatusBadRequest) w.Write([]byte(err.Error())) diff --git a/x/ibc/client/rest/transfer.go b/x/ibc/client/rest/transfer.go index 48a29ee801..d897c6e4f6 100644 --- a/x/ibc/client/rest/transfer.go +++ b/x/ibc/client/rest/transfer.go @@ -35,7 +35,14 @@ func TransferRequestHandlerFn(cdc *wire.Codec, kb keys.Keybase, ctx context.Core // collect data vars := mux.Vars(r) destChainID := vars["destchain"] - address := vars["address"] + bech32addr := vars["address"] + + address, err := sdk.GetAccAddressBech32(bech32addr) + if err != nil { + w.WriteHeader(http.StatusBadRequest) + w.Write([]byte(err.Error())) + return + } var m transferBody body, err := ioutil.ReadAll(r.Body) @@ -58,7 +65,7 @@ func TransferRequestHandlerFn(cdc *wire.Codec, kb keys.Keybase, ctx context.Core return } - bz, err := hex.DecodeString(address) + bz, err := hex.DecodeString(address.String()) if err != nil { w.WriteHeader(http.StatusBadRequest) w.Write([]byte(err.Error())) diff --git a/x/stake/client/rest/query.go b/x/stake/client/rest/query.go index 3e439c2b44..0da3260cb8 100644 --- a/x/stake/client/rest/query.go +++ b/x/stake/client/rest/query.go @@ -1,7 +1,6 @@ package rest import ( - "encoding/hex" "fmt" "net/http" @@ -30,24 +29,22 @@ func bondingStatusHandlerFn(ctx context.CoreContext, storeName string, cdc *wire // read parameters vars := mux.Vars(r) - delegator := vars["delegator"] - validator := vars["validator"] + bech32delegator := vars["delegator"] + bech32validator := vars["validator"] - bz, err := hex.DecodeString(delegator) + delegatorAddr, err := sdk.GetAccAddressBech32(bech32delegator) if err != nil { w.WriteHeader(http.StatusBadRequest) w.Write([]byte(err.Error())) return } - delegatorAddr := sdk.Address(bz) - bz, err = hex.DecodeString(validator) + validatorAddr, err := sdk.GetValAddressBech32(bech32validator) if err != nil { w.WriteHeader(http.StatusBadRequest) w.Write([]byte(err.Error())) return } - validatorAddr := sdk.Address(bz) key := stake.GetDelegationKey(delegatorAddr, validatorAddr, cdc) @@ -83,6 +80,62 @@ func bondingStatusHandlerFn(ctx context.CoreContext, storeName string, cdc *wire } } +// TODO inherit from Validator +type StakeValidatorOutput struct { + Owner string `json:"owner"` // in bech32 + PubKey string `json:"pub_key"` // in bech32 + Revoked bool `json:"revoked"` // has the validator been revoked from bonded status? + + PoolShares stake.PoolShares `json:"pool_shares"` // total shares for tokens held in the pool + DelegatorShares sdk.Rat `json:"delegator_shares"` // total shares issued to a validator's delegators + + Description stake.Description `json:"description"` // description terms for the validator + BondHeight int64 `json:"bond_height"` // earliest height as a bonded validator + BondIntraTxCounter int16 `json:"bond_intra_tx_counter"` // block-local tx index of validator change + ProposerRewardPool sdk.Coins `json:"proposer_reward_pool"` // XXX reward pool collected from being the proposer + + Commission sdk.Rat `json:"commission"` // XXX the commission rate of fees charged to any delegators + CommissionMax sdk.Rat `json:"commission_max"` // XXX maximum commission rate which this validator can ever charge + CommissionChangeRate sdk.Rat `json:"commission_change_rate"` // XXX maximum daily increase of the validator commission + CommissionChangeToday sdk.Rat `json:"commission_change_today"` // XXX commission rate change today, reset each day (UTC time) + + // fee related + PrevBondedShares sdk.Rat `json:"prev_bonded_shares"` // total shares of a global hold pools +} + +func bech32StakeValidatorOutput(validator stake.Validator) (StakeValidatorOutput, error) { + bechOwner, err := sdk.Bech32ifyVal(validator.Owner) + if err != nil { + return StakeValidatorOutput{}, err + } + bechValPubkey, err := sdk.Bech32ifyValPub(validator.PubKey) + if err != nil { + return StakeValidatorOutput{}, err + } + + return StakeValidatorOutput{ + Owner: bechOwner, + PubKey: bechValPubkey, + Revoked: validator.Revoked, + + PoolShares: validator.PoolShares, + DelegatorShares: validator.DelegatorShares, + + Description: validator.Description, + BondHeight: validator.BondHeight, + BondIntraTxCounter: validator.BondIntraTxCounter, + ProposerRewardPool: validator.ProposerRewardPool, + + Commission: validator.Commission, + CommissionMax: validator.CommissionMax, + CommissionChangeRate: validator.CommissionChangeRate, + CommissionChangeToday: validator.CommissionChangeToday, + + PrevBondedShares: validator.PrevBondedShares, + }, nil +} + +// TODO bech32 // http request handler to query list of validators func validatorsHandlerFn(ctx context.CoreContext, storeName string, cdc *wire.Codec) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { @@ -100,16 +153,20 @@ func validatorsHandlerFn(ctx context.CoreContext, storeName string, cdc *wire.Co } // parse out the validators - validators := make([]stake.Validator, len(kvs)) + validators := make([]StakeValidatorOutput, len(kvs)) for i, kv := range kvs { var validator stake.Validator + var bech32Validator StakeValidatorOutput err = cdc.UnmarshalBinary(kv.Value, &validator) + if err == nil { + bech32Validator, err = bech32StakeValidatorOutput(validator) + } if err != nil { w.WriteHeader(http.StatusInternalServerError) w.Write([]byte(fmt.Sprintf("Couldn't decode validator. Error: %s", err.Error()))) return } - validators[i] = validator + validators[i] = bech32Validator } output, err := cdc.MarshalJSON(validators) diff --git a/x/stake/client/rest/tx.go b/x/stake/client/rest/tx.go index eaf206bf6b..33fe73c699 100644 --- a/x/stake/client/rest/tx.go +++ b/x/stake/client/rest/tx.go @@ -3,6 +3,7 @@ package rest import ( "bytes" "encoding/json" + "fmt" "io/ioutil" "net/http" @@ -23,13 +24,24 @@ func registerTxRoutes(ctx context.CoreContext, r *mux.Router, cdc *wire.Codec, k ).Methods("POST") } +type msgDelegateInput struct { + DelegatorAddr string `json:"delegator_addr"` // in bech32 + ValidatorAddr string `json:"validator_addr"` // in bech32 + Bond sdk.Coin `json:"bond"` +} +type msgUnbondInput struct { + DelegatorAddr string `json:"delegator_addr"` // in bech32 + ValidatorAddr string `json:"validator_addr"` // in bech32 + Shares string `json:"shares"` +} + type editDelegationsBody struct { - LocalAccountName string `json:"name"` - Password string `json:"password"` - ChainID string `json:"chain_id"` - Sequence int64 `json:"sequence"` - Delegate []stake.MsgDelegate `json:"delegate"` - Unbond []stake.MsgUnbond `json:"unbond"` + LocalAccountName string `json:"name"` + Password string `json:"password"` + ChainID string `json:"chain_id"` + Sequence int64 `json:"sequence"` + Delegate []msgDelegateInput `json:"delegate"` + Unbond []msgUnbondInput `json:"unbond"` } func editDelegationsRequestHandlerFn(cdc *wire.Codec, kb keys.Keybase, ctx context.CoreContext) http.HandlerFunc { @@ -59,21 +71,53 @@ func editDelegationsRequestHandlerFn(cdc *wire.Codec, kb keys.Keybase, ctx conte messages := make([]sdk.Msg, len(m.Delegate)+len(m.Unbond)) i := 0 for _, msg := range m.Delegate { - if !bytes.Equal(info.Address(), msg.DelegatorAddr) { + delegatorAddr, err := sdk.GetAccAddressBech32(msg.DelegatorAddr) + if err != nil { + w.WriteHeader(http.StatusInternalServerError) + w.Write([]byte(fmt.Sprintf("Couldn't decode delegator. Error: %s", err.Error()))) + return + } + validatorAddr, err := sdk.GetValAddressBech32(msg.ValidatorAddr) + if err != nil { + w.WriteHeader(http.StatusInternalServerError) + w.Write([]byte(fmt.Sprintf("Couldn't decode validator. Error: %s", err.Error()))) + return + } + if !bytes.Equal(info.Address(), delegatorAddr) { w.WriteHeader(http.StatusUnauthorized) w.Write([]byte("Must use own delegator address")) return } - messages[i] = msg + messages[i] = stake.MsgDelegate{ + DelegatorAddr: delegatorAddr, + ValidatorAddr: validatorAddr, + Bond: msg.Bond, + } i++ } for _, msg := range m.Unbond { - if !bytes.Equal(info.Address(), msg.DelegatorAddr) { + delegatorAddr, err := sdk.GetAccAddressBech32(msg.DelegatorAddr) + if err != nil { + w.WriteHeader(http.StatusInternalServerError) + w.Write([]byte(fmt.Sprintf("Couldn't decode delegator. Error: %s", err.Error()))) + return + } + validatorAddr, err := sdk.GetValAddressBech32(msg.ValidatorAddr) + if err != nil { + w.WriteHeader(http.StatusInternalServerError) + w.Write([]byte(fmt.Sprintf("Couldn't decode validator. Error: %s", err.Error()))) + return + } + if !bytes.Equal(info.Address(), delegatorAddr) { w.WriteHeader(http.StatusUnauthorized) w.Write([]byte("Must use own delegator address")) return } - messages[i] = msg + messages[i] = stake.MsgUnbond{ + DelegatorAddr: delegatorAddr, + ValidatorAddr: validatorAddr, + Shares: msg.Shares, + } i++ }