governance: do not allow `approval` hex interpret (#302)

* governance: do not allow `approval` hex interpret
* gov: remove privacy, add reason, upd example
* gov: remove outdated comment
dependabot/go_modules/gopkg.in/yaml.v3-3.0.0 v1.4.2
Max 1 year ago committed by GitHub
parent aecd7314c0
commit c38f4e498c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 3
      README.md
  2. 11
      cmd/subcommands/governance.go
  3. 3
      cmd/subcommands/values.go
  4. 14
      pkg/governance/eip712.go
  5. 66
      pkg/governance/types.go

@ -129,8 +129,7 @@ Check README for details on json file format.
21. Vote on a governance proposal on https://snapshot.org
./hmy governance vote-proposal --space=[harmony-mainnet.eth] \
--proposal=<PROPOSAL_IPFS_HASH> --proposal-type=[single-choice] \
--choice=<VOTING_CHOICE(S)> --app=[APP] --key=<ACCOUNT_ADDRESS_OR_NAME> \
--privacy=[PRIVACY TYPE]
--choice=<VOTING_CHOICE(S)> --app=[APP] --key=<ACCOUNT_ADDRESS_OR_NAME>
PS: key must first use (hmy keys import-private-key) to import
22. Enter Console

@ -35,8 +35,9 @@ func commandVote() (cmd *cobra.Command) {
var choice string
var key string
var proposalType string
var privacy string
// var privacy string
var app string
var reason string
cmd = &cobra.Command{
Use: "vote-proposal",
@ -63,9 +64,10 @@ func commandVote() (cmd *cobra.Command) {
Proposal: proposal,
ProposalType: proposalType,
Choice: choice,
Privacy: privacy,
// Privacy: privacy,
App: app,
From: account.Address.Hex(),
Reason: reason,
})
},
}
@ -75,8 +77,9 @@ func commandVote() (cmd *cobra.Command) {
cmd.Flags().StringVar(&proposal, "proposal", "", "Proposal hash")
cmd.Flags().StringVar(&proposalType, "proposal-type", "single-choice", "Proposal type like single-choice, approval, quadratic, etc.")
cmd.Flags().StringVar(&choice, "choice", "", "Vote choice either as integer, list of integers (e.x. when using ranked choice voting), or string")
cmd.Flags().StringVar(&privacy, "privacy", "", "Vote privacy ex. shutter")
cmd.Flags().StringVar(&app, "app", "", "Voting app")
// cmd.Flags().StringVar(&privacy, "privacy", "", "Vote privacy e.x. shutter")
cmd.Flags().StringVar(&app, "app", "snapshot", "Voting app")
cmd.Flags().StringVar(&reason, "reason", "", "Reason for your choice")
cmd.Flags().BoolVar(&userProvidesPassphrase, "passphrase", false, ppPrompt)
cmd.MarkFlagRequired("key")

@ -107,8 +107,7 @@ Check README for details on json file format.
%s
./hmy governance vote-proposal --space=[harmony-mainnet.eth] \
--proposal=<PROPOSAL_IPFS_HASH> --proposal-type=[single-choice] \
--choice=<VOTING_CHOICE(S)> --app=[APP] --key=<ACCOUNT_ADDRESS_OR_NAME> \
--privacy=[PRIVACY TYPE]
--choice=<VOTING_CHOICE(S)> --app=[APP] --key=<ACCOUNT_ADDRESS_OR_NAME>
PS: key must first use (hmy keys import-private-key) to import
%s

@ -144,18 +144,32 @@ func (typedData *TypedData) String() (string, error) {
"proposal": typedData.Message["proposal"],
"choice": typedData.Message["choice"],
"app": typedData.Message["app"],
"reason": typedData.Message["reason"],
// this conversion is required to stop snapshot
// from complaining about `wrong envelope format`
"timestamp": ts,
"from": typedData.Message["from"],
},
}
// same comment as above
if typedData.Types["Vote"][4].Type == "uint32" {
if choice, err := toUint64(typedData.Message["choice"]); err != nil {
return "", errors.Wrapf(err, "choice")
} else {
formatted.Message["choice"] = choice
}
// prevent hex choice interpretation
} else if typedData.Types["Vote"][4].Type == "uint32[]" {
arr := typedData.Message["choice"].([]interface{})
res := make([]uint64, len(arr))
for i, a := range arr {
if c, err := toUint64(a); err != nil {
return "", errors.Wrapf(err, "choice member %d", i)
} else {
res[i] = c
}
}
formatted.Message["choice"] = res
}
message, err := json.Marshal(formatted)
if err != nil {

@ -13,14 +13,23 @@ import (
"github.com/pkg/errors"
)
var (
voteToNumberMapping = map[string]int64{
"for": 1,
"against": 2,
"abstain": 3,
}
)
type Vote struct {
From string // --key
Space string // --space
Proposal string // --proposal
ProposalType string // --proposal-type
Choice string // --choice
Privacy string // --privacy
// Privacy string // --privacy
App string // --app
Reason string // --reason
Timestamp int64 // not exposed to the end user
}
@ -65,7 +74,7 @@ func (v *Vote) ToEIP712() (*TypedData, error) {
proposal = v.Proposal
}
// vote type, vote choice and vote privacy
// vote type, vote choice and vote privacy (TODO)
// choice needs to be converted into its native format for envelope
var choice interface{}
// The space between [1, 2, 3] does not matter since we parse it
@ -74,7 +83,6 @@ func (v *Vote) ToEIP712() (*TypedData, error) {
// --proposal 0xTruncated \
// --proposal-type {"approval","ranked-choice"} \
// --choice "[1, 2, 3]" \
// --app my-app \
// --key <name of pk>
if v.ProposalType == "approval" || v.ProposalType == "ranked-choice" {
myType = append(myType, eip712.Type{
@ -93,14 +101,15 @@ func (v *Vote) ToEIP712() (*TypedData, error) {
"unexpected value of choice %s (expected uint32[])", choice,
)
}
// The space between [1, 2, 3] does not matter to snapshot.org
// The space between --choice {value} does not matter to snapshot.org
// But for comparing with the snapshot-js library, remove it
// hmy governance vote-proposal \
// --space harmony-mainnet.eth \
// --proposal 0xTruncated \
// # either quadratic or weighted
// --proposal-type {"quadratic","weighted"} \
// --choice "[1,2,3]" \
// --app my-app \
// # 20, 20, 40 of my vote (total 80) goes to 1, 2, 3 - note the single / double quotes
// --choice '{"1":20,"2":20,"3":40}' \
// --key <name of pk>
} else if v.ProposalType == "quadratic" || v.ProposalType == "weighted" {
myType = append(myType, eip712.Type{
@ -108,26 +117,25 @@ func (v *Vote) ToEIP712() (*TypedData, error) {
Type: "string",
})
choice = v.Choice
// TODO Untested
// hmy governance vote-proposal \
// --space harmony-mainnet.eth \
// --proposal 0xTruncated \
// --proposal-type ANY \
// --choice "unknown-format" \
// --app my-app \
// --key <name of pk>
// --privacy shutter
} else if v.Privacy == "shutter" {
myType = append(myType, eip712.Type{
Name: "choice",
Type: "string",
})
choice = v.Choice
// } else if v.Privacy == "shutter" {
// myType = append(myType, eip712.Type{
// Name: "choice",
// Type: "string",
// })
// choice = v.Choice
// hmy governance vote-proposal \
// --space harmony-mainnet.eth \
// --proposal 0xTruncated \
// --proposal-type single-choice \
// --choice 1 \
// --app my-app \
// --key <name of pk>
} else if v.ProposalType == "single-choice" {
myType = append(myType, eip712.Type{
@ -141,6 +149,28 @@ func (v *Vote) ToEIP712() (*TypedData, error) {
} else {
choice = math.NewHexOrDecimal256(int64(x))
}
// hmy governance vote-proposal \
// --space harmony-mainnet.eth \
// --proposal 0xTruncated \
// --proposal-type basic \
// # any character case works
// --choice {aBstAin/agAiNst/for} \
// --key <name of pk>
} else if v.ProposalType == "basic" {
myType = append(myType, eip712.Type{
Name: "choice",
Type: "uint32",
})
if number, ok := voteToNumberMapping[strings.ToLower(v.Choice)]; ok {
choice = math.NewHexOrDecimal256(number)
} else {
return nil, errors.New(
fmt.Sprintf(
"unknown basic choice %s",
v.Choice,
),
)
}
} else {
return nil, errors.New(
fmt.Sprintf(
@ -150,11 +180,16 @@ func (v *Vote) ToEIP712() (*TypedData, error) {
)
}
// order matters so this is added last
// order matters so these are added last
myType = append(myType, eip712.Type{
Name: "reason",
Type: "string",
})
myType = append(myType, eip712.Type{
Name: "app",
Type: "string",
})
// metadata is skipped in this code intentionally
if v.Timestamp == 0 {
v.Timestamp = time.Now().Unix()
@ -186,6 +221,7 @@ func (v *Vote) ToEIP712() (*TypedData, error) {
"timestamp": math.NewHexOrDecimal256(v.Timestamp),
"proposal": proposal,
"choice": choice,
"reason": v.Reason,
"app": v.App,
},
PrimaryType: "Vote",

Loading…
Cancel
Save