@@ -18,6 +18,7 @@ import (
18
18
"decred.org/cspp/v2/coinjoin"
19
19
"decred.org/dcrwallet/v3/deployments"
20
20
"decred.org/dcrwallet/v3/errors"
21
+ "decred.org/dcrwallet/v3/internal/uniformprng"
21
22
"decred.org/dcrwallet/v3/rpc/client/dcrd"
22
23
"decred.org/dcrwallet/v3/wallet/txauthor"
23
24
"decred.org/dcrwallet/v3/wallet/txrules"
@@ -1614,6 +1615,29 @@ func (w *Wallet) purchaseTickets(ctx context.Context, op errors.Op,
1614
1615
}
1615
1616
}
1616
1617
1618
+ // Calculate trickle times for published mixed tickets.
1619
+ // Random times between 20s to 1m from now are chosen for each ticket,
1620
+ // and tickets will not be published until their trickle time is reached.
1621
+ var trickleTickets []time.Time
1622
+ if req .CSPPServer != "" {
1623
+ now := time .Now ()
1624
+ trickleTickets = make ([]time.Time , 0 , len (splitOutputIndexes ))
1625
+ for range splitOutputIndexes {
1626
+ delay , err := uniformprng .Int63n (rand .Reader ,
1627
+ int64 (40 * time .Second ))
1628
+ if err != nil {
1629
+ return nil , err
1630
+ }
1631
+ t := now .Add (time .Duration (delay ) + 20 * time .Second )
1632
+ trickleTickets = append (trickleTickets , t )
1633
+ }
1634
+ sort .Slice (trickleTickets , func (i , j int ) bool {
1635
+ t1 := trickleTickets [i ]
1636
+ t2 := trickleTickets [j ]
1637
+ return t1 .Before (t2 )
1638
+ })
1639
+ }
1640
+
1617
1641
// Create each ticket.
1618
1642
ticketHashes := make ([]* chainhash.Hash , 0 , req .Count )
1619
1643
tickets := make ([]* wire.MsgTx , 0 , req .Count )
@@ -1670,7 +1694,8 @@ func (w *Wallet) purchaseTickets(ctx context.Context, op errors.Op,
1670
1694
if err != nil {
1671
1695
return nil , err
1672
1696
}
1673
- _ , err := w .signingAddressAtIdx (ctx , op , w .persistReturnedChild (ctx , nil ), req .VotingAccount , idx )
1697
+ _ , err := w .signingAddressAtIdx (ctx , op , w .persistReturnedChild (ctx , nil ),
1698
+ req .VotingAccount , idx )
1674
1699
if err != nil {
1675
1700
return nil , err
1676
1701
}
@@ -1762,39 +1787,55 @@ func (w *Wallet) purchaseTickets(ctx context.Context, op errors.Op,
1762
1787
if err != nil {
1763
1788
return purchaseTicketsResponse , errors .E (op , err )
1764
1789
}
1765
- // TODO: Send all tickets, and all split transactions, together. Purge
1766
- // transactions from DB if tickets cannot be sent.
1767
- if ! req .DontSignTx {
1768
- err = n .PublishTransactions (ctx , ticket )
1769
- if err != nil {
1770
- return purchaseTicketsResponse , errors .E (op , err )
1771
- }
1772
- log .Infof ("Published ticket purchase %v" , ticket .TxHash ())
1773
- }
1774
1790
}
1775
1791
1776
- if ! req .DontSignTx && req .VSPFeePaymentProcess != nil {
1777
- unlockCredits = false
1778
- for i , ticketHash := range purchaseTicketsResponse .TicketHashes {
1779
- feeTx := wire .NewMsgTx ()
1780
- for j := range vspFeeCredits [i ] {
1781
- in := & vspFeeCredits [i ][j ]
1782
- feeTx .AddTxIn (wire .NewTxIn (& in .OutPoint , in .PrevOut .Value , nil ))
1783
- }
1784
-
1785
- err = req .VSPFeePaymentProcess (ctx , ticketHash , feeTx )
1786
- if err != nil {
1787
- // unlock outpoints in case of error
1788
- for _ , outpoint := range vspFeeCredits [i ] {
1789
- w .UnlockOutpoint (& outpoint .OutPoint .Hash , outpoint .OutPoint .Index )
1792
+ for i , ticket := range tickets {
1793
+ // Wait for trickle time if this was a mixed buy.
1794
+ if len (trickleTickets ) > 0 {
1795
+ t := trickleTickets [0 ]
1796
+ trickleTickets = trickleTickets [1 :]
1797
+ timer := time .NewTimer (time .Until (t ))
1798
+ select {
1799
+ case <- ctx .Done ():
1800
+ if ! timer .Stop () {
1801
+ <- timer .C
1790
1802
}
1791
- continue
1803
+ return purchaseTicketsResponse , errors .E (op , ctx .Err ())
1804
+ case <- timer .C :
1792
1805
}
1793
- // watch for outpoints change.
1794
- _ , err = udb .NewTxRecordFromMsgTx (feeTx , time .Now ())
1795
- if err != nil {
1796
- return nil , err
1806
+ }
1807
+
1808
+ // Publish transaction
1809
+ err = n .PublishTransactions (ctx , ticket )
1810
+ if err != nil {
1811
+ return purchaseTicketsResponse , errors .E (op , err )
1812
+ }
1813
+ log .Infof ("Published ticket purchase %v" , ticket .TxHash ())
1814
+
1815
+ // Pay VSP fee when configured to do so.
1816
+ if req .VSPFeePaymentProcess == nil {
1817
+ continue
1818
+ }
1819
+ unlockCredits = false
1820
+ feeTx := wire .NewMsgTx ()
1821
+ for j := range vspFeeCredits [i ] {
1822
+ in := & vspFeeCredits [i ][j ]
1823
+ feeTx .AddTxIn (wire .NewTxIn (& in .OutPoint , in .PrevOut .Value , nil ))
1824
+ }
1825
+ ticketHash := purchaseTicketsResponse .TicketHashes [i ]
1826
+ err = req .VSPFeePaymentProcess (ctx , ticketHash , feeTx )
1827
+ if err != nil {
1828
+ // unlock outpoints in case of error
1829
+ for _ , outpoint := range vspFeeCredits [i ] {
1830
+ w .UnlockOutpoint (& outpoint .OutPoint .Hash ,
1831
+ outpoint .OutPoint .Index )
1797
1832
}
1833
+ continue
1834
+ }
1835
+ // watch for outpoints change.
1836
+ _ , err = udb .NewTxRecordFromMsgTx (feeTx , time .Now ())
1837
+ if err != nil {
1838
+ return nil , err
1798
1839
}
1799
1840
}
1800
1841
0 commit comments