Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement voting right delegation for oracle #148

Merged
merged 4 commits into from
May 28, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion x/bench/oracle_bench_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ func BenchmarkOracleFeedVotePerBlock(b *testing.B) {

for j := 0; j < numOfValidators; j++ {
for d := 0; d < len(denoms); d++ {
voteMsg := oracle.NewMsgPriceFeed(denoms[d], sdk.NewDec(1), addrs[j])
voteMsg := oracle.NewMsgPriceFeed(denoms[d], sdk.NewDec(1), addrs[j], sdk.ValAddress(addrs[j]))

res := h(ctx, voteMsg)
if !res.IsOK() {
Expand Down
6 changes: 3 additions & 3 deletions x/oracle/ballot_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ func TestPBPower(t *testing.T) {
ballotPower := sdk.ZeroInt()

for i := 0; i < len(mockValset.Validators); i++ {
vote := NewPriceVote(sdk.ZeroDec(), assets.MicroSDRDenom, valAccAddrs[i])
vote := NewPriceVote(sdk.ZeroDec(), assets.MicroSDRDenom, sdk.ValAddress(valAccAddrs[i]))
pb = append(pb, vote)

valPower, err := vote.getPower(input.ctx, mockValset)
Expand All @@ -68,7 +68,7 @@ func TestPBPower(t *testing.T) {
require.Equal(t, ballotPower, pb.power(input.ctx, mockValset))

// Mix in a fake validator, the total power should not have changed.
fakeVote := NewPriceVote(sdk.OneDec(), assets.MicroSDRDenom, addrs[0])
fakeVote := NewPriceVote(sdk.OneDec(), assets.MicroSDRDenom, sdk.ValAddress(addrs[0]))
pb = append(pb, fakeVote)
require.Equal(t, ballotPower, pb.power(input.ctx, mockValset))
}
Expand Down Expand Up @@ -125,7 +125,7 @@ func TestPBWeightedMedian(t *testing.T) {
if tc.isValidator[i] {
mockValset.Validators = append(mockValset.Validators, mockVal)
}
vote := NewPriceVote(sdk.NewDecWithPrec(int64(input*base), int64(oracleDecPrecision)), assets.MicroSDRDenom, valAccAddr)
vote := NewPriceVote(sdk.NewDecWithPrec(int64(input*base), int64(oracleDecPrecision)), assets.MicroSDRDenom, sdk.ValAddress(valAccAddr))
pb = append(pb, vote)
}

Expand Down
50 changes: 48 additions & 2 deletions x/oracle/client/cli/query.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,11 @@ package cli

import (
"fmt"
"strings"

"github.com/pkg/errors"
"github.com/terra-project/core/types/assets"
"github.com/terra-project/core/x/oracle"
"strings"

"github.com/cosmos/cosmos-sdk/client/context"
"github.com/cosmos/cosmos-sdk/codec"
Expand Down Expand Up @@ -118,7 +120,7 @@ returns oracle votes submitted by terrad8duyufdshs... for denom uusd
}
}

params := oracle.NewQueryVoteParams(voterAddress, denom)
params := oracle.NewQueryVoteParams(sdk.ValAddress(voterAddress), denom)
bz, err := cdc.MarshalJSON(params)
if err != nil {
return err
Expand Down Expand Up @@ -163,3 +165,47 @@ func GetCmdQueryParams(queryRoute string, cdc *codec.Codec) *cobra.Command {

return cmd
}

// GetCmdQueryFeederDelegation implements the query feeder delegation command
func GetCmdQueryFeederDelegation(queryRoute string, cdc *codec.Codec) *cobra.Command {
cmd := &cobra.Command{
Use: oracle.QueryFeederDelegation,
Short: "Query the account the validator's voting right is delegated to",
Long: strings.TrimSpace(`
Query the account the validator's voting right is delegated to.

$ terracli query oracle feeder-delegation --validator terravaloper1ifji3ifj
`),
RunE: func(cmd *cobra.Command, args []string) error {
cliCtx := context.NewCLIContext().WithCodec(cdc)

valString := viper.GetString(flagValidator)
if len(valString) == 0 {
return fmt.Errorf("--validator flag is required")
}
validator, err := sdk.ValAddressFromBech32(valString)
if err != nil {
return errors.Wrap(err, "invalid validator address")
}

params := oracle.NewQueryFeederDelegationParams(validator)
bz, err := cdc.MarshalJSON(params)
if err != nil {
return err
}

res, err := cliCtx.QueryWithData(fmt.Sprintf("custom/%s/%s", queryRoute, oracle.QueryFeederDelegation), bz)
if err != nil {
return err
}

var delegatee sdk.AccAddress
cdc.MustUnmarshalJSON(res, &delegatee)
return cliCtx.PrintOutput(delegatee)
},
}

cmd.Flags().String(flagValidator, "", "validator to get the delegation for")

return cmd
}
87 changes: 82 additions & 5 deletions x/oracle/client/cli/tx.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ import (
"fmt"
"strings"

"github.com/pkg/errors"

"github.com/terra-project/core/x/oracle"

"github.com/cosmos/cosmos-sdk/client/context"
Expand All @@ -16,9 +18,11 @@ import (
)

const (
flagDenom = "denom"
flagPrice = "price"
flagVoter = "voter"
flagDenom = "denom"
flagPrice = "price"
flagVoter = "voter"
flagValidator = "validator"
flagDelegatee = "delegated"
)

// GetCmdPriceVote will create a send tx and sign it with the given key.
Expand All @@ -31,7 +35,10 @@ Submit an oracle vote for the price of Luna denominated in the input denom.

$ terracli oracle vote --denom "ukrw" --price "8890" --from mykey

where "ukrw" is the denominating currency, and "8890" is the price of micro Luna in micro KRW from the voter's point of view.
where "ukrw" is the denominating currency, and "8890" is the price of micro Luna in micro KRW from the voter's point of view.

If voting from a voting delegate, set "validator" to the address of the validator to vote on behalf of:
$ terracli oracle vote --denom "ukrw" --price "8890" --from mykey --validator terravaloper1.......
`),
RunE: func(cmd *cobra.Command, args []string) error {

Expand Down Expand Up @@ -59,13 +66,26 @@ where "ukrw" is the denominating currency, and "8890" is the price of micro Luna
return fmt.Errorf("--price flag is required")
}

// By default the voter is voting on behalf of itself
validator := sdk.ValAddress(voter)

// Override validator if flag is set
valStr := viper.GetString(flagValidator)
if len(valStr) != 0 {
parsedVal, err := sdk.ValAddressFromBech32(valStr)
if err != nil {
return errors.Wrap(err, "validator address is invalid")
}
validator = parsedVal
}

// Parse the price to Dec
price, err := sdk.NewDecFromStr(priceStr)
if err != nil {
return fmt.Errorf("given price {%s} is not a valid format; price should be formatted as float", priceStr)
}

msg := oracle.NewMsgPriceFeed(denom, price, voter)
msg := oracle.NewMsgPriceFeed(denom, price, voter, validator)
err = msg.ValidateBasic()
if err != nil {
return err
Expand All @@ -77,6 +97,63 @@ where "ukrw" is the denominating currency, and "8890" is the price of micro Luna

cmd.Flags().String(flagDenom, "", "denominating currency")
cmd.Flags().String(flagPrice, "", "price of Luna in denom currency")
cmd.Flags().String(flagValidator, "", "validator on behalf of which to vote (for delegated feeders)")

return cmd
}

// GetCmdDelegateFeederPermission will create a feeder permission delegation tx and sign it with the given key.
func GetCmdDelegateFeederPermission(cdc *codec.Codec) *cobra.Command {
cmd := &cobra.Command{
Use: "set-feeder",
Short: "Delegate the permission to vote for the oracle to an address",
Long: strings.TrimSpace(`
Delegate the permission to vote for the oracle to an address.
That way you can keep your validator operator key offline and use a separate replaceable key online.

$ terracli oracle set-feeder --delegatee terra1...... --from mykey

where "terra1abceuihfu93fud" is the address you want to delegate your voting rights to.
`),
RunE: func(cmd *cobra.Command, args []string) error {

txBldr := authtxb.NewTxBuilderFromCLI().WithTxEncoder(utils.GetTxEncoder(cdc))
cliCtx := context.NewCLIContext().
WithCodec(cdc).
WithAccountDecoder(cdc)

if err := cliCtx.EnsureAccountExists(); err != nil {
return err
}

// Get from address
voter := cliCtx.GetFromAddress()

// The address the right is being delegated from
validator := sdk.ValAddress(voter)

delegateeStr := viper.GetString(flagDelegatee)
if len(delegateeStr) == 0 {
return fmt.Errorf("--delegate flag is required")
}
delegatee, err := sdk.AccAddressFromBech32(delegateeStr)
if err != nil {
return errors.Wrap(err, "delegate is not a valid account address")
}

msg := oracle.NewMsgDelegateFeederPermission(validator, delegatee)
err = msg.ValidateBasic()
if err != nil {
return err
}

return utils.GenerateOrBroadcastMsgs(cliCtx, txBldr, []sdk.Msg{msg}, false)
},
}

cmd.Flags().String(flagDelegatee, "", "account the voting right will be delegated to")

cmd.MarkFlagRequired(flagDelegatee)

return cmd
}
2 changes: 2 additions & 0 deletions x/oracle/client/module_client.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ func (mc ModuleClient) GetQueryCmd() *cobra.Command {
cli.GetCmdQueryVotes(mc.storeKey, mc.cdc),
cli.GetCmdQueryActive(mc.storeKey, mc.cdc),
cli.GetCmdQueryParams(mc.storeKey, mc.cdc),
cli.GetCmdQueryFeederDelegation(mc.storeKey, mc.cdc),
)...)

return oracleQueryCmd
Expand All @@ -44,6 +45,7 @@ func (mc ModuleClient) GetTxCmd() *cobra.Command {

oracleTxCmd.AddCommand(client.PostCommands(
cli.GetCmdPriceVote(mc.cdc),
cli.GetCmdDelegateFeederPermission(mc.cdc),
)...)

return oracleTxCmd
Expand Down
39 changes: 36 additions & 3 deletions x/oracle/client/rest/query.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,10 @@ package rest

import (
"fmt"
"net/http"

"github.com/terra-project/core/types/assets"
"github.com/terra-project/core/x/oracle"
"net/http"

"github.com/cosmos/cosmos-sdk/client/context"
"github.com/cosmos/cosmos-sdk/codec"
Expand All @@ -19,6 +20,7 @@ func registerQueryRoute(cliCtx context.CLIContext, r *mux.Router, cdc *codec.Cod
r.HandleFunc(fmt.Sprintf("/oracle/denoms/{%s}/price", RestDenom), queryPriceHandlerFunction(cdc, cliCtx)).Methods("GET")
r.HandleFunc("/oracle/denoms/actives", queryActivesHandlerFunction(cdc, cliCtx)).Methods("GET")
r.HandleFunc("/oracle/params", queryParamsHandlerFn(cdc, cliCtx)).Methods("GET")
r.HandleFunc(fmt.Sprintf("/oracle/voters/{%s}/delegation", RestVoter), queryFeederDelegationHandlerFn(cdc, cliCtx)).Methods("GET")
}

func queryVotesHandlerFunction(cdc *codec.Codec, cliCtx context.CLIContext) http.HandlerFunc {
Expand All @@ -34,12 +36,12 @@ func queryVotesHandlerFunction(cdc *codec.Codec, cliCtx context.CLIContext) http

voter := vars[RestVoter]

var voterAddress sdk.AccAddress
var voterAddress sdk.ValAddress
params := oracle.NewQueryVoteParams(voterAddress, denom)

if len(voter) != 0 {

voterAddress, err := sdk.AccAddressFromBech32(voter)
voterAddress, err := sdk.ValAddressFromBech32(voter)
if err != nil {
return
}
Expand Down Expand Up @@ -107,3 +109,34 @@ func queryParamsHandlerFn(cdc *codec.Codec, cliCtx context.CLIContext) http.Hand
rest.PostProcessResponse(w, cdc, res, cliCtx.Indent)
}
}

func queryFeederDelegationHandlerFn(cdc *codec.Codec, cliCtx context.CLIContext) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r)
voter := vars[RestVoter]

validator, err := sdk.ValAddressFromBech32(voter)
if err != nil {
rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error())
return
}

params := oracle.NewQueryFeederDelegationParams(validator)
bz, err := cdc.MarshalJSON(params)
if err != nil {
rest.WriteErrorResponse(w, http.StatusInternalServerError, err.Error())
return
}

res, err := cliCtx.QueryWithData(fmt.Sprintf("custom/%s/%s", oracle.QuerierRoute, oracle.QueryFeederDelegation), bz)
if err != nil {
rest.WriteErrorResponse(w, http.StatusNotFound, err.Error())
return
}

var delegatee sdk.AccAddress
cdc.MustUnmarshalJSON(res, &delegatee)

rest.PostProcessResponse(w, cdc, res, cliCtx.Indent)
}
}
Loading