Skip to content

Commit bf8adb2

Browse files
author
Sef
committed
Fix conflicts
2 parents 065b967 + 87f0a52 commit bf8adb2

File tree

2 files changed

+295
-435
lines changed

2 files changed

+295
-435
lines changed

wallet/createtx.go

Lines changed: 85 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
// Copyright (c) 2013-2016 The btcsuite developers
2-
// Copyright (c) 2015-2017 The Decred developers
2+
// Copyright (c) 2015-2019 The Decred developers
33
// Use of this source code is governed by an ISC
44
// license that can be found in the LICENSE file.
55

@@ -8,6 +8,7 @@ package wallet
88
import (
99
"context"
1010
"encoding/binary"
11+
"sync"
1112
"time"
1213

1314
"github.com/decred/dcrd/blockchain"
@@ -74,7 +75,7 @@ const (
7475
OutputSelectionAlgorithmDefault = iota
7576

7677
// OutputSelectionAlgorithmAll describes the output selection algorithm of
77-
// picking every possible availble output. This is useful for sweeping.
78+
// picking every possible available output. This is useful for sweeping.
7879
OutputSelectionAlgorithmAll
7980
)
8081

@@ -89,6 +90,21 @@ func (w *Wallet) NewUnsignedTransaction(outputs []*wire.TxOut, relayFeePerKb dcr
8990

9091
const op errors.Op = "wallet.NewUnsignedTransaction"
9192

93+
var unlockOutpoints []*wire.OutPoint
94+
defer func() {
95+
if len(unlockOutpoints) != 0 {
96+
w.lockedOutpointMu.Lock()
97+
for _, op := range unlockOutpoints {
98+
delete(w.lockedOutpoints, *op)
99+
}
100+
w.lockedOutpointMu.Unlock()
101+
}
102+
}()
103+
ignoreInput := func(op *wire.OutPoint) bool {
104+
_, ok := w.lockedOutpoints[*op]
105+
return ok
106+
}
107+
92108
var authoredTx *txauthor.AuthoredTx
93109
var changeSourceUpdates []func(walletdb.ReadWriteTx) error
94110
err := walletdb.View(w.db, func(dbtx walletdb.ReadTx) error {
@@ -106,8 +122,8 @@ func (w *Wallet) NewUnsignedTransaction(outputs []*wire.TxOut, relayFeePerKb dcr
106122
}
107123
}
108124

109-
sourceImpl := w.TxStore.MakeInputSource(txmgrNs, addrmgrNs, account,
110-
minConf, tipHeight)
125+
sourceImpl := w.TxStore.MakeIgnoredInputSource(txmgrNs, addrmgrNs, account,
126+
minConf, tipHeight, ignoreInput)
111127
var inputSource txauthor.InputSource
112128
switch algo {
113129
case OutputSelectionAlgorithmDefault:
@@ -135,10 +151,20 @@ func (w *Wallet) NewUnsignedTransaction(outputs []*wire.TxOut, relayFeePerKb dcr
135151
}
136152
}
137153

154+
defer w.lockedOutpointMu.Unlock()
155+
w.lockedOutpointMu.Lock()
156+
138157
var err error
139158
authoredTx, err = txauthor.NewUnsignedTransaction(outputs, relayFeePerKb,
140159
inputSource, changeSource)
141-
return err
160+
if err != nil {
161+
return err
162+
}
163+
for _, in := range authoredTx.Tx.TxIn {
164+
w.lockedOutpoints[in.PreviousOutPoint] = struct{}{}
165+
unlockOutpoints = append(unlockOutpoints, &in.PreviousOutPoint)
166+
}
167+
return nil
142168
})
143169
if err != nil {
144170
return nil, errors.E(op, err)
@@ -320,17 +346,36 @@ func (w *Wallet) txToOutputsInternal(op errors.Op, outputs []*wire.TxOut, accoun
320346
options = w.NewCreateTxOptions(false)
321347
}
322348

349+
var unlockOutpoints []*wire.OutPoint
350+
defer func() {
351+
if len(unlockOutpoints) != 0 {
352+
w.lockedOutpointMu.Lock()
353+
for _, op := range unlockOutpoints {
354+
delete(w.lockedOutpoints, *op)
355+
}
356+
w.lockedOutpointMu.Unlock()
357+
}
358+
}()
359+
ignoreInput := func(op *wire.OutPoint) bool {
360+
_, ok := w.lockedOutpoints[*op]
361+
return ok
362+
}
363+
323364
var atx *txauthor.AuthoredTx
324365
var changeSourceUpdates []func(walletdb.ReadWriteTx) error
325366

326367
err := walletdb.View(w.db, func(dbtx walletdb.ReadTx) error {
327368
addrmgrNs := dbtx.ReadBucket(waddrmgrNamespaceKey)
328369
txmgrNs := dbtx.ReadBucket(wtxmgrNamespaceKey)
329370

371+
var once sync.Once
372+
defer once.Do(w.lockedOutpointMu.Unlock)
373+
w.lockedOutpointMu.Lock()
374+
330375
// Create the unsigned transaction.
331376
_, tipHeight := w.TxStore.MainChainTip(txmgrNs)
332-
inputSource := w.TxStore.MakeInputSource(txmgrNs, addrmgrNs, account,
333-
minconf, tipHeight)
377+
inputSource := w.TxStore.MakeIgnoredInputSource(txmgrNs, addrmgrNs, account,
378+
minconf, tipHeight, ignoreInput)
334379
changeSource := &p2PKHChangeSource{
335380
persist: w.deferPersistReturnedChild(&changeSourceUpdates),
336381
account: account,
@@ -355,6 +400,11 @@ func (w *Wallet) txToOutputsInternal(op errors.Op, outputs []*wire.TxOut, accoun
355400
if err != nil {
356401
return err
357402
}
403+
for _, in := range atx.Tx.TxIn {
404+
w.lockedOutpoints[in.PreviousOutPoint] = struct{}{}
405+
unlockOutpoints = append(unlockOutpoints, &in.PreviousOutPoint)
406+
}
407+
once.Do(w.lockedOutpointMu.Unlock)
358408

359409
// Randomize change position, if change exists, before signing. This
360410
// doesn't affect the serialize size, so the change amount will still be
@@ -681,7 +731,7 @@ func (w *Wallet) compressWalletInternal(op errors.Op, dbtx walletdb.ReadWriteTx,
681731
return nil, errors.E(op, "too few outputs to consolidate")
682732
}
683733

684-
// Check if output address is default, and generate a new adress if needed
734+
// Check if output address is default, and generate a new address if needed
685735
if changeAddr == nil {
686736
changeAddr, err = w.newChangeAddress(op, w.persistReturnedChild(dbtx), account)
687737
if err != nil {
@@ -886,33 +936,28 @@ func makeTicket(params *chaincfg.Params, inputPool *extendedOutPoint, input *ext
886936
// wallet instance will be used. Also, when the spend limit in the request is
887937
// greater than or equal to 0, tickets that cost more than that limit will
888938
// return an error that not enough funds are available.
889-
func (w *Wallet) purchaseTickets(op errors.Op, req purchaseTicketRequest) ([]*chainhash.Hash, error) {
890-
n, err := w.NetworkBackend()
891-
if err != nil {
892-
return nil, errors.E(op, err)
893-
}
894-
939+
func (w *Wallet) purchaseTickets(ctx context.Context, op errors.Op, n NetworkBackend, req *PurchaseTicketsRequest) ([]*chainhash.Hash, error) {
895940
// Ensure the minimum number of required confirmations is positive.
896-
if req.minConf < 0 {
941+
if req.MinConf < 0 {
897942
return nil, errors.E(op, errors.Invalid, "negative minconf")
898943
}
899944
// Need a positive or zero expiry that is higher than the next block to
900945
// generate.
901-
if req.expiry < 0 {
946+
if req.Expiry < 0 {
902947
return nil, errors.E(op, errors.Invalid, "negative expiry")
903948
}
904949

905950
// Perform a sanity check on expiry.
906951
var tipHeight int32
907-
err = walletdb.View(w.db, func(tx walletdb.ReadTx) error {
952+
err := walletdb.View(w.db, func(tx walletdb.ReadTx) error {
908953
ns := tx.ReadBucket(wtxmgrNamespaceKey)
909954
_, tipHeight = w.TxStore.MainChainTip(ns)
910955
return nil
911956
})
912957
if err != nil {
913958
return nil, err
914959
}
915-
if req.expiry <= tipHeight+1 && req.expiry > 0 {
960+
if req.Expiry <= tipHeight+1 && req.Expiry > 0 {
916961
return nil, errors.E(op, errors.Invalid, "expiry height must be above next block height")
917962
}
918963

@@ -939,20 +984,20 @@ func (w *Wallet) purchaseTickets(op errors.Op, req purchaseTicketRequest) ([]*ch
939984
// required plus fees for the split is larger than the
940985
// balance we have, wasting an address. In the future,
941986
// address this better and prevent address burning.
942-
account := req.account
987+
account := req.SourceAccount
943988

944989
// Calculate the current ticket price. If the DCP0001 deployment is not
945990
// active, fallback to querying the ticket price over RPC.
946991
ticketPrice, err := w.NextStakeDifficulty()
947992
if errors.Is(errors.Deployment, err) {
948-
ticketPrice, err = n.StakeDifficulty(context.TODO())
993+
ticketPrice, err = n.StakeDifficulty(ctx)
949994
}
950995
if err != nil {
951996
return nil, err
952997
}
953998

954999
// Ensure the ticket price does not exceed the spend limit if set.
955-
if req.spendLimit >= 0 && ticketPrice > req.spendLimit {
1000+
if req.spendLimit > 0 && ticketPrice > req.spendLimit {
9561001
return nil, errors.E(op, errors.Invalid,
9571002
errors.Errorf("ticket price %v above spend limit %v", ticketPrice, req.spendLimit))
9581003
}
@@ -975,7 +1020,7 @@ func (w *Wallet) purchaseTickets(op errors.Op, req purchaseTicketRequest) ([]*ch
9751020
var stakeSubmissionPkScriptSize int
9761021

9771022
// The stake submission pkScript is tagged by an OP_SSTX.
978-
switch req.ticketAddr.(type) {
1023+
switch req.VotingAddress.(type) {
9791024
case *dcrutil.AddressScriptHash:
9801025
stakeSubmissionPkScriptSize = txsizes.P2SHPkScriptSize + 1
9811026
case *dcrutil.AddressPubKeyHash, nil:
@@ -1044,12 +1089,12 @@ func (w *Wallet) purchaseTickets(op errors.Op, req purchaseTicketRequest) ([]*ch
10441089
// end user through the legacy RPC, so it should only ever be
10451090
// set by internal calls e.g. automatic ticket purchase.
10461091
if req.minBalance > 0 {
1047-
bal, err := w.CalculateAccountBalance(account, req.minConf)
1092+
bal, err := w.CalculateAccountBalance(account, req.MinConf)
10481093
if err != nil {
10491094
return nil, err
10501095
}
10511096

1052-
estimatedFundsUsed := neededPerTicket * dcrutil.Amount(req.numTickets)
1097+
estimatedFundsUsed := neededPerTicket * dcrutil.Amount(req.Count)
10531098
if req.minBalance+estimatedFundsUsed > bal.Spendable {
10541099
return nil, errors.E(op, errors.InsufficientBalance, errors.Errorf(
10551100
"estimated ending balance %v is below minimum requested balance %v",
@@ -1061,7 +1106,7 @@ func (w *Wallet) purchaseTickets(op errors.Op, req purchaseTicketRequest) ([]*ch
10611106
// immediately be consumed as tickets.
10621107
//
10631108
// This opens a write transaction.
1064-
splitTxAddr, err := w.NewInternalAddress(req.account, WithGapPolicyWrap())
1109+
splitTxAddr, err := w.NewInternalAddress(req.SourceAccount, WithGapPolicyWrap())
10651110
if err != nil {
10661111
return nil, err
10671112
}
@@ -1078,7 +1123,7 @@ func (w *Wallet) purchaseTickets(op errors.Op, req purchaseTicketRequest) ([]*ch
10781123
// first ticket commitment of a smaller amount to the pool, while
10791124
// paying themselves with the larger ticket commitment.
10801125
var splitOuts []*wire.TxOut
1081-
for i := 0; i < req.numTickets; i++ {
1126+
for i := 0; i < req.Count; i++ {
10821127
// No pool used.
10831128
if poolAddress == nil {
10841129
splitOuts = append(splitOuts, wire.NewTxOut(int64(neededPerTicket), splitPkScript))
@@ -1099,10 +1144,8 @@ func (w *Wallet) purchaseTickets(op errors.Op, req purchaseTicketRequest) ([]*ch
10991144
if txFeeIncrement == 0 {
11001145
txFeeIncrement = w.RelayFee()
11011146
}
1102-
1103-
options := w.NewCreateTxOptions(false)
1104-
splitTx, err := w.txToOutputsInternal(op, splitOuts, account, req.minConf,
1105-
n, false, txFeeIncrement, options)
1147+
splitTx, err := w.txToOutputsInternal(op, splitOuts, account, req.MinConf,
1148+
n, false, txFeeIncrement, nil)
11061149
if err != nil {
11071150
return nil, err
11081151
}
@@ -1119,16 +1162,16 @@ func (w *Wallet) purchaseTickets(op errors.Op, req purchaseTicketRequest) ([]*ch
11191162
"purchases: %v", err)
11201163
}
11211164
if len(watchOutPoints) > 0 {
1122-
err := n.LoadTxFilter(context.TODO(), false, nil, watchOutPoints)
1165+
err := n.LoadTxFilter(ctx, false, nil, watchOutPoints)
11231166
if err != nil {
11241167
log.Errorf("Failed to watch outpoints: %v", err)
11251168
}
11261169
}
11271170
}()
11281171

11291172
// Generate the tickets individually.
1130-
ticketHashes := make([]*chainhash.Hash, 0, req.numTickets)
1131-
for i := 0; i < req.numTickets; i++ {
1173+
ticketHashes := make([]*chainhash.Hash, 0, req.Count)
1174+
for i := 0; i < req.Count; i++ {
11321175
// Generate the extended outpoints that we need to use for ticket
11331176
// inputs. There are two inputs for pool tickets corresponding to the
11341177
// fees and the user subsidy, while user-handled tickets have only one
@@ -1180,18 +1223,18 @@ func (w *Wallet) purchaseTickets(op errors.Op, req purchaseTicketRequest) ([]*ch
11801223
// an address.
11811224
var addrVote, addrSubsidy dcrutil.Address
11821225
err := walletdb.Update(w.db, func(dbtx walletdb.ReadWriteTx) error {
1183-
addrVote = req.ticketAddr
1226+
addrVote = req.VotingAddress
11841227
if addrVote == nil {
11851228
addrVote = w.ticketAddress
11861229
if addrVote == nil {
1187-
addrVote, err = addrFunc(op, w.persistReturnedChild(dbtx), req.account)
1230+
addrVote, err = addrFunc(op, w.persistReturnedChild(dbtx), req.SourceAccount)
11881231
if err != nil {
11891232
return err
11901233
}
11911234
}
11921235
}
11931236

1194-
addrSubsidy, err = addrFunc(op, w.persistReturnedChild(dbtx), req.account)
1237+
addrSubsidy, err = addrFunc(op, w.persistReturnedChild(dbtx), req.SourceAccount)
11951238
return err
11961239
})
11971240
if err != nil {
@@ -1227,7 +1270,7 @@ func (w *Wallet) purchaseTickets(op errors.Op, req purchaseTicketRequest) ([]*ch
12271270
forSigning = append(forSigning, eopCredit)
12281271

12291272
// Set the expiry.
1230-
ticket.Expiry = uint32(req.expiry)
1273+
ticket.Expiry = uint32(req.Expiry)
12311274

12321275
err = walletdb.View(w.db, func(tx walletdb.ReadTx) error {
12331276
ns := tx.ReadBucket(waddrmgrNamespaceKey)
@@ -1261,7 +1304,7 @@ func (w *Wallet) purchaseTickets(op errors.Op, req purchaseTicketRequest) ([]*ch
12611304
}
12621305
// TODO: Send all tickets, and all split transactions, together. Purge
12631306
// transactions from DB if tickets cannot be sent.
1264-
err = n.PublishTransactions(context.TODO(), ticket)
1307+
err = n.PublishTransactions(ctx, ticket)
12651308
if err != nil {
12661309
return ticketHashes, errors.E(op, err)
12671310
}
@@ -1288,7 +1331,7 @@ func (w *Wallet) findEligibleOutputs(dbtx walletdb.ReadTx, account uint32, minco
12881331
// should be handled by the call to UnspentOutputs (or similar).
12891332
// Because one of these filters requires matching the output script to
12901333
// the desired account, this change depends on making wtxmgr a waddrmgr
1291-
// dependancy and requesting unspent outputs for a single account.
1334+
// dependency and requesting unspent outputs for a single account.
12921335
eligible := make([]udb.Credit, 0, len(unspent))
12931336
for i := range unspent {
12941337
output := unspent[i]
@@ -1543,7 +1586,7 @@ func (w *Wallet) signRevocation(addrmgrNs walletdb.ReadBucket, ticketPurchase, r
15431586
func newVoteScript(voteBits stake.VoteBits) ([]byte, error) {
15441587
b := make([]byte, 2+len(voteBits.ExtendedBits))
15451588
binary.LittleEndian.PutUint16(b[0:2], voteBits.Bits)
1546-
copy(b[2:], voteBits.ExtendedBits[:])
1589+
copy(b[2:], voteBits.ExtendedBits)
15471590
return txscript.GenerateProvablyPruneableOut(b)
15481591
}
15491592

@@ -1655,11 +1698,11 @@ func createUnsignedRevocation(ticketHash *chainhash.Hash, ticketPurchase *wire.M
16551698
sizeEstimate := txsizes.EstimateSerializeSize(scriptSizes, revocation.TxOut, 0)
16561699
feeEstimate := txrules.FeeForSerializeSize(feePerKB, sizeEstimate)
16571700

1658-
// Reduce the output value of one of the outputs to accomodate for the relay
1701+
// Reduce the output value of one of the outputs to accommodate for the relay
16591702
// fee. To avoid creating dust outputs, a suitable output value is reduced
16601703
// by the fee estimate only if it is large enough to not create dust. This
16611704
// code does not currently handle reducing the output values of multiple
1662-
// commitment outputs to accomodate for the fee.
1705+
// commitment outputs to accommodate for the fee.
16631706
for _, output := range revocation.TxOut {
16641707
if dcrutil.Amount(output.Value) > feeEstimate {
16651708
amount := dcrutil.Amount(output.Value) - feeEstimate

0 commit comments

Comments
 (0)