Skip to content

Commit

Permalink
Add per-ticket treasury key and tspend policies
Browse files Browse the repository at this point in the history
The four JSON-RPC methods that deal with setting and returning
approval policies for tspend transactions (settreasurypolicy,
settspendpolicy, treasurypolicy, tspendpolicy) gain an additional
optional parameter for a ticket hash.  When this hash is provided, the
policies set or returned by these methods are bound to a specific
ticket.

This functionality will be used by vspd to set per-ticket tspend
approval policies on its voting wallets.  Solo voters should never
need to use the per-ticket policies.
  • Loading branch information
jrick committed Jun 7, 2021
1 parent 6a339be commit f9f9360
Show file tree
Hide file tree
Showing 9 changed files with 392 additions and 52 deletions.
78 changes: 65 additions & 13 deletions internal/rpc/jsonrpc/methods.go
Expand Up @@ -3485,21 +3485,31 @@ func (s *Server) sendOutputsFromTreasury(ctx context.Context, w *wallet.Wallet,

// treasuryPolicy returns voting policies for treasury spends by a particular
// key. If a key is specified, that policy is returned; otherwise the policies
// for all keys are returned in an array.
// for all keys are returned in an array. If both a key and ticket hash are
// provided, the per-ticket key policy is returned.
func (s *Server) treasuryPolicy(ctx context.Context, icmd interface{}) (interface{}, error) {
cmd := icmd.(*types.TreasuryPolicyCmd)
w, ok := s.walletLoader.LoadedWallet()
if !ok {
return nil, errUnloadedWallet
}

var ticketHash *chainhash.Hash
if cmd.Ticket != nil && *cmd.Ticket != "" {
var err error
ticketHash, err = chainhash.NewHashFromStr(*cmd.Ticket)
if err != nil {
return nil, rpcError(dcrjson.ErrRPCDecodeHexString, err)
}
}

if cmd.Key != nil && *cmd.Key != "" {
pikey, err := hex.DecodeString(*cmd.Key)
if err != nil {
return nil, rpcError(dcrjson.ErrRPCDecodeHexString, err)
}
var policy string
switch w.TreasuryKeyPolicy(pikey) {
switch w.TreasuryKeyPolicy(pikey, ticketHash) {
case stake.TreasuryVoteYes:
policy = "yes"
case stake.TreasuryVoteNo:
Expand All @@ -3511,6 +3521,9 @@ func (s *Server) treasuryPolicy(ctx context.Context, icmd interface{}) (interfac
Key: *cmd.Key,
Policy: policy,
}
if cmd.Ticket != nil {
res.Ticket = *cmd.Ticket
}
return res, nil
}

Expand All @@ -3524,10 +3537,14 @@ func (s *Server) treasuryPolicy(ctx context.Context, icmd interface{}) (interfac
case stake.TreasuryVoteNo:
policy = "no"
}
res = append(res, types.TreasuryPolicyResult{
r := types.TreasuryPolicyResult{
Key: hex.EncodeToString(policies[i].PiKey),
Policy: policy,
})
}
if policies[i].Ticket != nil {
r.Ticket = policies[i].Ticket.String()
}
res = append(res, r)
}
return res, nil
}
Expand All @@ -3551,14 +3568,23 @@ func (s *Server) setDisapprovePercent(ctx context.Context, icmd interface{}) (in
}

// setTreasuryPolicy saves the voting policy for treasury spends by a particular
// key.
// key, and optionally, setting the key policy used by a specific ticket.
func (s *Server) setTreasuryPolicy(ctx context.Context, icmd interface{}) (interface{}, error) {
cmd := icmd.(*types.SetTreasuryPolicyCmd)
w, ok := s.walletLoader.LoadedWallet()
if !ok {
return nil, errUnloadedWallet
}

var ticketHash *chainhash.Hash
if cmd.Ticket != nil && *cmd.Ticket != "" {
var err error
ticketHash, err = chainhash.NewHashFromStr(*cmd.Ticket)
if err != nil {
return nil, rpcError(dcrjson.ErrRPCDecodeHexString, err)
}
}

pikey, err := hex.DecodeString(cmd.Key)
if err != nil {
return nil, rpcError(dcrjson.ErrRPCDecodeHexString, err)
Expand All @@ -3580,28 +3606,38 @@ func (s *Server) setTreasuryPolicy(ctx context.Context, icmd interface{}) (inter
return nil, rpcError(dcrjson.ErrRPCInvalidParameter, err)
}

err = w.SetTreasuryKeyPolicy(ctx, pikey, policy)
err = w.SetTreasuryKeyPolicy(ctx, pikey, policy, ticketHash)
return nil, err
}

// tspendPolicy returns voting policies for particular treasury spends
// transactions. If a tspend transaction hash is specified, that policy is
// returned; otherwise the policies for all known tspends are returned in an
// array.
// array. If both a tspend transaction hash and a ticket hash are provided,
// the per-ticket tspend policy is returned.
func (s *Server) tspendPolicy(ctx context.Context, icmd interface{}) (interface{}, error) {
cmd := icmd.(*types.TSpendPolicyCmd)
w, ok := s.walletLoader.LoadedWallet()
if !ok {
return nil, errUnloadedWallet
}

var ticketHash *chainhash.Hash
if cmd.Ticket != nil && *cmd.Ticket != "" {
var err error
ticketHash, err = chainhash.NewHashFromStr(*cmd.Ticket)
if err != nil {
return nil, rpcError(dcrjson.ErrRPCDecodeHexString, err)
}
}

if cmd.Hash != nil && *cmd.Hash != "" {
hash, err := chainhash.NewHashFromStr(*cmd.Hash)
if err != nil {
return nil, rpcError(dcrjson.ErrRPCDecodeHexString, err)
}
var policy string
switch w.TSpendPolicy(hash) {
switch w.TSpendPolicy(hash, ticketHash) {
case stake.TreasuryVoteYes:
policy = "yes"
case stake.TreasuryVoteNo:
Expand All @@ -3613,14 +3649,17 @@ func (s *Server) tspendPolicy(ctx context.Context, icmd interface{}) (interface{
Hash: *cmd.Hash,
Policy: policy,
}
if cmd.Ticket != nil {
res.Ticket = *cmd.Ticket
}
return res, nil
}

tspends := w.GetAllTSpends(ctx)
res := make([]types.TSpendPolicyResult, 0, len(tspends))
for i := range tspends {
tspendHash := tspends[i].TxHash()
p := w.TSpendPolicy(&tspendHash)
p := w.TSpendPolicy(&tspendHash, ticketHash)

var policy string
switch p {
Expand All @@ -3629,16 +3668,20 @@ func (s *Server) tspendPolicy(ctx context.Context, icmd interface{}) (interface{
case stake.TreasuryVoteNo:
policy = "no"
}
res = append(res, types.TSpendPolicyResult{
r := types.TSpendPolicyResult{
Hash: tspendHash.String(),
Policy: policy,
})
}
if cmd.Ticket != nil {
r.Ticket = *cmd.Ticket
}
res = append(res, r)
}
return res, nil
}

// setTSpendPolicy saves the voting policy for a particular tspend transaction
// hash.
// hash, and optionally, setting the tspend policy used by a specific ticket.
func (s *Server) setTSpendPolicy(ctx context.Context, icmd interface{}) (interface{}, error) {
cmd := icmd.(*types.SetTSpendPolicyCmd)
w, ok := s.walletLoader.LoadedWallet()
Expand All @@ -3651,6 +3694,15 @@ func (s *Server) setTSpendPolicy(ctx context.Context, icmd interface{}) (interfa
return nil, rpcError(dcrjson.ErrRPCDecodeHexString, err)
}

var ticketHash *chainhash.Hash
if cmd.Ticket != nil && *cmd.Ticket != "" {
var err error
ticketHash, err = chainhash.NewHashFromStr(*cmd.Ticket)
if err != nil {
return nil, rpcError(dcrjson.ErrRPCDecodeHexString, err)
}
}

var policy stake.TreasuryVoteT
switch cmd.Policy {
case "abstain", "invalid", "":
Expand All @@ -3664,7 +3716,7 @@ func (s *Server) setTSpendPolicy(ctx context.Context, icmd interface{}) (interfa
return nil, rpcError(dcrjson.ErrRPCInvalidParameter, err)
}

err = w.SetTSpendPolicy(ctx, hash, policy)
err = w.SetTSpendPolicy(ctx, hash, policy, ticketHash)
return nil, err
}

Expand Down
10 changes: 5 additions & 5 deletions internal/rpc/jsonrpc/rpcserverhelp.go

Large diffs are not rendered by default.

6 changes: 6 additions & 0 deletions internal/rpchelp/helpdescs_en_US.go
Expand Up @@ -774,11 +774,13 @@ var helpDescsEnUS = map[string]string{
"settreasurypolicy--synopsis": "Set a voting policy for treasury spends by a particular key",
"settreasurypolicy-key": "Treasury key to set policy for",
"settreasurypolicy-policy": "Voting policy for a treasury key (invalid/abstain, yes, or no)",
"settreasurypolicy-ticket": "Ticket hash to set a per-ticket treasury key policy",

// SetTSpendPolicyCmd help.
"settspendpolicy--synopsis": "Set a voting policy for a treasury spend transaction",
"settspendpolicy-hash": "Hash of treasury spend transaction to set policy for",
"settspendpolicy-policy": "Voting policy for a tspend transaction (invalid/abstain, yes, or no)",
"settspendpolicy-ticket": "Ticket hash to set a per-ticket tspend approval policy",

// SetTxFeeCmd help.
"settxfee--synopsis": "Modify the fee per kB of the serialized tx size used each time more fee is required for an authored transaction.",
Expand Down Expand Up @@ -882,24 +884,28 @@ var helpDescsEnUS = map[string]string{
// TreasuryPolicyCmd help.
"treasurypolicy--synopsis": "Return voting policies for treasury spend transactions by key",
"treasurypolicy-key": "Return the policy for a particular key",
"treasurypolicy-ticket": "Return policies used by a specific ticket hash",
"treasurypolicy--condition0": "no key provided",
"treasurypolicy--condition1": "key specified",
"treasurypolicy--result0": "Array of all non-abstaining voting policies",
"treasurypolicy--result1": "Voting policy for a particular treasury key",

"treasurypolicyresult-key": "Treasury key associated with a policy",
"treasurypolicyresult-policy": "Voting policy description (abstain, yes, or no)",
"treasurypolicyresult-ticket": "Ticket hash of a per-ticket treasury key approval policy",

// TSpendPolicyCmd help.
"tspendpolicy--synopsis": "Return voting policies for treasury spend transactions",
"tspendpolicy-hash": "Return the policy for a particular tspend hash",
"tspendpolicy-ticket": "Return policies used by a specific ticket hash",
"tspendpolicy--condition0": "no tspend hash provided",
"tspendpolicy--condition1": "tspend hash specified",
"tspendpolicy--result0": "Array of all non-abstaining policies for known tspends",
"tspendpolicy--result1": "Voting policy for a particular tspend hash",

"tspendpolicyresult-hash": "Treasury spend transaction hash",
"tspendpolicyresult-policy": "Voting policy description (abstain, yes, or no)",
"tspendpolicyresult-ticket": "Ticket hash of a per-ticket tspend approval policy",

// UnlockAccountCmd help.
"unlockaccount--synopsis": "Unlock an individually-encrypted account",
Expand Down
8 changes: 6 additions & 2 deletions rpc/jsonrpc/types/methods.go
Expand Up @@ -915,27 +915,31 @@ type SetDisapprovePercentCmd struct {
// TreasuryPolicyCmd defines the parameters for the treasurypolicy JSON-RPC
// command.
type TreasuryPolicyCmd struct {
Key *string
Key *string
Ticket *string
}

// SetTreasuryPolicyCmd defines the parameters for the settreasurypolicy
// JSON-RPC command.
type SetTreasuryPolicyCmd struct {
Key string
Policy string
Ticket *string
}

// TSpendPolicyCmd defines the parameters for the tspendpolicy JSON-RPC
// command.
type TSpendPolicyCmd struct {
Hash *string
Hash *string
Ticket *string
}

// SetTSpendPolicyCmd defines the parameters for the settspendpolicy
// JSON-RPC command.
type SetTSpendPolicyCmd struct {
Hash string
Policy string
Ticket *string
}

// SetTxFeeCmd defines the settxfee JSON-RPC command.
Expand Down
2 changes: 2 additions & 0 deletions rpc/jsonrpc/types/results.go
Expand Up @@ -378,12 +378,14 @@ type TicketInfoResult struct {
type TreasuryPolicyResult struct {
Key string `json:"key"`
Policy string `json:"policy"`
Ticket string `json:"ticket,omitempty"`
}

// TSpendPolicyResult models objects returned by the tspendpolicy command.
type TSpendPolicyResult struct {
Hash string `json:"hash"`
Policy string `json:"policy"`
Ticket string `json:"ticket,omitempty"`
}

// ValidateAddressResult models the data returned by the wallet server
Expand Down
2 changes: 1 addition & 1 deletion wallet/chainntfns.go
Expand Up @@ -942,7 +942,7 @@ func (w *Wallet) VoteOnOwnedTickets(ctx context.Context, winningTicketHashes []*
// Get policy for tspend, falling back to any
// policy for the Pi key.
tspendHash := v.TxHash()
tspendVote := w.TSpendPolicy(&tspendHash)
tspendVote := w.TSpendPolicy(&tspendHash, ticketHash)
if tspendVote == stake.TreasuryVoteInvalid {
continue
}
Expand Down

0 comments on commit f9f9360

Please sign in to comment.