Skip to content

Commit b5fa3cc

Browse files
authored
Set tspend & treasury policies on VSP
Update the existing RPCs to update treasury/tspend voting preferences so that they forward updated preferences to VSP.
1 parent 80bcd2f commit b5fa3cc

File tree

5 files changed

+162
-36
lines changed

5 files changed

+162
-36
lines changed

internal/rpc/jsonrpc/methods.go

Lines changed: 46 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -3811,6 +3811,9 @@ func (s *Server) setDisapprovePercent(ctx context.Context, icmd interface{}) (in
38113811

38123812
// setTreasuryPolicy saves the voting policy for treasury spends by a particular
38133813
// key, and optionally, setting the key policy used by a specific ticket.
3814+
//
3815+
// If a VSP host is configured in the application settings, the voting
3816+
// preferences will also be set with the VSP.
38143817
func (s *Server) setTreasuryPolicy(ctx context.Context, icmd interface{}) (interface{}, error) {
38153818
cmd := icmd.(*types.SetTreasuryPolicyCmd)
38163819
w, ok := s.walletLoader.LoadedWallet()
@@ -3854,6 +3857,16 @@ func (s *Server) setTreasuryPolicy(ctx context.Context, icmd interface{}) (inter
38543857
}
38553858

38563859
err = w.SetTreasuryKeyPolicy(ctx, pikey, policy, ticketHash)
3860+
if err != nil {
3861+
return nil, err
3862+
}
3863+
3864+
// Update voting preferences on VSPs if required.
3865+
policyMap := map[string]string{
3866+
cmd.Key: cmd.Policy,
3867+
}
3868+
err = s.updateVSPVoteChoices(ctx, w, ticketHash, nil, nil, policyMap)
3869+
38573870
return nil, err
38583871
}
38593872

@@ -3929,6 +3942,9 @@ func (s *Server) tspendPolicy(ctx context.Context, icmd interface{}) (interface{
39293942

39303943
// setTSpendPolicy saves the voting policy for a particular tspend transaction
39313944
// hash, and optionally, setting the tspend policy used by a specific ticket.
3945+
//
3946+
// If a VSP host is configured in the application settings, the voting
3947+
// preferences will also be set with the VSP.
39323948
func (s *Server) setTSpendPolicy(ctx context.Context, icmd interface{}) (interface{}, error) {
39333949
cmd := icmd.(*types.SetTSpendPolicyCmd)
39343950
w, ok := s.walletLoader.LoadedWallet()
@@ -3975,6 +3991,15 @@ func (s *Server) setTSpendPolicy(ctx context.Context, icmd interface{}) (interfa
39753991
}
39763992

39773993
err = w.SetTSpendPolicy(ctx, hash, policy, ticketHash)
3994+
if err != nil {
3995+
return nil, err
3996+
}
3997+
3998+
// Update voting preferences on VSPs if required.
3999+
policyMap := map[string]string{
4000+
cmd.Hash: cmd.Policy,
4001+
}
4002+
err = s.updateVSPVoteChoices(ctx, w, ticketHash, nil, policyMap, nil)
39784003
return nil, err
39794004
}
39804005

@@ -4635,34 +4660,42 @@ func (s *Server) setVoteChoice(ctx context.Context, icmd interface{}) (interface
46354660
ticketHash = hash
46364661
}
46374662

4638-
choice := wallet.AgendaChoice{
4639-
AgendaID: cmd.AgendaID,
4640-
ChoiceID: cmd.ChoiceID,
4663+
choice := []wallet.AgendaChoice{
4664+
{
4665+
AgendaID: cmd.AgendaID,
4666+
ChoiceID: cmd.ChoiceID,
4667+
},
46414668
}
4642-
_, err := w.SetAgendaChoices(ctx, ticketHash, choice)
4669+
_, err := w.SetAgendaChoices(ctx, ticketHash, choice...)
46434670
if err != nil {
46444671
return nil, err
46454672
}
46464673

46474674
// Update voting preferences on VSPs if required.
4675+
err = s.updateVSPVoteChoices(ctx, w, ticketHash, choice, nil, nil)
4676+
return nil, err
4677+
}
4678+
4679+
func (s *Server) updateVSPVoteChoices(ctx context.Context, w *wallet.Wallet, ticketHash *chainhash.Hash,
4680+
choices []wallet.AgendaChoice, tspendPolicy map[string]string, treasuryPolicy map[string]string) error {
46484681
if ticketHash != nil {
46494682
vspHost, err := w.VSPHostForTicket(ctx, ticketHash)
46504683
if err != nil {
46514684
if errors.Is(err, errors.NotExist) {
46524685
// Ticket is not registered with a VSP, nothing more to do here.
4653-
return nil, nil
4686+
return nil
46544687
}
4655-
return nil, err
4688+
return err
46564689
}
46574690
vspClient, err := loader.LookupVSP(vspHost)
46584691
if err != nil {
4659-
return nil, err
4692+
return err
46604693
}
4661-
err = vspClient.SetVoteChoice(ctx, ticketHash, choice)
4662-
return nil, err
4694+
err = vspClient.SetVoteChoice(ctx, ticketHash, choices, tspendPolicy, treasuryPolicy)
4695+
return err
46634696
}
46644697
var firstErr error
4665-
err = w.ForUnspentUnexpiredTickets(ctx, func(hash *chainhash.Hash) error {
4698+
err := w.ForUnspentUnexpiredTickets(ctx, func(hash *chainhash.Hash) error {
46664699
vspHost, err := w.VSPHostForTicket(ctx, hash)
46674700
if err != nil && firstErr == nil {
46684701
if errors.Is(err, errors.NotExist) {
@@ -4679,16 +4712,16 @@ func (s *Server) setVoteChoice(ctx context.Context, icmd interface{}) (interface
46794712
}
46804713
// Never return errors here, so all tickets are tried.
46814714
// The first error will be returned to the user.
4682-
err = vspClient.SetVoteChoice(ctx, hash, choice)
4715+
err = vspClient.SetVoteChoice(ctx, hash, choices, tspendPolicy, treasuryPolicy)
46834716
if err != nil && firstErr == nil {
46844717
firstErr = err
46854718
}
46864719
return nil
46874720
})
46884721
if err != nil {
4689-
return nil, err
4722+
return err
46904723
}
4691-
return nil, firstErr
4724+
return firstErr
46924725
}
46934726

46944727
// signMessage signs the given message with the private key for the given

internal/rpc/rpcserver/server.go

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4069,7 +4069,10 @@ func (s *walletServer) SetVspdVoteChoices(ctx context.Context, req *pb.SetVspdVo
40694069
return err
40704070
}
40714071
if ticketHost == vspHost {
4072-
_ = vspClient.SetVoteChoice(ctx, hash, choices...)
4072+
tSpendChoices := s.wallet.TSpendPolicyForTicket(hash)
4073+
treasuryChoices := s.wallet.TreasuryKeyPolicyForTicket(hash)
4074+
4075+
_ = vspClient.SetVoteChoice(ctx, hash, choices, tSpendChoices, treasuryChoices)
40734076
}
40744077
return nil
40754078
})

internal/vsp/feepayment.go

Lines changed: 28 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -611,6 +611,8 @@ type ticketStatus struct {
611611
FeeTxStatus string `json:"feetxstatus"`
612612
FeeTxHash string `json:"feetxhash"`
613613
VoteChoices map[string]string `json:"votechoices"`
614+
TSpendPolicy map[string]string `json:"tspendpolicy"`
615+
TreasuryPolicy map[string]string `json:"treasurypolicy"`
614616
Request []byte `json:"request"`
615617
}
616618

@@ -662,7 +664,8 @@ func (c *Client) status(ctx context.Context, ticketHash *chainhash.Hash) (*ticke
662664
return &resp, nil
663665
}
664666

665-
func (c *Client) setVoteChoices(ctx context.Context, ticketHash *chainhash.Hash, choices []wallet.AgendaChoice) error {
667+
func (c *Client) setVoteChoices(ctx context.Context, ticketHash *chainhash.Hash,
668+
choices []wallet.AgendaChoice, tspendPolicy map[string]string, treasuryPolicy map[string]string) error {
666669
w := c.Wallet
667670
params := w.ChainParams()
668671

@@ -693,13 +696,17 @@ func (c *Client) setVoteChoices(ctx context.Context, ticketHash *chainhash.Hash,
693696

694697
var resp ticketStatus
695698
requestBody, err := json.Marshal(&struct {
696-
Timestamp int64 `json:"timestamp"`
697-
TicketHash string `json:"tickethash"`
698-
VoteChoices map[string]string `json:"votechoices"`
699+
Timestamp int64 `json:"timestamp"`
700+
TicketHash string `json:"tickethash"`
701+
VoteChoices map[string]string `json:"votechoices"`
702+
TSpendPolicy map[string]string `json:"tspendpolicy"`
703+
TreasuryPolicy map[string]string `json:"treasurypolicy"`
699704
}{
700-
Timestamp: time.Now().Unix(),
701-
TicketHash: ticketHash.String(),
702-
VoteChoices: agendaChoices,
705+
Timestamp: time.Now().Unix(),
706+
TicketHash: ticketHash.String(),
707+
VoteChoices: agendaChoices,
708+
TSpendPolicy: tspendPolicy,
709+
TreasuryPolicy: treasuryPolicy,
703710
})
704711
if err != nil {
705712
return err
@@ -882,17 +889,21 @@ func (fp *feePayment) submitPayment() (err error) {
882889
Request []byte `json:"request"`
883890
}
884891
requestBody, err := json.Marshal(&struct {
885-
Timestamp int64 `json:"timestamp"`
886-
TicketHash string `json:"tickethash"`
887-
FeeTx json.Marshaler `json:"feetx"`
888-
VotingKey string `json:"votingkey"`
889-
VoteChoices map[string]string `json:"votechoices"`
892+
Timestamp int64 `json:"timestamp"`
893+
TicketHash string `json:"tickethash"`
894+
FeeTx json.Marshaler `json:"feetx"`
895+
VotingKey string `json:"votingkey"`
896+
VoteChoices map[string]string `json:"votechoices"`
897+
TSpendPolicy map[string]string `json:"tspendpolicy"`
898+
TreasuryPolicy map[string]string `json:"treasurypolicy"`
890899
}{
891-
Timestamp: time.Now().Unix(),
892-
TicketHash: fp.ticketHash.String(),
893-
FeeTx: txMarshaler(feeTx),
894-
VotingKey: votingKey,
895-
VoteChoices: voteChoices,
900+
Timestamp: time.Now().Unix(),
901+
TicketHash: fp.ticketHash.String(),
902+
FeeTx: txMarshaler(feeTx),
903+
VotingKey: votingKey,
904+
VoteChoices: voteChoices,
905+
TSpendPolicy: w.TSpendPolicyForTicket(&fp.ticketHash),
906+
TreasuryPolicy: w.TreasuryKeyPolicyForTicket(&fp.ticketHash),
896907
})
897908
if err != nil {
898909
return err

internal/vsp/vsp.go

Lines changed: 36 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -292,10 +292,12 @@ func (c *Client) ProcessWithPolicy(ctx context.Context, ticketHash *chainhash.Ha
292292
return nil
293293
}
294294

295-
// SetVoteChoice takes the provided AgendaChoices and ticket hash, checks the
296-
// status of the ticket from the connected vsp. The status provides the
297-
// current vote choice so we can just update from there if need be.
298-
func (c *Client) SetVoteChoice(ctx context.Context, hash *chainhash.Hash, choices ...wallet.AgendaChoice) error {
295+
// SetVoteChoice takes the provided consensus, tspend and treasury key voting
296+
// preferences, and checks if they match the status of the specified ticket from
297+
// the connected VSP. The status provides the current voting preferences so we
298+
// can just update from there if need be.
299+
func (c *Client) SetVoteChoice(ctx context.Context, hash *chainhash.Hash,
300+
choices []wallet.AgendaChoice, tspendPolicy map[string]string, treasuryPolicy map[string]string) error {
299301

300302
// Retrieve current voting preferences from VSP.
301303
status, err := c.status(ctx, hash)
@@ -311,6 +313,7 @@ func (c *Client) SetVoteChoice(ctx context.Context, hash *chainhash.Hash, choice
311313
// VSP preferences to determine if VSP needs to be updated.
312314
update := false
313315

316+
// Check consensus vote choices.
314317
for _, newChoice := range choices {
315318
vspChoice, ok := status.VoteChoices[newChoice.AgendaID]
316319
if !ok {
@@ -323,13 +326,41 @@ func (c *Client) SetVoteChoice(ctx context.Context, hash *chainhash.Hash, choice
323326
}
324327
}
325328

329+
// Apply the above changes to the two checks below.
330+
331+
// Check tspend policies.
332+
for newTSpend, newChoice := range tspendPolicy {
333+
vspChoice, ok := status.TSpendPolicy[newTSpend]
334+
if !ok {
335+
update = true
336+
break
337+
}
338+
if vspChoice != newChoice {
339+
update = true
340+
break
341+
}
342+
}
343+
344+
// Check treasury policies.
345+
for newKey, newChoice := range treasuryPolicy {
346+
vspChoice, ok := status.TSpendPolicy[newKey]
347+
if !ok {
348+
update = true
349+
break
350+
}
351+
if vspChoice != newChoice {
352+
update = true
353+
break
354+
}
355+
}
356+
326357
if !update {
327358
log.Debugf("VSP already has correct vote choices for ticket %s", hash)
328359
return nil
329360
}
330361

331362
log.Debugf("Updating vote choices on VSP for ticket %s", hash)
332-
err = c.setVoteChoices(ctx, hash, choices)
363+
err = c.setVoteChoices(ctx, hash, choices, tspendPolicy, treasuryPolicy)
333364
if err != nil {
334365
return err
335366
}

wallet/wallet.go

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -549,6 +549,30 @@ func (w *Wallet) SetAgendaChoices(ctx context.Context, ticketHash *chainhash.Has
549549
return voteBits, nil
550550
}
551551

552+
// TreasuryKeyPolicyForTicket returns all of the treasury key policies set for a
553+
// single ticket. It does not consider the global wallet setting.
554+
func (w *Wallet) TreasuryKeyPolicyForTicket(ticketHash *chainhash.Hash) map[string]string {
555+
w.stakeSettingsLock.Lock()
556+
defer w.stakeSettingsLock.Unlock()
557+
558+
policies := make(map[string]string)
559+
for key, value := range w.vspTSpendKeyPolicy {
560+
if key.Ticket.IsEqual(ticketHash) {
561+
var choice string
562+
switch value {
563+
case stake.TreasuryVoteYes:
564+
choice = "yes"
565+
case stake.TreasuryVoteNo:
566+
choice = "no"
567+
default:
568+
choice = "abstain"
569+
}
570+
policies[key.TreasuryKey] = choice
571+
}
572+
}
573+
return policies
574+
}
575+
552576
// TreasuryKeyPolicy returns a vote policy for provided Pi key. If there is
553577
// no policy this method returns TreasuryVoteInvalid.
554578
// A non-nil ticket hash may be used by a VSP to return per-ticket policies.
@@ -566,6 +590,30 @@ func (w *Wallet) TreasuryKeyPolicy(pikey []byte, ticket *chainhash.Hash) stake.T
566590
return w.tspendKeyPolicy[string(pikey)]
567591
}
568592

593+
// TSpendPolicyForTicket returns all of the tspend policies set for a single
594+
// ticket. It does not consider the global wallet setting.
595+
func (w *Wallet) TSpendPolicyForTicket(ticketHash *chainhash.Hash) map[string]string {
596+
w.stakeSettingsLock.Lock()
597+
defer w.stakeSettingsLock.Unlock()
598+
599+
policies := make(map[string]string)
600+
for key, value := range w.vspTSpendPolicy {
601+
if key.Ticket.IsEqual(ticketHash) {
602+
var choice string
603+
switch value {
604+
case stake.TreasuryVoteYes:
605+
choice = "yes"
606+
case stake.TreasuryVoteNo:
607+
choice = "no"
608+
default:
609+
choice = "abstain"
610+
}
611+
policies[key.TSpend.String()] = choice
612+
}
613+
}
614+
return policies
615+
}
616+
569617
// TSpendPolicy returns a vote policy for a tspend. If a policy is set for a
570618
// particular tspend transaction, that policy is returned. Otherwise, if the
571619
// tspend is known, any policy for the treasury key which signs the tspend is

0 commit comments

Comments
 (0)