decred.org/dcrwallet/v3@v3.1.0/wallet/createtx.go (about) 1 // Copyright (c) 2013-2016 The btcsuite developers 2 // Copyright (c) 2015-2021 The Decred developers 3 // Use of this source code is governed by an ISC 4 // license that can be found in the LICENSE file. 5 6 package wallet 7 8 import ( 9 "context" 10 "crypto/rand" 11 "crypto/tls" 12 "encoding/binary" 13 "net" 14 "sort" 15 "time" 16 17 "decred.org/cspp/v2" 18 "decred.org/cspp/v2/coinjoin" 19 "decred.org/dcrwallet/v3/deployments" 20 "decred.org/dcrwallet/v3/errors" 21 "decred.org/dcrwallet/v3/internal/uniformprng" 22 "decred.org/dcrwallet/v3/rpc/client/dcrd" 23 "decred.org/dcrwallet/v3/wallet/txauthor" 24 "decred.org/dcrwallet/v3/wallet/txrules" 25 "decred.org/dcrwallet/v3/wallet/txsizes" 26 "decred.org/dcrwallet/v3/wallet/udb" 27 "decred.org/dcrwallet/v3/wallet/walletdb" 28 "github.com/decred/dcrd/blockchain/stake/v5" 29 blockchain "github.com/decred/dcrd/blockchain/standalone/v2" 30 "github.com/decred/dcrd/chaincfg/chainhash" 31 "github.com/decred/dcrd/chaincfg/v3" 32 "github.com/decred/dcrd/dcrec" 33 "github.com/decred/dcrd/dcrutil/v4" 34 "github.com/decred/dcrd/txscript/v4" 35 "github.com/decred/dcrd/txscript/v4/sign" 36 "github.com/decred/dcrd/txscript/v4/stdaddr" 37 "github.com/decred/dcrd/txscript/v4/stdscript" 38 "github.com/decred/dcrd/wire" 39 ) 40 41 // -------------------------------------------------------------------------------- 42 // Constants and simple functions 43 44 const ( 45 revocationFeeLimit = 1 << 14 46 47 // maxStandardTxSize is the maximum size allowed for transactions that 48 // are considered standard and will therefore be relayed and considered 49 // for mining. 50 // TODO: import from dcrd. 51 maxStandardTxSize = 100000 52 53 // sanityVerifyFlags are the flags used to enable and disable features of 54 // the txscript engine used for sanity checking of transactions signed by 55 // the wallet. 56 sanityVerifyFlags = txscript.ScriptDiscourageUpgradableNops | 57 txscript.ScriptVerifyCleanStack | 58 txscript.ScriptVerifyCheckLockTimeVerify | 59 txscript.ScriptVerifyCheckSequenceVerify | 60 txscript.ScriptVerifyTreasury 61 ) 62 63 // Input provides transaction inputs referencing spendable outputs. 64 type Input struct { 65 OutPoint wire.OutPoint 66 PrevOut wire.TxOut 67 } 68 69 // -------------------------------------------------------------------------------- 70 // Transaction creation 71 72 // OutputSelectionAlgorithm specifies the algorithm to use when selecting outputs 73 // to construct a transaction. 74 type OutputSelectionAlgorithm uint 75 76 const ( 77 // OutputSelectionAlgorithmDefault describes the default output selection 78 // algorithm. It is not optimized for any particular use case. 79 OutputSelectionAlgorithmDefault = iota 80 81 // OutputSelectionAlgorithmAll describes the output selection algorithm of 82 // picking every possible available output. This is useful for sweeping. 83 OutputSelectionAlgorithmAll 84 ) 85 86 // NewUnsignedTransaction constructs an unsigned transaction using unspent 87 // account outputs. 88 // 89 // The changeSource and inputSource parameters are optional and can be nil. 90 // When the changeSource is nil and change output should be added, an internal 91 // change address is created for the account. When the inputSource is nil, 92 // the inputs will be selected by the wallet. 93 func (w *Wallet) NewUnsignedTransaction(ctx context.Context, outputs []*wire.TxOut, 94 relayFeePerKb dcrutil.Amount, account uint32, minConf int32, 95 algo OutputSelectionAlgorithm, changeSource txauthor.ChangeSource, inputSource txauthor.InputSource) (*txauthor.AuthoredTx, error) { 96 97 const op errors.Op = "wallet.NewUnsignedTransaction" 98 99 ignoreInput := func(op *wire.OutPoint) bool { 100 _, ok := w.lockedOutpoints[outpoint{op.Hash, op.Index}] 101 return ok 102 } 103 defer w.lockedOutpointMu.Unlock() 104 w.lockedOutpointMu.Lock() 105 106 var authoredTx *txauthor.AuthoredTx 107 var changeSourceUpdates []func(walletdb.ReadWriteTx) error 108 err := walletdb.View(ctx, w.db, func(dbtx walletdb.ReadTx) error { 109 addrmgrNs := dbtx.ReadBucket(waddrmgrNamespaceKey) 110 _, tipHeight := w.txStore.MainChainTip(dbtx) 111 112 if account != udb.ImportedAddrAccount { 113 lastAcct, err := w.manager.LastAccount(addrmgrNs) 114 if err != nil { 115 return err 116 } 117 if account > lastAcct { 118 return errors.E(errors.NotExist, "missing account") 119 } 120 } 121 122 if inputSource == nil { 123 sourceImpl := w.txStore.MakeInputSource(dbtx, account, 124 minConf, tipHeight, ignoreInput) 125 switch algo { 126 case OutputSelectionAlgorithmDefault: 127 inputSource = sourceImpl.SelectInputs 128 case OutputSelectionAlgorithmAll: 129 // Wrap the source with one that always fetches the max amount 130 // available and ignores insufficient balance issues. 131 inputSource = func(dcrutil.Amount) (*txauthor.InputDetail, error) { 132 inputDetail, err := sourceImpl.SelectInputs(dcrutil.MaxAmount) 133 if errors.Is(err, errors.InsufficientBalance) { 134 err = nil 135 } 136 return inputDetail, err 137 } 138 default: 139 return errors.E(errors.Invalid, 140 errors.Errorf("unknown output selection algorithm %v", algo)) 141 } 142 } 143 144 if changeSource == nil { 145 changeSource = &p2PKHChangeSource{ 146 persist: w.deferPersistReturnedChild(ctx, &changeSourceUpdates), 147 account: account, 148 wallet: w, 149 ctx: context.Background(), 150 } 151 } 152 153 var err error 154 authoredTx, err = txauthor.NewUnsignedTransaction(outputs, relayFeePerKb, 155 inputSource, changeSource, w.chainParams.MaxTxSize) 156 if err != nil { 157 return err 158 } 159 160 return nil 161 }) 162 if err != nil { 163 return nil, errors.E(op, err) 164 } 165 if len(changeSourceUpdates) != 0 { 166 err := walletdb.Update(ctx, w.db, func(tx walletdb.ReadWriteTx) error { 167 for _, up := range changeSourceUpdates { 168 err := up(tx) 169 if err != nil { 170 return err 171 } 172 } 173 return nil 174 }) 175 if err != nil { 176 return nil, errors.E(op, err) 177 } 178 } 179 return authoredTx, nil 180 } 181 182 // secretSource is an implementation of txauthor.SecretSource for the wallet's 183 // address manager. 184 type secretSource struct { 185 *udb.Manager 186 addrmgrNs walletdb.ReadBucket 187 doneFuncs []func() 188 } 189 190 func (s *secretSource) GetKey(addr stdaddr.Address) ([]byte, dcrec.SignatureType, bool, error) { 191 privKey, done, err := s.Manager.PrivateKey(s.addrmgrNs, addr) 192 if err != nil { 193 return nil, 0, false, err 194 } 195 s.doneFuncs = append(s.doneFuncs, done) 196 return privKey.Serialize(), dcrec.STEcdsaSecp256k1, true, nil 197 } 198 199 func (s *secretSource) GetScript(addr stdaddr.Address) ([]byte, error) { 200 return s.Manager.RedeemScript(s.addrmgrNs, addr) 201 } 202 203 // CreatedTx holds the state of a newly-created transaction and the change 204 // output (if one was added). 205 type CreatedTx struct { 206 MsgTx *wire.MsgTx 207 ChangeAddr stdaddr.Address 208 ChangeIndex int // negative if no change 209 Fee dcrutil.Amount 210 } 211 212 // insertIntoTxMgr inserts a newly created transaction into the tx store 213 // as unconfirmed. 214 func (w *Wallet) insertIntoTxMgr(dbtx walletdb.ReadWriteTx, msgTx *wire.MsgTx) (*udb.TxRecord, error) { 215 // Create transaction record and insert into the db. 216 rec, err := udb.NewTxRecordFromMsgTx(msgTx, time.Now()) 217 if err != nil { 218 return nil, err 219 } 220 221 err = w.txStore.InsertMemPoolTx(dbtx, rec) 222 if err != nil { 223 return nil, err 224 } 225 return rec, nil 226 } 227 228 // insertCreditsIntoTxMgr inserts the wallet credits from msgTx to the wallet's 229 // transaction store. It assumes msgTx is a regular transaction, which will 230 // cause balance issues if this is called from a code path where msgtx is not 231 // guaranteed to be a regular tx. 232 func (w *Wallet) insertCreditsIntoTxMgr(op errors.Op, dbtx walletdb.ReadWriteTx, msgTx *wire.MsgTx, rec *udb.TxRecord) error { 233 addrmgrNs := dbtx.ReadWriteBucket(waddrmgrNamespaceKey) 234 235 // Check every output to determine whether it is controlled by a wallet 236 // key. If so, mark the output as a credit. 237 for i, output := range msgTx.TxOut { 238 _, addrs := stdscript.ExtractAddrs(output.Version, output.PkScript, w.chainParams) 239 for _, addr := range addrs { 240 ma, err := w.manager.Address(addrmgrNs, addr) 241 if err == nil { 242 // TODO: Credits should be added with the 243 // account they belong to, so wtxmgr is able to 244 // track per-account balances. 245 err = w.txStore.AddCredit(dbtx, rec, nil, 246 uint32(i), ma.Internal(), ma.Account()) 247 if err != nil { 248 return errors.E(op, err) 249 } 250 err = w.markUsedAddress(op, dbtx, ma) 251 if err != nil { 252 return err 253 } 254 log.Debugf("Marked address %v used", addr) 255 continue 256 } 257 258 // Missing addresses are skipped. Other errors should 259 // be propagated. 260 if !errors.Is(err, errors.NotExist) { 261 return errors.E(op, err) 262 } 263 } 264 } 265 266 return nil 267 } 268 269 // insertMultisigOutIntoTxMgr inserts a multisignature output into the 270 // transaction store database. 271 func (w *Wallet) insertMultisigOutIntoTxMgr(dbtx walletdb.ReadWriteTx, msgTx *wire.MsgTx, index uint32) error { 272 // Create transaction record and insert into the db. 273 rec, err := udb.NewTxRecordFromMsgTx(msgTx, time.Now()) 274 if err != nil { 275 return err 276 } 277 278 return w.txStore.AddMultisigOut(dbtx, rec, nil, index) 279 } 280 281 // checkHighFees performs a high fee check if enabled and possible, returning an 282 // error if the transaction pays high fees. 283 func (w *Wallet) checkHighFees(totalInput dcrutil.Amount, tx *wire.MsgTx) error { 284 if w.allowHighFees { 285 return nil 286 } 287 if txrules.PaysHighFees(totalInput, tx) { 288 return errors.E(errors.Policy, "high fee") 289 } 290 return nil 291 } 292 293 // publishAndWatch publishes an authored transaction to the network and begins watching for 294 // relevant transactions. 295 func (w *Wallet) publishAndWatch(ctx context.Context, op errors.Op, n NetworkBackend, tx *wire.MsgTx, 296 watch []wire.OutPoint) error { 297 298 if n == nil { 299 var err error 300 n, err = w.NetworkBackend() 301 if err != nil { 302 return errors.E(op, err) 303 } 304 } 305 306 err := n.PublishTransactions(ctx, tx) 307 if err != nil { 308 hash := tx.TxHash() 309 log.Errorf("Abandoning transaction %v which failed to publish", &hash) 310 if err := w.AbandonTransaction(ctx, &hash); err != nil { 311 log.Errorf("Cannot abandon %v: %v", &hash, err) 312 } 313 return errors.E(op, err) 314 } 315 316 // Watch for future relevant transactions. 317 _, err = w.watchHDAddrs(ctx, false, n) 318 if err != nil { 319 log.Errorf("Failed to watch for future address usage after publishing "+ 320 "transaction: %v", err) 321 } 322 if len(watch) > 0 { 323 err := n.LoadTxFilter(ctx, false, nil, watch) 324 if err != nil { 325 log.Errorf("Failed to watch outpoints: %v", err) 326 } 327 } 328 return nil 329 } 330 331 type authorTx struct { 332 outputs []*wire.TxOut 333 account uint32 334 changeAccount uint32 335 minconf int32 336 randomizeChangeIdx bool 337 txFee dcrutil.Amount 338 dontSignTx bool 339 isTreasury bool 340 341 atx *txauthor.AuthoredTx 342 changeSourceUpdates []func(walletdb.ReadWriteTx) error 343 watch []wire.OutPoint 344 } 345 346 // authorTx creates a (typically signed) transaction which includes each output 347 // from outputs. Previous outputs to redeem are chosen from the passed 348 // account's UTXO set and minconf policy. An additional output may be added to 349 // return change to the wallet. An appropriate fee is included based on the 350 // wallet's current relay fee. The wallet must be unlocked to create the 351 // transaction. 352 func (w *Wallet) authorTx(ctx context.Context, op errors.Op, a *authorTx) error { 353 var unlockOutpoints []*wire.OutPoint 354 defer func() { 355 for _, op := range unlockOutpoints { 356 delete(w.lockedOutpoints, outpoint{op.Hash, op.Index}) 357 } 358 w.lockedOutpointMu.Unlock() 359 }() 360 ignoreInput := func(op *wire.OutPoint) bool { 361 _, ok := w.lockedOutpoints[outpoint{op.Hash, op.Index}] 362 return ok 363 } 364 w.lockedOutpointMu.Lock() 365 366 var atx *txauthor.AuthoredTx 367 var changeSourceUpdates []func(walletdb.ReadWriteTx) error 368 err := walletdb.View(ctx, w.db, func(dbtx walletdb.ReadTx) error { 369 addrmgrNs := dbtx.ReadBucket(waddrmgrNamespaceKey) 370 371 // Create the unsigned transaction. 372 _, tipHeight := w.txStore.MainChainTip(dbtx) 373 inputSource := w.txStore.MakeInputSource(dbtx, a.account, 374 a.minconf, tipHeight, ignoreInput) 375 var changeSource txauthor.ChangeSource 376 if a.isTreasury { 377 changeSource = &p2PKHTreasuryChangeSource{ 378 persist: w.deferPersistReturnedChild(ctx, 379 &changeSourceUpdates), 380 account: a.changeAccount, 381 wallet: w, 382 ctx: ctx, 383 } 384 } else { 385 changeSource = &p2PKHChangeSource{ 386 persist: w.deferPersistReturnedChild(ctx, 387 &changeSourceUpdates), 388 account: a.changeAccount, 389 wallet: w, 390 ctx: ctx, 391 gapPolicy: gapPolicyWrap, 392 } 393 } 394 var err error 395 atx, err = txauthor.NewUnsignedTransaction(a.outputs, a.txFee, 396 inputSource.SelectInputs, changeSource, 397 w.chainParams.MaxTxSize) 398 if err != nil { 399 return err 400 } 401 for _, in := range atx.Tx.TxIn { 402 prev := &in.PreviousOutPoint 403 w.lockedOutpoints[outpoint{prev.Hash, prev.Index}] = struct{}{} 404 unlockOutpoints = append(unlockOutpoints, prev) 405 } 406 407 // Randomize change position, if change exists, before signing. 408 // This doesn't affect the serialize size, so the change amount 409 // will still be valid. 410 if atx.ChangeIndex >= 0 && a.randomizeChangeIdx { 411 atx.RandomizeChangePosition() 412 } 413 414 // TADDs need to use version 3 txs. 415 if a.isTreasury { 416 // This check ensures that if NewUnsignedTransaction is 417 // updated to generate a different transaction version 418 // we error out loudly instead of failing to validate 419 // in some obscure way. 420 // 421 // TODO: maybe isTreasury should be passed into 422 // NewUnsignedTransaction? 423 if atx.Tx.Version != wire.TxVersion { 424 return errors.E(op, "violated assumption: "+ 425 "expected unsigned tx to be version 1") 426 } 427 atx.Tx.Version = wire.TxVersionTreasury 428 } 429 430 if !a.dontSignTx { 431 // Sign the transaction. 432 secrets := &secretSource{Manager: w.manager, addrmgrNs: addrmgrNs} 433 err = atx.AddAllInputScripts(secrets) 434 for _, done := range secrets.doneFuncs { 435 done() 436 } 437 } 438 return err 439 }) 440 if err != nil { 441 return errors.E(op, err) 442 } 443 444 // Warn when spending UTXOs controlled by imported keys created change for 445 // the default account. 446 if atx.ChangeIndex >= 0 && a.account == udb.ImportedAddrAccount { 447 changeAmount := dcrutil.Amount(atx.Tx.TxOut[atx.ChangeIndex].Value) 448 log.Warnf("Spend from imported account produced change: moving"+ 449 " %v from imported account into default account.", changeAmount) 450 } 451 452 err = w.checkHighFees(atx.TotalInput, atx.Tx) 453 if err != nil { 454 return errors.E(op, err) 455 } 456 457 if !a.dontSignTx { 458 // Ensure valid signatures were created. 459 err = validateMsgTx(op, atx.Tx, atx.PrevScripts) 460 if err != nil { 461 return errors.E(op, err) 462 } 463 } 464 465 a.atx = atx 466 a.changeSourceUpdates = changeSourceUpdates 467 return nil 468 } 469 470 // recordAuthoredTx records an authored transaction to the wallet's database. It 471 // also updates the database for change addresses used by the new transaction. 472 // 473 // As a side effect of recording the transaction to the wallet, clients 474 // subscribed to new tx notifications will also be notified of the new 475 // transaction. 476 func (w *Wallet) recordAuthoredTx(ctx context.Context, op errors.Op, a *authorTx) error { 477 rec, err := udb.NewTxRecordFromMsgTx(a.atx.Tx, time.Now()) 478 if err != nil { 479 return errors.E(op, err) 480 } 481 482 w.lockedOutpointMu.Lock() 483 defer w.lockedOutpointMu.Unlock() 484 485 // To avoid a race between publishing a transaction and potentially opening 486 // a database view during PublishTransaction, the update must be committed 487 // before publishing the transaction to the network. 488 var watch []wire.OutPoint 489 err = walletdb.Update(ctx, w.db, func(dbtx walletdb.ReadWriteTx) error { 490 for _, up := range a.changeSourceUpdates { 491 err := up(dbtx) 492 if err != nil { 493 return err 494 } 495 } 496 497 // TODO: this can be improved by not using the same codepath as notified 498 // relevant transactions, since this does a lot of extra work. 499 var err error 500 watch, err = w.processTransactionRecord(ctx, dbtx, rec, nil, nil) 501 return err 502 }) 503 if err != nil { 504 return errors.E(op, err) 505 } 506 507 a.watch = watch 508 return nil 509 } 510 511 // txToMultisig spends funds to a multisig output, partially signs the 512 // transaction, then returns fund 513 func (w *Wallet) txToMultisig(ctx context.Context, op errors.Op, account uint32, amount dcrutil.Amount, pubkeys [][]byte, 514 nRequired int8, minconf int32) (*CreatedTx, stdaddr.Address, []byte, error) { 515 516 defer w.lockedOutpointMu.Unlock() 517 w.lockedOutpointMu.Lock() 518 519 var created *CreatedTx 520 var addr stdaddr.Address 521 var msScript []byte 522 err := walletdb.Update(ctx, w.db, func(dbtx walletdb.ReadWriteTx) error { 523 var err error 524 created, addr, msScript, err = w.txToMultisigInternal(ctx, op, dbtx, 525 account, amount, pubkeys, nRequired, minconf) 526 return err 527 }) 528 if err != nil { 529 return nil, nil, nil, errors.E(op, err) 530 } 531 return created, addr, msScript, nil 532 } 533 534 func (w *Wallet) txToMultisigInternal(ctx context.Context, op errors.Op, dbtx walletdb.ReadWriteTx, account uint32, amount dcrutil.Amount, 535 pubkeys [][]byte, nRequired int8, minconf int32) (*CreatedTx, stdaddr.Address, []byte, error) { 536 537 addrmgrNs := dbtx.ReadWriteBucket(waddrmgrNamespaceKey) 538 539 txToMultisigError := func(err error) (*CreatedTx, stdaddr.Address, []byte, error) { 540 return nil, nil, nil, err 541 } 542 543 n, err := w.NetworkBackend() 544 if err != nil { 545 return txToMultisigError(err) 546 } 547 548 // Get current block's height and hash. 549 _, topHeight := w.txStore.MainChainTip(dbtx) 550 551 // Add in some extra for fees. TODO In the future, make a better 552 // fee estimator. 553 var feeEstForTx dcrutil.Amount 554 switch w.chainParams.Net { 555 case wire.MainNet: 556 feeEstForTx = 5e7 557 case 0x48e7a065: // testnet2 558 feeEstForTx = 5e7 559 case wire.TestNet3: 560 feeEstForTx = 5e7 561 default: 562 feeEstForTx = 3e4 563 } 564 amountRequired := amount + feeEstForTx 565 566 // Instead of taking reward addresses by arg, just create them now and 567 // automatically find all eligible outputs from all current utxos. 568 const minAmount = 0 569 const maxResults = 0 570 eligible, err := w.findEligibleOutputsAmount(dbtx, account, minconf, 571 amountRequired, topHeight, minAmount, maxResults) 572 if err != nil { 573 return txToMultisigError(errors.E(op, err)) 574 } 575 if eligible == nil { 576 return txToMultisigError(errors.E(op, "not enough funds to send to multisig address")) 577 } 578 for i := range eligible { 579 op := &eligible[i].OutPoint 580 w.lockedOutpoints[outpoint{op.Hash, op.Index}] = struct{}{} 581 } 582 defer func() { 583 for i := range eligible { 584 op := &eligible[i].OutPoint 585 delete(w.lockedOutpoints, outpoint{op.Hash, op.Index}) 586 } 587 }() 588 589 msgtx := wire.NewMsgTx() 590 scriptSizes := make([]int, 0, len(eligible)) 591 // Fill out inputs. 592 forSigning := make([]Input, 0, len(eligible)) 593 totalInput := dcrutil.Amount(0) 594 for _, e := range eligible { 595 txIn := wire.NewTxIn(&e.OutPoint, e.PrevOut.Value, nil) 596 msgtx.AddTxIn(txIn) 597 totalInput += dcrutil.Amount(e.PrevOut.Value) 598 forSigning = append(forSigning, e) 599 scriptSizes = append(scriptSizes, txsizes.RedeemP2SHSigScriptSize) 600 } 601 602 // Insert a multi-signature output, then insert this P2SH 603 // hash160 into the address manager and the transaction 604 // manager. 605 msScript, err := stdscript.MultiSigScriptV0(int(nRequired), pubkeys...) 606 if err != nil { 607 return txToMultisigError(errors.E(op, err)) 608 } 609 _, err = w.manager.ImportScript(addrmgrNs, msScript) 610 if err != nil { 611 // We don't care if we've already used this address. 612 if !errors.Is(err, errors.Exist) { 613 return txToMultisigError(errors.E(op, err)) 614 } 615 } 616 scAddr, err := stdaddr.NewAddressScriptHashV0(msScript, w.chainParams) 617 if err != nil { 618 return txToMultisigError(errors.E(op, err)) 619 } 620 vers, p2shScript := scAddr.PaymentScript() 621 txOut := &wire.TxOut{ 622 Value: int64(amount), 623 PkScript: p2shScript, 624 Version: vers, 625 } 626 msgtx.AddTxOut(txOut) 627 628 // Add change if we need it. 629 changeSize := 0 630 if totalInput > amount+feeEstForTx { 631 changeSize = txsizes.P2PKHPkScriptSize 632 } 633 feeSize := txsizes.EstimateSerializeSize(scriptSizes, msgtx.TxOut, changeSize) 634 feeEst := txrules.FeeForSerializeSize(w.RelayFee(), feeSize) 635 636 if totalInput < amount+feeEst { 637 return txToMultisigError(errors.E(op, errors.InsufficientBalance)) 638 } 639 if totalInput > amount+feeEst { 640 changeSource := p2PKHChangeSource{ 641 persist: w.persistReturnedChild(ctx, dbtx), 642 account: account, 643 wallet: w, 644 ctx: ctx, 645 } 646 647 pkScript, vers, err := changeSource.Script() 648 if err != nil { 649 return txToMultisigError(err) 650 } 651 change := totalInput - (amount + feeEst) 652 msgtx.AddTxOut(&wire.TxOut{ 653 Value: int64(change), 654 Version: vers, 655 PkScript: pkScript, 656 }) 657 } 658 659 err = w.signP2PKHMsgTx(msgtx, forSigning, addrmgrNs) 660 if err != nil { 661 return txToMultisigError(errors.E(op, err)) 662 } 663 664 err = w.checkHighFees(totalInput, msgtx) 665 if err != nil { 666 return txToMultisigError(errors.E(op, err)) 667 } 668 669 err = n.PublishTransactions(ctx, msgtx) 670 if err != nil { 671 return txToMultisigError(errors.E(op, err)) 672 } 673 674 // Request updates from dcrd for new transactions sent to this 675 // script hash address. 676 err = n.LoadTxFilter(ctx, false, []stdaddr.Address{scAddr}, nil) 677 if err != nil { 678 return txToMultisigError(errors.E(op, err)) 679 } 680 681 err = w.insertMultisigOutIntoTxMgr(dbtx, msgtx, 0) 682 if err != nil { 683 return txToMultisigError(errors.E(op, err)) 684 } 685 686 created := &CreatedTx{ 687 MsgTx: msgtx, 688 ChangeAddr: nil, 689 ChangeIndex: -1, 690 } 691 692 return created, scAddr, msScript, nil 693 } 694 695 // validateMsgTx verifies transaction input scripts for tx. All previous output 696 // scripts from outputs redeemed by the transaction, in the same order they are 697 // spent, must be passed in the prevScripts slice. 698 func validateMsgTx(op errors.Op, tx *wire.MsgTx, prevScripts [][]byte) error { 699 for i, prevScript := range prevScripts { 700 vm, err := txscript.NewEngine(prevScript, tx, i, 701 sanityVerifyFlags, scriptVersionAssumed, nil) 702 if err != nil { 703 return errors.E(op, err) 704 } 705 err = vm.Execute() 706 if err != nil { 707 prevOut := &tx.TxIn[i].PreviousOutPoint 708 sigScript := tx.TxIn[i].SignatureScript 709 710 log.Errorf("Script validation failed (outpoint %v pkscript %x sigscript %x): %v", 711 prevOut, prevScript, sigScript, err) 712 return errors.E(op, errors.ScriptFailure, err) 713 } 714 } 715 return nil 716 } 717 718 func creditScripts(credits []Input) [][]byte { 719 scripts := make([][]byte, 0, len(credits)) 720 for _, c := range credits { 721 scripts = append(scripts, c.PrevOut.PkScript) 722 } 723 return scripts 724 } 725 726 // compressWallet compresses all the utxos in a wallet into a single change 727 // address. For use when it becomes dusty. 728 func (w *Wallet) compressWallet(ctx context.Context, op errors.Op, maxNumIns int, account uint32, changeAddr stdaddr.Address) (*chainhash.Hash, error) { 729 defer w.lockedOutpointMu.Unlock() 730 w.lockedOutpointMu.Lock() 731 732 var hash *chainhash.Hash 733 err := walletdb.Update(ctx, w.db, func(dbtx walletdb.ReadWriteTx) error { 734 var err error 735 hash, err = w.compressWalletInternal(ctx, op, dbtx, maxNumIns, account, changeAddr) 736 return err 737 }) 738 if err != nil { 739 return nil, errors.E(op, err) 740 } 741 return hash, nil 742 } 743 744 func (w *Wallet) compressWalletInternal(ctx context.Context, op errors.Op, dbtx walletdb.ReadWriteTx, maxNumIns int, account uint32, 745 changeAddr stdaddr.Address) (*chainhash.Hash, error) { 746 747 addrmgrNs := dbtx.ReadWriteBucket(waddrmgrNamespaceKey) 748 749 n, err := w.NetworkBackend() 750 if err != nil { 751 return nil, errors.E(op, err) 752 } 753 754 // Get current block's height 755 _, tipHeight := w.txStore.MainChainTip(dbtx) 756 757 minconf := int32(1) 758 eligible, err := w.findEligibleOutputs(dbtx, account, minconf, tipHeight) 759 if err != nil { 760 return nil, errors.E(op, err) 761 } 762 if len(eligible) <= 1 { 763 return nil, errors.E(op, "too few outputs to consolidate") 764 } 765 for i := range eligible { 766 op := eligible[i].OutPoint 767 w.lockedOutpoints[outpoint{op.Hash, op.Index}] = struct{}{} 768 } 769 770 defer func() { 771 for i := range eligible { 772 op := &eligible[i].OutPoint 773 delete(w.lockedOutpoints, outpoint{op.Hash, op.Index}) 774 } 775 }() 776 777 // Check if output address is default, and generate a new address if needed 778 if changeAddr == nil { 779 const accountName = "" // not used, so can be faked. 780 changeAddr, err = w.newChangeAddress(ctx, op, w.persistReturnedChild(ctx, dbtx), 781 accountName, account, gapPolicyIgnore) 782 if err != nil { 783 return nil, errors.E(op, err) 784 } 785 } 786 vers, pkScript := changeAddr.PaymentScript() 787 msgtx := wire.NewMsgTx() 788 msgtx.AddTxOut(&wire.TxOut{ 789 Value: 0, 790 PkScript: pkScript, 791 Version: vers, 792 }) 793 maximumTxSize := w.chainParams.MaxTxSize 794 if w.chainParams.Net == wire.MainNet { 795 maximumTxSize = maxStandardTxSize 796 } 797 798 // Add the txins using all the eligible outputs. 799 totalAdded := dcrutil.Amount(0) 800 scriptSizes := make([]int, 0, maxNumIns) 801 forSigning := make([]Input, 0, maxNumIns) 802 count := 0 803 for _, e := range eligible { 804 if count >= maxNumIns { 805 break 806 } 807 // Add the size of a wire.OutPoint 808 if msgtx.SerializeSize() > maximumTxSize { 809 break 810 } 811 812 txIn := wire.NewTxIn(&e.OutPoint, e.PrevOut.Value, nil) 813 msgtx.AddTxIn(txIn) 814 totalAdded += dcrutil.Amount(e.PrevOut.Value) 815 forSigning = append(forSigning, e) 816 scriptSizes = append(scriptSizes, txsizes.RedeemP2PKHSigScriptSize) 817 count++ 818 } 819 820 // Get an initial fee estimate based on the number of selected inputs 821 // and added outputs, with no change. 822 szEst := txsizes.EstimateSerializeSize(scriptSizes, msgtx.TxOut, 0) 823 feeEst := txrules.FeeForSerializeSize(w.RelayFee(), szEst) 824 825 msgtx.TxOut[0].Value = int64(totalAdded - feeEst) 826 827 err = w.signP2PKHMsgTx(msgtx, forSigning, addrmgrNs) 828 if err != nil { 829 return nil, errors.E(op, err) 830 } 831 err = validateMsgTx(op, msgtx, creditScripts(forSigning)) 832 if err != nil { 833 return nil, errors.E(op, err) 834 } 835 836 err = w.checkHighFees(totalAdded, msgtx) 837 if err != nil { 838 return nil, errors.E(op, err) 839 } 840 841 err = n.PublishTransactions(ctx, msgtx) 842 if err != nil { 843 return nil, errors.E(op, err) 844 } 845 846 // Insert the transaction and credits into the transaction manager. 847 rec, err := w.insertIntoTxMgr(dbtx, msgtx) 848 if err != nil { 849 return nil, errors.E(op, err) 850 } 851 err = w.insertCreditsIntoTxMgr(op, dbtx, msgtx, rec) 852 if err != nil { 853 return nil, err 854 } 855 856 txHash := msgtx.TxHash() 857 log.Infof("Successfully consolidated funds in transaction %v", &txHash) 858 859 return &txHash, nil 860 } 861 862 // makeTicket creates a ticket from a split transaction output. It can optionally 863 // create a ticket that pays a fee to a pool if a pool input and pool address are 864 // passed. 865 func makeTicket(params *chaincfg.Params, inputPool *Input, input *Input, addrVote stdaddr.StakeAddress, 866 addrSubsidy stdaddr.StakeAddress, ticketCost int64, addrPool stdaddr.StakeAddress) (*wire.MsgTx, error) { 867 868 mtx := wire.NewMsgTx() 869 870 if addrPool != nil && inputPool != nil { 871 txIn := wire.NewTxIn(&inputPool.OutPoint, inputPool.PrevOut.Value, []byte{}) 872 mtx.AddTxIn(txIn) 873 } 874 875 txIn := wire.NewTxIn(&input.OutPoint, input.PrevOut.Value, []byte{}) 876 mtx.AddTxIn(txIn) 877 878 // Create a new script which pays to the provided address with an 879 // SStx tagged output. 880 if addrVote == nil { 881 return nil, errors.E(errors.Invalid, "nil vote address") 882 } 883 vers, pkScript := addrVote.VotingRightsScript() 884 885 txOut := &wire.TxOut{ 886 Value: ticketCost, 887 PkScript: pkScript, 888 Version: vers, 889 } 890 mtx.AddTxOut(txOut) 891 892 // Obtain the commitment amounts. 893 var amountsCommitted []int64 894 userSubsidyNullIdx := 0 895 var err error 896 if addrPool == nil { 897 _, amountsCommitted, err = stake.SStxNullOutputAmounts( 898 []int64{input.PrevOut.Value}, []int64{0}, ticketCost) 899 if err != nil { 900 return nil, err 901 } 902 903 } else { 904 _, amountsCommitted, err = stake.SStxNullOutputAmounts( 905 []int64{inputPool.PrevOut.Value, input.PrevOut.Value}, []int64{0, 0}, ticketCost) 906 if err != nil { 907 return nil, err 908 } 909 userSubsidyNullIdx = 1 910 } 911 912 // Zero value P2PKH addr. 913 zeroed := [20]byte{} 914 addrZeroed, err := stdaddr.NewAddressPubKeyHashEcdsaSecp256k1V0(zeroed[:], params) 915 if err != nil { 916 return nil, err 917 } 918 919 // 2. (Optional) If we're passed a pool address, make an extra 920 // commitment to the pool. 921 if addrPool != nil { 922 vers, pkScript := addrPool.RewardCommitmentScript( 923 amountsCommitted[0], 0, revocationFeeLimit) 924 txout := &wire.TxOut{ 925 Value: 0, 926 PkScript: pkScript, 927 Version: vers, 928 } 929 mtx.AddTxOut(txout) 930 931 // Create a new script which pays to the provided address with an 932 // SStx change tagged output. 933 vers, pkScript = addrZeroed.StakeChangeScript() 934 935 txOut = &wire.TxOut{ 936 Value: 0, 937 PkScript: pkScript, 938 Version: vers, 939 } 940 mtx.AddTxOut(txOut) 941 } 942 943 // 3. Create the commitment and change output paying to the user. 944 // 945 // Create an OP_RETURN push containing the pubkeyhash to send rewards to. 946 // Apply limits to revocations for fees while not allowing 947 // fees for votes. 948 vers, pkScript = addrSubsidy.RewardCommitmentScript( 949 amountsCommitted[userSubsidyNullIdx], 0, revocationFeeLimit) 950 if err != nil { 951 return nil, errors.E(errors.Invalid, 952 errors.Errorf("commitment address %v", addrSubsidy)) 953 } 954 txout := &wire.TxOut{ 955 Value: 0, 956 PkScript: pkScript, 957 Version: vers, 958 } 959 mtx.AddTxOut(txout) 960 961 // Create a new script which pays to the provided address with an 962 // SStx change tagged output. 963 vers, pkScript = addrZeroed.StakeChangeScript() 964 txOut = &wire.TxOut{ 965 Value: 0, 966 PkScript: pkScript, 967 Version: vers, 968 } 969 mtx.AddTxOut(txOut) 970 971 // Make sure we generated a valid SStx. 972 if err := stake.CheckSStx(mtx); err != nil { 973 return nil, errors.E(errors.Op("stake.CheckSStx"), errors.Bug, err) 974 } 975 976 return mtx, nil 977 } 978 979 var p2pkhSizedScript = make([]byte, 25) 980 981 func (w *Wallet) mixedSplit(ctx context.Context, req *PurchaseTicketsRequest, neededPerTicket dcrutil.Amount) (tx *wire.MsgTx, outIndexes []int, err error) { 982 // Use txauthor to perform input selection and change amount 983 // calculations for the unmixed portions of the coinjoin. 984 mixOut := make([]*wire.TxOut, req.Count) 985 for i := 0; i < req.Count; i++ { 986 mixOut[i] = &wire.TxOut{Value: int64(neededPerTicket), Version: 0, PkScript: p2pkhSizedScript} 987 } 988 relayFee := w.RelayFee() 989 var changeSourceUpdates []func(walletdb.ReadWriteTx) error 990 defer func() { 991 if err != nil { 992 return 993 } 994 995 err = walletdb.Update(ctx, w.db, func(dbtx walletdb.ReadWriteTx) error { 996 for _, f := range changeSourceUpdates { 997 if err := f(dbtx); err != nil { 998 return err 999 } 1000 } 1001 return nil 1002 }) 1003 }() 1004 var unlockOutpoints []*wire.OutPoint 1005 defer func() { 1006 if len(unlockOutpoints) != 0 { 1007 w.lockedOutpointMu.Lock() 1008 for _, op := range unlockOutpoints { 1009 delete(w.lockedOutpoints, outpoint{op.Hash, op.Index}) 1010 } 1011 w.lockedOutpointMu.Unlock() 1012 } 1013 }() 1014 ignoreInput := func(op *wire.OutPoint) bool { 1015 _, ok := w.lockedOutpoints[outpoint{op.Hash, op.Index}] 1016 return ok 1017 } 1018 1019 w.lockedOutpointMu.Lock() 1020 var atx *txauthor.AuthoredTx 1021 err = walletdb.View(ctx, w.db, func(dbtx walletdb.ReadTx) error { 1022 _, tipHeight := w.txStore.MainChainTip(dbtx) 1023 inputSource := w.txStore.MakeInputSource(dbtx, req.SourceAccount, 1024 req.MinConf, tipHeight, ignoreInput) 1025 changeSource := &p2PKHChangeSource{ 1026 persist: w.deferPersistReturnedChild(ctx, &changeSourceUpdates), 1027 account: req.ChangeAccount, 1028 wallet: w, 1029 ctx: ctx, 1030 gapPolicy: gapPolicyIgnore, 1031 } 1032 var err error 1033 atx, err = txauthor.NewUnsignedTransaction(mixOut, relayFee, 1034 inputSource.SelectInputs, changeSource, 1035 w.chainParams.MaxTxSize) 1036 if err != nil { 1037 return err 1038 } 1039 for _, in := range atx.Tx.TxIn { 1040 prev := &in.PreviousOutPoint 1041 w.lockedOutpoints[outpoint{prev.Hash, prev.Index}] = struct{}{} 1042 unlockOutpoints = append(unlockOutpoints, prev) 1043 } 1044 return nil 1045 }) 1046 w.lockedOutpointMu.Unlock() 1047 if err != nil { 1048 return 1049 } 1050 for _, in := range atx.Tx.TxIn { 1051 log.Infof("selected input %v (%v) for ticket purchase split transaction", 1052 in.PreviousOutPoint, dcrutil.Amount(in.ValueIn)) 1053 } 1054 1055 var change *wire.TxOut 1056 if atx.ChangeIndex >= 0 { 1057 change = atx.Tx.TxOut[atx.ChangeIndex] 1058 } 1059 if change != nil && dcrutil.Amount(change.Value) < smallestMixChange(relayFee) { 1060 change = nil 1061 } 1062 const ( 1063 txVersion = 1 1064 locktime = 0 1065 expiry = 0 1066 ) 1067 pairing := coinjoin.EncodeDesc(coinjoin.P2PKHv0, int64(neededPerTicket), txVersion, locktime, expiry) 1068 cj := w.newCsppJoin(ctx, change, neededPerTicket, req.MixedSplitAccount, req.MixedAccountBranch, req.Count) 1069 for i, in := range atx.Tx.TxIn { 1070 cj.addTxIn(atx.PrevScripts[i], in) 1071 } 1072 1073 csppSession, err := cspp.NewSession(rand.Reader, debugLog, pairing, req.Count) 1074 if err != nil { 1075 return 1076 } 1077 var conn net.Conn 1078 if req.DialCSPPServer != nil { 1079 conn, err = req.DialCSPPServer(ctx, "tcp", req.CSPPServer) 1080 } else { 1081 conn, err = tls.Dial("tcp", req.CSPPServer, nil) 1082 } 1083 if err != nil { 1084 return 1085 } 1086 defer conn.Close() 1087 log.Infof("Dialed CSPPServer %v -> %v", conn.LocalAddr(), conn.RemoteAddr()) 1088 err = csppSession.DiceMix(ctx, conn, cj) 1089 if err != nil { 1090 return 1091 } 1092 splitTx := cj.tx 1093 splitTxHash := splitTx.TxHash() 1094 log.Infof("Completed CoinShuffle++ mix of ticket split transaction %v", &splitTxHash) 1095 return splitTx, cj.mixOutputIndexes(), nil 1096 } 1097 1098 func (w *Wallet) individualSplit(ctx context.Context, req *PurchaseTicketsRequest, neededPerTicket dcrutil.Amount) (tx *wire.MsgTx, outIndexes []int, err error) { 1099 // Fetch the single use split address to break tickets into, to 1100 // immediately be consumed as tickets. 1101 // 1102 // This opens a write transaction. 1103 splitTxAddr, err := w.NewInternalAddress(ctx, req.SourceAccount, WithGapPolicyWrap()) 1104 if err != nil { 1105 return 1106 } 1107 1108 vers, splitPkScript := splitTxAddr.PaymentScript() 1109 1110 // Create the split transaction by using txToOutputs. This varies 1111 // based upon whether or not the user is using a stake pool or not. 1112 // For the default stake pool implementation, the user pays out the 1113 // first ticket commitment of a smaller amount to the pool, while 1114 // paying themselves with the larger ticket commitment. 1115 var splitOuts []*wire.TxOut 1116 for i := 0; i < req.Count; i++ { 1117 splitOuts = append(splitOuts, &wire.TxOut{ 1118 Value: int64(neededPerTicket), 1119 PkScript: splitPkScript, 1120 Version: vers, 1121 }) 1122 outIndexes = append(outIndexes, i) 1123 } 1124 1125 const op errors.Op = "individualSplit" 1126 a := &authorTx{ 1127 outputs: splitOuts, 1128 account: req.SourceAccount, 1129 changeAccount: req.ChangeAccount, 1130 minconf: req.MinConf, 1131 randomizeChangeIdx: false, 1132 txFee: w.RelayFee(), 1133 dontSignTx: req.DontSignTx, 1134 isTreasury: false, 1135 } 1136 err = w.authorTx(ctx, op, a) 1137 if err != nil { 1138 return 1139 } 1140 err = w.recordAuthoredTx(ctx, op, a) 1141 if err != nil { 1142 return 1143 } 1144 if !req.DontSignTx { 1145 err = w.publishAndWatch(ctx, op, nil, a.atx.Tx, a.watch) 1146 if err != nil { 1147 return 1148 } 1149 } 1150 1151 tx = a.atx.Tx 1152 return 1153 } 1154 1155 func (w *Wallet) vspSplit(ctx context.Context, req *PurchaseTicketsRequest, neededPerTicket, vspFee dcrutil.Amount) (tx *wire.MsgTx, outIndexes []int, err error) { 1156 // Fetch the single use split address to break tickets into, to 1157 // immediately be consumed as tickets. 1158 // 1159 // This opens a write transaction. 1160 splitTxAddr, err := w.NewInternalAddress(ctx, req.SourceAccount, WithGapPolicyWrap()) 1161 if err != nil { 1162 return 1163 } 1164 1165 vers, splitPkScript := splitTxAddr.PaymentScript() 1166 1167 // Create the split transaction by using authorTx. This varies 1168 // based upon whether or not the user is using a stake pool or not. 1169 // For the default stake pool implementation, the user pays out the 1170 // first ticket commitment of a smaller amount to the pool, while 1171 // paying themselves with the larger ticket commitment. 1172 var splitOuts []*wire.TxOut 1173 for i := 0; i < req.Count; i++ { 1174 userAmt := neededPerTicket - vspFee 1175 splitOuts = append(splitOuts, &wire.TxOut{ 1176 Value: int64(vspFee), 1177 PkScript: splitPkScript, 1178 Version: vers, 1179 }) 1180 splitOuts = append(splitOuts, &wire.TxOut{ 1181 Value: int64(userAmt), 1182 PkScript: splitPkScript, 1183 Version: vers, 1184 }) 1185 outIndexes = append(outIndexes, i*2) 1186 } 1187 1188 const op errors.Op = "vspSplit" 1189 a := &authorTx{ 1190 outputs: splitOuts, 1191 account: req.SourceAccount, 1192 changeAccount: req.ChangeAccount, 1193 minconf: req.MinConf, 1194 randomizeChangeIdx: false, 1195 txFee: w.RelayFee(), 1196 dontSignTx: req.DontSignTx, 1197 isTreasury: false, 1198 } 1199 err = w.authorTx(ctx, op, a) 1200 if err != nil { 1201 return 1202 } 1203 err = w.recordAuthoredTx(ctx, op, a) 1204 if err != nil { 1205 return 1206 } 1207 if !req.DontSignTx { 1208 err = w.publishAndWatch(ctx, op, nil, a.atx.Tx, a.watch) 1209 if err != nil { 1210 return 1211 } 1212 } 1213 1214 tx = a.atx.Tx 1215 return 1216 } 1217 1218 var errVSPFeeRequiresUTXOSplit = errors.New("paying VSP fee requires UTXO split") 1219 1220 // purchaseTickets indicates to the wallet that a ticket should be purchased 1221 // using all currently available funds. The ticket address parameter in the 1222 // request can be nil in which case the ticket address associated with the 1223 // wallet instance will be used. Also, when the spend limit in the request is 1224 // greater than or equal to 0, tickets that cost more than that limit will 1225 // return an error that not enough funds are available. 1226 func (w *Wallet) purchaseTickets(ctx context.Context, op errors.Op, 1227 n NetworkBackend, req *PurchaseTicketsRequest) (*PurchaseTicketsResponse, error) { 1228 // Ensure the minimum number of required confirmations is positive. 1229 if req.MinConf < 0 { 1230 return nil, errors.E(op, errors.Invalid, "negative minconf") 1231 } 1232 // Need a positive or zero expiry that is higher than the next block to 1233 // generate. 1234 if req.Expiry < 0 { 1235 return nil, errors.E(op, errors.Invalid, "negative expiry") 1236 } 1237 1238 // Perform a sanity check on expiry. 1239 var tipHeight int32 1240 err := walletdb.View(ctx, w.db, func(dbtx walletdb.ReadTx) error { 1241 _, tipHeight = w.txStore.MainChainTip(dbtx) 1242 return nil 1243 }) 1244 if err != nil { 1245 return nil, err 1246 } 1247 if req.Expiry <= tipHeight+1 && req.Expiry > 0 { 1248 return nil, errors.E(op, errors.Invalid, "expiry height must be above next block height") 1249 } 1250 1251 stakeAddrFunc := func(op errors.Op, account, branch uint32) (stdaddr.StakeAddress, uint32, error) { 1252 const accountName = "" // not used, so can be faked. 1253 a, err := w.nextAddress(ctx, op, w.persistReturnedChild(ctx, nil), accountName, 1254 account, branch, WithGapPolicyIgnore()) 1255 if err != nil { 1256 return nil, 0, err 1257 } 1258 var idx uint32 1259 if xpa, ok := a.(*xpubAddress); ok { 1260 idx = xpa.child 1261 } 1262 switch a := a.(type) { 1263 case stdaddr.StakeAddress: 1264 return a, idx, nil 1265 default: 1266 return nil, 0, errors.E(errors.Invalid, "account does "+ 1267 "not return compatible stake addresses") 1268 } 1269 } 1270 1271 if w.addressReuse && req.CSPPServer == "" { 1272 xpub := w.addressBuffers[udb.DefaultAccountNum].albExternal.branchXpub 1273 addr, err := deriveChildAddress(xpub, 0, w.chainParams) 1274 if err != nil { 1275 err = errors.E(op, err) 1276 } 1277 stakeAddr, ok := addr.(stdaddr.StakeAddress) 1278 if !ok { 1279 err = errors.E(op, errors.Invalid, "account does not return "+ 1280 "compatible stake addresses") 1281 } 1282 stakeAddrFunc = func(errors.Op, uint32, uint32) (stdaddr.StakeAddress, uint32, error) { 1283 return stakeAddr, 0, err 1284 } 1285 } 1286 1287 // Calculate the current ticket price. If the DCP0001 deployment is not 1288 // active, fallback to querying the ticket price over RPC. 1289 ticketPrice, err := w.NextStakeDifficulty(ctx) 1290 if errors.Is(err, errors.Deployment) { 1291 ticketPrice, err = n.StakeDifficulty(ctx) 1292 } 1293 if err != nil { 1294 return nil, err 1295 } 1296 1297 // Try to get the pool address from the request. If none exists 1298 // in the request, try to get the global pool address. Then do 1299 // the same for pool fees, but check sanity too. 1300 poolAddress := req.VSPAddress 1301 if poolAddress == nil { 1302 poolAddress = w.poolAddress 1303 } 1304 poolFees := req.VSPFees 1305 if poolFees == 0.0 { 1306 poolFees = w.poolFees 1307 } 1308 if poolAddress != nil && poolFees == 0.0 { 1309 return nil, errors.E(op, errors.Invalid, "stakepool fee percent unset") 1310 } 1311 1312 var stakeSubmissionPkScriptSize int 1313 1314 // The stake submission pkScript is tagged by an OP_SSTX. 1315 switch req.VotingAddress.(type) { 1316 case *stdaddr.AddressScriptHashV0: 1317 stakeSubmissionPkScriptSize = txsizes.P2SHPkScriptSize + 1 1318 case *stdaddr.AddressPubKeyHashEcdsaSecp256k1V0, PubKeyHashAddress, nil: 1319 stakeSubmissionPkScriptSize = txsizes.P2PKHPkScriptSize + 1 1320 default: 1321 return nil, errors.E(op, errors.Invalid, 1322 "ticket address must either be P2SH or P2PKH") 1323 } 1324 1325 // Make sure that we have enough funds. Calculate different 1326 // ticket required amounts depending on whether or not a 1327 // pool output is needed. If the ticket fee increment is 1328 // unset in the request, use the global ticket fee increment. 1329 var neededPerTicket dcrutil.Amount 1330 var estSize int 1331 ticketRelayFee := w.RelayFee() 1332 1333 if poolAddress == nil { 1334 // A solo ticket has: 1335 // - a single input redeeming a P2PKH for the worst case size 1336 // - a P2PKH or P2SH stake submission output 1337 // - a ticket commitment output 1338 // - an OP_SSTXCHANGE tagged P2PKH or P2SH change output 1339 // 1340 // NB: The wallet currently only supports P2PKH change addresses. 1341 // The network supports both P2PKH and P2SH change addresses however. 1342 inSizes := []int{txsizes.RedeemP2PKHSigScriptSize} 1343 outSizes := []int{stakeSubmissionPkScriptSize, 1344 txsizes.TicketCommitmentScriptSize, txsizes.P2PKHPkScriptSize + 1} 1345 estSize = txsizes.EstimateSerializeSizeFromScriptSizes(inSizes, 1346 outSizes, 0) 1347 } else { 1348 // A pool ticket has: 1349 // - two inputs redeeming a P2PKH for the worst case size 1350 // - a P2PKH or P2SH stake submission output 1351 // - two ticket commitment outputs 1352 // - two OP_SSTXCHANGE tagged P2PKH or P2SH change outputs 1353 // 1354 // NB: The wallet currently only supports P2PKH change addresses. 1355 // The network supports both P2PKH and P2SH change addresses however. 1356 inSizes := []int{txsizes.RedeemP2PKHSigScriptSize, 1357 txsizes.RedeemP2PKHSigScriptSize} 1358 outSizes := []int{stakeSubmissionPkScriptSize, 1359 txsizes.TicketCommitmentScriptSize, txsizes.TicketCommitmentScriptSize, 1360 txsizes.P2PKHPkScriptSize + 1, txsizes.P2PKHPkScriptSize + 1} 1361 estSize = txsizes.EstimateSerializeSizeFromScriptSizes(inSizes, 1362 outSizes, 0) 1363 } 1364 1365 ticketFee := txrules.FeeForSerializeSize(ticketRelayFee, estSize) 1366 neededPerTicket = ticketFee + ticketPrice 1367 1368 // If we need to calculate the amount for a pool fee percentage, 1369 // do so now. 1370 var vspFee dcrutil.Amount 1371 if poolAddress != nil { 1372 // poolAddress is only used with the legacy stakepool 1373 const dcp0010Active = false 1374 vspFee = txrules.StakePoolTicketFee(ticketPrice, ticketFee, 1375 tipHeight, poolFees, w.ChainParams(), dcp0010Active) 1376 } 1377 1378 // After tickets are created and published, watch for future 1379 // relevant transactions 1380 var watchOutPoints []wire.OutPoint 1381 defer func() { 1382 // Cancellation of the request context should not prevent the 1383 // watching of addresses and outpoints that need to be watched. 1384 // A better solution would be to watch for the data first, 1385 // before publishing transactions. 1386 ctx := context.TODO() 1387 _, err := w.watchHDAddrs(ctx, false, n) 1388 if err != nil { 1389 log.Errorf("Failed to watch for future addresses after ticket "+ 1390 "purchases: %v", err) 1391 } 1392 if len(watchOutPoints) > 0 { 1393 err := n.LoadTxFilter(ctx, false, nil, watchOutPoints) 1394 if err != nil { 1395 log.Errorf("Failed to watch outpoints: %v", err) 1396 } 1397 } 1398 }() 1399 1400 var vspFeeCredits, ticketCredits [][]Input 1401 unlockCredits := true 1402 total := func(ins []Input) (v int64) { 1403 for _, in := range ins { 1404 v += in.PrevOut.Value 1405 } 1406 return 1407 } 1408 if req.VSPFeePaymentProcess != nil { 1409 if req.VSPFeeProcess == nil { 1410 return nil, errors.E(op, errors.Bug, "VSPFeeProcess "+ 1411 "may not be nil if VSPServerProcess is non-nil") 1412 } 1413 feePrice, err := req.VSPFeeProcess(ctx) 1414 if err != nil { 1415 return nil, err 1416 } 1417 // In SPV mode, DCP0010 is assumed to have activated. This 1418 // results in a larger fee calculation for the purposes of UTXO 1419 // selection. In RPC mode the actual activation can be 1420 // determined. 1421 dcp0010Active := true 1422 switch n := n.(type) { 1423 case *dcrd.RPC: 1424 dcp0010Active, err = deployments.DCP0010Active(ctx, 1425 tipHeight, w.chainParams, n) 1426 if err != nil { 1427 return nil, err 1428 } 1429 } 1430 fee := txrules.StakePoolTicketFee(ticketPrice, ticketFee, 1431 tipHeight, feePrice, w.chainParams, 1432 dcp0010Active) 1433 1434 // Reserve outputs for number of buys. 1435 vspFeeCredits = make([][]Input, 0, req.Count) 1436 ticketCredits = make([][]Input, 0, req.Count) 1437 defer func() { 1438 if unlockCredits { 1439 for _, credit := range vspFeeCredits { 1440 for _, c := range credit { 1441 log.Debugf("unlocked unneeded credit for vsp fee tx: %v", 1442 c.OutPoint.String()) 1443 w.UnlockOutpoint(&c.OutPoint.Hash, c.OutPoint.Index) 1444 } 1445 } 1446 } 1447 }() 1448 if req.extraSplitOutput != nil { 1449 vspFeeCredits = make([][]Input, 1) 1450 vspFeeCredits[0] = []Input{*req.extraSplitOutput} 1451 op := &req.extraSplitOutput.OutPoint 1452 w.LockOutpoint(&op.Hash, op.Index) 1453 } 1454 var lowBalance bool 1455 for i := 0; i < req.Count; i++ { 1456 if req.extraSplitOutput == nil { 1457 credits, err := w.ReserveOutputsForAmount(ctx, 1458 req.SourceAccount, fee, req.MinConf) 1459 1460 if errors.Is(err, errors.InsufficientBalance) { 1461 lowBalance = true 1462 break 1463 } 1464 if err != nil { 1465 log.Errorf("ReserveOutputsForAmount failed: %v", err) 1466 return nil, err 1467 } 1468 vspFeeCredits = append(vspFeeCredits, credits) 1469 } 1470 1471 credits, err := w.ReserveOutputsForAmount(ctx, req.SourceAccount, 1472 ticketPrice, req.MinConf) 1473 if errors.Is(err, errors.InsufficientBalance) { 1474 lowBalance = true 1475 credits, _ = w.reserveOutputs(ctx, req.SourceAccount, 1476 req.MinConf) 1477 if len(credits) != 0 { 1478 ticketCredits = append(ticketCredits, credits) 1479 } 1480 break 1481 } 1482 if err != nil { 1483 log.Errorf("ReserveOutputsForAmount failed: %v", err) 1484 return nil, err 1485 } 1486 ticketCredits = append(ticketCredits, credits) 1487 } 1488 for _, credits := range ticketCredits { 1489 for _, c := range credits { 1490 log.Debugf("unlocked credit for ticket tx: %v", 1491 c.OutPoint.String()) 1492 w.UnlockOutpoint(&c.OutPoint.Hash, c.OutPoint.Index) 1493 } 1494 } 1495 if lowBalance { 1496 // When there is UTXO contention between reserved fee 1497 // UTXOs and the tickets that can be purchased, UTXOs 1498 // which were selected for paying VSP fees are instead 1499 // allocated towards purchasing tickets. We sort the 1500 // UTXOs picked for fees and tickets by decreasing 1501 // amounts and incrementally reserve them for ticket 1502 // purchases while reducing the total number of fees 1503 // (and therefore tickets) that will be purchased. The 1504 // final UTXOs chosen for ticket purchases must be 1505 // unlocked for UTXO selection to work, while all inputs 1506 // for fee payments must be locked. 1507 credits := vspFeeCredits[:len(vspFeeCredits):len(vspFeeCredits)] 1508 credits = append(credits, ticketCredits...) 1509 sort.Slice(credits, func(i, j int) bool { 1510 return total(credits[i]) > total(credits[j]) 1511 }) 1512 if len(credits) == 0 { 1513 return nil, errors.E(errors.InsufficientBalance) 1514 } 1515 if req.Count > len(credits)-1 { 1516 req.Count = len(credits) - 1 1517 } 1518 var freedBalance int64 1519 extraSplit := true 1520 for req.Count > 1 { 1521 for _, c := range credits[0] { 1522 freedBalance += c.PrevOut.Value 1523 w.UnlockOutpoint(&c.OutPoint.Hash, c.OutPoint.Index) 1524 } 1525 credits = credits[1:] 1526 // XXX this is a bad estimate because it doesn't 1527 // consider the transaction fees 1528 if freedBalance > int64(ticketPrice)*int64(req.Count) { 1529 extraSplit = false 1530 break 1531 } 1532 req.Count-- 1533 } 1534 vspFeeCredits = credits 1535 var remaining int64 1536 for _, c := range vspFeeCredits { 1537 remaining += total(c) 1538 for i := range c { 1539 w.LockOutpoint(&c[i].OutPoint.Hash, c[i].OutPoint.Index) 1540 } 1541 } 1542 1543 if req.Count < 2 && extraSplit { 1544 // XXX still a bad estimate 1545 if int64(ticketPrice) > freedBalance+remaining { 1546 return nil, errors.E(errors.InsufficientBalance) 1547 } 1548 // A new transaction may need to be created to 1549 // split a single UTXO into two: one to pay the 1550 // VSP fee, and a second to fund the ticket 1551 // purchase. This error condition is left to 1552 // the caller to detect and perform. 1553 return nil, errVSPFeeRequiresUTXOSplit 1554 } 1555 } 1556 log.Infof("Reserved credits for %d tickets: total fee: %v", req.Count, fee) 1557 for _, credit := range vspFeeCredits { 1558 for _, c := range credit { 1559 log.Debugf("%s reserved for vsp fee transaction", c.OutPoint.String()) 1560 } 1561 } 1562 } 1563 1564 purchaseTicketsResponse := &PurchaseTicketsResponse{} 1565 var splitTx *wire.MsgTx 1566 var splitOutputIndexes []int 1567 for { 1568 switch { 1569 case req.CSPPServer != "": 1570 splitTx, splitOutputIndexes, err = w.mixedSplit(ctx, req, neededPerTicket) 1571 case req.VSPAddress != nil: 1572 splitTx, splitOutputIndexes, err = w.vspSplit(ctx, req, neededPerTicket, vspFee) 1573 default: 1574 splitTx, splitOutputIndexes, err = w.individualSplit(ctx, req, neededPerTicket) 1575 } 1576 if errors.Is(err, errors.InsufficientBalance) && req.Count > 1 { 1577 req.Count-- 1578 if len(vspFeeCredits) > 0 { 1579 for _, in := range vspFeeCredits[0] { 1580 w.UnlockOutpoint(&in.OutPoint.Hash, in.OutPoint.Index) 1581 } 1582 vspFeeCredits = vspFeeCredits[1:] 1583 } 1584 continue 1585 } 1586 if err != nil { 1587 return nil, errors.E(op, err) 1588 } 1589 break 1590 } 1591 purchaseTicketsResponse.SplitTx = splitTx 1592 1593 // Process and publish split tx. 1594 if !req.DontSignTx { 1595 rec, err := udb.NewTxRecordFromMsgTx(splitTx, time.Now()) 1596 if err != nil { 1597 return nil, err 1598 } 1599 w.lockedOutpointMu.Lock() 1600 err = walletdb.Update(ctx, w.db, func(dbtx walletdb.ReadWriteTx) error { 1601 watch, err := w.processTransactionRecord(ctx, dbtx, rec, nil, nil) 1602 watchOutPoints = append(watchOutPoints, watch...) 1603 return err 1604 }) 1605 w.lockedOutpointMu.Unlock() 1606 if err != nil { 1607 return nil, err 1608 } 1609 w.recentlyPublishedMu.Lock() 1610 w.recentlyPublished[rec.Hash] = struct{}{} 1611 w.recentlyPublishedMu.Unlock() 1612 err = n.PublishTransactions(ctx, splitTx) 1613 if err != nil { 1614 return nil, err 1615 } 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 1641 // Create each ticket. 1642 ticketHashes := make([]*chainhash.Hash, 0, req.Count) 1643 tickets := make([]*wire.MsgTx, 0, req.Count) 1644 outpoint := wire.OutPoint{Hash: splitTx.TxHash()} 1645 for _, index := range splitOutputIndexes { 1646 // Generate the extended outpoints that we need to use for ticket 1647 // inputs. There are two inputs for pool tickets corresponding to the 1648 // fees and the user subsidy, while user-handled tickets have only one 1649 // input. 1650 var eopPool, eop *Input 1651 if poolAddress == nil { 1652 op := outpoint 1653 op.Index = uint32(index) 1654 log.Infof("Split output is %v", &op) 1655 txOut := splitTx.TxOut[index] 1656 eop = &Input{ 1657 OutPoint: op, 1658 PrevOut: *txOut, 1659 } 1660 } else { 1661 vspOutPoint := outpoint 1662 vspOutPoint.Index = uint32(index) 1663 vspOutput := splitTx.TxOut[vspOutPoint.Index] 1664 eopPool = &Input{ 1665 OutPoint: vspOutPoint, 1666 PrevOut: *vspOutput, 1667 } 1668 myOutPoint := outpoint 1669 myOutPoint.Index = uint32(index + 1) 1670 myOutput := splitTx.TxOut[myOutPoint.Index] 1671 eop = &Input{ 1672 OutPoint: myOutPoint, 1673 PrevOut: *myOutput, 1674 } 1675 } 1676 1677 // If the user hasn't specified a voting address 1678 // to delegate voting to, just use an address from 1679 // this wallet. Check the passed address from the 1680 // request first, then check the ticket address 1681 // stored from the configuation. Finally, generate 1682 // an address. 1683 var addrVote stdaddr.StakeAddress 1684 1685 // If req.UseVotingAccount is true, always take the submission 1686 // script's address from the voting account. This is intended 1687 // to be used with a special account type. The signing address 1688 // for the same index is saved to the database. That address is 1689 // later used to sign messages sent to a vspd related to this 1690 // ticket. 1691 if req.UseVotingAccount { 1692 var idx uint32 1693 addrVote, idx, err = stakeAddrFunc(op, req.VotingAccount, 1) 1694 if err != nil { 1695 return nil, err 1696 } 1697 _, err := w.signingAddressAtIdx(ctx, op, w.persistReturnedChild(ctx, nil), 1698 req.VotingAccount, idx) 1699 if err != nil { 1700 return nil, err 1701 } 1702 } else { 1703 addrVote = req.VotingAddress 1704 if addrVote == nil && req.CSPPServer == "" { 1705 addrVote = w.ticketAddress 1706 } 1707 if addrVote == nil { 1708 addrVote, _, err = stakeAddrFunc(op, req.VotingAccount, 1) 1709 if err != nil { 1710 return nil, err 1711 } 1712 } 1713 } 1714 subsidyAccount := req.SourceAccount 1715 var branch uint32 = 1 1716 if req.CSPPServer != "" { 1717 subsidyAccount = req.MixedAccount 1718 branch = req.MixedAccountBranch 1719 } 1720 addrSubsidy, _, err := stakeAddrFunc(op, subsidyAccount, branch) 1721 if err != nil { 1722 return nil, err 1723 } 1724 1725 var ticket *wire.MsgTx 1726 w.lockedOutpointMu.Lock() 1727 err = walletdb.Update(ctx, w.db, func(dbtx walletdb.ReadWriteTx) error { 1728 // Generate the ticket msgTx and sign it if DontSignTx is false. 1729 ticket, err = makeTicket(w.chainParams, eopPool, eop, addrVote, 1730 addrSubsidy, int64(ticketPrice), poolAddress) 1731 if err != nil { 1732 return err 1733 } 1734 // Set the expiry. 1735 ticket.Expiry = uint32(req.Expiry) 1736 1737 ticketHash := ticket.TxHash() 1738 ticketHashes = append(ticketHashes, &ticketHash) 1739 tickets = append(tickets, ticket) 1740 1741 purchaseTicketsResponse.Tickets = tickets 1742 purchaseTicketsResponse.TicketHashes = ticketHashes 1743 1744 if req.DontSignTx { 1745 return nil 1746 } 1747 // Sign and publish tx if DontSignTx is false 1748 var forSigning []Input 1749 if eopPool != nil { 1750 forSigning = append(forSigning, *eopPool) 1751 } 1752 forSigning = append(forSigning, *eop) 1753 1754 ns := dbtx.ReadBucket(waddrmgrNamespaceKey) 1755 err = w.signP2PKHMsgTx(ticket, forSigning, ns) 1756 if err != nil { 1757 return err 1758 } 1759 err = validateMsgTx(op, ticket, creditScripts(forSigning)) 1760 if err != nil { 1761 return err 1762 } 1763 1764 err = w.checkHighFees(dcrutil.Amount(eop.PrevOut.Value), ticket) 1765 if err != nil { 1766 return err 1767 } 1768 1769 rec, err := udb.NewTxRecordFromMsgTx(ticket, time.Now()) 1770 if err != nil { 1771 return err 1772 } 1773 1774 watch, err := w.processTransactionRecord(ctx, dbtx, rec, nil, nil) 1775 watchOutPoints = append(watchOutPoints, watch...) 1776 if err != nil { 1777 return err 1778 } 1779 1780 w.recentlyPublishedMu.Lock() 1781 w.recentlyPublished[rec.Hash] = struct{}{} 1782 w.recentlyPublishedMu.Unlock() 1783 1784 return nil 1785 }) 1786 w.lockedOutpointMu.Unlock() 1787 if err != nil { 1788 return purchaseTicketsResponse, errors.E(op, err) 1789 } 1790 } 1791 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 1802 } 1803 return purchaseTicketsResponse, errors.E(op, ctx.Err()) 1804 case <-timer.C: 1805 } 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) 1832 } 1833 continue 1834 } 1835 // watch for outpoints change. 1836 _, err = udb.NewTxRecordFromMsgTx(feeTx, time.Now()) 1837 if err != nil { 1838 return nil, err 1839 } 1840 } 1841 1842 return purchaseTicketsResponse, err 1843 } 1844 1845 // ReserveOutputsForAmount returns locked spendable outpoints from the given 1846 // account. It is the responsibility of the caller to unlock the outpoints. 1847 func (w *Wallet) ReserveOutputsForAmount(ctx context.Context, account uint32, amount dcrutil.Amount, minconf int32) ([]Input, error) { 1848 defer w.lockedOutpointMu.Unlock() 1849 w.lockedOutpointMu.Lock() 1850 1851 var outputs []Input 1852 err := walletdb.View(ctx, w.db, func(dbtx walletdb.ReadTx) error { 1853 // Get current block's height 1854 _, tipHeight := w.txStore.MainChainTip(dbtx) 1855 1856 var err error 1857 const minAmount = 0 1858 const maxResults = 0 1859 outputs, err = w.findEligibleOutputsAmount(dbtx, account, minconf, amount, tipHeight, 1860 minAmount, maxResults) 1861 if err != nil { 1862 return err 1863 } 1864 1865 for _, output := range outputs { 1866 w.lockedOutpoints[outpoint{output.OutPoint.Hash, output.OutPoint.Index}] = struct{}{} 1867 } 1868 1869 return nil 1870 }) 1871 if err != nil { 1872 return nil, err 1873 } 1874 1875 return outputs, nil 1876 } 1877 1878 func (w *Wallet) reserveOutputs(ctx context.Context, account uint32, minconf int32) ([]Input, error) { 1879 defer w.lockedOutpointMu.Unlock() 1880 w.lockedOutpointMu.Lock() 1881 1882 var outputs []Input 1883 err := walletdb.View(ctx, w.db, func(dbtx walletdb.ReadTx) error { 1884 // Get current block's height 1885 _, tipHeight := w.txStore.MainChainTip(dbtx) 1886 1887 var err error 1888 outputs, err = w.findEligibleOutputs(dbtx, account, minconf, tipHeight) 1889 if err != nil { 1890 return err 1891 } 1892 1893 for _, output := range outputs { 1894 w.lockedOutpoints[outpoint{output.OutPoint.Hash, output.OutPoint.Index}] = struct{}{} 1895 } 1896 1897 return nil 1898 }) 1899 if err != nil { 1900 return nil, err 1901 } 1902 1903 return outputs, nil 1904 } 1905 1906 // This can't be optimized to use the random selection because it must read all 1907 // outputs. Prefer to use findEligibleOutputsAmount with various filter options 1908 // instead. 1909 func (w *Wallet) findEligibleOutputs(dbtx walletdb.ReadTx, account uint32, minconf int32, 1910 currentHeight int32) ([]Input, error) { 1911 1912 addrmgrNs := dbtx.ReadBucket(waddrmgrNamespaceKey) 1913 1914 unspent, err := w.txStore.UnspentOutputs(dbtx) 1915 if err != nil { 1916 return nil, err 1917 } 1918 1919 // TODO: Eventually all of these filters (except perhaps output locking) 1920 // should be handled by the call to UnspentOutputs (or similar). 1921 // Because one of these filters requires matching the output script to 1922 // the desired account, this change depends on making wtxmgr a waddrmgr 1923 // dependency and requesting unspent outputs for a single account. 1924 eligible := make([]Input, 0, len(unspent)) 1925 for i := range unspent { 1926 output := unspent[i] 1927 1928 // Locked unspent outputs are skipped. 1929 if _, locked := w.lockedOutpoints[outpoint{output.Hash, output.Index}]; locked { 1930 continue 1931 } 1932 1933 // Only include this output if it meets the required number of 1934 // confirmations. Coinbase transactions must have have reached 1935 // maturity before their outputs may be spent. 1936 if !confirmed(minconf, output.Height, currentHeight) { 1937 continue 1938 } 1939 1940 // Filter out unspendable outputs, that is, remove those that 1941 // (at this time) are not P2PKH outputs. Other inputs must be 1942 // manually included in transactions and sent (for example, 1943 // using createrawtransaction, signrawtransaction, and 1944 // sendrawtransaction). 1945 class, addrs := stdscript.ExtractAddrs(scriptVersionAssumed, output.PkScript, w.chainParams) 1946 if len(addrs) != 1 { 1947 continue 1948 } 1949 1950 // Make sure everything we're trying to spend is actually mature. 1951 switch class { 1952 case stdscript.STStakeGenPubKeyHash, stdscript.STStakeGenScriptHash: 1953 if !coinbaseMatured(w.chainParams, output.Height, currentHeight) { 1954 continue 1955 } 1956 case stdscript.STStakeRevocationPubKeyHash, stdscript.STStakeRevocationScriptHash: 1957 if !coinbaseMatured(w.chainParams, output.Height, currentHeight) { 1958 continue 1959 } 1960 case stdscript.STTreasuryAdd, stdscript.STTreasuryGenPubKeyHash, stdscript.STTreasuryGenScriptHash: 1961 if !coinbaseMatured(w.chainParams, output.Height, currentHeight) { 1962 continue 1963 } 1964 case stdscript.STStakeChangePubKeyHash, stdscript.STStakeChangeScriptHash: 1965 if !ticketChangeMatured(w.chainParams, output.Height, currentHeight) { 1966 continue 1967 } 1968 case stdscript.STPubKeyHashEcdsaSecp256k1: 1969 if output.FromCoinBase { 1970 if !coinbaseMatured(w.chainParams, output.Height, currentHeight) { 1971 continue 1972 } 1973 } 1974 default: 1975 continue 1976 } 1977 1978 // Only include the output if it is associated with the passed 1979 // account. 1980 // 1981 // TODO: Handle multisig outputs by determining if enough of the 1982 // addresses are controlled. 1983 addrAcct, err := w.manager.AddrAccount(addrmgrNs, addrs[0]) 1984 if err != nil || addrAcct != account { 1985 continue 1986 } 1987 1988 txOut := &wire.TxOut{ 1989 Value: int64(output.Amount), 1990 Version: wire.DefaultPkScriptVersion, // XXX 1991 PkScript: output.PkScript, 1992 } 1993 eligible = append(eligible, Input{ 1994 OutPoint: output.OutPoint, 1995 PrevOut: *txOut, 1996 }) 1997 } 1998 1999 return eligible, nil 2000 } 2001 2002 // findEligibleOutputsAmount uses wtxmgr to find a number of unspent outputs 2003 // while doing maturity checks there. 2004 func (w *Wallet) findEligibleOutputsAmount(dbtx walletdb.ReadTx, account uint32, minconf int32, 2005 amount dcrutil.Amount, currentHeight int32, minAmount dcrutil.Amount, maxResults int) ([]Input, error) { 2006 2007 addrmgrNs := dbtx.ReadBucket(waddrmgrNamespaceKey) 2008 2009 var eligible []Input 2010 var outTotal dcrutil.Amount 2011 seen := make(map[outpoint]struct{}) 2012 skip := func(output *udb.Credit) bool { 2013 if _, ok := seen[outpoint{output.Hash, output.Index}]; ok { 2014 return true 2015 } 2016 2017 // Locked unspent outputs are skipped. 2018 if _, locked := w.lockedOutpoints[outpoint{output.Hash, output.Index}]; locked { 2019 return true 2020 } 2021 2022 // Only include this output if it meets the required number of 2023 // confirmations. Coinbase transactions must have have reached 2024 // maturity before their outputs may be spent. 2025 if !confirmed(minconf, output.Height, currentHeight) { 2026 return true 2027 } 2028 2029 // When a minumum amount is required, skip when it is less. 2030 if minAmount != 0 && output.Amount < minAmount { 2031 return true 2032 } 2033 2034 // Filter out unspendable outputs, that is, remove those that 2035 // (at this time) are not P2PKH outputs. Other inputs must be 2036 // manually included in transactions and sent (for example, 2037 // using createrawtransaction, signrawtransaction, and 2038 // sendrawtransaction). 2039 class, addrs := stdscript.ExtractAddrs(scriptVersionAssumed, output.PkScript, w.chainParams) 2040 if len(addrs) != 1 { 2041 return true 2042 } 2043 2044 // Make sure everything we're trying to spend is actually mature. 2045 switch class { 2046 case stdscript.STStakeGenPubKeyHash, stdscript.STStakeGenScriptHash: 2047 if !coinbaseMatured(w.chainParams, output.Height, currentHeight) { 2048 return true 2049 } 2050 case stdscript.STStakeRevocationPubKeyHash, stdscript.STStakeRevocationScriptHash: 2051 if !coinbaseMatured(w.chainParams, output.Height, currentHeight) { 2052 return true 2053 } 2054 case stdscript.STTreasuryAdd, stdscript.STTreasuryGenPubKeyHash, stdscript.STTreasuryGenScriptHash: 2055 if !coinbaseMatured(w.chainParams, output.Height, currentHeight) { 2056 return true 2057 } 2058 case stdscript.STStakeChangePubKeyHash, stdscript.STStakeChangeScriptHash: 2059 if !ticketChangeMatured(w.chainParams, output.Height, currentHeight) { 2060 return true 2061 } 2062 case stdscript.STPubKeyHashEcdsaSecp256k1: 2063 if output.FromCoinBase { 2064 if !coinbaseMatured(w.chainParams, output.Height, currentHeight) { 2065 return true 2066 } 2067 } 2068 default: 2069 return true 2070 } 2071 2072 // Only include the output if it is associated with the passed 2073 // account. There should only be one address since this is a 2074 // P2PKH script. 2075 addrAcct, err := w.manager.AddrAccount(addrmgrNs, addrs[0]) 2076 if err != nil || addrAcct != account { 2077 return true 2078 } 2079 2080 return false 2081 } 2082 2083 randTries := 0 2084 maxTries := 0 2085 if (amount != 0 || maxResults != 0) && minconf > 0 { 2086 numUnspent := w.txStore.UnspentOutputCount(dbtx) 2087 log.Debugf("Unspent bucket k/v count: %v", numUnspent) 2088 maxTries = numUnspent / 2 2089 } 2090 for ; randTries < maxTries; randTries++ { 2091 output, err := w.txStore.RandomUTXO(dbtx, minconf, currentHeight) 2092 if err != nil { 2093 return nil, err 2094 } 2095 if output == nil { 2096 break 2097 } 2098 if skip(output) { 2099 continue 2100 } 2101 seen[outpoint{output.Hash, output.Index}] = struct{}{} 2102 2103 txOut := &wire.TxOut{ 2104 Value: int64(output.Amount), 2105 Version: wire.DefaultPkScriptVersion, // XXX 2106 PkScript: output.PkScript, 2107 } 2108 eligible = append(eligible, Input{ 2109 OutPoint: output.OutPoint, 2110 PrevOut: *txOut, 2111 }) 2112 outTotal += output.Amount 2113 if amount != 0 && outTotal >= amount { 2114 return eligible, nil 2115 } 2116 if maxResults != 0 && len(eligible) == maxResults { 2117 return eligible, nil 2118 } 2119 } 2120 if randTries > 0 { 2121 log.Debugf("Abandoned random UTXO selection "+ 2122 "attempts after %v tries", randTries) 2123 } 2124 2125 eligible = eligible[:0] 2126 seen = nil 2127 outTotal = 0 2128 unspent, err := w.txStore.UnspentOutputs(dbtx) 2129 if err != nil { 2130 return nil, err 2131 } 2132 shuffle(len(unspent), func(i, j int) { 2133 unspent[i], unspent[j] = unspent[j], unspent[i] 2134 }) 2135 2136 for i := range unspent { 2137 output := unspent[i] 2138 if skip(output) { 2139 continue 2140 } 2141 2142 txOut := &wire.TxOut{ 2143 Value: int64(output.Amount), 2144 Version: wire.DefaultPkScriptVersion, // XXX 2145 PkScript: output.PkScript, 2146 } 2147 eligible = append(eligible, Input{ 2148 OutPoint: output.OutPoint, 2149 PrevOut: *txOut, 2150 }) 2151 outTotal += output.Amount 2152 if amount != 0 && outTotal >= amount { 2153 return eligible, nil 2154 } 2155 if maxResults != 0 && len(eligible) == maxResults { 2156 return eligible, nil 2157 } 2158 } 2159 if amount != 0 && outTotal < amount { 2160 return nil, errors.InsufficientBalance 2161 } 2162 2163 return eligible, nil 2164 } 2165 2166 // signP2PKHMsgTx sets the SignatureScript for every item in msgtx.TxIn. 2167 // It must be called every time a msgtx is changed. 2168 // Only P2PKH outputs are supported at this point. 2169 func (w *Wallet) signP2PKHMsgTx(msgtx *wire.MsgTx, prevOutputs []Input, addrmgrNs walletdb.ReadBucket) error { 2170 if len(prevOutputs) != len(msgtx.TxIn) { 2171 return errors.Errorf( 2172 "Number of prevOutputs (%d) does not match number of tx inputs (%d)", 2173 len(prevOutputs), len(msgtx.TxIn)) 2174 } 2175 for i, output := range prevOutputs { 2176 _, addrs := stdscript.ExtractAddrs(output.PrevOut.Version, output.PrevOut.PkScript, w.chainParams) 2177 if len(addrs) != 1 { 2178 continue // not error? errors.E(errors.Bug, "previous output address is not P2PKH") 2179 } 2180 apkh, ok := addrs[0].(*stdaddr.AddressPubKeyHashEcdsaSecp256k1V0) 2181 if !ok { 2182 return errors.E(errors.Bug, "previous output address is not P2PKH") 2183 } 2184 2185 privKey, done, err := w.manager.PrivateKey(addrmgrNs, apkh) 2186 if err != nil { 2187 return err 2188 } 2189 defer done() 2190 2191 sigscript, err := sign.SignatureScript(msgtx, i, output.PrevOut.PkScript, 2192 txscript.SigHashAll, privKey.Serialize(), dcrec.STEcdsaSecp256k1, true) 2193 if err != nil { 2194 return errors.E(errors.Op("txscript.SignatureScript"), err) 2195 } 2196 msgtx.TxIn[i].SignatureScript = sigscript 2197 } 2198 2199 return nil 2200 } 2201 2202 // signVoteOrRevocation signs a vote or revocation, specified by the isVote 2203 // argument. This signs the transaction by modifying tx's input scripts. 2204 func (w *Wallet) signVoteOrRevocation(addrmgrNs walletdb.ReadBucket, ticketPurchase, tx *wire.MsgTx, isVote bool) error { 2205 // Create a slice of functions to run after the retreived secrets are no 2206 // longer needed. 2207 doneFuncs := make([]func(), 0, len(tx.TxIn)) 2208 defer func() { 2209 for _, done := range doneFuncs { 2210 done() 2211 } 2212 }() 2213 2214 // Prepare functions to look up private key and script secrets so signing 2215 // can be performed. 2216 var getKey sign.KeyClosure = func(addr stdaddr.Address) ([]byte, dcrec.SignatureType, bool, error) { 2217 key, done, err := w.manager.PrivateKey(addrmgrNs, addr) 2218 if err != nil { 2219 return nil, 0, false, err 2220 } 2221 doneFuncs = append(doneFuncs, done) 2222 2223 // secp256k1 pubkeys are always compressed in Decred 2224 return key.Serialize(), dcrec.STEcdsaSecp256k1, true, nil 2225 } 2226 var getScript sign.ScriptClosure = func(addr stdaddr.Address) ([]byte, error) { 2227 return w.manager.RedeemScript(addrmgrNs, addr) 2228 } 2229 2230 // Revocations only contain one input, which is the input that must be 2231 // signed. The first input for a vote is the stakebase and the second input 2232 // must be signed. 2233 inputToSign := 0 2234 if isVote { 2235 inputToSign = 1 2236 } 2237 2238 // Sign the input. 2239 redeemTicketScript := ticketPurchase.TxOut[0].PkScript 2240 signedScript, err := sign.SignTxOutput(w.chainParams, tx, inputToSign, 2241 redeemTicketScript, txscript.SigHashAll, getKey, getScript, 2242 tx.TxIn[inputToSign].SignatureScript, true) // Yes treasury 2243 if err != nil { 2244 return errors.E(errors.Op("txscript.SignTxOutput"), errors.ScriptFailure, err) 2245 } 2246 tx.TxIn[inputToSign].SignatureScript = signedScript 2247 2248 return nil 2249 } 2250 2251 // signVote signs a vote transaction. This modifies the input scripts pointed 2252 // to by the vote transaction. 2253 func (w *Wallet) signVote(addrmgrNs walletdb.ReadBucket, ticketPurchase, vote *wire.MsgTx) error { 2254 return w.signVoteOrRevocation(addrmgrNs, ticketPurchase, vote, true) 2255 } 2256 2257 // newVoteScript generates a voting script from the passed VoteBits, for 2258 // use in a vote. 2259 func newVoteScript(voteBits stake.VoteBits) ([]byte, error) { 2260 b := make([]byte, 2+len(voteBits.ExtendedBits)) 2261 binary.LittleEndian.PutUint16(b[0:2], voteBits.Bits) 2262 copy(b[2:], voteBits.ExtendedBits) 2263 return stdscript.ProvablyPruneableScriptV0(b) 2264 } 2265 2266 // createUnsignedVote creates an unsigned vote transaction that votes using the 2267 // ticket specified by a ticket purchase hash and transaction with the provided 2268 // vote bits. The block height and hash must be of the previous block the vote 2269 // is voting on. 2270 func createUnsignedVote(ticketHash *chainhash.Hash, ticketPurchase *wire.MsgTx, 2271 blockHeight int32, blockHash *chainhash.Hash, voteBits stake.VoteBits, 2272 subsidyCache *blockchain.SubsidyCache, params *chaincfg.Params, 2273 dcp0010Active, dcp0012Active bool) (*wire.MsgTx, error) { 2274 2275 // Parse the ticket purchase transaction to determine the required output 2276 // destinations for vote rewards or revocations. 2277 ticketPayKinds, ticketHash160s, ticketValues, _, _, _ := 2278 stake.TxSStxStakeOutputInfo(ticketPurchase) 2279 2280 // Calculate the subsidy for votes at this height. 2281 ssv := blockchain.SSVOriginal 2282 switch { 2283 case dcp0012Active: 2284 ssv = blockchain.SSVDCP0012 2285 case dcp0010Active: 2286 ssv = blockchain.SSVDCP0010 2287 } 2288 subsidy := subsidyCache.CalcStakeVoteSubsidyV3(int64(blockHeight), ssv) 2289 2290 // Calculate the output values from this vote using the subsidy. 2291 voteRewardValues := stake.CalculateRewards(ticketValues, 2292 ticketPurchase.TxOut[0].Value, subsidy) 2293 2294 // Begin constructing the vote transaction. 2295 vote := wire.NewMsgTx() 2296 2297 // Add stakebase input to the vote. 2298 stakebaseOutPoint := wire.NewOutPoint(&chainhash.Hash{}, ^uint32(0), 2299 wire.TxTreeRegular) 2300 stakebaseInput := wire.NewTxIn(stakebaseOutPoint, subsidy, 2301 params.StakeBaseSigScript) 2302 vote.AddTxIn(stakebaseInput) 2303 2304 // Votes reference the ticket purchase with the second input. 2305 ticketOutPoint := wire.NewOutPoint(ticketHash, 0, wire.TxTreeStake) 2306 ticketInput := wire.NewTxIn(ticketOutPoint, 2307 ticketPurchase.TxOut[ticketOutPoint.Index].Value, nil) 2308 vote.AddTxIn(ticketInput) 2309 2310 // The first output references the previous block the vote is voting on. 2311 // This function never errors. 2312 blockRefScript, _ := txscript.GenerateSSGenBlockRef(*blockHash, 2313 uint32(blockHeight)) 2314 vote.AddTxOut(&wire.TxOut{ 2315 Value: 0, 2316 Version: wire.DefaultPkScriptVersion, // XXX 2317 PkScript: blockRefScript, 2318 }) 2319 2320 // The second output contains the votebits encode as a null data script. 2321 voteScript, err := newVoteScript(voteBits) 2322 if err != nil { 2323 return nil, err 2324 } 2325 vote.AddTxOut(&wire.TxOut{ 2326 Value: 0, 2327 Version: wire.DefaultPkScriptVersion, // XXX 2328 PkScript: voteScript, 2329 }) 2330 2331 // All remaining outputs pay to the output destinations and amounts tagged 2332 // by the ticket purchase. 2333 for i, hash160 := range ticketHash160s { 2334 var addr stdaddr.StakeAddress 2335 var err error 2336 if ticketPayKinds[i] { // P2SH 2337 addr, err = stdaddr.NewAddressScriptHashV0FromHash(hash160, params) 2338 } else { // P2PKH 2339 addr, err = stdaddr.NewAddressPubKeyHashEcdsaSecp256k1V0(hash160, params) 2340 } 2341 if err != nil { 2342 return nil, err 2343 } 2344 vers, script := addr.PayVoteCommitmentScript() 2345 vote.AddTxOut(&wire.TxOut{ 2346 Value: voteRewardValues[i], 2347 Version: vers, 2348 PkScript: script, 2349 }) 2350 } 2351 2352 return vote, nil 2353 }